From 998739308a3d1e5c7167b3b4ac83d03df95d4f08 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:32:39 +0100 Subject: [PATCH] DM Design Tweaks (#3693) * dm design tweaks * adding equatable conformance * topic will now be shown in DMs * code suggestion * updated details * better check * code improvement --- .../en-US.lproj/Localizable.strings | 2 + .../en.lproj/Localizable.strings | 2 + .../RoomFlowCoordinator.swift | 2 + ElementX/Sources/Generated/Strings.swift | 7 ++++ .../Sources/Mocks/JoinedRoomProxyMock.swift | 11 +++++- .../SwiftUI/Views/AvatarHeaderView.swift | 12 +++++- .../RoomDetailsScreenCoordinator.swift | 3 ++ .../RoomDetailsScreenModels.swift | 6 ++- .../RoomDetailsScreenViewModel.swift | 5 +++ .../View/RoomDetailsScreen.swift | 37 +++++++------------ .../Sources/Services/Room/RoomDetails.swift | 1 + .../Services/Room/RoomProxyProtocol.swift | 3 +- ...t_roomDetailsScreen-iPad-en-GB.DM-Room.png | 4 +- ..._roomDetailsScreen-iPad-pseudo.DM-Room.png | 4 +- ...mDetailsScreen-iPhone-16-en-GB.DM-Room.png | 4 +- ...DetailsScreen-iPhone-16-pseudo.DM-Room.png | 4 +- 16 files changed, 69 insertions(+), 38 deletions(-) diff --git a/ElementX/Resources/Localizations/en-US.lproj/Localizable.strings b/ElementX/Resources/Localizations/en-US.lproj/Localizable.strings index d78453774..9068cd764 100644 --- a/ElementX/Resources/Localizations/en-US.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en-US.lproj/Localizable.strings @@ -21,6 +21,7 @@ "a11y_user_menu" = "User menu"; "a11y_voice_message_record" = "Record voice message."; "a11y_voice_message_stop_recording" = "Stop recording"; +"a11y.view_details" = "View details"; "action_accept" = "Accept"; "action_add_caption" = "Add caption"; "action_add_to_timeline" = "Add to timeline"; @@ -440,6 +441,7 @@ "screen_room_single_knock_request_title" = "%1$@ wants to join this room"; "screen_room_single_knock_request_view_button_title" = "View"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_room_details_profile_row_title" = "Profile"; "screen_room_details_requests_to_join_title" = "Requests to join"; "screen_room_details_security_and_privacy_title" = "Security & privacy"; "screen_roomlist_knock_event_sent_description" = "Request to join sent"; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 0345d1665..4e17cf27c 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -21,6 +21,7 @@ "a11y_user_menu" = "User menu"; "a11y_voice_message_record" = "Record voice message."; "a11y_voice_message_stop_recording" = "Stop recording"; +"a11y.view_details" = "View details"; "action_accept" = "Accept"; "action_add_caption" = "Add caption"; "action_add_to_timeline" = "Add to timeline"; @@ -440,6 +441,7 @@ "screen_room_single_knock_request_title" = "%1$@ wants to join this room"; "screen_room_single_knock_request_view_button_title" = "View"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_room_details_profile_row_title" = "Profile"; "screen_room_details_requests_to_join_title" = "Requests to join"; "screen_room_details_security_and_privacy_title" = "Security & privacy"; "screen_roomlist_knock_event_sent_description" = "Request to join sent"; diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index cd215d786..e375b494d 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -861,6 +861,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { stateMachine.tryEvent(.presentMediaEventsTimeline) case .presentSecurityAndPrivacyScreen: stateMachine.tryEvent(.presentSecurityAndPrivacyScreen) + case .presentRecipientDetails(let userID): + stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID)) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index a1d25f774..4a1e21c42 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1896,6 +1896,8 @@ internal enum L10n { internal static var screenRoomDetailsNotificationTitle: String { return L10n.tr("Localizable", "screen_room_details_notification_title") } /// Pinned messages internal static var screenRoomDetailsPinnedEventsRowTitle: String { return L10n.tr("Localizable", "screen_room_details_pinned_events_row_title") } + /// Profile + internal static var screenRoomDetailsProfileRowTitle: String { return L10n.tr("Localizable", "screen_room_details_profile_row_title") } /// Requests to join internal static var screenRoomDetailsRequestsToJoinTitle: String { return L10n.tr("Localizable", "screen_room_details_requests_to_join_title") } /// Roles and permissions @@ -2781,6 +2783,11 @@ internal enum L10n { /// Check UnifiedPush internal static var troubleshootNotificationsTestUnifiedPushTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_unified_push_title") } + internal enum A11y { + /// View details + internal static var viewDetails: String { return L10n.tr("Localizable", "a11y.view_details") } + } + internal enum Common { /// Copied to clipboard internal static var copiedToClipboard: String { return L10n.tr("Localizable", "common.copied_to_clipboard") } diff --git a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift index c14d414e0..2cce09177 100644 --- a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift @@ -31,6 +31,7 @@ struct JoinedRoomProxyMockConfiguration { var timelineStartReached = false var members: [RoomMemberProxyMock] = .allMembers + var heroes: [RoomMemberProxyMock] = [] var knockRequestsState: KnockRequestsState = .loaded([]) var ownUserID = RoomMemberProxyMock.mockMe.userID var inviter: RoomMemberProxyProtocol? @@ -159,7 +160,7 @@ extension RoomInfo { isIgnored: $0.isIgnored, suggestedRoleForPowerLevel: $0.role, membershipChangeReason: $0.membershipChangeReason) }, - heroes: [], + heroes: configuration.heroes.map(RoomHero.init), activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count), invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count), joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count), @@ -178,3 +179,11 @@ extension RoomInfo { historyVisibility: .shared) } } + +private extension RoomHero { + init(from memberProxy: RoomMemberProxyMock) { + self.init(userId: memberProxy.userID, + displayName: memberProxy.displayName, + avatarUrl: memberProxy.avatarURL?.absoluteString) + } +} diff --git a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift index ec000d196..91103af0d 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift @@ -37,7 +37,14 @@ struct AvatarHeaderView: View { @ViewBuilder footer: @escaping () -> Footer) { avatarInfo = .room(room.avatar) title = room.name ?? room.id - subtitle = room.canonicalAlias + + if let roomAlias = room.canonicalAlias { + subtitle = roomAlias + } else if room.isDirect, case let .heroes(heroes) = room.avatar, heroes.count == 1 { + subtitle = heroes[0].userID + } else { + subtitle = nil + } self.avatarSize = avatarSize self.mediaProvider = mediaProvider @@ -193,7 +200,8 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview { avatarURL: .mockMXCAvatar), canonicalAlias: "#test:matrix.org", isEncrypted: true, - isPublic: true), + isPublic: true, + isDirect: false), avatarSize: .room(on: .details), mediaProvider: MediaProviderMock(configuration: .init())) { HStack(spacing: 32) { diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift index 7214d9eca..a57719477 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift @@ -22,6 +22,7 @@ struct RoomDetailsScreenCoordinatorParameters { enum RoomDetailsScreenCoordinatorAction { case leftRoom case presentRoomMembersList + case presentRecipientDetails(userID: String) case presentRoomDetailsEditScreen case presentNotificationSettingsScreen case presentInviteUsersScreen @@ -88,6 +89,8 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol { actionsSubject.send(.presentKnockingRequestsListScreen) case .displaySecurityAndPrivacy: actionsSubject.send(.presentSecurityAndPrivacyScreen) + case .requestRecipientDetailsPresentation(let userID): + actionsSubject.send(.presentRecipientDetails(userID: userID)) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift index 3ca5f751c..1b71fdc9a 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift @@ -12,9 +12,10 @@ import SwiftUI // MARK: View model -enum RoomDetailsScreenViewModelAction { +enum RoomDetailsScreenViewModelAction: Equatable { case requestNotificationSettingsPresentation case requestMemberDetailsPresentation + case requestRecipientDetailsPresentation(userID: String) case requestInvitePeoplePresentation case leftRoom case requestEditDetailsPresentation @@ -68,7 +69,7 @@ struct RoomDetailsScreenViewState: BindableState { } var hasTopicSection: Bool { - topic != nil || (canEdit && canEditRoomTopic) + topic != nil || canEditRoomTopic } var bindings: RoomDetailsScreenViewStateBindings @@ -198,6 +199,7 @@ enum RoomDetailsScreenViewAction { case ignoreConfirmed case unignoreConfirmed case processTapNotifications + case processTapRecipientProfile case processToggleMuteNotifications case displayAvatar(URL) case processTapPolls diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index 49886cfa3..baba3beb9 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -170,6 +170,11 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr actionsSubject.send(.displayKnockingRequests) case .processTapSecurityAndPrivacy: actionsSubject.send(.displaySecurityAndPrivacy) + case .processTapRecipientProfile: + guard let userID = dmRecipient?.userID else { + return + } + actionsSubject.send(.requestRecipientDetailsPresentation(userID: userID)) } } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index bf1a51f76..5d169bdd8 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -15,13 +15,7 @@ struct RoomDetailsScreen: View { var body: some View { Form { - if let recipient = context.viewState.dmRecipient, - let accountOwner = context.viewState.accountOwner { - dmHeaderSection(accountOwner: accountOwner, - recipient: recipient) - } else { - normalRoomHeaderSection - } + roomHeaderSection topicSection @@ -66,7 +60,7 @@ struct RoomDetailsScreen: View { // MARK: - Private - private var normalRoomHeaderSection: some View { + private var roomHeaderSection: some View { AvatarHeaderView(room: context.viewState.details, avatarSize: .room(on: .details), mediaProvider: context.mediaProvider) { url in @@ -79,19 +73,6 @@ struct RoomDetailsScreen: View { .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.avatar) } - private func dmHeaderSection(accountOwner: RoomMemberDetails, recipient: RoomMemberDetails) -> some View { - AvatarHeaderView(accountOwner: accountOwner, - dmRecipient: recipient, - mediaProvider: context.mediaProvider) { url in - context.send(viewAction: .displayAvatar(url)) - } footer: { - if !context.viewState.shortcuts.isEmpty { - headerSectionShortcuts - } - } - .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.dmAvatar) - } - @ViewBuilder private var headerSectionShortcuts: some View { HStack(spacing: 8) { @@ -204,6 +185,14 @@ struct RoomDetailsScreen: View { context.send(viewAction: .processTapSecurityAndPrivacy) }) } + + if context.viewState.dmRecipient != nil { + ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, + icon: \.userProfile), + kind: .navigationLink { + context.send(viewAction: .processTapRecipientProfile) + }) + } } } @@ -369,12 +358,12 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { ] let roomProxy = JoinedRoomProxyMock(.init(id: "dm_room_id", - name: "DM Room", + name: "Dan", topic: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", isDirect: true, isEncrypted: true, - canonicalAlias: "#alias:domain.com", - members: members)) + members: members, + heroes: [.mockDan])) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) return RoomDetailsScreenViewModel(roomProxy: roomProxy, diff --git a/ElementX/Sources/Services/Room/RoomDetails.swift b/ElementX/Sources/Services/Room/RoomDetails.swift index 6fb90b209..a716eaaa4 100644 --- a/ElementX/Sources/Services/Room/RoomDetails.swift +++ b/ElementX/Sources/Services/Room/RoomDetails.swift @@ -14,4 +14,5 @@ struct RoomDetails { let canonicalAlias: String? let isEncrypted: Bool let isPublic: Bool + let isDirect: Bool } diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index e16bd484f..60e86bb05 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -182,7 +182,8 @@ extension JoinedRoomProxyProtocol { avatar: infoPublisher.value.avatar, canonicalAlias: infoPublisher.value.canonicalAlias, isEncrypted: isEncrypted, - isPublic: infoPublisher.value.isPublic) + isPublic: infoPublisher.value.isPublic, + isDirect: infoPublisher.value.isDirect) } var isDirectOneToOneRoom: Bool { diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room.png index 2eb2a00b7..b7de326aa 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94c47ee747ccae418bfb137fa8d7c3dfe9ff0a213d750cfa6c4e7267921b9f73 -size 233395 +oid sha256:28d5a6a12d3a170ef8759cf99881ca3c43a4831b9095c291e30a8e3d066a518e +size 215398 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room.png index 6f1f08408..8a3cc14cb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f1fa21072fc9fb7e5246d47c0b1fe52fc06451616edb4f5a85b5f8110a5113a -size 238539 +oid sha256:491028dd2b652536ceceabee66fd8200fe547880f2f38cb972fe72c7785efee6 +size 220301 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room.png index af9e87b6d..b43c743f1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbad14cfc4ef11542ce1fcd25a63c4206326b9db1eb4ef4ecb4c9d07ae96ad3c -size 169529 +oid sha256:2a1d66895acf2a40a77eedf055f7aa32b3b9d4f6bc1392427d5d500e84382441 +size 156430 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room.png index 11cf088d6..7842f38bd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a84ba5be353189c201895cd8be4892d91a9c8da26145bcdaf20b0ae5c2939528 -size 181603 +oid sha256:a4438b51dbb054c7c46b0842956269f0ddfba6c96a2020fd55ad1a346c3cccfe +size 167028