mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Fixes element-hq/element-meta/issues/2525 - Display a warning when a user's pinned identity changes
This commit is contained in:
parent
70652ed7be
commit
794d0eead1
@ -1035,6 +1035,7 @@
|
||||
E82E13CC3EB923CCB8F8273C /* TimelineProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E543072DE58E751F028998 /* TimelineProxy.swift */; };
|
||||
E84ADFE9696936C18C2424B5 /* SecureBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A00BB9CD12CF6AC98D5485 /* SecureBackupScreen.swift */; };
|
||||
E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */; };
|
||||
E8C65C19F7C40EE545172DD6 /* RoomScreenFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */; };
|
||||
E9347F56CF0683208F4D9249 /* RoomNotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81A9B5225D0881CEFA2CF7C9 /* RoomNotificationSettingsScreenViewModel.swift */; };
|
||||
E9560744F7B0292E20ECE5F2 /* RoomDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63E8A1E8EE094F570573B6E8 /* RoomDetailsScreenViewModelProtocol.swift */; };
|
||||
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */; };
|
||||
@ -1488,6 +1489,7 @@
|
||||
406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallServiceConstants.swift; sourceTree = "<group>"; };
|
||||
40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionSuggestionItemView.swift; sourceTree = "<group>"; };
|
||||
4137900E28201C314C835C11 /* RoomScreenFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenFooterView.swift; sourceTree = "<group>"; };
|
||||
4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreen.swift; sourceTree = "<group>"; };
|
||||
419957D7B1C983D7B3B93678 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
41BB37D96C3EA18F3CE8675D /* RoomDirectorySearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenModels.swift; sourceTree = "<group>"; };
|
||||
@ -4018,6 +4020,7 @@
|
||||
children = (
|
||||
422724361B6555364C43281E /* RoomHeaderView.swift */,
|
||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */,
|
||||
4137900E28201C314C835C11 /* RoomScreenFooterView.swift */,
|
||||
4552D3466B1453F287223ADA /* SwipeRightAction.swift */,
|
||||
464C6BFAA853DC755B9C1F60 /* PinnedItemsBanner */,
|
||||
);
|
||||
@ -6788,6 +6791,7 @@
|
||||
F8F47CE757EE656905F01F2C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift in Sources */,
|
||||
C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */,
|
||||
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */,
|
||||
E8C65C19F7C40EE545172DD6 /* RoomScreenFooterView.swift in Sources */,
|
||||
352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */,
|
||||
7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */,
|
||||
617624A97BDBB75ED3DD8156 /* RoomScreenViewModelProtocol.swift in Sources */,
|
||||
@ -7785,7 +7789,7 @@
|
||||
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 1.0.53;
|
||||
version = 1.0.55;
|
||||
};
|
||||
};
|
||||
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
||||
|
@ -149,8 +149,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
||||
"state" : {
|
||||
"revision" : "83abbdc8485340c20f27148153ff62f690ca210b",
|
||||
"version" : "1.0.53"
|
||||
"revision" : "8ee63edc76bccd12c17a22eaf4eddae69e5f1303",
|
||||
"version" : "1.0.55"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -124,6 +124,8 @@ final class AppSettings {
|
||||
let encryptionURL: URL = "https://element.io/help#encryption"
|
||||
/// A URL where users can go read more about the chat backup.
|
||||
let chatBackupDetailsURL: URL = "https://element.io/help#encryption5"
|
||||
/// A URL where users can go read more about identity pinning violations
|
||||
let identityPinningViolationDetailsURL: URL = "https://element.io/help#18"
|
||||
/// Any domains that Element web may be hosted on - used for handling links.
|
||||
let elementWebHosts = ["app.element.io", "staging.element.io", "develop.element.io"]
|
||||
|
||||
|
@ -590,7 +590,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
let composerDraftService = ComposerDraftService(roomProxy: roomProxy, timelineItemfactory: timelineItemFactory)
|
||||
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy,
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: userSession.clientProxy,
|
||||
roomProxy: roomProxy,
|
||||
focussedEvent: focussedEvent,
|
||||
timelineController: timelineController,
|
||||
mediaProvider: userSession.mediaProvider,
|
||||
|
@ -4426,6 +4426,76 @@ class ClientProxyMock: ClientProxyProtocol {
|
||||
return curve25519Base64ReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - pinUserIdentity
|
||||
|
||||
var pinUserIdentityUnderlyingCallsCount = 0
|
||||
var pinUserIdentityCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return pinUserIdentityUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = pinUserIdentityUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
pinUserIdentityUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
pinUserIdentityUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var pinUserIdentityCalled: Bool {
|
||||
return pinUserIdentityCallsCount > 0
|
||||
}
|
||||
var pinUserIdentityReceivedUserID: String?
|
||||
var pinUserIdentityReceivedInvocations: [String] = []
|
||||
|
||||
var pinUserIdentityUnderlyingReturnValue: Result<Void, ClientProxyError>!
|
||||
var pinUserIdentityReturnValue: Result<Void, ClientProxyError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return pinUserIdentityUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<Void, ClientProxyError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = pinUserIdentityUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
pinUserIdentityUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
pinUserIdentityUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var pinUserIdentityClosure: ((String) async -> Result<Void, ClientProxyError>)?
|
||||
|
||||
func pinUserIdentity(_ userID: String) async -> Result<Void, ClientProxyError> {
|
||||
pinUserIdentityCallsCount += 1
|
||||
pinUserIdentityReceivedUserID = userID
|
||||
DispatchQueue.main.async {
|
||||
self.pinUserIdentityReceivedInvocations.append(userID)
|
||||
}
|
||||
if let pinUserIdentityClosure = pinUserIdentityClosure {
|
||||
return await pinUserIdentityClosure(userID)
|
||||
} else {
|
||||
return pinUserIdentityReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - resetIdentity
|
||||
|
||||
var resetIdentityUnderlyingCallsCount = 0
|
||||
@ -5791,6 +5861,11 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol {
|
||||
set(value) { underlyingTypingMembersPublisher = value }
|
||||
}
|
||||
var underlyingTypingMembersPublisher: CurrentValuePublisher<[String], Never>!
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> {
|
||||
get { return underlyingIdentityStatusChangesPublisher }
|
||||
set(value) { underlyingIdentityStatusChangesPublisher = value }
|
||||
}
|
||||
var underlyingIdentityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never>!
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> {
|
||||
get { return underlyingActionsPublisher }
|
||||
set(value) { underlyingActionsPublisher = value }
|
||||
@ -12495,6 +12570,7 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol {
|
||||
}
|
||||
var underlyingUserID: String!
|
||||
var displayName: String?
|
||||
var disambiguatedDisplayName: String?
|
||||
var avatarURL: URL?
|
||||
var membership: MembershipState {
|
||||
get { return underlyingMembership }
|
||||
|
@ -625,6 +625,52 @@ open class ClientSDKMock: MatrixRustSDK.Client {
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - customLoginWithJwt
|
||||
|
||||
open var customLoginWithJwtJwtInitialDeviceNameDeviceIdThrowableError: Error?
|
||||
var customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount = 0
|
||||
open var customLoginWithJwtJwtInitialDeviceNameDeviceIdCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var customLoginWithJwtJwtInitialDeviceNameDeviceIdCalled: Bool {
|
||||
return customLoginWithJwtJwtInitialDeviceNameDeviceIdCallsCount > 0
|
||||
}
|
||||
open var customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedArguments: (jwt: String, initialDeviceName: String?, deviceId: String?)?
|
||||
open var customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedInvocations: [(jwt: String, initialDeviceName: String?, deviceId: String?)] = []
|
||||
open var customLoginWithJwtJwtInitialDeviceNameDeviceIdClosure: ((String, String?, String?) async throws -> Void)?
|
||||
|
||||
open override func customLoginWithJwt(jwt: String, initialDeviceName: String?, deviceId: String?) async throws {
|
||||
if let error = customLoginWithJwtJwtInitialDeviceNameDeviceIdThrowableError {
|
||||
throw error
|
||||
}
|
||||
customLoginWithJwtJwtInitialDeviceNameDeviceIdCallsCount += 1
|
||||
customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedArguments = (jwt: jwt, initialDeviceName: initialDeviceName, deviceId: deviceId)
|
||||
DispatchQueue.main.async {
|
||||
self.customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedInvocations.append((jwt: jwt, initialDeviceName: initialDeviceName, deviceId: deviceId))
|
||||
}
|
||||
try await customLoginWithJwtJwtInitialDeviceNameDeviceIdClosure?(jwt, initialDeviceName, deviceId)
|
||||
}
|
||||
|
||||
//MARK: - deactivateAccount
|
||||
|
||||
open var deactivateAccountAuthDataEraseDataThrowableError: Error?
|
||||
@ -4953,6 +4999,77 @@ open class ClientBuilderSDKMock: MatrixRustSDK.ClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - roomDecryptionTrustRequirement
|
||||
|
||||
var roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount = 0
|
||||
open var roomDecryptionTrustRequirementTrustRequirementCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var roomDecryptionTrustRequirementTrustRequirementCalled: Bool {
|
||||
return roomDecryptionTrustRequirementTrustRequirementCallsCount > 0
|
||||
}
|
||||
open var roomDecryptionTrustRequirementTrustRequirementReceivedTrustRequirement: TrustRequirement?
|
||||
open var roomDecryptionTrustRequirementTrustRequirementReceivedInvocations: [TrustRequirement] = []
|
||||
|
||||
var roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue: ClientBuilder!
|
||||
open var roomDecryptionTrustRequirementTrustRequirementReturnValue: ClientBuilder! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: ClientBuilder? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var roomDecryptionTrustRequirementTrustRequirementClosure: ((TrustRequirement) -> ClientBuilder)?
|
||||
|
||||
open override func roomDecryptionTrustRequirement(trustRequirement: TrustRequirement) -> ClientBuilder {
|
||||
roomDecryptionTrustRequirementTrustRequirementCallsCount += 1
|
||||
roomDecryptionTrustRequirementTrustRequirementReceivedTrustRequirement = trustRequirement
|
||||
DispatchQueue.main.async {
|
||||
self.roomDecryptionTrustRequirementTrustRequirementReceivedInvocations.append(trustRequirement)
|
||||
}
|
||||
if let roomDecryptionTrustRequirementTrustRequirementClosure = roomDecryptionTrustRequirementTrustRequirementClosure {
|
||||
return roomDecryptionTrustRequirementTrustRequirementClosure(trustRequirement)
|
||||
} else {
|
||||
return roomDecryptionTrustRequirementTrustRequirementReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - roomKeyRecipientStrategy
|
||||
|
||||
var roomKeyRecipientStrategyStrategyUnderlyingCallsCount = 0
|
||||
@ -12296,6 +12413,34 @@ open class RoomSDKMock: MatrixRustSDK.Room {
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - pinUserIdentity
|
||||
|
||||
open var pinUserIdentityUserIdThrowableError: Error?
|
||||
var pinUserIdentityUserIdUnderlyingCallsCount = 0
|
||||
open var pinUserIdentityUserIdCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return pinUserIdentityUserIdUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = pinUserIdentityUserIdUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
pinUserIdentityUserIdUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
pinUserIdentityUserIdUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - pinnedEventsTimeline
|
||||
|
||||
open var pinnedEventsTimelineInternalIdPrefixMaxEventsToLoadMaxConcurrentRequestsThrowableError: Error?
|
||||
@ -13068,6 +13213,77 @@ open class RoomSDKMock: MatrixRustSDK.Room {
|
||||
try await setUnreadFlagNewValueClosure?(newValue)
|
||||
}
|
||||
|
||||
//MARK: - subscribeToIdentityStatusChanges
|
||||
|
||||
var subscribeToIdentityStatusChangesListenerUnderlyingCallsCount = 0
|
||||
open var subscribeToIdentityStatusChangesListenerCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToIdentityStatusChangesListenerUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToIdentityStatusChangesListenerUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToIdentityStatusChangesListenerUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToIdentityStatusChangesListenerUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var subscribeToIdentityStatusChangesListenerCalled: Bool {
|
||||
return subscribeToIdentityStatusChangesListenerCallsCount > 0
|
||||
}
|
||||
open var subscribeToIdentityStatusChangesListenerReceivedListener: IdentityStatusChangeListener?
|
||||
open var subscribeToIdentityStatusChangesListenerReceivedInvocations: [IdentityStatusChangeListener] = []
|
||||
|
||||
var subscribeToIdentityStatusChangesListenerUnderlyingReturnValue: TaskHandle!
|
||||
open var subscribeToIdentityStatusChangesListenerReturnValue: TaskHandle! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToIdentityStatusChangesListenerUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: TaskHandle? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToIdentityStatusChangesListenerUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToIdentityStatusChangesListenerUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToIdentityStatusChangesListenerUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var subscribeToIdentityStatusChangesListenerClosure: ((IdentityStatusChangeListener) -> TaskHandle)?
|
||||
|
||||
open override func subscribeToIdentityStatusChanges(listener: IdentityStatusChangeListener) -> TaskHandle {
|
||||
subscribeToIdentityStatusChangesListenerCallsCount += 1
|
||||
subscribeToIdentityStatusChangesListenerReceivedListener = listener
|
||||
DispatchQueue.main.async {
|
||||
self.subscribeToIdentityStatusChangesListenerReceivedInvocations.append(listener)
|
||||
}
|
||||
if let subscribeToIdentityStatusChangesListenerClosure = subscribeToIdentityStatusChangesListenerClosure {
|
||||
return subscribeToIdentityStatusChangesListenerClosure(listener)
|
||||
} else {
|
||||
return subscribeToIdentityStatusChangesListenerReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - subscribeToRoomInfoUpdates
|
||||
|
||||
var subscribeToRoomInfoUpdatesListenerUnderlyingCallsCount = 0
|
||||
@ -14087,77 +14303,6 @@ open class RoomListSDKMock: MatrixRustSDK.RoomList {
|
||||
|
||||
fileprivate var pointer: UnsafeMutableRawPointer!
|
||||
|
||||
//MARK: - entries
|
||||
|
||||
var entriesListenerUnderlyingCallsCount = 0
|
||||
open var entriesListenerCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return entriesListenerUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = entriesListenerUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
entriesListenerUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
entriesListenerUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var entriesListenerCalled: Bool {
|
||||
return entriesListenerCallsCount > 0
|
||||
}
|
||||
open var entriesListenerReceivedListener: RoomListEntriesListener?
|
||||
open var entriesListenerReceivedInvocations: [RoomListEntriesListener] = []
|
||||
|
||||
var entriesListenerUnderlyingReturnValue: TaskHandle!
|
||||
open var entriesListenerReturnValue: TaskHandle! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return entriesListenerUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: TaskHandle? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = entriesListenerUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
entriesListenerUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
entriesListenerUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var entriesListenerClosure: ((RoomListEntriesListener) -> TaskHandle)?
|
||||
|
||||
open override func entries(listener: RoomListEntriesListener) -> TaskHandle {
|
||||
entriesListenerCallsCount += 1
|
||||
entriesListenerReceivedListener = listener
|
||||
DispatchQueue.main.async {
|
||||
self.entriesListenerReceivedInvocations.append(listener)
|
||||
}
|
||||
if let entriesListenerClosure = entriesListenerClosure {
|
||||
return entriesListenerClosure(listener)
|
||||
} else {
|
||||
return entriesListenerReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - entriesWithDynamicAdapters
|
||||
|
||||
var entriesWithDynamicAdaptersPageSizeListenerUnderlyingCallsCount = 0
|
||||
|
@ -80,6 +80,7 @@ extension JoinedRoomProxyMock {
|
||||
|
||||
membersPublisher = CurrentValueSubject(configuration.members).asCurrentValuePublisher()
|
||||
typingMembersPublisher = CurrentValueSubject([]).asCurrentValuePublisher()
|
||||
identityStatusChangesPublisher = CurrentValueSubject([]).asCurrentValuePublisher()
|
||||
|
||||
joinedMembersCount = configuration.members.filter { $0.membership == .join }.count
|
||||
activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count
|
||||
|
@ -25,6 +25,11 @@ extension RoomMemberProxyMock {
|
||||
self.init()
|
||||
userID = configuration.userID
|
||||
displayName = configuration.displayName
|
||||
|
||||
if let displayName = configuration.displayName {
|
||||
disambiguatedDisplayName = "\(displayName) (\(userID))"
|
||||
}
|
||||
|
||||
avatarURL = configuration.avatarURL
|
||||
|
||||
membership = configuration.membership
|
||||
|
@ -12,6 +12,7 @@ import SwiftUI
|
||||
import WysiwygComposer
|
||||
|
||||
struct RoomScreenCoordinatorParameters {
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let roomProxy: JoinedRoomProxyProtocol
|
||||
var focussedEvent: FocusEvent?
|
||||
let timelineController: RoomTimelineControllerProtocol
|
||||
@ -61,13 +62,15 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
selectedPinnedEventID = focussedEvent.shouldSetPin ? focussedEvent.eventID : nil
|
||||
}
|
||||
|
||||
roomViewModel = RoomScreenViewModel(roomProxy: parameters.roomProxy,
|
||||
roomViewModel = RoomScreenViewModel(clientProxy: parameters.clientProxy,
|
||||
roomProxy: parameters.roomProxy,
|
||||
initialSelectedPinnedEventID: selectedPinnedEventID,
|
||||
mediaProvider: parameters.mediaProvider,
|
||||
ongoingCallRoomIDPublisher: parameters.ongoingCallRoomIDPublisher,
|
||||
appMediator: parameters.appMediator,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
|
||||
timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy,
|
||||
focussedEventID: parameters.focussedEvent?.eventID,
|
||||
@ -149,10 +152,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
.store(in: &cancellables)
|
||||
|
||||
roomViewModel.actions
|
||||
.sink { [weak self] actions in
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch actions {
|
||||
switch action {
|
||||
case .focusEvent(eventID: let eventID):
|
||||
focusOnEvent(FocusEvent(eventID: eventID, shouldSetPin: false))
|
||||
case .displayPinnedEventsTimeline:
|
||||
|
@ -21,6 +21,7 @@ enum RoomScreenViewAction {
|
||||
case viewAllPins
|
||||
case displayRoomDetails
|
||||
case displayCall
|
||||
case footerViewAction(RoomScreenFooterViewAction)
|
||||
}
|
||||
|
||||
struct RoomScreenViewState: BindableState {
|
||||
@ -38,11 +39,21 @@ struct RoomScreenViewState: BindableState {
|
||||
var hasOngoingCall: Bool
|
||||
var shouldShowCallButton = true
|
||||
|
||||
var footerDetails: RoomScreenFooterViewDetails?
|
||||
|
||||
var bindings: RoomScreenViewStateBindings
|
||||
}
|
||||
|
||||
struct RoomScreenViewStateBindings { }
|
||||
|
||||
enum RoomScreenFooterViewAction {
|
||||
case resolvePinViolation(userID: String)
|
||||
}
|
||||
|
||||
enum RoomScreenFooterViewDetails {
|
||||
case pinViolation(member: RoomMemberProxyProtocol, learnMoreURL: URL)
|
||||
}
|
||||
|
||||
enum PinnedEventsBannerState: Equatable {
|
||||
case loading(numbersOfEvents: Int)
|
||||
case loaded(state: PinnedEventsState)
|
||||
|
@ -7,18 +7,24 @@
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
import OrderedCollections
|
||||
import SwiftUI
|
||||
|
||||
typealias RoomScreenViewModelType = StateStoreViewModel<RoomScreenViewState, RoomScreenViewAction>
|
||||
|
||||
class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol {
|
||||
private let clientProxy: ClientProxyProtocol
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let appMediator: AppMediatorProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let analyticsService: AnalyticsService
|
||||
private let pinnedEventStringBuilder: RoomEventStringBuilder
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
|
||||
private var initialSelectedPinnedEventID: String?
|
||||
private let pinnedEventStringBuilder: RoomEventStringBuilder
|
||||
|
||||
private var identityPinningViolations = [String: RoomMemberProxyProtocol]()
|
||||
|
||||
private let actionsSubject: PassthroughSubject<RoomScreenViewModelAction, Never> = .init()
|
||||
var actions: AnyPublisher<RoomScreenViewModelAction, Never> {
|
||||
@ -43,17 +49,22 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
|
||||
init(roomProxy: JoinedRoomProxyProtocol,
|
||||
init(clientProxy: ClientProxyProtocol,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
initialSelectedPinnedEventID: String?,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>,
|
||||
appMediator: AppMediatorProtocol,
|
||||
appSettings: AppSettings,
|
||||
analyticsService: AnalyticsService) {
|
||||
analyticsService: AnalyticsService,
|
||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
self.roomProxy = roomProxy
|
||||
self.appMediator = appMediator
|
||||
self.appSettings = appSettings
|
||||
self.analyticsService = analyticsService
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
self.initialSelectedPinnedEventID = initialSelectedPinnedEventID
|
||||
pinnedEventStringBuilder = .pinnedEventStringBuilder(userID: roomProxy.ownUserID)
|
||||
|
||||
@ -87,6 +98,11 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
actionsSubject.send(.displayCall)
|
||||
actionsSubject.send(.removeComposerFocus)
|
||||
analyticsService.trackInteraction(name: .MobileRoomCallButton)
|
||||
case .footerViewAction(let action):
|
||||
switch action {
|
||||
case .resolvePinViolation(let userID):
|
||||
Task { await resolveIdentityPinningViolation(userID) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +114,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
state.pinnedEventsBannerState.setSelectedPinnedEventID(eventID)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupSubscriptions(ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>) {
|
||||
let roomInfoSubscription = roomProxy
|
||||
.actionsPublisher
|
||||
@ -124,6 +142,19 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher.receive(on: DispatchQueue.main)
|
||||
|
||||
Task { [weak self] in
|
||||
for await changes in identityStatusChangesPublisher.values {
|
||||
guard !Task.isCancelled else {
|
||||
return
|
||||
}
|
||||
|
||||
await self?.processIdentityStatusChanges(changes)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
appMediator.networkMonitor.reachabilityPublisher
|
||||
.filter { $0 == .reachable }
|
||||
.receive(on: DispatchQueue.main)
|
||||
@ -141,6 +172,43 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func processIdentityStatusChanges(_ changes: [IdentityStatusChange]) async {
|
||||
for change in changes {
|
||||
switch change.changedTo {
|
||||
case .pinned:
|
||||
identityPinningViolations[change.userId] = nil
|
||||
case .pinViolation:
|
||||
guard case let .success(member) = await roomProxy.getMember(userID: change.userId) else {
|
||||
MXLog.error("Failed retrieving room member for identity status change: \(change)")
|
||||
continue
|
||||
}
|
||||
|
||||
identityPinningViolations[change.userId] = member
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let member = identityPinningViolations.values.first {
|
||||
state.footerDetails = .pinViolation(member: member,
|
||||
learnMoreURL: appSettings.identityPinningViolationDetailsURL)
|
||||
} else {
|
||||
state.footerDetails = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func resolveIdentityPinningViolation(_ userID: String) async {
|
||||
defer {
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
|
||||
showLoadingIndicator()
|
||||
|
||||
if case .failure = await clientProxy.pinUserIdentity(userID) {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
}
|
||||
}
|
||||
|
||||
private func buildPinnedEventContents(timelineItems: [TimelineItemProxy]) {
|
||||
var pinnedEventContents = OrderedDictionary<String, AttributedString>()
|
||||
|
||||
@ -190,16 +258,30 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Loading indicators
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(RoomScreenViewModel.self)-Loading"
|
||||
|
||||
private func showLoadingIndicator() {
|
||||
userIndicatorController.submitIndicator(.init(id: Self.loadingIndicatorIdentifier, type: .toast, title: L10n.commonLoading))
|
||||
}
|
||||
|
||||
private func hideLoadingIndicator() {
|
||||
userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomScreenViewModel {
|
||||
static func mock(roomProxyMock: JoinedRoomProxyMock) -> RoomScreenViewModel {
|
||||
RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
RoomScreenViewModel(clientProxy: ClientProxyMock(),
|
||||
roomProxy: roomProxyMock,
|
||||
initialSelectedPinnedEventID: nil,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
}
|
||||
}
|
||||
|
@ -29,21 +29,28 @@ struct RoomScreen: View {
|
||||
timeline
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
composerToolbar
|
||||
.padding(.bottom, composerToolbarContext.composerFormattingEnabled ? 8 : 12)
|
||||
.background {
|
||||
if composerToolbarContext.composerFormattingEnabled {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.compound.borderInteractiveSecondary, lineWidth: 0.5)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
VStack(spacing: 0) {
|
||||
RoomScreenFooterView(details: roomContext.viewState.footerDetails,
|
||||
mediaProvider: roomContext.mediaProvider) { action in
|
||||
roomContext.send(viewAction: .footerViewAction(action))
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.environmentObject(timelineContext)
|
||||
.environment(\.timelineContext, timelineContext)
|
||||
// Make sure the reply header honours the hideTimelineMedia setting too.
|
||||
.environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia)
|
||||
|
||||
composerToolbar
|
||||
.padding(.bottom, composerToolbarContext.composerFormattingEnabled ? 8 : 12)
|
||||
.background {
|
||||
if composerToolbarContext.composerFormattingEnabled {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.compound.borderInteractiveSecondary, lineWidth: 0.5)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.environmentObject(timelineContext)
|
||||
.environment(\.timelineContext, timelineContext)
|
||||
// Make sure the reply header honours the hideTimelineMedia setting too.
|
||||
.environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia)
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .top) {
|
||||
Group {
|
||||
|
@ -0,0 +1,78 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RoomScreenFooterView: View {
|
||||
let details: RoomScreenFooterViewDetails?
|
||||
let mediaProvider: MediaProviderProtocol?
|
||||
let callback: (RoomScreenFooterViewAction) -> Void
|
||||
|
||||
var body: some View {
|
||||
if let details {
|
||||
ZStack(alignment: .top) {
|
||||
VStack(spacing: 0) {
|
||||
Color.compound.borderInfoSubtle
|
||||
.frame(height: 1)
|
||||
LinearGradient(colors: [.compound.bgInfoSubtle, .compound.bgCanvasDefault],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom)
|
||||
}
|
||||
|
||||
switch details {
|
||||
case .pinViolation(let member, let learnMoreURL):
|
||||
pinViolation(member: member, learnMoreURL: learnMoreURL)
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func pinViolation(member: RoomMemberProxyProtocol,
|
||||
learnMoreURL: URL) -> some View {
|
||||
VStack(spacing: 16) {
|
||||
HStack(spacing: 16) {
|
||||
LoadableAvatarImage(url: member.avatarURL,
|
||||
name: member.disambiguatedDisplayName,
|
||||
contentID: member.userID,
|
||||
avatarSize: .user(on: .timeline),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
Text(pinViolationDescriptionWithLearnMoreLink(displayName: member.disambiguatedDisplayName ?? member.userID,
|
||||
url: learnMoreURL))
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
}
|
||||
|
||||
Button(L10n.actionOk) {
|
||||
callback(.resolvePinViolation(userID: member.userID))
|
||||
}
|
||||
.buttonStyle(.compound(.primary, size: .medium))
|
||||
}
|
||||
.padding(.top, 16)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
private func pinViolationDescriptionWithLearnMoreLink(displayName: String, url: URL) -> AttributedString {
|
||||
let linkPlaceholder = "{link}"
|
||||
var description = AttributedString(L10n.cryptoIdentityChangePinViolation(displayName, linkPlaceholder))
|
||||
var linkString = AttributedString(L10n.actionLearnMore)
|
||||
linkString.link = url
|
||||
linkString.bold()
|
||||
description.replace(linkPlaceholder, with: linkString)
|
||||
return description
|
||||
}
|
||||
}
|
||||
|
||||
struct RoomScreenFooterView_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
RoomScreenFooterView(details: .pinViolation(member: RoomMemberProxyMock.mockBob, learnMoreURL: "https://element.io/"),
|
||||
mediaProvider: MediaProviderMock(configuration: .init())) { _ in }
|
||||
}
|
||||
}
|
@ -895,6 +895,22 @@ class ClientProxy: ClientProxyProtocol {
|
||||
await client.encryption().curve25519Key()
|
||||
}
|
||||
|
||||
func pinUserIdentity(_ userID: String) async -> Result<Void, ClientProxyError> {
|
||||
MXLog.info("Pinning current identity for user: \(userID)")
|
||||
|
||||
do {
|
||||
guard let userIdentity = try await client.encryption().getUserIdentity(userId: userID) else {
|
||||
MXLog.error("Failed retrieving identity for user: \(userID)")
|
||||
return .failure(.failedRetrievingUserIdentity)
|
||||
}
|
||||
|
||||
return try await .success(userIdentity.pin())
|
||||
} catch {
|
||||
MXLog.error("Failed pinning current identity for user: \(error)")
|
||||
return .failure(.sdkError(error))
|
||||
}
|
||||
}
|
||||
|
||||
func resetIdentity() async -> Result<IdentityResetHandle?, ClientProxyError> {
|
||||
do {
|
||||
return try await .success(client.encryption().resetIdentity())
|
||||
|
@ -35,6 +35,7 @@ enum ClientProxyError: Error {
|
||||
case invalidServerName
|
||||
case failedUploadingMedia(Error, MatrixErrorCode)
|
||||
case roomPreviewIsPrivate
|
||||
case failedRetrievingUserIdentity
|
||||
}
|
||||
|
||||
enum SlidingSyncConstants {
|
||||
@ -196,5 +197,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
||||
func ed25519Base64() async -> String?
|
||||
func curve25519Base64() async -> String?
|
||||
|
||||
func pinUserIdentity(_ userID: String) async -> Result<Void, ClientProxyError>
|
||||
func resetIdentity() async -> Result<IdentityResetHandle?, ClientProxyError>
|
||||
}
|
||||
|
@ -295,10 +295,12 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
|
||||
roomProxy
|
||||
.actionsPublisher
|
||||
.map { action -> (Bool, [String]) in
|
||||
.compactMap { action -> (Bool, [String])? in
|
||||
switch action {
|
||||
case .roomInfoUpdate:
|
||||
return (roomProxy.hasOngoingCall, roomProxy.activeRoomCallParticipants)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
.removeDuplicates { $0 == $1 }
|
||||
|
@ -58,6 +58,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
private var roomInfoObservationToken: TaskHandle?
|
||||
// periphery:ignore - required for instance retention in the rust codebase
|
||||
private var typingNotificationObservationToken: TaskHandle?
|
||||
// periphery:ignore - required for instance retention in the rust codebase
|
||||
private var identityStatusChangesObservationToken: TaskHandle?
|
||||
|
||||
private var subscribedForUpdates = false
|
||||
|
||||
@ -70,6 +72,11 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
var typingMembersPublisher: CurrentValuePublisher<[String], Never> {
|
||||
typingMembersSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let identityStatusChangesSubject = CurrentValueSubject<[IdentityStatusChange], Never>([])
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> {
|
||||
identityStatusChangesSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> {
|
||||
@ -193,6 +200,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
|
||||
subscribeToRoomInfoUpdates()
|
||||
|
||||
subscribeToIdentityStatusChanges()
|
||||
|
||||
subscribeToTypingNotifications()
|
||||
}
|
||||
|
||||
@ -708,6 +717,16 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
typingMembersSubject.send(typingMembers)
|
||||
})
|
||||
}
|
||||
|
||||
private func subscribeToIdentityStatusChanges() {
|
||||
identityStatusChangesObservationToken = room.subscribeToIdentityStatusChanges(listener: RoomIdentityStatusChangeListener { [weak self] changes in
|
||||
guard let self else { return }
|
||||
|
||||
MXLog.info("Received identity status changes: \(changes)")
|
||||
|
||||
identityStatusChangesSubject.send(changes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomInfoUpdateListener: RoomInfoListener {
|
||||
@ -733,3 +752,15 @@ private final class RoomTypingNotificationUpdateListener: TypingNotificationsLis
|
||||
onUpdateClosure(typingUserIds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomIdentityStatusChangeListener: IdentityStatusChangeListener {
|
||||
private let onUpdateClosure: ([IdentityStatusChange]) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping ([IdentityStatusChange]) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func call(identityStatusChange: [IdentityStatusChange]) {
|
||||
onUpdateClosure(identityStatusChange)
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,24 @@ final class RoomMemberProxy: RoomMemberProxyProtocol {
|
||||
}
|
||||
|
||||
var userID: String { member.userId }
|
||||
|
||||
var displayName: String? { member.displayName }
|
||||
|
||||
var disambiguatedDisplayName: String? {
|
||||
guard let displayName else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return member.isNameAmbiguous ? "\(displayName) (\(userID))" : displayName
|
||||
}
|
||||
|
||||
var avatarURL: URL? { member.avatarUrl.flatMap(URL.init(string:)) }
|
||||
|
||||
var membership: MembershipState { member.membership }
|
||||
|
||||
var isIgnored: Bool { member.isIgnored }
|
||||
|
||||
var powerLevel: Int { Int(member.powerLevel) }
|
||||
|
||||
var role: RoomMemberRole { member.suggestedRoleForPowerLevel }
|
||||
}
|
||||
|
@ -11,13 +11,18 @@ import MatrixRustSDK
|
||||
// sourcery: AutoMockable
|
||||
protocol RoomMemberProxyProtocol: AnyObject {
|
||||
var userID: String { get }
|
||||
|
||||
var displayName: String? { get }
|
||||
var disambiguatedDisplayName: String? { get }
|
||||
|
||||
var avatarURL: URL? { get }
|
||||
|
||||
var membership: MembershipState { get }
|
||||
|
||||
var isIgnored: Bool { get }
|
||||
|
||||
var powerLevel: Int { get }
|
||||
|
||||
var role: RoomMemberRole { get }
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ protocol InvitedRoomProxyProtocol: RoomProxyProtocol {
|
||||
func acceptInvitation() async -> Result<Void, RoomProxyError>
|
||||
}
|
||||
|
||||
enum JoinedRoomProxyAction {
|
||||
enum JoinedRoomProxyAction: Equatable {
|
||||
case roomInfoUpdate
|
||||
}
|
||||
|
||||
@ -73,6 +73,8 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
|
||||
var typingMembersPublisher: CurrentValuePublisher<[String], Never> { get }
|
||||
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { get }
|
||||
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> { get }
|
||||
|
||||
var timeline: TimelineProxyProtocol { get }
|
||||
|
@ -19,7 +19,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
case unknownDevice(color: Color)
|
||||
case unsignedDevice(color: Color)
|
||||
case unverifiedIdentity(color: Color)
|
||||
case previouslyVerified(color: Color)
|
||||
case verificationViolation(color: Color)
|
||||
case sentInClear(color: Color)
|
||||
|
||||
var message: String {
|
||||
@ -32,7 +32,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
L10n.eventShieldReasonUnsignedDevice
|
||||
case .unverifiedIdentity:
|
||||
L10n.eventShieldReasonUnverifiedIdentity
|
||||
case .previouslyVerified:
|
||||
case .verificationViolation:
|
||||
L10n.eventShieldReasonPreviouslyVerified
|
||||
case .sentInClear:
|
||||
L10n.eventShieldReasonSentInClear
|
||||
@ -45,7 +45,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
.unknownDevice(let color),
|
||||
.unsignedDevice(let color),
|
||||
.unverifiedIdentity(let color),
|
||||
.previouslyVerified(let color),
|
||||
.verificationViolation(let color),
|
||||
.sentInClear(let color):
|
||||
color
|
||||
}
|
||||
@ -54,7 +54,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
var icon: KeyPath<CompoundIcons, Image> {
|
||||
switch self {
|
||||
case .notGuaranteed: \.info
|
||||
case .unknownDevice, .unsignedDevice, .unverifiedIdentity, .previouslyVerified: \.helpSolid
|
||||
case .unknownDevice, .unsignedDevice, .unverifiedIdentity, .verificationViolation: \.helpSolid
|
||||
case .sentInClear: \.lockOff
|
||||
}
|
||||
}
|
||||
@ -82,8 +82,8 @@ extension EncryptionAuthenticity {
|
||||
self = .unsignedDevice(color: color)
|
||||
case .unverifiedIdentity:
|
||||
self = .unverifiedIdentity(color: color)
|
||||
case .previouslyVerified:
|
||||
self = .previouslyVerified(color: color)
|
||||
case .verificationViolation:
|
||||
self = .verificationViolation(color: color)
|
||||
case .sentInClear:
|
||||
self = .sentInClear(color: color)
|
||||
}
|
||||
|
@ -233,7 +233,8 @@ class MockScreen: Identifiable {
|
||||
return navigationStackCoordinator
|
||||
case .roomPlainNoAvatar:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)),
|
||||
timelineController: MockRoomTimelineController(),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -251,7 +252,8 @@ class MockScreen: Identifiable {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -269,7 +271,8 @@ class MockScreen: Identifiable {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.default
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -287,7 +290,8 @@ class MockScreen: Identifiable {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunkWithReadReceipts
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -308,7 +312,8 @@ class MockScreen: Identifiable {
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk]
|
||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -329,7 +334,8 @@ class MockScreen: Identifiable {
|
||||
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -350,7 +356,8 @@ class MockScreen: Identifiable {
|
||||
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -372,7 +379,8 @@ class MockScreen: Identifiable {
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -393,7 +401,8 @@ class MockScreen: Identifiable {
|
||||
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -413,7 +422,8 @@ class MockScreen: Identifiable {
|
||||
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.permalinkChunk
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -447,7 +457,8 @@ class MockScreen: Identifiable {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.disclosedPolls
|
||||
timelineController.incomingItems = []
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -468,7 +479,8 @@ class MockScreen: Identifiable {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.undisclosedPolls
|
||||
timelineController.incomingItems = []
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
@ -489,7 +501,8 @@ class MockScreen: Identifiable {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.outgoingPolls
|
||||
timelineController.incomingItems = []
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)),
|
||||
let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(),
|
||||
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)),
|
||||
timelineController: timelineController,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
|
@ -689,6 +689,12 @@ extension PreviewTests {
|
||||
}
|
||||
}
|
||||
|
||||
func test_roomScreenFooterView() {
|
||||
for preview in RoomScreenFooterView_Previews._allPreviews {
|
||||
assertSnapshots(matching: preview)
|
||||
}
|
||||
}
|
||||
|
||||
func test_roomScreen() {
|
||||
for preview in RoomScreen_Previews._allPreviews {
|
||||
assertSnapshots(matching: preview)
|
||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -218,9 +218,32 @@ class LoggingTests: XCTestCase {
|
||||
let rustEmoteMessage = EmoteMessageContent(body: emoteString,
|
||||
formatted: FormattedBody(format: .html, body: "<b>\(emoteString)</b>"))
|
||||
|
||||
let rustImageMessage = ImageMessageContent(body: "ImageString", formatted: nil, filename: nil, source: MediaSource(noPointer: .init()), info: nil)
|
||||
let rustVideoMessage = VideoMessageContent(body: "VideoString", formatted: nil, filename: nil, source: MediaSource(noPointer: .init()), info: nil)
|
||||
let rustFileMessage = FileMessageContent(body: "FileString", formatted: nil, filename: "FileName", source: MediaSource(noPointer: .init()), info: nil)
|
||||
let rustImageMessage = ImageMessageContent(body: "ImageString",
|
||||
formatted: nil,
|
||||
rawFilename: "ImageString",
|
||||
filename: "ImageString",
|
||||
caption: "ImageString",
|
||||
formattedCaption: nil,
|
||||
source: MediaSource(noPointer: .init()),
|
||||
info: nil)
|
||||
|
||||
let rustVideoMessage = VideoMessageContent(body: "VideoString",
|
||||
formatted: nil,
|
||||
rawFilename: "VideoString",
|
||||
filename: "VideoString",
|
||||
caption: "VideoString",
|
||||
formattedCaption: nil,
|
||||
source: MediaSource(noPointer: .init()),
|
||||
info: nil)
|
||||
|
||||
let rustFileMessage = FileMessageContent(body: "FileString",
|
||||
formatted: nil,
|
||||
rawFilename: "FileString",
|
||||
filename: "FileString",
|
||||
caption: "FileString",
|
||||
formattedCaption: nil,
|
||||
source: MediaSource(noPointer: .init()),
|
||||
info: nil)
|
||||
|
||||
// When logging that value
|
||||
MXLog.info(rustTextMessage)
|
||||
|
@ -33,13 +33,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
// setup the room proxy actions publisher
|
||||
roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher()
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(),
|
||||
roomProxy: roomProxyMock,
|
||||
initialSelectedPinnedEventID: nil,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
self.viewModel = viewModel
|
||||
|
||||
// check if in the default state is not showing but is indeed loading
|
||||
@ -111,13 +113,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2")),
|
||||
.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: "3"))]
|
||||
roomProxyMock.underlyingPinnedEventsTimeline = pinnedTimelineMock
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(),
|
||||
roomProxy: roomProxyMock,
|
||||
initialSelectedPinnedEventID: "test1",
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
self.viewModel = viewModel
|
||||
|
||||
// check if the banner is now in a loaded state and is showing the counter
|
||||
@ -158,13 +162,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
// setup the room proxy actions publisher
|
||||
roomProxyMock.canUserJoinCallUserIDReturnValue = .success(false)
|
||||
roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher()
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(),
|
||||
roomProxy: roomProxyMock,
|
||||
initialSelectedPinnedEventID: nil,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
self.viewModel = viewModel
|
||||
|
||||
var deferred = deferFulfillment(viewModel.context.$viewState) { viewState in
|
||||
@ -195,13 +201,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
// Given a room screen with no ongoing call.
|
||||
let ongoingCallRoomIDSubject = CurrentValueSubject<String?, Never>(nil)
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "MyRoomID"))
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(),
|
||||
roomProxy: roomProxyMock,
|
||||
initialSelectedPinnedEventID: nil,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: ongoingCallRoomIDSubject.asCurrentValuePublisher(),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
self.viewModel = viewModel
|
||||
XCTAssertTrue(viewModel.state.shouldShowCallButton)
|
||||
|
||||
|
@ -60,7 +60,7 @@ packages:
|
||||
# Element/Matrix dependencies
|
||||
MatrixRustSDK:
|
||||
url: https://github.com/element-hq/matrix-rust-components-swift
|
||||
exactVersion: 1.0.53
|
||||
exactVersion: 1.0.55
|
||||
# path: ../matrix-rust-sdk
|
||||
Compound:
|
||||
url: https://github.com/element-hq/compound-ios
|
||||
|
Loading…
x
Reference in New Issue
Block a user