mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Use the room heroes when computing a DM avatar. (#2900)
* Use room heroes for DM avatar content ID. * Use RoomAvatar.heroes for the DM Details stack.
This commit is contained in:
@@ -558,6 +558,7 @@
|
|||||||
854E82E064BA53CD0BC45600 /* LocationRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6613DE16AD26B3A74DA1F5 /* LocationRoomTimelineItemContent.swift */; };
|
854E82E064BA53CD0BC45600 /* LocationRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6613DE16AD26B3A74DA1F5 /* LocationRoomTimelineItemContent.swift */; };
|
||||||
85813D87DDD7F67A46BD9AF7 /* ImageProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A8047B50E3607ACD354E /* ImageProviderProtocol.swift */; };
|
85813D87DDD7F67A46BD9AF7 /* ImageProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A8047B50E3607ACD354E /* ImageProviderProtocol.swift */; };
|
||||||
858276B19C7C0AD4CA98EA78 /* portrait_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = AF042B0FB2EE88977C91E330 /* portrait_test_image.jpg */; };
|
858276B19C7C0AD4CA98EA78 /* portrait_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = AF042B0FB2EE88977C91E330 /* portrait_test_image.jpg */; };
|
||||||
|
8587A53DE8EF94FD796DC375 /* RoomAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEF5FE93A06F563B477F024A /* RoomAvatarImage.swift */; };
|
||||||
858C04B62166B5BAFCD20F2D /* TimelineItemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1BF12A5E7C76777C4BF0F2B /* TimelineItemMenu.swift */; };
|
858C04B62166B5BAFCD20F2D /* TimelineItemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1BF12A5E7C76777C4BF0F2B /* TimelineItemMenu.swift */; };
|
||||||
859E2CA2EDF343BD24DE52EB /* RoomDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */; };
|
859E2CA2EDF343BD24DE52EB /* RoomDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */; };
|
||||||
85F89F3F320F4FADCFFFE68B /* ServerSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */; };
|
85F89F3F320F4FADCFFFE68B /* ServerSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */; };
|
||||||
@@ -569,6 +570,7 @@
|
|||||||
8691186F9B99BCDDB7CACDD8 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; };
|
8691186F9B99BCDDB7CACDD8 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; };
|
||||||
86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D879FC4E881E748BB9B34DC /* RoomChangePermissionsScreenCoordinator.swift */; };
|
86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D879FC4E881E748BB9B34DC /* RoomChangePermissionsScreenCoordinator.swift */; };
|
||||||
872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349F590E35CE514A71E6764 /* LoginHomeserver.swift */; };
|
872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349F590E35CE514A71E6764 /* LoginHomeserver.swift */; };
|
||||||
|
8739553CDFA5D8ED5FD05CBC /* RoomSummaryDetailsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA520B4F65D162E555C8761 /* RoomSummaryDetailsTests.swift */; };
|
||||||
874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */; };
|
874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */; };
|
||||||
878070573C7BF19E735707B4 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */; };
|
878070573C7BF19E735707B4 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */; };
|
||||||
87B4E59A4467F8EC75F82372 /* VoiceMessageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */; };
|
87B4E59A4467F8EC75F82372 /* VoiceMessageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */; };
|
||||||
@@ -1892,6 +1894,7 @@
|
|||||||
BEA38B9851CFCC4D67F5587D /* EmojiPickerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenCoordinator.swift; sourceTree = "<group>"; };
|
BEA38B9851CFCC4D67F5587D /* EmojiPickerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreProtocol.swift; sourceTree = "<group>"; };
|
BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreProtocol.swift; sourceTree = "<group>"; };
|
||||||
BEE365C5A4E90ACBE398EFFE /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/SAS.strings; sourceTree = "<group>"; };
|
BEE365C5A4E90ACBE398EFFE /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
|
BEF5FE93A06F563B477F024A /* RoomAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAvatarImage.swift; sourceTree = "<group>"; };
|
||||||
BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderScreenCoordinator.swift; sourceTree = "<group>"; };
|
BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
BFA9EA59D5C0DA1BFC7B3621 /* QRCodeLoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreen.swift; sourceTree = "<group>"; };
|
BFA9EA59D5C0DA1BFC7B3621 /* QRCodeLoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreen.swift; sourceTree = "<group>"; };
|
||||||
BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = "<group>"; };
|
BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = "<group>"; };
|
||||||
@@ -1965,6 +1968,7 @@
|
|||||||
CD700E035C85738EE4B97129 /* PerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
|
CD700E035C85738EE4B97129 /* PerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
|
||||||
CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = "<group>"; };
|
CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = "<group>"; };
|
||||||
CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
|
CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
|
||||||
|
CEA520B4F65D162E555C8761 /* RoomSummaryDetailsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryDetailsTests.swift; sourceTree = "<group>"; };
|
||||||
CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
||||||
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||||
@@ -2725,6 +2729,7 @@
|
|||||||
50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */,
|
50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */,
|
||||||
648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */,
|
648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */,
|
||||||
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */,
|
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */,
|
||||||
|
BEF5FE93A06F563B477F024A /* RoomAvatarImage.swift */,
|
||||||
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */,
|
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */,
|
||||||
DE7C80EF77AD102053D3646E /* RoundedLabelItem.swift */,
|
DE7C80EF77AD102053D3646E /* RoundedLabelItem.swift */,
|
||||||
AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */,
|
AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */,
|
||||||
@@ -3595,6 +3600,7 @@
|
|||||||
48FEFF746DB341CDB18D7AAA /* RoomRolesAndPermissionsScreenViewModelTests.swift */,
|
48FEFF746DB341CDB18D7AAA /* RoomRolesAndPermissionsScreenViewModelTests.swift */,
|
||||||
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */,
|
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */,
|
||||||
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */,
|
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */,
|
||||||
|
CEA520B4F65D162E555C8761 /* RoomSummaryDetailsTests.swift */,
|
||||||
2E88534A39781D76487D59DF /* SecureBackupKeyBackupScreenViewModelTests.swift */,
|
2E88534A39781D76487D59DF /* SecureBackupKeyBackupScreenViewModelTests.swift */,
|
||||||
848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */,
|
848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */,
|
||||||
C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */,
|
C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */,
|
||||||
@@ -5802,6 +5808,7 @@
|
|||||||
84C631E734FD2555B39B681C /* RoomRolesAndPermissionsScreenViewModelTests.swift in Sources */,
|
84C631E734FD2555B39B681C /* RoomRolesAndPermissionsScreenViewModelTests.swift in Sources */,
|
||||||
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */,
|
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */,
|
||||||
CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */,
|
CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */,
|
||||||
|
8739553CDFA5D8ED5FD05CBC /* RoomSummaryDetailsTests.swift in Sources */,
|
||||||
7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */,
|
7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */,
|
||||||
EB87DF90CF6F8D5D12404C6E /* SecureBackupLogoutConfirmationScreenViewModelTests.swift in Sources */,
|
EB87DF90CF6F8D5D12404C6E /* SecureBackupLogoutConfirmationScreenViewModelTests.swift in Sources */,
|
||||||
06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */,
|
06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */,
|
||||||
@@ -6297,6 +6304,7 @@
|
|||||||
680062C402ECB8FCAAE85A5C /* ResetRecoveryKeyScreenViewModelProtocol.swift in Sources */,
|
680062C402ECB8FCAAE85A5C /* ResetRecoveryKeyScreenViewModelProtocol.swift in Sources */,
|
||||||
A494741843F087881299ACF0 /* RestorationToken.swift in Sources */,
|
A494741843F087881299ACF0 /* RestorationToken.swift in Sources */,
|
||||||
6E391F7F628D984AF44385D9 /* RoomAttachmentPicker.swift in Sources */,
|
6E391F7F628D984AF44385D9 /* RoomAttachmentPicker.swift in Sources */,
|
||||||
|
8587A53DE8EF94FD796DC375 /* RoomAvatarImage.swift in Sources */,
|
||||||
F8C87130FD999F7F1076208C /* RoomChangePermissionsScreen.swift in Sources */,
|
F8C87130FD999F7F1076208C /* RoomChangePermissionsScreen.swift in Sources */,
|
||||||
86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */,
|
86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */,
|
||||||
4D4D236F0BBCDC4D2CBCCBB5 /* RoomChangePermissionsScreenModels.swift in Sources */,
|
4D4D236F0BBCDC4D2CBCCBB5 /* RoomChangePermissionsScreenModels.swift in Sources */,
|
||||||
|
|||||||
@@ -8056,6 +8056,11 @@ class RoomProxyMock: RoomProxyProtocol {
|
|||||||
var underlyingOwnUserID: String!
|
var underlyingOwnUserID: String!
|
||||||
var name: String?
|
var name: String?
|
||||||
var topic: String?
|
var topic: String?
|
||||||
|
var avatar: RoomAvatar {
|
||||||
|
get { return underlyingAvatar }
|
||||||
|
set(value) { underlyingAvatar = value }
|
||||||
|
}
|
||||||
|
var underlyingAvatar: RoomAvatar!
|
||||||
var avatarURL: URL?
|
var avatarURL: URL?
|
||||||
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> {
|
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> {
|
||||||
get { return underlyingMembersPublisher }
|
get { return underlyingMembersPublisher }
|
||||||
|
|||||||
@@ -10733,6 +10733,71 @@ open class RoomSDKMock: MatrixRustSDK.Room {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: - heroes
|
||||||
|
|
||||||
|
var heroesUnderlyingCallsCount = 0
|
||||||
|
open var heroesCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return heroesUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = heroesUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
heroesUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
heroesUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open var heroesCalled: Bool {
|
||||||
|
return heroesCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var heroesUnderlyingReturnValue: [RoomHero]!
|
||||||
|
open var heroesReturnValue: [RoomHero]! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return heroesUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: [RoomHero]? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = heroesUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
heroesUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
heroesUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open var heroesClosure: (() -> [RoomHero])?
|
||||||
|
|
||||||
|
open override func heroes() -> [RoomHero] {
|
||||||
|
heroesCallsCount += 1
|
||||||
|
if let heroesClosure = heroesClosure {
|
||||||
|
return heroesClosure()
|
||||||
|
} else {
|
||||||
|
return heroesReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - id
|
//MARK: - id
|
||||||
|
|
||||||
var idUnderlyingCallsCount = 0
|
var idUnderlyingCallsCount = 0
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ extension RoomProxyMock {
|
|||||||
id = configuration.id
|
id = configuration.id
|
||||||
name = configuration.name
|
name = configuration.name
|
||||||
topic = configuration.topic
|
topic = configuration.topic
|
||||||
|
avatar = .room(id: configuration.id, name: configuration.name, avatarURL: configuration.avatarURL) // Note: This doesn't replicate the real proxy logic.
|
||||||
avatarURL = configuration.avatarURL
|
avatarURL = configuration.avatarURL
|
||||||
isDirect = configuration.isDirect
|
isDirect = configuration.isDirect
|
||||||
isSpace = configuration.isSpace
|
isSpace = configuration.isSpace
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Foundation 🔭🪐🌌",
|
name: "Foundation 🔭🪐🌌",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"),
|
lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"),
|
||||||
lastMessageFormattedTimestamp: "14:56",
|
lastMessageFormattedTimestamp: "14:56",
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
@@ -100,6 +101,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Foundation and Empire",
|
name: "Foundation and Empire",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: URL.picturesDirectory,
|
avatarURL: URL.picturesDirectory,
|
||||||
|
heroes: [],
|
||||||
lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"),
|
lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"),
|
||||||
lastMessageFormattedTimestamp: "2:56 PM",
|
lastMessageFormattedTimestamp: "2:56 PM",
|
||||||
unreadMessagesCount: 2,
|
unreadMessagesCount: 2,
|
||||||
@@ -116,6 +118,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Second Foundation",
|
name: "Second Foundation",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"),
|
lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"),
|
||||||
lastMessageFormattedTimestamp: "Some time ago",
|
lastMessageFormattedTimestamp: "Some time ago",
|
||||||
unreadMessagesCount: 3,
|
unreadMessagesCount: 3,
|
||||||
@@ -132,6 +135,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Foundation's Edge",
|
name: "Foundation's Edge",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: AttributedString("There's an archaic measure of time that's called the month"),
|
lastMessage: AttributedString("There's an archaic measure of time that's called the month"),
|
||||||
lastMessageFormattedTimestamp: "Just now",
|
lastMessageFormattedTimestamp: "Just now",
|
||||||
unreadMessagesCount: 2,
|
unreadMessagesCount: 2,
|
||||||
@@ -148,6 +152,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Foundation and Earth",
|
name: "Foundation and Earth",
|
||||||
isDirect: true,
|
isDirect: true,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"),
|
lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"),
|
||||||
lastMessageFormattedTimestamp: "1986",
|
lastMessageFormattedTimestamp: "1986",
|
||||||
unreadMessagesCount: 1,
|
unreadMessagesCount: 1,
|
||||||
@@ -164,6 +169,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Prelude to Foundation",
|
name: "Prelude to Foundation",
|
||||||
isDirect: true,
|
isDirect: true,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: AttributedString("Are you groping for the word 'paranoia'?"),
|
lastMessage: AttributedString("Are you groping for the word 'paranoia'?"),
|
||||||
lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです",
|
lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです",
|
||||||
unreadMessagesCount: 6,
|
unreadMessagesCount: 6,
|
||||||
@@ -180,6 +186,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Unknown",
|
name: "Unknown",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: nil,
|
lastMessage: nil,
|
||||||
lastMessageFormattedTimestamp: nil,
|
lastMessageFormattedTimestamp: nil,
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
@@ -229,6 +236,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "First room",
|
name: "First room",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: URL.picturesDirectory,
|
avatarURL: URL.picturesDirectory,
|
||||||
|
heroes: [],
|
||||||
lastMessage: nil,
|
lastMessage: nil,
|
||||||
lastMessageFormattedTimestamp: nil,
|
lastMessageFormattedTimestamp: nil,
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
@@ -245,6 +253,7 @@ extension Array where Element == RoomSummary {
|
|||||||
name: "Second room",
|
name: "Second room",
|
||||||
isDirect: true,
|
isDirect: true,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: nil,
|
lastMessage: nil,
|
||||||
lastMessageFormattedTimestamp: nil,
|
lastMessageFormattedTimestamp: nil,
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
|
|||||||
@@ -17,28 +17,9 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct AvatarHeaderView<Footer: View>: View {
|
struct AvatarHeaderView<Footer: View>: View {
|
||||||
private struct AvatarInfo {
|
private enum AvatarInfo {
|
||||||
let id: String
|
case room(RoomAvatar)
|
||||||
let name: String?
|
case user(UserProfileProxy)
|
||||||
let avatarURL: URL?
|
|
||||||
|
|
||||||
init(from room: RoomDetails) {
|
|
||||||
id = room.id
|
|
||||||
name = room.name
|
|
||||||
avatarURL = room.avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from member: RoomMemberDetails) {
|
|
||||||
id = member.id
|
|
||||||
name = member.isBanned ? nil : member.name
|
|
||||||
avatarURL = member.isBanned ? nil : member.avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from user: UserProfileProxy) {
|
|
||||||
id = user.userID
|
|
||||||
name = user.displayName
|
|
||||||
avatarURL = user.avatarURL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Badge: Hashable {
|
private enum Badge: Hashable {
|
||||||
@@ -46,8 +27,8 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
case `public`
|
case `public`
|
||||||
}
|
}
|
||||||
|
|
||||||
private let mainAvatarInfo: AvatarInfo
|
private let avatarInfo: AvatarInfo
|
||||||
private let secondaryAvatarInfo: AvatarInfo?
|
private let title: String
|
||||||
private let subtitle: String?
|
private let subtitle: String?
|
||||||
private let badges: [Badge]
|
private let badges: [Badge]
|
||||||
|
|
||||||
@@ -61,8 +42,8 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
imageProvider: ImageProviderProtocol? = nil,
|
imageProvider: ImageProviderProtocol? = nil,
|
||||||
onAvatarTap: (() -> Void)? = nil,
|
onAvatarTap: (() -> Void)? = nil,
|
||||||
@ViewBuilder footer: @escaping () -> Footer) {
|
@ViewBuilder footer: @escaping () -> Footer) {
|
||||||
mainAvatarInfo = .init(from: room)
|
avatarInfo = .room(room.avatar)
|
||||||
secondaryAvatarInfo = nil
|
title = room.name ?? room.id
|
||||||
subtitle = room.canonicalAlias
|
subtitle = room.canonicalAlias
|
||||||
|
|
||||||
self.avatarSize = avatarSize
|
self.avatarSize = avatarSize
|
||||||
@@ -83,9 +64,10 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
imageProvider: ImageProviderProtocol? = nil,
|
imageProvider: ImageProviderProtocol? = nil,
|
||||||
onAvatarTap: (() -> Void)? = nil,
|
onAvatarTap: (() -> Void)? = nil,
|
||||||
@ViewBuilder footer: @escaping () -> Footer) {
|
@ViewBuilder footer: @escaping () -> Footer) {
|
||||||
mainAvatarInfo = .init(from: dmRecipient)
|
let dmRecipientProfile = UserProfileProxy(member: dmRecipient)
|
||||||
secondaryAvatarInfo = .init(from: accountOwner)
|
avatarInfo = .room(.heroes([dmRecipientProfile, UserProfileProxy(member: accountOwner)]))
|
||||||
subtitle = dmRecipient.isBanned ? nil : dmRecipient.name == nil ? nil : dmRecipient.id
|
title = dmRecipientProfile.displayName ?? dmRecipientProfile.userID
|
||||||
|
subtitle = dmRecipientProfile.displayName == nil ? nil : dmRecipientProfile.userID
|
||||||
|
|
||||||
avatarSize = .user(on: .dmDetails)
|
avatarSize = .user(on: .dmDetails)
|
||||||
self.imageProvider = imageProvider
|
self.imageProvider = imageProvider
|
||||||
@@ -100,15 +82,13 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
imageProvider: ImageProviderProtocol? = nil,
|
imageProvider: ImageProviderProtocol? = nil,
|
||||||
onAvatarTap: (() -> Void)? = nil,
|
onAvatarTap: (() -> Void)? = nil,
|
||||||
@ViewBuilder footer: @escaping () -> Footer) {
|
@ViewBuilder footer: @escaping () -> Footer) {
|
||||||
mainAvatarInfo = .init(from: member)
|
let profile = UserProfileProxy(member: member)
|
||||||
secondaryAvatarInfo = nil
|
|
||||||
subtitle = member.isBanned ? nil : member.name == nil ? nil : member.id
|
|
||||||
|
|
||||||
self.avatarSize = avatarSize
|
self.init(user: profile,
|
||||||
self.imageProvider = imageProvider
|
avatarSize: avatarSize,
|
||||||
self.onAvatarTap = onAvatarTap
|
imageProvider: imageProvider,
|
||||||
self.footer = footer
|
onAvatarTap: onAvatarTap,
|
||||||
badges = []
|
footer: footer)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(user: UserProfileProxy,
|
init(user: UserProfileProxy,
|
||||||
@@ -116,8 +96,8 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
imageProvider: ImageProviderProtocol? = nil,
|
imageProvider: ImageProviderProtocol? = nil,
|
||||||
onAvatarTap: (() -> Void)? = nil,
|
onAvatarTap: (() -> Void)? = nil,
|
||||||
@ViewBuilder footer: @escaping () -> Footer) {
|
@ViewBuilder footer: @escaping () -> Footer) {
|
||||||
mainAvatarInfo = .init(from: user)
|
avatarInfo = .user(user)
|
||||||
secondaryAvatarInfo = nil
|
title = user.displayName ?? user.userID
|
||||||
subtitle = user.displayName == nil ? nil : user.userID
|
subtitle = user.displayName == nil ? nil : user.userID
|
||||||
|
|
||||||
self.avatarSize = avatarSize
|
self.avatarSize = avatarSize
|
||||||
@@ -152,40 +132,15 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var avatar: some View {
|
private var avatar: some View {
|
||||||
if let secondaryAvatarInfo {
|
switch avatarInfo {
|
||||||
ZStack {
|
case .room(let roomAvatar):
|
||||||
LoadableAvatarImage(url: mainAvatarInfo.avatarURL,
|
RoomAvatarImage(avatar: roomAvatar,
|
||||||
name: mainAvatarInfo.name,
|
avatarSize: avatarSize,
|
||||||
contentID: mainAvatarInfo.id,
|
imageProvider: imageProvider)
|
||||||
avatarSize: avatarSize,
|
case .user(let userProfile):
|
||||||
imageProvider: imageProvider)
|
LoadableAvatarImage(url: userProfile.avatarURL,
|
||||||
.scaledFrame(size: 120, alignment: .topTrailing)
|
name: userProfile.displayName,
|
||||||
LoadableAvatarImage(url: secondaryAvatarInfo.avatarURL,
|
contentID: userProfile.userID,
|
||||||
name: secondaryAvatarInfo.name,
|
|
||||||
contentID: secondaryAvatarInfo.id,
|
|
||||||
avatarSize: avatarSize,
|
|
||||||
imageProvider: imageProvider)
|
|
||||||
.mask {
|
|
||||||
Rectangle()
|
|
||||||
.fill(Color.white)
|
|
||||||
.overlay {
|
|
||||||
Circle()
|
|
||||||
.inset(by: -4)
|
|
||||||
.fill(Color.black)
|
|
||||||
.scaledOffset(x: 120 - avatarSize.value,
|
|
||||||
y: -120 + avatarSize.value)
|
|
||||||
}
|
|
||||||
.compositingGroup()
|
|
||||||
.luminanceToAlpha()
|
|
||||||
}
|
|
||||||
.scaledFrame(size: 120, alignment: .bottomLeading)
|
|
||||||
}
|
|
||||||
.scaledFrame(size: 120)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
LoadableAvatarImage(url: mainAvatarInfo.avatarURL,
|
|
||||||
name: mainAvatarInfo.name,
|
|
||||||
contentID: mainAvatarInfo.id,
|
|
||||||
avatarSize: avatarSize,
|
avatarSize: avatarSize,
|
||||||
imageProvider: imageProvider)
|
imageProvider: imageProvider)
|
||||||
}
|
}
|
||||||
@@ -203,7 +158,7 @@ struct AvatarHeaderView<Footer: View>: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 9)
|
.frame(height: 9)
|
||||||
|
|
||||||
Text(mainAvatarInfo.name ?? mainAvatarInfo.id)
|
Text(title)
|
||||||
.foregroundColor(.compound.textPrimary)
|
.foregroundColor(.compound.textPrimary)
|
||||||
.font(.compound.headingMDBold)
|
.font(.compound.headingMDBold)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -237,7 +192,9 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview {
|
|||||||
Form {
|
Form {
|
||||||
AvatarHeaderView(room: .init(id: "@test:matrix.org",
|
AvatarHeaderView(room: .init(id: "@test:matrix.org",
|
||||||
name: "Test Room",
|
name: "Test Room",
|
||||||
avatarURL: URL.picturesDirectory,
|
avatar: .room(id: "@test:matrix.org",
|
||||||
|
name: "Test Room",
|
||||||
|
avatarURL: .picturesDirectory),
|
||||||
canonicalAlias: "#test:matrix.org",
|
canonicalAlias: "#test:matrix.org",
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
isPublic: true),
|
isPublic: true),
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// Information about a room avatar such as it's URL or the heroes to use as a fallback.
|
||||||
|
enum RoomAvatar: Equatable {
|
||||||
|
/// An avatar generated from the room's details.
|
||||||
|
case room(id: String, name: String?, avatarURL: URL?)
|
||||||
|
/// An avatar generated from the room's heroes.
|
||||||
|
case heroes([UserProfileProxy])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view that shows the avatar for a room, or a cluster of heroes if provided.
|
||||||
|
///
|
||||||
|
/// This should be preferred over `LoadableAvatarImage` when displaying a
|
||||||
|
/// room avatar so that DMs have a consistent appearance throughout the app.
|
||||||
|
struct RoomAvatarImage: View {
|
||||||
|
let avatar: RoomAvatar
|
||||||
|
|
||||||
|
let avatarSize: AvatarSize
|
||||||
|
let imageProvider: ImageProviderProtocol?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
switch avatar {
|
||||||
|
case .room(let id, let name, let avatarURL):
|
||||||
|
LoadableAvatarImage(url: avatarURL,
|
||||||
|
name: name,
|
||||||
|
contentID: id,
|
||||||
|
avatarSize: avatarSize,
|
||||||
|
imageProvider: imageProvider)
|
||||||
|
case .heroes(let users):
|
||||||
|
// We will expand upon this with more stack sizes in the future.
|
||||||
|
if users.count == 0 {
|
||||||
|
let _ = assertionFailure("We should never pass empty heroes here.")
|
||||||
|
PlaceholderAvatarImage(name: nil, contentID: nil)
|
||||||
|
} else if users.count == 2 {
|
||||||
|
let clusterSize = avatarSize.value * 1.6
|
||||||
|
ZStack {
|
||||||
|
LoadableAvatarImage(url: users[0].avatarURL,
|
||||||
|
name: users[0].displayName,
|
||||||
|
contentID: users[0].userID,
|
||||||
|
avatarSize: avatarSize,
|
||||||
|
imageProvider: imageProvider)
|
||||||
|
.scaledFrame(size: clusterSize, alignment: .topTrailing)
|
||||||
|
|
||||||
|
LoadableAvatarImage(url: users[1].avatarURL,
|
||||||
|
name: users[1].displayName,
|
||||||
|
contentID: users[1].userID,
|
||||||
|
avatarSize: avatarSize,
|
||||||
|
imageProvider: imageProvider)
|
||||||
|
.mask {
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color.white)
|
||||||
|
.overlay {
|
||||||
|
Circle()
|
||||||
|
.inset(by: -4)
|
||||||
|
.fill(Color.black)
|
||||||
|
.scaledOffset(x: clusterSize - avatarSize.value,
|
||||||
|
y: -clusterSize + avatarSize.value)
|
||||||
|
}
|
||||||
|
.compositingGroup()
|
||||||
|
.luminanceToAlpha()
|
||||||
|
}
|
||||||
|
.scaledFrame(size: clusterSize, alignment: .bottomLeading)
|
||||||
|
}
|
||||||
|
.scaledFrame(size: clusterSize)
|
||||||
|
} else {
|
||||||
|
LoadableAvatarImage(url: users[0].avatarURL,
|
||||||
|
name: users[0].displayName,
|
||||||
|
contentID: users[0].userID,
|
||||||
|
avatarSize: avatarSize,
|
||||||
|
imageProvider: imageProvider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RoomAvatarImage_Previews: PreviewProvider, TestablePreview {
|
||||||
|
static var previews: some View {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
RoomAvatarImage(avatar: .room(id: "!1:server.com",
|
||||||
|
name: "Room",
|
||||||
|
avatarURL: nil),
|
||||||
|
avatarSize: .room(on: .home),
|
||||||
|
imageProvider: MockMediaProvider())
|
||||||
|
|
||||||
|
RoomAvatarImage(avatar: .room(id: "!2:server.com",
|
||||||
|
name: "Room",
|
||||||
|
avatarURL: .picturesDirectory),
|
||||||
|
avatarSize: .room(on: .home),
|
||||||
|
imageProvider: MockMediaProvider())
|
||||||
|
|
||||||
|
RoomAvatarImage(avatar: .heroes([.init(userID: "@user:server.com",
|
||||||
|
displayName: "User",
|
||||||
|
avatarURL: nil)]),
|
||||||
|
avatarSize: .room(on: .home),
|
||||||
|
imageProvider: MockMediaProvider())
|
||||||
|
|
||||||
|
RoomAvatarImage(avatar: .heroes([.init(userID: "@user:server.com",
|
||||||
|
displayName: "User",
|
||||||
|
avatarURL: .picturesDirectory)]),
|
||||||
|
avatarSize: .room(on: .home),
|
||||||
|
imageProvider: MockMediaProvider())
|
||||||
|
|
||||||
|
RoomAvatarImage(avatar: .heroes([.init(userID: "@alice:server.com", displayName: "Alice", avatarURL: nil),
|
||||||
|
.init(userID: "@bob:server.net", displayName: "Bob", avatarURL: nil)]),
|
||||||
|
avatarSize: .room(on: .home),
|
||||||
|
imageProvider: MockMediaProvider())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,5 +41,5 @@ struct GlobalSearchRoom: Identifiable, Equatable {
|
|||||||
let id: String
|
let id: String
|
||||||
let name: String
|
let name: String
|
||||||
let alias: String?
|
let alias: String?
|
||||||
let avatarURL: URL?
|
let avatar: RoomAvatar
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
|
|||||||
return GlobalSearchRoom(id: details.id,
|
return GlobalSearchRoom(id: details.id,
|
||||||
name: details.name,
|
name: details.name,
|
||||||
alias: details.canonicalAlias,
|
alias: details.canonicalAlias,
|
||||||
avatarURL: details.avatarURL)
|
avatar: details.avatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,9 @@ struct GlobalSearchScreenListRow: View {
|
|||||||
@ViewBuilder @MainActor
|
@ViewBuilder @MainActor
|
||||||
var avatar: some View {
|
var avatar: some View {
|
||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
LoadableAvatarImage(url: room.avatarURL,
|
RoomAvatarImage(avatar: room.avatar,
|
||||||
name: room.name,
|
avatarSize: .room(on: .messageForwarding),
|
||||||
contentID: room.id,
|
imageProvider: context.imageProvider)
|
||||||
avatarSize: .room(on: .messageForwarding),
|
|
||||||
imageProvider: context.imageProvider)
|
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
@@ -55,7 +53,9 @@ struct GlobalSearchScreenListRow_Previews: PreviewProvider, TestablePreview {
|
|||||||
GlobalSearchScreenListRow(room: .init(id: "123",
|
GlobalSearchScreenListRow(room: .init(id: "123",
|
||||||
name: "Tech central",
|
name: "Tech central",
|
||||||
alias: "The best place in the whole wide world",
|
alias: "The best place in the whole wide world",
|
||||||
avatarURL: .picturesDirectory),
|
avatar: .room(id: "123",
|
||||||
|
name: "Tech central",
|
||||||
|
avatarURL: .picturesDirectory)),
|
||||||
context: viewModel.context)
|
context: viewModel.context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
|||||||
let id: String
|
let id: String
|
||||||
|
|
||||||
/// The real room identifier this item points to
|
/// The real room identifier this item points to
|
||||||
let roomId: String?
|
let roomID: String?
|
||||||
|
|
||||||
let type: RoomType
|
let type: RoomType
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
|||||||
|
|
||||||
let lastMessage: AttributedString?
|
let lastMessage: AttributedString?
|
||||||
|
|
||||||
let avatarURL: URL?
|
let avatar: RoomAvatar
|
||||||
|
|
||||||
let inviter: InviterDetails?
|
let inviter: InviterDetails?
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
|||||||
|
|
||||||
static func placeholder() -> HomeScreenRoom {
|
static func placeholder() -> HomeScreenRoom {
|
||||||
HomeScreenRoom(id: UUID().uuidString,
|
HomeScreenRoom(id: UUID().uuidString,
|
||||||
roomId: nil,
|
roomID: nil,
|
||||||
type: .placeholder,
|
type: .placeholder,
|
||||||
badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, isCallShown: false),
|
badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, isCallShown: false),
|
||||||
name: "Placeholder room name",
|
name: "Placeholder room name",
|
||||||
@@ -198,7 +198,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
|||||||
isFavourite: false,
|
isFavourite: false,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
lastMessage: placeholderLastMessage,
|
lastMessage: placeholderLastMessage,
|
||||||
avatarURL: nil,
|
avatar: .room(id: "", name: "", avatarURL: nil),
|
||||||
inviter: nil,
|
inviter: nil,
|
||||||
canonicalAlias: nil)
|
canonicalAlias: nil)
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ extension HomeScreenRoom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.init(id: identifier,
|
self.init(id: identifier,
|
||||||
roomId: details.id,
|
roomID: details.id,
|
||||||
type: details.isInvite ? .invite : .room,
|
type: details.isInvite ? .invite : .room,
|
||||||
badges: .init(isDotShown: isDotShown,
|
badges: .init(isDotShown: isDotShown,
|
||||||
isMentionShown: isMentionShown,
|
isMentionShown: isMentionShown,
|
||||||
@@ -236,7 +236,7 @@ extension HomeScreenRoom {
|
|||||||
isFavourite: details.isFavourite,
|
isFavourite: details.isFavourite,
|
||||||
timestamp: details.lastMessageFormattedTimestamp,
|
timestamp: details.lastMessageFormattedTimestamp,
|
||||||
lastMessage: details.lastMessage,
|
lastMessage: details.lastMessage,
|
||||||
avatarURL: details.avatarURL,
|
avatar: details.avatar,
|
||||||
inviter: inviter,
|
inviter: inviter,
|
||||||
canonicalAlias: details.canonicalAlias)
|
canonicalAlias: details.canonicalAlias)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
|
import Compound
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -27,11 +28,9 @@ struct HomeScreenInviteCell: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .top, spacing: 16) {
|
HStack(alignment: .top, spacing: 16) {
|
||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
LoadableAvatarImage(url: room.avatarURL,
|
RoomAvatarImage(avatar: room.avatar,
|
||||||
name: title,
|
avatarSize: .custom(52),
|
||||||
contentID: room.id,
|
imageProvider: context.imageProvider)
|
||||||
avatarSize: .custom(52),
|
|
||||||
imageProvider: context.imageProvider)
|
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
@@ -48,8 +47,8 @@ struct HomeScreenInviteCell: View {
|
|||||||
.padding(.top, 12)
|
.padding(.top, 12)
|
||||||
.padding(.leading, 16)
|
.padding(.leading, 16)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if let roomId = room.roomId {
|
if let roomID = room.roomID {
|
||||||
context.send(viewAction: .selectRoom(roomIdentifier: roomId))
|
context.send(viewAction: .selectRoom(roomIdentifier: roomID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,6 +213,7 @@ private extension HomeScreenRoom {
|
|||||||
name: "Some Guy",
|
name: "Some Guy",
|
||||||
isDirect: true,
|
isDirect: true,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [.init(userID: "@someone:somewhere.com")],
|
||||||
lastMessage: nil,
|
lastMessage: nil,
|
||||||
lastMessageFormattedTimestamp: nil,
|
lastMessageFormattedTimestamp: nil,
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
@@ -240,6 +240,7 @@ private extension HomeScreenRoom {
|
|||||||
name: "Awesome Room",
|
name: "Awesome Room",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: avatarURL,
|
avatarURL: avatarURL,
|
||||||
|
heroes: [.init(userID: "@someone:somewhere.com")],
|
||||||
lastMessage: nil,
|
lastMessage: nil,
|
||||||
lastMessageFormattedTimestamp: nil,
|
lastMessageFormattedTimestamp: nil,
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ struct HomeScreenRoomCell: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button {
|
Button {
|
||||||
if let roomId = room.roomId {
|
if let roomID = room.roomID {
|
||||||
context.send(viewAction: .selectRoom(roomIdentifier: roomId))
|
context.send(viewAction: .selectRoom(roomIdentifier: roomID))
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack(spacing: 16.0) {
|
HStack(spacing: 16.0) {
|
||||||
@@ -57,11 +57,9 @@ struct HomeScreenRoomCell: View {
|
|||||||
@ViewBuilder @MainActor
|
@ViewBuilder @MainActor
|
||||||
private var avatar: some View {
|
private var avatar: some View {
|
||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
LoadableAvatarImage(url: room.avatarURL,
|
RoomAvatarImage(avatar: room.avatar,
|
||||||
name: room.name,
|
avatarSize: .room(on: .home),
|
||||||
contentID: room.roomId,
|
imageProvider: context.imageProvider)
|
||||||
avatarSize: .room(on: .home),
|
|
||||||
imageProvider: context.imageProvider)
|
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ struct JoinRoomScreenViewState: BindableState {
|
|||||||
var mode: JoinRoomScreenInteractionMode = .loading
|
var mode: JoinRoomScreenInteractionMode = .loading
|
||||||
|
|
||||||
var bindings = JoinRoomScreenViewStateBindings()
|
var bindings = JoinRoomScreenViewStateBindings()
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
roomDetails?.name ?? L10n.screenJoinRoomTitleNoPreview
|
||||||
|
}
|
||||||
|
|
||||||
|
var avatar: RoomAvatar {
|
||||||
|
.room(id: roomID, name: title, avatarURL: roomDetails?.avatarURL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct JoinRoomScreenViewStateBindings {
|
struct JoinRoomScreenViewStateBindings {
|
||||||
|
|||||||
@@ -40,17 +40,13 @@ struct JoinRoomScreen: View {
|
|||||||
|
|
||||||
var mainContent: some View {
|
var mainContent: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
let title = context.viewState.roomDetails?.name ?? L10n.screenJoinRoomTitleNoPreview
|
RoomAvatarImage(avatar: context.viewState.avatar,
|
||||||
|
avatarSize: .room(on: .joinRoom),
|
||||||
LoadableAvatarImage(url: context.viewState.roomDetails?.avatarURL,
|
imageProvider: context.imageProvider)
|
||||||
name: title,
|
|
||||||
contentID: context.viewState.roomID,
|
|
||||||
avatarSize: .room(on: .joinRoom),
|
|
||||||
imageProvider: context.imageProvider)
|
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
|
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
Text(title)
|
Text(context.viewState.title)
|
||||||
.font(.compound.headingMDBold)
|
.font(.compound.headingMDBold)
|
||||||
.foregroundStyle(.compound.textPrimary)
|
.foregroundStyle(.compound.textPrimary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ struct MessageForwardingRoom: Identifiable, Equatable {
|
|||||||
let id: String
|
let id: String
|
||||||
let name: String
|
let name: String
|
||||||
let alias: String?
|
let alias: String?
|
||||||
let avatarURL: URL?
|
let avatar: RoomAvatar
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MessageForwardingItem: Hashable {
|
struct MessageForwardingItem: Hashable {
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let room = MessageForwardingRoom(id: details.id, name: details.name, alias: details.canonicalAlias, avatarURL: details.avatarURL)
|
let room = MessageForwardingRoom(id: details.id, name: details.name, alias: details.canonicalAlias, avatar: details.avatar)
|
||||||
rooms.append(room)
|
rooms.append(room)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,11 +88,9 @@ private struct MessageForwardingListRow: View {
|
|||||||
@ViewBuilder @MainActor
|
@ViewBuilder @MainActor
|
||||||
var avatar: some View {
|
var avatar: some View {
|
||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
LoadableAvatarImage(url: room.avatarURL,
|
RoomAvatarImage(avatar: room.avatar,
|
||||||
name: room.name,
|
avatarSize: .room(on: .messageForwarding),
|
||||||
contentID: room.id,
|
imageProvider: context.imageProvider)
|
||||||
avatarSize: .room(on: .messageForwarding),
|
|
||||||
imageProvider: context.imageProvider)
|
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,11 +44,9 @@ struct RoomDirectorySearchCell: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var avatar: some View {
|
private var avatar: some View {
|
||||||
LoadableAvatarImage(url: result.avatarURL,
|
RoomAvatarImage(avatar: result.avatar,
|
||||||
name: result.name,
|
avatarSize: .room(on: .roomDirectorySearch),
|
||||||
contentID: result.id,
|
imageProvider: imageProvider)
|
||||||
avatarSize: .room(on: .roomDirectorySearch),
|
|
||||||
imageProvider: imageProvider)
|
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,7 +60,9 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: "#test:example.com",
|
alias: "#test:example.com",
|
||||||
name: "Test title",
|
name: "Test title",
|
||||||
topic: "test description",
|
topic: "test description",
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_1:matrix.org",
|
||||||
|
name: "Test title",
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: true),
|
canBeJoined: true),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
|
|
||||||
@@ -70,7 +70,9 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: "#test:example.com",
|
alias: "#test:example.com",
|
||||||
name: nil,
|
name: nil,
|
||||||
topic: "test description",
|
topic: "test description",
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_2:matrix.org",
|
||||||
|
name: nil,
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: true),
|
canBeJoined: true),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
|
|
||||||
@@ -78,7 +80,9 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: "#test_no_topic:example.com",
|
alias: "#test_no_topic:example.com",
|
||||||
name: "Test title no topic",
|
name: "Test title no topic",
|
||||||
topic: nil,
|
topic: nil,
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_3:example.com",
|
||||||
|
name: "Test title no topic",
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: true),
|
canBeJoined: true),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
|
|
||||||
@@ -86,7 +90,9 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: "#test_no_topic:example.com",
|
alias: "#test_no_topic:example.com",
|
||||||
name: nil,
|
name: nil,
|
||||||
topic: nil,
|
topic: nil,
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_4:example.com",
|
||||||
|
name: nil,
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: true),
|
canBeJoined: true),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
|
|
||||||
@@ -94,7 +100,9 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: nil,
|
alias: nil,
|
||||||
name: "Test title no alias",
|
name: "Test title no alias",
|
||||||
topic: nil,
|
topic: nil,
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_5:example.com",
|
||||||
|
name: "Test title no alias",
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: false),
|
canBeJoined: false),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
|
|
||||||
@@ -102,7 +110,9 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: nil,
|
alias: nil,
|
||||||
name: "Test title no alias",
|
name: "Test title no alias",
|
||||||
topic: "Topic",
|
topic: "Topic",
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_6:example.com",
|
||||||
|
name: "Test title no alias",
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: false),
|
canBeJoined: false),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
|
|
||||||
@@ -110,15 +120,18 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: nil,
|
alias: nil,
|
||||||
name: nil,
|
name: nil,
|
||||||
topic: "Topic",
|
topic: "Topic",
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_7:example.com",
|
||||||
|
name: nil,
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: false),
|
canBeJoined: false),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
RoomDirectorySearchCell(result: .init(id: "!test_id_8:example.com",
|
RoomDirectorySearchCell(result: .init(id: "!test_id_8:example.com",
|
||||||
|
|
||||||
alias: nil,
|
alias: nil,
|
||||||
name: nil,
|
name: nil,
|
||||||
topic: nil,
|
topic: nil,
|
||||||
avatarURL: nil,
|
avatar: .room(id: "!test_id_8:example.com",
|
||||||
|
name: nil,
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: false),
|
canBeJoined: false),
|
||||||
imageProvider: MockMediaProvider()) { }
|
imageProvider: MockMediaProvider()) { }
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -83,13 +83,17 @@ struct RoomDirectorySearchScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
alias: "#test_1:example.com",
|
alias: "#test_1:example.com",
|
||||||
name: "Test 1",
|
name: "Test 1",
|
||||||
topic: "Test description 1",
|
topic: "Test description 1",
|
||||||
avatarURL: nil,
|
avatar: .room(id: "test_1",
|
||||||
|
name: "Test 1",
|
||||||
|
avatarURL: nil),
|
||||||
canBeJoined: true),
|
canBeJoined: true),
|
||||||
RoomDirectorySearchResult(id: "test_2",
|
RoomDirectorySearchResult(id: "test_2",
|
||||||
alias: "#test_2:example.com",
|
alias: "#test_2:example.com",
|
||||||
name: "Test 2",
|
name: "Test 2",
|
||||||
topic: nil,
|
topic: nil,
|
||||||
avatarURL: URL.documentsDirectory,
|
avatar: .room(id: "test_2",
|
||||||
|
name: "Test 2",
|
||||||
|
avatarURL: .documentsDirectory),
|
||||||
canBeJoined: false)]
|
canBeJoined: false)]
|
||||||
|
|
||||||
let roomDirectorySearchProxy = RoomDirectorySearchProxyMock(configuration: .init(results: results))
|
let roomDirectorySearchProxy = RoomDirectorySearchProxyMock(configuration: .init(results: results))
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ enum RoomScreenComposerAction {
|
|||||||
struct RoomScreenViewState: BindableState {
|
struct RoomScreenViewState: BindableState {
|
||||||
var roomID: String
|
var roomID: String
|
||||||
var roomTitle = ""
|
var roomTitle = ""
|
||||||
var roomAvatarURL: URL?
|
var roomAvatar: RoomAvatar
|
||||||
var members: [String: RoomMemberState] = [:]
|
var members: [String: RoomMemberState] = [:]
|
||||||
var typingMembers: [String] = []
|
var typingMembers: [String] = []
|
||||||
var showLoading = false
|
var showLoading = false
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
analyticsService: analyticsService)
|
analyticsService: analyticsService)
|
||||||
|
|
||||||
super.init(initialViewState: RoomScreenViewState(roomID: timelineController.roomID,
|
super.init(initialViewState: RoomScreenViewState(roomID: roomProxy.id,
|
||||||
roomTitle: roomProxy.roomTitle,
|
roomTitle: roomProxy.roomTitle,
|
||||||
roomAvatarURL: roomProxy.avatarURL,
|
roomAvatar: roomProxy.avatar,
|
||||||
timelineStyle: appSettings.timelineStyle,
|
timelineStyle: appSettings.timelineStyle,
|
||||||
isEncryptedOneToOneRoom: roomProxy.isEncryptedOneToOneRoom,
|
isEncryptedOneToOneRoom: roomProxy.isEncryptedOneToOneRoom,
|
||||||
timelineViewState: TimelineViewState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }),
|
timelineViewState: TimelineViewState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }),
|
||||||
@@ -389,9 +389,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
.throttle(for: .seconds(1), scheduler: DispatchQueue.main, latest: true)
|
.throttle(for: .seconds(1), scheduler: DispatchQueue.main, latest: true)
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.state.roomTitle = roomProxy.roomTitle
|
state.roomTitle = roomProxy.roomTitle
|
||||||
self.state.roomAvatarURL = roomProxy.avatarURL
|
state.roomAvatar = roomProxy.avatar
|
||||||
self.state.hasOngoingCall = roomProxy.hasOngoingCall
|
state.hasOngoingCall = roomProxy.hasOngoingCall
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
|||||||
@@ -15,19 +15,18 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Compound
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct RoomHeaderView: View {
|
struct RoomHeaderView: View {
|
||||||
let roomID: String
|
|
||||||
let roomName: String
|
let roomName: String
|
||||||
let avatarURL: URL?
|
let roomAvatar: RoomAvatar
|
||||||
|
|
||||||
let imageProvider: ImageProviderProtocol?
|
let imageProvider: ImageProviderProtocol?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
roomAvatar
|
avatarImage
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
Text(roomName)
|
Text(roomName)
|
||||||
.font(.compound.bodyLGSemibold)
|
.font(.compound.bodyLGSemibold)
|
||||||
@@ -37,28 +36,28 @@ struct RoomHeaderView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var roomAvatar: some View {
|
private var avatarImage: some View {
|
||||||
LoadableAvatarImage(url: avatarURL,
|
RoomAvatarImage(avatar: roomAvatar,
|
||||||
name: roomName,
|
avatarSize: .room(on: .timeline),
|
||||||
contentID: roomID,
|
imageProvider: imageProvider)
|
||||||
avatarSize: .room(on: .timeline),
|
|
||||||
imageProvider: imageProvider)
|
|
||||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.avatar)
|
.accessibilityIdentifier(A11yIdentifiers.roomScreen.avatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
|
struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
RoomHeaderView(roomID: "1",
|
RoomHeaderView(roomName: "Some Room name",
|
||||||
roomName: "Some Room name",
|
roomAvatar: .room(id: "1",
|
||||||
avatarURL: URL.picturesDirectory,
|
name: "Some Room Name",
|
||||||
|
avatarURL: URL.picturesDirectory),
|
||||||
imageProvider: MockMediaProvider())
|
imageProvider: MockMediaProvider())
|
||||||
.previewLayout(.sizeThatFits)
|
.previewLayout(.sizeThatFits)
|
||||||
.padding()
|
.padding()
|
||||||
|
|
||||||
RoomHeaderView(roomID: "1",
|
RoomHeaderView(roomName: "Some Room name",
|
||||||
roomName: "Some Room name",
|
roomAvatar: .room(id: "1",
|
||||||
avatarURL: nil,
|
name: "Some Room Name",
|
||||||
|
avatarURL: nil),
|
||||||
imageProvider: MockMediaProvider())
|
imageProvider: MockMediaProvider())
|
||||||
.previewLayout(.sizeThatFits)
|
.previewLayout(.sizeThatFits)
|
||||||
.padding()
|
.padding()
|
||||||
|
|||||||
@@ -137,9 +137,8 @@ struct RoomScreen: View {
|
|||||||
// .principal + .primaryAction works better than .navigation leading + trailing
|
// .principal + .primaryAction works better than .navigation leading + trailing
|
||||||
// as the latter disables interaction in the action button for rooms with long names
|
// as the latter disables interaction in the action button for rooms with long names
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
RoomHeaderView(roomID: context.viewState.roomID,
|
RoomHeaderView(roomName: context.viewState.roomTitle,
|
||||||
roomName: context.viewState.roomTitle,
|
roomAvatar: context.viewState.roomAvatar,
|
||||||
avatarURL: context.viewState.roomAvatarURL,
|
|
||||||
imageProvider: context.imageProvider)
|
imageProvider: context.imageProvider)
|
||||||
// Using a button stops it from getting truncated in the navigation bar
|
// Using a button stops it from getting truncated in the navigation bar
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
@@ -184,7 +183,9 @@ struct RoomScreen: View {
|
|||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct RoomScreen_Previews: PreviewProvider, TestablePreview {
|
struct RoomScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = RoomScreenViewModel(roomProxy: RoomProxyMock(.init(name: "Preview room", hasOngoingCall: true)),
|
static let viewModel = RoomScreenViewModel(roomProxy: RoomProxyMock(.init(id: "stable_id",
|
||||||
|
name: "Preview room",
|
||||||
|
hasOngoingCall: true)),
|
||||||
timelineController: MockRoomTimelineController(),
|
timelineController: MockRoomTimelineController(),
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ struct TimelineView: UIViewControllerRepresentable {
|
|||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = RoomScreenViewModel(roomProxy: RoomProxyMock(.init(name: "Preview room")),
|
static let viewModel = RoomScreenViewModel(roomProxy: RoomProxyMock(.init(id: "stable_id",
|
||||||
|
name: "Preview room")),
|
||||||
timelineController: MockRoomTimelineController(),
|
timelineController: MockRoomTimelineController(),
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
|
|||||||
+1
-1
@@ -110,7 +110,7 @@ struct NotificationSettingsEditScreenRoom: Identifiable, Equatable {
|
|||||||
|
|
||||||
var name = ""
|
var name = ""
|
||||||
|
|
||||||
var avatarURL: URL?
|
var avatar: RoomAvatar
|
||||||
|
|
||||||
var notificationMode: RoomNotificationModeProxy?
|
var notificationMode: RoomNotificationModeProxy?
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -168,7 +168,7 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie
|
|||||||
return NotificationSettingsEditScreenRoom(id: details.id,
|
return NotificationSettingsEditScreenRoom(id: details.id,
|
||||||
roomId: details.id,
|
roomId: details.id,
|
||||||
name: details.name,
|
name: details.name,
|
||||||
avatarURL: details.avatarURL,
|
avatar: details.avatar,
|
||||||
notificationMode: notificationMode)
|
notificationMode: notificationMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-6
@@ -39,11 +39,9 @@ struct NotificationSettingsEditScreenRoomCell: View {
|
|||||||
@ViewBuilder @MainActor
|
@ViewBuilder @MainActor
|
||||||
var avatar: some View {
|
var avatar: some View {
|
||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
LoadableAvatarImage(url: room.avatarURL,
|
RoomAvatarImage(avatar: room.avatar,
|
||||||
name: room.name,
|
avatarSize: .room(on: .notificationSettings),
|
||||||
contentID: room.roomId,
|
imageProvider: context.imageProvider)
|
||||||
avatarSize: .room(on: .notificationSettings),
|
|
||||||
imageProvider: context.imageProvider)
|
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
@@ -76,7 +74,8 @@ struct NotificationSettingsEditScreenRoomCell_Previews: PreviewProvider, Testabl
|
|||||||
case .filled(let details):
|
case .filled(let details):
|
||||||
return NotificationSettingsEditScreenRoom(id: UUID().uuidString,
|
return NotificationSettingsEditScreenRoom(id: UUID().uuidString,
|
||||||
roomId: details.id,
|
roomId: details.id,
|
||||||
name: details.name)
|
name: details.name,
|
||||||
|
avatar: details.avatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import Foundation
|
|||||||
struct RoomDetails {
|
struct RoomDetails {
|
||||||
let id: String
|
let id: String
|
||||||
let name: String?
|
let name: String?
|
||||||
let avatarURL: URL?
|
let avatar: RoomAvatar
|
||||||
let canonicalAlias: String?
|
let canonicalAlias: String?
|
||||||
let isEncrypted: Bool
|
let isEncrypted: Bool
|
||||||
let isPublic: Bool
|
let isPublic: Bool
|
||||||
|
|||||||
@@ -101,6 +101,18 @@ class RoomProxy: RoomProxyProtocol {
|
|||||||
roomListItem.avatarUrl().flatMap(URL.init(string:))
|
roomListItem.avatarUrl().flatMap(URL.init(string:))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var avatar: RoomAvatar {
|
||||||
|
if isDirect, avatarURL == nil {
|
||||||
|
let heroes = room.heroes()
|
||||||
|
|
||||||
|
if heroes.count == 1 {
|
||||||
|
return .heroes(heroes.map(UserProfileProxy.init))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .room(id: id, name: name, avatarURL: avatarURL)
|
||||||
|
}
|
||||||
|
|
||||||
var joinedMembersCount: Int {
|
var joinedMembersCount: Int {
|
||||||
Int(room.joinedMembersCount())
|
Int(room.joinedMembersCount())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ protocol RoomProxyProtocol {
|
|||||||
|
|
||||||
var topic: String? { get }
|
var topic: String? { get }
|
||||||
|
|
||||||
|
/// The room's avatar info for use in a ``RoomAvatarImage``.
|
||||||
|
var avatar: RoomAvatar { get }
|
||||||
|
/// The room's avatar URL. Use this for editing and favour ``avatar`` for display.
|
||||||
var avatarURL: URL? { get }
|
var avatarURL: URL? { get }
|
||||||
|
|
||||||
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get }
|
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get }
|
||||||
@@ -146,7 +149,7 @@ extension RoomProxyProtocol {
|
|||||||
var details: RoomDetails {
|
var details: RoomDetails {
|
||||||
RoomDetails(id: id,
|
RoomDetails(id: id,
|
||||||
name: name,
|
name: name,
|
||||||
avatarURL: avatarURL,
|
avatar: avatar,
|
||||||
canonicalAlias: canonicalAlias,
|
canonicalAlias: canonicalAlias,
|
||||||
isEncrypted: isEncrypted,
|
isEncrypted: isEncrypted,
|
||||||
isPublic: isPublic)
|
isPublic: isPublic)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ struct RoomSummaryDetails {
|
|||||||
let name: String
|
let name: String
|
||||||
let isDirect: Bool
|
let isDirect: Bool
|
||||||
let avatarURL: URL?
|
let avatarURL: URL?
|
||||||
|
let heroes: [UserProfileProxy]
|
||||||
let lastMessage: AttributedString?
|
let lastMessage: AttributedString?
|
||||||
let lastMessageFormattedTimestamp: String?
|
let lastMessageFormattedTimestamp: String?
|
||||||
let unreadMessagesCount: UInt
|
let unreadMessagesCount: UInt
|
||||||
@@ -64,6 +65,7 @@ extension RoomSummaryDetails {
|
|||||||
name = string
|
name = string
|
||||||
isDirect = true
|
isDirect = true
|
||||||
avatarURL = nil
|
avatarURL = nil
|
||||||
|
heroes = []
|
||||||
lastMessage = AttributedString(string)
|
lastMessage = AttributedString(string)
|
||||||
lastMessageFormattedTimestamp = "Now"
|
lastMessageFormattedTimestamp = "Now"
|
||||||
unreadMessagesCount = hasUnreadMessages ? 1 : 0
|
unreadMessagesCount = hasUnreadMessages ? 1 : 0
|
||||||
@@ -78,4 +80,12 @@ extension RoomSummaryDetails {
|
|||||||
isMarkedUnread = false
|
isMarkedUnread = false
|
||||||
isFavourite = false
|
isFavourite = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var avatar: RoomAvatar {
|
||||||
|
if isDirect, avatarURL == nil, heroes.count == 1 {
|
||||||
|
.heroes(heroes)
|
||||||
|
} else {
|
||||||
|
.room(id: id, name: name, avatarURL: avatarURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
|||||||
name: roomInfo.displayName ?? roomInfo.id,
|
name: roomInfo.displayName ?? roomInfo.id,
|
||||||
isDirect: roomInfo.isDirect,
|
isDirect: roomInfo.isDirect,
|
||||||
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
|
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
|
||||||
|
heroes: roomInfo.heroes.map(UserProfileProxy.init),
|
||||||
lastMessage: attributedLastMessage,
|
lastMessage: attributedLastMessage,
|
||||||
lastMessageFormattedTimestamp: lastMessageFormattedTimestamp,
|
lastMessageFormattedTimestamp: lastMessageFormattedTimestamp,
|
||||||
unreadMessagesCount: UInt(roomInfo.numUnreadMessages),
|
unreadMessagesCount: UInt(roomInfo.numUnreadMessages),
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ final class RoomDirectorySearchProxy: RoomDirectorySearchProxyProtocol {
|
|||||||
alias: value.alias,
|
alias: value.alias,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
topic: value.topic,
|
topic: value.topic,
|
||||||
avatarURL: value.avatarUrl.flatMap(URL.init(string:)),
|
avatar: .room(id: value.roomId, name: value.name, avatarURL: value.avatarUrl.flatMap(URL.init(string:))),
|
||||||
canBeJoined: value.joinRule == .public)
|
canBeJoined: value.joinRule == .public)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -34,6 +34,6 @@ struct RoomDirectorySearchResult: Identifiable {
|
|||||||
let alias: String?
|
let alias: String?
|
||||||
let name: String?
|
let name: String?
|
||||||
let topic: String?
|
let topic: String?
|
||||||
let avatarURL: URL?
|
let avatar: RoomAvatar
|
||||||
let canBeJoined: Bool
|
let canBeJoined: Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,24 @@ struct UserProfileProxy: Equatable, Hashable {
|
|||||||
self.avatarURL = avatarURL
|
self.avatarURL = avatarURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(member: RoomMemberDetails) {
|
||||||
|
userID = member.id
|
||||||
|
displayName = member.isBanned ? nil : member.name
|
||||||
|
avatarURL = member.isBanned ? nil : member.avatarURL
|
||||||
|
}
|
||||||
|
|
||||||
init(sdkUserProfile: MatrixRustSDK.UserProfile) {
|
init(sdkUserProfile: MatrixRustSDK.UserProfile) {
|
||||||
userID = sdkUserProfile.userId
|
userID = sdkUserProfile.userId
|
||||||
displayName = sdkUserProfile.displayName
|
displayName = sdkUserProfile.displayName
|
||||||
avatarURL = sdkUserProfile.avatarUrl.flatMap(URL.init(string:))
|
avatarURL = sdkUserProfile.avatarUrl.flatMap(URL.init(string:))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(sdkRoomHero: MatrixRustSDK.RoomHero) {
|
||||||
|
userID = sdkRoomHero.userId
|
||||||
|
displayName = sdkRoomHero.displayName
|
||||||
|
avatarURL = sdkRoomHero.avatarUrl.flatMap(URL.init(string:))
|
||||||
|
}
|
||||||
|
|
||||||
/// A user is meant to be "verified" when the GET profile returns back either the display name or the avatar
|
/// A user is meant to be "verified" when the GET profile returns back either the display name or the avatar
|
||||||
/// If isn't we aren't sure that the related matrix id really exists.
|
/// If isn't we aren't sure that the related matrix id really exists.
|
||||||
var isVerified: Bool {
|
var isVerified: Bool {
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:071913940bdcbe70a375842502c5c5efff0ff5c2725ea552ac2c1f8d491a83f5
|
oid sha256:cc367376247f9fe140c23b8c0c72565c77f1786012bf9b0dd77e4d2ded78cb4a
|
||||||
size 159526
|
size 163975
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:071913940bdcbe70a375842502c5c5efff0ff5c2725ea552ac2c1f8d491a83f5
|
oid sha256:cc367376247f9fe140c23b8c0c72565c77f1786012bf9b0dd77e4d2ded78cb4a
|
||||||
size 159526
|
size 163975
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:381b73e3230cfb17b25dee9b8355d9118861c7018e57dfad7760e8290066411d
|
oid sha256:40bd6face9670a1b109a37d9c9678153b9706b97030317c4a42861d209b39d93
|
||||||
size 107842
|
size 111737
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:381b73e3230cfb17b25dee9b8355d9118861c7018e57dfad7760e8290066411d
|
oid sha256:40bd6face9670a1b109a37d9c9678153b9706b97030317c4a42861d209b39d93
|
||||||
size 107842
|
size 111737
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2ec69becf9b8ca97645ef74fa5219282f9d499ba222f4d0b0dba4c9172072569
|
||||||
|
size 94910
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2ec69becf9b8ca97645ef74fa5219282f9d499ba222f4d0b0dba4c9172072569
|
||||||
|
size 94910
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e5c9296206be65a3965ef5ad32a79dd19108e6ed844ba22264a175f5b282e461
|
||||||
|
size 53615
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e5c9296206be65a3965ef5ad32a79dd19108e6ed844ba22264a175f5b282e461
|
||||||
|
size 53615
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:681f42cd5c242b4564dc9fb97f47217eb05e02cb16b33ae0ea6675e4ba6dee80
|
oid sha256:7a5840ed0428865293d7aed63ffcb9ae9016e695b3469e96b15e7ba7560eb549
|
||||||
size 416815
|
size 416817
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:79cbf258b45844d0b29515aab55a3ac979f3aa6bdb7a21fef508c2025a428258
|
oid sha256:565f5550e758f9bc8cbd83cfd88fef7395a3948c15b3d125bcd915d116cc877d
|
||||||
size 427856
|
size 427888
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9a3471828f2b36c052dd0583628cb4cfaa0b3cc5cfc767bc0907f20b33d2b55b
|
oid sha256:6586a55668fd39a8eed3717061f6e881540551b0c31d65a7a6360e916cabff55
|
||||||
size 270815
|
size 270771
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:32dca0d76ee5447d16211aa51b7d963502cfe3a5eb49daa216d06e9022782e0e
|
oid sha256:28c6e573a78d26c5c664d0c0f96299fe1158256ca786509f7fd3def44e66caa0
|
||||||
size 278037
|
size 278016
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:681f42cd5c242b4564dc9fb97f47217eb05e02cb16b33ae0ea6675e4ba6dee80
|
oid sha256:7a5840ed0428865293d7aed63ffcb9ae9016e695b3469e96b15e7ba7560eb549
|
||||||
size 416815
|
size 416817
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:79cbf258b45844d0b29515aab55a3ac979f3aa6bdb7a21fef508c2025a428258
|
oid sha256:565f5550e758f9bc8cbd83cfd88fef7395a3948c15b3d125bcd915d116cc877d
|
||||||
size 427856
|
size 427888
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9a3471828f2b36c052dd0583628cb4cfaa0b3cc5cfc767bc0907f20b33d2b55b
|
oid sha256:6586a55668fd39a8eed3717061f6e881540551b0c31d65a7a6360e916cabff55
|
||||||
size 270815
|
size 270771
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:32dca0d76ee5447d16211aa51b7d963502cfe3a5eb49daa216d06e9022782e0e
|
oid sha256:28c6e573a78d26c5c664d0c0f96299fe1158256ca786509f7fd3def44e66caa0
|
||||||
size 278037
|
size 278016
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class HomeScreenRoomTests: XCTestCase {
|
|||||||
name: "Test room",
|
name: "Test room",
|
||||||
isDirect: false,
|
isDirect: false,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [],
|
||||||
lastMessage: nil,
|
lastMessage: nil,
|
||||||
lastMessageFormattedTimestamp: nil,
|
lastMessageFormattedTimestamp: nil,
|
||||||
unreadMessagesCount: unreadMessagesCount,
|
unreadMessagesCount: unreadMessagesCount,
|
||||||
|
|||||||
@@ -86,12 +86,14 @@ class LoggingTests: XCTestCase {
|
|||||||
// Given a room summary that contains sensitive information
|
// Given a room summary that contains sensitive information
|
||||||
let roomName = "Private Conversation"
|
let roomName = "Private Conversation"
|
||||||
let lastMessage = "Secret information"
|
let lastMessage = "Secret information"
|
||||||
|
let heroName = "Pseudonym"
|
||||||
let roomSummary = RoomSummaryDetails(id: "myroomid",
|
let roomSummary = RoomSummaryDetails(id: "myroomid",
|
||||||
isInvite: false,
|
isInvite: false,
|
||||||
inviter: nil,
|
inviter: nil,
|
||||||
name: roomName,
|
name: roomName,
|
||||||
isDirect: true,
|
isDirect: true,
|
||||||
avatarURL: nil,
|
avatarURL: nil,
|
||||||
|
heroes: [.init(userID: "", displayName: heroName)],
|
||||||
lastMessage: AttributedString(lastMessage),
|
lastMessage: AttributedString(lastMessage),
|
||||||
lastMessageFormattedTimestamp: "Now",
|
lastMessageFormattedTimestamp: "Now",
|
||||||
unreadMessagesCount: 0,
|
unreadMessagesCount: 0,
|
||||||
@@ -116,6 +118,7 @@ class LoggingTests: XCTestCase {
|
|||||||
XCTAssertTrue(content.contains(roomSummary.id))
|
XCTAssertTrue(content.contains(roomSummary.id))
|
||||||
XCTAssertFalse(content.contains(roomName))
|
XCTAssertFalse(content.contains(roomName))
|
||||||
XCTAssertFalse(content.contains(lastMessage))
|
XCTAssertFalse(content.contains(lastMessage))
|
||||||
|
XCTAssertFalse(content.contains(heroName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateTimelineContentIsRedacted() throws {
|
func validateTimelineContentIsRedacted() throws {
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ElementX
|
||||||
|
|
||||||
|
class RoomSummaryDetailsTests: XCTestCase {
|
||||||
|
// swiftlint:disable:next large_tuple
|
||||||
|
let roomDetails: (id: String, name: String, avatarURL: URL) = ("room_id", "Room Name", "mxc://hs.tld/room/avatar")
|
||||||
|
let heroes = [UserProfileProxy(userID: "hero_1", displayName: "Hero 1", avatarURL: "mxc://hs.tld/user/avatar")]
|
||||||
|
|
||||||
|
func testRoomAvatar() {
|
||||||
|
let details = makeDetails(isDirect: false, hasRoomAvatar: true)
|
||||||
|
|
||||||
|
switch details.avatar {
|
||||||
|
case .room(let id, let name, let avatarURL):
|
||||||
|
XCTAssertEqual(id, roomDetails.id)
|
||||||
|
XCTAssertEqual(name, roomDetails.name)
|
||||||
|
XCTAssertEqual(avatarURL, roomDetails.avatarURL)
|
||||||
|
case .heroes:
|
||||||
|
XCTFail("A room shouldn't use the heroes for its avatar.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDMAvatarSet() {
|
||||||
|
let details = makeDetails(isDirect: true, hasRoomAvatar: true)
|
||||||
|
|
||||||
|
switch details.avatar {
|
||||||
|
case .room(let id, let name, let avatarURL):
|
||||||
|
XCTAssertEqual(id, roomDetails.id)
|
||||||
|
XCTAssertEqual(name, roomDetails.name)
|
||||||
|
XCTAssertEqual(avatarURL, roomDetails.avatarURL)
|
||||||
|
case .heroes:
|
||||||
|
XCTFail("A DM with an avatar set shouldn't use the heroes instead.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDMAvatarNotSet() {
|
||||||
|
let details = makeDetails(isDirect: true, hasRoomAvatar: false)
|
||||||
|
|
||||||
|
switch details.avatar {
|
||||||
|
case .room:
|
||||||
|
XCTFail("A DM without an avatar should defer to the hero for the correct placeholder tint colour.")
|
||||||
|
case .heroes(let heroes):
|
||||||
|
XCTAssertEqual(heroes, self.heroes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
func makeDetails(isDirect: Bool, hasRoomAvatar: Bool) -> RoomSummaryDetails {
|
||||||
|
RoomSummaryDetails(id: roomDetails.id,
|
||||||
|
isInvite: false,
|
||||||
|
inviter: nil,
|
||||||
|
name: roomDetails.name,
|
||||||
|
isDirect: isDirect,
|
||||||
|
avatarURL: hasRoomAvatar ? roomDetails.avatarURL : nil,
|
||||||
|
heroes: heroes,
|
||||||
|
lastMessage: nil,
|
||||||
|
lastMessageFormattedTimestamp: nil,
|
||||||
|
unreadMessagesCount: 0,
|
||||||
|
unreadMentionsCount: 0,
|
||||||
|
unreadNotificationsCount: 0,
|
||||||
|
notificationMode: nil,
|
||||||
|
canonicalAlias: nil,
|
||||||
|
hasOngoingCall: false,
|
||||||
|
isMarkedUnread: false,
|
||||||
|
isFavourite: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user