Skip to content

Commit

Permalink
Desktop: Seamless-Updates - creation of update notification (laurent2…
Browse files Browse the repository at this point in the history
  • Loading branch information
AliceHincu authored Aug 8, 2024
1 parent 24731ed commit 88b3c7f
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/app-desktop/ElectronAppWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ export default class ElectronAppWrapper {

this.createWindow();

if (!shim.isLinux) {
if (!shim.isLinux()) {
this.updaterService_ = new AutoUpdaterService();
this.updaterService_.startPeriodicUpdateCheck();
}
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface Font {
declare global {
interface Window {
queryLocalFonts(): Promise<Font[]>;
openChangelogLink: ()=> void;
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/app-desktop/gui/MainScreen/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import NotePropertiesDialog from '../NotePropertiesDialog';
import { NoteListColumns } from '@joplin/lib/services/plugins/api/noteListType';
import validateColumns from '../NoteListHeader/utils/validateColumns';
import TrashNotification from '../TrashNotification/TrashNotification';
import UpdateNotification from '../UpdateNotification/UpdateNotification';

const PluginManager = require('@joplin/lib/services/PluginManager');
const ipcRenderer = require('electron').ipcRenderer;
Expand Down Expand Up @@ -935,6 +936,7 @@ class MainScreenComponent extends React.Component<Props, State> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
dispatch={this.props.dispatch as any}
/>
<UpdateNotification themeId={this.props.themeId} />
{messageComp}
{layoutComp}
{pluginDialog}
Expand Down
106 changes: 106 additions & 0 deletions packages/app-desktop/gui/UpdateNotification/UpdateNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as React from 'react';
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import { themeStyle } from '@joplin/lib/theme';
import NotyfContext from '../NotyfContext';
import { UpdateInfo } from 'electron-updater';
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { AutoUpdaterEvents } from '../../services/autoUpdater/AutoUpdaterService';
import { NotyfNotification } from 'notyf';
import { _ } from '@joplin/lib/locale';
import { htmlentities } from '@joplin/utils/html';

interface UpdateNotificationProps {
themeId: number;
}

export enum UpdateNotificationEvents {
ApplyUpdate = 'apply-update',
Dismiss = 'dismiss-update-notification',
}

const changelogLink = 'https://github.com/laurent22/joplin/releases';

window.openChangelogLink = () => {
ipcRenderer.send('open-link', changelogLink);
};

const UpdateNotification = ({ themeId }: UpdateNotificationProps) => {
const notyfContext = useContext(NotyfContext);
const notificationRef = useRef<NotyfNotification | null>(null); // Use ref to hold the current notification

const theme = useMemo(() => themeStyle(themeId), [themeId]);

const notyf = useMemo(() => {
const output = notyfContext;
output.options.types = notyfContext.options.types.map(type => {
if (type.type === 'success') {
type.background = theme.backgroundColor5;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
(type.icon as any).color = theme.backgroundColor5;
}
return type;
});
return output;
}, [notyfContext, theme]);

const handleDismissNotification = useCallback(() => {
notyf.dismiss(notificationRef.current);
notificationRef.current = null;
}, [notyf]);

const handleApplyUpdate = useCallback(() => {
ipcRenderer.send('apply-update-now');
handleDismissNotification();
}, [handleDismissNotification]);


const handleUpdateDownloaded = useCallback((_event: IpcRendererEvent, info: UpdateInfo) => {
if (notificationRef.current) return;

const updateAvailableHtml = htmlentities(_('A new update (%s) is available', info.version));
const seeChangelogHtml = htmlentities(_('See changelog'));
const restartNowHtml = htmlentities(_('Restart now'));
const updateLaterHtml = htmlentities(_('Update later'));

const messageHtml = `
<div class="update-notification" style="color: ${theme.color2};">
${updateAvailableHtml} <a href="#" onclick="openChangelogLink()" style="color: ${theme.color2};">${seeChangelogHtml}</a>
<div style="display: flex; gap: 10px; margin-top: 8px;">
<button onclick="document.dispatchEvent(new CustomEvent('${UpdateNotificationEvents.ApplyUpdate}'))" class="notyf__button notyf__button--confirm" style="color: ${theme.color2};">${restartNowHtml}</button>
<button onclick="document.dispatchEvent(new CustomEvent('${UpdateNotificationEvents.Dismiss}'))" class="notyf__button notyf__button--dismiss" style="color: ${theme.color2};">${updateLaterHtml}</button>
</div>
</div>
`;

const notification: NotyfNotification = notyf.open({
type: 'success',
message: messageHtml,
position: {
x: 'right',
y: 'bottom',
},
duration: 0,
});

notificationRef.current = notification;
}, [notyf, theme]);

useEffect(() => {
ipcRenderer.on(AutoUpdaterEvents.UpdateDownloaded, handleUpdateDownloaded);
document.addEventListener(UpdateNotificationEvents.ApplyUpdate, handleApplyUpdate);
document.addEventListener(UpdateNotificationEvents.Dismiss, handleDismissNotification);

return () => {
ipcRenderer.removeListener(AutoUpdaterEvents.UpdateDownloaded, handleUpdateDownloaded);
document.removeEventListener(UpdateNotificationEvents.ApplyUpdate, handleApplyUpdate);
document.removeEventListener(UpdateNotificationEvents.Dismiss, handleDismissNotification);
};
}, [handleApplyUpdate, handleDismissNotification, handleUpdateDownloaded]);


return (
<div style={{ display: 'none' }}/>
);
};

export default UpdateNotification;
27 changes: 27 additions & 0 deletions packages/app-desktop/gui/UpdateNotification/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.update-notification {
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;
}
}
1 change: 1 addition & 0 deletions packages/app-desktop/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@use 'gui/NoteList/style.scss' as note-list;
@use 'gui/JoplinCloudLoginScreen.scss' as joplin-cloud-login-screen;
@use 'gui/NoteListHeader/style.scss' as note-list-header;
@use 'gui/UpdateNotification/style.scss' as update-notification;
@use 'gui/TrashNotification/style.scss' as trash-notification;
@use 'gui/Sidebar/style.scss' as sidebar-styles;
@use 'gui/styles/index.scss';
Expand Down
7 changes: 7 additions & 0 deletions packages/lib/utils/processStartFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ const processStartFlags = async (argv: string[], setDefaults = true) => {
continue;
}

if (arg === '--updated') {
// Electron-specific flag - ignore it
// Allows to restart with the updated application after the update option is selected by the user
argv.splice(0, 1);
continue;
}

if (arg.length && arg[0] === '-') {
throw new JoplinError(_('Unknown flag: %s', arg), 'flagError');
} else {
Expand Down

0 comments on commit 88b3c7f

Please sign in to comment.