summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-11-11 18:04:15 +0100
committerOskar Nyberg <oskar@mullvad.net>2020-11-13 19:53:59 +0100
commit90db7b7922192cc96eae37caa1f20e2fefe6bc15 (patch)
treefa371841095d5ff8f3f0a25908d436f514d2b50c /gui/src
parent52499b482483ba1dac7fc8d756f2bf671362b178 (diff)
downloadmullvadvpn-90db7b7922192cc96eae37caa1f20e2fefe6bc15.tar.xz
mullvadvpn-90db7b7922192cc96eae37caa1f20e2fefe6bc15.zip
Update notification messages
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/main/index.ts21
-rw-r--r--gui/src/main/notification-controller.ts4
-rw-r--r--gui/src/shared/auth-failure.ts14
-rw-r--r--gui/src/shared/notifications/account-expired.ts5
-rw-r--r--gui/src/shared/notifications/block-when-disconnected.ts18
-rw-r--r--gui/src/shared/notifications/close-to-account-expiry.ts12
-rw-r--r--gui/src/shared/notifications/connected.ts2
-rw-r--r--gui/src/shared/notifications/connecting.ts1
-rw-r--r--gui/src/shared/notifications/disconnected.ts12
-rw-r--r--gui/src/shared/notifications/error.ts214
-rw-r--r--gui/src/shared/notifications/inconsistent-version.ts12
-rw-r--r--gui/src/shared/notifications/notification.ts2
-rw-r--r--gui/src/shared/notifications/reconnecting.ts1
-rw-r--r--gui/src/shared/notifications/unsupported-version.ts27
-rw-r--r--gui/src/shared/notifications/update-available.ts43
15 files changed, 175 insertions, 213 deletions
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 95cfc35a55..514fa4b4e8 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -40,6 +40,7 @@ import {
CloseToAccountExpiryNotificationProvider,
InconsistentVersionNotificationProvider,
UnsupportedVersionNotificationProvider,
+ UpdateAvailableNotificationProvider,
} from '../shared/notifications/notification';
import consumePromise from '../shared/promise';
import { Scheduler } from '../shared/scheduler';
@@ -802,12 +803,20 @@ class ApplicationMain {
this.upgradeVersion = latestVersionInfo;
// notify user to update the app if it became unsupported
- const notificationProvider = new UnsupportedVersionNotificationProvider({
- supported: latestVersionInfo.supported,
- consistent: this.currentVersion.isConsistent,
- suggestedUpgrade: latestVersionInfo.suggestedUpgrade,
- });
- if (notificationProvider.mayDisplay()) {
+ const notificationProviders = [
+ new UnsupportedVersionNotificationProvider({
+ supported: latestVersionInfo.supported,
+ consistent: this.currentVersion.isConsistent,
+ suggestedUpgrade: latestVersionInfo.suggestedUpgrade,
+ }),
+ new UpdateAvailableNotificationProvider({
+ suggestedUpgrade: latestVersionInfo.suggestedUpgrade,
+ }),
+ ];
+ const notificationProvider = notificationProviders.find((notificationProvider) =>
+ notificationProvider.mayDisplay(),
+ );
+ if (notificationProvider) {
this.notificationController.notify(notificationProvider.getSystemNotification());
}
diff --git a/gui/src/main/notification-controller.ts b/gui/src/main/notification-controller.ts
index c5c30b42ef..95c1216b72 100644
--- a/gui/src/main/notification-controller.ts
+++ b/gui/src/main/notification-controller.ts
@@ -4,7 +4,6 @@ import os from 'os';
import path from 'path';
import { TunnelState } from '../shared/daemon-rpc-types';
import {
- BlockWhenDisconnectedNotificationProvider,
ConnectedNotificationProvider,
ConnectingNotificationProvider,
DisconnectedNotificationProvider,
@@ -58,8 +57,7 @@ export default class NotificationController {
new ConnectingNotificationProvider({ tunnelState, reconnecting: this.reconnecting }),
new ConnectedNotificationProvider(tunnelState),
new ReconnectingNotificationProvider(tunnelState),
- new BlockWhenDisconnectedNotificationProvider({ tunnelState, blockWhenDisconnected }),
- new DisconnectedNotificationProvider(tunnelState),
+ new DisconnectedNotificationProvider({ tunnelState, blockWhenDisconnected }),
new ErrorNotificationProvider({ tunnelState, accountExpiry }),
];
diff --git a/gui/src/shared/auth-failure.ts b/gui/src/shared/auth-failure.ts
index bbd990bf89..f8db50d82e 100644
--- a/gui/src/shared/auth-failure.ts
+++ b/gui/src/shared/auth-failure.ts
@@ -60,22 +60,22 @@ function messageForFailureKind(kind: AuthFailureKind): string {
case AuthFailureKind.invalidAccount:
return messages.pgettext(
'auth-failure',
- "You've logged in with an account number that is not valid. Please log out and try another one.",
+ 'You are logged in with an invalid account number. Please log out and try another one.',
);
case AuthFailureKind.expiredAccount:
- return messages.pgettext(
- 'auth-failure',
- 'You have no more VPN time left on this account. Please log in on our website to buy more credit.',
- );
+ return messages.pgettext('auth-failure', 'Blocking internet: account is out of time');
case AuthFailureKind.tooManyConnections:
return messages.pgettext(
'auth-failure',
- 'This account has too many simultaneous connections. Disconnect another device or try connecting again shortly.',
+ 'Too many simultaneous connections on this account. Disconnect another device or try connecting again shortly.',
);
case AuthFailureKind.unknown:
- return messages.pgettext('auth-failure', 'Account authentication failed.');
+ return messages.pgettext(
+ 'auth-failure',
+ 'Unable to authenticate account. Please contact support.',
+ );
}
}
diff --git a/gui/src/shared/notifications/account-expired.ts b/gui/src/shared/notifications/account-expired.ts
index c89845bc4b..cbfc782eeb 100644
--- a/gui/src/shared/notifications/account-expired.ts
+++ b/gui/src/shared/notifications/account-expired.ts
@@ -22,10 +22,7 @@ export class AccountExpiredNotificationProvider implements SystemNotificationPro
public getSystemNotification(): SystemNotification {
return {
- message: messages.pgettext(
- 'notifications',
- 'You have no more VPN time left on this account.',
- ),
+ message: messages.pgettext('notifications', 'Account is out of time'),
critical: true,
presentOnce: { value: true, name: this.constructor.name },
action: {
diff --git a/gui/src/shared/notifications/block-when-disconnected.ts b/gui/src/shared/notifications/block-when-disconnected.ts
index 16b65c0672..fb19655993 100644
--- a/gui/src/shared/notifications/block-when-disconnected.ts
+++ b/gui/src/shared/notifications/block-when-disconnected.ts
@@ -1,18 +1,13 @@
import { messages } from '../../shared/gettext';
import { TunnelState } from '../daemon-rpc-types';
-import {
- InAppNotification,
- InAppNotificationProvider,
- SystemNotificationProvider,
-} from './notification';
+import { InAppNotification, InAppNotificationProvider } from './notification';
interface BlockWhenDisconnectedNotificationContext {
tunnelState: TunnelState;
blockWhenDisconnected: boolean;
}
-export class BlockWhenDisconnectedNotificationProvider
- implements InAppNotificationProvider, SystemNotificationProvider {
+export class BlockWhenDisconnectedNotificationProvider implements InAppNotificationProvider {
public constructor(private context: BlockWhenDisconnectedNotificationContext) {}
public mayDisplay() {
@@ -23,16 +18,9 @@ export class BlockWhenDisconnectedNotificationProvider
);
}
- public getSystemNotification() {
- return {
- message: messages.pgettext('notifications', 'Blocking internet'),
- critical: false,
- };
- }
-
public getInAppNotification(): InAppNotification {
return {
- indicator: 'error',
+ indicator: 'warning',
title: messages.pgettext('in-app-notifications', 'BLOCKING INTERNET'),
subtitle: messages.pgettext('in-app-notifications', '"Always require VPN" is enabled.'),
};
diff --git a/gui/src/shared/notifications/close-to-account-expiry.ts b/gui/src/shared/notifications/close-to-account-expiry.ts
index ad1969929f..2e3c5ae49e 100644
--- a/gui/src/shared/notifications/close-to-account-expiry.ts
+++ b/gui/src/shared/notifications/close-to-account-expiry.ts
@@ -32,7 +32,10 @@ export class CloseToAccountExpiryNotificationProvider
// TRANSLATORS: The system notification displayed to the user when the account credit is close to expiry.
// TRANSLATORS: Available placeholder:
// TRANSLATORS: %(duration)s - remaining time, e.g. "2 days"
- messages.pgettext('notifications', 'Account credit expires in %(duration)s'),
+ messages.pgettext(
+ 'notifications',
+ 'Account credit expires in %(duration)s. Buy more credit.',
+ ),
{
duration: formatDurationUntilExpiry(this.context.accountExpiry, this.context.locale),
},
@@ -51,10 +54,15 @@ export class CloseToAccountExpiryNotificationProvider
}
public getInAppNotification(): InAppNotification {
+ const subtitle = sprintf(
+ messages.pgettext('in-app-notifications', '%(duration)s. Buy more credit.'),
+ { duration: formatRemainingTime(this.context.accountExpiry, this.context.locale, true) },
+ );
+
return {
indicator: 'warning',
title: messages.pgettext('in-app-notifications', 'ACCOUNT CREDIT EXPIRES SOON'),
- subtitle: formatRemainingTime(this.context.accountExpiry, this.context.locale, true),
+ subtitle,
action: { type: 'open-url', url: links.purchase, withAuth: true },
};
}
diff --git a/gui/src/shared/notifications/connected.ts b/gui/src/shared/notifications/connected.ts
index 901b386e97..0ce1dceeb5 100644
--- a/gui/src/shared/notifications/connected.ts
+++ b/gui/src/shared/notifications/connected.ts
@@ -10,7 +10,7 @@ export class ConnectedNotificationProvider implements SystemNotificationProvider
public getSystemNotification() {
if (this.context.state === 'connected') {
- let message = messages.pgettext('notifications', 'Secured');
+ let message = messages.pgettext('notifications', 'Connected');
const location = this.context.details.location?.hostname;
if (location) {
message = sprintf(
diff --git a/gui/src/shared/notifications/connecting.ts b/gui/src/shared/notifications/connecting.ts
index 822be2bc74..3b574dad18 100644
--- a/gui/src/shared/notifications/connecting.ts
+++ b/gui/src/shared/notifications/connecting.ts
@@ -47,7 +47,6 @@ export class ConnectingNotificationProvider
public getInAppNotification(): InAppNotification {
return {
- indicator: 'error',
title: messages.pgettext('in-app-notifications', 'BLOCKING INTERNET'),
};
}
diff --git a/gui/src/shared/notifications/disconnected.ts b/gui/src/shared/notifications/disconnected.ts
index 52d6ba96db..6a867a7452 100644
--- a/gui/src/shared/notifications/disconnected.ts
+++ b/gui/src/shared/notifications/disconnected.ts
@@ -2,14 +2,20 @@ import { messages } from '../../shared/gettext';
import { TunnelState } from '../daemon-rpc-types';
import { SystemNotificationProvider } from './notification';
+interface DisconnectedNotificationContext {
+ tunnelState: TunnelState;
+ blockWhenDisconnected: boolean;
+}
+
export class DisconnectedNotificationProvider implements SystemNotificationProvider {
- public constructor(private context: TunnelState) {}
+ public constructor(private context: DisconnectedNotificationContext) {}
- public mayDisplay = () => this.context.state === 'disconnected';
+ public mayDisplay = () =>
+ this.context.tunnelState.state === 'disconnected' && !this.context.blockWhenDisconnected;
public getSystemNotification() {
return {
- message: messages.pgettext('notifications', 'Unsecured'),
+ message: messages.pgettext('notifications', 'Disconnected and unsecured'),
critical: false,
};
}
diff --git a/gui/src/shared/notifications/error.ts b/gui/src/shared/notifications/error.ts
index 7b438edb54..d9fd12de1a 100644
--- a/gui/src/shared/notifications/error.ts
+++ b/gui/src/shared/notifications/error.ts
@@ -1,13 +1,6 @@
-import { sprintf } from 'sprintf-js';
import { hasExpired } from '../account-expiry';
import { AuthFailureKind, parseAuthFailure } from '../auth-failure';
-import {
- IErrorState,
- TunnelState,
- TunnelParameterError,
- ErrorStateCause,
- FirewallPolicyError,
-} from '../daemon-rpc-types';
+import { IErrorState, TunnelState, TunnelParameterError } from '../daemon-rpc-types';
import { messages } from '../gettext';
import {
InAppNotification,
@@ -29,10 +22,7 @@ export class ErrorNotificationProvider
public getSystemNotification() {
return this.context.tunnelState.state === 'error'
? {
- message: getSystemNotificationMessage(
- this.context.tunnelState,
- this.context.accountExpiry,
- ),
+ message: getMessage(this.context.tunnelState.details, this.context.accountExpiry),
critical: !!this.context.tunnelState.details.blockFailure,
}
: undefined;
@@ -41,125 +31,99 @@ export class ErrorNotificationProvider
public getInAppNotification(): InAppNotification | undefined {
return this.context.tunnelState.state === 'error'
? {
- indicator: 'error',
+ indicator:
+ this.context.tunnelState.details.cause.reason === 'is_offline' ? 'warning' : 'error',
title: !this.context.tunnelState.details.blockFailure
? messages.pgettext('in-app-notifications', 'BLOCKING INTERNET')
- : messages.pgettext('in-app-notifications', 'YOU MIGHT BE LEAKING NETWORK TRAFFIC'),
- subtitle: getInAppNotificationSubtitle(this.context.tunnelState),
+ : messages.pgettext('in-app-notifications', 'NETWORK TRAFFIC MIGHT BE LEAKING'),
+ subtitle: getMessage(this.context.tunnelState.details, this.context.accountExpiry),
}
: undefined;
}
}
-function getSystemNotificationMessage(
- tunnelState: { state: 'error'; details: IErrorState },
- accountExpiry?: string,
-) {
- if (tunnelState.details.blockFailure) {
- return messages.pgettext('notifications', 'Critical error (your attention is required)');
- } else if (
- (tunnelState.details.cause.reason === 'auth_failed' &&
- parseAuthFailure(tunnelState.details.cause.details).kind ===
- AuthFailureKind.expiredAccount) ||
- (accountExpiry && hasExpired(accountExpiry))
- ) {
- return sprintf('%(blocking)s %(message)s', {
- blocking: messages.pgettext('notifications', 'Blocking internet:'),
- message: messages.pgettext(
- 'notifications',
- 'You have no more VPN time left on this account.',
- ),
- });
- } else if (
- tunnelState.details.cause.reason === 'tunnel_parameter_error' &&
- tunnelState.details.cause.details === 'no_wireguard_key'
- ) {
- return messages.pgettext('notifications', 'Blocking internet: Valid WireGuard key is missing');
- } else {
- return messages.pgettext('notifications', 'Blocking internet');
- }
-}
-
-function getInAppNotificationSubtitle(tunnelState: { state: 'error'; details: IErrorState }) {
- let blockFailureMessage = null;
- if (tunnelState.details.blockFailure) {
- const extraMessage = getPolicyMessage(tunnelState.details.blockFailure);
- blockFailureMessage = `${messages.pgettext(
- 'in-app-notifications',
- 'Failed to block all network traffic',
- )}${extraMessage ? '. ' + extraMessage : ''}`;
- }
-
- const blockMessage = getBlockMessage(tunnelState.details.cause);
- return blockFailureMessage
- ? `${blockFailureMessage}. ${messages.pgettext(
- 'in-app-notifications',
- 'Original block reason',
- )}: ${blockMessage}`
- : blockMessage;
-}
-
-function getBlockMessage(blockReason: ErrorStateCause): string {
- switch (blockReason.reason) {
- case 'auth_failed':
- return parseAuthFailure(blockReason.details).message;
- case 'ipv6_unavailable':
- return messages.pgettext(
- 'in-app-notifications',
- 'Could not configure IPv6, please enable it on your system or disable it in the app',
- );
- case 'set_firewall_policy_error': {
- const extraMessage = getPolicyMessage(blockReason.details);
- return `${messages.pgettext(
- 'in-app-notifications',
- 'Failed to apply firewall rules. The device might currently be unsecured',
- )}${extraMessage ? '. ' + extraMessage : ''}`;
+function getMessage(errorDetails: IErrorState, accountExpiry?: string): string {
+ if (errorDetails.blockFailure) {
+ if (errorDetails.cause.reason === 'set_firewall_policy_error') {
+ switch (process.platform) {
+ case 'win32':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to block all network traffic. Try disabling any third-party antivirus or security software or contact support.',
+ );
+ case 'linux':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to block all network traffic. Try updating your kernel or contact support.',
+ );
+ }
}
- case 'set_dns_error':
- return messages.pgettext('in-app-notifications', 'Failed to set system DNS server');
- case 'start_tunnel_error':
- return messages.pgettext('in-app-notifications', 'Failed to start tunnel connection');
- case 'tunnel_parameter_error':
- return getTunnelParameterMessage(blockReason.details);
- case 'is_offline':
- return messages.pgettext(
- 'in-app-notifications',
- 'This device is offline, no tunnels can be established',
- );
- case 'tap_adapter_problem':
- return messages.pgettext(
- 'in-app-notifications',
- "Unable to detect a working TAP adapter on this device. If you've disabled it, enable it again. Otherwise, please reinstall the app",
- );
- }
-}
-function getPolicyMessage(err: FirewallPolicyError): string | null {
- let extraMessage = null;
- switch (process.platform) {
- case 'linux':
- extraMessage = messages.pgettext('in-app-notifications', 'Your kernel may be outdated');
- break;
- case 'win32':
- switch (err.reason) {
- case 'locked':
- if (err.details) {
- // TODO: Check if this message is ok
- extraMessage = `${messages.pgettext(
- 'in-app-notifications',
- 'An application prevented the policy from being set',
- )}: ${err.details.name}`;
- } else {
- extraMessage = messages.pgettext(
- 'in-app-notifications',
- 'This might be caused by third party security software',
- );
- }
- break;
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to block all network traffic. Please troubleshoot or contact support.',
+ );
+ } else {
+ switch (errorDetails.cause.reason) {
+ case 'auth_failed': {
+ const authFailure = parseAuthFailure(errorDetails.cause.details);
+ if (
+ authFailure.kind === AuthFailureKind.unknown &&
+ accountExpiry &&
+ hasExpired(accountExpiry)
+ ) {
+ return messages.pgettext(
+ 'auth-failure',
+ 'You are logged in with an invalid account number. Please log out and try another one.',
+ );
+ } else {
+ return authFailure.message;
+ }
}
- break;
+ case 'ipv6_unavailable':
+ return messages.pgettext(
+ 'notifications',
+ 'Could not configure IPv6. Disable it in the app or enable it on your device.',
+ );
+ case 'set_firewall_policy_error':
+ switch (process.platform) {
+ case 'win32':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to apply firewall rules. Try disabling any third-party antivirus or security software.',
+ );
+ case 'linux':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to apply firewall rules. Try updating your kernel.',
+ );
+ default:
+ return messages.pgettext('notifications', 'Unable to apply firewall rules.');
+ }
+ case 'set_dns_error':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to set system DNS server. Please contact support.',
+ );
+ case 'start_tunnel_error':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to start tunnel connection. Please contact support.',
+ );
+ case 'tunnel_parameter_error':
+ return getTunnelParameterMessage(errorDetails.cause.details);
+ case 'is_offline':
+ return messages.pgettext(
+ 'notifications',
+ "Your device is offline. Try connecting when it's back online.",
+ );
+ case 'tap_adapter_problem':
+ return messages.pgettext(
+ 'notifications',
+ 'Unable to detect a working TAP adapter on this device. Try enabling it. Otherwise, please reinstall the app.',
+ );
+ }
}
- return extraMessage;
}
function getTunnelParameterMessage(err: TunnelParameterError): string {
@@ -168,18 +132,18 @@ function getTunnelParameterMessage(err: TunnelParameterError): string {
case 'no_matching_bridge_relay':
case 'no_matching_relay':
return messages.pgettext(
- 'in-app-notifications',
- 'No relay server matches the current settings. You can try changing the location or the relay settings.',
+ 'notifications',
+ "Your selected server and tunnel protocol don't match. Please adjust your settings.",
);
case 'no_wireguard_key':
return messages.pgettext(
- 'in-app-notifications',
+ 'notifications',
'Valid WireGuard key is missing. Manage keys under Advanced settings.',
);
case 'custom_tunnel_host_resultion_error':
return messages.pgettext(
- 'in-app-notifications',
- 'Failed to resolve host of custom tunnel. Consider changing the settings',
+ 'notifications',
+ 'Unable to resolve host of custom tunnel. Try changing your settings.',
);
}
}
diff --git a/gui/src/shared/notifications/inconsistent-version.ts b/gui/src/shared/notifications/inconsistent-version.ts
index 94c33bd925..e6118c883e 100644
--- a/gui/src/shared/notifications/inconsistent-version.ts
+++ b/gui/src/shared/notifications/inconsistent-version.ts
@@ -18,10 +18,7 @@ export class InconsistentVersionNotificationProvider
public getSystemNotification(): SystemNotification {
return {
- message: messages.pgettext(
- 'notifications',
- 'Inconsistent internal version information, please restart the app',
- ),
+ message: messages.pgettext('notifications', 'App is out of sync. Please quit and restart.'),
critical: true,
presentOnce: { value: true, name: this.constructor.name },
suppressInDevelopment: true,
@@ -31,11 +28,8 @@ export class InconsistentVersionNotificationProvider
public getInAppNotification(): InAppNotification {
return {
indicator: 'error',
- title: messages.pgettext('in-app-notifications', 'INCONSISTENT VERSION'),
- subtitle: messages.pgettext(
- 'in-app-notifications',
- 'Inconsistent internal version information, please restart the app',
- ),
+ title: messages.pgettext('in-app-notifications', 'APP IS OUT OF SYNC'),
+ subtitle: messages.pgettext('in-app-notifications', 'Please quit and restart the app.'),
};
}
}
diff --git a/gui/src/shared/notifications/notification.ts b/gui/src/shared/notifications/notification.ts
index 4bfd90463f..570bf2f12b 100644
--- a/gui/src/shared/notifications/notification.ts
+++ b/gui/src/shared/notifications/notification.ts
@@ -20,7 +20,7 @@ export interface SystemNotification {
}
export interface InAppNotification {
- indicator: InAppNotificationIndicatorType;
+ indicator?: InAppNotificationIndicatorType;
title: string;
subtitle?: string;
action?: NotificationAction;
diff --git a/gui/src/shared/notifications/reconnecting.ts b/gui/src/shared/notifications/reconnecting.ts
index 2b328aeab2..9c20cad8c9 100644
--- a/gui/src/shared/notifications/reconnecting.ts
+++ b/gui/src/shared/notifications/reconnecting.ts
@@ -23,7 +23,6 @@ export class ReconnectingNotificationProvider
public getInAppNotification(): InAppNotification {
return {
- indicator: 'error',
title: messages.pgettext('in-app-notifications', 'BLOCKING INTERNET'),
};
}
diff --git a/gui/src/shared/notifications/unsupported-version.ts b/gui/src/shared/notifications/unsupported-version.ts
index 097a142815..f324c38291 100644
--- a/gui/src/shared/notifications/unsupported-version.ts
+++ b/gui/src/shared/notifications/unsupported-version.ts
@@ -1,4 +1,3 @@
-import { sprintf } from 'sprintf-js';
import { links } from '../../config.json';
import { messages } from '../../shared/gettext';
import {
@@ -23,9 +22,8 @@ export class UnsupportedVersionNotificationProvider
}
public getSystemNotification(): SystemNotification {
- const message = this.getMessage();
return {
- message,
+ message: this.getMessage(),
critical: true,
action: {
type: 'open-url',
@@ -38,32 +36,19 @@ export class UnsupportedVersionNotificationProvider
}
public getInAppNotification(): InAppNotification {
- const subtitle = this.getMessage();
-
return {
indicator: 'error',
title: messages.pgettext('in-app-notifications', 'UNSUPPORTED VERSION'),
- subtitle,
+ subtitle: this.getMessage(),
action: { type: 'open-url', url: links.download },
};
}
private getMessage(): string {
// TRANSLATORS: The in-app banner and system notification which are displayed to the user when the running app becomes unsupported.
- let message = messages.pgettext('notifications', 'You are running an unsupported app version.');
- if (this.context.suggestedUpgrade) {
- message += ' ';
- message += sprintf(
- // TRANSLATORS: Appendix to the system notification and in-app banner about the app becoming unsupported with the suggested supported version.
- // TRANSLATORS: Available placeholder:
- // TRANSLATORS: %(version) - the newest available version of the app
- messages.pgettext(
- 'notifications',
- 'Please upgrade to %(version)s now to ensure your security',
- ),
- { version: this.context.suggestedUpgrade },
- );
- }
- return message;
+ return messages.pgettext(
+ 'notifications',
+ 'Your privacy might be at risk with this unsupported app version. Please update now.',
+ );
}
}
diff --git a/gui/src/shared/notifications/update-available.ts b/gui/src/shared/notifications/update-available.ts
index d386765087..220f371e88 100644
--- a/gui/src/shared/notifications/update-available.ts
+++ b/gui/src/shared/notifications/update-available.ts
@@ -1,13 +1,18 @@
-import { sprintf } from 'sprintf-js';
import { links } from '../../config.json';
import { messages } from '../../shared/gettext';
-import { InAppNotification, InAppNotificationProvider } from './notification';
+import {
+ InAppNotification,
+ InAppNotificationProvider,
+ SystemNotification,
+ SystemNotificationProvider,
+} from './notification';
interface UpdateAvailableNotificationContext {
suggestedUpgrade?: string;
}
-export class UpdateAvailableNotificationProvider implements InAppNotificationProvider {
+export class UpdateAvailableNotificationProvider
+ implements InAppNotificationProvider, SystemNotificationProvider {
public constructor(private context: UpdateAvailableNotificationContext) {}
public mayDisplay() {
@@ -15,22 +20,32 @@ export class UpdateAvailableNotificationProvider implements InAppNotificationPro
}
public getInAppNotification(): InAppNotification {
- const subtitle = sprintf(
+ return {
+ indicator: 'warning',
+ title: messages.pgettext('in-app-notifications', 'UPDATE AVAILABLE'),
// TRANSLATORS: The in-app banner displayed to the user when the app update is available.
- // TRANSLATORS: Available placeholders:
- // TRANSLATORS: %(version)s - the newest available version of the app
- messages.pgettext(
+ subtitle: messages.pgettext(
'in-app-notifications',
- 'Install Mullvad VPN (%(version)s) to stay up to date',
+ 'Install the latest app version to stay up to date.',
),
- { version: this.context.suggestedUpgrade },
- );
+ action: { type: 'open-url', url: links.download },
+ };
+ }
+ public getSystemNotification(): SystemNotification {
return {
- indicator: 'warning',
- title: messages.pgettext('in-app-notifications', 'UPDATE AVAILABLE'),
- subtitle,
- action: { type: 'open-url', url: links.download },
+ message: messages.pgettext(
+ 'notifications',
+ 'Update available. Install the latest app version to stay up to date',
+ ),
+ critical: false,
+ action: {
+ type: 'open-url',
+ url: links.download,
+ text: messages.pgettext('notifications', 'Upgrade'),
+ },
+ presentOnce: { value: true, name: this.constructor.name },
+ suppressInDevelopment: true,
};
}
}