diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 6f48f8b5f..c72bb502d 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -240,6 +240,7 @@ "common_shared_location" = "Shared location"; "common_signing_out" = "Signing out"; "common_something_went_wrong" = "Something went wrong"; +"common_something_went_wrong_message" = "We encountered an issue. Please try again."; "common_starting_chat" = "Starting chat…"; "common_sticker" = "Sticker"; "common_success" = "Success"; @@ -486,7 +487,7 @@ "screen_report_room_leave_failed_alert_message" = "Your report was submitted successfully, but we encountered an issue while trying to leave the room. Please try again."; "screen_report_room_leave_failed_alert_title" = "Unable to Leave Room"; "screen_report_room_reason_footer" = "Report this room to your admin. If the messages are encrypted, your admin will not be able to read them."; -"screen_report_room_reason_placeholder" = "Describe the reason…"; +"screen_report_room_reason_placeholder" = "Describe the reason to report…"; "screen_report_room_title" = "Report room"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 136ee6da0..7076ff023 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -544,6 +544,8 @@ internal enum L10n { internal static var commonSigningOut: String { return L10n.tr("Localizable", "common_signing_out") } /// Something went wrong internal static var commonSomethingWentWrong: String { return L10n.tr("Localizable", "common_something_went_wrong") } + /// We encountered an issue. Please try again. + internal static var commonSomethingWentWrongMessage: String { return L10n.tr("Localizable", "common_something_went_wrong_message") } /// Starting chat… internal static var commonStartingChat: String { return L10n.tr("Localizable", "common_starting_chat") } /// Sticker @@ -1884,7 +1886,7 @@ internal enum L10n { internal static var screenReportRoomLeaveFailedAlertTitle: String { return L10n.tr("Localizable", "screen_report_room_leave_failed_alert_title") } /// Report this room to your admin. If the messages are encrypted, your admin will not be able to read them. internal static var screenReportRoomReasonFooter: String { return L10n.tr("Localizable", "screen_report_room_reason_footer") } - /// Describe the reason… + /// Describe the reason to report… internal static var screenReportRoomReasonPlaceholder: String { return L10n.tr("Localizable", "screen_report_room_reason_placeholder") } /// Report room internal static var screenReportRoomTitle: String { return L10n.tr("Localizable", "screen_report_room_title") } diff --git a/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenModels.swift b/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenModels.swift index fdc319c45..f1b6af286 100644 --- a/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenModels.swift +++ b/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenModels.swift @@ -15,7 +15,10 @@ struct DeclineAndBlockScreenViewState: BindableState { var bindings = DeclineAndBlockScreenViewStateBindings() var isDeclineDisabled: Bool { - !bindings.shouldBlockUser && !bindings.shouldReport + if bindings.shouldReport { + return bindings.reportReason.isEmpty + } + return !bindings.shouldBlockUser && !bindings.shouldReport } } @@ -23,9 +26,15 @@ struct DeclineAndBlockScreenViewStateBindings { var shouldBlockUser = true var shouldReport = false var reportReason = "" + + var alert: AlertInfo? } enum DeclineAndBlockScreenViewAction { case decline case dismiss } + +enum DeclineAndBlockAlertType { + case declineFailed +} diff --git a/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenViewModel.swift b/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenViewModel.swift index 61d923592..63f2e2aca 100644 --- a/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenViewModel.swift +++ b/ElementX/Sources/Screens/DeclineAndBlockScreen/DeclineAndBlockScreenViewModel.swift @@ -50,7 +50,7 @@ class DeclineAndBlockScreenViewModel: DeclineAndBlockScreenViewModelType, Declin guard case let .invited(roomProxy) = await clientProxy.roomForIdentifier(roomID) else { MXLog.error("DeclineAndBlockScreenViewModel: Unable to find an invited room for identifier \(roomID)") hideLoadingIndicator() - showError() + showTryAgainAlert { [weak self] in Task { await self?.decline() } } return } @@ -58,7 +58,7 @@ class DeclineAndBlockScreenViewModel: DeclineAndBlockScreenViewModelType, Declin case .success: var shouldShowFailure = false if state.bindings.shouldReport { - shouldShowFailure = await clientProxy.reportRoomForIdentifier(roomID, reason: state.bindings.reportReason.isBlank ? nil : state.bindings.reportReason).isFailure + shouldShowFailure = await clientProxy.reportRoomForIdentifier(roomID, reason: state.bindings.reportReason.isEmpty ? nil : state.bindings.reportReason).isFailure } if state.bindings.shouldBlockUser { @@ -74,7 +74,7 @@ class DeclineAndBlockScreenViewModel: DeclineAndBlockScreenViewModelType, Declin actionsSubject.send(.dismiss(hasDeclined: true)) case .failure: hideLoadingIndicator() - showError() + showTryAgainAlert { [weak self] in Task { await self?.decline() } } } } @@ -97,6 +97,14 @@ class DeclineAndBlockScreenViewModel: DeclineAndBlockScreenViewModelType, Declin userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) } + private func showTryAgainAlert(retryAction: @escaping () -> Void) { + state.bindings.alert = .init(id: .declineFailed, + title: L10n.commonSomethingWentWrong, + message: L10n.commonSomethingWentWrongMessage, + primaryButton: .init(title: L10n.actionDismiss, role: .cancel, action: nil), + secondaryButton: .init(title: L10n.actionTryAgain, action: retryAction)) + } + private func showSuccess() { userIndicatorController.submitIndicator(.init(title: L10n.commonSuccess, iconName: "checkmark")) } diff --git a/ElementX/Sources/Screens/DeclineAndBlockScreen/View/DeclineAndBlockScreen.swift b/ElementX/Sources/Screens/DeclineAndBlockScreen/View/DeclineAndBlockScreen.swift index b51d13dfd..d368ac3a5 100644 --- a/ElementX/Sources/Screens/DeclineAndBlockScreen/View/DeclineAndBlockScreen.swift +++ b/ElementX/Sources/Screens/DeclineAndBlockScreen/View/DeclineAndBlockScreen.swift @@ -24,6 +24,7 @@ struct DeclineAndBlockScreen: View { .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } .animation(.elementDefault, value: context.shouldReport) + .alert(item: $context.alert) } private var blockUserSection: some View { diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift index 906be8621..525fea3ef 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift @@ -73,7 +73,7 @@ struct HomeScreenRoomList: View { Button(role: .destructive) { context.send(viewAction: .reportRoom(roomIdentifier: room.id)) } label: { - Label(room.isDirect ? L10n.actionReportDm : L10n.actionReportRoom, icon: \.chatProblem) + Label(L10n.actionReportRoom, icon: \.chatProblem) } } diff --git a/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenModels.swift b/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenModels.swift index d397001a3..03819a900 100644 --- a/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenModels.swift +++ b/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenModels.swift @@ -13,6 +13,10 @@ enum ReportRoomScreenViewModelAction: Equatable { struct ReportRoomScreenViewState: BindableState { var bindings = ReportRoomScreenViewStateBindings() + + var canReport: Bool { + !bindings.reason.isEmpty + } } struct ReportRoomScreenViewStateBindings { @@ -27,5 +31,6 @@ enum ReportRoomScreenViewAction { } enum ReportRoomScreenAlertType { + case reportRoomFailed case leaveRoomFailed } diff --git a/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenViewModel.swift b/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenViewModel.swift index f62b8e6dc..dd680f633 100644 --- a/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/ReportRoomScreen/ReportRoomScreenViewModel.swift @@ -38,7 +38,7 @@ class ReportRoomScreenViewModel: ReportRoomScreenViewModelType, ReportRoomScreen private func report() async { showLoadingIndicator() - let result = await roomProxy.reportRoom(reason: state.bindings.reason.isBlank ? nil : state.bindings.reason) + let result = await roomProxy.reportRoom(reason: state.bindings.reason.isEmpty ? nil : state.bindings.reason) switch result { case .success: @@ -51,7 +51,11 @@ class ReportRoomScreenViewModel: ReportRoomScreenViewModelType, ReportRoomScreen } case .failure: hideLoadingIndicator() - userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) + state.bindings.alert = .init(id: .reportRoomFailed, + title: L10n.commonSomethingWentWrong, + message: L10n.commonSomethingWentWrongMessage, + primaryButton: .init(title: L10n.actionDismiss, role: .cancel, action: nil), + secondaryButton: .init(title: L10n.actionTryAgain) { [weak self] in Task { await self?.report() } }) } } @@ -72,7 +76,7 @@ class ReportRoomScreenViewModel: ReportRoomScreenViewModelType, ReportRoomScreen title: L10n.screenReportRoomLeaveFailedAlertTitle, message: L10n.screenReportRoomLeaveFailedAlertMessage, primaryButton: .init(title: L10n.actionDismiss, role: .cancel) { [weak self] in self?.actionsSubject.send(.dismiss(shouldLeaveRoom: false)) }, - secondaryButton: .init(title: L10n.actionRetry) { [weak self] in Task { await self?.leaveRoom(showLoading: true) } }) + secondaryButton: .init(title: L10n.actionTryAgain) { [weak self] in Task { await self?.leaveRoom(showLoading: true) } }) } } diff --git a/ElementX/Sources/Screens/ReportRoomScreen/View/ReportRoomScreen.swift b/ElementX/Sources/Screens/ReportRoomScreen/View/ReportRoomScreen.swift index ff0fdd404..0010127a6 100644 --- a/ElementX/Sources/Screens/ReportRoomScreen/View/ReportRoomScreen.swift +++ b/ElementX/Sources/Screens/ReportRoomScreen/View/ReportRoomScreen.swift @@ -53,6 +53,7 @@ struct ReportRoomScreen: View { Button(L10n.actionReport) { context.send(viewAction: .report) } + .disabled(!context.viewState.canReport) } } } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index b9fd05f43..f672dc0c4 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -272,23 +272,15 @@ struct RoomDetailsScreen: View { } } - private var leaveRoomTitle: String { - context.viewState.dmRecipientInfo == nil ? L10n.screenRoomDetailsLeaveRoomTitle : L10n.screenRoomDetailsLeaveConversationTitle - } - - private var reportRoomTitle: String { - context.viewState.dmRecipientInfo == nil ? L10n.actionReportRoom : L10n.actionReportDm - } - private var leaveRoomSection: some View { Section { if context.viewState.reportRoomEnabled { - ListRow(label: .action(title: reportRoomTitle, + ListRow(label: .action(title: L10n.actionReportRoom, icon: \.chatProblem, role: .destructive), kind: .button { context.send(viewAction: .processTapReport) }) } - ListRow(label: .action(title: leaveRoomTitle, + ListRow(label: .action(title: L10n.screenRoomDetailsLeaveRoomTitle, icon: \.leave, role: .destructive), kind: .button { context.send(viewAction: .processTapLeave) }) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-en-GB.png index fddb0773e..cb7e290f7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75f8f6719667c51e24b0e81fc0087e3dc89c78455eb4c5034ff390c1a6dedcc3 -size 123998 +oid sha256:ae55a771e173a36bb0c430f07ef9eb1163949251261c1ab34b6767d270000d75 +size 123938 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-pseudo.png index 53d853371..7bd268ecc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97a51bbcea7e4196cc8be8c7a1a7e09ec9ad8fa0c45c544e2ce401aedd339d74 -size 136178 +oid sha256:c86228750f172b2c8beba3c353d0be92c07f29d42a43ff96445585fa38235d8e +size 136142 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-en-GB.png index 2d3916b11..56e7d7af5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fa1b401510bd73e8ac4e9fda8ccc4504b0dada4c8f9b55b7df9fc8c5b205697 -size 74827 +oid sha256:9f8f68c34e6e3f5328a5e09194c5b95e8a598beac9950ff78429414b27f142fb +size 74746 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-pseudo.png index 3bdb815a6..9d3186be5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/declineAndBlockScreen.Report-room-selected-iPhone-16-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c25422fabd1dc5710b7f9e2736bb5d06a944dfe4ee94af7dd0b5bfa7ece5507c -size 93432 +oid sha256:992e28b1f10659c3169b5b1835bfeb2390d3c1137da02dffe13b196bbd899d74 +size 93388 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-en-GB-0.png index 52100f78c..937058f53 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac131e760c33abc71ff2561532486148184b2145ab8908fc93c8f59fd52a63bd -size 111036 +oid sha256:8e5baed17a92e79e8e318646e04eaf1afb2d7eb3ae536eb113c603e660dbc8d7 +size 111296 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-pseudo-0.png index 3459f0ea7..45602d1a9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPad-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce3deae0c993d297780b9983f7864252b38ec5eb139b82c34eac5f66c344f30c -size 124615 +oid sha256:bfddb027602a811e1dc71fd38606a16443eee31776cf88068a665ccbf75e9e52 +size 124490 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-en-GB-0.png index 561705419..0892cff13 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:176befcab1eb8966e6aa976b7d08e0742229ed97d2d5d1df068d6fc04ecfb22a -size 63720 +oid sha256:909fcf1c5e767941cf0f6173fa0c839e6e8a3f1cf38959ea70622e0f7042be0a +size 64555 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-pseudo-0.png index 2b15b68c4..6223f99de 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/reportRoomScreen.iPhone-16-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65656c94698bb9e761ab88770432c3626e35eaea1eb09b0b439038f1d14f6465 -size 82085 +oid sha256:5596ae33489facd4c10d6dedbdc9fec041c8e68e9cff1e8105b36dc3acc0bd30 +size 82025 diff --git a/UnitTests/Sources/DeclineAndBlockScreenViewModelTests.swift b/UnitTests/Sources/DeclineAndBlockScreenViewModelTests.swift index 46df2ae09..260298419 100644 --- a/UnitTests/Sources/DeclineAndBlockScreenViewModelTests.swift +++ b/UnitTests/Sources/DeclineAndBlockScreenViewModelTests.swift @@ -37,6 +37,11 @@ class DeclineAndBlockScreenViewModelTests: XCTestCase { XCTAssertTrue(context.viewState.isDeclineDisabled) XCTAssertFalse(context.shouldReport) XCTAssertFalse(context.shouldBlockUser) + context.shouldReport = true + // Should report set to `true` always requires a non empty reason + XCTAssertTrue(context.viewState.isDeclineDisabled) + context.reportReason = "Test reason" + XCTAssertFalse(context.viewState.isDeclineDisabled) } func testDeclineBlockAndReport() async throws {