mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Knock Requests banner display logic (#3550)
* added the join rule to the info * update SDK and added logic to display the banner in the room screen * added the logic to display the banner
This commit is contained in:
parent
9180bac13c
commit
26e07f2457
@ -8195,7 +8195,7 @@
|
||||
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 1.0.73;
|
||||
version = 1.0.74;
|
||||
};
|
||||
};
|
||||
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
||||
|
@ -149,8 +149,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
||||
"state" : {
|
||||
"revision" : "b3f97292695e8d63469c0d3ec608eb74423c6a2e",
|
||||
"version" : "1.0.73"
|
||||
"revision" : "66d32e79ae20dd31201cd16eced53cfcc0c3239d",
|
||||
"version" : "1.0.74"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -59,7 +59,8 @@ extension RoomInfo {
|
||||
numUnreadMessages: 0,
|
||||
numUnreadNotifications: 0,
|
||||
numUnreadMentions: 0,
|
||||
pinnedEventIds: [])
|
||||
pinnedEventIds: [],
|
||||
joinRule: .invite)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ struct JoinedRoomProxyMockConfiguration {
|
||||
var canUserPin = true
|
||||
|
||||
var shouldUseAutoUpdatingTimeline = false
|
||||
var joinRule: JoinRule?
|
||||
}
|
||||
|
||||
extension JoinedRoomProxyMock {
|
||||
@ -166,6 +167,7 @@ extension RoomInfo {
|
||||
numUnreadMessages: 0,
|
||||
numUnreadNotifications: 0,
|
||||
numUnreadMentions: 0,
|
||||
pinnedEventIds: Array(configuration.pinnedEventIDs))
|
||||
pinnedEventIds: Array(configuration.pinnedEventIDs),
|
||||
joinRule: configuration.joinRule)
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ extension RoomInfo {
|
||||
numUnreadMessages: 0,
|
||||
numUnreadNotifications: 0,
|
||||
numUnreadMentions: 0,
|
||||
pinnedEventIds: [])
|
||||
pinnedEventIds: [],
|
||||
joinRule: .knock)
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,17 @@ enum KnockRequestsListScreenViewModelAction { }
|
||||
|
||||
struct KnockRequestsListScreenViewState: BindableState {
|
||||
var requests: [KnockRequestCellInfo] = []
|
||||
var canAccept = false
|
||||
var canDecline = false
|
||||
var canBan = false
|
||||
// If you are in this view one of these must have been true so by default we assume all of them to be true
|
||||
var canAccept = true
|
||||
var canDecline = true
|
||||
var canBan = true
|
||||
var isKnockableRoom = true
|
||||
|
||||
// If all the permissions are denied or the join rule changes while we are in the view
|
||||
// we want to stop displaying any request
|
||||
var shouldDisplayRequests: Bool {
|
||||
!requests.isEmpty && isKnockableRoom && (canAccept || canDecline || canBan)
|
||||
}
|
||||
}
|
||||
|
||||
enum KnockRequestsListScreenViewAction {
|
||||
|
@ -22,6 +22,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
|
||||
self.roomProxy = roomProxy
|
||||
super.init(initialViewState: KnockRequestsListScreenViewState(), mediaProvider: mediaProvider)
|
||||
|
||||
updateRoomInfo(roomInfo: roomProxy.infoPublisher.value)
|
||||
Task {
|
||||
await updatePermissions()
|
||||
}
|
||||
@ -48,13 +49,23 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
|
||||
|
||||
private func setupSubscriptions() {
|
||||
roomProxy.infoPublisher
|
||||
.throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true)
|
||||
.sink { [weak self] _ in
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] roomInfo in
|
||||
self?.updateRoomInfo(roomInfo: roomInfo)
|
||||
Task { await self?.updatePermissions() }
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func updateRoomInfo(roomInfo: RoomInfoProxy) {
|
||||
switch roomInfo.joinRule {
|
||||
case .knock, .knockRestricted:
|
||||
state.isKnockableRoom = true
|
||||
default:
|
||||
state.isKnockableRoom = false
|
||||
}
|
||||
}
|
||||
|
||||
private func updatePermissions() async {
|
||||
state.canAccept = await (try? roomProxy.canUserInvite(userID: roomProxy.ownUserID).get()) == true
|
||||
state.canDecline = await (try? roomProxy.canUserKick(userID: roomProxy.ownUserID).get()) == true
|
||||
|
@ -17,12 +17,12 @@ struct KnockRequestsListScreen: View {
|
||||
.navigationTitle(L10n.screenKnockRequestsListTitle)
|
||||
.background(.compound.bgCanvasDefault)
|
||||
.overlay {
|
||||
if context.viewState.requests.isEmpty {
|
||||
if !context.viewState.shouldDisplayRequests {
|
||||
KnockRequestsListEmptyStateView()
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
if !context.viewState.requests.isEmpty {
|
||||
if context.viewState.shouldDisplayRequests {
|
||||
acceptAllButton
|
||||
}
|
||||
}
|
||||
@ -32,14 +32,16 @@ struct KnockRequestsListScreen: View {
|
||||
private var mainContent: some View {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(context.viewState.requests) { requestInfo in
|
||||
ListRow(kind: .custom {
|
||||
KnockRequestCell(cellInfo: requestInfo,
|
||||
mediaProvider: context.mediaProvider,
|
||||
onAccept: context.viewState.canAccept ? onAccept : nil,
|
||||
onDecline: context.viewState.canDecline ? onDecline : nil,
|
||||
onDeclineAndBan: context.viewState.canBan ? onDeclineAndBan : nil)
|
||||
})
|
||||
if context.viewState.shouldDisplayRequests {
|
||||
ForEach(context.viewState.requests) { requestInfo in
|
||||
ListRow(kind: .custom {
|
||||
KnockRequestCell(cellInfo: requestInfo,
|
||||
mediaProvider: context.mediaProvider,
|
||||
onAccept: context.viewState.canAccept ? onAccept : nil,
|
||||
onDecline: context.viewState.canDecline ? onDecline : nil,
|
||||
onDeclineAndBan: context.viewState.canBan ? onDeclineAndBan : nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, 40)
|
||||
@ -79,10 +81,7 @@ struct KnockRequestsListScreen_Previews: PreviewProvider, TestablePreview {
|
||||
// swiftlint:disable:next line_length
|
||||
.init(id: "@bob:matrix.org", displayName: "Bob", avatarUrl: nil, timestamp: "Now", reason: "Hello this one is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long reason"),
|
||||
.init(id: "@charlie:matrix.org", displayName: "Charlie", avatarUrl: nil, timestamp: "Now", reason: nil),
|
||||
.init(id: "@dan:matrix.org", displayName: "Dan", avatarUrl: nil, timestamp: "Now", reason: "Hello! It's a me! Dan!")],
|
||||
canAccept: true,
|
||||
canDecline: true,
|
||||
canBan: true))
|
||||
.init(id: "@dan:matrix.org", displayName: "Dan", avatarUrl: nil, timestamp: "Now", reason: "Hello! It's a me! Dan!")]))
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -49,9 +49,10 @@ struct RoomDetailsScreenViewState: BindableState {
|
||||
var canJoinCall = false
|
||||
var pinnedEventsActionState = RoomDetailsScreenPinnedEventsActionState.loading
|
||||
var knockingEnabled = false
|
||||
var isKnockableRoom = false
|
||||
|
||||
var canSeeKnockingRequests: Bool {
|
||||
knockingEnabled && dmRecipient == nil && (canInviteUsers || canKickUsers || canBanUsers)
|
||||
knockingEnabled && dmRecipient == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers)
|
||||
}
|
||||
|
||||
var canEdit: Bool {
|
||||
|
@ -189,6 +189,12 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
||||
state.topicSummary = topic?.unattributedStringByReplacingNewlinesWithSpaces()
|
||||
state.joinedMembersCount = roomInfo.joinedMembersCount
|
||||
state.bindings.isFavourite = roomInfo.isFavourite
|
||||
switch roomInfo.joinRule {
|
||||
case .knock, .knockRestricted:
|
||||
state.isKnockableRoom = true
|
||||
default:
|
||||
state.isKnockableRoom = false
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchMembersIfNeeded() async {
|
||||
|
@ -323,7 +323,8 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
|
||||
isDirect: false,
|
||||
isEncrypted: true,
|
||||
canonicalAlias: "#alias:domain.com",
|
||||
members: members))
|
||||
members: members,
|
||||
joinRule: .knock))
|
||||
|
||||
var notificationSettingsProxyMockConfiguration = NotificationSettingsProxyMockConfiguration()
|
||||
notificationSettingsProxyMockConfiguration.roomMode.isDefault = false
|
||||
@ -378,7 +379,8 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
|
||||
name: "Room A",
|
||||
isDirect: false,
|
||||
isEncrypted: false,
|
||||
members: members))
|
||||
members: members,
|
||||
joinRule: .knock))
|
||||
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
|
||||
|
||||
return RoomDetailsScreenViewModel(roomProxy: roomProxy,
|
||||
|
@ -39,6 +39,18 @@ struct RoomScreenViewState: BindableState {
|
||||
var hasOngoingCall: Bool
|
||||
var shouldShowCallButton = true
|
||||
|
||||
var isKnockingEnabled = false
|
||||
var isKnockableRoom = false
|
||||
var canAcceptKnocks = false
|
||||
var canDeclineKnocks = false
|
||||
var canBan = false
|
||||
// TODO: We still don't know how to get these, but these will be the non already seen knock requests of the room, for now we are using this as a mock for testing purposes
|
||||
var unseenKnockRequests: [KnockRequestInfo] = [.init(displayName: "Alice", avatarURL: nil, userID: "@alice:matrix.org", reason: "Helloooo")]
|
||||
|
||||
var shouldSeeKnockRequests: Bool {
|
||||
isKnockingEnabled && isKnockableRoom && !unseenKnockRequests.isEmpty && (canAcceptKnocks || canDeclineKnocks || canBan)
|
||||
}
|
||||
|
||||
var footerDetails: RoomScreenFooterViewDetails?
|
||||
|
||||
var bindings: RoomScreenViewStateBindings
|
||||
|
@ -117,6 +117,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
// MARK: - Private
|
||||
|
||||
private func setupSubscriptions(ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>) {
|
||||
appSettings.$knockingEnabled
|
||||
.weakAssign(to: \.state.isKnockingEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
let roomInfoSubscription = roomProxy
|
||||
.infoPublisher
|
||||
|
||||
@ -236,10 +240,18 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count)
|
||||
}
|
||||
|
||||
let userID = roomProxy.ownUserID
|
||||
if case let .success(permission) = await roomProxy.canUserJoinCall(userID: userID) {
|
||||
state.canJoinCall = permission
|
||||
switch (roomProxy.isEncryptedOneToOneRoom, roomInfo.joinRule) {
|
||||
case (false, .knock), (false, .knockRestricted):
|
||||
state.isKnockableRoom = true
|
||||
default:
|
||||
state.isKnockableRoom = false
|
||||
}
|
||||
|
||||
let ownUserID = roomProxy.ownUserID
|
||||
state.canJoinCall = await (try? roomProxy.canUserJoinCall(userID: ownUserID).get()) == true
|
||||
state.canAcceptKnocks = await (try? roomProxy.canUserInvite(userID: ownUserID).get()) == true
|
||||
state.canDeclineKnocks = await (try? roomProxy.canUserKick(userID: ownUserID).get()) == true
|
||||
state.canBan = await (try? roomProxy.canUserBan(userID: ownUserID).get()) == true
|
||||
}
|
||||
|
||||
private func setupPinnedEventsTimelineProviderIfNeeded() {
|
||||
|
@ -29,12 +29,11 @@ struct RoomScreen: View {
|
||||
timeline
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.overlay(alignment: .top) {
|
||||
Group {
|
||||
if roomContext.viewState.shouldShowPinnedEventsBanner {
|
||||
pinnedItemsBanner
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: roomContext.viewState.shouldShowPinnedEventsBanner)
|
||||
pinnedItemsBanner
|
||||
}
|
||||
// This can overlay on top of the pinnedItemsBanner
|
||||
.overlay(alignment: .top) {
|
||||
knockRequestsBanner
|
||||
}
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
VStack(spacing: 0) {
|
||||
@ -119,11 +118,45 @@ struct RoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var pinnedItemsBanner: some View {
|
||||
PinnedItemsBannerView(state: roomContext.viewState.pinnedEventsBannerState,
|
||||
onMainButtonTap: { roomContext.send(viewAction: .tappedPinnedEventsBanner) },
|
||||
onViewAllButtonTap: { roomContext.send(viewAction: .viewAllPins) })
|
||||
.transition(.move(edge: .top))
|
||||
Group {
|
||||
if roomContext.viewState.shouldShowPinnedEventsBanner {
|
||||
PinnedItemsBannerView(state: roomContext.viewState.pinnedEventsBannerState,
|
||||
onMainButtonTap: { roomContext.send(viewAction: .tappedPinnedEventsBanner) },
|
||||
onViewAllButtonTap: { roomContext.send(viewAction: .viewAllPins) })
|
||||
.transition(.move(edge: .top))
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: roomContext.viewState.shouldShowPinnedEventsBanner)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var knockRequestsBanner: some View {
|
||||
Group {
|
||||
if roomContext.viewState.shouldSeeKnockRequests {
|
||||
KnockRequestsBannerView(requests: roomContext.viewState.unseenKnockRequests,
|
||||
onDismiss: dismissKnockRequestsBanner,
|
||||
onAccept: roomContext.viewState.canAcceptKnocks ? acceptKnockRequest : nil,
|
||||
onViewAll: onViewAllKnockRequests,
|
||||
mediaProvider: roomContext.mediaProvider)
|
||||
.padding(.top, 16)
|
||||
.transition(.move(edge: .top))
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: roomContext.viewState.shouldSeeKnockRequests)
|
||||
}
|
||||
|
||||
private func dismissKnockRequestsBanner() {
|
||||
// TODO: Implement
|
||||
}
|
||||
|
||||
private func acceptKnockRequest(userID: String) {
|
||||
// TODO: Implement
|
||||
}
|
||||
|
||||
private func onViewAllKnockRequests() {
|
||||
// TODO: Implement
|
||||
}
|
||||
|
||||
private var scrollToBottomButton: some View {
|
||||
|
@ -65,6 +65,7 @@ struct RoomInfoProxy: BaseRoomInfoProxyProtocol {
|
||||
var unreadNotificationsCount: UInt { UInt(roomInfo.numUnreadNotifications) }
|
||||
var unreadMentionsCount: UInt { UInt(roomInfo.numUnreadMentions) }
|
||||
var pinnedEventIDs: Set<String> { Set(roomInfo.pinnedEventIds) }
|
||||
var joinRule: JoinRule? { roomInfo.joinRule }
|
||||
}
|
||||
|
||||
struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
|
||||
|
@ -61,7 +61,7 @@ packages:
|
||||
# Element/Matrix dependencies
|
||||
MatrixRustSDK:
|
||||
url: https://github.com/element-hq/matrix-rust-components-swift
|
||||
exactVersion: 1.0.73
|
||||
exactVersion: 1.0.74
|
||||
# path: ../matrix-rust-sdk
|
||||
Compound:
|
||||
url: https://github.com/element-hq/compound-ios
|
||||
|
Loading…
x
Reference in New Issue
Block a user