Ignore User UI (#737)

* generated files

* Revert "generated files"

This reverts commit f62c1dbcd9e505083ad4ff17532e2c054e253187.

* renaming files to RoomMembersList

* completed the renaming of the list files

* added generated files

* basic setup of the view and the mock

* added a new mock with a avatar

* share/copy link

* copyUserLink implemented

* removed unimplemented tests

* block user UI

* navigation to room member details added

* implemented but we require a sync from the Rust side

* adjusted some UI test screens

* alert for unblocking

* completed

* some tests

* changelog

* ignore user ui enabled

* loader inside the button when the request is fetching

* removed unused code

* blocking the button while loading

* improved the code

* changelog

* UI tests

* unit tests

* added collection concurrency kit

* Revert "added collection concurrency kit"

This reverts commit 499fbe129f73a75e903d9f4952fe2ad672930f04.

* replaced the asyncMap with a @MainActor builder function

* pr comments

* added localazy to setup

* sdk bump to 1.0.49
This commit is contained in:
Mauro
2023-03-28 11:00:40 +02:00
committed by GitHub
parent 2b753b1135
commit e1df5310b7
23 changed files with 187 additions and 58 deletions
@@ -27,6 +27,7 @@ struct A11yIdentifiers {
static let sessionVerificationScreen = SessionVerificationScreen()
static let softLogoutScreen = SoftLogoutScreen()
static let startChatScreen = StartChatScreen()
static let roomMemberDetailsScreen = RoomMemberDetailsScreen()
struct BugReportScreen {
let report = "bug_report-report"
@@ -98,4 +99,9 @@ struct A11yIdentifiers {
let closeStartChat = "start_chat-close"
let inviteFriends = "start_chat-invite_friends"
}
struct RoomMemberDetailsScreen {
let ignore = "room_member_details-ignore"
let unignore = "room_member_details-unignore"
}
}
@@ -1,37 +0,0 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
extension Sequence {
func asyncMap<T>(_ transform: @escaping (Element) async -> T) async -> [T] {
await withTaskGroup(of: T.self) { group in
var transformedElements = [T]()
for element in self {
group.addTask {
await transform(element)
}
}
for await transformedElement in group {
transformedElements.append(transformedElement)
}
return transformedElements
}
}
}
@@ -19,13 +19,16 @@ import SwiftUI
/// A view to be added on the trailing edge of a form row.
enum FormRowAccessory: View {
case navigationLink
case progressView
var body: some View {
switch self {
case .navigationLink:
return Image(systemName: "chevron.forward")
Image(systemName: "chevron.forward")
.font(.element.subheadlineBold)
.foregroundColor(.element.quaternaryContent)
case .progressView:
ProgressView()
}
}
}
@@ -25,6 +25,7 @@ struct RoomMemberDetailsViewState: BindableState {
let isAccountOwner: Bool
let permalink: URL?
var isIgnored: Bool
var isProcessingIgnoreRequest = false
var bindings: RoomMemberDetailsViewStateBindings
}
@@ -34,7 +35,7 @@ struct RoomMemberDetailsViewStateBindings {
var errorAlert: ErrorAlertItem?
}
struct IgnoreUserAlertItem: AlertItem {
struct IgnoreUserAlertItem: AlertItem, Equatable {
enum Action {
case ignore
case unignore
@@ -73,8 +74,8 @@ struct IgnoreUserAlertItem: AlertItem {
}
enum RoomMemberDetailsViewAction {
case showUnblockAlert
case showBlockAlert
case showUnignoreAlert
case showIgnoreAlert
case ignoreConfirmed
case unignoreConfirmed
case copyUserLink
@@ -39,9 +39,9 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
override func process(viewAction: RoomMemberDetailsViewAction) async {
switch viewAction {
case .showUnblockAlert:
case .showUnignoreAlert:
state.bindings.ignoreUserAlert = .init(action: .unignore)
case .showBlockAlert:
case .showIgnoreAlert:
state.bindings.ignoreUserAlert = .init(action: .ignore)
case .copyUserLink:
copyUserLink()
@@ -64,7 +64,10 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
}
private func ignoreUser() async {
switch await roomMemberProxy.ignoreUser() {
state.isProcessingIgnoreRequest = true
let result = await roomMemberProxy.ignoreUser()
state.isProcessingIgnoreRequest = false
switch result {
case .success:
state.isIgnored = true
case .failure:
@@ -73,7 +76,10 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
}
private func unignoreUser() async {
switch await roomMemberProxy.unignoreUser() {
state.isProcessingIgnoreRequest = true
let result = await roomMemberProxy.unignoreUser()
state.isProcessingIgnoreRequest = false
switch result {
case .success:
state.isIgnored = false
case .failure:
@@ -23,10 +23,9 @@ struct RoomMemberDetailsScreen: View {
Form {
headerSection
// TODO: Uncomment when the feature is ready
// if !context.viewState.isAccountOwner {
// blockUserSection
// }
if !context.viewState.isAccountOwner {
blockUserSection
}
}
.scrollContentBackground(.hidden)
.background(Color.element.formBackground.ignoresSafeArea())
@@ -77,18 +76,22 @@ struct RoomMemberDetailsScreen: View {
Section {
if context.viewState.isIgnored {
Button {
context.send(viewAction: .showUnblockAlert)
context.send(viewAction: .showUnignoreAlert)
} label: {
Label(L10n.screenRoomMemberDetailsUnblockUser, systemImage: "slash.circle")
}
.buttonStyle(FormButtonStyle(accessory: nil))
.accessibilityIdentifier(A11yIdentifiers.roomMemberDetailsScreen.unignore)
.buttonStyle(FormButtonStyle(accessory: context.viewState.isProcessingIgnoreRequest ? .progressView : nil))
.disabled(context.viewState.isProcessingIgnoreRequest)
} else {
Button(role: .destructive) {
context.send(viewAction: .showBlockAlert)
context.send(viewAction: .showIgnoreAlert)
} label: {
Label(L10n.screenRoomMemberDetailsBlockUser, systemImage: "slash.circle")
}
.buttonStyle(FormButtonStyle(accessory: nil))
.accessibilityIdentifier(A11yIdentifiers.roomMemberDetailsScreen.ignore)
.buttonStyle(FormButtonStyle(accessory: context.viewState.isProcessingIgnoreRequest ? .progressView : nil))
.disabled(context.viewState.isProcessingIgnoreRequest)
}
}
.formSectionStyle()
@@ -278,12 +278,17 @@ class RoomProxy: RoomProxyProtocol {
return members
}
let proxiedMembers = await members.asyncMap { RoomMemberProxy(member: $0, backgroundTaskService: self.backgroundTaskService) }
let proxiedMembers = buildRoomMemberProxies(members: members)
return .success(proxiedMembers)
} catch {
return .failure(.failedRetrievingMembers)
}
}
@MainActor
private func buildRoomMemberProxies(members: [RoomMember]) -> [RoomMemberProxy] {
members.map { RoomMemberProxy(member: $0, backgroundTaskService: backgroundTaskService) }
}
func retryDecryption(for sessionID: String) async {
await Task.dispatch(on: .global()) { [weak self] in
@@ -318,6 +318,16 @@ class MockScreen: Identifiable {
let coordinator = RoomMemberDetailsCoordinator(parameters: .init(roomMemberProxy: RoomMemberProxyMock.mockMe, mediaProvider: MockMediaProvider()))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .roomMemberDetails:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsCoordinator(parameters: .init(roomMemberProxy: RoomMemberProxyMock.mockAlice, mediaProvider: MockMediaProvider()))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .roomMemberDetailsIgnoredUser:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsCoordinator(parameters: .init(roomMemberProxy: RoomMemberProxyMock.mockIgnored, mediaProvider: MockMediaProvider()))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
}
}()
}
@@ -44,6 +44,8 @@ enum UITestsScreenIdentifier: String {
case roomDetailsScreenWithRoomAvatar
case roomMembersListScreen
case roomMemberDetailsAccountOwner
case roomMemberDetails
case roomMemberDetailsIgnoredUser
case reportContent
case startChat
}