Files
letro-ios/ElementX/Sources/Services/Room/RoomInfoProxy.swift
2025-05-16 09:58:05 +03:00

145 lines
6.2 KiB
Swift

//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Foundation
import MatrixRustSDK
protocol BaseRoomInfoProxyProtocol {
var id: String { get }
var displayName: String? { get }
var avatar: RoomAvatar { get }
var topic: String? { get }
var canonicalAlias: String? { get }
var avatarURL: URL? { get }
var activeMembersCount: Int { get }
var joinedMembersCount: Int { get }
var isDirect: Bool { get }
var isSpace: Bool { get }
}
struct RoomInfoProxy: BaseRoomInfoProxyProtocol {
let roomInfo: RoomInfo
var id: String { roomInfo.id }
var creator: String? { roomInfo.creator }
var displayName: String? { roomInfo.displayName }
var rawName: String? { roomInfo.rawName }
var topic: String? { roomInfo.topic }
/// The room's avatar URL. Use this for editing and favour ``avatar`` for display.
var avatarURL: URL? { roomInfo.avatarUrl.flatMap(URL.init) }
/// The room's avatar info for use in a ``RoomAvatarImage``.
var avatar: RoomAvatar {
if isDirect, avatarURL == nil, heroes.count == 1 {
return .heroes(heroes.map(UserProfileProxy.init))
}
return .room(id: id, name: displayName, avatarURL: avatarURL)
}
// Here we're assuming unknown rooms are unencrypted.
// Fortunately https://github.com/matrix-org/matrix-rust-sdk/pull/4778 makes that very much of an edge case and we
// also automatically start a `latestEncryptionState` fetch if needed.
// In the worst case, even if we are to assume a room is unencrypted, the SDK will still determine the correct
// state before any message is sent.
var isEncrypted: Bool { roomInfo.encryptionState == .encrypted }
var isDirect: Bool { roomInfo.isDirect }
var isPublic: Bool { roomInfo.isPublic }
// A room might be non public but also not private given the fact that the join rule might be missing or unsupported.
var isPrivate: Bool {
switch roomInfo.joinRule {
case .invite, .knock, .restricted, .knockRestricted:
true
default:
false
}
}
var isSpace: Bool { roomInfo.isSpace }
var tombstoneInfo: RoomTombstoneInfo? { roomInfo.tombstone }
var isFavourite: Bool { roomInfo.isFavourite }
var canonicalAlias: String? { roomInfo.canonicalAlias }
var alternativeAliases: [String] { roomInfo.alternativeAliases }
var membership: Membership { roomInfo.membership }
var inviter: RoomMemberProxy? { roomInfo.inviter.map(RoomMemberProxy.init) }
var heroes: [RoomHero] { roomInfo.heroes }
var activeMembersCount: Int { Int(roomInfo.activeMembersCount) }
var invitedMembersCount: Int { Int(roomInfo.invitedMembersCount) }
var joinedMembersCount: Int { Int(roomInfo.joinedMembersCount) }
var userPowerLevels: [String: Int] { roomInfo.userPowerLevels.mapValues(Int.init) }
var highlightCount: Int { Int(roomInfo.highlightCount) }
var notificationCount: Int { Int(roomInfo.notificationCount) }
var cachedUserDefinedNotificationMode: RoomNotificationMode? { roomInfo.cachedUserDefinedNotificationMode }
var hasRoomCall: Bool { roomInfo.hasRoomCall }
var activeRoomCallParticipants: [String] { roomInfo.activeRoomCallParticipants }
var isMarkedUnread: Bool { roomInfo.isMarkedUnread }
var unreadMessagesCount: UInt { UInt(roomInfo.numUnreadMessages) }
var unreadNotificationsCount: UInt { UInt(roomInfo.numUnreadNotifications) }
var unreadMentionsCount: UInt { UInt(roomInfo.numUnreadMentions) }
var pinnedEventIDs: Set<String> { Set(roomInfo.pinnedEventIds) }
var joinRule: JoinRule? { roomInfo.joinRule }
var historyVisibility: RoomHistoryVisibility { roomInfo.historyVisibility }
/// Find the first alias that matches the given homeserver
/// - Parameters:
/// - serverName: the homserver in question
/// - useFallback: whether to return any alias if none match
func firstAliasMatching(serverName: String?, useFallback: Bool) -> String? {
guard let serverName else { return nil }
// Check if the canonical alias matches the homeserver
if let canonicalAlias = roomInfo.canonicalAlias,
canonicalAlias.range(of: serverName) != nil {
return canonicalAlias
}
// Otherwise check the alternative aliases and return the first one that matches
if let matchingAlternativeAlias = roomInfo.alternativeAliases.filter({ $0.range(of: serverName) != nil }).first {
return matchingAlternativeAlias
}
guard useFallback else {
return nil
}
// Or just return the canonical alias if any
if let canonicalAlias = roomInfo.canonicalAlias {
return canonicalAlias
}
// And finally return whatever the first alternative alias is
return roomInfo.alternativeAliases.first
}
}
struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
let roomPreviewInfo: RoomPreviewInfo
var id: String { roomPreviewInfo.roomId }
var displayName: String? { roomPreviewInfo.name }
var heroes: [RoomHero] { roomPreviewInfo.heroes ?? [] }
var topic: String? { roomPreviewInfo.topic }
var canonicalAlias: String? { roomPreviewInfo.canonicalAlias }
var avatarURL: URL? { roomPreviewInfo.avatarUrl.flatMap(URL.init) }
var isDirect: Bool { roomPreviewInfo.isDirect ?? false }
var isSpace: Bool { roomPreviewInfo.roomType == .space }
var activeMembersCount: Int { Int(roomPreviewInfo.numActiveMembers ?? roomPreviewInfo.numJoinedMembers) }
var joinedMembersCount: Int { Int(roomPreviewInfo.numJoinedMembers) }
var joinRule: JoinRule { roomPreviewInfo.joinRule }
var membership: Membership? { roomPreviewInfo.membership }
/// The room's avatar info for use in a ``RoomAvatarImage``.
var avatar: RoomAvatar {
if isDirect, avatarURL == nil, heroes.count == 1 {
return .heroes(heroes.map(UserProfileProxy.init))
}
return .room(id: id, name: displayName, avatarURL: avatarURL)
}
}