use isDM from the SDK to determine if a spaceRoom, a joinedRoom or a notificationItem are from a dm room.

This commit is contained in:
Mauro Romito
2026-05-13 17:43:58 +02:00
committed by Mauro
parent ff5dcb040d
commit a4a857003c
29 changed files with 81 additions and 45 deletions
@@ -13383,6 +13383,11 @@ class NotificationItemProxyMock: NotificationItemProxyProtocol, @unchecked Senda
set(value) { underlyingIsRoomPrivate = value } set(value) { underlyingIsRoomPrivate = value }
} }
var underlyingIsRoomPrivate: Bool! var underlyingIsRoomPrivate: Bool!
var isDM: Bool {
get { return underlyingIsDM }
set(value) { underlyingIsDM = value }
}
var underlyingIsDM: Bool!
var isNoisy: Bool { var isNoisy: Bool {
get { return underlyingIsNoisy } get { return underlyingIsNoisy }
set(value) { underlyingIsNoisy = value } set(value) { underlyingIsNoisy = value }
@@ -15349,6 +15354,11 @@ class RoomInfoProxyMock: RoomInfoProxyProtocol, @unchecked Sendable {
set(value) { underlyingIsDirect = value } set(value) { underlyingIsDirect = value }
} }
var underlyingIsDirect: Bool! var underlyingIsDirect: Bool!
var isDM: Bool {
get { return underlyingIsDM }
set(value) { underlyingIsDM = value }
}
var underlyingIsDM: Bool!
var isSpace: Bool { var isSpace: Bool {
get { return underlyingIsSpace } get { return underlyingIsSpace }
set(value) { underlyingIsSpace = value } set(value) { underlyingIsSpace = value }
@@ -15469,6 +15479,11 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol, @unchecked Sendable {
set(value) { underlyingPowerLevel = value } set(value) { underlyingPowerLevel = value }
} }
var underlyingPowerLevel: RoomPowerLevel! var underlyingPowerLevel: RoomPowerLevel!
var isServiceMember: Bool {
get { return underlyingIsServiceMember }
set(value) { underlyingIsServiceMember = value }
}
var underlyingIsServiceMember: Bool!
} }
class RoomMembershipDetailsProxyMock: RoomMembershipDetailsProxyProtocol, @unchecked Sendable { class RoomMembershipDetailsProxyMock: RoomMembershipDetailsProxyProtocol, @unchecked Sendable {
@@ -85,6 +85,6 @@ private extension RoomMember {
isIgnored: proxy.isIgnored, isIgnored: proxy.isIgnored,
suggestedRoleForPowerLevel: proxy.role.rustRole, suggestedRoleForPowerLevel: proxy.role.rustRole,
membershipChangeReason: proxy.membershipChangeReason, membershipChangeReason: proxy.membershipChangeReason,
isServiceMember: false) isServiceMember: proxy.isServiceMember)
} }
} }
@@ -182,6 +182,7 @@ extension RoomInfoProxyMock {
activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count
invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count invitedMembersCount = configuration.members.filter { $0.membership == .invite }.count
joinedMembersCount = configuration.members.filter { $0.membership == .join }.count joinedMembersCount = configuration.members.filter { $0.membership == .join }.count
isDM = isDirect && activeMembersCount <= 2
highlightCount = 0 highlightCount = 0
notificationCount = 0 notificationCount = 0
cachedUserDefinedNotificationMode = .allMessages cachedUserDefinedNotificationMode = .allMessages
@@ -52,5 +52,6 @@ extension NotificationItemProxyMock {
underlyingIsNoisy = configuration.isNoisy underlyingIsNoisy = configuration.isNoisy
underlyingHasMention = configuration.hasMention underlyingHasMention = configuration.hasMention
threadRootEventID = configuration.threadRootEventID threadRootEventID = configuration.threadRootEventID
underlyingIsDM = configuration.isRoomDirect && configuration.roomJoinedMembers <= 2
} }
} }
@@ -151,7 +151,8 @@ private extension SpaceRoom {
childrenCount: UInt64 = 0, childrenCount: UInt64 = 0,
membership: Membership? = .joined, membership: Membership? = .joined,
heroes: [RoomHero]? = [], heroes: [RoomHero]? = [],
via: [String] = []) { via: [String] = [],
isDM: Bool? = false) {
self.init(roomId: id, self.init(roomId: id,
canonicalAlias: canonicalAlias, canonicalAlias: canonicalAlias,
displayName: name, displayName: name,
@@ -168,6 +169,6 @@ private extension SpaceRoom {
state: membership, state: membership,
heroes: heroes, heroes: heroes,
via: via, via: via,
isDm: false) isDm: isDM)
} }
} }
@@ -29,6 +29,7 @@ extension ClientBuilder {
timeout: requestTimeout, timeout: requestTimeout,
maxConcurrentRequests: nil, maxConcurrentRequests: nil,
maxRetryTime: maxRequestRetryTime)) maxRetryTime: maxRequestRetryTime))
.dmRoomDefinition(dmRoomDefinition: .twoMembers)
builder = switch slidingSync { builder = switch slidingSync {
case .restored: builder case .restored: builder
@@ -156,7 +156,7 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers, canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin, canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs, pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
isDM: timelineContext.viewState.isDirectOneToOneRoom, isDM: timelineContext.viewState.isDM,
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled, isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
areThreadsEnabled: timelineContext.viewState.areThreadsEnabled, areThreadsEnabled: timelineContext.viewState.areThreadsEnabled,
timelineKind: timelineContext.viewState.timelineKind, timelineKind: timelineContext.viewState.timelineKind,
@@ -404,12 +404,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
guard roomProxy.infoPublisher.value.joinedMembersCount > 1 else { guard roomProxy.infoPublisher.value.joinedMembersCount > 1 else {
state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomID, state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomID,
isDM: roomProxy.isDirectOneToOneRoom, isDM: roomProxy.infoPublisher.value.isDM,
state: roomProxy.infoPublisher.value.isPrivate ?? true ? .empty : .public) state: roomProxy.infoPublisher.value.isPrivate ?? true ? .empty : .public)
return return
} }
if !roomProxy.isDirectOneToOneRoom { if !roomProxy.infoPublisher.value.isDM {
if case let .success(ownMember) = await roomProxy.getMember(userID: roomProxy.ownUserID), if case let .success(ownMember) = await roomProxy.getMember(userID: roomProxy.ownUserID),
ownMember.role.isOwner { ownMember.role.isOwner {
await roomProxy.updateMembers() 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)
} }
} }
@@ -228,7 +228,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
if case .space(let spaceServiceRoom) = source { if case .space(let spaceServiceRoom) = source {
switch spaceServiceRoom.state { switch spaceServiceRoom.state {
case .invited: case .invited:
state.mode = .invited(isDM: spaceServiceRoom.isDirect == true && spaceServiceRoom.joinedMembersCount == 1) state.mode = .invited(isDM: spaceServiceRoom.isDM == true)
case .knocked: case .knocked:
state.mode = .knocked state.mode = .knocked
case .banned: case .banned:
@@ -172,12 +172,12 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
guard state.joinedMembersCount > 1 else { guard state.joinedMembersCount > 1 else {
state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id,
isDM: roomProxy.isDirectOneToOneRoom, isDM: roomProxy.infoPublisher.value.isDM,
state: roomProxy.infoPublisher.value.isPrivate ?? true ? .empty : .public) state: roomProxy.infoPublisher.value.isPrivate ?? true ? .empty : .public)
return return
} }
if !roomProxy.isDirectOneToOneRoom, state.accountOwner?.role.isOwner == true { if !roomProxy.infoPublisher.value.isDM, state.accountOwner?.role.isOwner == true {
var isLastOwner = true var isLastOwner = true
for member in roomProxy.membersPublisher.value where member.userID != roomProxy.ownUserID && member.membership == .join { for member in roomProxy.membersPublisher.value where member.userID != roomProxy.ownUserID && member.membership == .join {
if member.role.isOwner { if member.role.isOwner {
@@ -199,7 +199,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
} }
state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id,
isDM: roomProxy.isDirectOneToOneRoom, isDM: roomProxy.infoPublisher.value.isDM,
state: roomProxy.infoPublisher.value.isPrivate ?? true ? .private : .public) state: roomProxy.infoPublisher.value.isPrivate ?? true ? .private : .public)
} }
@@ -307,7 +307,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
self.state.accountOwner = .init(withProxy: accountOwner) self.state.accountOwner = .init(withProxy: accountOwner)
} }
guard roomProxy.isDirectOneToOneRoom else { guard roomProxy.infoPublisher.value.isDM else {
return return
} }
@@ -328,7 +328,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
return return
} }
if roomProxy.isDirectOneToOneRoom { if roomProxy.infoPublisher.value.isDM {
if var dmRecipientInfo = state.dmRecipientInfo { if var dmRecipientInfo = state.dmRecipientInfo {
if case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: dmRecipientInfo.member.id, fallBackToServer: true) { if case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: dmRecipientInfo.member.id, fallBackToServer: true) {
dmRecipientInfo.verificationState = userIdentity?.verificationState dmRecipientInfo.verificationState = userIdentity?.verificationState
@@ -91,7 +91,7 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
} }
if canMentionAllUsers, if canMentionAllUsers,
!roomProxy.isDirectOneToOneRoom, !roomProxy.infoPublisher.value.isDM,
Self.shouldIncludeMember(userID: PillUtilities.atRoom, displayName: PillUtilities.everyone, searchText: suggestionTrigger.text) { Self.shouldIncludeMember(userID: PillUtilities.atRoom, displayName: PillUtilities.everyone, searchText: suggestionTrigger.text) {
membersSuggestion membersSuggestion
.insert(SuggestionItem(suggestionType: .allUsers(roomProxy.details.avatar), range: suggestionTrigger.range, rawSuggestionText: suggestionTrigger.text), at: 0) .insert(SuggestionItem(suggestionType: .allUsers(roomProxy.details.avatar), range: suggestionTrigger.range, rawSuggestionText: suggestionTrigger.text), at: 0)
@@ -70,7 +70,7 @@ struct RoomScreenViewState: BindableState {
} }
/// Whether the current room is a DM /// Whether the current room is a DM
var isDirectOneToOneRoom: Bool var isDM: Bool
var roomThreadListEnabled = false var roomThreadListEnabled = false
var isKnockableRoom = false var isKnockableRoom = false
@@ -70,7 +70,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
let viewState = RoomScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayName ?? roomProxy.id, let viewState = RoomScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayName ?? roomProxy.id,
roomAvatar: roomProxy.infoPublisher.value.avatar, roomAvatar: roomProxy.infoPublisher.value.avatar,
hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall, hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall,
isDirectOneToOneRoom: roomProxy.isDirectOneToOneRoom, isDM: roomProxy.infoPublisher.value.isDM,
hasSuccessor: roomProxy.infoPublisher.value.successor != nil, hasSuccessor: roomProxy.infoPublisher.value.successor != nil,
roomHistorySharingState: roomProxy.infoPublisher.value.historySharingState) roomHistorySharingState: roomProxy.infoPublisher.value.historySharingState)
super.init(initialViewState: appHooks.roomScreenHook.update(viewState), super.init(initialViewState: appHooks.roomScreenHook.update(viewState),
@@ -271,7 +271,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
} }
private func updateVerificationBadge() async { private func updateVerificationBadge() async {
guard roomProxy.isDirectOneToOneRoom, guard roomProxy.infoPublisher.value.isDM,
let dmRecipient = roomProxy.membersPublisher.value.first(where: { $0.userID != roomProxy.ownUserID }), let dmRecipient = roomProxy.membersPublisher.value.first(where: { $0.userID != roomProxy.ownUserID }),
case let .success(userIdentity) = await clientProxy.userIdentity(for: dmRecipient.userID, fallBackToServer: true) else { case let .success(userIdentity) = await clientProxy.userIdentity(for: dmRecipient.userID, fallBackToServer: true) else {
state.dmRecipientVerificationState = .notVerified state.dmRecipientVerificationState = .notVerified
@@ -338,6 +338,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.hasOngoingCall = roomInfo.hasRoomCall state.hasOngoingCall = roomInfo.hasRoomCall
state.activeRoomCallIntent = roomInfo.activeRoomCallIntent state.activeRoomCallIntent = roomInfo.activeRoomCallIntent
state.hasSuccessor = roomInfo.successor != nil state.hasSuccessor = roomInfo.successor != nil
state.isDM = roomInfo.isDM
let pinnedEventIDs = roomInfo.pinnedEventIDs let pinnedEventIDs = roomInfo.pinnedEventIDs
// Only update the loading state of the banner // Only update the loading state of the banner
@@ -345,7 +346,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count) state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count)
} }
switch (roomProxy.isDirectOneToOneRoom, roomInfo.joinRule) { switch (roomInfo.isDM, roomInfo.joinRule) {
case (false, .knock), (false, .knockRestricted): case (false, .knock), (false, .knockRestricted):
state.isKnockableRoom = true state.isKnockableRoom = true
default: default:
@@ -22,7 +22,7 @@ struct RoomCallControlsToolbar: ToolbarContent {
.disabled(!viewState.canJoinCall) .disabled(!viewState.canJoinCall)
} }
} else { } else {
if viewState.isDirectOneToOneRoom { if viewState.isDM {
if viewState.roomThreadListEnabled { if viewState.roomThreadListEnabled {
// If the developer mode room thread list option is enabled there // If the developer mode room thread list option is enabled there
// is not enough place for 2 calls buttons // 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 } } Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: true)) { _ in } }
} }
ElementNavigationStack { ElementNavigationStack {
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDirectOneToOneRoom: true)) { _ in } } Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDM: true)) { _ in } }
} }
ElementNavigationStack { 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 { ElementNavigationStack {
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false)) { _ in } } Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false)) { _ in } }
@@ -106,12 +106,12 @@ struct RoomCallControlsToolbar_Previews: PreviewProvider {
} }
private extension RoomScreenViewState { 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), RoomScreenViewState(roomAvatar: .room(id: "mock", name: "Mock Room", avatarURL: nil),
canJoinCall: canJoinCall, canJoinCall: canJoinCall,
hasOngoingCall: hasOngoingCall, hasOngoingCall: hasOngoingCall,
activeRoomCallIntent: activeRoomCallIntent, activeRoomCallIntent: activeRoomCallIntent,
isDirectOneToOneRoom: isDirectOneToOneRoom, isDM: isDM,
roomThreadListEnabled: roomThreadListEnabled, roomThreadListEnabled: roomThreadListEnabled,
hasSuccessor: false) hasSuccessor: false)
} }
@@ -84,7 +84,7 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi
// MARK: - Private // MARK: - Private
private func updateVerificationBadge() async { private func updateVerificationBadge() async {
guard roomProxy.isDirectOneToOneRoom, guard roomProxy.infoPublisher.value.isDM,
let dmRecipient = roomProxy.membersPublisher.value.first(where: { $0.userID != roomProxy.ownUserID }), 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 { case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: dmRecipient.userID, fallBackToServer: true) else {
state.dmRecipientVerificationState = .notVerified state.dmRecipientVerificationState = .notVerified
@@ -103,7 +103,7 @@ struct TimelineViewState: BindableState {
var typingMembers: [String] = [] var typingMembers: [String] = []
var showLoading = false var showLoading = false
var showReadReceipts = false var showReadReceipts = false
var isDirectOneToOneRoom = false var isDM = false
var timelineState: TimelineState // check the doc before changing this var timelineState: TimelineState // check the doc before changing this
var ownUserID: String var ownUserID: String
@@ -95,7 +95,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
} }
super.init(initialViewState: TimelineViewState(timelineKind: timelineController.timelineKind, super.init(initialViewState: TimelineViewState(timelineKind: timelineController.timelineKind,
roomID: roomProxy.id, roomID: roomProxy.id,
isDirectOneToOneRoom: roomProxy.isDirectOneToOneRoom, isDM: roomProxy.infoPublisher.value.isDM,
timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }), timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }),
ownUserID: roomProxy.ownUserID, ownUserID: roomProxy.ownUserID,
hideTimelineMedia: hideTimelineMedia, hideTimelineMedia: hideTimelineMedia,
@@ -420,6 +420,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) { private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) {
state.pinnedEventIDs = roomInfo.pinnedEventIDs state.pinnedEventIDs = roomInfo.pinnedEventIDs
state.isDM = roomInfo.isDM
if let powerLevels = roomInfo.powerLevels { if let powerLevels = roomInfo.powerLevels {
state.canCurrentUserSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage) state.canCurrentUserSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage)
@@ -18,8 +18,8 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
let adjustedDeliveryStatus: TimelineItemDeliveryStatus? let adjustedDeliveryStatus: TimelineItemDeliveryStatus?
@ViewBuilder let content: () -> Content @ViewBuilder let content: () -> Content
private var isDirectOneToOneRoom: Bool { private var isDM: Bool {
context.viewState.isDirectOneToOneRoom context.viewState.isDM
} }
private var isFocussed: Bool { private var isFocussed: Bool {
@@ -40,14 +40,14 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
private let bubbleHorizontalPadding: CGFloat = 8 private let bubbleHorizontalPadding: CGFloat = 8
/// Additional padding applied to outgoing bubbles when the avatar is shown /// Additional padding applied to outgoing bubbles when the avatar is shown
private var bubbleAvatarPadding: CGFloat { private var bubbleAvatarPadding: CGFloat {
guard !timelineItem.isOutgoing, !isDirectOneToOneRoom else { return 0 } guard !timelineItem.isOutgoing, !isDM else { return 0 }
return 8 return 8
} }
var body: some View { var body: some View {
ZStack(alignment: .trailingFirstTextBaseline) { ZStack(alignment: .trailingFirstTextBaseline) {
VStack(alignment: alignment, spacing: -12) { VStack(alignment: alignment, spacing: -12) {
if !timelineItem.isOutgoing, !isDirectOneToOneRoom { if !timelineItem.isOutgoing, !isDM {
header header
.zIndex(1) .zIndex(1)
} }
@@ -164,7 +164,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
canCurrentUserRedactOthers: context.viewState.canCurrentUserRedactOthers, canCurrentUserRedactOthers: context.viewState.canCurrentUserRedactOthers,
canCurrentUserPin: context.viewState.canCurrentUserPin, canCurrentUserPin: context.viewState.canCurrentUserPin,
pinnedEventIDs: context.viewState.pinnedEventIDs, pinnedEventIDs: context.viewState.pinnedEventIDs,
isDM: context.viewState.isDirectOneToOneRoom, isDM: context.viewState.isDM,
isViewSourceEnabled: context.viewState.isViewSourceEnabled, isViewSourceEnabled: context.viewState.isViewSourceEnabled,
areThreadsEnabled: context.viewState.areThreadsEnabled, areThreadsEnabled: context.viewState.areThreadsEnabled,
timelineKind: context.viewState.timelineKind, timelineKind: context.viewState.timelineKind,
@@ -218,7 +218,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
} }
private var messageBubbleTopPadding: CGFloat { 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 return timelineGroupStyle == .single || timelineGroupStyle == .first ? 8 : 0
} }
@@ -19,7 +19,7 @@ struct TimelineStartRoomTimelineView: View {
upgradeDialogue upgradeDialogue
} }
// We don't display the title for tombstoned DMs // We don't display the title for tombstoned DMs
if context?.viewState.isDirectOneToOneRoom != true { if context?.viewState.isDM != true {
Text(title) Text(title)
.font(.compound.bodySM) .font(.compound.bodySM)
.foregroundColor(.compound.textSecondary) .foregroundColor(.compound.textSecondary)
@@ -34,7 +34,7 @@ struct TimelineView: View {
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers, canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin, canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs, pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
isDM: timelineContext.viewState.isDirectOneToOneRoom, isDM: timelineContext.viewState.isDM,
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled, isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
areThreadsEnabled: timelineContext.viewState.areThreadsEnabled, areThreadsEnabled: timelineContext.viewState.areThreadsEnabled,
timelineKind: timelineContext.viewState.timelineKind, timelineKind: timelineContext.viewState.timelineKind,
@@ -49,6 +49,10 @@ struct RoomInfoProxy: RoomInfoProxyProtocol {
var isDirect: Bool { var isDirect: Bool {
roomInfo.isDirect roomInfo.isDirect
} }
var isDM: Bool {
roomInfo.isDm
}
var isSpace: Bool { var isSpace: Bool {
roomInfo.isSpace roomInfo.isSpace
@@ -36,6 +36,7 @@ protocol RoomInfoProxyProtocol: BaseRoomInfoProxyProtocol {
var isEncrypted: Bool { get } var isEncrypted: Bool { get }
var isDirect: Bool { get } var isDirect: Bool { get }
var isDM: Bool { get }
var isSpace: Bool { get } var isSpace: Bool { get }
var isFavourite: Bool { get } var isFavourite: Bool { get }
@@ -51,4 +51,8 @@ final class RoomMemberProxy: RoomMemberProxyProtocol {
var powerLevel: RoomPowerLevel { var powerLevel: RoomPowerLevel {
.init(rustPowerLevel: member.powerLevel) .init(rustPowerLevel: member.powerLevel)
} }
var isServiceMember: Bool {
member.isServiceMember
}
} }
@@ -25,6 +25,8 @@ protocol RoomMemberProxyProtocol: AnyObject {
var isIgnored: Bool { get } var isIgnored: Bool { get }
var powerLevel: RoomPowerLevel { get } var powerLevel: RoomPowerLevel { get }
var isServiceMember: Bool { get }
} }
extension RoomMemberProxyProtocol { extension RoomMemberProxyProtocol {
@@ -222,10 +222,6 @@ extension JoinedRoomProxyProtocol {
isDirect: infoPublisher.value.isDirect, isDirect: infoPublisher.value.isDirect,
historySharingState: historySharingState) historySharingState: historySharingState)
} }
var isDirectOneToOneRoom: Bool {
infoPublisher.value.isDirect && infoPublisher.value.activeMembersCount <= 2
}
func members() async -> [RoomMemberProxyProtocol]? { func members() async -> [RoomMemberProxyProtocol]? {
await updateMembers() await updateMembers()
@@ -24,6 +24,7 @@ struct SpaceServiceRoom {
var isSpace: Bool var isSpace: Bool
var isDirect: Bool? var isDirect: Bool?
var isDM: Bool?
var childrenCount: Int var childrenCount: Int
var joinedMembersCount: Int var joinedMembersCount: Int
@@ -70,6 +71,7 @@ extension SpaceServiceRoom {
isSpace = spaceRoom.roomType == .space isSpace = spaceRoom.roomType == .space
isDirect = spaceRoom.isDirect isDirect = spaceRoom.isDirect
isDM = spaceRoom.isDm
childrenCount = Int(spaceRoom.childrenCount) childrenCount = Int(spaceRoom.childrenCount)
joinedMembersCount = Int(spaceRoom.numJoinedMembers) joinedMembersCount = Int(spaceRoom.numJoinedMembers)
@@ -92,6 +94,7 @@ extension SpaceServiceRoom {
avatarURL: URL? = nil, avatarURL: URL? = nil,
isSpace: Bool, isSpace: Bool,
isDirect: Bool? = nil, isDirect: Bool? = nil,
isDM: Bool? = nil,
childrenCount: Int = 0, childrenCount: Int = 0,
joinedMembersCount: Int = 0, joinedMembersCount: Int = 0,
heroes: [UserProfileProxy] = [], heroes: [UserProfileProxy] = [],
@@ -108,6 +111,7 @@ extension SpaceServiceRoom {
avatarURL: avatarURL, avatarURL: avatarURL,
isSpace: isSpace, isSpace: isSpace,
isDirect: isDirect, isDirect: isDirect,
isDM: isDM,
childrenCount: childrenCount, childrenCount: childrenCount,
joinedMembersCount: joinedMembersCount, joinedMembersCount: joinedMembersCount,
heroes: heroes, heroes: heroes,
@@ -419,7 +419,7 @@ class TimelineController: TimelineControllerProtocol {
let isNewTimeline = isSwitchingTimelines let isNewTimeline = isSwitchingTimelines
isSwitchingTimelines = false isSwitchingTimelines = false
let isDM = roomProxy.isDirectOneToOneRoom let isDM = roomProxy.infoPublisher.value.isDM
let displayName = roomProxy.infoPublisher.value.displayName let displayName = roomProxy.infoPublisher.value.displayName
let hasPredecessor = roomProxy.predecessorRoom != nil let hasPredecessor = roomProxy.predecessorRoom != nil
@@ -45,6 +45,10 @@ struct NotificationItemProxy: NotificationItemProxyProtocol {
notificationItem.roomInfo.isDirect notificationItem.roomInfo.isDirect
} }
var isDM: Bool {
notificationItem.roomInfo.isDm
}
var isRoomPrivate: Bool { var isRoomPrivate: Bool {
switch notificationItem.roomInfo.joinRule { switch notificationItem.roomInfo.joinRule {
case .invite, .knock, .restricted, .knockRestricted: case .invite, .knock, .restricted, .knockRestricted:
@@ -122,6 +126,10 @@ struct EmptyNotificationItemProxy: NotificationItemProxyProtocol {
false false
} }
var isDM: Bool {
false
}
var isRoomPrivate: Bool { var isRoomPrivate: Bool {
false false
} }
@@ -35,6 +35,8 @@ protocol NotificationItemProxyProtocol {
var isRoomDirect: Bool { get } var isRoomDirect: Bool { get }
var isRoomPrivate: Bool { get } var isRoomPrivate: Bool { get }
var isDM: Bool { get }
var isNoisy: Bool { get } var isNoisy: Bool { get }
@@ -42,9 +44,3 @@ protocol NotificationItemProxyProtocol {
var threadRootEventID: String? { get } var threadRootEventID: String? { get }
} }
extension NotificationItemProxyProtocol {
var isDM: Bool {
isRoomDirect && roomJoinedMembers <= 2
}
}