summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2024-12-17 12:17:39 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-12-17 12:17:39 +0100
commit489a788d81e78d123b73f06dbf7ce663f48c1b33 (patch)
treed035eb3a307a420cd3bb4df3e5607acdc27c0f1a
parenta6b6f093d1d932ea825b0a25ee32e3292777d24a (diff)
parentac5676f75212fd3851324a02879d9173290f3bf7 (diff)
downloadmullvadvpn-489a788d81e78d123b73f06dbf7ce663f48c1b33.tar.xz
mullvadvpn-489a788d81e78d123b73f06dbf7ce663f48c1b33.zip
Merge branch 'implement-changes-dialog-available-elsewhere-des-1168'
-rw-r--r--desktop/packages/mullvad-vpn/locales/messages.pot59
-rw-r--r--desktop/packages/mullvad-vpn/src/config.json3
-rw-r--r--desktop/packages/mullvad-vpn/src/main/account.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/main/index.ts2
-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/app.tsx12
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/AppRouter.tsx3
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Changelog.tsx78
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/DaitaSettings.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Debug.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/MultiButton.tsx31
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx41
-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/SettingsHeader.tsx4
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SmallButton.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx4
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/CellButton.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/Container.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/Footer.tsx4
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/Label.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/Section.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common-styles.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Container.tsx28
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Flex.tsx15
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Layout.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/layout/margin.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/layout/padding.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/layout/types.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx105
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx65
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmall.tsx13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmallSemiBold.tsx13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/FootnoteMini.tsx13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/LabelTiny.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/Link.tsx62
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/Text.tsx46
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleBig.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleLarge.tsx13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleMedium.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/text/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/variables/VariablesGlobalStyle.tsx29
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/variables/color-variables.ts67
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/variables/index.ts5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/variables/radius-variables.ts15
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/variables/spacing-variables.ts33
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/variables/typography-variables.ts132
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionActionButton.tsx17
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/main-view/SelectLocationButton.tsx32
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/AppInfoView.tsx48
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/AppVersionListItem.tsx65
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/ChangelogListItem.tsx17
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/index.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/ChangelogView.tsx106
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-device.ts (renamed from desktop/packages/mullvad-vpn/src/shared/notifications/new-device.ts)6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-version.ts44
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/routes.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/styles.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/actions.ts14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/reducers.ts8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/tokens/color-tokens.ts (renamed from desktop/packages/mullvad-vpn/src/renderer/tokens/colors.ts)2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/tokens/index.ts7
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/tokens/radius-tokens.ts6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/tokens/spacing-tokens.ts (renamed from desktop/packages/mullvad-vpn/src/renderer/tokens/spacings.ts)2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/tokens/typography-tokens.ts28
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/tokens/typography.ts96
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/localization-contexts.ts4
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/notifications/inconsistent-version.ts5
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/notifications/index.ts13
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/notifications/notification.ts22
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/setup/main.ts1
-rw-r--r--desktop/packages/mullvad-vpn/test/unit/notification-evaluation.spec.ts2
85 files changed, 1190 insertions, 440 deletions
diff --git a/desktop/packages/mullvad-vpn/locales/messages.pot b/desktop/packages/mullvad-vpn/locales/messages.pot
index bb4882f513..3d6ac611fc 100644
--- a/desktop/packages/mullvad-vpn/locales/messages.pot
+++ b/desktop/packages/mullvad-vpn/locales/messages.pot
@@ -168,7 +168,6 @@ msgstr ""
msgid "Go to System Settings"
msgstr ""
-#. This is a button which closes a dialog.
msgid "Got it!"
msgstr ""
@@ -344,6 +343,10 @@ msgid "More information"
msgstr ""
msgctxt "accessibility"
+msgid "New version installed, click here to see the changelog"
+msgstr ""
+
+msgctxt "accessibility"
msgid "Opens externally"
msgstr ""
@@ -579,6 +582,22 @@ msgctxt "api-access-methods-view"
msgid "With the “Mullvad bridges” method, the app communicates with a Mullvad API server via a Mullvad bridge server. It does this by sending the traffic obfuscated by Shadowsocks."
msgstr ""
+msgctxt "app-info-view"
+msgid "App info"
+msgstr ""
+
+msgctxt "app-info-view"
+msgid "App is out of sync. Please quit and restart."
+msgstr ""
+
+msgctxt "app-info-view"
+msgid "App version"
+msgstr ""
+
+msgctxt "app-info-view"
+msgid "Update available. Install the latest app version to stay up to date."
+msgstr ""
+
msgctxt "auth-failure"
msgid "Blocking internet: account is out of time"
msgstr ""
@@ -596,7 +615,15 @@ msgid "You are logged in with an invalid account number. Please log out and try
msgstr ""
msgctxt "changelog"
-msgid "Changes in this version:"
+msgid "See full changelog"
+msgstr ""
+
+msgctxt "changelog-view"
+msgid "No updates or changes were made in this release for this platform."
+msgstr ""
+
+msgctxt "changelog-view"
+msgid "What's new"
msgstr ""
#. The selected location label displayed on the main view, when a user selected a specific host to connect to.
@@ -885,6 +912,10 @@ msgctxt "in-app-notifications"
msgid "BLOCKING INTERNET"
msgstr ""
+msgctxt "in-app-notifications"
+msgid "Click here to see what's new."
+msgstr ""
+
#. The in-app banner displayed to the user when the app update is available.
msgctxt "in-app-notifications"
msgid "Install the latest app version to stay up to date."
@@ -899,6 +930,10 @@ msgid "NEW DEVICE CREATED"
msgstr ""
msgctxt "in-app-notifications"
+msgid "NEW VERSION INSTALLED"
+msgstr ""
+
+msgctxt "in-app-notifications"
msgid "Please quit and restart the app."
msgstr ""
@@ -1523,11 +1558,7 @@ msgid "API access"
msgstr ""
msgctxt "settings-view"
-msgid "App is out of sync. Please quit and restart."
-msgstr ""
-
-msgctxt "settings-view"
-msgid "App version"
+msgid "App info"
msgstr ""
msgctxt "settings-view"
@@ -1538,10 +1569,6 @@ msgctxt "settings-view"
msgid "Support"
msgstr ""
-msgctxt "settings-view"
-msgid "Update available. Install the latest app version to stay up to date."
-msgstr ""
-
#. Navigation button to the 'User interface settings' view
msgctxt "settings-view"
msgid "User interface settings"
@@ -1552,6 +1579,10 @@ msgctxt "settings-view"
msgid "VPN settings"
msgstr ""
+msgctxt "settings-view"
+msgid "What's new"
+msgstr ""
+
msgctxt "split-tunneling-view"
msgid "%(applicationName)s is problematic and can’t be excluded from the VPN tunnel."
msgstr ""
@@ -2306,9 +2337,6 @@ msgstr ""
msgid "Always-on VPN assigned to other VPN"
msgstr ""
-msgid "App info"
-msgstr ""
-
msgid "At least one method needs to be enabled"
msgstr ""
@@ -2345,6 +2373,9 @@ msgstr ""
msgid "Changelog"
msgstr ""
+msgid "Changes in this version:"
+msgstr ""
+
msgid "Changes to DNS related settings might not go into effect immediately due to cached results."
msgstr ""
diff --git a/desktop/packages/mullvad-vpn/src/config.json b/desktop/packages/mullvad-vpn/src/config.json
index a77c47de45..43229834ca 100644
--- a/desktop/packages/mullvad-vpn/src/config.json
+++ b/desktop/packages/mullvad-vpn/src/config.json
@@ -4,7 +4,8 @@
"purchase": "https://mullvad.net/account/",
"faq": "https://mullvad.net/help/tag/mullvad-app/",
"privacyGuide": "https://mullvad.net/help/first-steps-towards-online-privacy/",
- "download": "https://mullvad.net/download/vpn/"
+ "download": "https://mullvad.net/download/vpn/",
+ "changelog": "https://github.com/mullvad/mullvadvpn-app/blob/main/CHANGELOG.md"
},
"colors": {
"darkerBlue": "rgba(25, 38, 56, 0.95)",
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..f855278ba9 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();
@@ -786,7 +787,6 @@ class ApplicationMain
splitTunnelingApplications: this.splitTunnelingApplications,
macOsScrollbarVisibility: this.macOsScrollbarVisibility,
changelog: this.changelog ?? [],
- forceShowChanges: CommandLineOptions.showChanges.match,
navigationHistory: this.navigationHistory,
currentApiAccessMethod: this.currentApiAccessMethod,
isMacOs13OrNewer: isMacOs13OrNewer(),
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/app.tsx b/desktop/packages/mullvad-vpn/src/renderer/app.tsx
index 7c7b0edbfa..67e7a4db33 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/app.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/app.tsx
@@ -39,7 +39,7 @@ import log, { ConsoleOutput } from '../shared/logging';
import { LogLevel } from '../shared/logging-types';
import { Scheduler } from '../shared/scheduler';
import AppRouter from './components/AppRouter';
-import { Changelog } from './components/Changelog';
+import { VariablesGlobalStyle } from './components/common/variables';
import ErrorBoundary from './components/ErrorBoundary';
import KeyboardNavigation from './components/KeyboardNavigation';
import Lang from './components/Lang';
@@ -243,7 +243,7 @@ export default class AppRenderer {
this.setUpgradeVersion(initialState.upgradeVersion);
this.setGuiSettings(initialState.guiSettings);
this.storeAutoStart(initialState.autoStart);
- this.setChangelog(initialState.changelog, initialState.forceShowChanges);
+ this.setChangelog(initialState.changelog);
this.setCurrentApiAccessMethod(initialState.currentApiAccessMethod);
this.reduxActions.userInterface.setIsMacOs13OrNewer(initialState.isMacOs13OrNewer);
@@ -296,8 +296,9 @@ export default class AppRenderer {
<ErrorBoundary>
<ModalContainer>
<KeyboardNavigation>
- <AppRouter />
- <Changelog />
+ <VariablesGlobalStyle>
+ <AppRouter />
+ </VariablesGlobalStyle>
</KeyboardNavigation>
{window.env.platform === 'darwin' && <MacOsScrollbarDetection />}
</ModalContainer>
@@ -1002,9 +1003,8 @@ export default class AppRenderer {
this.reduxActions.settings.updateAutoStart(autoStart);
}
- private setChangelog(changelog: IChangelog, forceShowChanges: boolean) {
+ private setChangelog(changelog: IChangelog) {
this.reduxActions.userInterface.setChangelog(changelog);
- this.reduxActions.userInterface.setForceShowChanges(forceShowChanges);
}
private updateLocation() {
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx
index 99f405b2a8..f1e5a5e5ae 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx
@@ -12,9 +12,9 @@ import { generateRoutePath } from '../lib/routeHelpers';
import { RoutePath } from '../lib/routes';
import { useBoolean } from '../lib/utility-hooks';
import { useSelector } from '../redux/store';
-import { Spacings } from '../tokens';
import * as Cell from './cell';
import { Flex } from './common/layout';
+import { Spacings } from './common/variables';
import {
ContextMenu,
ContextMenuContainer,
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/AppRouter.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/AppRouter.tsx
index 3541c095d6..6ad0a9e199 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/AppRouter.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/AppRouter.tsx
@@ -38,6 +38,7 @@ import TooManyDevices from './TooManyDevices';
import TransitionContainer, { TransitionView } from './TransitionContainer';
import UdpOverTcp from './UdpOverTcp';
import UserInterfaceSettings from './UserInterfaceSettings';
+import { AppInfoView, ChangelogView } from './views';
import VpnSettings from './VpnSettings';
import WireguardSettings from './WireguardSettings';
@@ -103,6 +104,8 @@ export default function AppRouter() {
<Route exact path={RoutePath.selectLocation} component={SelectLocation} />
<Route exact path={RoutePath.editCustomBridge} component={EditCustomBridge} />
<Route exact path={RoutePath.filter} component={Filter} />
+ <Route exact path={RoutePath.appInfo} component={AppInfoView} />
+ <Route exact path={RoutePath.changelog} component={ChangelogView} />
</Switch>
</TransitionView>
</TransitionContainer>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Changelog.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Changelog.tsx
deleted file mode 100644
index ea1b42f5c2..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Changelog.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { useCallback } from 'react';
-import styled from 'styled-components';
-
-import { messages } from '../../shared/gettext';
-import { useAppContext } from '../context';
-import { useBoolean } from '../lib/utility-hooks';
-import { useSelector } from '../redux/store';
-import * as AppButton from './AppButton';
-import { hugeText, smallText } from './common-styles';
-import { ModalAlert, ModalMessage } from './Modal';
-
-const StyledTitle = styled.h1(hugeText, {
- textAlign: 'center',
- margin: '7px 0 4px',
-});
-
-const StyledSubTitle = styled.span(smallText, {
- marginTop: '10px',
- fontWeight: 700,
-});
-
-const StyledList = styled.ul({
- listStyle: 'disc outside',
- marginLeft: '20px',
-});
-
-const StyledMessage = styled(ModalMessage)({
- fontSize: '12px',
- marginTop: '6px',
-});
-
-export function Changelog() {
- const currentVersion = useSelector((state) => state.version.current);
- const changelogDisplayedForVersion = useSelector(
- (state) => state.settings.guiSettings.changelogDisplayedForVersion,
- );
- const changelog = useSelector((state) => state.userInterface.changelog);
- const initialForceShowChanges = useSelector((state) => state.userInterface.forceShowChanges);
-
- const { setDisplayedChangelog } = useAppContext();
-
- const [forceShowChanges, , stopForceShowChanges] = useBoolean(initialForceShowChanges);
-
- const close = useCallback(() => {
- setDisplayedChangelog();
- stopForceShowChanges();
- }, [setDisplayedChangelog, stopForceShowChanges]);
-
- const visible =
- forceShowChanges ||
- (changelogDisplayedForVersion !== currentVersion &&
- changelog.length > 0 &&
- !window.env.development &&
- !/-dev-[0-9a-f]{6}$/.test(currentVersion));
-
- return (
- <ModalAlert
- isOpen={visible}
- buttons={[
- <AppButton.BlueButton key="close" onClick={close}>
- {
- // TRANSLATORS: This is a button which closes a dialog.
- messages.gettext('Got it!')
- }
- </AppButton.BlueButton>,
- ]}>
- <StyledTitle>{currentVersion}</StyledTitle>
- <StyledSubTitle>{messages.pgettext('changelog', 'Changes in this version:')}</StyledSubTitle>
- <StyledMessage>
- <StyledList>
- {changelog.map((item, i) => (
- <li key={i}>{item}</li>
- ))}
- </StyledList>
- </StyledMessage>
- </ModalAlert>
- );
-}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/DaitaSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/DaitaSettings.tsx
index 54b101d5fd..b3a88688e0 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/DaitaSettings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/DaitaSettings.tsx
@@ -8,10 +8,10 @@ import { useAppContext } from '../context';
import { useHistory } from '../lib/history';
import { useBoolean } from '../lib/utility-hooks';
import { useSelector } from '../redux/store';
-import { Spacings } from '../tokens';
import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup';
import * as Cell from './cell';
import { Flex } from './common/layout';
+import { Spacings } from './common/variables';
import InfoButton from './InfoButton';
import { BackAction } from './KeyboardNavigation';
import { Layout, SettingsContainer } from './Layout';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Debug.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Debug.tsx
index 9522b422e5..a1aa849b50 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Debug.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/Debug.tsx
@@ -3,8 +3,8 @@ import styled from 'styled-components';
import { useHistory } from '../lib/history';
import { useBoolean } from '../lib/utility-hooks';
-import { Spacings } from '../tokens';
import * as AppButton from './AppButton';
+import { Spacings } from './common/variables';
import { measurements } from './common-styles';
import { BackAction } from './KeyboardNavigation';
import { Layout, SettingsContainer } from './Layout';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx
index a7ece12a7d..6145d902ce 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx
@@ -1,7 +1,7 @@
import styled from 'styled-components';
-import { Colors, Spacings } from '../tokens';
import { Flex } from './common/layout';
+import { Colors, Spacings } from './common/variables';
import { measurements } from './common-styles';
import HeaderBar from './HeaderBar';
import { NavigationScrollbars } from './NavigationBar';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/MultiButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/MultiButton.tsx
index 3129abcbb2..ef002c023d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/MultiButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/MultiButton.tsx
@@ -1,42 +1,39 @@
import React from 'react';
import styled from 'styled-components';
-const SIDE_BUTTON_WIDTH = 44;
+import { ButtonProps } from './common/molecules';
const ButtonRow = styled.div({
display: 'flex',
- flexDirection: 'row',
+ gap: '1px',
});
const MainButton = styled.button({
- display: 'flex',
- flex: 1,
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
+ paddingLeft: '44px',
+ '&:focus-visible': {
+ zIndex: 10,
+ },
});
const SideButton = styled.button({
- display: 'flex',
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
- width: SIDE_BUTTON_WIDTH,
- marginLeft: '1px !important',
+ '&:focus-visible': {
+ zIndex: 10,
+ },
});
-export interface MultiButtonCompatibleProps {
- className?: string;
- textOffset?: number;
-}
-
-interface IMultiButtonProps {
- mainButton: React.ComponentType<MultiButtonCompatibleProps>;
- sideButton: React.ComponentType<MultiButtonCompatibleProps>;
+interface MultiButtonProps {
+ mainButton: React.ComponentType<ButtonProps>;
+ sideButton: React.ComponentType<ButtonProps>;
}
-export function MultiButton(props: IMultiButtonProps) {
+export function MultiButton(props: MultiButtonProps) {
return (
<ButtonRow>
- <MainButton as={props.mainButton} textOffset={SIDE_BUTTON_WIDTH + 1} />
+ <MainButton as={props.mainButton} />
<SideButton as={props.sideButton} />
</ButtonRow>
);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
index dac62db192..a63754a691 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,
@@ -16,15 +14,21 @@ import {
ReconnectingNotificationProvider,
UnsupportedVersionNotificationProvider,
UpdateAvailableNotificationProvider,
-} from '../../shared/notifications/notification';
+} from '../../shared/notifications';
import { useAppContext } from '../context';
import useActions from '../lib/actionsHook';
import { transitions, useHistory } from '../lib/history';
import { formatHtml } from '../lib/html-formatter';
+import {
+ NewDeviceNotificationProvider,
+ NewVersionNotificationProvider,
+} from '../lib/notifications';
import { RoutePath } from '../lib/routes';
import accountActions from '../redux/account/actions';
-import { IReduxState } from '../redux/store';
+import { IReduxState, useSelector } from '../redux/store';
import * as AppButton from './AppButton';
+import { Link } from './common/text';
+import { Colors } from './common/variables';
import { ModalAlert, ModalAlertType, ModalMessage, ModalMessageList } from './Modal';
import {
NotificationActions,
@@ -59,6 +63,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 +100,12 @@ export default function NotificationArea(props: IProps) {
deviceName: account.deviceName ?? '',
close: hideNewDeviceBanner,
}),
+ new NewVersionNotificationProvider({
+ currentVersion,
+ displayedForVersion,
+ changelog,
+ close,
+ }),
new UpdateAvailableNotificationProvider(version),
);
@@ -106,7 +128,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..2294d74a7d 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';
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/SettingsHeader.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SettingsHeader.tsx
index deac04b26b..adb8357ac8 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SettingsHeader.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SettingsHeader.tsx
@@ -1,9 +1,9 @@
import * as React from 'react';
import styled from 'styled-components';
-import { Colors, Spacings } from '../tokens';
import { Flex } from './common/layout';
import { LabelTiny, TitleBig } from './common/text';
+import { Colors, Spacings } from './common/variables';
export const HeaderTitle = styled(TitleBig)({
wordWrap: 'break-word',
@@ -11,7 +11,7 @@ export const HeaderTitle = styled(TitleBig)({
});
export const HeaderSubTitle = styled(LabelTiny).attrs({
- $color: Colors.white60,
+ color: Colors.white60,
})({});
interface SettingsHeaderProps {
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/renderer/components/SplitTunnelingSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx
index b2e25c9e63..97254d5e3f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx
@@ -14,12 +14,12 @@ import { useHistory } from '../lib/history';
import { formatHtml } from '../lib/html-formatter';
import { useEffectEvent, useStyledRef } from '../lib/utility-hooks';
import { IReduxState } from '../redux/store';
-import { Colors, Spacings } from '../tokens';
import Accordion from './Accordion';
import * as AppButton from './AppButton';
import * as Cell from './cell';
import { Flex } from './common/layout/Flex';
import { FootnoteMini } from './common/text/FootnoteMini';
+import { Colors, Spacings } from './common/variables';
import { CustomScrollbarsRef } from './CustomScrollbars';
import ImageView from './ImageView';
import { BackAction } from './KeyboardNavigation';
@@ -551,7 +551,7 @@ function MacOsSplitTunnelingAvailability({
<WideSmallButton onClick={showFullDiskAccessSettings}>
{messages.pgettext('split-tunneling-view', 'Open System Settings')}
</WideSmallButton>
- <FootnoteMini $color={Colors.white60}>
+ <FootnoteMini color={Colors.white60}>
{messages.pgettext(
'split-tunneling-view',
'Enabled "Full disk access" and still having issues?',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx
index 84764439ef..5423c8a212 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx
@@ -1,9 +1,9 @@
import styled from 'styled-components';
import { colors } from '../../config.json';
-import { Spacings } from '../tokens';
import * as AppButton from './AppButton';
import * as Cell from './cell';
+import { Spacings } from './common/variables';
import { measurements, normalText } from './common-styles';
import ImageView from './ImageView';
import { NavigationScrollbars } from './NavigationBar';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx
index c27072cab2..7c58f43ab0 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx
@@ -15,11 +15,11 @@ import { RoutePath } from '../lib/routes';
import { useBoolean } from '../lib/utility-hooks';
import { RelaySettingsRedux } from '../redux/settings/reducers';
import { useSelector } from '../redux/store';
-import { Colors, Spacings } from '../tokens';
import * as AppButton from './AppButton';
import { AriaDescription, AriaDetails, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup';
import * as Cell from './cell';
import Selector, { SelectorItem } from './cell/Selector';
+import { Colors, Spacings } from './common/variables';
import CustomDnsSettings from './CustomDnsSettings';
import InfoButton, { InfoIcon } from './InfoButton';
import { BackAction } from './KeyboardNavigation';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/CellButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/CellButton.tsx
index f637008a68..8f0336c13c 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/CellButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/CellButton.tsx
@@ -1,8 +1,8 @@
import React, { useContext } from 'react';
import styled from 'styled-components';
-import { Colors, Spacings } from '../../tokens';
import { Center } from '../common/layout';
+import { Colors, Spacings } from '../common/variables';
import { IImageViewProps } from '../ImageView';
import { CellDisabledContext } from './Container';
import { Icon } from './Label';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Container.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Container.tsx
index f8db1c45e6..551b5d7c20 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Container.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Container.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'styled-components';
-import { Spacings } from '../../tokens';
+import { Spacings } from '../common/variables';
import { Row } from './Row';
const StyledContainer = styled(Row)({
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Footer.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Footer.tsx
index 2454225638..058c96db82 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Footer.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Footer.tsx
@@ -1,12 +1,12 @@
import styled from 'styled-components';
-import { Colors, Spacings } from '../../tokens';
import { LabelTiny } from '../common/text';
+import { Colors, Spacings } from '../common/variables';
export const CellFooter = styled.div({
margin: `${Spacings.spacing1} ${Spacings.spacing6} 0px`,
});
export const CellFooterText = styled(LabelTiny).attrs({
- $color: Colors.white60,
+ color: Colors.white60,
})({});
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Label.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Label.tsx
index 3123a31dc4..5bce2c71af 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Label.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Label.tsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import styled from 'styled-components';
-import { Colors, Spacings } from '../../tokens';
+import { Colors, Spacings } from '../common/variables';
import { buttonText, normalText, tinyText } from '../common-styles';
import ImageView, { IImageViewProps } from '../ImageView';
import { CellButton } from './CellButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Section.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Section.tsx
index 0e390eb901..002b3ac478 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Section.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Section.tsx
@@ -4,9 +4,9 @@ import styled from 'styled-components';
import { useAppContext } from '../../context';
import { useHistory } from '../../lib/history';
import { useBoolean, useEffectEvent } from '../../lib/utility-hooks';
-import { Colors, Spacings } from '../../tokens';
import Accordion from '../Accordion';
import ChevronButton from '../ChevronButton';
+import { Colors, Spacings } from '../common/variables';
import { buttonText, openSans, sourceSansPro } from '../common-styles';
import { Container } from './Container';
import { Row } from './Row';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common-styles.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common-styles.ts
index fc08d8411d..51ddba958f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common-styles.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common-styles.ts
@@ -1,6 +1,6 @@
import React from 'react';
-import { Colors, Spacings } from '../tokens';
+import { Colors, Spacings } from './common/variables';
export const openSans: React.CSSProperties = {
fontFamily: 'Open Sans',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Container.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Container.tsx
new file mode 100644
index 0000000000..79e8ed316a
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Container.tsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import styled from 'styled-components';
+
+import { Spacings } from '../variables';
+import { Flex, FlexProps } from './Flex';
+
+export interface ContainerProps extends FlexProps {
+ size?: '3' | '4';
+ children: React.ReactNode;
+}
+
+const sizes: Record<'3' | '4', string> = {
+ '3': `calc(100% - ${Spacings.spacing6} * 2)`,
+ '4': `calc(100% - ${Spacings.spacing5} * 2)`,
+};
+
+const StyledFlex = styled(Flex)<{ $size: string }>((props) => ({
+ width: props.$size,
+ margin: 'auto',
+}));
+
+export const Container = React.forwardRef<HTMLDivElement, ContainerProps>(
+ ({ size = '4', ...props }, ref) => {
+ return <StyledFlex ref={ref} $size={sizes[size]} {...props} />;
+ },
+);
+
+Container.displayName = 'Container';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Flex.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Flex.tsx
index ede836194b..13a4804fa9 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Flex.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Flex.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'styled-components';
-import { Spacings } from '../../../tokens';
+import { Spacings } from '../variables';
import { Layout, LayoutProps } from './Layout';
export interface FlexProps extends LayoutProps {
@@ -12,11 +12,21 @@ export interface FlexProps extends LayoutProps {
$justifyContent?: React.CSSProperties['justifyContent'];
$flexGrow?: React.CSSProperties['flexGrow'];
$flexShrink?: React.CSSProperties['flexShrink'];
+ $flexBasis?: React.CSSProperties['flexBasis'];
children?: React.ReactNode;
}
export const Flex = styled(Layout)<FlexProps>(
- ({ $gap, $flex, $flexDirection, $alignItems, $justifyContent, $flexGrow, $flexShrink }) => ({
+ ({
+ $gap,
+ $flex,
+ $flexDirection,
+ $alignItems,
+ $justifyContent,
+ $flexGrow,
+ $flexShrink,
+ $flexBasis,
+ }) => ({
display: 'flex',
gap: $gap,
flex: $flex,
@@ -25,5 +35,6 @@ export const Flex = styled(Layout)<FlexProps>(
justifyContent: $justifyContent,
flexGrow: $flexGrow,
flexShrink: $flexShrink,
+ flexBasis: $flexBasis,
}),
);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Layout.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Layout.tsx
index fe1dfbb94e..03e4d5371d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Layout.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/Layout.tsx
@@ -1,6 +1,6 @@
import styled from 'styled-components';
-import { Spacings } from '../../../tokens';
+import { Spacings } from '../variables';
import { margin } from './margin';
import { padding } from './padding';
import { LayoutSpacings } from './types';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/margin.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/margin.ts
index c410e47c34..bcc32fcaee 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/margin.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/margin.ts
@@ -1,4 +1,4 @@
-import { Spacings } from '../../../tokens';
+import { Spacings } from '../variables';
import { LayoutSpacings } from './types';
export const all = (margin: Spacings) => ({ margin });
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/padding.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/padding.ts
index d98a209203..16c66f6e64 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/padding.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/padding.ts
@@ -1,4 +1,4 @@
-import { Spacings } from '../../../tokens';
+import { Spacings } from '../variables';
import { LayoutSpacings } from './types';
export const all = (padding: Spacings) => ({ padding });
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/types.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/types.ts
index f31c458917..1f6ff5407f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/types.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/layout/types.ts
@@ -1,4 +1,4 @@
-import { Spacings } from '../../../tokens';
+import { Spacings } from '../variables';
export interface LayoutSpacings {
all?: 'string';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts
new file mode 100644
index 0000000000..4ef5844150
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts
@@ -0,0 +1,10 @@
+export const buttonReset = {
+ border: 'none',
+ padding: 0,
+ margin: 0,
+ font: 'inherit',
+ color: 'inherit',
+ textAlign: 'inherit',
+ lineHeight: 'inherit',
+ cursor: 'default',
+} as React.CSSProperties;
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts
new file mode 100644
index 0000000000..c546f737b6
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts
@@ -0,0 +1 @@
+export * from './button-reset';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx
new file mode 100644
index 0000000000..ca8ff74883
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx
@@ -0,0 +1,105 @@
+import React, { forwardRef } from 'react';
+import styled from 'styled-components';
+
+import { Flex } from '../layout';
+import { buttonReset } from '../mixins';
+import { BodySmallSemiBold } from '../text';
+import { Colors, Radius, Spacings } from '../variables';
+
+export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
+ variant?: 'primary' | 'success' | 'destructive';
+ size?: 'tiny' | 'small' | 'regular' | 'full' | '1/2';
+ leading?: React.ReactNode;
+ trailing?: React.ReactNode;
+}
+
+const variants = {
+ primary: {
+ background: Colors.blue,
+ hover: Colors.blue60,
+ disabled: Colors.blue50,
+ },
+ success: {
+ background: Colors.green,
+ hover: Colors.green90,
+ disabled: Colors.green40,
+ },
+ destructive: {
+ background: Colors.red,
+ hover: Colors.red80,
+ disabled: Colors.red60,
+ },
+} as const;
+
+const sizes = {
+ tiny: '44px',
+ small: '60px',
+ regular: '272px',
+ full: '100%',
+ '1/2': '50%',
+};
+
+const StyledButton = styled.button({
+ ...buttonReset,
+
+ minHeight: '32px',
+ borderRadius: Radius.radius4,
+ width: 'var(--size)',
+ background: 'var(--background)',
+ '&:not(:disabled):hover': {
+ background: 'var(--hover)',
+ },
+ '&:disabled': {
+ background: 'var(--disabled)',
+ },
+ '&:focus-visible': {
+ outline: `2px solid ${Colors.white}`,
+ outlineOffset: '2px',
+ },
+});
+
+export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
+ (
+ { variant = 'primary', size = 'regular', leading, trailing, children, disabled, ...props },
+ ref,
+ ) => {
+ const styles = variants[variant];
+ return (
+ <StyledButton
+ ref={ref}
+ style={
+ {
+ '--background': styles.background,
+ '--hover': styles.hover,
+ '--disabled': styles.disabled,
+ '--size': sizes[size],
+ } as React.CSSProperties
+ }
+ disabled={disabled}
+ {...props}>
+ <Flex
+ $flex={1}
+ $gap={Spacings.spacing3}
+ $justifyContent="space-between"
+ $padding={{
+ horizontal: Spacings.spacing3,
+ }}
+ $alignItems="center">
+ {leading}
+ <Flex $flex={1} $justifyContent="center" $alignItems="center">
+ {typeof children === 'string' ? (
+ <BodySmallSemiBold color={disabled ? Colors.white40 : Colors.white}>
+ {children}
+ </BodySmallSemiBold>
+ ) : (
+ children
+ )}
+ </Flex>
+ {trailing}
+ </Flex>
+ </StyledButton>
+ );
+ },
+);
+
+Button.displayName = 'Button';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx
new file mode 100644
index 0000000000..6d43fef048
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx
@@ -0,0 +1,65 @@
+import React, { forwardRef } from 'react';
+import styled from 'styled-components';
+
+import ImageView from '../../ImageView';
+import { buttonReset } from '../mixins';
+import { Colors } from '../variables';
+
+export interface IconButtonProps
+ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
+ variant?: 'primary' | 'secondary';
+ size?: 'small' | 'regular';
+ icon: string;
+}
+
+const variants = {
+ primary: {
+ background: Colors.white,
+ hover: Colors.white60,
+ disabled: Colors.white50,
+ },
+ secondary: {
+ background: Colors.white60,
+ hover: Colors.white80,
+ disabled: Colors.white50,
+ },
+} as const;
+
+const sizes = {
+ small: 16,
+ regular: 24,
+};
+
+const StyledButton = styled.button({
+ ...buttonReset,
+
+ background: 'transparent',
+ height: 'var(--size)',
+ width: 'var(--size)',
+ '&:focus-visible': {
+ outline: `2px solid ${Colors.white}`,
+ outlineOffset: '2px',
+ borderRadius: '100%',
+ },
+});
+
+export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
+ ({ icon, variant = 'primary', size: sizeProp = 'regular', disabled, ...props }, ref) => {
+ const styles = variants[variant];
+ const size = sizes[sizeProp];
+ return (
+ <StyledButton ref={ref} disabled={disabled} {...props}>
+ <ImageView
+ source={icon}
+ tintColor={styles.background}
+ tintHoverColor={styles.hover}
+ disabled={disabled}
+ height={size}
+ width={size}
+ />
+ </StyledButton>
+ );
+ },
+);
+
+IconButton.displayName = 'Button';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts
new file mode 100644
index 0000000000..66f0cf90a7
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts
@@ -0,0 +1,2 @@
+export * from './Button';
+export * from './IconButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmall.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmall.tsx
index 9cfe1bc9c0..18ab10e202 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmall.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmall.tsx
@@ -1,8 +1,9 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
-import { typography } from '../../../tokens';
-import { Text } from './Text';
+export type BodySmallProps = Omit<TextProps, 'variant'>;
-export const BodySmall = styled(Text)({
- ...typography['bodySmall'],
-});
+export const BodySmall = ({ children, ...props }: BodySmallProps) => (
+ <Text variant="bodySmall" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmallSemiBold.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmallSemiBold.tsx
index 705a939e5d..f70a2c7b11 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmallSemiBold.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/BodySmallSemiBold.tsx
@@ -1,8 +1,9 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
-import { typography } from '../../../tokens';
-import { Text } from './Text';
+export type BodySmallSemiBoldProps = Omit<TextProps, 'variant'>;
-export const BodySmallSemiBold = styled(Text)({
- ...typography['bodySmallSemibold'],
-});
+export const BodySmallSemiBold = ({ children, ...props }: BodySmallSemiBoldProps) => (
+ <Text variant="bodySmallSemibold" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/FootnoteMini.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/FootnoteMini.tsx
index adbafb90d1..77d563e03c 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/FootnoteMini.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/FootnoteMini.tsx
@@ -1,8 +1,9 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
-import { typography } from '../../../tokens';
-import { Text } from './Text';
+export type FoonoteMiniProps = Omit<TextProps, 'variant'>;
-export const FootnoteMini = styled(Text)({
- ...typography['footnoteMini'],
-});
+export const FootnoteMini = ({ children, ...props }: FoonoteMiniProps) => (
+ <Text variant="footnoteMini" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/LabelTiny.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/LabelTiny.tsx
index 9f6f8a143c..f7a952e67e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/LabelTiny.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/LabelTiny.tsx
@@ -1,8 +1,8 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
+export type LabelTinyProps = Omit<TextProps, 'variant'>;
-import { typography } from '../../../tokens';
-import { Text } from './Text';
-
-export const LabelTiny = styled(Text)({
- ...typography['labelTiny'],
-});
+export const LabelTiny = ({ children, ...props }: LabelTinyProps) => (
+ <Text variant="labelTiny" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Link.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Link.tsx
new file mode 100644
index 0000000000..e1bb272120
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Link.tsx
@@ -0,0 +1,62 @@
+import React, { useCallback } from 'react';
+import styled from 'styled-components';
+
+import { useHistory } from '../../../lib/history';
+import { RoutePath } from '../../../lib/routes';
+import { buttonReset } from '../mixins';
+import { Colors, Radius } from '../variables';
+import { Text, TextProps } from './Text';
+
+export interface LinkProps extends TextProps, Omit<React.HtmlHTMLAttributes<'button'>, 'color'> {
+ to: RoutePath;
+}
+
+const StyledText = styled(Text)<{
+ $hoverColor: Colors | undefined;
+}>((props) => ({
+ ...buttonReset,
+ background: 'transparent',
+
+ '&:hover': {
+ textDecorationLine: 'underline',
+ textUnderlineOffset: '2px',
+ color: props.$hoverColor,
+ },
+ '&:focus-visible': {
+ borderRadius: Radius.radius4,
+ outline: `2px solid ${Colors.white}`,
+ outlineOffset: '2px',
+ },
+}));
+
+const getHoverColor = (color: Colors | undefined) => {
+ switch (color) {
+ case Colors.white60:
+ return Colors.white;
+ default:
+ return undefined;
+ }
+};
+
+export const Link = ({ to, children, color, onClick, ...props }: LinkProps) => {
+ const history = useHistory();
+ const navigate = useCallback(
+ (e: React.MouseEvent<'button'>) => {
+ if (onClick) {
+ onClick(e);
+ }
+ return history.push(to);
+ },
+ [history, to, onClick],
+ );
+ return (
+ <StyledText
+ onClick={navigate}
+ as={'button'}
+ color={color}
+ $hoverColor={getHoverColor(color)}
+ {...props}>
+ {children}
+ </StyledText>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Text.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Text.tsx
index f4b32d988d..33b0cdc6d9 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Text.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/Text.tsx
@@ -1,11 +1,45 @@
+import { forwardRef } from 'react';
import styled from 'styled-components';
-import { Colors } from '../../../tokens';
+import { TransientProps } from '../../../lib/styles';
+import { Colors, Typography, typography, TypographyProperties } from '../variables';
-interface TextProps {
- $color?: Colors;
-}
+export type TextProps = React.PropsWithChildren<{
+ variant?: Typography;
+ color?: Colors;
+ as?: React.ElementType;
+ style?: React.CSSProperties;
+}>;
-export const Text = styled.div<TextProps>(({ $color }) => ({
- color: $color ? $color : Colors.white,
+const StyledText = styled.span<TransientProps<TypographyProperties>>((props) => ({
+ color: 'var(--color)',
+ fontFamily: props.$fontFamily,
+ fontWeight: props.$fontWeight,
+ fontSize: props.$fontSize,
+ lineHeight: props.$lineHeight,
}));
+
+export const Text = forwardRef(
+ ({ variant = 'bodySmall', color = Colors.white, children, style, ...props }: TextProps, ref) => {
+ const { fontFamily, fontSize, fontWeight, lineHeight } = typography[variant];
+ return (
+ <StyledText
+ ref={ref}
+ style={
+ {
+ '--color': color,
+ ...style,
+ } as React.CSSProperties
+ }
+ $fontFamily={fontFamily}
+ $fontWeight={fontWeight}
+ $fontSize={fontSize}
+ $lineHeight={lineHeight}
+ {...props}>
+ {children}
+ </StyledText>
+ );
+ },
+);
+
+Text.displayName = 'Text';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleBig.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleBig.tsx
index 16d76c04ef..4b3b1d84ef 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleBig.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleBig.tsx
@@ -1,8 +1,8 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
+export type TitleBigProps = Omit<TextProps, 'variant'>;
-import { typography } from '../../../tokens';
-import { Text } from './Text';
-
-export const TitleBig = styled(Text)({
- ...typography['titleBig'],
-});
+export const TitleBig = ({ children, ...props }: TitleBigProps) => (
+ <Text variant="titleBig" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleLarge.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleLarge.tsx
index e939b099fc..c80814362e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleLarge.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleLarge.tsx
@@ -1,7 +1,8 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
+export type TitleLargeProps = Omit<TextProps, 'variant'>;
-import { typography } from '../../../tokens';
-import { Text } from './Text';
-export const TitleLarge = styled(Text)({
- ...typography['titleLarge'],
-});
+export const TitleLarge = ({ children, ...props }: TitleLargeProps) => (
+ <Text variant="titleLarge" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleMedium.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleMedium.tsx
index 578d435ef3..5e188698a4 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleMedium.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/TitleMedium.tsx
@@ -1,8 +1,8 @@
-import styled from 'styled-components';
+import { Text, TextProps } from './Text';
+export type TitleMediumProps = Omit<TextProps, 'variant'>;
-import { typography } from '../../../tokens';
-import { Text } from './Text';
-
-export const TitleMedium = styled(Text)({
- ...typography['titleMedium'],
-});
+export const TitleMedium = ({ children, ...props }: TitleMediumProps) => (
+ <Text variant="titleMedium" {...props}>
+ {children}
+ </Text>
+);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/index.ts
index 56db1927fc..542c9d5642 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/common/text/index.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/text/index.ts
@@ -6,3 +6,4 @@ export * from './Text';
export * from './TitleBig';
export * from './TitleLarge';
export * from './TitleMedium';
+export * from './Link';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/VariablesGlobalStyle.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/VariablesGlobalStyle.tsx
new file mode 100644
index 0000000000..fbd7487dc5
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/VariablesGlobalStyle.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { createGlobalStyle } from 'styled-components';
+
+import { colors, fontFamilies, fontSizes, fontWeights, lineHeights, radius, spacings } from './';
+
+type VariablesProps = React.PropsWithChildren<object>;
+
+const GlobalStyle = createGlobalStyle`
+ :root {
+ ${Object.entries({
+ ...colors,
+ ...spacings,
+ ...radius,
+ ...fontFamilies,
+ ...fontSizes,
+ ...fontWeights,
+ ...lineHeights,
+ }).reduce((styleString, [key, value]) => ({ ...styleString, [key]: value }), {})}
+ }
+`;
+
+export const VariablesGlobalStyle = ({ children }: VariablesProps) => {
+ return (
+ <>
+ <GlobalStyle />
+ {children}
+ </>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/color-variables.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/color-variables.ts
new file mode 100644
index 0000000000..c048456544
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/color-variables.ts
@@ -0,0 +1,67 @@
+import { ColorTokens } from '../../../tokens';
+
+export const colors = {
+ '--color-darker-blue': ColorTokens.darkBlue,
+ '--color-dark-blue': ColorTokens.darkBlue,
+ '--color-blue': ColorTokens.blue,
+ '--color-dark-green': ColorTokens.darkGreen,
+ '--color-green': ColorTokens.green,
+ '--color-red': ColorTokens.red,
+ '--color-darkYellow': ColorTokens.darkYellow,
+ '--color-yellow': ColorTokens.yellow,
+ '--color-black': ColorTokens.black,
+ '--color-white': ColorTokens.white,
+ '--color-white-90': ColorTokens.white90,
+ '--color-white-80': ColorTokens.white80,
+ '--color-white-60': ColorTokens.white60,
+ '--color-white-50': ColorTokens.white50,
+ '--color-white-40': ColorTokens.white40,
+ '--color-white-20': ColorTokens.white20,
+ '--color-white-10': ColorTokens.white10,
+ '--color-blue10': ColorTokens.blue10,
+ '--color-blue-20': ColorTokens.blue20,
+ '--color-blue-40': ColorTokens.blue40,
+ '--color-blue-50': ColorTokens.blue50,
+ '--color-blue-60': ColorTokens.blue60,
+ '--color-blue-80': ColorTokens.blue80,
+ '--color-red-95': ColorTokens.red95,
+ '--color-red-80': ColorTokens.red80,
+ '--color-red-60': ColorTokens.red60,
+ '--color-red-45': ColorTokens.red45,
+ '--color-red-40': ColorTokens.red40,
+ '--color-green-90': ColorTokens.green90,
+ '--color-green-40': ColorTokens.green40,
+};
+
+export enum Colors {
+ darkerBlue = 'var(--color-darker-blue)',
+ darkBlue = 'var(--color-dark-blue)',
+ blue = 'var(--color-blue)',
+ darkGreen = 'var(--color-dark-green)',
+ green = 'var(--color-green)',
+ red = 'var(--color-red)',
+ darkYellow = 'var(--color-dark-yellow)',
+ yellow = 'var(--color-yellow)',
+ black = 'var(--color-black)',
+ white = 'var(--color-white)',
+ white90 = 'var(--color-white-90)',
+ white80 = 'var(--color-white-80)',
+ white60 = 'var(--color-white-60)',
+ white50 = 'var(--color-white-50)',
+ white40 = 'var(--color-white-40)',
+ white20 = 'var(--color-white-20)',
+ white10 = 'var(--color-white-10)',
+ blue10 = 'var(--color-blue-10)',
+ blue20 = 'var(--color-blue-20)',
+ blue40 = 'var(--color-blue-40)',
+ blue50 = 'var(--color-blue-50)',
+ blue60 = 'var(--color-blue-60)',
+ blue80 = 'var(--color-blue-80)',
+ red95 = 'var(--color-red-95)',
+ red80 = 'var(--color-red-80)',
+ red60 = 'var(--color-red-60)',
+ red45 = 'var(--color-red-45)',
+ red40 = 'var(--color-red-40)',
+ green90 = 'var(--color-green-90)',
+ green40 = 'var(--color-green-40)',
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/index.ts
new file mode 100644
index 0000000000..3cabd68ee7
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/index.ts
@@ -0,0 +1,5 @@
+export * from './color-variables';
+export * from './radius-variables';
+export * from './typography-variables';
+export * from './spacing-variables';
+export * from './VariablesGlobalStyle';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/radius-variables.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/radius-variables.ts
new file mode 100644
index 0000000000..78ea604156
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/radius-variables.ts
@@ -0,0 +1,15 @@
+import { RadiusTokens } from '../../../tokens';
+
+export const radius = {
+ '--radius-4': RadiusTokens.radius4,
+ '--radius-8': RadiusTokens.radius8,
+ '--radius-11': RadiusTokens.radius11,
+ '--radius-12': RadiusTokens.radius12,
+};
+
+export enum Radius {
+ radius4 = 'var(--radius-4)',
+ radius8 = 'var(--radius-8)',
+ radius11 = 'var(--radius-11)',
+ radius12 = 'var(--radius-12)',
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/spacing-variables.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/spacing-variables.ts
new file mode 100644
index 0000000000..b6929f070d
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/spacing-variables.ts
@@ -0,0 +1,33 @@
+import { SpacingTokens } from '../../../tokens';
+
+export const spacings = {
+ '--spacing-1': SpacingTokens.spacing1,
+ '--spacing-2': SpacingTokens.spacing2,
+ '--spacing-3': SpacingTokens.spacing3,
+ '--spacing-4': SpacingTokens.spacing4,
+ '--spacing-5': SpacingTokens.spacing5,
+ '--spacing-6': SpacingTokens.spacing6,
+ '--spacing-7': SpacingTokens.spacing7,
+ '--spacing-8': SpacingTokens.spacing8,
+ '--spacing-9': SpacingTokens.spacing9,
+ '--spacing-10': SpacingTokens.spacing10,
+ '--spacing-11': SpacingTokens.spacing11,
+ '--spacing-12': SpacingTokens.spacing12,
+ '--spacing-13': SpacingTokens.spacing13,
+};
+
+export enum Spacings {
+ spacing1 = 'var(--spacing-1)',
+ spacing2 = 'var(--spacing-2)',
+ spacing3 = 'var(--spacing-3)',
+ spacing4 = 'var(--spacing-4)',
+ spacing5 = 'var(--spacing-5)',
+ spacing6 = 'var(--spacing-6)',
+ spacing7 = 'var(--spacing-7)',
+ spacing8 = 'var(--spacing-8)',
+ spacing9 = 'var(--spacing-9)',
+ spacing10 = 'var(--spacing-10)',
+ spacing11 = 'var(--spacing-11)',
+ spacing12 = 'var(--spacing-12)',
+ spacing13 = 'var(--spacing-13)',
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/typography-variables.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/typography-variables.ts
new file mode 100644
index 0000000000..68f6530c26
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/variables/typography-variables.ts
@@ -0,0 +1,132 @@
+import {
+ FontFamilyTokens,
+ FontSizeTokens,
+ FontWeightTokens,
+ LineHeightTokens,
+} from '../../../tokens';
+
+export const fontFamilies = {
+ '--font-family-open-sans': FontFamilyTokens.openSans,
+ '--font-family-source-sans-pro': FontFamilyTokens.sourceSansPro,
+};
+
+export enum FontFamilies {
+ openSans = 'var(--font-family-open-sans)',
+ sourceSansPro = 'var(--font-family-source-sans-pro)',
+}
+
+export const fontWeights = {
+ '--font-weight-regular': FontWeightTokens.regular,
+ '--font-weight-semi-bold': FontWeightTokens.semiBold,
+ '--font-weight-bold': FontWeightTokens.bold,
+};
+
+export enum FontWeights {
+ regular = 'var(--font-weight-regular)',
+ semiBold = 'var(--font-weight-semi-bold)',
+ bold = 'var(--font-weight-bold)',
+}
+
+export const fontSizes = {
+ '--font-size-big': FontSizeTokens.big,
+ '--font-size-large': FontSizeTokens.large,
+ '--font-size-medium': FontSizeTokens.medium,
+ '--font-size-small': FontSizeTokens.small,
+ '--font-size-tiny': FontSizeTokens.tiny,
+ '--font-size-mini': FontSizeTokens.mini,
+};
+
+export enum FontSizes {
+ big = 'var(--font-size-big)',
+ large = 'var(--font-size-large)',
+ medium = 'var(--font-size-medium)',
+ small = 'var(--font-size-small)',
+ tiny = 'var(--font-size-tiny)',
+ mini = 'var(--font-size-mini)',
+}
+
+export const lineHeights = {
+ '--line-height-big': LineHeightTokens.big,
+ '--line-height-large': LineHeightTokens.large,
+ '--line-height-medium': LineHeightTokens.medium,
+ '--line-height-small': LineHeightTokens.small,
+ '--line-height-tiny': LineHeightTokens.tiny,
+ '--line-height-mini': LineHeightTokens.mini,
+};
+
+export enum LineHeights {
+ big = 'var(--line-height-big)',
+ large = 'var(--line-height-large)',
+ medium = 'var(--line-height-medium)',
+ small = 'var(--line-height-small)',
+ tiny = 'var(--line-height-tiny)',
+ mini = 'var(--line-height-mini)',
+}
+
+export enum Fonts {
+ title = FontFamilies.sourceSansPro,
+ body = FontFamilies.openSans,
+ label = FontFamilies.openSans,
+ footnote = FontFamilies.openSans,
+}
+
+export type Typography =
+ | 'titleBig'
+ | 'titleLarge'
+ | 'titleMedium'
+ | 'bodySmall'
+ | 'bodySmallSemibold'
+ | 'labelTiny'
+ | 'footnoteMini';
+
+export interface TypographyProperties {
+ fontFamily: React.CSSProperties['fontFamily'];
+ fontSize: React.CSSProperties['fontSize'];
+ fontWeight: React.CSSProperties['fontWeight'];
+ lineHeight: React.CSSProperties['lineHeight'];
+}
+
+export const typography: Record<Typography, TypographyProperties> = {
+ titleBig: {
+ fontFamily: Fonts.title,
+ fontWeight: FontWeights.bold,
+ fontSize: FontSizes.big,
+ lineHeight: LineHeights.big,
+ },
+ titleLarge: {
+ fontFamily: Fonts.title,
+ fontWeight: FontWeights.bold,
+ fontSize: FontSizes.large,
+ lineHeight: LineHeights.large,
+ },
+ titleMedium: {
+ fontFamily: Fonts.title,
+ fontWeight: FontWeights.semiBold,
+ fontSize: FontSizes.medium,
+ lineHeight: LineHeights.medium,
+ },
+ bodySmall: {
+ fontFamily: Fonts.body,
+ fontWeight: FontWeights.regular,
+ fontSize: FontSizes.small,
+ lineHeight: LineHeights.small,
+ },
+ bodySmallSemibold: {
+ fontFamily: Fonts.body,
+ fontWeight: FontWeights.semiBold,
+ fontSize: FontSizes.small,
+ lineHeight: LineHeights.small,
+ },
+ labelTiny: {
+ fontFamily: Fonts.label,
+ fontWeight: FontWeights.semiBold,
+ fontSize: FontSizes.tiny,
+ lineHeight: LineHeights.tiny,
+ },
+ footnoteMini: {
+ fontFamily: Fonts.footnote,
+ fontWeight: FontWeights.semiBold,
+ fontSize: FontSizes.mini,
+ lineHeight: LineHeights.mini,
+ },
+} as const;
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionActionButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionActionButton.tsx
index 437fa93321..a0126bcd1d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionActionButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionActionButton.tsx
@@ -1,15 +1,10 @@
import { useCallback } from 'react';
-import styled from 'styled-components';
import { messages } from '../../../shared/gettext';
import log from '../../../shared/logging';
import { useAppContext } from '../../context';
import { useSelector } from '../../redux/store';
-import { SmallButton, SmallButtonColor } from '../SmallButton';
-
-const StyledConnectionButton = styled(SmallButton)({
- margin: 0,
-});
+import { Button } from '../common/molecules';
export default function ConnectionActionButton() {
const tunnelState = useSelector((state) => state.connection.status.state);
@@ -21,7 +16,7 @@ export default function ConnectionActionButton() {
}
}
-function ConnectButton(props: Partial<Parameters<typeof SmallButton>[0]>) {
+function ConnectButton(props: Partial<Parameters<typeof Button>[0]>) {
const { connectTunnel } = useAppContext();
const onConnect = useCallback(async () => {
@@ -34,9 +29,9 @@ function ConnectButton(props: Partial<Parameters<typeof SmallButton>[0]>) {
}, [connectTunnel]);
return (
- <StyledConnectionButton color={SmallButtonColor.green} onClick={onConnect} {...props}>
+ <Button variant="success" size="full" onClick={onConnect} {...props}>
{messages.pgettext('tunnel-control', 'Connect')}
- </StyledConnectionButton>
+ </Button>
);
}
@@ -56,8 +51,8 @@ function DisconnectButton() {
const displayAsCancel = tunnelState !== 'connected';
return (
- <StyledConnectionButton color={SmallButtonColor.red} onClick={onDisconnect}>
+ <Button variant="destructive" size="full" onClick={onDisconnect}>
{displayAsCancel ? messages.gettext('Cancel') : messages.gettext('Disconnect')}
- </StyledConnectionButton>
+ </Button>
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/SelectLocationButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/SelectLocationButton.tsx
index 50508a3192..7a6ab7d968 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/SelectLocationButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/SelectLocationButton.tsx
@@ -1,6 +1,5 @@
import { useCallback, useMemo } from 'react';
import { sprintf } from 'sprintf-js';
-import styled from 'styled-components';
import { ICustomList } from '../../../shared/daemon-rpc-types';
import { messages, relayLocations } from '../../../shared/gettext';
@@ -10,17 +9,9 @@ import { transitions, useHistory } from '../../lib/history';
import { RoutePath } from '../../lib/routes';
import { IRelayLocationCountryRedux, RelaySettingsRedux } from '../../redux/settings/reducers';
import { useSelector } from '../../redux/store';
+import { Button, ButtonProps } from '../common/molecules';
import ImageView from '../ImageView';
-import { MultiButton, MultiButtonCompatibleProps } from '../MultiButton';
-import { SmallButton, SmallButtonColor } from '../SmallButton';
-
-const StyledSmallButton = styled(SmallButton)({
- margin: 0,
-});
-
-const StyledReconnectButton = styled(StyledSmallButton)({
- padding: '4px 8px 4px 8px',
-});
+import { MultiButton } from '../MultiButton';
export default function SelectLocationButtons() {
const tunnelState = useSelector((state) => state.connection.status.state);
@@ -32,7 +23,7 @@ export default function SelectLocationButtons() {
}
}
-function SelectLocationButton(props: MultiButtonCompatibleProps) {
+function SelectLocationButton(props: ButtonProps) {
const { push } = useHistory();
const tunnelState = useSelector((state) => state.connection.status.state);
@@ -50,8 +41,9 @@ function SelectLocationButton(props: MultiButtonCompatibleProps) {
}, [push]);
return (
- <StyledSmallButton
- color={SmallButtonColor.blue}
+ <Button
+ variant="primary"
+ size="full"
onClick={onSelectLocation}
aria-label={sprintf(
messages.pgettext('accessibility', 'Select location. Current location is %(location)s'),
@@ -61,7 +53,7 @@ function SelectLocationButton(props: MultiButtonCompatibleProps) {
{tunnelState === 'disconnected'
? selectedRelayName
: messages.pgettext('tunnel-control', 'Switch location')}
- </StyledSmallButton>
+ </Button>
);
}
@@ -119,7 +111,7 @@ function getRelayName(
}
}
-function ReconnectButton(props: MultiButtonCompatibleProps) {
+function ReconnectButton(props: ButtonProps) {
const { reconnectTunnel } = useAppContext();
const onReconnect = useCallback(async () => {
@@ -132,12 +124,8 @@ function ReconnectButton(props: MultiButtonCompatibleProps) {
}, [reconnectTunnel]);
return (
- <StyledReconnectButton
- color={SmallButtonColor.blue}
- onClick={onReconnect}
- aria-label={messages.gettext('Reconnect')}
- {...props}>
+ <Button onClick={onReconnect} size="tiny" aria-label={messages.gettext('Reconnect')} {...props}>
<ImageView height={24} width={24} source="icon-reload" tintColor="white" />
- </StyledReconnectButton>
+ </Button>
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/AppInfoView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/AppInfoView.tsx
new file mode 100644
index 0000000000..41671c46a9
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/AppInfoView.tsx
@@ -0,0 +1,48 @@
+import { messages } from '../../../../shared/gettext';
+import { useHistory } from '../../../lib/history';
+import { BackAction } from '../../KeyboardNavigation';
+import { Layout, SettingsContainer, SettingsGroup, SettingsStack } from '../../Layout';
+import {
+ NavigationBar,
+ NavigationContainer,
+ NavigationItems,
+ NavigationScrollbars,
+ TitleBarItem,
+} from '../../NavigationBar';
+import SettingsHeader, { HeaderTitle } from '../../SettingsHeader';
+import { AppVersionListItem, ChangelogListItem } from './components';
+
+export const AppInfoView = () => {
+ const { pop } = useHistory();
+ return (
+ <BackAction action={pop}>
+ <Layout>
+ <SettingsContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <TitleBarItem>{messages.pgettext('app-info-view', 'App info')}</TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
+
+ <NavigationScrollbars>
+ <SettingsHeader>
+ <HeaderTitle>{messages.pgettext('app-info-view', 'App info')}</HeaderTitle>
+ </SettingsHeader>
+ <SettingsContainer>
+ <SettingsStack>
+ <SettingsGroup>
+ <AppVersionListItem />
+ </SettingsGroup>
+ <SettingsGroup>
+ <ChangelogListItem />
+ </SettingsGroup>
+ </SettingsStack>
+ </SettingsContainer>
+ </NavigationScrollbars>
+ </NavigationContainer>
+ </SettingsContainer>
+ </Layout>
+ </BackAction>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/AppVersionListItem.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/AppVersionListItem.tsx
new file mode 100644
index 0000000000..539a7e46c3
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/AppVersionListItem.tsx
@@ -0,0 +1,65 @@
+import { useCallback } from 'react';
+
+import { messages } from '../../../../../shared/gettext';
+import { getDownloadUrl } from '../../../../../shared/version';
+import { useAppContext } from '../../../../context';
+import { useSelector } from '../../../../redux/store';
+import * as Cell from '../../../cell';
+import { Colors } from '../../../common/variables';
+import { LabelStack } from '../../../Layout';
+
+export function AppVersionListItem() {
+ 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(
+ 'app-info-view',
+ 'App is out of sync. Please quit and restart.',
+ );
+
+ const updateAvailableMessage = messages.pgettext(
+ 'app-info-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('app-info-view', 'App version')}</Cell.Label>
+ </LabelStack>
+ <Cell.SubText>{appVersion}</Cell.SubText>
+ </Cell.CellNavigationButton>
+ {footer}
+ </>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/ChangelogListItem.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/ChangelogListItem.tsx
new file mode 100644
index 0000000000..c05b0286b6
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/ChangelogListItem.tsx
@@ -0,0 +1,17 @@
+import { useCallback } from 'react';
+
+import { messages } from '../../../../../shared/gettext';
+import { useHistory } from '../../../../lib/history';
+import { RoutePath } from '../../../../lib/routes';
+import * as Cell from '../../../cell';
+
+export function ChangelogListItem() {
+ const history = useHistory();
+ const navigate = useCallback(() => history.push(RoutePath.changelog), [history]);
+
+ return (
+ <Cell.CellNavigationButton onClick={navigate}>
+ <Cell.Label>{messages.pgettext('settings-view', "What's new")}</Cell.Label>
+ </Cell.CellNavigationButton>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/index.ts
new file mode 100644
index 0000000000..905bc45397
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/components/index.ts
@@ -0,0 +1,2 @@
+export * from './AppVersionListItem';
+export * from './ChangelogListItem';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/index.ts
new file mode 100644
index 0000000000..afbe9686f6
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-info/index.ts
@@ -0,0 +1 @@
+export * from './AppInfoView';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/ChangelogView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/ChangelogView.tsx
new file mode 100644
index 0000000000..df5453a68f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/ChangelogView.tsx
@@ -0,0 +1,106 @@
+import { useCallback } from 'react';
+import styled from 'styled-components';
+
+import { links } from '../../../../config.json';
+import { messages } from '../../../../shared/gettext';
+import { useAppContext } from '../../../context';
+import { useHistory } from '../../../lib/history';
+import { useSelector } from '../../../redux/store';
+import { Flex } from '../../common/layout';
+import { Container } from '../../common/layout/Container';
+import { Button } from '../../common/molecules/';
+import { BodySmall, TitleBig, TitleLarge } from '../../common/text';
+import { Colors, Spacings } from '../../common/variables';
+import ImageView from '../../ImageView';
+import { BackAction } from '../../KeyboardNavigation';
+import { Layout, SettingsContainer } from '../../Layout';
+import {
+ NavigationBar,
+ NavigationContainer,
+ NavigationItems,
+ NavigationScrollbars,
+ TitleBarItem,
+} from '../../NavigationBar';
+import SettingsHeader from '../../SettingsHeader';
+
+const StyledList = styled(Flex)({
+ listStyleType: 'disc',
+ paddingLeft: 0,
+ li: {
+ marginLeft: '1.5em',
+ },
+});
+
+const StyledFooter = styled(Flex)({
+ position: 'sticky',
+ minHeight: '64px',
+ bottom: 0,
+ background: Colors.darkBlue,
+});
+
+export const ChangelogView = () => {
+ const { pop } = useHistory();
+ const { openUrl } = useAppContext();
+ const changelog = useSelector((state) => state.userInterface.changelog);
+ const version = useSelector((state) => state.version.current);
+
+ const url = links.changelog;
+ const openDownloadLink = useCallback(() => openUrl(url), [openUrl, url]);
+ return (
+ <BackAction action={pop}>
+ <Layout>
+ <SettingsContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <TitleBarItem>{messages.pgettext('changelog-view', "What's new")}</TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
+
+ <NavigationScrollbars>
+ <SettingsHeader>
+ <TitleBig as={'h1'}>{messages.pgettext('changelog-view', "What's new")}</TitleBig>
+ </SettingsHeader>
+ <Flex $flexDirection="column" $gap={Spacings.spacing3}>
+ <Container size="4">
+ <TitleLarge as="h2">{version}</TitleLarge>
+ </Container>
+ <Container size="3" $flexDirection="column">
+ {changelog.length ? (
+ <StyledList as="ul" $flexDirection="column" $gap={Spacings.spacing5}>
+ {changelog.map((item, i) => (
+ <BodySmall as="li" key={i} color={Colors.white60}>
+ {item}
+ </BodySmall>
+ ))}
+ </StyledList>
+ ) : (
+ <BodySmall color={Colors.white60}>
+ {messages.pgettext(
+ 'changelog-view',
+ 'No updates or changes were made in this release for this platform.',
+ )}
+ </BodySmall>
+ )}
+ </Container>
+ </Flex>
+ </NavigationScrollbars>
+ </NavigationContainer>
+ </SettingsContainer>
+ <StyledFooter $alignItems="center" $justifyContent="center">
+ <Button
+ onClick={openDownloadLink}
+ trailing={
+ <ImageView
+ source="icon-extLink"
+ aria-label={messages.pgettext('accessibility', 'Opens externally')}
+ tintColor={Colors.white}
+ />
+ }>
+ {messages.pgettext('changelog', 'See full changelog')}
+ </Button>
+ </StyledFooter>
+ </Layout>
+ </BackAction>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/index.ts
new file mode 100644
index 0000000000..2c823d4f13
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/changelog/index.ts
@@ -0,0 +1 @@
+export * from './ChangelogView';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts
new file mode 100644
index 0000000000..dff5aa7a90
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts
@@ -0,0 +1,2 @@
+export * from './app-info';
+export * from './changelog';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts
new file mode 100644
index 0000000000..a03af00d06
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/index.ts
@@ -0,0 +1,2 @@
+export * from './new-device';
+export * from './new-version';
diff --git a/desktop/packages/mullvad-vpn/src/shared/notifications/new-device.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-device.ts
index 7d0fe9f299..f5bd085d89 100644
--- a/desktop/packages/mullvad-vpn/src/shared/notifications/new-device.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-device.ts
@@ -1,8 +1,8 @@
import { sprintf } from 'sprintf-js';
-import { messages } from '../../shared/gettext';
-import { capitalizeEveryWord } from '../string-helpers';
-import { InAppNotification, InAppNotificationProvider } from './notification';
+import { messages } from '../../../shared/gettext';
+import { InAppNotification, InAppNotificationProvider } from '../../../shared/notifications';
+import { capitalizeEveryWord } from '../../../shared/string-helpers';
interface NewDeviceNotificationContext {
shouldDisplay: boolean;
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-version.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-version.ts
new file mode 100644
index 0000000000..73ccf61525
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/notifications/new-version.ts
@@ -0,0 +1,44 @@
+import { messages } from '../../../shared/gettext';
+import { IChangelog } from '../../../shared/ipc-types';
+import { InAppNotification, InAppNotificationProvider } from '../../../shared/notifications';
+import { RoutePath } from '../routes';
+
+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/renderer/lib/routes.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/routes.ts
index 89b50c1fb0..73f510ea3a 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/routes.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/routes.ts
@@ -31,4 +31,6 @@ export enum RoutePath {
selectLocation = '/select-location',
editCustomBridge = '/select-location/edit-custom-bridge',
filter = '/select-location/filter',
+ appInfo = '/settings/app-info',
+ changelog = '/settings/changelog',
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/styles.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/styles.ts
index 554e669373..ea527ab791 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/styles.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/styles.ts
@@ -4,6 +4,6 @@ export type NonTransientProps<T, K extends NonTransientPropKey<keyof T>> = {
[P in keyof T as NonTransientPropKey<P> extends K ? NonTransientPropKey<P> : P]: T[P];
};
-export type TransientProps<T, K extends keyof T> = {
+export type TransientProps<T, K extends keyof T = keyof T> = {
[P in keyof T as P extends K ? `$${P & string}` : P]: T[P];
};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/actions.ts b/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/actions.ts
index 238835318e..83c0252452 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/actions.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/actions.ts
@@ -41,11 +41,6 @@ export interface ISetChangelog {
changelog: IChangelog;
}
-export interface ISetForceShowChanges {
- type: 'SET_FORCE_SHOW_CHANGES';
- forceShowChanges: boolean;
-}
-
export interface ISetIsPerformingPostUpgrade {
type: 'SET_IS_PERFORMING_POST_UPGRADE';
isPerformingPostUpgrade: boolean;
@@ -70,7 +65,6 @@ export type UserInterfaceAction =
| ISetConnectedToDaemon
| ISetDaemonAllowed
| ISetChangelog
- | ISetForceShowChanges
| ISetIsPerformingPostUpgrade
| ISetSelectLocationView
| ISetIsMacOs13OrNewer;
@@ -132,13 +126,6 @@ function setChangelog(changelog: IChangelog): ISetChangelog {
};
}
-function setForceShowChanges(forceShowChanges: boolean): ISetForceShowChanges {
- return {
- type: 'SET_FORCE_SHOW_CHANGES',
- forceShowChanges,
- };
-}
-
function setIsPerformingPostUpgrade(isPerformingPostUpgrade: boolean): ISetIsPerformingPostUpgrade {
return {
type: 'SET_IS_PERFORMING_POST_UPGRADE',
@@ -169,7 +156,6 @@ export default {
setConnectedToDaemon,
setDaemonAllowed,
setChangelog,
- setForceShowChanges,
setIsPerformingPostUpgrade,
setSelectLocationView,
setIsMacOs13OrNewer,
diff --git a/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/reducers.ts b/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/reducers.ts
index 89427e9b06..46bdfc6308 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/reducers.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/redux/userinterface/reducers.ts
@@ -12,7 +12,6 @@ export interface IUserInterfaceReduxState {
connectedToDaemon: boolean;
daemonAllowed?: boolean;
changelog: IChangelog;
- forceShowChanges: boolean;
isPerformingPostUpgrade: boolean;
selectLocationView: LocationType;
isMacOs13OrNewer: boolean;
@@ -26,7 +25,6 @@ const initialState: IUserInterfaceReduxState = {
connectedToDaemon: false,
daemonAllowed: undefined,
changelog: [],
- forceShowChanges: false,
isPerformingPostUpgrade: false,
selectLocationView: LocationType.exit,
isMacOs13OrNewer: true,
@@ -64,12 +62,6 @@ export default function (
changelog: action.changelog,
};
- case 'SET_FORCE_SHOW_CHANGES':
- return {
- ...state,
- forceShowChanges: action.forceShowChanges,
- };
-
case 'SET_IS_PERFORMING_POST_UPGRADE':
return {
...state,
diff --git a/desktop/packages/mullvad-vpn/src/renderer/tokens/colors.ts b/desktop/packages/mullvad-vpn/src/renderer/tokens/color-tokens.ts
index 46dc8508be..fe42f71f4e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/tokens/colors.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/tokens/color-tokens.ts
@@ -1,4 +1,4 @@
-export enum Colors {
+export enum ColorTokens {
darkerBlue = 'rgba(25, 38, 56, 0.95)',
darkBlue = 'rgb(25, 46, 69)',
blue = 'rgb(41, 77, 115)',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/tokens/index.ts b/desktop/packages/mullvad-vpn/src/renderer/tokens/index.ts
index 53555f1d5a..3c59aa34fd 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/tokens/index.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/tokens/index.ts
@@ -1,3 +1,4 @@
-export * from './colors';
-export * from './spacings';
-export * from './typography';
+export * from './color-tokens';
+export * from './radius-tokens';
+export * from './spacing-tokens';
+export * from './typography-tokens';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/tokens/radius-tokens.ts b/desktop/packages/mullvad-vpn/src/renderer/tokens/radius-tokens.ts
new file mode 100644
index 0000000000..7eecb31eca
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/tokens/radius-tokens.ts
@@ -0,0 +1,6 @@
+export enum RadiusTokens {
+ radius4 = '4px',
+ radius8 = '8px',
+ radius11 = '11px',
+ radius12 = '12px',
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/tokens/spacings.ts b/desktop/packages/mullvad-vpn/src/renderer/tokens/spacing-tokens.ts
index fb0c924863..4216dde90a 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/tokens/spacings.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/tokens/spacing-tokens.ts
@@ -1,4 +1,4 @@
-export enum Spacings {
+export enum SpacingTokens {
spacing1 = '4px',
spacing2 = '6px',
spacing3 = '8px',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/tokens/typography-tokens.ts b/desktop/packages/mullvad-vpn/src/renderer/tokens/typography-tokens.ts
new file mode 100644
index 0000000000..9d42a89c9f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/tokens/typography-tokens.ts
@@ -0,0 +1,28 @@
+export enum FontFamilyTokens {
+ openSans = 'Open Sans',
+ sourceSansPro = '"Source Sans Pro", "Noto Sans Myanmar", "Noto Sans Thai", sans-serif',
+}
+
+export enum FontWeightTokens {
+ regular = 400,
+ semiBold = 600,
+ bold = 700,
+}
+
+export enum FontSizeTokens {
+ big = '32px',
+ large = '24px',
+ medium = '18px',
+ small = '14px',
+ tiny = '12px',
+ mini = '10px',
+}
+
+export enum LineHeightTokens {
+ big = '34px',
+ large = '28px',
+ medium = '24px',
+ small = '20px',
+ tiny = '18px',
+ mini = '15px',
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/tokens/typography.ts b/desktop/packages/mullvad-vpn/src/renderer/tokens/typography.ts
deleted file mode 100644
index df7359b8f0..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/tokens/typography.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-export enum FontFamilies {
- openSans = 'Open Sans',
- openSansPro = '"Source Sans Pro", "Noto Sans Myanmar", "Noto Sans Thai", sans-serif',
-}
-
-export enum Fonts {
- title = FontFamilies.openSansPro,
- body = FontFamilies.openSans,
- label = FontFamilies.openSans,
- footnote = FontFamilies.openSans,
-}
-
-export enum FontWeights {
- regular = 400,
- semiBold = 600,
- bold = 700,
-}
-
-export enum FontSizes {
- big = '32px',
- large = '24px',
- medium = '18px',
- small = '14px',
- tiny = '12px',
- mini = '10px',
-}
-
-export enum LineHeights {
- big = '34px',
- large = '28px',
- medium = '24px',
- small = '20px',
- tiny = '18px',
- mini = '15px',
-}
-
-interface Typography {
- fontFamily: React.CSSProperties['fontFamily'];
- fontSize: React.CSSProperties['fontSize'];
- fontWeight: React.CSSProperties['fontWeight'];
- lineHeight: React.CSSProperties['lineHeight'];
-}
-
-export const typography: Record<
- | 'titleBig'
- | 'titleLarge'
- | 'titleMedium'
- | 'bodySmall'
- | 'bodySmallSemibold'
- | 'labelTiny'
- | 'footnoteMini',
- Typography
-> = {
- titleBig: {
- fontFamily: Fonts.title,
- fontWeight: FontWeights.bold,
- fontSize: FontSizes.big,
- lineHeight: LineHeights.big,
- },
- titleLarge: {
- fontFamily: Fonts.title,
- fontWeight: FontWeights.bold,
- fontSize: FontSizes.large,
- lineHeight: LineHeights.large,
- },
- titleMedium: {
- fontFamily: Fonts.title,
- fontWeight: FontWeights.semiBold,
- fontSize: FontSizes.medium,
- lineHeight: LineHeights.medium,
- },
- bodySmall: {
- fontFamily: Fonts.body,
- fontWeight: FontWeights.regular,
- fontSize: FontSizes.small,
- lineHeight: LineHeights.small,
- },
- bodySmallSemibold: {
- fontFamily: Fonts.body,
- fontWeight: FontWeights.semiBold,
- fontSize: FontSizes.small,
- lineHeight: LineHeights.small,
- },
- labelTiny: {
- fontFamily: Fonts.label,
- fontWeight: FontWeights.semiBold,
- fontSize: FontSizes.tiny,
- lineHeight: LineHeights.tiny,
- },
- footnoteMini: {
- fontFamily: Fonts.footnote,
- fontWeight: FontWeights.semiBold,
- fontSize: FontSizes.mini,
- lineHeight: LineHeights.mini,
- },
-} as const;
diff --git a/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts b/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts
index 856239c0f5..3580c679f3 100644
--- a/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts
@@ -73,7 +73,6 @@ export interface IAppStateSnapshot {
splitTunnelingApplications?: ISplitTunnelingApplication[];
macOsScrollbarVisibility?: MacOsScrollbarVisibility;
changelog: IChangelog;
- forceShowChanges: boolean;
navigationHistory?: IHistoryObject;
currentApiAccessMethod?: AccessMethodSetting;
isMacOs13OrNewer: boolean;
diff --git a/desktop/packages/mullvad-vpn/src/shared/localization-contexts.ts b/desktop/packages/mullvad-vpn/src/shared/localization-contexts.ts
index f30212025c..c4d47b1afa 100644
--- a/desktop/packages/mullvad-vpn/src/shared/localization-contexts.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/localization-contexts.ts
@@ -37,4 +37,6 @@ export type LocalizationContexts =
| 'select-language-nav'
| 'tray-icon-context-menu'
| 'tray-icon-tooltip'
- | 'troubleshoot';
+ | 'troubleshoot'
+ | 'app-info-view'
+ | 'changelog-view';
diff --git a/desktop/packages/mullvad-vpn/src/shared/notifications/inconsistent-version.ts b/desktop/packages/mullvad-vpn/src/shared/notifications/inconsistent-version.ts
index e4f7a8ddc1..4cfed9f777 100644
--- a/desktop/packages/mullvad-vpn/src/shared/notifications/inconsistent-version.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/notifications/inconsistent-version.ts
@@ -17,7 +17,10 @@ export class InconsistentVersionNotificationProvider
{
public constructor(private context: InconsistentVersionNotificationContext) {}
- public mayDisplay = () => !this.context.consistent;
+ public mayDisplay = () =>
+ !this.context.consistent &&
+ process.env.NODE_ENV !== undefined &&
+ process.env.NODE_ENV !== 'development';
public getSystemNotification(): SystemNotification {
return {
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..5321917af6
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/shared/notifications/index.ts
@@ -0,0 +1,13 @@
+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 './notification';
+export * from './reconnecting';
+export * from './unsupported-version';
+export * from './update-available';
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/e2e/setup/main.ts b/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
index d8f3ceee48..1795451046 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
@@ -171,7 +171,6 @@ class ApplicationMain {
splitTunnelingApplications: [],
macOsScrollbarVisibility: MacOsScrollbarVisibility.whenScrolling,
changelog: [],
- forceShowChanges: false,
navigationHistory: undefined,
scrollPositions: {},
isMacOs13OrNewer: true,
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({