Add Block user to Report Content screen. (#742)

This commit is contained in:
Doug
2023-04-03 10:21:24 +01:00
committed by GitHub
parent 7d1ab997ce
commit 220138d492
33 changed files with 220 additions and 130 deletions
@@ -18,6 +18,7 @@ import SwiftUI
struct ReportContentCoordinatorParameters {
let itemID: String
let senderID: String
let roomProxy: RoomProxyProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
}
@@ -36,7 +37,7 @@ final class ReportContentCoordinator: CoordinatorProtocol {
init(parameters: ReportContentCoordinatorParameters) {
self.parameters = parameters
viewModel = ReportContentViewModel(itemID: parameters.itemID, roomProxy: parameters.roomProxy)
viewModel = ReportContentViewModel(itemID: parameters.itemID, senderID: parameters.senderID, roomProxy: parameters.roomProxy)
}
// MARK: - Public
@@ -29,6 +29,7 @@ struct ReportContentViewState: BindableState {
struct ReportContentViewStateBindings {
var reasonText: String
var ignoreUser: Bool
}
enum ReportContentViewAction {
@@ -22,12 +22,15 @@ class ReportContentViewModel: ReportContentViewModelType, ReportContentViewModel
var callback: ((ReportContentViewModelAction) -> Void)?
private let itemID: String
private let senderID: String
private let roomProxy: RoomProxyProtocol
init(itemID: String, roomProxy: RoomProxyProtocol) {
init(itemID: String, senderID: String, roomProxy: RoomProxyProtocol) {
self.itemID = itemID
self.senderID = senderID
self.roomProxy = roomProxy
super.init(initialViewState: ReportContentViewState(bindings: ReportContentViewStateBindings(reasonText: "")))
super.init(initialViewState: ReportContentViewState(bindings: ReportContentViewStateBindings(reasonText: "", ignoreUser: false)))
}
// MARK: - Public
@@ -45,13 +48,21 @@ class ReportContentViewModel: ReportContentViewModelType, ReportContentViewModel
private func submitReport() async {
callback?(.submitStarted)
switch await roomProxy.reportContent(itemID, reason: state.bindings.reasonText) {
case .success:
MXLog.info("Submit Report Content succeeded")
callback?(.submitFinished)
case let .failure(error):
if case let .failure(error) = await roomProxy.reportContent(itemID, reason: state.bindings.reasonText) {
MXLog.error("Submit Report Content failed: \(error)")
callback?(.submitFailed(error: error))
return
}
// Ignore the sender if the user wants to.
if state.bindings.ignoreUser, case let .failure(error) = await roomProxy.ignoreUser(senderID) {
MXLog.error("Ignore user failed: \(error)")
callback?(.submitFailed(error: error))
return
}
MXLog.info("Submit Report Content succeeded")
callback?(.submitFinished)
}
}
@@ -26,35 +26,43 @@ struct ReportContentScreen: View {
}
var body: some View {
ScrollView {
mainContent
.padding(.top, 50)
.padding(.horizontal, horizontalPadding)
Form {
reasonSection
ignoreUserSection
}
.scrollDismissesKeyboard(.immediately)
.background(Color.element.formBackground.ignoresSafeArea())
.compoundForm()
.navigationTitle(L10n.actionReportContent)
.navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar }
.interactiveDismissDisabled()
}
/// The main content of the view to be shown in a scroll view.
var mainContent: some View {
VStack(alignment: .leading, spacing: 24) {
infoText
reasonTextEditor
private var reasonSection: some View {
Section {
TextField(L10n.reportContentHint,
text: $context.reasonText,
prompt: Text(L10n.reportContentHint).compoundFormTextFieldPlaceholder(),
axis: .vertical)
.lineLimit(4, reservesSpace: true)
.textFieldStyle(.compoundForm)
} footer: {
Text(L10n.reportContentExplanation)
.compoundFormSectionFooter()
}
.compoundFormSection()
}
private var infoText: some View {
Text(L10n.reportContentExplanation)
.font(.element.body)
.foregroundColor(Color.element.primaryContent)
}
private var reasonTextEditor: some View {
FormTextEditor(text: $context.reasonText, placeholder: L10n.reportContentHint)
private var ignoreUserSection: some View {
Section {
Toggle(L10n.screenReportContentBlockUser, isOn: $context.ignoreUser)
.toggleStyle(.compoundForm)
.accessibilityIdentifier(A11yIdentifiers.reportContent.ignoreUser)
} footer: {
Text(L10n.screenReportContentBlockUserHint)
.compoundFormSectionFooter()
}
}
@ToolbarContentBuilder
@@ -76,9 +84,13 @@ struct ReportContentScreen: View {
// MARK: - Previews
struct ReportContent_Previews: PreviewProvider {
static let viewModel = ReportContentViewModel(itemID: "", roomProxy: RoomProxyMock(with: .init(displayName: nil)))
static let viewModel = ReportContentViewModel(itemID: "",
senderID: "",
roomProxy: RoomProxyMock(with: .init(displayName: nil)))
static var previews: some View {
ReportContentScreen(context: viewModel.context)
NavigationStack {
ReportContentScreen(context: viewModel.context)
}
}
}
@@ -58,10 +58,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
self.displayRoomDetails()
case .displayMediaFile(let file, let title):
self.displayFilePreview(for: file, with: title)
case .displayEmojiPicker(let itemId):
self.displayEmojiPickerScreen(for: itemId)
case .displayReportContent(let itemId):
self.displayReportContent(for: itemId)
case .displayEmojiPicker(let itemID):
self.displayEmojiPickerScreen(for: itemID)
case .displayReportContent(let itemID, let senderID):
self.displayReportContent(for: itemID, from: senderID)
case .displayCameraPicker:
self.displayMediaPickerWithSource(.camera)
case .displayMediaPicker:
@@ -159,10 +159,11 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
navigationStackCoordinator.push(coordinator)
}
private func displayReportContent(for itemId: String) {
private func displayReportContent(for itemID: String, from senderID: String) {
let navigationCoordinator = NavigationStackCoordinator()
let userIndicatorController = UserIndicatorController(rootCoordinator: NavigationStackCoordinator())
let parameters = ReportContentCoordinatorParameters(itemID: itemId,
let userIndicatorController = UserIndicatorController(rootCoordinator: navigationCoordinator)
let parameters = ReportContentCoordinatorParameters(itemID: itemID,
senderID: senderID,
roomProxy: parameters.roomProxy,
userIndicatorController: userIndicatorController)
let coordinator = ReportContentCoordinator(parameters: parameters)
@@ -21,8 +21,8 @@ import UIKit
enum RoomScreenViewModelAction {
case displayRoomDetails
case displayMediaFile(file: MediaFileHandleProxy, title: String?)
case displayEmojiPicker(itemId: String)
case displayReportContent(itemId: String)
case displayEmojiPicker(itemID: String)
case displayReportContent(itemID: String, senderID: String)
case displayCameraPicker
case displayMediaPicker
case displayDocumentPicker
@@ -155,7 +155,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private func itemDoubleTapped(with itemId: String) {
guard let item = state.items.first(where: { $0.id == itemId }), item.isReactable else { return }
callback?(.displayEmojiPicker(itemId: itemId))
callback?(.displayEmojiPicker(itemID: itemId))
}
private func buildTimelineViews() {
@@ -294,7 +294,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
switch action {
case .react:
callback?(.displayEmojiPicker(itemId: item.id))
callback?(.displayEmojiPicker(itemID: item.id))
case .copy:
UIPasteboard.general.string = item.body
case .edit:
@@ -327,7 +327,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
await timelineController.retryDecryption(for: sessionID)
}
case .report:
callback?(.displayReportContent(itemId: itemID))
callback?(.displayReportContent(itemID: itemID, senderID: item.sender.id))
}
if action.switchToDefaultComposer {