summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOliver <oliver@mohlin.dev>2025-03-13 09:03:16 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-03-19 15:06:37 +0100
commitcbafe25da4a7e13199351363eb0e81208dccb9f3 (patch)
tree8d06014f3b1bd8d3e38752cdbc83b4f169fb7831
parent0f122446c65e59af0bec90630aff2089df5afb49 (diff)
downloadmullvadvpn-cbafe25da4a7e13199351363eb0e81208dccb9f3.tar.xz
mullvadvpn-cbafe25da4a7e13199351363eb0e81208dccb9f3.zip
Add no vpn server available notification
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/notifications/no-open-vpn-server-available.ts122
2 files changed, 123 insertions, 0 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts
index 513b97fefb..d949ff0428 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts
@@ -1,3 +1,4 @@
export * from './new-device';
export * from './new-version';
export * from './open-vpn-support-ending';
+export * from './no-open-vpn-server-available';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/no-open-vpn-server-available.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/no-open-vpn-server-available.ts
new file mode 100644
index 0000000000..dc8819835a
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/no-open-vpn-server-available.ts
@@ -0,0 +1,122 @@
+import { sprintf } from 'sprintf-js';
+
+import { strings } from '../../../shared/constants';
+import {
+ ErrorStateCause,
+ TunnelParameterError,
+ TunnelProtocol,
+ TunnelState,
+} from '../../../shared/daemon-rpc-types';
+import { messages } from '../../../shared/gettext';
+import {
+ InAppNotification,
+ InAppNotificationProvider,
+ InAppNotificationSubtitle,
+} from '../../../shared/notifications';
+import { IRelayLocationCountryRedux } from '../../redux/settings/reducers';
+import { RoutePath } from '../routes';
+
+interface NoOpenVpnServerAvailableNotificationContext {
+ tunnelProtocol: TunnelProtocol;
+ tunnelState: TunnelState;
+ relayLocations: IRelayLocationCountryRedux[];
+}
+
+export class NoOpenVpnServerAvailableNotificationProvider implements InAppNotificationProvider {
+ public constructor(private context: NoOpenVpnServerAvailableNotificationContext) {}
+
+ public mayDisplay = () => {
+ const { tunnelState } = this.context;
+ return (
+ tunnelState.state === 'error' &&
+ tunnelState.details.cause === ErrorStateCause.tunnelParameterError &&
+ tunnelState.details.parameterError === TunnelParameterError.noMatchingRelay
+ );
+ };
+
+ public getInAppNotification(): InAppNotification {
+ let title: string = '';
+ const subtitle: InAppNotificationSubtitle[] = [];
+ const capitalizedOpenVpn = strings.openvpn.toUpperCase();
+ if (this.anyOpenVpnLocationsEnabled()) {
+ title = sprintf(
+ // TRANSLATORS: Notification title when there are no openVPN servers
+ // TRANSLATORS: matching current settings.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(openVpn)s - Will be replaced with OPENVPN
+ messages.pgettext('in-app-notifications', 'NO %(openVpn)s SERVER AVAILABLE'),
+ { openVpn: capitalizedOpenVpn },
+ );
+ subtitle.push({
+ content: sprintf(
+ // TRANSLATORS: First part of notification subtitle when there are no openVPN servers
+ // TRANSLATORS: matching current settings. Will be followed by a link to VPN settings.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(openVpn)s - Will be replaced with OpenVPN
+ messages.pgettext(
+ 'in-app-notifications',
+ '%(openVpn)s support is ending. Switch location or',
+ ),
+ { openVpn: strings.openvpn },
+ ),
+ });
+ } else {
+ title = sprintf(
+ // TRANSLATORS: Notification title when there are no openVPN servers available.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(openVpn)s - Will be replaced with OPENVPN
+ messages.pgettext('in-app-notifications', 'NO %(openVpn)s SERVERS AVAILABLE'),
+ { openVpn: capitalizedOpenVpn },
+ );
+ subtitle.push({
+ content: sprintf(
+ // TRANSLATORS: First part of notification subtitle when there are no openVPN servers available.
+ // TRANSLATORS: Will be followed by a link to VPN settings.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(openVpn)s - Will be replaced with OpenVPN
+ messages.pgettext(
+ 'in-app-notifications',
+ '%(openVpn)s support has ended. Please update the app or',
+ ),
+ { openVpn: strings.openvpn },
+ ),
+ });
+ }
+ subtitle.push({
+ content: sprintf(
+ // TRANSLATORS: Link following the first part of the notification subtitle.
+ // TRANSLATORS: Will navigate the user to the VPN settings.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(wireGuard)s - Will be replaced with WireGuard
+ messages.pgettext('in-app-notifications', 'change tunnel protocol to %(wireGuard)s'),
+ { wireGuard: strings.wireguard },
+ ),
+ action: {
+ type: 'navigate-internal',
+ link: {
+ to: RoutePath.vpnSettings,
+ 'aria-label':
+ // TRANSLATORS: Accessibility label for link to VPN settings where
+ // TRANSLATORS: the user can change tunnel protocol.
+ messages.pgettext('accessibility', 'Go to VPN settings to change tunnel protocol'),
+ },
+ },
+ });
+
+ return {
+ indicator: 'error',
+ title,
+ subtitle,
+ };
+ }
+
+ private anyOpenVpnLocationsEnabled() {
+ return this.context.relayLocations.some((location) => {
+ return location.cities.some((city) => {
+ return city.relays.some((relay) => {
+ return relay.endpointType === 'openvpn' && relay.active;
+ });
+ });
+ });
+ }
+}