mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Add Block user to Report Content screen. (#742)
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user