diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-11-11 18:04:15 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-11-13 19:53:59 +0100 |
| commit | 90db7b7922192cc96eae37caa1f20e2fefe6bc15 (patch) | |
| tree | fa371841095d5ff8f3f0a25908d436f514d2b50c /gui/src/shared | |
| parent | 52499b482483ba1dac7fc8d756f2bf671362b178 (diff) | |
| download | mullvadvpn-90db7b7922192cc96eae37caa1f20e2fefe6bc15.tar.xz mullvadvpn-90db7b7922192cc96eae37caa1f20e2fefe6bc15.zip | |
Update notification messages
Diffstat (limited to 'gui/src/shared')
| -rw-r--r-- | gui/src/shared/auth-failure.ts | 14 | ||||
| -rw-r--r-- | gui/src/shared/notifications/account-expired.ts | 5 | ||||
| -rw-r--r-- | gui/src/shared/notifications/block-when-disconnected.ts | 18 | ||||
| -rw-r--r-- | gui/src/shared/notifications/close-to-account-expiry.ts | 12 | ||||
| -rw-r--r-- | gui/src/shared/notifications/connected.ts | 2 | ||||
| -rw-r--r-- | gui/src/shared/notifications/connecting.ts | 1 | ||||
| -rw-r--r-- | gui/src/shared/notifications/disconnected.ts | 12 | ||||
| -rw-r--r-- | gui/src/shared/notifications/error.ts | 214 | ||||
| -rw-r--r-- | gui/src/shared/notifications/inconsistent-version.ts | 12 | ||||
| -rw-r--r-- | gui/src/shared/notifications/notification.ts | 2 | ||||
| -rw-r--r-- | gui/src/shared/notifications/reconnecting.ts | 1 | ||||
| -rw-r--r-- | gui/src/shared/notifications/unsupported-version.ts | 27 | ||||
| -rw-r--r-- | gui/src/shared/notifications/update-available.ts | 43 |
13 files changed, 159 insertions, 204 deletions
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, }; } } |
