Add Block user to Report Content screen. (#742)
This commit is contained in:
@@ -114,7 +114,7 @@
|
||||
"preference_rageshake" = "Rageshake to report bug";
|
||||
"rageshake_detection_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
|
||||
"rageshake_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
|
||||
"report_content_explanation" = "Reporting this message will send it’s unique ‘event ID’ to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.";
|
||||
"report_content_explanation" = "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages.";
|
||||
"report_content_hint" = "Reason for reporting this content";
|
||||
"rich_text_editor_bullet_list" = "Toggle bullet list";
|
||||
"rich_text_editor_code_block" = "Toggle code block";
|
||||
@@ -166,6 +166,8 @@
|
||||
"screen_login_username_hint" = "Username";
|
||||
"screen_onboarding_welcome_subtitle" = "Welcome to the %1$@ Beta. Supercharged, for speed and simplicity.";
|
||||
"screen_onboarding_welcome_title" = "Be in your Element";
|
||||
"screen_report_content_block_user" = "Block user";
|
||||
"screen_report_content_block_user_hint" = "Check if you want to hide all current and future messages from this user";
|
||||
"screen_room_details_encryption_enabled_subtitle" = "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them.";
|
||||
"screen_room_details_encryption_enabled_title" = "Message encryption enabled";
|
||||
"screen_room_details_invite_people_title" = "Invite people";
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"preference_rageshake" = "Rageshake to report bug";
|
||||
"rageshake_detection_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
|
||||
"rageshake_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
|
||||
"report_content_explanation" = "Reporting this message will send it’s unique ‘event ID’ to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.";
|
||||
"report_content_explanation" = "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages.";
|
||||
"report_content_hint" = "Reason for reporting this content";
|
||||
"rich_text_editor_bullet_list" = "Toggle bullet list";
|
||||
"rich_text_editor_code_block" = "Toggle code block";
|
||||
@@ -166,6 +166,8 @@
|
||||
"screen_login_username_hint" = "Username";
|
||||
"screen_onboarding_welcome_subtitle" = "Welcome to the %1$@ Beta. Supercharged, for speed and simplicity.";
|
||||
"screen_onboarding_welcome_title" = "Be in your Element";
|
||||
"screen_report_content_block_user" = "Block user";
|
||||
"screen_report_content_block_user_hint" = "Check if you want to hide all current and future messages from this user";
|
||||
"screen_room_details_encryption_enabled_subtitle" = "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them.";
|
||||
"screen_room_details_encryption_enabled_title" = "Message encryption enabled";
|
||||
"screen_room_details_invite_people_title" = "Invite people";
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"preference_rageshake" = "Rageshake to report bug";
|
||||
"rageshake_detection_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
|
||||
"rageshake_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
|
||||
"report_content_explanation" = "Reporting this message will send it’s unique ‘event ID’ to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.";
|
||||
"report_content_explanation" = "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages.";
|
||||
"report_content_hint" = "Reason for reporting this content";
|
||||
"rich_text_editor_bullet_list" = "Toggle bullet list";
|
||||
"rich_text_editor_code_block" = "Toggle code block";
|
||||
@@ -166,6 +166,8 @@
|
||||
"screen_login_username_hint" = "Username";
|
||||
"screen_onboarding_welcome_subtitle" = "Welcome to the %1$@ Beta. Supercharged, for speed and simplicity.";
|
||||
"screen_onboarding_welcome_title" = "Be in your Element";
|
||||
"screen_report_content_block_user" = "Block user";
|
||||
"screen_report_content_block_user_hint" = "Check if you want to hide all current and future messages from this user";
|
||||
"screen_room_details_encryption_enabled_subtitle" = "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them.";
|
||||
"screen_room_details_encryption_enabled_title" = "Message encryption enabled";
|
||||
"screen_room_details_invite_people_title" = "Invite people";
|
||||
|
||||
@@ -256,7 +256,7 @@ public enum L10n {
|
||||
public static var rageshakeDetectionDialogContent: String { return L10n.tr("Localizable", "rageshake_detection_dialog_content") }
|
||||
/// You seem to be shaking the phone in frustration. Would you like to open the bug report screen?
|
||||
public static var rageshakeDialogContent: String { return L10n.tr("Localizable", "rageshake_dialog_content") }
|
||||
/// Reporting this message will send it’s unique ‘event ID’ to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.
|
||||
/// This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages.
|
||||
public static var reportContentExplanation: String { return L10n.tr("Localizable", "report_content_explanation") }
|
||||
/// Reason for reporting this content
|
||||
public static var reportContentHint: String { return L10n.tr("Localizable", "report_content_hint") }
|
||||
@@ -372,6 +372,10 @@ public enum L10n {
|
||||
}
|
||||
/// Be in your Element
|
||||
public static var screenOnboardingWelcomeTitle: String { return L10n.tr("Localizable", "screen_onboarding_welcome_title") }
|
||||
/// Block user
|
||||
public static var screenReportContentBlockUser: String { return L10n.tr("Localizable", "screen_report_content_block_user") }
|
||||
/// Check if you want to hide all current and future messages from this user
|
||||
public static var screenReportContentBlockUserHint: String { return L10n.tr("Localizable", "screen_report_content_block_user_hint") }
|
||||
/// Messages are secured with locks. Only you and the recipients have the unique keys to unlock them.
|
||||
public static var screenRoomDetailsEncryptionEnabledSubtitle: String { return L10n.tr("Localizable", "screen_room_details_encryption_enabled_subtitle") }
|
||||
/// Message encryption enabled
|
||||
|
||||
@@ -427,6 +427,27 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return membersReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - ignoreUser
|
||||
|
||||
var ignoreUserCallsCount = 0
|
||||
var ignoreUserCalled: Bool {
|
||||
return ignoreUserCallsCount > 0
|
||||
}
|
||||
var ignoreUserReceivedUserID: String?
|
||||
var ignoreUserReceivedInvocations: [String] = []
|
||||
var ignoreUserReturnValue: Result<Void, RoomProxyError>!
|
||||
var ignoreUserClosure: ((String) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError> {
|
||||
ignoreUserCallsCount += 1
|
||||
ignoreUserReceivedUserID = userID
|
||||
ignoreUserReceivedInvocations.append(userID)
|
||||
if let ignoreUserClosure = ignoreUserClosure {
|
||||
return await ignoreUserClosure(userID)
|
||||
} else {
|
||||
return ignoreUserReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - retryDecryption
|
||||
|
||||
var retryDecryptionForCallsCount = 0
|
||||
|
||||
@@ -22,6 +22,7 @@ struct A11yIdentifiers {
|
||||
static let homeScreen = HomeScreen()
|
||||
static let loginScreen = LoginScreen()
|
||||
static let onboardingScreen = OnboardingScreen()
|
||||
static let reportContent = ReportContent()
|
||||
static let roomScreen = RoomScreen()
|
||||
static let roomDetailsScreen = RoomDetailsScreen()
|
||||
static let sessionVerificationScreen = SessionVerificationScreen()
|
||||
@@ -66,6 +67,10 @@ struct A11yIdentifiers {
|
||||
let signIn = "onboarding-sign_in"
|
||||
let hidden = "onboarding-hidden"
|
||||
}
|
||||
|
||||
struct ReportContent {
|
||||
let ignoreUser = "report_content-ignore_user"
|
||||
}
|
||||
|
||||
struct RoomScreen {
|
||||
let name = "room-name"
|
||||
|
||||
@@ -1,64 +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 SwiftUI
|
||||
|
||||
struct FormTextEditor: View {
|
||||
@Binding var text: String
|
||||
let placeholder: String
|
||||
var editorAccessibilityIdentifier: String?
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
RoundedRectangle(cornerRadius: 14, style: .continuous)
|
||||
.fill(Color.element.formRowBackground)
|
||||
|
||||
let textEditor = TextEditor(text: $text)
|
||||
.tint(.element.brand)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 4)
|
||||
.cornerRadius(14)
|
||||
.scrollContentBackground(.hidden)
|
||||
if let editorAccessibilityIdentifier {
|
||||
textEditor
|
||||
.accessibilityIdentifier(editorAccessibilityIdentifier)
|
||||
} else {
|
||||
textEditor
|
||||
}
|
||||
|
||||
if text.isEmpty {
|
||||
Text(placeholder)
|
||||
.font(.element.body)
|
||||
.foregroundColor(Color.element.secondaryContent)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
|
||||
RoundedRectangle(cornerRadius: 14, style: .continuous)
|
||||
.stroke(Color.element.quaternaryContent)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 220)
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
|
||||
struct FormTextEditor_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
FormTextEditor(text: .constant(""), placeholder: "test", editorAccessibilityIdentifier: nil)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -288,6 +288,22 @@ class RoomProxy: RoomProxyProtocol {
|
||||
return .failure(.failedRetrievingMembers)
|
||||
}
|
||||
}
|
||||
|
||||
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: userInitiatedDispatchQueue) {
|
||||
do {
|
||||
try self.room.ignoreUser(userId: userID)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedReportingContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func buildRoomMemberProxies(members: [RoomMember]) -> [RoomMemberProxy] {
|
||||
|
||||
@@ -76,10 +76,12 @@ protocol RoomProxyProtocol {
|
||||
func editMessage(_ newMessage: String, original eventID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
|
||||
func reportContent(_ eventID: String, reason: String?) async -> Result<Void, RoomProxyError>
|
||||
|
||||
|
||||
func members() async -> Result<[RoomMemberProxyProtocol], RoomProxyError>
|
||||
|
||||
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func retryDecryption(for sessionID: String) async
|
||||
|
||||
|
||||
@@ -305,7 +305,9 @@ class MockScreen: Identifiable {
|
||||
return navigationStackCoordinator
|
||||
case .reportContent:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = ReportContentCoordinator(parameters: .init(itemID: "test", roomProxy: RoomProxyMock(with: .init(displayName: "test"))))
|
||||
let coordinator = ReportContentCoordinator(parameters: .init(itemID: "test",
|
||||
senderID: RoomMemberProxyMock.mockAlice.userID,
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "test"))))
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .startChat:
|
||||
|
||||
@@ -20,6 +20,21 @@ import XCTest
|
||||
class ReportContentScreenUITests: XCTestCase {
|
||||
func testInitialStateComponents() {
|
||||
let app = Application.launch(.reportContent)
|
||||
app.assertScreenshot(.reportContent)
|
||||
app.assertScreenshot(.reportContent, step: 0)
|
||||
}
|
||||
|
||||
func testToggleIgnoreUser() {
|
||||
let app = Application.launch(.reportContent)
|
||||
|
||||
// Don't know why, but there's an issue on CI where the toggle is tapped but doesn't respond. Waiting for
|
||||
// it fixes this (even it it already exists). Reproducible by running the test after quitting the simulator.
|
||||
let sendingLogsToggle = app.switches[A11yIdentifiers.reportContent.ignoreUser]
|
||||
XCTAssertTrue(sendingLogsToggle.waitForExistence(timeout: 1))
|
||||
XCTAssertFalse(sendingLogsToggle.isOn)
|
||||
|
||||
sendingLogsToggle.tap()
|
||||
|
||||
XCTAssertTrue(sendingLogsToggle.isOn)
|
||||
app.assertScreenshot(.reportContent, step: 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c5e16b7b56132cb5c7c30d502911dcb29b41efe3228c10053576fe4b34e5178e
|
||||
size 94846
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:06d95ccd192c2c9550e4a405b76352d8f75d86a1d72cbc42dfab15743bcac2de
|
||||
size 96766
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:71b413b0ec66b086cbcb077defbf5890332f56d2c865fb0ddc2ec251224a7000
|
||||
size 102265
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5a330b8acd8f2a4ebdda45d125c29c17e21f6cd8fb2397443d7527988ff5a943
|
||||
size 117146
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:63627788babeceb6940989acf8e07c7244d50b4ab84c97bc74b4e19b9ee6ce47
|
||||
size 120785
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8c816fa74f027b401c84513b2c4d8f912fb91e3229e4b4fa1cdda17db2d88be4
|
||||
size 131307
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c253c4c823104c55bf916c9d112ad87e4de3db24fd324a61350a834584edf275
|
||||
size 116667
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:324756300a6ca0abca19892b5ff4fea9bcd1718fb9375ed87de8e9e981a06908
|
||||
size 118557
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a5f69a58df0557ef1e1adb04a1dbe9b245a003684f0b96228a7a438da168ec25
|
||||
size 133827
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4781fa334f9c08adf7ba7e3b0885e9b04a7f28c7e1e407553a74171570775a41
|
||||
size 162791
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b4120dca6e5eb55094f9e301bb3fa6e538b6f9a1226f29a2ab4832886661b429
|
||||
size 163825
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c47eb8afbde353f625bb0d70712a62df83e769284a4c1a7d0918f27369414d65
|
||||
size 193082
|
||||
@@ -97,6 +97,8 @@ private class MockMediaLoadingClient: ClientProtocol {
|
||||
|
||||
func getMediaFile(source: MatrixRustSDK.MediaSource, mimeType: String) throws -> MatrixRustSDK.MediaFileHandle { fatalError() }
|
||||
|
||||
func getProfile(userId: String) throws -> MatrixRustSDK.UserProfile { fatalError() }
|
||||
|
||||
func getSessionVerificationController() throws -> MatrixRustSDK.SessionVerificationController { fatalError() }
|
||||
|
||||
func fullSlidingSync() throws -> MatrixRustSDK.SlidingSync { fatalError() }
|
||||
|
||||
@@ -20,10 +20,52 @@ import XCTest
|
||||
|
||||
@MainActor
|
||||
class ReportContentScreenViewModelTests: XCTestCase {
|
||||
func testInitialState() {
|
||||
let viewModel = ReportContentViewModel(itemID: "test-id", roomProxy: RoomProxyMock(with: .init(displayName: "test")))
|
||||
let context = viewModel.context
|
||||
|
||||
XCTAssertEqual(context.reasonText, "")
|
||||
let itemID = "test-id"
|
||||
let senderID = "@meany:server.com"
|
||||
let reportReason = "I don't like it."
|
||||
|
||||
func testReportContent() async {
|
||||
// Given the report content view for some content.
|
||||
let roomProxy = RoomProxyMock(with: .init(displayName: "test"))
|
||||
roomProxy.reportContentReasonReturnValue = .success(())
|
||||
let viewModel = ReportContentViewModel(itemID: itemID,
|
||||
senderID: senderID,
|
||||
roomProxy: roomProxy)
|
||||
|
||||
// When reporting the content without ignoring the user.
|
||||
viewModel.state.bindings.reasonText = reportReason
|
||||
viewModel.state.bindings.ignoreUser = false
|
||||
viewModel.context.send(viewAction: .submit)
|
||||
await Task.yield()
|
||||
|
||||
// Then the content should be reported, but the user should not be included.
|
||||
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
|
||||
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.eventID, itemID, "The event ID should match the content being reported.")
|
||||
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.reason, reportReason, "The reason should match the user input.")
|
||||
XCTAssertEqual(roomProxy.ignoreUserCallsCount, 0, "A call to ignore a user should not have been made.")
|
||||
XCTAssertNil(roomProxy.ignoreUserReceivedUserID, "The sender shouldn't have been ignored.")
|
||||
}
|
||||
|
||||
func testReportIgnoringSender() async {
|
||||
// Given the report content view for some content.
|
||||
let roomProxy = RoomProxyMock(with: .init(displayName: "test"))
|
||||
roomProxy.reportContentReasonReturnValue = .success(())
|
||||
roomProxy.ignoreUserReturnValue = .success(())
|
||||
let viewModel = ReportContentViewModel(itemID: itemID,
|
||||
senderID: senderID,
|
||||
roomProxy: roomProxy)
|
||||
|
||||
// When reporting the content and also ignoring the user.
|
||||
viewModel.state.bindings.reasonText = reportReason
|
||||
viewModel.state.bindings.ignoreUser = true
|
||||
viewModel.context.send(viewAction: .submit)
|
||||
await Task.yield()
|
||||
|
||||
// Then the content should be reported, and the user should be ignored.
|
||||
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
|
||||
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.eventID, itemID, "The event ID should match the content being reported.")
|
||||
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.reason, reportReason, "The reason should match the user input.")
|
||||
XCTAssertEqual(roomProxy.ignoreUserCallsCount, 1, "A call should have been made to ignore the sender.")
|
||||
XCTAssertEqual(roomProxy.ignoreUserReceivedUserID, senderID, "The ignored user ID should match the sender.")
|
||||
}
|
||||
}
|
||||
|
||||
1
changelog.d/115.change
Normal file
1
changelog.d/115.change
Normal file
@@ -0,0 +1 @@
|
||||
Add Block user toggle to Report Content screen.
|
||||
Reference in New Issue
Block a user