From a4a857003cbf7e858745e44f1a2f1f178149beac Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 13 May 2026 17:43:58 +0200 Subject: [PATCH] use isDM from the SDK to determine if a spaceRoom, a joinedRoom or a notificationItem are from a dm room. --- .../Sources/Mocks/Generated/GeneratedMocks.swift | 15 +++++++++++++++ ElementX/Sources/Mocks/InvitedRoomProxyMock.swift | 2 +- ElementX/Sources/Mocks/JoinedRoomProxyMock.swift | 1 + .../Sources/Mocks/NotificationItemProxyMock.swift | 1 + .../Mocks/SDK/LeaveSpaceHandleSDKMock.swift | 5 +++-- .../Sources/Other/Extensions/ClientBuilder.swift | 1 + .../TimelineMediaPreviewViewModel.swift | 2 +- .../Screens/HomeScreen/HomeScreenViewModel.swift | 6 +++--- .../JoinRoomScreen/JoinRoomScreenViewModel.swift | 2 +- .../RoomDetailsScreenViewModel.swift | 10 +++++----- .../CompletionSuggestionService.swift | 2 +- .../Screens/RoomScreen/RoomScreenModels.swift | 2 +- .../Screens/RoomScreen/RoomScreenViewModel.swift | 7 ++++--- .../RoomScreen/View/RoomCallControlsToolbar.swift | 10 +++++----- .../ThreadTimelineScreenViewModel.swift | 2 +- .../Sources/Screens/Timeline/TimelineModels.swift | 2 +- .../Screens/Timeline/TimelineViewModel.swift | 3 ++- .../Style/TimelineItemBubbledStylerView.swift | 12 ++++++------ .../TimelineStartRoomTimelineView.swift | 2 +- .../Screens/Timeline/View/TimelineView.swift | 2 +- .../Sources/Services/Room/RoomInfoProxy.swift | 4 ++++ .../Services/Room/RoomInfoProxyProtocol.swift | 1 + .../Room/RoomMember/RoomMemberProxy.swift | 4 ++++ .../Room/RoomMember/RoomMemberProxyProtocol.swift | 2 ++ .../Sources/Services/Room/RoomProxyProtocol.swift | 4 ---- .../Services/Spaces/SpaceServiceRoom.swift | 4 ++++ .../TimelineController/TimelineController.swift | 2 +- .../NotificationItemProxy.swift | 8 ++++++++ .../NotificationItemProxyProtocol.swift | 8 ++------ 29 files changed, 81 insertions(+), 45 deletions(-) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 4d46d5124..c68ed7ecf 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -13383,6 +13383,11 @@ class NotificationItemProxyMock: NotificationItemProxyProtocol, @unchecked Senda set(value) { underlyingIsRoomPrivate = value } } var underlyingIsRoomPrivate: Bool! + var isDM: Bool { + get { return underlyingIsDM } + set(value) { underlyingIsDM = value } + } + var underlyingIsDM: Bool! var isNoisy: Bool { get { return underlyingIsNoisy } set(value) { underlyingIsNoisy = value } @@ -15349,6 +15354,11 @@ class RoomInfoProxyMock: RoomInfoProxyProtocol, @unchecked Sendable { set(value) { underlyingIsDirect = value } } var underlyingIsDirect: Bool! + var isDM: Bool { + get { return underlyingIsDM } + set(value) { underlyingIsDM = value } + } + var underlyingIsDM: Bool! var isSpace: Bool { get { return underlyingIsSpace } set(value) { underlyingIsSpace = value } @@ -15469,6 +15479,11 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol, @unchecked Sendable { set(value) { underlyingPowerLevel = value } } var underlyingPowerLevel: RoomPowerLevel! + var isServiceMember: Bool { + get { return underlyingIsServiceMember } + set(value) { underlyingIsServiceMember = value } + } + var underlyingIsServiceMember: Bool! } class RoomMembershipDetailsProxyMock: RoomMembershipDetailsProxyProtocol, @unchecked Sendable { diff --git a/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift b/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift index bca494dcc..b552d2a6d 100644 --- a/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift @@ -85,6 +85,6 @@ private extension RoomMember { isIgnored: proxy.isIgnored, suggestedRoleForPowerLevel: proxy.role.rustRole, membershipChangeReason: proxy.membershipChangeReason, - isServiceMember: false) + isServiceMember: proxy.isServiceMember) } } diff --git a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift index 30302bbdc..40594c3b0 100644 --- a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift @@ -182,6 +182,7 @@ extension RoomInfoProxyMock { activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count joinedMembersCount = configuration.members.filter { $0.membership == .join }.count + isDM = isDirect && activeMembersCount <= 2 highlightCount = 0 notificationCount = 0 cachedUserDefinedNotificationMode = .allMessages diff --git a/ElementX/Sources/Mocks/NotificationItemProxyMock.swift b/ElementX/Sources/Mocks/NotificationItemProxyMock.swift index e9f9384b4..45a280134 100644 --- a/ElementX/Sources/Mocks/NotificationItemProxyMock.swift +++ b/ElementX/Sources/Mocks/NotificationItemProxyMock.swift @@ -52,5 +52,6 @@ extension NotificationItemProxyMock { underlyingIsNoisy = configuration.isNoisy underlyingHasMention = configuration.hasMention threadRootEventID = configuration.threadRootEventID + underlyingIsDM = configuration.isRoomDirect && configuration.roomJoinedMembers <= 2 } } diff --git a/ElementX/Sources/Mocks/SDK/LeaveSpaceHandleSDKMock.swift b/ElementX/Sources/Mocks/SDK/LeaveSpaceHandleSDKMock.swift index a5fa71517..3001e3b61 100644 --- a/ElementX/Sources/Mocks/SDK/LeaveSpaceHandleSDKMock.swift +++ b/ElementX/Sources/Mocks/SDK/LeaveSpaceHandleSDKMock.swift @@ -151,7 +151,8 @@ private extension SpaceRoom { childrenCount: UInt64 = 0, membership: Membership? = .joined, heroes: [RoomHero]? = [], - via: [String] = []) { + via: [String] = [], + isDM: Bool? = false) { self.init(roomId: id, canonicalAlias: canonicalAlias, displayName: name, @@ -168,6 +169,6 @@ private extension SpaceRoom { state: membership, heroes: heroes, via: via, - isDm: false) + isDm: isDM) } } diff --git a/ElementX/Sources/Other/Extensions/ClientBuilder.swift b/ElementX/Sources/Other/Extensions/ClientBuilder.swift index f22837b29..fc6fa9aaa 100644 --- a/ElementX/Sources/Other/Extensions/ClientBuilder.swift +++ b/ElementX/Sources/Other/Extensions/ClientBuilder.swift @@ -29,6 +29,7 @@ extension ClientBuilder { timeout: requestTimeout, maxConcurrentRequests: nil, maxRetryTime: maxRequestRetryTime)) + .dmRoomDefinition(dmRoomDefinition: .twoMembers) builder = switch slidingSync { case .restored: builder diff --git a/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewViewModel.swift b/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewViewModel.swift index 74a297cc4..fb329eb00 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewViewModel.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewViewModel.swift @@ -156,7 +156,7 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType { canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers, canCurrentUserPin: timelineContext.viewState.canCurrentUserPin, pinnedEventIDs: timelineContext.viewState.pinnedEventIDs, - isDM: timelineContext.viewState.isDirectOneToOneRoom, + isDM: timelineContext.viewState.isDM, isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled, areThreadsEnabled: timelineContext.viewState.areThreadsEnabled, timelineKind: timelineContext.viewState.timelineKind, diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index b2ec28d41..1322fbe9b 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -404,12 +404,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol guard roomProxy.infoPublisher.value.joinedMembersCount > 1 else { state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomID, - isDM: roomProxy.isDirectOneToOneRoom, + isDM: roomProxy.infoPublisher.value.isDM, state: roomProxy.infoPublisher.value.isPrivate ?? true ? .empty : .public) return } - if !roomProxy.isDirectOneToOneRoom { + if !roomProxy.infoPublisher.value.isDM { if case let .success(ownMember) = await roomProxy.getMember(userID: roomProxy.ownUserID), ownMember.role.isOwner { await roomProxy.updateMembers() @@ -434,7 +434,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol } } - state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomID, isDM: roomProxy.isDirectOneToOneRoom, state: roomProxy.infoPublisher.value.isPrivate ?? true ? .private : .public) + state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomID, isDM: roomProxy.infoPublisher.value.isDM, state: roomProxy.infoPublisher.value.isPrivate ?? true ? .private : .public) } } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index f6c10078d..b4c337a14 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -228,7 +228,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo if case .space(let spaceServiceRoom) = source { switch spaceServiceRoom.state { case .invited: - state.mode = .invited(isDM: spaceServiceRoom.isDirect == true && spaceServiceRoom.joinedMembersCount == 1) + state.mode = .invited(isDM: spaceServiceRoom.isDM == true) case .knocked: state.mode = .knocked case .banned: diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index 5fe189b4d..9c8a8b6a3 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -172,12 +172,12 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr guard state.joinedMembersCount > 1 else { state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, - isDM: roomProxy.isDirectOneToOneRoom, + isDM: roomProxy.infoPublisher.value.isDM, state: roomProxy.infoPublisher.value.isPrivate ?? true ? .empty : .public) return } - if !roomProxy.isDirectOneToOneRoom, state.accountOwner?.role.isOwner == true { + if !roomProxy.infoPublisher.value.isDM, state.accountOwner?.role.isOwner == true { var isLastOwner = true for member in roomProxy.membersPublisher.value where member.userID != roomProxy.ownUserID && member.membership == .join { if member.role.isOwner { @@ -199,7 +199,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, - isDM: roomProxy.isDirectOneToOneRoom, + isDM: roomProxy.infoPublisher.value.isDM, state: roomProxy.infoPublisher.value.isPrivate ?? true ? .private : .public) } @@ -307,7 +307,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr self.state.accountOwner = .init(withProxy: accountOwner) } - guard roomProxy.isDirectOneToOneRoom else { + guard roomProxy.infoPublisher.value.isDM else { return } @@ -328,7 +328,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr return } - if roomProxy.isDirectOneToOneRoom { + if roomProxy.infoPublisher.value.isDM { if var dmRecipientInfo = state.dmRecipientInfo { if case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: dmRecipientInfo.member.id, fallBackToServer: true) { dmRecipientInfo.verificationState = userIdentity?.verificationState diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift index 2027c2367..a98bfcbeb 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift @@ -91,7 +91,7 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol { } if canMentionAllUsers, - !roomProxy.isDirectOneToOneRoom, + !roomProxy.infoPublisher.value.isDM, Self.shouldIncludeMember(userID: PillUtilities.atRoom, displayName: PillUtilities.everyone, searchText: suggestionTrigger.text) { membersSuggestion .insert(SuggestionItem(suggestionType: .allUsers(roomProxy.details.avatar), range: suggestionTrigger.range, rawSuggestionText: suggestionTrigger.text), at: 0) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index a427e83bc..328127a1b 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -70,7 +70,7 @@ struct RoomScreenViewState: BindableState { } /// Whether the current room is a DM - var isDirectOneToOneRoom: Bool + var isDM: Bool var roomThreadListEnabled = false var isKnockableRoom = false diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index aef76cd64..ad32d15a5 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -70,7 +70,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol let viewState = RoomScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayName ?? roomProxy.id, roomAvatar: roomProxy.infoPublisher.value.avatar, hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall, - isDirectOneToOneRoom: roomProxy.isDirectOneToOneRoom, + isDM: roomProxy.infoPublisher.value.isDM, hasSuccessor: roomProxy.infoPublisher.value.successor != nil, roomHistorySharingState: roomProxy.infoPublisher.value.historySharingState) super.init(initialViewState: appHooks.roomScreenHook.update(viewState), @@ -271,7 +271,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } private func updateVerificationBadge() async { - guard roomProxy.isDirectOneToOneRoom, + guard roomProxy.infoPublisher.value.isDM, let dmRecipient = roomProxy.membersPublisher.value.first(where: { $0.userID != roomProxy.ownUserID }), case let .success(userIdentity) = await clientProxy.userIdentity(for: dmRecipient.userID, fallBackToServer: true) else { state.dmRecipientVerificationState = .notVerified @@ -338,6 +338,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol state.hasOngoingCall = roomInfo.hasRoomCall state.activeRoomCallIntent = roomInfo.activeRoomCallIntent state.hasSuccessor = roomInfo.successor != nil + state.isDM = roomInfo.isDM let pinnedEventIDs = roomInfo.pinnedEventIDs // Only update the loading state of the banner @@ -345,7 +346,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count) } - switch (roomProxy.isDirectOneToOneRoom, roomInfo.joinRule) { + switch (roomInfo.isDM, roomInfo.joinRule) { case (false, .knock), (false, .knockRestricted): state.isKnockableRoom = true default: diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomCallControlsToolbar.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomCallControlsToolbar.swift index 1e4cef40a..a2fea76fd 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomCallControlsToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomCallControlsToolbar.swift @@ -22,7 +22,7 @@ struct RoomCallControlsToolbar: ToolbarContent { .disabled(!viewState.canJoinCall) } } else { - if viewState.isDirectOneToOneRoom { + if viewState.isDM { if viewState.roomThreadListEnabled { // If the developer mode room thread list option is enabled there // is not enough place for 2 calls buttons @@ -85,11 +85,11 @@ struct RoomCallControlsToolbar_Previews: PreviewProvider { Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: true)) { _ in } } } ElementNavigationStack { - Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDirectOneToOneRoom: true)) { _ in } } + Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDM: true)) { _ in } } } ElementNavigationStack { - Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDirectOneToOneRoom: true, roomThreadListEnabled: true)) { _ in } } + Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDM: true, roomThreadListEnabled: true)) { _ in } } } ElementNavigationStack { Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false)) { _ in } } @@ -106,12 +106,12 @@ struct RoomCallControlsToolbar_Previews: PreviewProvider { } private extension RoomScreenViewState { - static func mock(hasOngoingCall: Bool, isDirectOneToOneRoom: Bool = false, canJoinCall: Bool = true, activeRoomCallIntent: CallIntent? = nil, roomThreadListEnabled: Bool = false) -> RoomScreenViewState { + static func mock(hasOngoingCall: Bool, isDM: Bool = false, canJoinCall: Bool = true, activeRoomCallIntent: CallIntent? = nil, roomThreadListEnabled: Bool = false) -> RoomScreenViewState { RoomScreenViewState(roomAvatar: .room(id: "mock", name: "Mock Room", avatarURL: nil), canJoinCall: canJoinCall, hasOngoingCall: hasOngoingCall, activeRoomCallIntent: activeRoomCallIntent, - isDirectOneToOneRoom: isDirectOneToOneRoom, + isDM: isDM, roomThreadListEnabled: roomThreadListEnabled, hasSuccessor: false) } diff --git a/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift b/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift index c9904d621..2003c546f 100644 --- a/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift +++ b/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift @@ -84,7 +84,7 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi // MARK: - Private private func updateVerificationBadge() async { - guard roomProxy.isDirectOneToOneRoom, + guard roomProxy.infoPublisher.value.isDM, let dmRecipient = roomProxy.membersPublisher.value.first(where: { $0.userID != roomProxy.ownUserID }), case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: dmRecipient.userID, fallBackToServer: true) else { state.dmRecipientVerificationState = .notVerified diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index eff01e5de..3ede2b35e 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -103,7 +103,7 @@ struct TimelineViewState: BindableState { var typingMembers: [String] = [] var showLoading = false var showReadReceipts = false - var isDirectOneToOneRoom = false + var isDM = false var timelineState: TimelineState // check the doc before changing this var ownUserID: String diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index ce37d99d1..3b5eb7263 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -95,7 +95,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } super.init(initialViewState: TimelineViewState(timelineKind: timelineController.timelineKind, roomID: roomProxy.id, - isDirectOneToOneRoom: roomProxy.isDirectOneToOneRoom, + isDM: roomProxy.infoPublisher.value.isDM, timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }), ownUserID: roomProxy.ownUserID, hideTimelineMedia: hideTimelineMedia, @@ -420,6 +420,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) { state.pinnedEventIDs = roomInfo.pinnedEventIDs + state.isDM = roomInfo.isDM if let powerLevels = roomInfo.powerLevels { state.canCurrentUserSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage) diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index f40b19278..9aa0fb694 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -18,8 +18,8 @@ struct TimelineItemBubbledStylerView: View { let adjustedDeliveryStatus: TimelineItemDeliveryStatus? @ViewBuilder let content: () -> Content - private var isDirectOneToOneRoom: Bool { - context.viewState.isDirectOneToOneRoom + private var isDM: Bool { + context.viewState.isDM } private var isFocussed: Bool { @@ -40,14 +40,14 @@ struct TimelineItemBubbledStylerView: View { private let bubbleHorizontalPadding: CGFloat = 8 /// Additional padding applied to outgoing bubbles when the avatar is shown private var bubbleAvatarPadding: CGFloat { - guard !timelineItem.isOutgoing, !isDirectOneToOneRoom else { return 0 } + guard !timelineItem.isOutgoing, !isDM else { return 0 } return 8 } var body: some View { ZStack(alignment: .trailingFirstTextBaseline) { VStack(alignment: alignment, spacing: -12) { - if !timelineItem.isOutgoing, !isDirectOneToOneRoom { + if !timelineItem.isOutgoing, !isDM { header .zIndex(1) } @@ -164,7 +164,7 @@ struct TimelineItemBubbledStylerView: View { canCurrentUserRedactOthers: context.viewState.canCurrentUserRedactOthers, canCurrentUserPin: context.viewState.canCurrentUserPin, pinnedEventIDs: context.viewState.pinnedEventIDs, - isDM: context.viewState.isDirectOneToOneRoom, + isDM: context.viewState.isDM, isViewSourceEnabled: context.viewState.isViewSourceEnabled, areThreadsEnabled: context.viewState.areThreadsEnabled, timelineKind: context.viewState.timelineKind, @@ -218,7 +218,7 @@ struct TimelineItemBubbledStylerView: View { } private var messageBubbleTopPadding: CGFloat { - guard timelineItem.isOutgoing || isDirectOneToOneRoom else { return 0 } + guard timelineItem.isOutgoing || isDM else { return 0 } return timelineGroupStyle == .single || timelineGroupStyle == .first ? 8 : 0 } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TimelineStartRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TimelineStartRoomTimelineView.swift index 5426756ce..48ccab327 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TimelineStartRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TimelineStartRoomTimelineView.swift @@ -19,7 +19,7 @@ struct TimelineStartRoomTimelineView: View { upgradeDialogue } // We don't display the title for tombstoned DMs - if context?.viewState.isDirectOneToOneRoom != true { + if context?.viewState.isDM != true { Text(title) .font(.compound.bodySM) .foregroundColor(.compound.textSecondary) diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift index 6f5f7a8ab..994573250 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift @@ -34,7 +34,7 @@ struct TimelineView: View { canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers, canCurrentUserPin: timelineContext.viewState.canCurrentUserPin, pinnedEventIDs: timelineContext.viewState.pinnedEventIDs, - isDM: timelineContext.viewState.isDirectOneToOneRoom, + isDM: timelineContext.viewState.isDM, isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled, areThreadsEnabled: timelineContext.viewState.areThreadsEnabled, timelineKind: timelineContext.viewState.timelineKind, diff --git a/ElementX/Sources/Services/Room/RoomInfoProxy.swift b/ElementX/Sources/Services/Room/RoomInfoProxy.swift index f6ab6d825..80ebf17d6 100644 --- a/ElementX/Sources/Services/Room/RoomInfoProxy.swift +++ b/ElementX/Sources/Services/Room/RoomInfoProxy.swift @@ -49,6 +49,10 @@ struct RoomInfoProxy: RoomInfoProxyProtocol { var isDirect: Bool { roomInfo.isDirect } + + var isDM: Bool { + roomInfo.isDm + } var isSpace: Bool { roomInfo.isSpace diff --git a/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift index a329ba197..feed35012 100644 --- a/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift @@ -36,6 +36,7 @@ protocol RoomInfoProxyProtocol: BaseRoomInfoProxyProtocol { var isEncrypted: Bool { get } var isDirect: Bool { get } + var isDM: Bool { get } var isSpace: Bool { get } var isFavourite: Bool { get } diff --git a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift index 69f56644f..a3e2a6e11 100644 --- a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift +++ b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift @@ -51,4 +51,8 @@ final class RoomMemberProxy: RoomMemberProxyProtocol { var powerLevel: RoomPowerLevel { .init(rustPowerLevel: member.powerLevel) } + + var isServiceMember: Bool { + member.isServiceMember + } } diff --git a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift index acbb26038..59084c414 100644 --- a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift @@ -25,6 +25,8 @@ protocol RoomMemberProxyProtocol: AnyObject { var isIgnored: Bool { get } var powerLevel: RoomPowerLevel { get } + + var isServiceMember: Bool { get } } extension RoomMemberProxyProtocol { diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index 6d67a1f5f..47b010ff6 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -222,10 +222,6 @@ extension JoinedRoomProxyProtocol { isDirect: infoPublisher.value.isDirect, historySharingState: historySharingState) } - - var isDirectOneToOneRoom: Bool { - infoPublisher.value.isDirect && infoPublisher.value.activeMembersCount <= 2 - } func members() async -> [RoomMemberProxyProtocol]? { await updateMembers() diff --git a/ElementX/Sources/Services/Spaces/SpaceServiceRoom.swift b/ElementX/Sources/Services/Spaces/SpaceServiceRoom.swift index a01d6b685..37618a739 100644 --- a/ElementX/Sources/Services/Spaces/SpaceServiceRoom.swift +++ b/ElementX/Sources/Services/Spaces/SpaceServiceRoom.swift @@ -24,6 +24,7 @@ struct SpaceServiceRoom { var isSpace: Bool var isDirect: Bool? + var isDM: Bool? var childrenCount: Int var joinedMembersCount: Int @@ -70,6 +71,7 @@ extension SpaceServiceRoom { isSpace = spaceRoom.roomType == .space isDirect = spaceRoom.isDirect + isDM = spaceRoom.isDm childrenCount = Int(spaceRoom.childrenCount) joinedMembersCount = Int(spaceRoom.numJoinedMembers) @@ -92,6 +94,7 @@ extension SpaceServiceRoom { avatarURL: URL? = nil, isSpace: Bool, isDirect: Bool? = nil, + isDM: Bool? = nil, childrenCount: Int = 0, joinedMembersCount: Int = 0, heroes: [UserProfileProxy] = [], @@ -108,6 +111,7 @@ extension SpaceServiceRoom { avatarURL: avatarURL, isSpace: isSpace, isDirect: isDirect, + isDM: isDM, childrenCount: childrenCount, joinedMembersCount: joinedMembersCount, heroes: heroes, diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift index 7f3f74d3e..cd055c56b 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift @@ -419,7 +419,7 @@ class TimelineController: TimelineControllerProtocol { let isNewTimeline = isSwitchingTimelines isSwitchingTimelines = false - let isDM = roomProxy.isDirectOneToOneRoom + let isDM = roomProxy.infoPublisher.value.isDM let displayName = roomProxy.infoPublisher.value.displayName let hasPredecessor = roomProxy.predecessorRoom != nil diff --git a/NSE/Sources/NotificationItemProxy/NotificationItemProxy.swift b/NSE/Sources/NotificationItemProxy/NotificationItemProxy.swift index 1854221e1..390bfbd33 100644 --- a/NSE/Sources/NotificationItemProxy/NotificationItemProxy.swift +++ b/NSE/Sources/NotificationItemProxy/NotificationItemProxy.swift @@ -45,6 +45,10 @@ struct NotificationItemProxy: NotificationItemProxyProtocol { notificationItem.roomInfo.isDirect } + var isDM: Bool { + notificationItem.roomInfo.isDm + } + var isRoomPrivate: Bool { switch notificationItem.roomInfo.joinRule { case .invite, .knock, .restricted, .knockRestricted: @@ -122,6 +126,10 @@ struct EmptyNotificationItemProxy: NotificationItemProxyProtocol { false } + var isDM: Bool { + false + } + var isRoomPrivate: Bool { false } diff --git a/NSE/Sources/NotificationItemProxy/NotificationItemProxyProtocol.swift b/NSE/Sources/NotificationItemProxy/NotificationItemProxyProtocol.swift index 2ff5d0c7b..f8b918a0e 100644 --- a/NSE/Sources/NotificationItemProxy/NotificationItemProxyProtocol.swift +++ b/NSE/Sources/NotificationItemProxy/NotificationItemProxyProtocol.swift @@ -35,6 +35,8 @@ protocol NotificationItemProxyProtocol { var isRoomDirect: Bool { get } var isRoomPrivate: Bool { get } + + var isDM: Bool { get } var isNoisy: Bool { get } @@ -42,9 +44,3 @@ protocol NotificationItemProxyProtocol { var threadRootEventID: String? { get } } - -extension NotificationItemProxyProtocol { - var isDM: Bool { - isRoomDirect && roomJoinedMembers <= 2 - } -}