-
-
+ const notification = popupManager.createPopup(() => (
+
+ {_('A new update (%s) is available', info.version)}
+
+
+
-
- `;
-
- const notification: NotyfNotification = notyf.open({
- type: 'success',
- message: messageHtml,
- position: {
- x: 'right',
- y: 'bottom',
- },
- duration: 0,
- });
-
- notificationRef.current = notification;
- }, [notyf, theme]);
+ ));
+ }, [popupManager]);
const handleUpdateNotAvailable = useCallback(() => {
- if (notificationRef.current) return;
-
- const noUpdateMessageHtml = htmlentities(_('No updates available'));
-
- const messageHtml = `
-
- ${noUpdateMessageHtml}
+ const notification = popupManager.createPopup(() => (
+
+ {_('No updates available')}
- `;
-
- const notification: NotyfNotification = notyf.open({
- type: 'success',
- message: messageHtml,
- position: {
- x: 'right',
- y: 'bottom',
- },
- duration: 5000,
- });
-
- notification.on(NotyfEvent.Dismiss, () => {
- notificationRef.current = null;
- });
-
- notificationRef.current = notification;
- }, [notyf, theme]);
+ ), { type: NotificationType.Info });
+ notification.scheduleDismiss();
+ }, [popupManager]);
useEffect(() => {
ipcRenderer.on(AutoUpdaterEvents.UpdateDownloaded, handleUpdateDownloaded);
ipcRenderer.on(AutoUpdaterEvents.UpdateNotAvailable, handleUpdateNotAvailable);
- document.addEventListener(UpdateNotificationEvents.ApplyUpdate, handleApplyUpdate);
- document.addEventListener(UpdateNotificationEvents.Dismiss, handleDismissNotification);
return () => {
ipcRenderer.removeListener(AutoUpdaterEvents.UpdateDownloaded, handleUpdateDownloaded);
ipcRenderer.removeListener(AutoUpdaterEvents.UpdateNotAvailable, handleUpdateNotAvailable);
- document.removeEventListener(UpdateNotificationEvents.ApplyUpdate, handleApplyUpdate);
};
- }, [handleApplyUpdate, handleDismissNotification, handleUpdateDownloaded, handleUpdateNotAvailable]);
+ }, [handleUpdateDownloaded, handleUpdateNotAvailable]);
return (
diff --git a/packages/app-desktop/gui/UpdateNotification/style.scss b/packages/app-desktop/gui/UpdateNotification/style.scss
index 65fbb36d03..553a67b081 100644
--- a/packages/app-desktop/gui/UpdateNotification/style.scss
+++ b/packages/app-desktop/gui/UpdateNotification/style.scss
@@ -1,27 +1,11 @@
.update-notification {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
- .button-container {
- display: flex;
- gap: 10px;
- margin-top: 8px;
- }
-
- .notyf__button {
- padding: 5px 10px;
- border: 1px solid;
- border-radius: 4px;
- background-color: transparent;
- cursor: pointer;
-
- &:hover {
- background-color: rgba(255, 255, 255, 0.2);
- }
- }
-
- a {
- text-decoration: underline;
- }
+ > .buttons {
+ display: flex;
+ gap: 10px;
+ margin-top: 8px;
+ }
}
\ No newline at end of file
diff --git a/packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx b/packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx
index f8984dfcee..870c6aa007 100644
--- a/packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx
+++ b/packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx
@@ -18,6 +18,7 @@ import useWindowCommands from './utils/useWindowCommands';
import PluginDialogs from './PluginDialogs';
import useSyncDialogState from './utils/useSyncDialogState';
import AppDialogs from './AppDialogs';
+import PopupNotificationList from '../PopupNotification/PopupNotificationList';
const PluginManager = require('@joplin/lib/services/PluginManager');
@@ -113,7 +114,9 @@ const WindowCommandsAndDialogs: React.FC
= props => {
const dialogInfo = PluginManager.instance().pluginDialogToShow(props.pluginsLegacy);
const pluginDialog = !dialogInfo ? null : ;
- const { noteContentPropertiesDialogOptions, notePropertiesDialogOptions, shareNoteDialogOptions, shareFolderDialogOptions, promptOptions } = dialogState;
+ const {
+ noteContentPropertiesDialogOptions, notePropertiesDialogOptions, shareNoteDialogOptions, shareFolderDialogOptions, promptOptions,
+ } = dialogState;
return <>
@@ -173,6 +176,8 @@ const WindowCommandsAndDialogs: React.FC = props => {
buttons={promptOptions && 'buttons' in promptOptions ? promptOptions.buttons : null}
inputType={promptOptions && 'inputType' in promptOptions ? promptOptions.inputType : null}
/>
+
+
>;
};
diff --git a/packages/app-desktop/gui/menuCommandNames.ts b/packages/app-desktop/gui/menuCommandNames.ts
index e04b001ed0..8602ba8e22 100644
--- a/packages/app-desktop/gui/menuCommandNames.ts
+++ b/packages/app-desktop/gui/menuCommandNames.ts
@@ -48,7 +48,8 @@ export default function() {
'toggleTabMovesFocus',
'editor.deleteLine',
'editor.duplicateLine',
- 'newAppInstance',
+ 'openSecondaryAppInstance',
+ 'openPrimaryAppInstance',
// We cannot put the undo/redo commands in the menu because they are
// editor-specific commands. If we put them there it will break the
// undo/redo in regular text fields.
diff --git a/packages/app-desktop/gui/note-viewer/index.html b/packages/app-desktop/gui/note-viewer/index.html
index 8ef3db3f6a..635fd5083d 100644
--- a/packages/app-desktop/gui/note-viewer/index.html
+++ b/packages/app-desktop/gui/note-viewer/index.html
@@ -139,11 +139,8 @@
const viewerPercent = scrollmap.translateL2V(percent);
const newScrollTop = viewerPercent * maxScrollTop();
- // Even if the scroll position hasn't changed (percent is the same),
- // we still ignore the next scroll event, so that it doesn't create
- // undesired side effects.
- // https://github.com/laurent22/joplin/issues/7617
- ignoreNextScrollEvent();
+ // The next scroll event cannot be skipped in order to correctly
+ // scroll to the target section in a different note when follwing a link
if (Math.floor(contentElement.scrollTop) !== Math.floor(newScrollTop)) {
percentScroll_ = percent;
diff --git a/packages/app-desktop/gui/styles/index.scss b/packages/app-desktop/gui/styles/index.scss
index b87f736e29..1c061eff6e 100644
--- a/packages/app-desktop/gui/styles/index.scss
+++ b/packages/app-desktop/gui/styles/index.scss
@@ -3,6 +3,7 @@
@use './user-webview-dialog.scss';
@use './prompt-dialog.scss';
@use './flat-button.scss';
+@use './link-button.scss';
@use './help-text.scss';
@use './toolbar-button.scss';
@use './toolbar-icon.scss';
@@ -14,3 +15,5 @@
@use './combobox-wrapper.scss';
@use './combobox-suggestion-option.scss';
@use './change-app-layout-dialog.scss';
+@use './popup-notification-list.scss';
+@use './popup-notification-item.scss';
diff --git a/packages/app-desktop/gui/styles/link-button.scss b/packages/app-desktop/gui/styles/link-button.scss
new file mode 100644
index 0000000000..e10d040dc3
--- /dev/null
+++ b/packages/app-desktop/gui/styles/link-button.scss
@@ -0,0 +1,13 @@
+
+.link-button {
+ background: transparent;
+ border: none;
+ font-size: inherit;
+ font-weight: inherit;
+ color: inherit;
+ padding: 0;
+ margin: 0;
+
+ text-decoration: underline;
+ cursor: pointer;
+}
diff --git a/packages/app-desktop/gui/styles/popup-notification-item.scss b/packages/app-desktop/gui/styles/popup-notification-item.scss
new file mode 100644
index 0000000000..8a985e368f
--- /dev/null
+++ b/packages/app-desktop/gui/styles/popup-notification-item.scss
@@ -0,0 +1,126 @@
+@keyframes slide-in {
+ from {
+ opacity: 0;
+ transform: translateY(25%);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slide-out {
+ from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(25%);
+ }
+}
+
+@keyframes grow {
+ from {
+ transform: scale(0);
+ }
+ to {
+ transform: scale(1);
+ }
+}
+
+.popup-notification-item {
+ margin: 12px;
+ padding: 13px 15px;
+ border-radius: 4px;
+
+ overflow: clip;
+ position: relative;
+ display: flex;
+ align-items: center;
+
+ box-shadow: 0 3px 7px 0px rgba(0, 0, 0, 0.25);
+
+ --text-color: var(--joplin-color5);
+ --ripple-color: var(--joplin-background-color5);
+ background-color: color-mix(in srgb, var(--ripple-color) 20%, transparent 70%);
+ color: var(--text-color);
+
+ animation: slide-in 0.3s ease-in both;
+
+ > .icon {
+ font-size: 14px;
+ text-align: center;
+
+ width: 24px;
+ height: 24px;
+ // Make the line hight slightly larger than the icon size
+ // to vertically center the text
+ line-height: 26px;
+
+ margin-inline-end: 13px;
+ border-radius: 50%;
+
+ color: var(--ripple-color);
+ background-color: var(--text-color);
+ }
+
+ > .content {
+ padding: 10px 0;
+ max-width: min(280px, 70vw);
+ font-size: 1.1em;
+ font-weight: 500;
+ }
+
+ > .ripple {
+ --ripple-size: 500px;
+
+ position: absolute;
+ transform-origin: bottom right;
+ top: calc(var(--ripple-size) / -2);
+ right: -40px;
+ z-index: -1;
+
+ background-color: var(--ripple-color);
+ width: var(--ripple-size);
+ height: var(--ripple-size);
+ border-radius: calc(var(--ripple-size) / 2);
+
+ transform: scale(0);
+ animation: grow 0.4s ease-out forwards;
+ }
+
+ &.-dismissing {
+ // Animate the icon and content first
+ animation: slide-out 0.25s ease-out both;
+ animation-delay: 0.25s;
+
+ & > .content, & > .icon {
+ animation: slide-out 0.3s ease-out both;
+ }
+ }
+
+ &.-success {
+ --ripple-color: var(--joplin-color-correct);
+ }
+
+ &.-error {
+ --ripple-color: var(--joplin-color-error);
+ }
+
+ &.-info {
+ --text-color: var(--joplin-color5);
+ --ripple-color: var(--joplin-background-color5);
+ }
+
+ @media (prefers-reduced-motion) {
+ &, & > .content, & > .icon {
+ transform: none !important;
+ }
+
+ > .ripple {
+ transform: scale(1);
+ animation: none;
+ }
+ }
+}
diff --git a/packages/app-desktop/gui/styles/popup-notification-list.scss b/packages/app-desktop/gui/styles/popup-notification-list.scss
new file mode 100644
index 0000000000..fbf5a2f648
--- /dev/null
+++ b/packages/app-desktop/gui/styles/popup-notification-list.scss
@@ -0,0 +1,22 @@
+
+.popup-notification-list {
+ display: flex;
+ align-items: end;
+ flex-direction: column;
+ list-style-type: none;
+ padding-left: 0;
+ padding-right: 0;
+
+ &.-overlay {
+ // Focus should jump to the bottom item first
+ flex-direction: column-reverse;
+
+ position: absolute;
+ bottom: 0;
+ inset-inline-end: 0; // right: 0 in ltr, left: 0 in rtl
+ z-index: 10;
+
+ max-height: 100vh;
+ overflow-y: auto;
+ }
+}
diff --git a/packages/app-desktop/index.html b/packages/app-desktop/index.html
index d5c39a52ce..43e07a8c6a 100644
--- a/packages/app-desktop/index.html
+++ b/packages/app-desktop/index.html
@@ -10,7 +10,6 @@
Joplin
-
@@ -19,6 +18,5 @@
-