MOBILE-4724 ux: Change confirm message when leaving page

main
Dani Palou 2025-01-13 10:05:00 +01:00
parent 9e006424f6
commit c51cca9fd3
22 changed files with 53 additions and 60 deletions

View File

@ -1561,9 +1561,9 @@
"core.completion-alt-manual-n-override": "completion", "core.completion-alt-manual-n-override": "completion",
"core.completion-alt-manual-y": "completion", "core.completion-alt-manual-y": "completion",
"core.completion-alt-manual-y-override": "completion", "core.completion-alt-manual-y-override": "completion",
"core.confirmcanceledit": "local_moodlemobileapp",
"core.confirmdeletefile": "repository", "core.confirmdeletefile": "repository",
"core.confirmleaveunknownchanges": "local_moodlemobileapp", "core.confirmleavepagedescription": "local_moodlemobileapp",
"core.confirmleavepagetitle": "local_moodlemobileapp",
"core.confirmloss": "local_moodlemobileapp", "core.confirmloss": "local_moodlemobileapp",
"core.confirmopeninbrowser": "local_moodlemobileapp", "core.confirmopeninbrowser": "local_moodlemobileapp",
"core.confirmremoveselectedfile": "local_moodlemobileapp", "core.confirmremoveselectedfile": "local_moodlemobileapp",
@ -2091,6 +2091,7 @@
"core.lastmodified": "moodle", "core.lastmodified": "moodle",
"core.lastsync": "local_moodlemobileapp", "core.lastsync": "local_moodlemobileapp",
"core.layoutgrid": "workshopform_rubric", "core.layoutgrid": "workshopform_rubric",
"core.leave": "local_moodlemobileapp",
"core.list": "moodle", "core.list": "moodle",
"core.listsep": "langconfig", "core.listsep": "langconfig",
"core.loading": "moodle", "core.loading": "moodle",

View File

@ -41,7 +41,6 @@ import { CoreNetwork } from '@services/network';
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
import { CoreSync } from '@services/sync'; import { CoreSync } from '@services/sync';
import { CoreWSError } from '@classes/errors/wserror'; import { CoreWSError } from '@classes/errors/wserror';
import { Translate } from '@singletons';
import { CoreEvents } from '@singletons/events'; import { CoreEvents } from '@singletons/events';
import { CoreForms } from '@singletons/form'; import { CoreForms } from '@singletons/form';
import { CoreFileEntry } from '@services/file-helper'; import { CoreFileEntry } from '@services/file-helper';
@ -412,7 +411,7 @@ export default class AddonBlogEditEntryPage implements CanLeave, OnInit, OnDestr
if ((!this.entry && this.hasDataChangedForNewEntry) || (this.entry && this.hasDataChangedForEdit)) { if ((!this.entry && this.hasDataChangedForNewEntry) || (this.entry && this.hasDataChangedForEdit)) {
// Modified, confirm user wants to go back. // Modified, confirm user wants to go back.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());

View File

@ -593,7 +593,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
async canLeave(): Promise<boolean> { async canLeave(): Promise<boolean> {
if (AddonCalendarHelper.hasEventDataChanged(this.form.value, this.originalData)) { if (AddonCalendarHelper.hasEventDataChanged(this.form.value, this.originalData)) {
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
CoreForms.triggerFormCancelledEvent(this.formElement, this.currentSite.getId()); CoreForms.triggerFormCancelledEvent(this.formElement, this.currentSite.getId());

View File

@ -16,7 +16,7 @@ import { Component, Input, ViewChild, ElementRef } from '@angular/core';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreFormFields, CoreForms } from '@singletons/form'; import { CoreFormFields, CoreForms } from '@singletons/form';
import { CorePromiseUtils } from '@singletons/promise-utils'; import { CorePromiseUtils } from '@singletons/promise-utils';
import { ModalController, Translate } from '@singletons'; import { ModalController } from '@singletons';
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../../services/assign'; import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../../services/assign';
import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate'; import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate';
import { CoreSharedModule } from '@/core/shared.module'; import { CoreSharedModule } from '@/core/shared.module';
@ -50,7 +50,7 @@ export class AddonModAssignEditFeedbackModalComponent {
async closeModal(): Promise<void> { async closeModal(): Promise<void> {
const changed = await this.hasDataChanged(); const changed = await this.hasDataChanged();
if (changed) { if (changed) {
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());

View File

@ -296,7 +296,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
if (modified) { if (modified) {
// Modified, confirm user wants to go back. // Modified, confirm user wants to go back.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
await this.discardDrafts(); await this.discardDrafts();
} }

View File

@ -125,7 +125,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave {
// Check if data has changed. // Check if data has changed.
const changed = await this.hasDataChanged(); const changed = await this.hasDataChanged();
if (changed) { if (changed) {
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
// Nothing has changed or user confirmed to leave. Clear temporary data from plugins. // Nothing has changed or user confirmed to leave. Clear temporary data from plugins.

View File

@ -156,7 +156,7 @@ export class AddonModDataEditPage implements OnInit {
if (changed) { if (changed) {
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
// Delete the local files from the tmp folder. // Delete the local files from the tmp folder.

View File

@ -160,7 +160,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
if (this.items && !this.completed && this.originalData) { if (this.items && !this.completed && this.originalData) {
// Form submitted. Check if there is any change. // Form submitted. Check if there is any change.
if (!CoreObject.basicLeftCompare(responses, this.originalData, 3)) { if (!CoreObject.basicLeftCompare(responses, this.originalData, 3)) {
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
} }
} }

View File

@ -303,7 +303,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
async canLeave(): Promise<boolean> { async canLeave(): Promise<boolean> {
if (AddonModForumHelper.hasPostDataChanged(this.formData, this.originalData)) { if (AddonModForumHelper.hasPostDataChanged(this.formData, this.originalData)) {
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
// Delete the local files from the tmp folder. // Delete the local files from the tmp folder.

View File

@ -662,7 +662,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea
if (AddonModForumHelper.hasPostDataChanged(this.newDiscussion, this.originalData)) { if (AddonModForumHelper.hasPostDataChanged(this.newDiscussion, this.originalData)) {
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
// Delete the local files from the tmp folder. // Delete the local files from the tmp folder.

View File

@ -187,7 +187,7 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave {
if (this.hasDataChanged()) { if (this.hasDataChanged()) {
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
// Delete the local files from the tmp folder. // Delete the local files from the tmp folder.

View File

@ -16,7 +16,6 @@ import { Component, OnDestroy, ViewChild } from '@angular/core';
import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page'; import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
import { CanLeave } from '@guards/can-leave'; import { CanLeave } from '@guards/can-leave';
import { Translate } from '@singletons';
import { AddonModH5PActivityIndexComponent } from '../../components/index'; import { AddonModH5PActivityIndexComponent } from '../../components/index';
import { CoreAlerts } from '@services/overlays/alerts'; import { CoreAlerts } from '@services/overlays/alerts';
@ -45,7 +44,7 @@ export class AddonModH5PActivityIndexPage extends CoreCourseModuleMainActivityPa
if (!this.canLeaveSafely) { if (!this.canLeaveSafely) {
try { try {
await CoreAlerts.confirm(Translate.instant('core.confirmleaveunknownchanges')); await CoreAlerts.confirmLeaveWithChanges();
return true; return true;
} catch { } catch {

View File

@ -173,7 +173,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave {
if (this.question && !this.eolData && !this.processData && this.originalData) { if (this.question && !this.eolData && !this.processData && this.originalData) {
// Question shown. Check if there is any change. // Question shown. Check if there is any change.
if (!CoreObject.basicLeftCompare(this.questionForm.getRawValue(), this.originalData, 3)) { if (!CoreObject.basicLeftCompare(this.questionForm.getRawValue(), this.originalData, 3)) {
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
} }

View File

@ -337,7 +337,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave {
// Check if data has changed. // Check if data has changed.
if (this.hasDataChanged()) { if (this.hasDataChanged()) {
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());

View File

@ -172,7 +172,7 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy, CanLea
} }
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
CoreForms.triggerFormCancelledEvent(this.formElement, this.siteId); CoreForms.triggerFormCancelledEvent(this.formElement, this.siteId);

View File

@ -147,7 +147,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy, Ca
// Check if data has changed. // Check if data has changed.
if (this.hasDataChanged()) { if (this.hasDataChanged()) {
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} }
if (this.submission?.attachmentfiles) { if (this.submission?.attachmentfiles) {

View File

@ -184,7 +184,7 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea
} }
// Show confirmation if some data has been modified. // Show confirmation if some data has been modified.
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
CoreForms.triggerFormCancelledEvent(this.formElement, this.siteId); CoreForms.triggerFormCancelledEvent(this.formElement, this.siteId);

View File

@ -18,7 +18,7 @@ import { MediaFile } from '@awesome-cordova-plugins/media-capture/ngx';
import { CoreFile, CoreFileProvider } from '@services/file'; import { CoreFile, CoreFileProvider } from '@services/file';
import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreMimetypeUtils } from '@services/utils/mimetype';
import { CoreTimeUtils } from '@services/utils/time'; import { CoreTimeUtils } from '@services/utils/time';
import { ModalController, Translate } from '@singletons'; import { ModalController } from '@singletons';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreCaptureError } from '@classes/errors/captureerror'; import { CoreCaptureError } from '@classes/errors/captureerror';
import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreCanceledError } from '@classes/errors/cancelederror';
@ -235,7 +235,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
async cancel(): Promise<void> { async cancel(): Promise<void> {
if (this.hasCaptured) { if (this.hasCaptured) {
try { try {
await CoreAlerts.confirm(Translate.instant('core.confirmcanceledit')); await CoreAlerts.confirmLeaveWithChanges();
} catch { } catch {
// Canceled. // Canceled.
return; return;

View File

@ -87,12 +87,13 @@ Feature: Test different cases of logout and switch account
| Message | An awesome message | | Message | An awesome message |
And I press the user menu button in the app And I press the user menu button in the app
And I press "Log out" in the app And I press "Log out" in the app
Then I should find "Are you sure you want to leave this page?" in the app Then I should find "Leave page?" in the app
And I should find "Unsaved changes will be lost." in the app
# Check that the app continues working fine if the user cancels the logout. # Check that the app continues working fine if the user cancels the logout.
When I press "Cancel" in the app When I press "Cancel" in the app
And I press "Forum topic 1" in the app And I press "Forum topic 1" in the app
And I press "OK" in the app And I press "Leave" in the app
Then I should find "Forum message 1" in the app Then I should find "Forum message 1" in the app
When I press "Forum topic 2" in the app When I press "Forum topic 2" in the app
@ -105,7 +106,7 @@ Feature: Test different cases of logout and switch account
| Message | An awesome message | | Message | An awesome message |
And I press the user menu button in the app And I press the user menu button in the app
And I press "Log out" in the app And I press "Log out" in the app
And I press "OK" in the app And I press "Leave" in the app
And I wait the app to restart And I wait the app to restart
Then the header should be "Accounts" in the app Then the header should be "Accounts" in the app
@ -135,12 +136,13 @@ Feature: Test different cases of logout and switch account
And I press the user menu button in the app And I press the user menu button in the app
And I press "Switch account" in the app And I press "Switch account" in the app
And I press "pau student2" in the app And I press "pau student2" in the app
Then I should find "Are you sure you want to leave this page?" in the app Then I should find "Leave page?" in the app
And I should find "Unsaved changes will be lost." in the app
# Check that the app continues working fine if the user cancels the switch account. # Check that the app continues working fine if the user cancels the switch account.
When I press "Cancel" in the app When I press "Cancel" in the app
And I press "Forum topic 1" in the app And I press "Forum topic 1" in the app
And I press "OK" in the app And I press "Leave" in the app
Then I should find "Forum message 1" in the app Then I should find "Forum message 1" in the app
When I press "Forum topic 2" in the app When I press "Forum topic 2" in the app
@ -154,7 +156,7 @@ Feature: Test different cases of logout and switch account
And I press the user menu button in the app And I press the user menu button in the app
And I press "Switch account" in the app And I press "Switch account" in the app
And I press "pau student2" in the app And I press "pau student2" in the app
And I press "OK" in the app And I press "Leave" in the app
And I wait the app to restart And I wait the app to restart
And I press the user menu button in the app And I press the user menu button in the app
Then I should find "pau student2" in the app Then I should find "pau student2" in the app

View File

@ -48,9 +48,9 @@
"completion-alt-manual-n-override": "Not completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as complete.", "completion-alt-manual-n-override": "Not completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as complete.",
"completion-alt-manual-y": "Completed: {{$a}}. Select to mark as not complete.", "completion-alt-manual-y": "Completed: {{$a}}. Select to mark as not complete.",
"completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.", "completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.",
"confirmcanceledit": "Are you sure you want to leave this page? All changes will be lost.",
"confirmdeletefile": "Are you sure you want to delete this file?", "confirmdeletefile": "Are you sure you want to delete this file?",
"confirmleaveunknownchanges": "Are you sure you want to leave this page? If you have unsaved changes they will be lost.", "confirmleavepagedescription": "Unsaved changes will be lost.",
"confirmleavepagetitle": "Leave page?",
"confirmloss": "Are you sure? All changes will be lost.", "confirmloss": "Are you sure? All changes will be lost.",
"confirmopeninbrowser": "Do you want to open it in a web browser?", "confirmopeninbrowser": "Do you want to open it in a web browser?",
"confirmremoveselectedfile": "This will permanently delete '{{filename}}'. You can't undo this.", "confirmremoveselectedfile": "This will permanently delete '{{filename}}'. You can't undo this.",
@ -168,6 +168,7 @@
"lastmodified": "Last modified", "lastmodified": "Last modified",
"lastsync": "Last synchronisation", "lastsync": "Last synchronisation",
"layoutgrid": "Grid", "layoutgrid": "Grid",
"leave": "Leave",
"list": "List", "list": "List",
"listsep": ",", "listsep": ",",
"loading": "Loading", "loading": "Loading",

View File

@ -70,7 +70,7 @@ export class CoreAlertsService {
*/ */
confirm<T>(message: string, options: CoreAlertsConfirmOptions = {}): Promise<T> { confirm<T>(message: string, options: CoreAlertsConfirmOptions = {}): Promise<T> {
return new Promise<T>((resolve, reject): void => { return new Promise<T>((resolve, reject): void => {
const { okText, cancelText, ...alertOptions } = options; const { okText, cancelText, isDestructive, ...alertOptions } = options;
const buttons = [ const buttons = [
{ {
text: cancelText || Translate.instant('core.cancel'), text: cancelText || Translate.instant('core.cancel'),
@ -81,6 +81,7 @@ export class CoreAlertsService {
}, },
{ {
text: okText || Translate.instant('core.ok'), text: okText || Translate.instant('core.ok'),
role: isDestructive ? 'destructive' : undefined,
handler: (data: T) => { handler: (data: T) => {
resolve(data); resolve(data);
}, },
@ -111,34 +112,22 @@ export class CoreAlertsService {
async confirmDelete(message: string, options: Omit<AlertOptions, 'message'|'buttons'> = {}): Promise<void> { async confirmDelete(message: string, options: Omit<AlertOptions, 'message'|'buttons'> = {}): Promise<void> {
message = await CoreLang.filterMultilang(message); message = await CoreLang.filterMultilang(message);
const alertOptions: AlertOptions = { await this.confirm(message, {
...options, ...options,
message, okText: Translate.instant('core.delete'),
}; isDestructive: true,
});
return new Promise((resolve, reject): void => {
alertOptions.buttons = [
{
text: Translate.instant('core.cancel'),
role: 'cancel',
handler: () => {
reject(new CoreCanceledError());
},
},
{
text: Translate.instant('core.delete'),
role: 'destructive',
handler: () => {
resolve();
},
},
];
if (!alertOptions.header) {
alertOptions.cssClass = (alertOptions.cssClass || '') + ' core-nohead';
} }
this.show(alertOptions); /**
* Show a confirmation modal to confirm leaving a page with unsaves changes.
*
* @returns Promise resolved if the user confirms and rejected with a canceled error if he cancels.
*/
async confirmLeaveWithChanges(): Promise<void> {
await this.confirm(Translate.instant('core.confirmleavepagedescription'), {
header: Translate.instant('core.confirmleavepagetitle'),
okText: Translate.instant('core.leave'),
}); });
} }
@ -504,6 +493,7 @@ export const CoreAlerts = makeSingleton(CoreAlertsService);
export type CoreAlertsConfirmOptions = Omit<AlertOptions, 'message'|'buttons'> & { export type CoreAlertsConfirmOptions = Omit<AlertOptions, 'message'|'buttons'> & {
okText?: string; // Text of the OK button. By default, 'OK'. okText?: string; // Text of the OK button. By default, 'OK'.
cancelText?: string; // Text of the Cancel button. By default, 'Cancel'. cancelText?: string; // Text of the Cancel button. By default, 'Cancel'.
isDestructive?: boolean; // Whether confirming is destructive (will remove data), so the button will have a danger color.
}; };
/** /**

View File

@ -155,7 +155,8 @@ Feature: It navigates properly using deep links.
Then I should find "This link belongs to another site" in the app Then I should find "This link belongs to another site" in the app
When I press "OK" in the app When I press "OK" in the app
Then I should find "Are you sure you want to leave this page?" in the app Then I should find "Leave page?" in the app
And I should find "Unsaved changes will be lost." in the app
When I press "Cancel" in the app When I press "Cancel" in the app
Then I should not find "Forum message" in the app Then I should not find "Forum message" in the app
@ -164,7 +165,7 @@ Feature: It navigates properly using deep links.
| discussion | user | | discussion | user |
| Forum topic | student2 | | Forum topic | student2 |
And I press "OK" in the app And I press "OK" in the app
And I press "OK" in the app And I press "Leave" in the app
And I wait the app to restart And I wait the app to restart
Then I should find "Forum topic" in the app Then I should find "Forum topic" in the app
And I should find "Forum message" in the app And I should find "Forum message" in the app