refactor: migrate all NotificationDialog to AlertDialog and remove factories - #1489
Conversation
…ctories Replace all NotificationDialog subclasses with AlertDialog patterns: - Deleted 10 NotificationDialog subclasses (dead-simple factories with zero reusable logic): AccessTokenDeletionConfirmationNotification, BatchDeletionConfirmationNotification, MeasurementDeletionConfirmationNotification, QCItemDeletionConfirmationNotification, PurchaseItemDeletionConfirmationNotification, ProjectUserRemovalConfirmationNotification, ExistingGroupsPreventVariableEdit, ExistingSamplesPreventVariableEdit, ExistingSamplesPreventSampleOriginEdit, ExistingSamplesPreventGroupEdit - Deleted CancelConfirmationDialogFactory — callers now use AlertDialog.alert().warning() directly - Deprecated MessageSourceNotificationFactory.dialog() — single call-site inlined as AlertDialog - Converted NotificationDialog.java to deprecated stub throwing UnsupportedOperationException - Migrated UiExceptionHandler to AlertDialog - Updated front-end-components.md with AlertDialog pattern documentation GitHub Issue: #1487
…og() method - Delete NotificationDialog.java stub entirely - Delete deprecated MessageSourceNotificationFactory.dialog() method and its helper methods (parseTitle, parseConfirmText, DEFAULT_CONFIRM_TEXT) - Remove unused AlertDialog import from MessageSourceNotificationFactory
- WARNING intent now gets red danger button (same as DANGER) - Shortened button labels: 'Discard' instead of 'Discard changes', 'Cancel' instead of 'Continue editing' - Updated AppDialog.createConfirmDialog() for consistency
| // style. If no cancel action is set, the builder skips the cancel | ||
| // button (single-action confirmation). | ||
| if (intent == Intent.DANGER && cancelLabel != null && cancelAction != null) { | ||
| if ((intent == Intent.DANGER || intent == Intent.WARNING) && cancelLabel != null && cancelAction != null) { |
There was a problem hiding this comment.
These if else conditions check if it is confirm only and if not, if the confirm button should be marked as dangerous. As there cannot be a dangerous confirm only dialog (intent DANGER or WARNING and no cancel action), should this be checked explicitly? I think it is very sensible to forbid a dangerous operation where you can only confirm.
| AlertDialog.alert(this) | ||
| .warning() | ||
| .title("Discard changes?") | ||
| .message("By aborting the editing process and closing the dialog, you will lose all information entered.") | ||
| .confirmButton("Discard", () -> editExperimentDialog.close()) | ||
| .cancelButton("Cancel", () -> {}) | ||
| .build() |
There was a problem hiding this comment.
NIT: This pattern is repeating a lot. The repetition is partly due to the missing adaption of the AppDialog. However, would it make sense to provide a factory method for the cancel confirmation case as long as it is used in multiple locations or would you prefer the eventual refactor to the AppDialog to tackle that at some point? The goal would be that the developer can change the way the cancellation is confirmed centrally somewhere. What do you think?
There was a problem hiding this comment.
I am not sure what is missing in the AppDialog? For the factory method, I am unsure if it is a little over engineered for a simple problem. The builder pattern is pretty straight forward and adaptions easy to refactor?
There was a problem hiding this comment.
AlertDialog.alert(this)
.warning()
.title("Discard changes?")
.message("By aborting the editing process and closing the dialog, you will lose all information entered.")
.confirmButton("Discard", () -> editExperimentDialog.close())
.cancelButton("Cancel", () -> {})
.build()This code is duplicated across a lot of dialogs right now. Nothing in the AppDialog needs changing. There are some dialogs still using the QbicDialog or similar classes. Thats why you have to wire the cancellation dialog there explicitly. In the AppDialog this is handled centrally. When moving to the AppDialog, the issue is gone. So I think the code is fine as it is. Just wanted to highlight this.
There was a problem hiding this comment.
AlertDialog.alert(this)
.warning()
.title("Discard changes?")
.message("By aborting the editing process and closing the dialog, you will lose all information entered.")
.confirmButton("Discard", () -> editExperimentDialog.close())
.cancelButton("Cancel", () -> {})
.build()
This code is duplicated across a lot of dialogs right now. Nothing in the AppDialog needs changing. There are some dialogs still using the QbicDialog or similar classes. Thats why you have to wire the cancellation dialog there explicitly. In the AppDialog this is handled centrally. When moving to the AppDialog, the issue is gone. So I think the code is fine as it is. Just wanted to highlight this.
…ented descriptions UX research indicates that AlertDialog confirm/cancel buttons with single-word labels (Confirm, Discard, Cancel, Okay) create unnecessary cognitive load because users must infer the actual outcome from context. This commit improves transparency by making buttons explicitly state their effect: Destructive actions (was: 'Confirm' / 'Cancel'): - 'Remove' / 'Remove user' → 'Keep user' - 'Disconnect' / 'Disconnect dataset' → 'Keep connection' - 'Delete' / 'Delete offer' → 'Keep offer' - 'Delete file' → 'Keep file' - 'Delete batch' → 'Keep batch' - 'Delete measurements' → 'Keep measurements' - 'Delete token' → 'Keep token' Discard-changes confirmation (was: 'Discard' / 'Cancel'): - 'Discard changes' → 'Keep editing' → applied to 6 call sites including internal AppDialog unsaved-changes alert Error acknowledgment (was: 'Okay'): - 'Got it' → applied to 4 error/informational dialogs Every button pair now follows the pattern: both buttons state their outcome. Files changed: - AlertDialog: danger() factory gains cancelLabel parameter - AppDialog: internal createConfirmDialog() uses new labels - 11 call sites updated to use new API - AlertDialogTest: updated for new signature (14 tests, all passing)
Summary
Replace all
NotificationDialogsubclasses withAlertDialogpatterns and remove the notification factory boilerplate.Changes
Deleted (12 files)
AccessTokenDeletionConfirmationNotification.javaBatchDeletionConfirmationNotification.javaMeasurementDeletionConfirmationNotification.javaQCItemDeletionConfirmationNotification.javaPurchaseItemDeletionConfirmationNotification.javaProjectUserRemovalConfirmationNotification.javaExistingGroupsPreventVariableEdit.javaExistingSamplesPreventVariableEdit.javaExistingSamplesPreventSampleOriginEdit.javaExistingSamplesPreventGroupEdit.javaCancelConfirmationDialogFactory.javaNotificationDialog.java— completely removed (caller must useAlertDialog)Deleted from MessageSourceNotificationFactory
dialog()method — fully removed (caller inlined asAlertDialog.alert().error())parseTitle()helper — removed (no longer needed)parseConfirmText()helper — removed (no longer needed)DEFAULT_CONFIRM_TEXTconstant — removedModified call-sites (12 files)
PersonalAccessTokenMain.java— usesAlertDialog.danger()SampleInformationMain.java— usesAlertDialog.danger()+ inlined error dialogMeasurementMain.java— usesAlertDialog.danger()(3 sites)ProjectInformationMain.java— usesAlertDialog.danger()+ inlined cancel dialogProjectAccessComponent.java— usesAlertDialog.danger()EditExperimentDialog.java— usesAlertDialog.alert().error()ExperimentDetailsComponent.java— usesAlertDialog.alert().error()+ inlined cancel dialogProjectMainLayout.java— removed factory parameterProjectOverviewMain.java— removed factory parameterExperimentMainLayout.java— removed factory parameterAddProjectDialog.java— removed factory parameter, usesAlertDialogProjectSideNavigationComponent.java— removed factory parameter, usesAlertDialogDocumentation
front-end-components.md— added AlertDialog section with class diagram, decision tree, and code examplesWhy
Every NotificationDialog subclass was a dead-simple factory with zero reusable logic — just a few lines of boilerplate. AlertDialog handles the dialog lifecycle internally, so callers can write one-liners:
CancelConfirmationDialogFactory was pure boilerplate — identical content everywhere. Callers now use AlertDialog directly.
Related
docs/implementation-plan/notification-dialog-migration.md