Refactor the JoinRoom screen to take advantage of newer APIs and support more joinRule/membership combinations (i.e. invite required, restricted, banned) (#3685)

- expose the full RoomPreview and RoomMembershipDetails through their own proxies
- implement standard mocks for all the different combinations
- converge on a single room info provider
- rebuild all the previews
- prioritise the preview data over the room one.
This commit is contained in:
Stefan Ceriu
2025-01-20 19:07:11 +02:00
committed by GitHub
parent f20847f62b
commit 8577f53613
48 changed files with 504 additions and 248 deletions
@@ -516,11 +516,10 @@ class ClientProxy: ClientProxyProtocol {
return await buildRoomForIdentifier(identifier)
}
func roomPreviewForIdentifier(_ identifier: String, via: [String]) async -> Result<RoomPreviewDetails, ClientProxyError> {
func roomPreviewForIdentifier(_ identifier: String, via: [String]) async -> Result<RoomPreviewProxyProtocol, ClientProxyError> {
do {
let roomPreview = try await client.getRoomPreviewFromRoomId(roomId: identifier, viaServers: via)
let roomPreviewInfo = try roomPreview.info()
return .success(.init(roomPreviewInfo))
return try .success(RoomPreviewProxy(roomPreview: roomPreview))
} catch let error as ClientError where error.code == .forbidden {
return .failure(.roomPreviewIsPrivate)
} catch {
@@ -1155,40 +1154,3 @@ private class SendQueueRoomErrorListenerProxy: SendQueueRoomErrorListener {
onErrorClosure(roomId, error)
}
}
private extension RoomPreviewDetails {
init(_ roomPreviewInfo: RoomPreviewInfo) {
self = RoomPreviewDetails(roomID: roomPreviewInfo.roomId,
name: roomPreviewInfo.name,
canonicalAlias: roomPreviewInfo.canonicalAlias,
topic: roomPreviewInfo.topic,
avatarURL: roomPreviewInfo.avatarUrl.flatMap(URL.init(string:)),
memberCount: UInt(roomPreviewInfo.numJoinedMembers),
isHistoryWorldReadable: roomPreviewInfo.isHistoryWorldReadable ?? false,
isJoined: roomPreviewInfo.membership == .joined,
isInvited: roomPreviewInfo.membership == .invited,
isPublic: roomPreviewInfo.isPublic,
canKnock: roomPreviewInfo.canKnock)
}
}
private extension RoomPreviewInfo {
var canKnock: Bool {
switch joinRule {
case .knock, .knockRestricted:
return true
default:
return false
}
}
var isPublic: Bool {
switch joinRule {
// for restricted rooms we want to show optimistically that the we may be able to join the room
case .public, .restricted:
return true
default:
return false
}
}
}
@@ -61,20 +61,6 @@ enum SessionVerificationState {
case unverified
}
struct RoomPreviewDetails {
let roomID: String
let name: String?
let canonicalAlias: String?
let topic: String?
let avatarURL: URL?
let memberCount: UInt
let isHistoryWorldReadable: Bool?
let isJoined: Bool
let isInvited: Bool
let isPublic: Bool
let canKnock: Bool
}
// sourcery: AutoMockable
protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
var actionsPublisher: AnyPublisher<ClientProxyAction, Never> { get }
@@ -154,7 +140,7 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
func roomForIdentifier(_ identifier: String) async -> RoomProxyType?
func roomPreviewForIdentifier(_ identifier: String, via: [String]) async -> Result<RoomPreviewDetails, ClientProxyError>
func roomPreviewForIdentifier(_ identifier: String, via: [String]) async -> Result<RoomPreviewProxyProtocol, ClientProxyError>
@discardableResult func loadUserDisplayName() async -> Result<Void, ClientProxyError>
@@ -7,7 +7,6 @@
import Foundation
import MatrixRustSDK
import UIKit
class KnockedRoomProxy: KnockedRoomProxyProtocol {
private let roomListItem: RoomListItemProtocol
@@ -113,6 +113,9 @@ struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
var isSpace: Bool { roomPreviewInfo.roomType == .space }
var activeMembersCount: Int { Int(roomPreviewInfo.numActiveMembers ?? 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 {
@@ -0,0 +1,13 @@
//
// 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
struct RoomMembershipDetailsProxy: RoomMembershipDetailsProxyProtocol {
let ownRoomMember: RoomMemberProxyProtocol
let senderRoomMember: RoomMemberProxyProtocol?
}
@@ -0,0 +1,14 @@
//
// 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
// sourcery: AutoMockable
protocol RoomMembershipDetailsProxyProtocol {
var ownRoomMember: RoomMemberProxyProtocol { get }
var senderRoomMember: RoomMemberProxyProtocol? { get }
}
@@ -0,0 +1,35 @@
//
// 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 MatrixRustSDK
final class RoomPreviewProxy: RoomPreviewProxyProtocol {
private let roomPreview: RoomPreview
let info: RoomPreviewInfoProxy
init(roomPreview: RoomPreview) throws {
self.roomPreview = roomPreview
info = try .init(roomPreviewInfo: roomPreview.info())
}
var ownMembershipDetails: RoomMembershipDetailsProxyProtocol? {
get async {
guard let details = await roomPreview.ownMembershipDetails() else {
return nil
}
var senderRoomMember: RoomMemberProxy?
if let member = details.senderRoomMember {
senderRoomMember = .init(member: member)
}
return RoomMembershipDetailsProxy(ownRoomMember: RoomMemberProxy(member: details.ownRoomMember),
senderRoomMember: senderRoomMember)
}
}
}
@@ -0,0 +1,14 @@
//
// 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
// sourcery: AutoMockable
protocol RoomPreviewProxyProtocol {
var info: RoomPreviewInfoProxy { get }
var ownMembershipDetails: RoomMembershipDetailsProxyProtocol? { get async }
}