Skip to content

feat(web-server): allow dismissing updates in the GUI (#5276) - #5449

Open
fliptrigga13 wants to merge 1 commit into
Dokploy:canaryfrom
fliptrigga13:feat/issue-5276-dismiss-updates-gui
Open

fliptrigga13 wants to merge 1 commit into
Dokploy:canaryfrom
fliptrigga13:feat/issue-5276-dismiss-updates-gui

Conversation

@fliptrigga13

@fliptrigga13 fliptrigga13 commented Sep 13, 2026

Copy link
Copy Markdown

Summary

Resolves #5276: Add an option to dismiss available updates from the GUI.

Problem

Previously, when an update was detected, Dokploy displayed a persistent "Update Available" button with ping indicator in the sidebar navigation with no way to dismiss it. Users who preferred not to install the update immediately had to either ignore the persistent button or update immediately.

Solution

  • Update Dismissal Storage: Created utility helpers in apps/dokploy/lib/update.ts (isUpdateDismissed, dismissUpdateVersion, clearDismissedUpdateVersion, shouldShowUpdate) persisting dismissed version tags in localStorage.
  • Version-Scoped Dismissal: Dismissing an update is scoped to the current version. When a newer version is later detected (fetchedUpdateData.latestVersion !== dismissedVersion), the update notification automatically reappears.
  • UI Integration:
    • Added "Dismiss update" button to the update dialog footer when an update is available.
    • Added an onDismiss callback to UpdateServer that immediately hides the sidebar button and closes the dialog with toast notification.
    • Added a status indicator and "Restore notification" button in the dialog so users can undismiss an update if desired.
    • Handled localStorage security/quota exceptions gracefully with defensive fallbacks.

Test Plan

  • Added unit tests in apps/dokploy/__test__/settings/update-dismiss.test.ts (10/10 passed).
  • Verified dismiss version recording, version comparison, newer release notification, and restore functionality.
  • Ran Biome lint check across all touched files.

Closes #5276

RetriggerConfidence Score: 2/5

The PR is not safe to merge until polling continues after dismissed versions, restricted-storage access is handled, and restoration updates the sidebar.

Summary

  • Adds guarded storage helpers and unit tests for dismissal behavior.
  • Integrates dismiss and restore actions into the update dialog.
  • Hides dismissed updates in the persistent sidebar.
  • The polling lifecycle and cross-instance restoration currently prevent notifications from reliably reappearing.

Reviews (1) · Last reviewed commit: "feat(web-server): allow dismissing updat..."

Comment on lines 56 to +59
clearUpdatesInterval();
if (isUpdateDismissed(fetchedUpdateData.latestVersion)) {
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Dismissal Stops Future Polling

When polling finds a version already dismissed in localStorage, this code clears the interval before returning. Because the persistent sidebar has no other mechanism to restart polling, it will not detect a newer release during the same session, and the notification cannot automatically reappear until a reload or remount.

Comment on lines +69 to +74
useEffect(() => {
if (typeof window !== "undefined") {
const stored = localStorage.getItem("dismissedUpdateVersion");
setDismissedVersion(stored);
}
}, [isOpen, latestVersion]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Storage Errors Crash Client

If localStorage access throws a SecurityError, this effect lets the error escape whenever the component mounts. This bypasses the guarded storage helper and can unmount the application tree instead of gracefully treating the update as not dismissed.

Suggested change
useEffect(() => {
if (typeof window !== "undefined") {
const stored = localStorage.getItem("dismissedUpdateVersion");
setDismissedVersion(stored);
}
}, [isOpen, latestVersion]);
useEffect(() => {
if (typeof window !== "undefined") {
try {
const stored = localStorage.getItem("dismissedUpdateVersion");
setDismissedVersion(stored);
} catch {
setDismissedVersion(null);
}
}
}, [isOpen, latestVersion]);

Comment on lines +292 to +296
onClick={() => {
clearDismissedUpdateVersion();
setDismissedVersion(null);
toast.success("Update notification restored");
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Restore Leaves Sidebar Hidden

Restoring from the settings dialog only clears localStorage and updates that dialog's private state. The persistent sidebar is a separate component whose dismiss callback set its own updateAvailable state to false, so it receives no update and remains hidden until it remounts and fetches again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: allow dismissing updates (GUI)

1 participant