mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Replace individual RoomProxy properties with a stored RoomInfo. (#3445)
* Store RoomInfo updates in JoinedRoomProxy and read from them directly. * Remove all RoomProxy properties that were reading from the RoomInfo.
This commit is contained in:
@@ -696,7 +696,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
|
||||
for roomID in roomIdentifiers {
|
||||
guard case let .joined(roomProxy) = await roomForIdentifier(roomID),
|
||||
roomProxy.isDirect,
|
||||
roomProxy.infoPublisher.value.isDirect,
|
||||
let members = await roomProxy.members() else {
|
||||
continue
|
||||
}
|
||||
@@ -869,15 +869,15 @@ class ClientProxy: ClientProxyProtocol {
|
||||
|
||||
switch roomListItem.membership() {
|
||||
case .invited:
|
||||
return try .invited(InvitedRoomProxy(roomListItem: roomListItem,
|
||||
room: roomListItem.invitedRoom()))
|
||||
return try await .invited(InvitedRoomProxy(roomListItem: roomListItem,
|
||||
room: roomListItem.invitedRoom()))
|
||||
case .knocked:
|
||||
if appSettings.knockingEnabled {
|
||||
return try .knocked(KnockedRoomProxy(roomListItem: roomListItem,
|
||||
room: roomListItem.invitedRoom()))
|
||||
return try await .knocked(KnockedRoomProxy(roomListItem: roomListItem,
|
||||
room: roomListItem.invitedRoom()))
|
||||
} else {
|
||||
return try .invited(InvitedRoomProxy(roomListItem: roomListItem,
|
||||
room: roomListItem.invitedRoom()))
|
||||
return try await .invited(InvitedRoomProxy(roomListItem: roomListItem,
|
||||
room: roomListItem.invitedRoom()))
|
||||
}
|
||||
case .joined:
|
||||
if roomListItem.isTimelineInitialized() == false {
|
||||
|
||||
@@ -291,16 +291,11 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
// it from what we have. If the call is running before subscribing then wait
|
||||
// for it to change to `false` otherwise wait for it to turn `true` before
|
||||
// changing to `false`
|
||||
let isCallOngoing = roomProxy.hasOngoingCall
|
||||
let isCallOngoing = roomProxy.infoPublisher.value.hasRoomCall
|
||||
|
||||
roomProxy
|
||||
.actionsPublisher
|
||||
.compactMap { action -> (Bool, [String])? in
|
||||
switch action {
|
||||
case .roomInfoUpdate:
|
||||
return (roomProxy.hasOngoingCall, roomProxy.activeRoomCallParticipants)
|
||||
}
|
||||
}
|
||||
.infoPublisher
|
||||
.compactMap { ($0.hasRoomCall, $0.activeRoomCallParticipants) }
|
||||
.removeDuplicates { $0 == $1 }
|
||||
.dropFirst(isCallOngoing ? 0 : 1)
|
||||
.sink { [weak self] hasOngoingCall, activeRoomCallParticipants in
|
||||
|
||||
@@ -17,68 +17,15 @@ class InvitedRoomProxy: InvitedRoomProxyProtocol {
|
||||
// multiple times over FFI
|
||||
lazy var id: String = room.id()
|
||||
|
||||
var canonicalAlias: String? {
|
||||
room.canonicalAlias()
|
||||
}
|
||||
var ownUserID: String { room.ownUserId() }
|
||||
|
||||
var ownUserID: String {
|
||||
room.ownUserId()
|
||||
}
|
||||
|
||||
var name: String? {
|
||||
roomListItem.displayName()
|
||||
}
|
||||
|
||||
var topic: String? {
|
||||
room.topic()
|
||||
}
|
||||
|
||||
var avatarURL: URL? {
|
||||
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 isDirect: Bool {
|
||||
room.isDirect()
|
||||
}
|
||||
|
||||
var isPublic: Bool {
|
||||
room.isPublic()
|
||||
}
|
||||
|
||||
var isSpace: Bool {
|
||||
room.isSpace()
|
||||
}
|
||||
|
||||
var joinedMembersCount: Int {
|
||||
Int(room.joinedMembersCount())
|
||||
}
|
||||
|
||||
var activeMembersCount: Int {
|
||||
Int(room.activeMembersCount())
|
||||
}
|
||||
|
||||
var inviter: RoomMemberProxyProtocol? {
|
||||
get async {
|
||||
await (try? roomListItem.roomInfo().inviter).map(RoomMemberProxy.init)
|
||||
}
|
||||
}
|
||||
let info: RoomInfoProxy
|
||||
|
||||
init(roomListItem: RoomListItemProtocol,
|
||||
room: RoomProtocol) {
|
||||
room: RoomProtocol) async throws {
|
||||
self.roomListItem = roomListItem
|
||||
self.room = room
|
||||
info = try await RoomInfoProxy(roomInfo: room.roomInfo())
|
||||
}
|
||||
|
||||
func acceptInvitation() async -> Result<Void, RoomProxyError> {
|
||||
|
||||
@@ -62,6 +62,11 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
private var identityStatusChangesObservationToken: TaskHandle?
|
||||
|
||||
private var subscribedForUpdates = false
|
||||
|
||||
private let infoSubject: CurrentValueSubject<RoomInfoProxy, Never>
|
||||
var infoPublisher: CurrentValuePublisher<RoomInfoProxy, Never> {
|
||||
infoSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let membersSubject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([])
|
||||
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> {
|
||||
@@ -77,95 +82,17 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> {
|
||||
identityStatusChangesSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// A room identifier is constant and lazy stops it from being fetched
|
||||
// multiple times over FFI
|
||||
lazy var id: String = room.id()
|
||||
|
||||
var canonicalAlias: String? {
|
||||
room.canonicalAlias()
|
||||
}
|
||||
|
||||
var ownUserID: String {
|
||||
room.ownUserId()
|
||||
}
|
||||
|
||||
var name: String? {
|
||||
roomListItem.displayName()
|
||||
}
|
||||
|
||||
var topic: String? {
|
||||
room.topic()
|
||||
}
|
||||
|
||||
var avatarURL: URL? {
|
||||
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 isDirect: Bool {
|
||||
room.isDirect()
|
||||
}
|
||||
|
||||
var isPublic: Bool {
|
||||
room.isPublic()
|
||||
}
|
||||
|
||||
var isSpace: Bool {
|
||||
room.isSpace()
|
||||
}
|
||||
|
||||
var joinedMembersCount: Int {
|
||||
Int(room.joinedMembersCount())
|
||||
}
|
||||
|
||||
var activeMembersCount: Int {
|
||||
Int(room.activeMembersCount())
|
||||
}
|
||||
var ownUserID: String { room.ownUserId() }
|
||||
var info: RoomInfoProxy { infoSubject.value }
|
||||
|
||||
var isEncrypted: Bool {
|
||||
(try? room.isEncrypted()) ?? false
|
||||
}
|
||||
|
||||
var isFavourite: Bool {
|
||||
get async {
|
||||
await (try? room.roomInfo().isFavourite) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
var pinnedEventIDs: Set<String> {
|
||||
get async {
|
||||
guard let pinnedEventIDs = try? await room.roomInfo().pinnedEventIds else {
|
||||
return []
|
||||
}
|
||||
return .init(pinnedEventIDs)
|
||||
}
|
||||
}
|
||||
|
||||
var hasOngoingCall: Bool {
|
||||
room.hasActiveRoomCall()
|
||||
}
|
||||
|
||||
var activeRoomCallParticipants: [String] {
|
||||
room.activeRoomCallParticipants()
|
||||
}
|
||||
|
||||
init(roomListService: RoomListServiceProtocol,
|
||||
roomListItem: RoomListItemProtocol,
|
||||
room: RoomProtocol) async throws {
|
||||
@@ -173,6 +100,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
self.roomListItem = roomListItem
|
||||
self.room = room
|
||||
|
||||
infoSubject = try await .init(RoomInfoProxy(roomInfo: room.roomInfo()))
|
||||
timeline = try await TimelineProxy(timeline: room.timeline(), kind: .live)
|
||||
|
||||
Task {
|
||||
@@ -210,9 +138,9 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in
|
||||
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] roomInfo in
|
||||
MXLog.info("Received room info update")
|
||||
self?.actionsSubject.send(.roomInfoUpdate)
|
||||
self?.infoSubject.send(.init(roomInfo: roomInfo))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -730,14 +658,14 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
}
|
||||
|
||||
private final class RoomInfoUpdateListener: RoomInfoListener {
|
||||
private let onUpdateClosure: () -> Void
|
||||
private let onUpdateClosure: (RoomInfo) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping () -> Void) {
|
||||
init(_ onUpdateClosure: @escaping (RoomInfo) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func call(roomInfo: RoomInfo) {
|
||||
onUpdateClosure()
|
||||
onUpdateClosure(roomInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,62 +17,15 @@ class KnockedRoomProxy: KnockedRoomProxyProtocol {
|
||||
// multiple times over FFI
|
||||
lazy var id: String = room.id()
|
||||
|
||||
var canonicalAlias: String? {
|
||||
room.canonicalAlias()
|
||||
}
|
||||
var ownUserID: String { room.ownUserId() }
|
||||
|
||||
var ownUserID: String {
|
||||
room.ownUserId()
|
||||
}
|
||||
|
||||
var name: String? {
|
||||
roomListItem.displayName()
|
||||
}
|
||||
|
||||
var topic: String? {
|
||||
room.topic()
|
||||
}
|
||||
|
||||
var avatarURL: URL? {
|
||||
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 isDirect: Bool {
|
||||
room.isDirect()
|
||||
}
|
||||
|
||||
var isPublic: Bool {
|
||||
room.isPublic()
|
||||
}
|
||||
|
||||
var isSpace: Bool {
|
||||
room.isSpace()
|
||||
}
|
||||
|
||||
var joinedMembersCount: Int {
|
||||
Int(room.joinedMembersCount())
|
||||
}
|
||||
|
||||
var activeMembersCount: Int {
|
||||
Int(room.activeMembersCount())
|
||||
}
|
||||
let info: RoomInfoProxy
|
||||
|
||||
init(roomListItem: RoomListItemProtocol,
|
||||
room: RoomProtocol) {
|
||||
room: RoomProtocol) async throws {
|
||||
self.roomListItem = roomListItem
|
||||
self.room = room
|
||||
info = try await RoomInfoProxy(roomInfo: room.roomInfo())
|
||||
}
|
||||
|
||||
func cancelKnock() async -> Result<Void, RoomProxyError> {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
struct RoomInfoProxy {
|
||||
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 {
|
||||
if heroes.count == 1 {
|
||||
return .heroes(heroes.map(UserProfileProxy.init))
|
||||
}
|
||||
}
|
||||
|
||||
return .room(id: id, name: displayName, avatarURL: avatarURL)
|
||||
}
|
||||
|
||||
var isDirect: Bool { roomInfo.isDirect }
|
||||
var isPublic: Bool { roomInfo.isPublic }
|
||||
var isSpace: Bool { roomInfo.isSpace }
|
||||
var isTombstoned: Bool { roomInfo.isTombstoned }
|
||||
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) }
|
||||
}
|
||||
@@ -28,37 +28,19 @@ enum RoomProxyType {
|
||||
// sourcery: AutoMockable
|
||||
protocol RoomProxyProtocol {
|
||||
var id: String { get }
|
||||
var canonicalAlias: String? { get }
|
||||
|
||||
var ownUserID: String { get }
|
||||
|
||||
var name: 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 isPublic: Bool { get }
|
||||
var isDirect: Bool { get }
|
||||
var isSpace: Bool { get }
|
||||
|
||||
var joinedMembersCount: Int { get }
|
||||
|
||||
var activeMembersCount: Int { get }
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol InvitedRoomProxyProtocol: RoomProxyProtocol {
|
||||
var inviter: RoomMemberProxyProtocol? { get async }
|
||||
|
||||
var info: RoomInfoProxy { get }
|
||||
func rejectInvitation() async -> Result<Void, RoomProxyError>
|
||||
func acceptInvitation() async -> Result<Void, RoomProxyError>
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol KnockedRoomProxyProtocol: RoomProxyProtocol {
|
||||
var info: RoomInfoProxy { get }
|
||||
func cancelKnock() async -> Result<Void, RoomProxyError>
|
||||
}
|
||||
|
||||
@@ -69,11 +51,8 @@ enum JoinedRoomProxyAction: Equatable {
|
||||
// sourcery: AutoMockable
|
||||
protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
var isEncrypted: Bool { get }
|
||||
var isFavourite: Bool { get async }
|
||||
var pinnedEventIDs: Set<String> { get async }
|
||||
|
||||
var hasOngoingCall: Bool { get }
|
||||
var activeRoomCallParticipants: [String] { get }
|
||||
var infoPublisher: CurrentValuePublisher<RoomInfoProxy, Never> { get }
|
||||
|
||||
var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get }
|
||||
|
||||
@@ -81,8 +60,6 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { get }
|
||||
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> { get }
|
||||
|
||||
var timeline: TimelineProxyProtocol { get }
|
||||
|
||||
var pinnedEventsTimeline: TimelineProxyProtocol? { get async }
|
||||
@@ -176,21 +153,15 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
extension JoinedRoomProxyProtocol {
|
||||
var details: RoomDetails {
|
||||
RoomDetails(id: id,
|
||||
name: name,
|
||||
avatar: avatar,
|
||||
canonicalAlias: canonicalAlias,
|
||||
name: infoPublisher.value.displayName,
|
||||
avatar: infoPublisher.value.avatar,
|
||||
canonicalAlias: infoPublisher.value.canonicalAlias,
|
||||
isEncrypted: isEncrypted,
|
||||
isPublic: isPublic)
|
||||
}
|
||||
|
||||
// Avoids to duplicate the same logic around in the app
|
||||
// Probably this should be done in rust.
|
||||
var roomTitle: String {
|
||||
name ?? "Unknown room 💥"
|
||||
isPublic: infoPublisher.value.isPublic)
|
||||
}
|
||||
|
||||
var isEncryptedOneToOneRoom: Bool {
|
||||
isDirect && isEncrypted && activeMembersCount <= 2
|
||||
infoPublisher.value.isDirect && isEncrypted && infoPublisher.value.activeMembersCount <= 2
|
||||
}
|
||||
|
||||
func members() async -> [RoomMemberProxyProtocol]? {
|
||||
|
||||
@@ -326,7 +326,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
switch paginationState.backward {
|
||||
case .timelineEndReached:
|
||||
if timelineKind != .pinned, !roomProxy.isEncryptedOneToOneRoom {
|
||||
let timelineStart = TimelineStartRoomTimelineItem(name: roomProxy.name)
|
||||
let timelineStart = TimelineStartRoomTimelineItem(name: roomProxy.infoPublisher.value.displayName)
|
||||
newTimelineItems.insert(timelineStart, at: 0)
|
||||
}
|
||||
case .paginating:
|
||||
|
||||
Reference in New Issue
Block a user