mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Adopt room info power levels (#4245)
* Use the newly RoomInfo published PowerLevels; update permissions at the same time as the room info and avoid unnecessary async calls. * Introduce a RoomInfoProxyProtocol and associated mock and move away from mocked SDK RoomInfos * Bump the SDK to v25.06.23 * Expose new non-throwing methods for checking one's own user permissions * Fix the unit and preview tests (except the TimelineMediaPreviewDetails for which simplified snapshots were generated) * Converge on one single implementation for computing a room's avatar. * Fix more unit tests * Fix the TimelineMediaPreviewDetailsView snapshot tests This reverts commit 9b6c819e611ad2fa3de2c34a4a7aa556fc9faadd. --------- Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
@@ -350,6 +350,7 @@
|
|||||||
42A5A42ACF063EEE6B1980D2 /* ReportContentScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B17B1F29448D1B9049B11C /* ReportContentScreenViewModel.swift */; };
|
42A5A42ACF063EEE6B1980D2 /* ReportContentScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B17B1F29448D1B9049B11C /* ReportContentScreenViewModel.swift */; };
|
||||||
42B084FDE621FBEE433AF444 /* LegalInformationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */; };
|
42B084FDE621FBEE433AF444 /* LegalInformationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */; };
|
||||||
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A307A44F952CD73E63AE31 /* RoomEventStringBuilder.swift */; };
|
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A307A44F952CD73E63AE31 /* RoomEventStringBuilder.swift */; };
|
||||||
|
432EA37BDC97CEDBAB7B23A6 /* RoomInfoProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14517E5597594956FCE1950D /* RoomInfoProxyProtocol.swift */; };
|
||||||
43F06DF42EC00B3CE2B020A4 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; };
|
43F06DF42EC00B3CE2B020A4 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; };
|
||||||
43F35A7E5703D64DB0519C59 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */; };
|
43F35A7E5703D64DB0519C59 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */; };
|
||||||
440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; };
|
440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; };
|
||||||
@@ -1486,6 +1487,7 @@
|
|||||||
13F354AD441E2FD83DED89AF /* SeparatorMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorMediaEventsTimelineView.swift; sourceTree = "<group>"; };
|
13F354AD441E2FD83DED89AF /* SeparatorMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorMediaEventsTimelineView.swift; sourceTree = "<group>"; };
|
||||||
1423AB065857FA546444DB15 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
1423AB065857FA546444DB15 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
1434D5169F0EE319E226DA7F /* RoomMembershipDetailsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembershipDetailsProxyProtocol.swift; sourceTree = "<group>"; };
|
1434D5169F0EE319E226DA7F /* RoomMembershipDetailsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembershipDetailsProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
14517E5597594956FCE1950D /* RoomInfoProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomInfoProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
1454CF3AABD242F55C8A2615 /* InviteUsersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenModels.swift; sourceTree = "<group>"; };
|
1454CF3AABD242F55C8A2615 /* InviteUsersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenModels.swift; sourceTree = "<group>"; };
|
||||||
1511B1DCECC0DC75EB267328 /* KnockRequestsListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreen.swift; sourceTree = "<group>"; };
|
1511B1DCECC0DC75EB267328 /* KnockRequestsListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreen.swift; sourceTree = "<group>"; };
|
||||||
1562EAF6231151A675BED7A9 /* RoomDirectorySearchScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenCoordinator.swift; sourceTree = "<group>"; };
|
1562EAF6231151A675BED7A9 /* RoomDirectorySearchScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -3649,6 +3651,7 @@
|
|||||||
C07851F4EA81AA3339806A7B /* KnockRequestProxyProtocol.swift */,
|
C07851F4EA81AA3339806A7B /* KnockRequestProxyProtocol.swift */,
|
||||||
B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */,
|
B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */,
|
||||||
40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */,
|
40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */,
|
||||||
|
14517E5597594956FCE1950D /* RoomInfoProxyProtocol.swift */,
|
||||||
974AEAF3FE0C577A6C04AD6E /* RoomPermissions.swift */,
|
974AEAF3FE0C577A6C04AD6E /* RoomPermissions.swift */,
|
||||||
4D635709C1D6D37C225AD40E /* RoomPowerLevelProxyProtocol.swift */,
|
4D635709C1D6D37C225AD40E /* RoomPowerLevelProxyProtocol.swift */,
|
||||||
CFFA5E881D281810AB428EA3 /* RoomPowerLevelsProxy.swift */,
|
CFFA5E881D281810AB428EA3 /* RoomPowerLevelsProxy.swift */,
|
||||||
@@ -7561,6 +7564,7 @@
|
|||||||
8DF0EBD97753033C715D716E /* RoomFlowCoordinatorStateMachine.swift in Sources */,
|
8DF0EBD97753033C715D716E /* RoomFlowCoordinatorStateMachine.swift in Sources */,
|
||||||
9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */,
|
9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */,
|
||||||
5C8804B4F25903516E2DAB81 /* RoomInfoProxy.swift in Sources */,
|
5C8804B4F25903516E2DAB81 /* RoomInfoProxy.swift in Sources */,
|
||||||
|
432EA37BDC97CEDBAB7B23A6 /* RoomInfoProxyProtocol.swift in Sources */,
|
||||||
8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */,
|
8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */,
|
||||||
F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */,
|
F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */,
|
||||||
4A9CEEE612D6D8B3DDBD28BA /* RoomListFilterView.swift in Sources */,
|
4A9CEEE612D6D8B3DDBD28BA /* RoomListFilterView.swift in Sources */,
|
||||||
@@ -8744,7 +8748,7 @@
|
|||||||
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = exactVersion;
|
kind = exactVersion;
|
||||||
version = 25.06.20;
|
version = 25.06.23;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
||||||
|
|||||||
@@ -158,8 +158,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "68abbf0622825f008e2e9f70678b2433006e38ef",
|
"revision" : "f6220486952f912ec3e1224f49f9ab4d141799d0",
|
||||||
"version" : "25.6.20"
|
"version" : "25.6.23"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,47 +19,52 @@ struct BannedRoomProxyMockConfiguration {
|
|||||||
|
|
||||||
extension BannedRoomProxyMock {
|
extension BannedRoomProxyMock {
|
||||||
@MainActor
|
@MainActor
|
||||||
convenience init(_ configuration: KnockedRoomProxyMockConfiguration) {
|
convenience init(_ configuration: BannedRoomProxyMockConfiguration) {
|
||||||
self.init()
|
self.init()
|
||||||
id = configuration.id
|
id = configuration.id
|
||||||
info = RoomInfoProxy(roomInfo: .init(configuration))
|
info = RoomInfoProxyMock(configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RoomInfo {
|
extension RoomInfoProxyMock {
|
||||||
@MainActor init(_ configuration: BannedRoomProxyMockConfiguration) {
|
@MainActor convenience init(_ configuration: BannedRoomProxyMockConfiguration) {
|
||||||
self.init(id: configuration.id,
|
self.init()
|
||||||
encryptionState: .notEncrypted,
|
|
||||||
creator: nil,
|
id = configuration.id
|
||||||
displayName: configuration.name,
|
isEncrypted = false
|
||||||
rawName: nil,
|
creator = nil
|
||||||
topic: nil,
|
displayName = configuration.name
|
||||||
avatarUrl: configuration.avatarURL?.absoluteString,
|
rawName = nil
|
||||||
isDirect: false,
|
topic = nil
|
||||||
isPublic: false,
|
|
||||||
isSpace: false,
|
avatarURL = configuration.avatarURL
|
||||||
successorRoom: nil,
|
|
||||||
isFavourite: false,
|
isDirect = false
|
||||||
canonicalAlias: nil,
|
isPublic = false
|
||||||
alternativeAliases: [],
|
isSpace = false
|
||||||
membership: .knocked,
|
successor = nil
|
||||||
inviter: nil,
|
isFavourite = false
|
||||||
heroes: [],
|
canonicalAlias = nil
|
||||||
activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count),
|
alternativeAliases = []
|
||||||
invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count),
|
membership = .knocked
|
||||||
joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count),
|
inviter = nil
|
||||||
userPowerLevels: [:],
|
heroes = []
|
||||||
highlightCount: 0,
|
activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count
|
||||||
notificationCount: 0,
|
invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count
|
||||||
cachedUserDefinedNotificationMode: nil,
|
joinedMembersCount = configuration.members.filter { $0.membership == .join }.count
|
||||||
hasRoomCall: false,
|
highlightCount = 0
|
||||||
activeRoomCallParticipants: [],
|
notificationCount = 0
|
||||||
isMarkedUnread: false,
|
cachedUserDefinedNotificationMode = nil
|
||||||
numUnreadMessages: 0,
|
hasRoomCall = false
|
||||||
numUnreadNotifications: 0,
|
activeRoomCallParticipants = []
|
||||||
numUnreadMentions: 0,
|
isMarkedUnread = false
|
||||||
pinnedEventIds: [],
|
unreadMessagesCount = 0
|
||||||
joinRule: .knock,
|
unreadNotificationsCount = 0
|
||||||
historyVisibility: .shared)
|
unreadMentionsCount = 0
|
||||||
|
pinnedEventIDs = []
|
||||||
|
joinRule = .knock
|
||||||
|
historyVisibility = .shared
|
||||||
|
|
||||||
|
powerLevels = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6472,11 +6472,11 @@ class InvitedRoomProxyMock: InvitedRoomProxyProtocol, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable {
|
class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable {
|
||||||
var infoPublisher: CurrentValuePublisher<RoomInfoProxy, Never> {
|
var infoPublisher: CurrentValuePublisher<RoomInfoProxyProtocol, Never> {
|
||||||
get { return underlyingInfoPublisher }
|
get { return underlyingInfoPublisher }
|
||||||
set(value) { underlyingInfoPublisher = value }
|
set(value) { underlyingInfoPublisher = value }
|
||||||
}
|
}
|
||||||
var underlyingInfoPublisher: CurrentValuePublisher<RoomInfoProxy, Never>!
|
var underlyingInfoPublisher: CurrentValuePublisher<RoomInfoProxyProtocol, Never>!
|
||||||
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> {
|
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> {
|
||||||
get { return underlyingMembersPublisher }
|
get { return underlyingMembersPublisher }
|
||||||
set(value) { underlyingMembersPublisher = value }
|
set(value) { underlyingMembersPublisher = value }
|
||||||
@@ -13406,6 +13406,127 @@ class RoomDirectorySearchProxyMock: RoomDirectorySearchProxyProtocol, @unchecked
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class RoomInfoProxyMock: RoomInfoProxyProtocol, @unchecked Sendable {
|
||||||
|
var id: String {
|
||||||
|
get { return underlyingId }
|
||||||
|
set(value) { underlyingId = value }
|
||||||
|
}
|
||||||
|
var underlyingId: String!
|
||||||
|
var creator: String?
|
||||||
|
var displayName: String?
|
||||||
|
var rawName: String?
|
||||||
|
var topic: String?
|
||||||
|
var avatarURL: URL?
|
||||||
|
var isEncrypted: Bool {
|
||||||
|
get { return underlyingIsEncrypted }
|
||||||
|
set(value) { underlyingIsEncrypted = value }
|
||||||
|
}
|
||||||
|
var underlyingIsEncrypted: Bool!
|
||||||
|
var isDirect: Bool {
|
||||||
|
get { return underlyingIsDirect }
|
||||||
|
set(value) { underlyingIsDirect = value }
|
||||||
|
}
|
||||||
|
var underlyingIsDirect: Bool!
|
||||||
|
var isPublic: Bool {
|
||||||
|
get { return underlyingIsPublic }
|
||||||
|
set(value) { underlyingIsPublic = value }
|
||||||
|
}
|
||||||
|
var underlyingIsPublic: Bool!
|
||||||
|
var isPrivate: Bool {
|
||||||
|
get { return underlyingIsPrivate }
|
||||||
|
set(value) { underlyingIsPrivate = value }
|
||||||
|
}
|
||||||
|
var underlyingIsPrivate: Bool!
|
||||||
|
var isSpace: Bool {
|
||||||
|
get { return underlyingIsSpace }
|
||||||
|
set(value) { underlyingIsSpace = value }
|
||||||
|
}
|
||||||
|
var underlyingIsSpace: Bool!
|
||||||
|
var isFavourite: Bool {
|
||||||
|
get { return underlyingIsFavourite }
|
||||||
|
set(value) { underlyingIsFavourite = value }
|
||||||
|
}
|
||||||
|
var underlyingIsFavourite: Bool!
|
||||||
|
var canonicalAlias: String?
|
||||||
|
var alternativeAliases: [String] = []
|
||||||
|
var membership: Membership {
|
||||||
|
get { return underlyingMembership }
|
||||||
|
set(value) { underlyingMembership = value }
|
||||||
|
}
|
||||||
|
var underlyingMembership: Membership!
|
||||||
|
var inviter: RoomMemberProxyProtocol?
|
||||||
|
var activeMembersCount: Int {
|
||||||
|
get { return underlyingActiveMembersCount }
|
||||||
|
set(value) { underlyingActiveMembersCount = value }
|
||||||
|
}
|
||||||
|
var underlyingActiveMembersCount: Int!
|
||||||
|
var invitedMembersCount: Int {
|
||||||
|
get { return underlyingInvitedMembersCount }
|
||||||
|
set(value) { underlyingInvitedMembersCount = value }
|
||||||
|
}
|
||||||
|
var underlyingInvitedMembersCount: Int!
|
||||||
|
var joinedMembersCount: Int {
|
||||||
|
get { return underlyingJoinedMembersCount }
|
||||||
|
set(value) { underlyingJoinedMembersCount = value }
|
||||||
|
}
|
||||||
|
var underlyingJoinedMembersCount: Int!
|
||||||
|
var highlightCount: Int {
|
||||||
|
get { return underlyingHighlightCount }
|
||||||
|
set(value) { underlyingHighlightCount = value }
|
||||||
|
}
|
||||||
|
var underlyingHighlightCount: Int!
|
||||||
|
var notificationCount: Int {
|
||||||
|
get { return underlyingNotificationCount }
|
||||||
|
set(value) { underlyingNotificationCount = value }
|
||||||
|
}
|
||||||
|
var underlyingNotificationCount: Int!
|
||||||
|
var cachedUserDefinedNotificationMode: RoomNotificationMode?
|
||||||
|
var hasRoomCall: Bool {
|
||||||
|
get { return underlyingHasRoomCall }
|
||||||
|
set(value) { underlyingHasRoomCall = value }
|
||||||
|
}
|
||||||
|
var underlyingHasRoomCall: Bool!
|
||||||
|
var activeRoomCallParticipants: [String] = []
|
||||||
|
var isMarkedUnread: Bool {
|
||||||
|
get { return underlyingIsMarkedUnread }
|
||||||
|
set(value) { underlyingIsMarkedUnread = value }
|
||||||
|
}
|
||||||
|
var underlyingIsMarkedUnread: Bool!
|
||||||
|
var unreadMessagesCount: UInt {
|
||||||
|
get { return underlyingUnreadMessagesCount }
|
||||||
|
set(value) { underlyingUnreadMessagesCount = value }
|
||||||
|
}
|
||||||
|
var underlyingUnreadMessagesCount: UInt!
|
||||||
|
var unreadNotificationsCount: UInt {
|
||||||
|
get { return underlyingUnreadNotificationsCount }
|
||||||
|
set(value) { underlyingUnreadNotificationsCount = value }
|
||||||
|
}
|
||||||
|
var underlyingUnreadNotificationsCount: UInt!
|
||||||
|
var unreadMentionsCount: UInt {
|
||||||
|
get { return underlyingUnreadMentionsCount }
|
||||||
|
set(value) { underlyingUnreadMentionsCount = value }
|
||||||
|
}
|
||||||
|
var underlyingUnreadMentionsCount: UInt!
|
||||||
|
var pinnedEventIDs: Set<String> {
|
||||||
|
get { return underlyingPinnedEventIDs }
|
||||||
|
set(value) { underlyingPinnedEventIDs = value }
|
||||||
|
}
|
||||||
|
var underlyingPinnedEventIDs: Set<String>!
|
||||||
|
var joinRule: JoinRule?
|
||||||
|
var historyVisibility: RoomHistoryVisibility {
|
||||||
|
get { return underlyingHistoryVisibility }
|
||||||
|
set(value) { underlyingHistoryVisibility = value }
|
||||||
|
}
|
||||||
|
var underlyingHistoryVisibility: RoomHistoryVisibility!
|
||||||
|
var powerLevels: RoomPowerLevelsProxyProtocol {
|
||||||
|
get { return underlyingPowerLevels }
|
||||||
|
set(value) { underlyingPowerLevels = value }
|
||||||
|
}
|
||||||
|
var underlyingPowerLevels: RoomPowerLevelsProxyProtocol!
|
||||||
|
var successor: SuccessorRoom?
|
||||||
|
var heroes: [RoomHero] = []
|
||||||
|
|
||||||
|
}
|
||||||
class RoomMemberProxyMock: RoomMemberProxyProtocol, @unchecked Sendable {
|
class RoomMemberProxyMock: RoomMemberProxyProtocol, @unchecked Sendable {
|
||||||
var userID: String {
|
var userID: String {
|
||||||
get { return underlyingUserID }
|
get { return underlyingUserID }
|
||||||
@@ -13467,6 +13588,658 @@ class RoomPowerLevelsProxyMock: RoomPowerLevelsProxyProtocol, @unchecked Sendabl
|
|||||||
}
|
}
|
||||||
var underlyingValues: RoomPowerLevelsValues!
|
var underlyingValues: RoomPowerLevelsValues!
|
||||||
|
|
||||||
|
//MARK: - canOwnUser
|
||||||
|
|
||||||
|
var canOwnUserSendMessageUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserSendMessageCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserSendMessageUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserSendMessageUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserSendMessageUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserSendMessageUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserSendMessageCalled: Bool {
|
||||||
|
return canOwnUserSendMessageCallsCount > 0
|
||||||
|
}
|
||||||
|
var canOwnUserSendMessageReceivedMessageType: MessageLikeEventType?
|
||||||
|
var canOwnUserSendMessageReceivedInvocations: [MessageLikeEventType] = []
|
||||||
|
|
||||||
|
var canOwnUserSendMessageUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserSendMessageReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserSendMessageUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserSendMessageUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserSendMessageUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserSendMessageUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserSendMessageClosure: ((MessageLikeEventType) -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool {
|
||||||
|
canOwnUserSendMessageCallsCount += 1
|
||||||
|
canOwnUserSendMessageReceivedMessageType = messageType
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.canOwnUserSendMessageReceivedInvocations.append(messageType)
|
||||||
|
}
|
||||||
|
if let canOwnUserSendMessageClosure = canOwnUserSendMessageClosure {
|
||||||
|
return canOwnUserSendMessageClosure(messageType)
|
||||||
|
} else {
|
||||||
|
return canOwnUserSendMessageReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUser
|
||||||
|
|
||||||
|
var canOwnUserSendStateEventUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserSendStateEventCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserSendStateEventUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserSendStateEventUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserSendStateEventUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserSendStateEventUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserSendStateEventCalled: Bool {
|
||||||
|
return canOwnUserSendStateEventCallsCount > 0
|
||||||
|
}
|
||||||
|
var canOwnUserSendStateEventReceivedEvent: StateEventType?
|
||||||
|
var canOwnUserSendStateEventReceivedInvocations: [StateEventType] = []
|
||||||
|
|
||||||
|
var canOwnUserSendStateEventUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserSendStateEventReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserSendStateEventUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserSendStateEventUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserSendStateEventUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserSendStateEventUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserSendStateEventClosure: ((StateEventType) -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUser(sendStateEvent event: StateEventType) -> Bool {
|
||||||
|
canOwnUserSendStateEventCallsCount += 1
|
||||||
|
canOwnUserSendStateEventReceivedEvent = event
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.canOwnUserSendStateEventReceivedInvocations.append(event)
|
||||||
|
}
|
||||||
|
if let canOwnUserSendStateEventClosure = canOwnUserSendStateEventClosure {
|
||||||
|
return canOwnUserSendStateEventClosure(event)
|
||||||
|
} else {
|
||||||
|
return canOwnUserSendStateEventReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserInvite
|
||||||
|
|
||||||
|
var canOwnUserInviteUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserInviteCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserInviteUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserInviteUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserInviteUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserInviteUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserInviteCalled: Bool {
|
||||||
|
return canOwnUserInviteCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserInviteUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserInviteReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserInviteUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserInviteUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserInviteUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserInviteUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserInviteClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserInvite() -> Bool {
|
||||||
|
canOwnUserInviteCallsCount += 1
|
||||||
|
if let canOwnUserInviteClosure = canOwnUserInviteClosure {
|
||||||
|
return canOwnUserInviteClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserInviteReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserRedactOther
|
||||||
|
|
||||||
|
var canOwnUserRedactOtherUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserRedactOtherCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserRedactOtherUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserRedactOtherUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserRedactOtherUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserRedactOtherUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserRedactOtherCalled: Bool {
|
||||||
|
return canOwnUserRedactOtherCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserRedactOtherUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserRedactOtherReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserRedactOtherUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserRedactOtherUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserRedactOtherUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserRedactOtherUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserRedactOtherClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserRedactOther() -> Bool {
|
||||||
|
canOwnUserRedactOtherCallsCount += 1
|
||||||
|
if let canOwnUserRedactOtherClosure = canOwnUserRedactOtherClosure {
|
||||||
|
return canOwnUserRedactOtherClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserRedactOtherReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserRedactOwn
|
||||||
|
|
||||||
|
var canOwnUserRedactOwnUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserRedactOwnCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserRedactOwnUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserRedactOwnUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserRedactOwnUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserRedactOwnUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserRedactOwnCalled: Bool {
|
||||||
|
return canOwnUserRedactOwnCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserRedactOwnUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserRedactOwnReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserRedactOwnUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserRedactOwnUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserRedactOwnUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserRedactOwnUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserRedactOwnClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserRedactOwn() -> Bool {
|
||||||
|
canOwnUserRedactOwnCallsCount += 1
|
||||||
|
if let canOwnUserRedactOwnClosure = canOwnUserRedactOwnClosure {
|
||||||
|
return canOwnUserRedactOwnClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserRedactOwnReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserKick
|
||||||
|
|
||||||
|
var canOwnUserKickUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserKickCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserKickUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserKickUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserKickUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserKickUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserKickCalled: Bool {
|
||||||
|
return canOwnUserKickCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserKickUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserKickReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserKickUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserKickUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserKickUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserKickUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserKickClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserKick() -> Bool {
|
||||||
|
canOwnUserKickCallsCount += 1
|
||||||
|
if let canOwnUserKickClosure = canOwnUserKickClosure {
|
||||||
|
return canOwnUserKickClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserKickReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserBan
|
||||||
|
|
||||||
|
var canOwnUserBanUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserBanCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserBanUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserBanUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserBanUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserBanUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserBanCalled: Bool {
|
||||||
|
return canOwnUserBanCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserBanUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserBanReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserBanUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserBanUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserBanUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserBanUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserBanClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserBan() -> Bool {
|
||||||
|
canOwnUserBanCallsCount += 1
|
||||||
|
if let canOwnUserBanClosure = canOwnUserBanClosure {
|
||||||
|
return canOwnUserBanClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserBanReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserTriggerRoomNotification
|
||||||
|
|
||||||
|
var canOwnUserTriggerRoomNotificationUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserTriggerRoomNotificationCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserTriggerRoomNotificationUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserTriggerRoomNotificationUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserTriggerRoomNotificationUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserTriggerRoomNotificationUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserTriggerRoomNotificationCalled: Bool {
|
||||||
|
return canOwnUserTriggerRoomNotificationCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserTriggerRoomNotificationUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserTriggerRoomNotificationReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserTriggerRoomNotificationUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserTriggerRoomNotificationUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserTriggerRoomNotificationUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserTriggerRoomNotificationUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserTriggerRoomNotificationClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserTriggerRoomNotification() -> Bool {
|
||||||
|
canOwnUserTriggerRoomNotificationCallsCount += 1
|
||||||
|
if let canOwnUserTriggerRoomNotificationClosure = canOwnUserTriggerRoomNotificationClosure {
|
||||||
|
return canOwnUserTriggerRoomNotificationClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserTriggerRoomNotificationReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserPinOrUnpin
|
||||||
|
|
||||||
|
var canOwnUserPinOrUnpinUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserPinOrUnpinCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserPinOrUnpinUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserPinOrUnpinUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserPinOrUnpinUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserPinOrUnpinUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserPinOrUnpinCalled: Bool {
|
||||||
|
return canOwnUserPinOrUnpinCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserPinOrUnpinUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserPinOrUnpinReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserPinOrUnpinUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserPinOrUnpinUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserPinOrUnpinUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserPinOrUnpinUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserPinOrUnpinClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserPinOrUnpin() -> Bool {
|
||||||
|
canOwnUserPinOrUnpinCallsCount += 1
|
||||||
|
if let canOwnUserPinOrUnpinClosure = canOwnUserPinOrUnpinClosure {
|
||||||
|
return canOwnUserPinOrUnpinClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserPinOrUnpinReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - canOwnUserJoinCall
|
||||||
|
|
||||||
|
var canOwnUserJoinCallUnderlyingCallsCount = 0
|
||||||
|
var canOwnUserJoinCallCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserJoinCallUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserJoinCallUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserJoinCallUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserJoinCallUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserJoinCallCalled: Bool {
|
||||||
|
return canOwnUserJoinCallCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOwnUserJoinCallUnderlyingReturnValue: Bool!
|
||||||
|
var canOwnUserJoinCallReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return canOwnUserJoinCallUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = canOwnUserJoinCallUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
canOwnUserJoinCallUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
canOwnUserJoinCallUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var canOwnUserJoinCallClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
func canOwnUserJoinCall() -> Bool {
|
||||||
|
canOwnUserJoinCallCallsCount += 1
|
||||||
|
if let canOwnUserJoinCallClosure = canOwnUserJoinCallClosure {
|
||||||
|
return canOwnUserJoinCallClosure()
|
||||||
|
} else {
|
||||||
|
return canOwnUserJoinCallReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
//MARK: - canUser
|
//MARK: - canUser
|
||||||
|
|
||||||
var canUserUserIDSendMessageUnderlyingCallsCount = 0
|
var canUserUserIDSendMessageUnderlyingCallsCount = 0
|
||||||
|
|||||||
@@ -24,47 +24,52 @@ extension InvitedRoomProxyMock {
|
|||||||
self.init()
|
self.init()
|
||||||
id = configuration.id
|
id = configuration.id
|
||||||
inviter = configuration.inviter
|
inviter = configuration.inviter
|
||||||
info = RoomInfoProxy(roomInfo: .init(configuration))
|
info = RoomInfoProxyMock(configuration)
|
||||||
|
|
||||||
rejectInvitationReturnValue = .success(())
|
rejectInvitationReturnValue = .success(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RoomInfo {
|
extension RoomInfoProxyMock {
|
||||||
@MainActor init(_ configuration: InvitedRoomProxyMockConfiguration) {
|
@MainActor convenience init(_ configuration: InvitedRoomProxyMockConfiguration) {
|
||||||
self.init(id: configuration.id,
|
self.init()
|
||||||
encryptionState: .notEncrypted,
|
|
||||||
creator: nil,
|
id = configuration.id
|
||||||
displayName: configuration.name,
|
isEncrypted = false
|
||||||
rawName: nil,
|
creator = nil
|
||||||
topic: nil,
|
displayName = configuration.name
|
||||||
avatarUrl: configuration.avatarURL?.absoluteString,
|
rawName = nil
|
||||||
isDirect: false,
|
topic = nil
|
||||||
isPublic: false,
|
|
||||||
isSpace: false,
|
avatarURL = configuration.avatarURL
|
||||||
successorRoom: nil,
|
|
||||||
isFavourite: false,
|
isDirect = false
|
||||||
canonicalAlias: nil,
|
isPublic = false
|
||||||
alternativeAliases: [],
|
isSpace = false
|
||||||
membership: .knocked,
|
successor = nil
|
||||||
inviter: .init(configuration.inviter),
|
isFavourite = false
|
||||||
heroes: [],
|
canonicalAlias = nil
|
||||||
activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count),
|
alternativeAliases = []
|
||||||
invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count),
|
membership = .knocked
|
||||||
joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count),
|
inviter = .init(configuration.inviter)
|
||||||
userPowerLevels: [:],
|
heroes = []
|
||||||
highlightCount: 0,
|
activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count
|
||||||
notificationCount: 0,
|
invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count
|
||||||
cachedUserDefinedNotificationMode: nil,
|
joinedMembersCount = configuration.members.filter { $0.membership == .join }.count
|
||||||
hasRoomCall: false,
|
highlightCount = 0
|
||||||
activeRoomCallParticipants: [],
|
notificationCount = 0
|
||||||
isMarkedUnread: false,
|
cachedUserDefinedNotificationMode = nil
|
||||||
numUnreadMessages: 0,
|
hasRoomCall = false
|
||||||
numUnreadNotifications: 0,
|
activeRoomCallParticipants = []
|
||||||
numUnreadMentions: 0,
|
isMarkedUnread = false
|
||||||
pinnedEventIds: [],
|
unreadMessagesCount = 0
|
||||||
joinRule: .invite,
|
unreadNotificationsCount = 0
|
||||||
historyVisibility: .shared)
|
unreadMentionsCount = 0
|
||||||
|
pinnedEventIDs = []
|
||||||
|
joinRule = .invite
|
||||||
|
historyVisibility = .shared
|
||||||
|
|
||||||
|
powerLevels = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ extension JoinedRoomProxyMock {
|
|||||||
|
|
||||||
ownUserID = configuration.ownUserID
|
ownUserID = configuration.ownUserID
|
||||||
|
|
||||||
infoPublisher = CurrentValueSubject(.init(roomInfo: .init(configuration))).asCurrentValuePublisher()
|
|
||||||
membersPublisher = CurrentValueSubject(configuration.members).asCurrentValuePublisher()
|
membersPublisher = CurrentValueSubject(configuration.members).asCurrentValuePublisher()
|
||||||
knockRequestsStatePublisher = CurrentValueSubject(configuration.knockRequestsState).asCurrentValuePublisher()
|
knockRequestsStatePublisher = CurrentValueSubject(configuration.knockRequestsState).asCurrentValuePublisher()
|
||||||
typingMembersPublisher = CurrentValueSubject([]).asCurrentValuePublisher()
|
typingMembersPublisher = CurrentValueSubject([]).asCurrentValuePublisher()
|
||||||
@@ -134,53 +133,52 @@ extension JoinedRoomProxyMock {
|
|||||||
isVisibleInRoomDirectoryReturnValue = .success(configuration.isVisibleInPublicDirectory)
|
isVisibleInRoomDirectoryReturnValue = .success(configuration.isVisibleInPublicDirectory)
|
||||||
|
|
||||||
predecessorRoom = configuration.predecessor
|
predecessorRoom = configuration.predecessor
|
||||||
|
|
||||||
|
let roomInfoProxyMock = RoomInfoProxyMock(configuration)
|
||||||
|
roomInfoProxyMock.powerLevels = powerLevelsProxyMock
|
||||||
|
|
||||||
|
infoPublisher = CurrentValueSubject(roomInfoProxyMock).asCurrentValuePublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RoomInfo {
|
extension RoomInfoProxyMock {
|
||||||
@MainActor init(_ configuration: JoinedRoomProxyMockConfiguration) {
|
@MainActor convenience init(_ configuration: JoinedRoomProxyMockConfiguration) {
|
||||||
self.init(id: configuration.id,
|
self.init()
|
||||||
encryptionState: configuration.isEncrypted ? .encrypted : .notEncrypted,
|
|
||||||
creator: nil,
|
id = configuration.id
|
||||||
displayName: configuration.name,
|
isEncrypted = configuration.isEncrypted
|
||||||
rawName: configuration.name,
|
creator = nil
|
||||||
topic: configuration.topic,
|
displayName = configuration.name
|
||||||
avatarUrl: configuration.avatarURL?.absoluteString,
|
rawName = configuration.name
|
||||||
isDirect: configuration.isDirect,
|
topic = configuration.topic
|
||||||
isPublic: configuration.isPublic,
|
avatarURL = configuration.avatarURL
|
||||||
isSpace: configuration.isSpace,
|
isDirect = configuration.isDirect
|
||||||
successorRoom: configuration.successor,
|
isPublic = configuration.isPublic
|
||||||
isFavourite: false,
|
isSpace = configuration.isSpace
|
||||||
canonicalAlias: configuration.canonicalAlias,
|
successor = configuration.successor
|
||||||
alternativeAliases: configuration.alternativeAliases,
|
isFavourite = false
|
||||||
membership: configuration.membership,
|
canonicalAlias = configuration.canonicalAlias
|
||||||
inviter: configuration.inviter.map { RoomMember(userId: $0.userID,
|
alternativeAliases = configuration.alternativeAliases
|
||||||
displayName: $0.displayName,
|
membership = configuration.membership
|
||||||
avatarUrl: $0.avatarURL?.absoluteString,
|
inviter = configuration.inviter
|
||||||
membership: $0.membership,
|
heroes = configuration.heroes.map(RoomHero.init)
|
||||||
isNameAmbiguous: false,
|
activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count
|
||||||
powerLevel: Int64($0.powerLevel),
|
invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count
|
||||||
normalizedPowerLevel: Int64($0.powerLevel),
|
joinedMembersCount = configuration.members.filter { $0.membership == .join }.count
|
||||||
isIgnored: $0.isIgnored,
|
highlightCount = 0
|
||||||
suggestedRoleForPowerLevel: $0.role,
|
notificationCount = 0
|
||||||
membershipChangeReason: $0.membershipChangeReason) },
|
cachedUserDefinedNotificationMode = .allMessages
|
||||||
heroes: configuration.heroes.map(RoomHero.init),
|
hasRoomCall = configuration.hasOngoingCall
|
||||||
activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count),
|
activeRoomCallParticipants = []
|
||||||
invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count),
|
isMarkedUnread = false
|
||||||
joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count),
|
unreadMessagesCount = 0
|
||||||
userPowerLevels: [:],
|
unreadNotificationsCount = 0
|
||||||
highlightCount: 0,
|
unreadMentionsCount = 0
|
||||||
notificationCount: 0,
|
pinnedEventIDs = configuration.pinnedEventIDs
|
||||||
cachedUserDefinedNotificationMode: .allMessages,
|
joinRule = configuration.joinRule
|
||||||
hasRoomCall: configuration.hasOngoingCall,
|
historyVisibility = .shared
|
||||||
activeRoomCallParticipants: [],
|
|
||||||
isMarkedUnread: false,
|
powerLevels = RoomPowerLevelsProxyMock(configuration: configuration.powerLevelsConfiguration)
|
||||||
numUnreadMessages: 0,
|
|
||||||
numUnreadNotifications: 0,
|
|
||||||
numUnreadMentions: 0,
|
|
||||||
pinnedEventIds: Array(configuration.pinnedEventIDs),
|
|
||||||
joinRule: configuration.joinRule,
|
|
||||||
historyVisibility: .shared)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,44 +22,49 @@ extension KnockedRoomProxyMock {
|
|||||||
convenience init(_ configuration: KnockedRoomProxyMockConfiguration) {
|
convenience init(_ configuration: KnockedRoomProxyMockConfiguration) {
|
||||||
self.init()
|
self.init()
|
||||||
id = configuration.id
|
id = configuration.id
|
||||||
info = RoomInfoProxy(roomInfo: .init(configuration))
|
info = RoomInfoProxyMock(configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RoomInfo {
|
extension RoomInfoProxyMock {
|
||||||
@MainActor init(_ configuration: KnockedRoomProxyMockConfiguration) {
|
@MainActor convenience init(_ configuration: KnockedRoomProxyMockConfiguration) {
|
||||||
self.init(id: configuration.id,
|
self.init()
|
||||||
encryptionState: .notEncrypted,
|
|
||||||
creator: nil,
|
id = configuration.id
|
||||||
displayName: configuration.name,
|
isEncrypted = false
|
||||||
rawName: nil,
|
creator = nil
|
||||||
topic: nil,
|
displayName = configuration.name
|
||||||
avatarUrl: configuration.avatarURL?.absoluteString,
|
rawName = nil
|
||||||
isDirect: false,
|
topic = nil
|
||||||
isPublic: false,
|
|
||||||
isSpace: false,
|
avatarURL = configuration.avatarURL
|
||||||
successorRoom: nil,
|
|
||||||
isFavourite: false,
|
isDirect = false
|
||||||
canonicalAlias: nil,
|
isPublic = false
|
||||||
alternativeAliases: [],
|
isSpace = false
|
||||||
membership: .knocked,
|
successor = nil
|
||||||
inviter: nil,
|
isFavourite = false
|
||||||
heroes: [],
|
canonicalAlias = nil
|
||||||
activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count),
|
alternativeAliases = []
|
||||||
invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count),
|
membership = .knocked
|
||||||
joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count),
|
inviter = nil
|
||||||
userPowerLevels: [:],
|
heroes = []
|
||||||
highlightCount: 0,
|
activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count
|
||||||
notificationCount: 0,
|
invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count
|
||||||
cachedUserDefinedNotificationMode: nil,
|
joinedMembersCount = configuration.members.filter { $0.membership == .join }.count
|
||||||
hasRoomCall: false,
|
highlightCount = 0
|
||||||
activeRoomCallParticipants: [],
|
notificationCount = 0
|
||||||
isMarkedUnread: false,
|
cachedUserDefinedNotificationMode = nil
|
||||||
numUnreadMessages: 0,
|
hasRoomCall = false
|
||||||
numUnreadNotifications: 0,
|
activeRoomCallParticipants = []
|
||||||
numUnreadMentions: 0,
|
isMarkedUnread = false
|
||||||
pinnedEventIds: [],
|
unreadMessagesCount = 0
|
||||||
joinRule: .knock,
|
unreadNotificationsCount = 0
|
||||||
historyVisibility: .shared)
|
unreadMentionsCount = 0
|
||||||
|
pinnedEventIDs = []
|
||||||
|
joinRule = .knock
|
||||||
|
historyVisibility = .shared
|
||||||
|
|
||||||
|
powerLevels = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,17 @@ extension RoomPowerLevelsProxyMock {
|
|||||||
|
|
||||||
underlyingValues = RoomPowerLevelsValues.mock
|
underlyingValues = RoomPowerLevelsValues.mock
|
||||||
|
|
||||||
|
canOwnUserSendMessageReturnValue = configuration.canUserSendMessage
|
||||||
|
canOwnUserSendStateEventReturnValue = configuration.canUserSendState
|
||||||
|
canOwnUserInviteReturnValue = configuration.canUserInvite
|
||||||
|
canOwnUserRedactOtherReturnValue = configuration.canUserRedactOther
|
||||||
|
canOwnUserRedactOwnReturnValue = configuration.canUserRedactOwn
|
||||||
|
canOwnUserKickReturnValue = configuration.canUserKick
|
||||||
|
canOwnUserBanReturnValue = configuration.canUserBan
|
||||||
|
canOwnUserTriggerRoomNotificationReturnValue = configuration.canUserTriggerRoomNotification
|
||||||
|
canOwnUserPinOrUnpinReturnValue = configuration.canUserPin
|
||||||
|
canOwnUserJoinCallReturnValue = configuration.canUserJoinCall
|
||||||
|
|
||||||
canUserUserIDSendMessageReturnValue = .success(configuration.canUserSendMessage)
|
canUserUserIDSendMessageReturnValue = .success(configuration.canUserSendMessage)
|
||||||
canUserUserIDSendStateEventReturnValue = .success(configuration.canUserSendState)
|
canUserUserIDSendStateEventReturnValue = .success(configuration.canUserSendState)
|
||||||
canUserInviteUserIDReturnValue = .success(configuration.canUserInvite)
|
canUserInviteUserIDReturnValue = .success(configuration.canUserInvite)
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ enum TimelineMediaPreviewItem: Equatable {
|
|||||||
case loading(Loading)
|
case loading(Loading)
|
||||||
|
|
||||||
/// Wraps a media file and title to be previewed with QuickLook.
|
/// Wraps a media file and title to be previewed with QuickLook.
|
||||||
class Media: NSObject, QLPreviewItem, Identifiable {
|
@Observable class Media: NSObject, QLPreviewItem, Identifiable {
|
||||||
fileprivate(set) var timelineItem: EventBasedMessageTimelineItemProtocol
|
fileprivate(set) var timelineItem: EventBasedMessageTimelineItemProtocol
|
||||||
var fileHandle: MediaFileHandleProxy?
|
var fileHandle: MediaFileHandleProxy?
|
||||||
var downloadError: Error?
|
var downloadError: Error?
|
||||||
|
|||||||
+3
-6
@@ -180,27 +180,24 @@ struct TimelineMediaPreviewDetailsView_Previews: PreviewProvider, TestablePrevie
|
|||||||
if case let .media(mediaItem) = viewModel.state.currentItem {
|
if case let .media(mediaItem) = viewModel.state.currentItem {
|
||||||
TimelineMediaPreviewDetailsView(item: mediaItem, context: viewModel.context, sheetHeight: $sheetHeight)
|
TimelineMediaPreviewDetailsView(item: mediaItem, context: viewModel.context, sheetHeight: $sheetHeight)
|
||||||
.previewDisplayName("Image")
|
.previewDisplayName("Image")
|
||||||
.snapshotPreferences(expect: viewModel.context.$viewState.map { state in
|
.snapshotPreferences(expect: mediaItem.observe(\.fileHandle).map { $0 != nil }.eraseToStream())
|
||||||
state.currentItemActions?.secondaryActions.contains(.redact) ?? false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .media(mediaItem) = loadingViewModel.state.currentItem {
|
if case let .media(mediaItem) = loadingViewModel.state.currentItem {
|
||||||
TimelineMediaPreviewDetailsView(item: mediaItem, context: loadingViewModel.context, sheetHeight: $sheetHeight)
|
TimelineMediaPreviewDetailsView(item: mediaItem, context: loadingViewModel.context, sheetHeight: $sheetHeight)
|
||||||
.previewDisplayName("Loading")
|
.previewDisplayName("Loading")
|
||||||
.snapshotPreferences(expect: loadingViewModel.context.$viewState.map { state in
|
|
||||||
state.currentItemActions?.secondaryActions.contains(.redact) ?? false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .media(mediaItem) = unknownTypeViewModel.state.currentItem {
|
if case let .media(mediaItem) = unknownTypeViewModel.state.currentItem {
|
||||||
TimelineMediaPreviewDetailsView(item: mediaItem, context: unknownTypeViewModel.context, sheetHeight: $sheetHeight)
|
TimelineMediaPreviewDetailsView(item: mediaItem, context: unknownTypeViewModel.context, sheetHeight: $sheetHeight)
|
||||||
.previewDisplayName("Unknown type")
|
.previewDisplayName("Unknown type")
|
||||||
|
.snapshotPreferences(expect: mediaItem.observe(\.fileHandle).map { $0 != nil }.eraseToStream())
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .media(mediaItem) = presentedOnRoomViewModel.state.currentItem {
|
if case let .media(mediaItem) = presentedOnRoomViewModel.state.currentItem {
|
||||||
TimelineMediaPreviewDetailsView(item: mediaItem, context: presentedOnRoomViewModel.context, sheetHeight: $sheetHeight)
|
TimelineMediaPreviewDetailsView(item: mediaItem, context: presentedOnRoomViewModel.context, sheetHeight: $sheetHeight)
|
||||||
.previewDisplayName("Incoming on Room")
|
.previewDisplayName("Incoming on Room")
|
||||||
|
.snapshotPreferences(expect: mediaItem.observe(\.fileHandle).map { $0 != nil }.eraseToStream())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-12
@@ -27,9 +27,6 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
|
|||||||
super.init(initialViewState: KnockRequestsListScreenViewState(), mediaProvider: mediaProvider)
|
super.init(initialViewState: KnockRequestsListScreenViewState(), mediaProvider: mediaProvider)
|
||||||
|
|
||||||
updateRoomInfo(roomInfo: roomProxy.infoPublisher.value)
|
updateRoomInfo(roomInfo: roomProxy.infoPublisher.value)
|
||||||
Task {
|
|
||||||
await updatePermissions()
|
|
||||||
}
|
|
||||||
|
|
||||||
setupSubscriptions()
|
setupSubscriptions()
|
||||||
}
|
}
|
||||||
@@ -189,7 +186,6 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
|
|||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] roomInfo in
|
.sink { [weak self] roomInfo in
|
||||||
self?.updateRoomInfo(roomInfo: roomInfo)
|
self?.updateRoomInfo(roomInfo: roomInfo)
|
||||||
Task { await self?.updatePermissions() }
|
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@@ -214,20 +210,17 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateRoomInfo(roomInfo: RoomInfoProxy) {
|
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
|
||||||
switch roomInfo.joinRule {
|
switch roomInfo.joinRule {
|
||||||
case .knock, .knockRestricted:
|
case .knock, .knockRestricted:
|
||||||
state.isKnockableRoom = true
|
state.isKnockableRoom = true
|
||||||
default:
|
default:
|
||||||
state.isKnockableRoom = false
|
state.isKnockableRoom = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
state.canAccept = (try? roomInfo.powerLevels.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
||||||
private func updatePermissions() async {
|
state.canDecline = (try? roomInfo.powerLevels.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
||||||
let powerLevels = try? await roomProxy.powerLevels().get()
|
state.canBan = (try? roomInfo.powerLevels.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canAccept = (try? powerLevels?.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
state.canDecline = (try? powerLevels?.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
state.canBan = (try? powerLevels?.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let loadingIndicatorIdentifier = "\(KnockRequestsListScreenViewModel.self)-Loading"
|
private static let loadingIndicatorIdentifier = "\(KnockRequestsListScreenViewModel.self)-Loading"
|
||||||
|
|||||||
@@ -39,12 +39,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
|||||||
avatarURL: roomAvatar,
|
avatarURL: roomAvatar,
|
||||||
bindings: .init(name: roomName ?? "", topic: roomTopic ?? "")), mediaProvider: mediaProvider)
|
bindings: .init(name: roomName ?? "", topic: roomTopic ?? "")), mediaProvider: mediaProvider)
|
||||||
|
|
||||||
Task {
|
updateRoomInfo(roomInfo: roomProxy.infoPublisher.value)
|
||||||
let powerLevels = try? await roomProxy.powerLevels().get()
|
|
||||||
state.canEditAvatar = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomAvatar).get()) == .some(true)
|
|
||||||
state.canEditName = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName).get()) == .some(true)
|
|
||||||
state.canEditTopic = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomTopic).get()) == .some(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -91,6 +86,12 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
|
||||||
|
state.canEditAvatar = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomAvatar).get()) == .some(true)
|
||||||
|
state.canEditName = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName).get()) == .some(true)
|
||||||
|
state.canEditTopic = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomTopic).get()) == .some(true)
|
||||||
|
}
|
||||||
|
|
||||||
private func saveRoomDetails() {
|
private func saveRoomDetails() {
|
||||||
Task {
|
Task {
|
||||||
let userIndicatorID = UUID().uuidString
|
let userIndicatorID = UUID().uuidString
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateRoomInfo(roomProxy.infoPublisher.value)
|
updateRoomInfo(roomProxy.infoPublisher.value)
|
||||||
Task { await updatePowerLevelPermissions() }
|
|
||||||
|
|
||||||
setupRoomSubscription()
|
setupRoomSubscription()
|
||||||
Task { await fetchMembersIfNeeded() }
|
Task { await fetchMembersIfNeeded() }
|
||||||
@@ -183,7 +182,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
|||||||
.throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true)
|
.throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true)
|
||||||
.sink { [weak self] roomInfo in
|
.sink { [weak self] roomInfo in
|
||||||
self?.updateRoomInfo(roomInfo)
|
self?.updateRoomInfo(roomInfo)
|
||||||
Task { await self?.updatePowerLevelPermissions() }
|
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@@ -206,7 +204,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateRoomInfo(_ roomInfo: RoomInfoProxy) {
|
private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) {
|
||||||
state.isEncrypted = roomInfo.isEncrypted
|
state.isEncrypted = roomInfo.isEncrypted
|
||||||
state.isDirect = roomInfo.isDirect
|
state.isDirect = roomInfo.isDirect
|
||||||
state.bindings.isFavourite = roomInfo.isFavourite
|
state.bindings.isFavourite = roomInfo.isFavourite
|
||||||
@@ -225,6 +223,18 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
|||||||
default:
|
default:
|
||||||
state.isKnockableRoom = false
|
state.isKnockableRoom = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.canEditRoomName = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName).get()) == true
|
||||||
|
state.canEditRoomTopic = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomTopic).get()) == true
|
||||||
|
state.canEditRoomAvatar = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomAvatar).get()) == true
|
||||||
|
state.canInviteUsers = (try? roomInfo.powerLevels.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
||||||
|
state.canKickUsers = (try? roomInfo.powerLevels.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
||||||
|
state.canBanUsers = (try? roomInfo.powerLevels.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
||||||
|
state.canJoinCall = (try? roomInfo.powerLevels.canUserJoinCall(userID: roomProxy.ownUserID).get()) == true
|
||||||
|
|
||||||
|
Task {
|
||||||
|
state.canEditRolesOrPermissions = await (try? roomProxy.suggestedRole(for: roomProxy.ownUserID).get()) == .administrator
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchMembersIfNeeded() async {
|
private func fetchMembersIfNeeded() async {
|
||||||
@@ -280,19 +290,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePowerLevelPermissions() async {
|
|
||||||
state.canEditRolesOrPermissions = await (try? roomProxy.suggestedRole(for: roomProxy.ownUserID).get()) == .administrator
|
|
||||||
|
|
||||||
let powerLevels = try? await roomProxy.powerLevels().get()
|
|
||||||
state.canEditRoomName = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName).get()) == true
|
|
||||||
state.canEditRoomTopic = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomTopic).get()) == true
|
|
||||||
state.canEditRoomAvatar = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomAvatar).get()) == true
|
|
||||||
state.canInviteUsers = (try? powerLevels?.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
state.canKickUsers = (try? powerLevels?.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
state.canBanUsers = (try? powerLevels?.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
state.canJoinCall = (try? powerLevels?.canUserJoinCall(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupNotificationSettingsSubscription() {
|
private func setupNotificationSettingsSubscription() {
|
||||||
notificationSettingsProxy.callbacks
|
notificationSettingsProxy.callbacks
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
|
|||||||
@@ -338,13 +338,13 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
RoomDetailsScreen(context: genericRoomViewModel.context)
|
RoomDetailsScreen(context: genericRoomViewModel.context)
|
||||||
.snapshotPreferences(expect: genericRoomViewModel.context.$viewState.map { state in
|
.snapshotPreferences(expect: genericRoomViewModel.context.$viewState.map { state in
|
||||||
state.shortcuts.contains(.invite)
|
state.canSeeSecurityAndPrivacy == true
|
||||||
})
|
})
|
||||||
.previewDisplayName("Generic Room")
|
.previewDisplayName("Generic Room")
|
||||||
|
|
||||||
RoomDetailsScreen(context: simpleRoomViewModel.context)
|
RoomDetailsScreen(context: simpleRoomViewModel.context)
|
||||||
.snapshotPreferences(expect: simpleRoomViewModel.context.$viewState.map { state in
|
.snapshotPreferences(expect: simpleRoomViewModel.context.$viewState.map { state in
|
||||||
state.shortcuts.contains(.invite)
|
state.canSeeSecurityAndPrivacy == true
|
||||||
})
|
})
|
||||||
.previewDisplayName("Simple Room")
|
.previewDisplayName("Simple Room")
|
||||||
|
|
||||||
|
|||||||
@@ -99,10 +99,10 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
bannedMembers: roomMembersDetails.bannedMembers,
|
bannedMembers: roomMembersDetails.bannedMembers,
|
||||||
bindings: state.bindings)
|
bindings: state.bindings)
|
||||||
|
|
||||||
let powerLevels = try? await roomProxy.powerLevels().get()
|
let powerLevels = roomProxy.infoPublisher.value.powerLevels
|
||||||
self.state.canInviteUsers = (try? powerLevels?.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
self.state.canInviteUsers = (try? powerLevels.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
||||||
self.state.canKickUsers = (try? powerLevels?.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
self.state.canKickUsers = (try? powerLevels.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
||||||
self.state.canBanUsers = (try? powerLevels?.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
self.state.canBanUsers = (try? powerLevels.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
||||||
|
|
||||||
hideLoadingIndicator(Self.updateStateLoadingIndicatorIdentifier)
|
hideLoadingIndicator(Self.updateStateLoadingIndicatorIdentifier)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-10
@@ -39,12 +39,12 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
|
|||||||
// Automatically update the room permissions
|
// Automatically update the room permissions
|
||||||
roomProxy.infoPublisher
|
roomProxy.infoPublisher
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] roomInfo in
|
||||||
Task { await self?.updatePermissions() }
|
self?.updateRoomInfo(roomInfo: roomInfo)
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
Task { await updatePermissions() }
|
updateRoomInfo(roomInfo: roomProxy.infoPublisher.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -113,13 +113,8 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
|
|||||||
|
|
||||||
// MARK: - Permissions
|
// MARK: - Permissions
|
||||||
|
|
||||||
private func updatePermissions() async {
|
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
|
||||||
switch await roomProxy.powerLevels() {
|
state.permissions = .init(powerLevels: roomInfo.powerLevels.values)
|
||||||
case .success(let powerLevels):
|
|
||||||
state.permissions = .init(powerLevels: powerLevels.values)
|
|
||||||
case .failure:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func editPermissions(group: RoomRolesAndPermissionsScreenPermissionsGroup) {
|
private func editPermissions(group: RoomRolesAndPermissionsScreenPermissionsGroup) {
|
||||||
|
|||||||
+1
-3
@@ -47,9 +47,7 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
|||||||
self?.suggestionTriggerSubject.value != nil ? .milliseconds(500) : .milliseconds(0)
|
self?.suggestionTriggerSubject.value != nil ? .milliseconds(500) : .milliseconds(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
Task {
|
canMentionAllUsers = (try? roomProxy.infoPublisher.value.powerLevels.canUserTriggerRoomNotification(userID: roomProxy.ownUserID).get()) == true
|
||||||
canMentionAllUsers = await (try? roomProxy.powerLevels().get().canUserTriggerRoomNotification(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processTextMessage(_ textMessage: String, selectedRange: NSRange) {
|
func processTextMessage(_ textMessage: String, selectedRange: NSRange) {
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxy) async {
|
private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxyProtocol) async {
|
||||||
state.hasSuccessor = roomInfo.successor != nil
|
state.hasSuccessor = roomInfo.successor != nil
|
||||||
|
|
||||||
let pinnedEventIDs = roomInfo.pinnedEventIDs
|
let pinnedEventIDs = roomInfo.pinnedEventIDs
|
||||||
@@ -348,12 +348,11 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
state.isKnockableRoom = false
|
state.isKnockableRoom = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let powerLevels = try? await roomProxy.powerLevels().get()
|
state.canSendMessage = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendMessage: .roomMessage).get()) == true
|
||||||
state.canSendMessage = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendMessage: .roomMessage).get()) == true
|
state.canJoinCall = (try? roomInfo.powerLevels.canUserJoinCall(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canJoinCall = (try? powerLevels?.canUserJoinCall(userID: roomProxy.ownUserID).get()) == true
|
state.canAcceptKnocks = (try? roomInfo.powerLevels.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canAcceptKnocks = (try? powerLevels?.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
state.canDeclineKnocks = (try? roomInfo.powerLevels.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canDeclineKnocks = (try? powerLevels?.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
state.canBan = (try? roomInfo.powerLevels.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canBan = (try? powerLevels?.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupPinnedEventsTimelineItemProviderIfNeeded() {
|
private func setupPinnedEventsTimelineItemProviderIfNeeded() {
|
||||||
|
|||||||
@@ -29,14 +29,12 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await self?.handleRoomInfoUpdate(roomInfo)
|
self?.handleRoomInfoUpdate(roomInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
Task {
|
handleRoomInfoUpdate(roomProxy.infoPublisher.value)
|
||||||
await handleRoomInfoUpdate(roomProxy.infoPublisher.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -64,7 +62,7 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxy) async {
|
private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxyProtocol) {
|
||||||
state.canSendMessage = await (try? roomProxy.powerLevels().get().canUser(userID: roomProxy.ownUserID, sendMessage: .roomMessage).get()) == true
|
state.canSendMessage = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendMessage: .roomMessage).get()) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,9 +119,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
setupSubscriptions()
|
setupSubscriptions()
|
||||||
setupDirectRoomSubscriptionsIfNeeded()
|
setupDirectRoomSubscriptionsIfNeeded()
|
||||||
|
|
||||||
// Set initial values for redacting from the macOS context menu.
|
updateRoomInfo(roomInfo: roomProxy.infoPublisher.value)
|
||||||
Task { await updatePermissions() }
|
|
||||||
|
|
||||||
state.audioPlayerStateProvider = { [weak self] itemID -> AudioPlayerState? in
|
state.audioPlayerStateProvider = { [weak self] itemID -> AudioPlayerState? in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return nil
|
return nil
|
||||||
@@ -403,14 +402,15 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePermissions() async {
|
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
|
||||||
let powerLevels = try? await roomProxy.powerLevels().get()
|
state.pinnedEventIDs = roomInfo.pinnedEventIDs
|
||||||
state.canCurrentUserSendMessage = (try? powerLevels?.canUser(userID: roomProxy.ownUserID, sendMessage: .roomMessage).get()) == true
|
|
||||||
state.canCurrentUserRedactOthers = (try? powerLevels?.canUserRedactOther(userID: roomProxy.ownUserID).get()) == true
|
state.canCurrentUserSendMessage = (try? roomInfo.powerLevels.canUser(userID: roomProxy.ownUserID, sendMessage: .roomMessage).get()) == true
|
||||||
state.canCurrentUserRedactSelf = (try? powerLevels?.canUserRedactOwn(userID: roomProxy.ownUserID).get()) == true
|
state.canCurrentUserRedactOthers = (try? roomInfo.powerLevels.canUserRedactOther(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canCurrentUserPin = (try? powerLevels?.canUserPinOrUnpin(userID: roomProxy.ownUserID).get()) == true
|
state.canCurrentUserRedactSelf = (try? roomInfo.powerLevels.canUserRedactOwn(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canCurrentUserKick = (try? powerLevels?.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
state.canCurrentUserPin = (try? roomInfo.powerLevels.canUserPinOrUnpin(userID: roomProxy.ownUserID).get()) == true
|
||||||
state.canCurrentUserBan = (try? powerLevels?.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
state.canCurrentUserKick = (try? roomInfo.powerLevels.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
||||||
|
state.canCurrentUserBan = (try? roomInfo.powerLevels.canUserBan(userID: roomProxy.ownUserID).get()) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSubscriptions() {
|
private func setupSubscriptions() {
|
||||||
@@ -445,8 +445,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
guard !Task.isCancelled else {
|
guard !Task.isCancelled else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self?.state.pinnedEventIDs = roomInfo.pinnedEventIDs
|
|
||||||
await self?.updatePermissions()
|
self?.updateRoomInfo(roomInfo: roomInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -487,10 +487,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
case .displayMediaUploadPreviewScreen(let url):
|
case .displayMediaUploadPreviewScreen(let url):
|
||||||
actionsSubject.send(.displayMediaUploadPreviewScreen(url: url))
|
actionsSubject.send(.displayMediaUploadPreviewScreen(url: url))
|
||||||
case .showActionMenu(let actionMenuInfo):
|
case .showActionMenu(let actionMenuInfo):
|
||||||
Task {
|
self.state.bindings.actionMenuInfo = actionMenuInfo
|
||||||
await self.updatePermissions()
|
|
||||||
self.state.bindings.actionMenuInfo = actionMenuInfo
|
|
||||||
}
|
|
||||||
case .showDebugInfo(let debugInfo):
|
case .showDebugInfo(let debugInfo):
|
||||||
state.bindings.debugInfo = debugInfo
|
state.bindings.debugInfo = debugInfo
|
||||||
case .viewInRoomTimeline(let eventID):
|
case .viewInRoomTimeline(let eventID):
|
||||||
@@ -996,13 +993,6 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension RoomInfoProxy {
|
|
||||||
/// Checks if the other person left the room in a direct chat
|
|
||||||
var isUserAloneInDirectRoom: Bool {
|
|
||||||
isDirect && activeMembersCount == 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Mocks
|
// MARK: - Mocks
|
||||||
|
|
||||||
extension TimelineViewModel {
|
extension TimelineViewModel {
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
|
|
||||||
let timeline: TimelineProxyProtocol
|
let timeline: TimelineProxyProtocol
|
||||||
|
|
||||||
private let infoSubject: CurrentValueSubject<RoomInfoProxy, Never>
|
private let infoSubject: CurrentValueSubject<RoomInfoProxyProtocol, Never>
|
||||||
var infoPublisher: CurrentValuePublisher<RoomInfoProxy, Never> {
|
var infoPublisher: CurrentValuePublisher<RoomInfoProxyProtocol, Never> {
|
||||||
infoSubject.asCurrentValuePublisher()
|
infoSubject.asCurrentValuePublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
|
|
||||||
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: SDKListener { [weak self] roomInfo in
|
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: SDKListener { [weak self] roomInfo in
|
||||||
MXLog.info("Received room info update")
|
MXLog.info("Received room info update")
|
||||||
self?.infoSubject.send(.init(roomInfo: roomInfo))
|
self?.infoSubject.send(RoomInfoProxy(roomInfo: roomInfo))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,20 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
protocol BaseRoomInfoProxyProtocol {
|
struct RoomInfoProxy: RoomInfoProxyProtocol {
|
||||||
var id: String { get }
|
|
||||||
var displayName: String? { get }
|
|
||||||
var avatar: RoomAvatar { get }
|
|
||||||
var topic: String? { get }
|
|
||||||
var canonicalAlias: String? { get }
|
|
||||||
var avatarURL: URL? { get }
|
|
||||||
var activeMembersCount: Int { get }
|
|
||||||
var joinedMembersCount: Int { get }
|
|
||||||
var isDirect: Bool { get }
|
|
||||||
var isSpace: Bool { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RoomInfoProxy: BaseRoomInfoProxyProtocol {
|
|
||||||
let roomInfo: RoomInfo
|
let roomInfo: RoomInfo
|
||||||
|
|
||||||
var id: String { roomInfo.id }
|
var id: String { roomInfo.id }
|
||||||
@@ -31,18 +18,6 @@ struct RoomInfoProxy: BaseRoomInfoProxyProtocol {
|
|||||||
var topic: String? { roomInfo.topic }
|
var topic: String? { roomInfo.topic }
|
||||||
/// The room's avatar URL. Use this for editing and favour ``avatar`` for display.
|
/// The room's avatar URL. Use this for editing and favour ``avatar`` for display.
|
||||||
var avatarURL: URL? { roomInfo.avatarUrl.flatMap(URL.init) }
|
var avatarURL: URL? { roomInfo.avatarUrl.flatMap(URL.init) }
|
||||||
/// The room's avatar info for use in a ``RoomAvatarImage``.
|
|
||||||
var avatar: RoomAvatar {
|
|
||||||
guard successor == nil else {
|
|
||||||
return .tombstoned
|
|
||||||
}
|
|
||||||
|
|
||||||
if isDirect, avatarURL == nil, heroes.count == 1 {
|
|
||||||
return .heroes(heroes.map(UserProfileProxy.init))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .room(id: id, name: displayName, avatarURL: avatarURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we're assuming unknown rooms are unencrypted.
|
// Here we're assuming unknown rooms are unencrypted.
|
||||||
// Fortunately https://github.com/matrix-org/matrix-rust-sdk/pull/4778 makes that very much of an edge case and we
|
// Fortunately https://github.com/matrix-org/matrix-rust-sdk/pull/4778 makes that very much of an edge case and we
|
||||||
@@ -54,28 +29,17 @@ struct RoomInfoProxy: BaseRoomInfoProxyProtocol {
|
|||||||
var isDirect: Bool { roomInfo.isDirect }
|
var isDirect: Bool { roomInfo.isDirect }
|
||||||
var isPublic: Bool { roomInfo.isPublic }
|
var isPublic: Bool { roomInfo.isPublic }
|
||||||
|
|
||||||
// A room might be non public but also not private given the fact that the join rule might be missing or unsupported.
|
|
||||||
var isPrivate: Bool {
|
|
||||||
switch roomInfo.joinRule {
|
|
||||||
case .invite, .knock, .restricted, .knockRestricted:
|
|
||||||
true
|
|
||||||
default:
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isSpace: Bool { roomInfo.isSpace }
|
var isSpace: Bool { roomInfo.isSpace }
|
||||||
var successor: SuccessorRoom? { roomInfo.successorRoom }
|
var successor: SuccessorRoom? { roomInfo.successorRoom }
|
||||||
var isFavourite: Bool { roomInfo.isFavourite }
|
var isFavourite: Bool { roomInfo.isFavourite }
|
||||||
var canonicalAlias: String? { roomInfo.canonicalAlias }
|
var canonicalAlias: String? { roomInfo.canonicalAlias }
|
||||||
var alternativeAliases: [String] { roomInfo.alternativeAliases }
|
var alternativeAliases: [String] { roomInfo.alternativeAliases }
|
||||||
var membership: Membership { roomInfo.membership }
|
var membership: Membership { roomInfo.membership }
|
||||||
var inviter: RoomMemberProxy? { roomInfo.inviter.map(RoomMemberProxy.init) }
|
var inviter: RoomMemberProxyProtocol? { roomInfo.inviter.map(RoomMemberProxy.init) }
|
||||||
var heroes: [RoomHero] { roomInfo.heroes }
|
var heroes: [RoomHero] { roomInfo.heroes }
|
||||||
var activeMembersCount: Int { Int(roomInfo.activeMembersCount) }
|
var activeMembersCount: Int { Int(roomInfo.activeMembersCount) }
|
||||||
var invitedMembersCount: Int { Int(roomInfo.invitedMembersCount) }
|
var invitedMembersCount: Int { Int(roomInfo.invitedMembersCount) }
|
||||||
var joinedMembersCount: Int { Int(roomInfo.joinedMembersCount) }
|
var joinedMembersCount: Int { Int(roomInfo.joinedMembersCount) }
|
||||||
var userPowerLevels: [String: Int] { roomInfo.userPowerLevels.mapValues(Int.init) }
|
|
||||||
var highlightCount: Int { Int(roomInfo.highlightCount) }
|
var highlightCount: Int { Int(roomInfo.highlightCount) }
|
||||||
var notificationCount: Int { Int(roomInfo.notificationCount) }
|
var notificationCount: Int { Int(roomInfo.notificationCount) }
|
||||||
var cachedUserDefinedNotificationMode: RoomNotificationMode? { roomInfo.cachedUserDefinedNotificationMode }
|
var cachedUserDefinedNotificationMode: RoomNotificationMode? { roomInfo.cachedUserDefinedNotificationMode }
|
||||||
@@ -89,41 +53,14 @@ struct RoomInfoProxy: BaseRoomInfoProxyProtocol {
|
|||||||
var joinRule: JoinRule? { roomInfo.joinRule }
|
var joinRule: JoinRule? { roomInfo.joinRule }
|
||||||
var historyVisibility: RoomHistoryVisibility { roomInfo.historyVisibility }
|
var historyVisibility: RoomHistoryVisibility { roomInfo.historyVisibility }
|
||||||
|
|
||||||
/// Find the first alias that matches the given homeserver
|
var powerLevels: RoomPowerLevelsProxyProtocol { RoomPowerLevelsProxy(roomInfo.powerLevels) }
|
||||||
/// - Parameters:
|
|
||||||
/// - serverName: the homserver in question
|
|
||||||
/// - useFallback: whether to return any alias if none match
|
|
||||||
func firstAliasMatching(serverName: String?, useFallback: Bool) -> String? {
|
|
||||||
guard let serverName else { return nil }
|
|
||||||
|
|
||||||
// Check if the canonical alias matches the homeserver
|
|
||||||
if let canonicalAlias = roomInfo.canonicalAlias,
|
|
||||||
canonicalAlias.range(of: serverName) != nil {
|
|
||||||
return canonicalAlias
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise check the alternative aliases and return the first one that matches
|
|
||||||
if let matchingAlternativeAlias = roomInfo.alternativeAliases.filter({ $0.range(of: serverName) != nil }).first {
|
|
||||||
return matchingAlternativeAlias
|
|
||||||
}
|
|
||||||
|
|
||||||
guard useFallback else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Or just return the canonical alias if any
|
|
||||||
if let canonicalAlias = roomInfo.canonicalAlias {
|
|
||||||
return canonicalAlias
|
|
||||||
}
|
|
||||||
|
|
||||||
// And finally return whatever the first alternative alias is
|
|
||||||
return roomInfo.alternativeAliases.first
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
|
struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
|
||||||
let roomPreviewInfo: RoomPreviewInfo
|
let roomPreviewInfo: RoomPreviewInfo
|
||||||
|
|
||||||
|
let successor: SuccessorRoom? = nil
|
||||||
|
|
||||||
var id: String { roomPreviewInfo.roomId }
|
var id: String { roomPreviewInfo.roomId }
|
||||||
var displayName: String? { roomPreviewInfo.name }
|
var displayName: String? { roomPreviewInfo.name }
|
||||||
var heroes: [RoomHero] { roomPreviewInfo.heroes ?? [] }
|
var heroes: [RoomHero] { roomPreviewInfo.heroes ?? [] }
|
||||||
@@ -137,12 +74,4 @@ struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
|
|||||||
|
|
||||||
var joinRule: JoinRule { roomPreviewInfo.joinRule }
|
var joinRule: JoinRule { roomPreviewInfo.joinRule }
|
||||||
var membership: Membership? { roomPreviewInfo.membership }
|
var membership: Membership? { roomPreviewInfo.membership }
|
||||||
|
|
||||||
/// The room's avatar info for use in a ``RoomAvatarImage``.
|
|
||||||
var avatar: RoomAvatar {
|
|
||||||
if isDirect, avatarURL == nil, heroes.count == 1 {
|
|
||||||
return .heroes(heroes.map(UserProfileProxy.init))
|
|
||||||
}
|
|
||||||
return .room(id: id, name: displayName, avatarURL: avatarURL)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2025 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
// Please see LICENSE files in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
protocol BaseRoomInfoProxyProtocol {
|
||||||
|
var id: String { get }
|
||||||
|
var displayName: String? { get }
|
||||||
|
var topic: String? { get }
|
||||||
|
var canonicalAlias: String? { get }
|
||||||
|
var avatarURL: URL? { get }
|
||||||
|
var activeMembersCount: Int { get }
|
||||||
|
var joinedMembersCount: Int { get }
|
||||||
|
var isDirect: Bool { get }
|
||||||
|
var isSpace: Bool { get }
|
||||||
|
|
||||||
|
var successor: SuccessorRoom? { get }
|
||||||
|
var heroes: [RoomHero] { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
protocol RoomInfoProxyProtocol: BaseRoomInfoProxyProtocol {
|
||||||
|
var id: String { get }
|
||||||
|
var creator: String? { get }
|
||||||
|
var displayName: String? { get }
|
||||||
|
var rawName: String? { get }
|
||||||
|
var topic: String? { get }
|
||||||
|
/// The room's avatar URL. Use this for editing and favour ``avatar`` for display.
|
||||||
|
var avatarURL: URL? { get }
|
||||||
|
|
||||||
|
var isEncrypted: Bool { get }
|
||||||
|
var isDirect: Bool { get }
|
||||||
|
var isPublic: Bool { get }
|
||||||
|
|
||||||
|
var isPrivate: Bool { get }
|
||||||
|
|
||||||
|
var isSpace: Bool { get }
|
||||||
|
|
||||||
|
var isFavourite: Bool { get }
|
||||||
|
var canonicalAlias: String? { get }
|
||||||
|
var alternativeAliases: [String] { get }
|
||||||
|
var membership: Membership { get }
|
||||||
|
var inviter: RoomMemberProxyProtocol? { get }
|
||||||
|
|
||||||
|
var activeMembersCount: Int { get }
|
||||||
|
var invitedMembersCount: Int { get }
|
||||||
|
var joinedMembersCount: Int { get }
|
||||||
|
var highlightCount: Int { get }
|
||||||
|
var notificationCount: Int { get }
|
||||||
|
var cachedUserDefinedNotificationMode: RoomNotificationMode? { get }
|
||||||
|
var hasRoomCall: Bool { get }
|
||||||
|
var activeRoomCallParticipants: [String] { get }
|
||||||
|
var isMarkedUnread: Bool { get }
|
||||||
|
var unreadMessagesCount: UInt { get }
|
||||||
|
var unreadNotificationsCount: UInt { get }
|
||||||
|
var unreadMentionsCount: UInt { get }
|
||||||
|
var pinnedEventIDs: Set<String> { get }
|
||||||
|
var joinRule: JoinRule? { get }
|
||||||
|
var historyVisibility: RoomHistoryVisibility { get }
|
||||||
|
|
||||||
|
var powerLevels: RoomPowerLevelsProxyProtocol { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BaseRoomInfoProxyProtocol {
|
||||||
|
/// The room's avatar info for use in a ``RoomAvatarImage``.
|
||||||
|
var avatar: RoomAvatar {
|
||||||
|
guard successor == nil else {
|
||||||
|
return .tombstoned
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDirect, avatarURL == nil, heroes.count == 1 {
|
||||||
|
return .heroes(heroes.map(UserProfileProxy.init))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .room(id: id, name: displayName, avatarURL: avatarURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RoomInfoProxyProtocol {
|
||||||
|
/// A room might be non public but also not private given the fact that the join rule might be missing or unsupported.
|
||||||
|
var isPrivate: Bool {
|
||||||
|
switch joinRule {
|
||||||
|
case .invite, .knock, .restricted, .knockRestricted:
|
||||||
|
true
|
||||||
|
default:
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the other person left the room in a direct chat
|
||||||
|
var isUserAloneInDirectRoom: Bool {
|
||||||
|
isDirect && activeMembersCount == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the first alias that matches the given homeserver
|
||||||
|
/// - Parameters:
|
||||||
|
/// - serverName: the homserver in question
|
||||||
|
/// - useFallback: whether to return any alias if none match
|
||||||
|
func firstAliasMatching(serverName: String?, useFallback: Bool) -> String? {
|
||||||
|
guard let serverName else { return nil }
|
||||||
|
|
||||||
|
// Check if the canonical alias matches the homeserver
|
||||||
|
if let canonicalAlias,
|
||||||
|
canonicalAlias.range(of: serverName) != nil {
|
||||||
|
return canonicalAlias
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise check the alternative aliases and return the first one that matches
|
||||||
|
if let matchingAlternativeAlias = alternativeAliases.filter({ $0.range(of: serverName) != nil }).first {
|
||||||
|
return matchingAlternativeAlias
|
||||||
|
}
|
||||||
|
|
||||||
|
guard useFallback else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or just return the canonical alias if any
|
||||||
|
if let canonicalAlias {
|
||||||
|
return canonicalAlias
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally return whatever the first alternative alias is
|
||||||
|
return alternativeAliases.first
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,17 @@ import MatrixRustSDK
|
|||||||
protocol RoomPowerLevelsProxyProtocol {
|
protocol RoomPowerLevelsProxyProtocol {
|
||||||
var values: RoomPowerLevelsValues { get }
|
var values: RoomPowerLevelsValues { get }
|
||||||
|
|
||||||
|
func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool
|
||||||
|
func canOwnUser(sendStateEvent event: StateEventType) -> Bool
|
||||||
|
func canOwnUserInvite() -> Bool
|
||||||
|
func canOwnUserRedactOther() -> Bool
|
||||||
|
func canOwnUserRedactOwn() -> Bool
|
||||||
|
func canOwnUserKick() -> Bool
|
||||||
|
func canOwnUserBan() -> Bool
|
||||||
|
func canOwnUserTriggerRoomNotification() -> Bool
|
||||||
|
func canOwnUserPinOrUnpin() -> Bool
|
||||||
|
func canOwnUserJoinCall() -> Bool
|
||||||
|
|
||||||
func canUser(userID: String, sendMessage messageType: MessageLikeEventType) -> Result<Bool, RoomProxyError>
|
func canUser(userID: String, sendMessage messageType: MessageLikeEventType) -> Result<Bool, RoomProxyError>
|
||||||
func canUser(userID: String, sendStateEvent event: StateEventType) -> Result<Bool, RoomProxyError>
|
func canUser(userID: String, sendStateEvent event: StateEventType) -> Result<Bool, RoomProxyError>
|
||||||
func canUserInvite(userID: String) -> Result<Bool, RoomProxyError>
|
func canUserInvite(userID: String) -> Result<Bool, RoomProxyError>
|
||||||
|
|||||||
@@ -18,6 +18,46 @@ struct RoomPowerLevelsProxy: RoomPowerLevelsProxyProtocol {
|
|||||||
powerLevels.values()
|
powerLevels.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool {
|
||||||
|
powerLevels.canOwnUserSendMessage(message: messageType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUser(sendStateEvent stateEventType: StateEventType) -> Bool {
|
||||||
|
powerLevels.canOwnUserSendState(stateEvent: stateEventType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserInvite() -> Bool {
|
||||||
|
powerLevels.canOwnUserInvite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserRedactOther() -> Bool {
|
||||||
|
powerLevels.canOwnUserRedactOther()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserRedactOwn() -> Bool {
|
||||||
|
powerLevels.canOwnUserRedactOwn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserKick() -> Bool {
|
||||||
|
powerLevels.canOwnUserKick()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserBan() -> Bool {
|
||||||
|
powerLevels.canOwnUserBan()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserTriggerRoomNotification() -> Bool {
|
||||||
|
powerLevels.canOwnUserTriggerRoomNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserPinOrUnpin() -> Bool {
|
||||||
|
powerLevels.canOwnUserPinUnpin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOwnUserJoinCall() -> Bool {
|
||||||
|
powerLevels.canOwnUserSendState(stateEvent: .callMember)
|
||||||
|
}
|
||||||
|
|
||||||
func canUser(userID: String, sendMessage messageType: MessageLikeEventType) -> Result<Bool, RoomProxyError> {
|
func canUser(userID: String, sendMessage messageType: MessageLikeEventType) -> Result<Bool, RoomProxyError> {
|
||||||
do {
|
do {
|
||||||
return try .success(powerLevels.canUserSendMessage(userId: userID, message: messageType))
|
return try .success(powerLevels.canUserSendMessage(userId: userID, message: messageType))
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ enum KnockRequestsState {
|
|||||||
|
|
||||||
// sourcery: AutoMockable
|
// sourcery: AutoMockable
|
||||||
protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||||
var infoPublisher: CurrentValuePublisher<RoomInfoProxy, Never> { get }
|
var infoPublisher: CurrentValuePublisher<RoomInfoProxyProtocol, Never> { get }
|
||||||
|
|
||||||
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get }
|
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get }
|
||||||
|
|
||||||
|
|||||||
@@ -355,14 +355,24 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testCanEditAvatar() async {
|
func testCanEditAvatar() async {
|
||||||
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
|
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
|
||||||
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
|
|
||||||
|
|
||||||
let powerLevelsMock = RoomPowerLevelsProxyMock(configuration: .init())
|
let configuration = JoinedRoomProxyMockConfiguration(name: "Test",
|
||||||
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsMock)
|
isDirect: false,
|
||||||
|
isPublic: false,
|
||||||
|
members: mockedMembers)
|
||||||
|
|
||||||
powerLevelsMock.canUserUserIDSendStateEventClosure = { _, event in
|
roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
|
|
||||||
|
let powerLevelsProxyMock = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
|
powerLevelsProxyMock.canUserUserIDSendStateEventClosure = { _, event in
|
||||||
.success(event == .roomAvatar)
|
.success(event == .roomAvatar)
|
||||||
}
|
}
|
||||||
|
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsProxyMock)
|
||||||
|
|
||||||
|
let roomInfoProxyMock = RoomInfoProxyMock(configuration)
|
||||||
|
roomInfoProxyMock.powerLevels = powerLevelsProxyMock
|
||||||
|
roomProxyMock.infoPublisher = CurrentValueSubject(roomInfoProxyMock).asCurrentValuePublisher()
|
||||||
|
|
||||||
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
|
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
|
||||||
clientProxy: ClientProxyMock(.init()),
|
clientProxy: ClientProxyMock(.init()),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
@@ -383,14 +393,24 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testCanEditName() async {
|
func testCanEditName() async {
|
||||||
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
|
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
|
||||||
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
|
|
||||||
|
|
||||||
let powerLevelsMock = RoomPowerLevelsProxyMock(configuration: .init())
|
let configuration = JoinedRoomProxyMockConfiguration(name: "Test",
|
||||||
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsMock)
|
isDirect: false,
|
||||||
|
isPublic: false,
|
||||||
|
members: mockedMembers)
|
||||||
|
|
||||||
powerLevelsMock.canUserUserIDSendStateEventClosure = { _, event in
|
roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
|
|
||||||
|
let powerLevelsProxyMock = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
|
powerLevelsProxyMock.canUserUserIDSendStateEventClosure = { _, event in
|
||||||
.success(event == .roomName)
|
.success(event == .roomName)
|
||||||
}
|
}
|
||||||
|
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsProxyMock)
|
||||||
|
|
||||||
|
let roomInfoProxyMock = RoomInfoProxyMock(configuration)
|
||||||
|
roomInfoProxyMock.powerLevels = powerLevelsProxyMock
|
||||||
|
roomProxyMock.infoPublisher = CurrentValueSubject(roomInfoProxyMock).asCurrentValuePublisher()
|
||||||
|
|
||||||
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
|
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
|
||||||
clientProxy: ClientProxyMock(.init()),
|
clientProxy: ClientProxyMock(.init()),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
@@ -411,14 +431,24 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testCanEditTopic() async {
|
func testCanEditTopic() async {
|
||||||
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
|
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
|
||||||
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
|
|
||||||
|
|
||||||
let powerLevelsMock = RoomPowerLevelsProxyMock(configuration: .init())
|
let configuration = JoinedRoomProxyMockConfiguration(name: "Test",
|
||||||
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsMock)
|
isDirect: false,
|
||||||
|
isPublic: false,
|
||||||
|
members: mockedMembers)
|
||||||
|
|
||||||
powerLevelsMock.canUserUserIDSendStateEventClosure = { _, event in
|
roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
|
|
||||||
|
let powerLevelsProxyMock = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
|
powerLevelsProxyMock.canUserUserIDSendStateEventClosure = { _, event in
|
||||||
.success(event == .roomTopic)
|
.success(event == .roomTopic)
|
||||||
}
|
}
|
||||||
|
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsProxyMock)
|
||||||
|
|
||||||
|
let roomInfoProxyMock = RoomInfoProxyMock(configuration)
|
||||||
|
roomInfoProxyMock.powerLevels = powerLevelsProxyMock
|
||||||
|
roomProxyMock.infoPublisher = CurrentValueSubject(roomInfoProxyMock).asCurrentValuePublisher()
|
||||||
|
|
||||||
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
|
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
|
||||||
clientProxy: ClientProxyMock(.init()),
|
clientProxy: ClientProxyMock(.init()),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
|||||||
var configuration = JoinedRoomProxyMockConfiguration()
|
var configuration = JoinedRoomProxyMockConfiguration()
|
||||||
let roomProxy = JoinedRoomProxyMock(configuration)
|
let roomProxy = JoinedRoomProxyMock(configuration)
|
||||||
|
|
||||||
let roomInfoSubject = CurrentValueSubject<RoomInfoProxy, Never>(.init(roomInfo: .init(configuration)))
|
let roomInfoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(RoomInfoProxyMock(configuration))
|
||||||
roomProxy.infoPublisher = roomInfoSubject.asCurrentValuePublisher()
|
roomProxy.infoPublisher = roomInfoSubject.asCurrentValuePublisher()
|
||||||
|
|
||||||
clientProxy.roomForIdentifierClosure = { _ in
|
clientProxy.roomForIdentifierClosure = { _ in
|
||||||
@@ -288,7 +288,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configuration.membership = .left
|
configuration.membership = .left
|
||||||
roomInfoSubject.send(.init(roomInfo: .init(configuration)))
|
roomInfoSubject.send(RoomInfoProxyMock(configuration))
|
||||||
|
|
||||||
try await fulfillment.fulfill()
|
try await fulfillment.fulfill()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
func testPinnedEventsBanner() async throws {
|
func testPinnedEventsBanner() async throws {
|
||||||
var configuration = JoinedRoomProxyMockConfiguration()
|
var configuration = JoinedRoomProxyMockConfiguration()
|
||||||
let timelineSubject = PassthroughSubject<TimelineProxyProtocol, Never>()
|
let timelineSubject = PassthroughSubject<TimelineProxyProtocol, Never>()
|
||||||
let infoSubject = CurrentValueSubject<RoomInfoProxy, Never>(.init(roomInfo: RoomInfo(configuration)))
|
let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(RoomInfoProxyMock(configuration))
|
||||||
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
// setup a way to inject the mock of the pinned events timeline
|
// setup a way to inject the mock of the pinned events timeline
|
||||||
roomProxyMock.pinnedEventsTimelineClosure = {
|
roomProxyMock.pinnedEventsTimelineClosure = {
|
||||||
@@ -63,7 +63,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
viewState.pinnedEventsBannerState.count == 2
|
viewState.pinnedEventsBannerState.count == 2
|
||||||
}
|
}
|
||||||
configuration.pinnedEventIDs = ["test1", "test2"]
|
configuration.pinnedEventIDs = ["test1", "test2"]
|
||||||
infoSubject.send(.init(roomInfo: RoomInfo(configuration)))
|
infoSubject.send(RoomInfoProxyMock(configuration))
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
XCTAssertTrue(viewModel.context.viewState.pinnedEventsBannerState.isLoading)
|
XCTAssertTrue(viewModel.context.viewState.pinnedEventsBannerState.isLoading)
|
||||||
XCTAssertTrue(viewModel.context.viewState.shouldShowPinnedEventsBanner)
|
XCTAssertTrue(viewModel.context.viewState.shouldShowPinnedEventsBanner)
|
||||||
@@ -165,7 +165,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testRoomInfoUpdate() async throws {
|
func testRoomInfoUpdate() async throws {
|
||||||
var configuration = JoinedRoomProxyMockConfiguration(id: "TestID", name: "StartingName", avatarURL: nil, hasOngoingCall: false)
|
var configuration = JoinedRoomProxyMockConfiguration(id: "TestID", name: "StartingName", avatarURL: nil, hasOngoingCall: false)
|
||||||
let infoSubject = CurrentValueSubject<RoomInfoProxy, Never>(.init(roomInfo: RoomInfo(configuration)))
|
let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(RoomInfoProxyMock(configuration))
|
||||||
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
|
|
||||||
let powerLevelsMock = RoomPowerLevelsProxyMock(configuration: .init())
|
let powerLevelsMock = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
@@ -206,7 +206,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
viewState.hasOngoingCall
|
viewState.hasOngoingCall
|
||||||
}
|
}
|
||||||
|
|
||||||
infoSubject.send(.init(roomInfo: RoomInfo(configuration)))
|
infoSubject.send(RoomInfoProxyMock(configuration))
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
var configuration = JoinedRoomProxyMockConfiguration(name: "",
|
var configuration = JoinedRoomProxyMockConfiguration(name: "",
|
||||||
pinnedEventIDs: .init(["test1"]))
|
pinnedEventIDs: .init(["test1"]))
|
||||||
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
let infoSubject = CurrentValueSubject<RoomInfoProxy, Never>(.init(roomInfo: RoomInfo(configuration)))
|
let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(RoomInfoProxyMock(configuration))
|
||||||
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
|
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
|
||||||
|
|
||||||
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
||||||
@@ -489,7 +489,7 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
value.pinnedEventIDs == ["test1", "test2"]
|
value.pinnedEventIDs == ["test1", "test2"]
|
||||||
}
|
}
|
||||||
infoSubject.send(.init(roomInfo: RoomInfo(configuration)))
|
infoSubject.send(RoomInfoProxyMock(configuration))
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,7 +497,7 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
let configuration = JoinedRoomProxyMockConfiguration(name: "",
|
let configuration = JoinedRoomProxyMockConfiguration(name: "",
|
||||||
powerLevelsConfiguration: .init(canUserPin: true))
|
powerLevelsConfiguration: .init(canUserPin: true))
|
||||||
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
let roomProxyMock = JoinedRoomProxyMock(configuration)
|
||||||
let infoSubject = CurrentValueSubject<RoomInfoProxy, Never>(.init(roomInfo: RoomInfo(configuration)))
|
let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(RoomInfoProxyMock(configuration))
|
||||||
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
|
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
|
||||||
|
|
||||||
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
||||||
@@ -518,14 +518,17 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
let powerLevelsMock = RoomPowerLevelsProxyMock(configuration: .init())
|
let powerLevelsProxyMock = RoomPowerLevelsProxyMock(configuration: .init())
|
||||||
powerLevelsMock.canUserPinOrUnpinUserIDReturnValue = .success(false)
|
powerLevelsProxyMock.canUserPinOrUnpinUserIDReturnValue = .success(false)
|
||||||
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsMock)
|
roomProxyMock.powerLevelsReturnValue = .success(powerLevelsProxyMock)
|
||||||
|
|
||||||
|
let roomInfoProxyMock = RoomInfoProxyMock(configuration)
|
||||||
|
roomInfoProxyMock.powerLevels = powerLevelsProxyMock
|
||||||
|
|
||||||
deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
!value.canCurrentUserPin
|
!value.canCurrentUserPin
|
||||||
}
|
}
|
||||||
infoSubject.send(.init(roomInfo: RoomInfo(configuration)))
|
infoSubject.send(roomInfoProxyMock)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ packages:
|
|||||||
# Element/Matrix dependencies
|
# Element/Matrix dependencies
|
||||||
MatrixRustSDK:
|
MatrixRustSDK:
|
||||||
url: https://github.com/element-hq/matrix-rust-components-swift
|
url: https://github.com/element-hq/matrix-rust-components-swift
|
||||||
exactVersion: 25.06.20
|
exactVersion: 25.06.23
|
||||||
# path: ../matrix-rust-sdk
|
# path: ../matrix-rust-sdk
|
||||||
Compound:
|
Compound:
|
||||||
url: https://github.com/element-hq/compound-ios
|
url: https://github.com/element-hq/compound-ios
|
||||||
|
|||||||
Reference in New Issue
Block a user