summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOliver <oliver@mohlin.dev>2024-12-10 14:27:16 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-12-17 12:15:39 +0100
commit07c93b1d518e7e072a54889d3ca74d507dc742ee (patch)
tree17a6516c8027463bf945ed43e154f7ef28c6e51e
parent662eae9872baf4dddc08e668e1fbacd40111a36e (diff)
downloadmullvadvpn-07c93b1d518e7e072a54889d3ca74d507dc742ee.tar.xz
mullvadvpn-07c93b1d518e7e072a54889d3ca74d507dc742ee.zip
Show notification instead of dialog when new version installed
-rw-r--r--desktop/packages/mullvad-vpn/src/main/account.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/main/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/main/notification-controller.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/main/version.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx39
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Settings.tsx68
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SmallButton.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/notifications/index.ts14
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/notifications/new-version.ts44
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/notifications/notification.ts22
-rw-r--r--desktop/packages/mullvad-vpn/test/unit/notification-evaluation.spec.ts2
12 files changed, 128 insertions, 85 deletions
diff --git a/desktop/packages/mullvad-vpn/src/main/account.ts b/desktop/packages/mullvad-vpn/src/main/account.ts
index 024e4a7208..7fc6b48d47 100644
--- a/desktop/packages/mullvad-vpn/src/main/account.ts
+++ b/desktop/packages/mullvad-vpn/src/main/account.ts
@@ -13,7 +13,7 @@ import {
AccountExpiredNotificationProvider,
CloseToAccountExpiryNotificationProvider,
SystemNotificationCategory,
-} from '../shared/notifications/notification';
+} from '../shared/notifications';
import { Scheduler } from '../shared/scheduler';
import AccountDataCache from './account-data-cache';
import { DaemonRpc } from './daemon-rpc';
diff --git a/desktop/packages/mullvad-vpn/src/main/index.ts b/desktop/packages/mullvad-vpn/src/main/index.ts
index ea2f12199c..efbd5e1654 100644
--- a/desktop/packages/mullvad-vpn/src/main/index.ts
+++ b/desktop/packages/mullvad-vpn/src/main/index.ts
@@ -363,6 +363,7 @@ class ApplicationMain
this.daemonRpc.disconnect();
}
+ this.settings.gui.changelogDisplayedForVersion = this.version.currentVersion.gui;
for (const logger of [log, this.rendererLog]) {
try {
logger?.disposeDisposableOutputs();
diff --git a/desktop/packages/mullvad-vpn/src/main/notification-controller.ts b/desktop/packages/mullvad-vpn/src/main/notification-controller.ts
index 925bff0812..664f102709 100644
--- a/desktop/packages/mullvad-vpn/src/main/notification-controller.ts
+++ b/desktop/packages/mullvad-vpn/src/main/notification-controller.ts
@@ -16,7 +16,7 @@ import {
SystemNotificationCategory,
SystemNotificationProvider,
SystemNotificationSeverityType,
-} from '../shared/notifications/notification';
+} from '../shared/notifications';
import { Scheduler } from '../shared/scheduler';
const THROTTLE_DELAY = 500;
diff --git a/desktop/packages/mullvad-vpn/src/main/version.ts b/desktop/packages/mullvad-vpn/src/main/version.ts
index 1bab7728b4..676391e150 100644
--- a/desktop/packages/mullvad-vpn/src/main/version.ts
+++ b/desktop/packages/mullvad-vpn/src/main/version.ts
@@ -8,7 +8,7 @@ import {
SystemNotificationCategory,
UnsupportedVersionNotificationProvider,
UpdateAvailableNotificationProvider,
-} from '../shared/notifications/notification';
+} from '../shared/notifications';
import { DaemonRpc } from './daemon-rpc';
import { IpcMainEventChannel } from './ipc-event-channel';
import { NotificationSender } from './notification-controller';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
index dac62db192..6aac11c9d5 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
@@ -1,9 +1,7 @@
import { useCallback, useState } from 'react';
-import { useSelector } from 'react-redux';
import { messages } from '../../shared/gettext';
import log from '../../shared/logging';
-import { NewDeviceNotificationProvider } from '../../shared/notifications/new-device';
import {
BlockWhenDisconnectedNotificationProvider,
CloseToAccountExpiryNotificationProvider,
@@ -13,18 +11,22 @@ import {
InAppNotificationProvider,
InAppNotificationTroubleshootInfo,
InconsistentVersionNotificationProvider,
+ NewVersionNotificationProvider,
ReconnectingNotificationProvider,
UnsupportedVersionNotificationProvider,
UpdateAvailableNotificationProvider,
-} from '../../shared/notifications/notification';
+} from '../../shared/notifications';
+import { NewDeviceNotificationProvider } from '../../shared/notifications/new-device';
import { useAppContext } from '../context';
import useActions from '../lib/actionsHook';
import { transitions, useHistory } from '../lib/history';
import { formatHtml } from '../lib/html-formatter';
import { RoutePath } from '../lib/routes';
import accountActions from '../redux/account/actions';
-import { IReduxState } from '../redux/store';
+import { IReduxState, useSelector } from '../redux/store';
+import { Colors } from '../tokens';
import * as AppButton from './AppButton';
+import { Link } from './common/text';
import { ModalAlert, ModalAlertType, ModalMessage, ModalMessageList } from './Modal';
import {
NotificationActions,
@@ -59,6 +61,18 @@ export default function NotificationArea(props: IProps) {
const { hideNewDeviceBanner } = useActions(accountActions);
+ const { setDisplayedChangelog } = useAppContext();
+
+ const currentVersion = useSelector((state) => state.version.current);
+ const displayedForVersion = useSelector(
+ (state) => state.settings.guiSettings.changelogDisplayedForVersion,
+ );
+ const changelog = useSelector((state) => state.userInterface.changelog);
+
+ const close = useCallback(() => {
+ setDisplayedChangelog();
+ }, [setDisplayedChangelog]);
+
const notificationProviders: InAppNotificationProvider[] = [
new ConnectingNotificationProvider({ tunnelState }),
new ReconnectingNotificationProvider(tunnelState),
@@ -84,6 +98,12 @@ export default function NotificationArea(props: IProps) {
deviceName: account.deviceName ?? '',
close: hideNewDeviceBanner,
}),
+ new NewVersionNotificationProvider({
+ currentVersion,
+ displayedForVersion,
+ changelog,
+ close,
+ }),
new UpdateAvailableNotificationProvider(version),
);
@@ -106,7 +126,16 @@ export default function NotificationArea(props: IProps) {
{notification.title}
</NotificationTitle>
<NotificationSubtitle data-testid="notificationSubTitle">
- {formatHtml(notification.subtitle ?? '')}
+ {notification.subtitleAction?.type === 'navigate' ? (
+ <Link
+ variant="labelTiny"
+ color={Colors.white60}
+ {...notification.subtitleAction.link}>
+ {formatHtml(notification.subtitle ?? '')}
+ </Link>
+ ) : (
+ formatHtml(notification.subtitle ?? '')
+ )}
</NotificationSubtitle>
</NotificationContent>
{notification.action && <NotificationActionWrapper action={notification.action} />}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx
index 924f65ff99..12f9fbef6e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx
@@ -6,6 +6,7 @@ import { messages } from '../../shared/gettext';
import { InAppNotificationIndicatorType } from '../../shared/notifications/notification';
import { useEffectEvent, useLastDefinedValue, useStyledRef } from '../lib/utility-hooks';
import * as AppButton from './AppButton';
+import { IconButton } from './common/molecules';
import { tinyText } from './common-styles';
import ImageView from './ImageView';
@@ -81,12 +82,14 @@ export function NotificationTroubleshootDialogAction(props: NotificationActionPr
export function NotificationCloseAction(props: NotificationActionProps) {
return (
- <NotificationActionButton
+ <IconButton
aria-describedby={NOTIFICATION_AREA_ID}
aria-label={messages.pgettext('accessibility', 'Close notification')}
- onClick={props.onClick}>
- <NotificationActionButtonInner source="icon-close" width={16} tintColor={colors.white60} />
- </NotificationActionButton>
+ onClick={props.onClick}
+ icon="icon-close"
+ size="small"
+ variant="secondary"
+ />
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Settings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Settings.tsx
index 15f760941f..2bc05a5f04 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Settings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/Settings.tsx
@@ -2,20 +2,17 @@ import { useCallback } from 'react';
import { strings } from '../../config.json';
import { messages } from '../../shared/gettext';
-import { getDownloadUrl } from '../../shared/version';
import { useAppContext } from '../context';
import { useHistory } from '../lib/history';
import { RoutePath } from '../lib/routes';
import { useSelector } from '../redux/store';
-import { Colors } from '../tokens';
-import { RedButton } from './AppButton';
import * as Cell from './cell';
+import { Button } from './common/molecules/Button';
import { TitleBig } from './common/text';
import { BackAction } from './KeyboardNavigation';
import {
ButtonStack,
Footer,
- LabelStack,
Layout,
SettingsContainer,
SettingsContent,
@@ -86,7 +83,7 @@ export default function Support() {
<SettingsGroup>
<SupportButton />
- <AppVersionButton />
+ <AppInfoButton />
</SettingsGroup>
{window.env.development && (
@@ -204,59 +201,16 @@ function ApiAccessMethodsButton() {
);
}
-function AppVersionButton() {
+function AppInfoButton() {
+ const history = useHistory();
+ const navigate = useCallback(() => history.push(RoutePath.appInfo), [history]);
const appVersion = useSelector((state) => state.version.current);
- const consistentVersion = useSelector((state) => state.version.consistent);
- const upToDateVersion = useSelector((state) => (state.version.suggestedUpgrade ? false : true));
- const suggestedIsBeta = useSelector((state) => state.version.suggestedIsBeta ?? false);
- const isOffline = useSelector((state) => state.connection.isBlocked);
-
- const { openUrl } = useAppContext();
- const openDownloadLink = useCallback(
- () => openUrl(getDownloadUrl(suggestedIsBeta)),
- [openUrl, suggestedIsBeta],
- );
-
- let alertIcon;
- let footer;
- if (!consistentVersion || !upToDateVersion) {
- const inconsistentVersionMessage = messages.pgettext(
- 'settings-view',
- 'App is out of sync. Please quit and restart.',
- );
-
- const updateAvailableMessage = messages.pgettext(
- 'settings-view',
- 'Update available. Install the latest app version to stay up to date.',
- );
-
- const message = !consistentVersion ? inconsistentVersionMessage : updateAvailableMessage;
-
- alertIcon = <Cell.UntintedIcon source="icon-alert" width={18} tintColor={Colors.red} />;
- footer = (
- <Cell.CellFooter>
- <Cell.CellFooterText>{message}</Cell.CellFooterText>
- </Cell.CellFooter>
- );
- }
return (
- <>
- <Cell.CellNavigationButton
- disabled={isOffline}
- onClick={openDownloadLink}
- icon={{
- source: 'icon-extLink',
- 'aria-label': messages.pgettext('accessibility', 'Opens externally'),
- }}>
- <LabelStack>
- {alertIcon}
- <Cell.Label>{messages.pgettext('settings-view', 'App version')}</Cell.Label>
- </LabelStack>
- <Cell.SubText>{appVersion}</Cell.SubText>
- </Cell.CellNavigationButton>
- {footer}
- </>
+ <Cell.CellNavigationButton onClick={navigate}>
+ <Cell.Label>{messages.pgettext('settings-view', 'App info')}</Cell.Label>
+ <Cell.SubText>{appVersion}</Cell.SubText>
+ </Cell.CellNavigationButton>
);
}
@@ -287,10 +241,10 @@ function QuitButton() {
const tunnelState = useSelector((state) => state.connection.status);
return (
- <RedButton onClick={quit}>
+ <Button variant="destructive" onClick={quit}>
{tunnelState.state === 'disconnected'
? messages.gettext('Quit')
: messages.gettext('Disconnect & quit')}
- </RedButton>
+ </Button>
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SmallButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SmallButton.tsx
index c91fbbdb20..b9ce4a6a59 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SmallButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SmallButton.tsx
@@ -3,7 +3,6 @@ import styled from 'styled-components';
import { colors } from '../../config.json';
import { smallText } from './common-styles';
-import { MultiButtonCompatibleProps } from './MultiButton';
export enum SmallButtonColor {
blue,
@@ -84,6 +83,11 @@ const StyledTextOffset = styled.span<{ $width: number }>((props) => ({
flex: `0 1 ${props.$width}px`,
}));
+export interface MultiButtonCompatibleProps {
+ className?: string;
+ textOffset?: number;
+}
+
interface SmallButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'color'>,
MultiButtonCompatibleProps {
diff --git a/desktop/packages/mullvad-vpn/src/shared/notifications/index.ts b/desktop/packages/mullvad-vpn/src/shared/notifications/index.ts
new file mode 100644
index 0000000000..615ce42250
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/shared/notifications/index.ts
@@ -0,0 +1,14 @@
+export * from './account-expired';
+export * from './close-to-account-expiry';
+export * from './block-when-disconnected';
+export * from './connected';
+export * from './connecting';
+export * from './disconnected';
+export * from './daemon-disconnected';
+export * from './error';
+export * from './inconsistent-version';
+export * from './new-version';
+export * from './notification';
+export * from './reconnecting';
+export * from './unsupported-version';
+export * from './update-available';
diff --git a/desktop/packages/mullvad-vpn/src/shared/notifications/new-version.ts b/desktop/packages/mullvad-vpn/src/shared/notifications/new-version.ts
new file mode 100644
index 0000000000..991266adbe
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/shared/notifications/new-version.ts
@@ -0,0 +1,44 @@
+import { RoutePath } from '../../renderer/lib/routes';
+import { messages } from '../gettext';
+import { IChangelog } from '../ipc-types';
+import { InAppNotification, InAppNotificationProvider } from './notification';
+
+interface NewVersionNotificationContext {
+ currentVersion: string;
+ displayedForVersion: string;
+ changelog: IChangelog;
+ close: () => void;
+}
+
+export class NewVersionNotificationProvider implements InAppNotificationProvider {
+ public constructor(private context: NewVersionNotificationContext) {}
+
+ public mayDisplay = () => {
+ return (
+ this.context.displayedForVersion !== this.context.currentVersion &&
+ this.context.changelog.length > 0
+ );
+ };
+
+ public getInAppNotification(): InAppNotification {
+ const title = messages.pgettext('in-app-notifications', 'NEW VERSION INSTALLED');
+ const subtitle = messages.pgettext('in-app-notifications', "Click here to see what's new.");
+ return {
+ indicator: 'success',
+ action: { type: 'close', close: this.context.close },
+ title,
+ subtitle,
+ subtitleAction: {
+ type: 'navigate',
+ link: {
+ to: RoutePath.changelog,
+ onClick: this.context.close,
+ 'aria-label': messages.pgettext(
+ 'accessibility',
+ 'New version installed, click here to see the changelog',
+ ),
+ },
+ },
+ };
+ }
+}
diff --git a/desktop/packages/mullvad-vpn/src/shared/notifications/notification.ts b/desktop/packages/mullvad-vpn/src/shared/notifications/notification.ts
index 87166aab4d..96754811a6 100644
--- a/desktop/packages/mullvad-vpn/src/shared/notifications/notification.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/notifications/notification.ts
@@ -1,3 +1,5 @@
+import { LinkProps } from '../../renderer/components/common/text';
+
export type NotificationAction = {
type: 'open-url';
url: string;
@@ -25,6 +27,10 @@ export type InAppNotificationAction =
| {
type: 'close';
close: () => void;
+ }
+ | {
+ type: 'navigate';
+ link: Pick<LinkProps, 'to' | 'onClick' | 'aria-label'>;
};
export type InAppNotificationIndicatorType = 'success' | 'warning' | 'error';
@@ -59,9 +65,10 @@ export interface SystemNotification {
export interface InAppNotification {
indicator?: InAppNotificationIndicatorType;
+ action?: InAppNotificationAction;
title: string;
subtitle?: string;
- action?: InAppNotificationAction;
+ subtitleAction?: InAppNotificationAction;
}
export interface SystemNotificationProvider extends NotificationProvider {
@@ -71,16 +78,3 @@ export interface SystemNotificationProvider extends NotificationProvider {
export interface InAppNotificationProvider extends NotificationProvider {
getInAppNotification(): InAppNotification | undefined;
}
-
-export * from './account-expired';
-export * from './close-to-account-expiry';
-export * from './block-when-disconnected';
-export * from './connected';
-export * from './connecting';
-export * from './disconnected';
-export * from './daemon-disconnected';
-export * from './error';
-export * from './inconsistent-version';
-export * from './reconnecting';
-export * from './unsupported-version';
-export * from './update-available';
diff --git a/desktop/packages/mullvad-vpn/test/unit/notification-evaluation.spec.ts b/desktop/packages/mullvad-vpn/test/unit/notification-evaluation.spec.ts
index d05e967efc..50acda1393 100644
--- a/desktop/packages/mullvad-vpn/test/unit/notification-evaluation.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/unit/notification-evaluation.spec.ts
@@ -9,7 +9,7 @@ import { FirewallPolicyErrorType } from '../../src/shared/daemon-rpc-types';
import {
UnsupportedVersionNotificationProvider,
UpdateAvailableNotificationProvider,
-} from '../../src/shared/notifications/notification';
+} from '../../src/shared/notifications';
function createController() {
return new NotificationController({