summaryrefslogtreecommitdiffhomepage
path: root/desktop
diff options
context:
space:
mode:
authorTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-04-04 17:10:55 +0200
committerSebastian Holmin <sebastian.holmin@mullvad.net>2025-05-28 13:25:24 +0200
commit77b8433a08a6610e668de657fe1ff9fdaa421504 (patch)
tree0c8720d403127c8159455894904b63abd78e8e91 /desktop
parent1ad02949c426ccd72f0036319c7259a185148502 (diff)
downloadmullvadvpn-77b8433a08a6610e668de657fe1ff9fdaa421504.tar.xz
mullvadvpn-77b8433a08a6610e668de657fe1ff9fdaa421504.zip
Add app-upgrade view
Diffstat (limited to 'desktop')
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/AppUpgradeView.tsx74
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/CancelButton.tsx20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/useDisabled.ts13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/DownloadProgress.tsx20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/index.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageError.ts21
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageTimeLeft.ts42
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/useMessage.ts37
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/index.ts8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/InstallButton.tsx18
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/ReportProblemButton.tsx20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/RetryUpgradeButton.tsx20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/useDisabled.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/UpgradeButton.tsx20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/useDisabled.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/UpgradeDetails.tsx50
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/NoChangelogUpdates.tsx17
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/index.ts5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useChangelog.ts17
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useHasChangelog.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowChangelogList.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowNoChangelogUpdates.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useTitle.ts20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/UpgradeLabel.tsx49
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/ConnectionBlocked.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/useMessage.ts16
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/DownloadProgress.tsx8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/useMessage.ts23
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/DownloadStarted.tsx13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/index.ts7
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/InstallerReady.tsx17
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/StartingInstaller.tsx17
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/UpgradeError.tsx18
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/useMessage.ts43
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/VerifyingInstaller.tsx16
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/index.ts8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/usePresent.ts11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowCancelButton.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowDownloadProgress.ts28
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowInstallButton.ts9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowReportProblemButton.ts14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowRetryUpgradeButton.ts20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeButton.ts23
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeLabel.ts16
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts1
71 files changed, 875 insertions, 0 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/AppUpgradeView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/AppUpgradeView.tsx
new file mode 100644
index 0000000000..0febc7a44c
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/AppUpgradeView.tsx
@@ -0,0 +1,74 @@
+import styled from 'styled-components';
+
+import { Flex } from '../../../lib/components';
+import { Animate } from '../../../lib/components/animate';
+import { useHistory } from '../../../lib/history';
+import { BackAction } from '../../KeyboardNavigation';
+import { Layout } from '../../Layout';
+import {
+ CancelButton,
+ DownloadProgress,
+ InstallButton,
+ ReportProblemButton,
+ RetryUpgradeButton,
+ UpgradeButton,
+ UpgradeDetails,
+ UpgradeLabel,
+} from './components';
+import {
+ usePresent,
+ useShowCancelButton,
+ useShowDownloadProgress,
+ useShowInstallButton,
+ useShowReportProblemButton,
+ useShowRetryUpgradeButton,
+ useShowUpgradeButton,
+ useShowUpgradeLabel,
+} from './hooks';
+
+const StyledFooter = styled.div`
+ // TODO: Use color from Colors
+ background-color: rgba(21, 39, 58, 1);
+ position: sticky;
+ bottom: 0;
+ width: 100%;
+`;
+
+export const AppUpgradeView = () => {
+ const { pop } = useHistory();
+ const present = usePresent();
+ const showCancelButton = useShowCancelButton();
+ const showDownloadProgress = useShowDownloadProgress();
+ const showInstallButton = useShowInstallButton();
+ const showReportProblemButton = useShowReportProblemButton();
+ const showRetryUpgradeButton = useShowRetryUpgradeButton();
+ const showUpgradeButton = useShowUpgradeButton();
+ const showUpgradeLabel = useShowUpgradeLabel();
+
+ return (
+ <BackAction action={pop}>
+ <Layout>
+ <UpgradeDetails />
+ <StyledFooter>
+ <Flex $padding="large" $flexDirection="column">
+ <Animate
+ animations={[{ type: 'fade' }, { type: 'wipe', direction: 'vertical' }]}
+ present={present}>
+ <Flex $gap="medium" $flexDirection="column" $margin={{ bottom: 'medium' }}>
+ {showUpgradeLabel && <UpgradeLabel />}
+ {showDownloadProgress && <DownloadProgress />}
+ </Flex>
+ </Animate>
+ <Flex $gap="medium" $flexDirection="column">
+ {showReportProblemButton && <ReportProblemButton />}
+ {showRetryUpgradeButton && <RetryUpgradeButton />}
+ {showUpgradeButton && <UpgradeButton />}
+ {showInstallButton && <InstallButton />}
+ {showCancelButton && <CancelButton />}
+ </Flex>
+ </Flex>
+ </StyledFooter>
+ </Layout>
+ </BackAction>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/CancelButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/CancelButton.tsx
new file mode 100644
index 0000000000..b04e84ccbf
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/CancelButton.tsx
@@ -0,0 +1,20 @@
+import { messages } from '../../../../../../shared/gettext';
+import { useAppContext } from '../../../../../context';
+import { Button } from '../../../../../lib/components';
+import { useDisabled } from './hooks';
+
+export function CancelButton() {
+ const disabled = useDisabled();
+ const { appUpgradeAbort } = useAppContext();
+
+ return (
+ <Button disabled={disabled} onClick={appUpgradeAbort}>
+ <Button.Text>
+ {
+ // TRANSLATORS: Button text to cancel the download of an update
+ messages.pgettext('app-upgrade-view', 'Cancel')
+ }
+ </Button.Text>
+ </Button>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/index.ts
new file mode 100644
index 0000000000..73e963a519
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useDisabled';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/useDisabled.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/useDisabled.ts
new file mode 100644
index 0000000000..11b4742b5b
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/hooks/useDisabled.ts
@@ -0,0 +1,13 @@
+import { useAppUpgradeEventType } from '../../../../../../hooks';
+
+export const useDisabled = () => {
+ const appUpgradeEventType = useAppUpgradeEventType();
+
+ switch (appUpgradeEventType) {
+ case 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS':
+ case 'APP_UPGRADE_STATUS_DOWNLOAD_STARTED':
+ return false;
+ default:
+ return true;
+ }
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/index.ts
new file mode 100644
index 0000000000..886c5b0107
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/cancel-button/index.ts
@@ -0,0 +1 @@
+export * from './CancelButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/DownloadProgress.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/DownloadProgress.tsx
new file mode 100644
index 0000000000..fd1a60a0fa
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/DownloadProgress.tsx
@@ -0,0 +1,20 @@
+import { useAppUpgradeDownloadProgressValue } from '../../../../../hooks';
+import { Progress } from '../../../../../lib/components/progress';
+import { useMessage } from './hooks';
+
+export function DownloadProgress() {
+ const message = useMessage();
+ const value = useAppUpgradeDownloadProgressValue();
+
+ return (
+ <Progress value={value}>
+ <Progress.Track>
+ <Progress.Range />
+ </Progress.Track>
+ <Progress.Footer>
+ <Progress.Percent />
+ <Progress.Text>{message}</Progress.Text>
+ </Progress.Footer>
+ </Progress>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/index.ts
new file mode 100644
index 0000000000..107ba71afc
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useMessage';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/index.ts
new file mode 100644
index 0000000000..22e7b37790
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/index.ts
@@ -0,0 +1,2 @@
+export * from './useGetMessageError';
+export * from './useGetMessageTimeLeft';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageError.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageError.ts
new file mode 100644
index 0000000000..40362deffa
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageError.ts
@@ -0,0 +1,21 @@
+import { messages } from '../../../../../../../../../shared/gettext';
+import { useAppUpgradeError } from '../../../../../../../../redux/hooks';
+
+export const useGetMessageError = () => {
+ const { appUpgradeError } = useAppUpgradeError();
+
+ const getMessageError = () => {
+ if (
+ appUpgradeError === 'START_INSTALLER_FAILED' ||
+ appUpgradeError === 'START_INSTALLER_AUTOMATIC_FAILED' ||
+ appUpgradeError === 'VERIFICATION_FAILED'
+ ) {
+ // TRANSLATORS: Status text displayed below a progress bar when the download of an update is complete
+ return messages.pgettext('app-upgrade-view', 'Download complete!');
+ }
+
+ return null;
+ };
+
+ return getMessageError;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageTimeLeft.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageTimeLeft.ts
new file mode 100644
index 0000000000..ee2bd530d5
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/hooks/useGetMessageTimeLeft.ts
@@ -0,0 +1,42 @@
+import { sprintf } from 'sprintf-js';
+
+import { messages } from '../../../../../../../../../shared/gettext';
+import { useAppUpgradeEvent } from '../../../../../../../../redux/hooks';
+
+export const useGetMessageTimeLeft = () => {
+ const { appUpgradeEvent } = useAppUpgradeEvent();
+
+ const getMessageTimeLeft = () => {
+ if (appUpgradeEvent?.type === 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS') {
+ const { timeLeft } = appUpgradeEvent;
+
+ if (timeLeft > 90) {
+ const minutes = Math.round(timeLeft / 60);
+
+ return sprintf(
+ // TRANSLATORS: Status text displayed below a progress bar when the update is being downloaded
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(minutes)s - Will be replaced with remaining minutes until download is complete
+ messages.pgettext('app-upgrade-view', 'About %(minutes)s minutes remaining...'),
+ {
+ minutes,
+ },
+ );
+ }
+
+ return sprintf(
+ // TRANSLATORS: Status text displayed below a progress bar when the update is being downloaded
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(second)s - Will be replaced with remaining seconds until download is complete
+ messages.pgettext('app-upgrade-view', 'About %(seconds)s seconds remaining...'),
+ {
+ seconds: timeLeft,
+ },
+ );
+ }
+
+ return null;
+ };
+
+ return getMessageTimeLeft;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/index.ts
new file mode 100644
index 0000000000..107ba71afc
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/index.ts
@@ -0,0 +1 @@
+export * from './useMessage';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/useMessage.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/useMessage.ts
new file mode 100644
index 0000000000..711a9f9653
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/hooks/useMessage/useMessage.ts
@@ -0,0 +1,37 @@
+import { messages } from '../../../../../../../../shared/gettext';
+import { useAppUpgradeEventType, useHasAppUpgradeError } from '../../../../../../../hooks';
+import { useConnectionIsBlocked } from '../../../../../../../redux/hooks';
+import { useGetMessageError, useGetMessageTimeLeft } from './hooks';
+
+export const useMessage = () => {
+ const { isBlocked } = useConnectionIsBlocked();
+ const appUpgradeEventType = useAppUpgradeEventType();
+ const getMessageError = useGetMessageError();
+ const getMessageTimeLeft = useGetMessageTimeLeft();
+ const hasAppUpgradeError = useHasAppUpgradeError();
+
+ if (isBlocked) {
+ // TRANSLATORS: Status text displayed below a progress bar when the download of an update has been paused
+ return messages.pgettext('app-upgrade-view', 'Download paused');
+ }
+
+ if (hasAppUpgradeError) {
+ return getMessageError();
+ }
+
+ switch (appUpgradeEventType) {
+ case 'APP_UPGRADE_STATUS_DOWNLOAD_STARTED':
+ // TRANSLATORS: Status text displayed below a progress bar when the download of an update is starting
+ return messages.pgettext('app-upgrade-view', 'Starting download...');
+ case 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS':
+ return getMessageTimeLeft();
+ case 'APP_UPGRADE_STATUS_STARTED_INSTALLER':
+ case 'APP_UPGRADE_STATUS_STARTING_INSTALLER':
+ case 'APP_UPGRADE_STATUS_VERIFIED_INSTALLER':
+ case 'APP_UPGRADE_STATUS_VERIFYING_INSTALLER':
+ // TRANSLATORS: Status text displayed below a progress bar when the download of an update is complete
+ return messages.pgettext('app-upgrade-view', 'Download complete!');
+ default:
+ return null;
+ }
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/index.ts
new file mode 100644
index 0000000000..213bb26f82
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/download-progress/index.ts
@@ -0,0 +1 @@
+export * from './DownloadProgress';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/index.ts
new file mode 100644
index 0000000000..b38a80c80e
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/index.ts
@@ -0,0 +1,8 @@
+export * from './cancel-button';
+export * from './download-progress';
+export * from './install-button';
+export * from './report-problem-button';
+export * from './retry-upgrade-button';
+export * from './upgrade-button';
+export * from './upgrade-details';
+export * from './upgrade-label';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/InstallButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/InstallButton.tsx
new file mode 100644
index 0000000000..eeaec246d8
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/InstallButton.tsx
@@ -0,0 +1,18 @@
+import { messages } from '../../../../../../shared/gettext';
+import { useAppContext } from '../../../../../context';
+import { Button } from '../../../../../lib/components';
+
+export function InstallButton() {
+ const { appUpgradeInstallerStart } = useAppContext();
+
+ return (
+ <Button onClick={appUpgradeInstallerStart}>
+ <Button.Text>
+ {
+ // TRANSLATORS: Button text to install an update
+ messages.pgettext('app-upgrade-view', 'Install update')
+ }
+ </Button.Text>
+ </Button>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/index.ts
new file mode 100644
index 0000000000..5e616d2943
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/install-button/index.ts
@@ -0,0 +1 @@
+export * from './InstallButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/ReportProblemButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/ReportProblemButton.tsx
new file mode 100644
index 0000000000..e04762a42c
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/ReportProblemButton.tsx
@@ -0,0 +1,20 @@
+import { messages } from '../../../../../../shared/gettext';
+import { usePushProblemReport } from '../../../../../history/hooks';
+import { Button } from '../../../../../lib/components';
+
+export function ReportProblemButton() {
+ const pushProblemReport = usePushProblemReport({
+ search: '?suppress-outdated-version-warning=true',
+ });
+
+ return (
+ <Button onClick={pushProblemReport}>
+ <Button.Text>
+ {
+ // TRANSLATORS: Button text to report a problem
+ messages.pgettext('app-upgrade-view', 'Report a problem')
+ }
+ </Button.Text>
+ </Button>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/index.ts
new file mode 100644
index 0000000000..808f304f69
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/report-problem-button/index.ts
@@ -0,0 +1 @@
+export * from './ReportProblemButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/RetryUpgradeButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/RetryUpgradeButton.tsx
new file mode 100644
index 0000000000..7c51580a7e
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/RetryUpgradeButton.tsx
@@ -0,0 +1,20 @@
+import { messages } from '../../../../../../shared/gettext';
+import { useAppContext } from '../../../../../context';
+import { Button } from '../../../../../lib/components';
+import { useDisabled } from './hooks';
+
+export function RetryUpgradeButton() {
+ const { appUpgrade } = useAppContext();
+ const disabled = useDisabled();
+
+ return (
+ <Button disabled={disabled} onClick={appUpgrade}>
+ <Button.Text>
+ {
+ // TRANSLATORS: Button text to retry download of an update
+ messages.pgettext('app-upgrade-view', 'Retry download')
+ }
+ </Button.Text>
+ </Button>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/index.ts
new file mode 100644
index 0000000000..73e963a519
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useDisabled';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/useDisabled.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/useDisabled.ts
new file mode 100644
index 0000000000..e41c5b12ff
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/hooks/useDisabled.ts
@@ -0,0 +1,9 @@
+import { useConnectionIsBlocked } from '../../../../../../redux/hooks';
+
+export const useDisabled = () => {
+ const { isBlocked } = useConnectionIsBlocked();
+
+ const disabled = isBlocked;
+
+ return disabled;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/index.ts
new file mode 100644
index 0000000000..4aebbc2d17
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/retry-upgrade-button/index.ts
@@ -0,0 +1 @@
+export * from './RetryUpgradeButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/UpgradeButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/UpgradeButton.tsx
new file mode 100644
index 0000000000..69380f71a6
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/UpgradeButton.tsx
@@ -0,0 +1,20 @@
+import { messages } from '../../../../../../shared/gettext';
+import { useAppContext } from '../../../../../context';
+import { Button } from '../../../../../lib/components';
+import { useDisabled } from './hooks';
+
+export function UpgradeButton() {
+ const { appUpgrade } = useAppContext();
+ const disabled = useDisabled();
+
+ return (
+ <Button disabled={disabled} onClick={appUpgrade}>
+ <Button.Text>
+ {
+ // TRANSLATORS: Button text to download and install an update
+ messages.pgettext('app-upgrade-view', 'Download and install')
+ }
+ </Button.Text>
+ </Button>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/index.ts
new file mode 100644
index 0000000000..73e963a519
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useDisabled';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/useDisabled.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/useDisabled.ts
new file mode 100644
index 0000000000..e41c5b12ff
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/hooks/useDisabled.ts
@@ -0,0 +1,9 @@
+import { useConnectionIsBlocked } from '../../../../../../redux/hooks';
+
+export const useDisabled = () => {
+ const { isBlocked } = useConnectionIsBlocked();
+
+ const disabled = isBlocked;
+
+ return disabled;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/index.ts
new file mode 100644
index 0000000000..472b50ea34
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-button/index.ts
@@ -0,0 +1 @@
+export * from './UpgradeButton';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/UpgradeDetails.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/UpgradeDetails.tsx
new file mode 100644
index 0000000000..00cc856c6c
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/UpgradeDetails.tsx
@@ -0,0 +1,50 @@
+import { messages } from '../../../../../../shared/gettext';
+import { Container, Flex, TitleBig, TitleLarge } from '../../../../../lib/components';
+import { AppNavigationHeader } from '../../../../app-navigation-header';
+import { ChangelogList } from '../../../../changelog-list';
+import { SettingsContainer } from '../../../../Layout';
+import { NavigationContainer } from '../../../../NavigationContainer';
+import { NavigationScrollbars } from '../../../../NavigationScrollbars';
+import { NoChangelogUpdates } from './components';
+import { useChangelog, useShowChangelogList, useShowNoChangelogUpdates, useTitle } from './hooks';
+
+export function UpgradeDetails() {
+ const changelog = useChangelog();
+ const showChangelogList = useShowChangelogList();
+ const showNoChangelogUpdates = useShowNoChangelogUpdates();
+ const title = useTitle();
+
+ return (
+ <SettingsContainer>
+ <NavigationContainer>
+ <AppNavigationHeader
+ title={
+ // TRANSLATORS: Title in navigation bar
+ messages.pgettext('app-upgrade-view', 'Update available')
+ }
+ />
+ <NavigationScrollbars>
+ <Flex $flexDirection="column" $gap="large" $padding={{ bottom: 'medium' }}>
+ <Container size="4">
+ <TitleBig as="h2">
+ {
+ // TRANSLATORS: Main title for the update available view
+ messages.pgettext('app-upgrade-view', 'Update available')
+ }
+ </TitleBig>
+ </Container>
+ <Flex $flexDirection="column" $gap="small">
+ <Container size="4">
+ <TitleLarge as="h2">{title}</TitleLarge>
+ </Container>
+ <Container size="3" $flexDirection="column">
+ {showChangelogList && <ChangelogList changelog={changelog} />}
+ {showNoChangelogUpdates && <NoChangelogUpdates />}
+ </Container>
+ </Flex>
+ </Flex>
+ </NavigationScrollbars>
+ </NavigationContainer>
+ </SettingsContainer>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/index.ts
new file mode 100644
index 0000000000..e838cd8521
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/index.ts
@@ -0,0 +1 @@
+export * from './no-changelog-updates';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/NoChangelogUpdates.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/NoChangelogUpdates.tsx
new file mode 100644
index 0000000000..d9179e3029
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/NoChangelogUpdates.tsx
@@ -0,0 +1,17 @@
+import { messages } from '../../../../../../../../shared/gettext';
+import { BodySmall } from '../../../../../../../lib/components';
+import { Colors } from '../../../../../../../lib/foundations';
+
+export function NoChangelogUpdates() {
+ return (
+ <BodySmall color={Colors.white60}>
+ {
+ // TRANSLATORS: Text displayed when there are no updates for this platform in the next app version
+ messages.pgettext(
+ 'app-upgrade-view',
+ 'No updates or changes were made in this release for this platform.',
+ )
+ }
+ </BodySmall>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/index.ts
new file mode 100644
index 0000000000..095239a335
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/components/no-changelog-updates/index.ts
@@ -0,0 +1 @@
+export * from './NoChangelogUpdates';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/index.ts
new file mode 100644
index 0000000000..91bafea1fb
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/index.ts
@@ -0,0 +1,5 @@
+export * from './useChangelog';
+export * from './useHasChangelog';
+export * from './useShowChangelogList';
+export * from './useShowNoChangelogUpdates';
+export * from './useTitle';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useChangelog.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useChangelog.ts
new file mode 100644
index 0000000000..25bcb761e7
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useChangelog.ts
@@ -0,0 +1,17 @@
+import { useMemo } from 'react';
+
+import { useVersionSuggestedUpgrade } from '../../../../../../redux/hooks';
+
+export const useChangelog = () => {
+ const { suggestedUpgrade } = useVersionSuggestedUpgrade();
+
+ const changelogMemo = useMemo(() => {
+ if (suggestedUpgrade) {
+ return suggestedUpgrade.changelog;
+ }
+
+ return [];
+ }, [suggestedUpgrade]);
+
+ return changelogMemo;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useHasChangelog.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useHasChangelog.ts
new file mode 100644
index 0000000000..b1e232122a
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useHasChangelog.ts
@@ -0,0 +1,9 @@
+import { useChangelog } from './useChangelog';
+
+export const useHasChangelog = () => {
+ const changelog = useChangelog();
+
+ const hasChangeLog = changelog.length > 0;
+
+ return hasChangeLog;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowChangelogList.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowChangelogList.ts
new file mode 100644
index 0000000000..10aa854c15
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowChangelogList.ts
@@ -0,0 +1,9 @@
+import { useHasChangelog } from './useHasChangelog';
+
+export const useShowChangelogList = () => {
+ const hasChangelog = useHasChangelog();
+
+ const showChangelogList = hasChangelog;
+
+ return showChangelogList;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowNoChangelogUpdates.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowNoChangelogUpdates.ts
new file mode 100644
index 0000000000..cc30f5c5ac
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useShowNoChangelogUpdates.ts
@@ -0,0 +1,9 @@
+import { useHasChangelog } from './useHasChangelog';
+
+export const useShowNoChangelogUpdates = () => {
+ const hasChangelog = useHasChangelog();
+
+ const showNoChangelogUpdates = !hasChangelog;
+
+ return showNoChangelogUpdates;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useTitle.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useTitle.ts
new file mode 100644
index 0000000000..0741d54ea3
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useTitle.ts
@@ -0,0 +1,20 @@
+import { sprintf } from 'sprintf-js';
+
+import { messages } from '../../../../../../../shared/gettext';
+import { useVersionSuggestedUpgrade } from '../../../../../../redux/hooks';
+
+export const useTitle = () => {
+ const { suggestedUpgrade } = useVersionSuggestedUpgrade();
+
+ const title = sprintf(
+ // TRANSLATORS: Heading which shows the version of the app which can be upgraded to.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(version)s - The new version of the app.
+ messages.pgettext('app-upgrade-view', 'Version %(version)s'),
+ {
+ version: suggestedUpgrade?.version,
+ },
+ );
+
+ return title;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/index.ts
new file mode 100644
index 0000000000..307c9ef208
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/index.ts
@@ -0,0 +1 @@
+export * from './UpgradeDetails';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/UpgradeLabel.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/UpgradeLabel.tsx
new file mode 100644
index 0000000000..d351463faf
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/UpgradeLabel.tsx
@@ -0,0 +1,49 @@
+import {
+ useAppUpgradeEventType,
+ useHasAppUpgradeError,
+ useShouldAppUpgradeInstallManually,
+} from '../../../../../hooks';
+import { useConnectionIsBlocked } from '../../../../../redux/hooks';
+import {
+ ConnectionBlocked,
+ DownloadProgress,
+ DownloadStarted,
+ InstallerReady,
+ StartingInstaller,
+ UpgradeError,
+ VerifyingInstaller,
+} from './components';
+
+export function UpgradeLabel() {
+ const { isBlocked } = useConnectionIsBlocked();
+ const appUpgradeEventType = useAppUpgradeEventType();
+ const hasAppUpgradeError = useHasAppUpgradeError();
+ const shouldAppUpgradeInstallManually = useShouldAppUpgradeInstallManually();
+
+ if (shouldAppUpgradeInstallManually) {
+ return <InstallerReady />;
+ }
+
+ if (isBlocked) {
+ return <ConnectionBlocked />;
+ }
+
+ if (hasAppUpgradeError) {
+ return <UpgradeError />;
+ }
+
+ switch (appUpgradeEventType) {
+ case 'APP_UPGRADE_STATUS_DOWNLOAD_STARTED':
+ return <DownloadStarted />;
+ case 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS':
+ return <DownloadProgress />;
+ case 'APP_UPGRADE_STATUS_VERIFYING_INSTALLER':
+ return <VerifyingInstaller />;
+ case 'APP_UPGRADE_STATUS_STARTED_INSTALLER':
+ case 'APP_UPGRADE_STATUS_STARTING_INSTALLER':
+ case 'APP_UPGRADE_STATUS_VERIFIED_INSTALLER':
+ return <StartingInstaller />;
+ default:
+ return null;
+ }
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/ConnectionBlocked.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/ConnectionBlocked.tsx
new file mode 100644
index 0000000000..4eb3f03afa
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/ConnectionBlocked.tsx
@@ -0,0 +1,14 @@
+import { Flex, LabelTiny } from '../../../../../../../lib/components';
+import { Dot } from '../../../../../../../lib/components/dot';
+import { useMessage } from './hooks';
+
+export function ConnectionBlocked() {
+ const message = useMessage();
+
+ return (
+ <Flex $gap="small" $alignItems="baseline">
+ <Dot size="small" variant="error" />
+ <LabelTiny>{message}</LabelTiny>
+ </Flex>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/index.ts
new file mode 100644
index 0000000000..107ba71afc
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useMessage';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/useMessage.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/useMessage.ts
new file mode 100644
index 0000000000..60c619ff6d
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/hooks/useMessage.ts
@@ -0,0 +1,16 @@
+import { messages } from '../../../../../../../../../shared/gettext';
+import { useConnectionIsBlocked } from '../../../../../../../../redux/hooks';
+
+export const useMessage = () => {
+ const { isBlocked } = useConnectionIsBlocked();
+
+ if (isBlocked) {
+ // TRANSLATORS: Label displayed when an error occurred due to the connection being blocked
+ return messages.pgettext(
+ 'app-upgrade-view',
+ 'Connection blocked. Try changing server or other settings',
+ );
+ }
+
+ return null;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/index.ts
new file mode 100644
index 0000000000..c24311f6b3
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/connection-blocked/index.ts
@@ -0,0 +1 @@
+export * from './ConnectionBlocked';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/DownloadProgress.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/DownloadProgress.tsx
new file mode 100644
index 0000000000..1a5a67541f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/DownloadProgress.tsx
@@ -0,0 +1,8 @@
+import { LabelTiny } from '../../../../../../../lib/components';
+import { useMessage } from './hooks';
+
+export function DownloadProgress() {
+ const message = useMessage();
+
+ return <LabelTiny>{message}</LabelTiny>;
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/index.ts
new file mode 100644
index 0000000000..107ba71afc
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useMessage';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/useMessage.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/useMessage.ts
new file mode 100644
index 0000000000..2edfb8c81b
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/hooks/useMessage.ts
@@ -0,0 +1,23 @@
+import { sprintf } from 'sprintf-js';
+
+import { messages } from '../../../../../../../../../shared/gettext';
+import { useAppUpgradeEvent } from '../../../../../../../../redux/hooks';
+
+export const useMessage = () => {
+ const { appUpgradeEvent } = useAppUpgradeEvent();
+
+ if (appUpgradeEvent?.type === 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS') {
+ const { server } = appUpgradeEvent;
+
+ return sprintf(
+ // TRANSLATORS: Label displayed above a progress bar informing the user which server
+ // TRANSLATORS: the update is downloading from
+ messages.pgettext('app-upgrade-view', 'Downloading from: %(server)s'),
+ {
+ server,
+ },
+ );
+ }
+
+ return null;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/index.ts
new file mode 100644
index 0000000000..213bb26f82
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-progress/index.ts
@@ -0,0 +1 @@
+export * from './DownloadProgress';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/DownloadStarted.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/DownloadStarted.tsx
new file mode 100644
index 0000000000..4fbe251f0f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/DownloadStarted.tsx
@@ -0,0 +1,13 @@
+import { messages } from '../../../../../../../../shared/gettext';
+import { LabelTiny } from '../../../../../../../lib/components';
+
+export function DownloadStarted() {
+ return (
+ <LabelTiny>
+ {
+ // TRANSLATORS: Label displayed above a progress bar when a download is in progress
+ messages.pgettext('app-upgrade-view', 'Downloading...')
+ }
+ </LabelTiny>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/index.ts
new file mode 100644
index 0000000000..ed30650883
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/download-started/index.ts
@@ -0,0 +1 @@
+export * from './DownloadStarted';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/index.ts
new file mode 100644
index 0000000000..e10fc7e8ae
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/index.ts
@@ -0,0 +1,7 @@
+export * from './connection-blocked';
+export * from './download-progress';
+export * from './download-started';
+export * from './installer-ready';
+export * from './starting-installer';
+export * from './upgrade-error';
+export * from './verifying-installer';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/InstallerReady.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/InstallerReady.tsx
new file mode 100644
index 0000000000..31b6f83a90
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/InstallerReady.tsx
@@ -0,0 +1,17 @@
+import { messages } from '../../../../../../../../shared/gettext';
+import { Flex, Icon, LabelTiny } from '../../../../../../../lib/components';
+import { Colors } from '../../../../../../../lib/foundations';
+
+export function InstallerReady() {
+ return (
+ <Flex $gap="small">
+ <Icon icon="checkmark" color={Colors.green} size="small" />
+ <LabelTiny>
+ {
+ // TRANSLATORS: Label displayed above a progress bar when the update is ready to be installed
+ messages.pgettext('app-upgrade-view', 'Verification successful! Ready to install.')
+ }
+ </LabelTiny>
+ </Flex>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/index.ts
new file mode 100644
index 0000000000..ab9fed2358
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/installer-ready/index.ts
@@ -0,0 +1 @@
+export * from './InstallerReady';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/StartingInstaller.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/StartingInstaller.tsx
new file mode 100644
index 0000000000..6ebaf95537
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/StartingInstaller.tsx
@@ -0,0 +1,17 @@
+import { messages } from '../../../../../../../../shared/gettext';
+import { Flex, Icon, LabelTiny } from '../../../../../../../lib/components';
+import { Colors } from '../../../../../../../lib/foundations';
+
+export function StartingInstaller() {
+ return (
+ <Flex $gap="small">
+ <Icon icon="checkmark" color={Colors.green} size="small" />
+ <LabelTiny>
+ {
+ // TRANSLATORS: Label displayed above a progress bar when the update is ready to be installed
+ messages.pgettext('app-upgrade-view', 'Verification successful! Starting installer...')
+ }
+ </LabelTiny>
+ </Flex>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/index.ts
new file mode 100644
index 0000000000..f324d568b0
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/starting-installer/index.ts
@@ -0,0 +1 @@
+export * from './StartingInstaller';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/UpgradeError.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/UpgradeError.tsx
new file mode 100644
index 0000000000..85e3c7bf8e
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/UpgradeError.tsx
@@ -0,0 +1,18 @@
+import { Flex, Icon, LabelTiny } from '../../../../../../../lib/components';
+import { Colors } from '../../../../../../../lib/foundations';
+import { useMessage } from './hooks';
+
+export function UpgradeError() {
+ const message = useMessage();
+
+ return (
+ <Flex $gap="small" $flexDirection="row">
+ <div>
+ <Icon size="small" icon="alert-circle" color={Colors.red} />
+ </div>
+ <Flex $flexDirection="column">
+ <LabelTiny>{message}</LabelTiny>
+ </Flex>
+ </Flex>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/index.ts
new file mode 100644
index 0000000000..107ba71afc
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useMessage';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/useMessage.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/useMessage.ts
new file mode 100644
index 0000000000..ffd4272244
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/hooks/useMessage.ts
@@ -0,0 +1,43 @@
+import { messages } from '../../../../../../../../../shared/gettext';
+import { useAppUpgradeError } from '../../../../../../../../redux/hooks';
+
+export const useMessage = () => {
+ const { appUpgradeError } = useAppUpgradeError();
+
+ switch (appUpgradeError) {
+ case 'DOWNLOAD_FAILED':
+ return [
+ // TRANSLATORS: Label displayed when an error occurred due to the download failing
+ messages.pgettext(
+ 'app-upgrade-view',
+ 'Unable to download update. Check your connection and/or firewall then try again. If this problem persists, please contact support.',
+ ),
+ ];
+ case 'START_INSTALLER_AUTOMATIC_FAILED':
+ case 'START_INSTALLER_FAILED':
+ return [
+ // TRANSLATORS: Label displayed when an error occurred due to the installer failing to start
+ // TRANSLATORS: and the suggested resolution is to download the update again.
+ messages.pgettext(
+ 'app-upgrade-view',
+ 'Could not start the update installer, try downloading it again. If this problem persists, please contact support.',
+ ),
+ ];
+ case 'VERIFICATION_FAILED':
+ return [
+ // TRANSLATORS: Label displayed when an error occurred due to the installer failed verification
+ messages.pgettext(
+ 'app-upgrade-view',
+ 'Verification failed. Try again. If this problem persists, please contact support.',
+ ),
+ ];
+ default:
+ return [
+ // TRANSLATORS: Label displayed when an unknown error occurred
+ messages.pgettext(
+ 'app-upgrade-view',
+ 'An unknown error occurred. Please try again. If this problem persists, please contact support.',
+ ),
+ ];
+ }
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/index.ts
new file mode 100644
index 0000000000..91d0d9f595
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/upgrade-error/index.ts
@@ -0,0 +1 @@
+export * from './UpgradeError';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/VerifyingInstaller.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/VerifyingInstaller.tsx
new file mode 100644
index 0000000000..b2ed866f05
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/VerifyingInstaller.tsx
@@ -0,0 +1,16 @@
+import { messages } from '../../../../../../../../shared/gettext';
+import { Flex, LabelTiny, Spinner } from '../../../../../../../lib/components';
+
+export function VerifyingInstaller() {
+ return (
+ <Flex $gap="small">
+ <Spinner size="small" />
+ <LabelTiny>
+ {
+ // TRANSLATORS: Label displayed above a progress bar when the update is being verified
+ messages.pgettext('app-upgrade-view', 'Verifying installer...')
+ }
+ </LabelTiny>
+ </Flex>
+ );
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/index.ts
new file mode 100644
index 0000000000..29fba8f5bb
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/components/verifying-installer/index.ts
@@ -0,0 +1 @@
+export * from './VerifyingInstaller';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/index.ts
new file mode 100644
index 0000000000..fa5638908e
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-label/index.ts
@@ -0,0 +1 @@
+export * from './UpgradeLabel';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/index.ts
new file mode 100644
index 0000000000..8c34ac2d52
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/index.ts
@@ -0,0 +1,8 @@
+export * from './usePresent';
+export * from './useShowCancelButton';
+export * from './useShowDownloadProgress';
+export * from './useShowInstallButton';
+export * from './useShowReportProblemButton';
+export * from './useShowRetryUpgradeButton';
+export * from './useShowUpgradeButton';
+export * from './useShowUpgradeLabel';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/usePresent.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/usePresent.ts
new file mode 100644
index 0000000000..40cb594078
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/usePresent.ts
@@ -0,0 +1,11 @@
+import { useShowDownloadProgress } from './useShowDownloadProgress';
+import { useShowUpgradeLabel } from './useShowUpgradeLabel';
+
+export const usePresent = () => {
+ const showUpgradeLabel = useShowUpgradeLabel();
+ const showDownloadProgress = useShowDownloadProgress();
+
+ const present = showUpgradeLabel || showDownloadProgress;
+
+ return present;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowCancelButton.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowCancelButton.ts
new file mode 100644
index 0000000000..3a42a68b53
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowCancelButton.ts
@@ -0,0 +1,9 @@
+import { useIsAppUpgradeInProgress } from '../../../../hooks';
+
+export const useShowCancelButton = () => {
+ const isAppUpgradeInProgress = useIsAppUpgradeInProgress();
+
+ const showCancelButton = isAppUpgradeInProgress;
+
+ return showCancelButton;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowDownloadProgress.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowDownloadProgress.ts
new file mode 100644
index 0000000000..8faea2713d
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowDownloadProgress.ts
@@ -0,0 +1,28 @@
+import {
+ useHasAppUpgradeError,
+ useHasAppUpgradeInitiated,
+ useIsAppUpgradePending,
+} from '../../../../hooks';
+import { useAppUpgradeError, useConnectionIsBlocked } from '../../../../redux/hooks';
+
+export const useShowDownloadProgress = () => {
+ const { isBlocked } = useConnectionIsBlocked();
+ const { appUpgradeError } = useAppUpgradeError();
+ const hasAppUpgradeInitiated = useHasAppUpgradeInitiated();
+ const hasAppUpgradeError = useHasAppUpgradeError();
+ const isAppUpgradePending = useIsAppUpgradePending();
+
+ if (isBlocked && hasAppUpgradeInitiated) {
+ return true;
+ }
+
+ if (hasAppUpgradeError) {
+ if (appUpgradeError === 'VERIFICATION_FAILED') {
+ return true;
+ }
+
+ return false;
+ }
+
+ return isAppUpgradePending;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowInstallButton.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowInstallButton.ts
new file mode 100644
index 0000000000..3c4f65ba9f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowInstallButton.ts
@@ -0,0 +1,9 @@
+import { useShouldAppUpgradeInstallManually } from '../../../../hooks';
+
+export const useShowInstallButton = () => {
+ const shouldAppUpgradeInstallManually = useShouldAppUpgradeInstallManually();
+
+ const showInstallButton = shouldAppUpgradeInstallManually;
+
+ return showInstallButton;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowReportProblemButton.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowReportProblemButton.ts
new file mode 100644
index 0000000000..adc8c23400
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowReportProblemButton.ts
@@ -0,0 +1,14 @@
+import { useAppUpgradeError } from '../../../../redux/hooks';
+
+export const useShowReportProblemButton = () => {
+ const { appUpgradeError } = useAppUpgradeError();
+
+ switch (appUpgradeError) {
+ case 'DOWNLOAD_FAILED':
+ case 'GENERAL_ERROR':
+ case 'VERIFICATION_FAILED':
+ return true;
+ default:
+ return false;
+ }
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowRetryUpgradeButton.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowRetryUpgradeButton.ts
new file mode 100644
index 0000000000..ba4f77e89d
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowRetryUpgradeButton.ts
@@ -0,0 +1,20 @@
+import { useHasAppUpgradeError } from '../../../../hooks';
+import { useAppUpgradeError } from '../../../../redux/hooks';
+
+export const useShowRetryUpgradeButton = () => {
+ const { appUpgradeError } = useAppUpgradeError();
+ const hasAppUpgradeError = useHasAppUpgradeError();
+
+ if (hasAppUpgradeError) {
+ switch (appUpgradeError) {
+ case 'DOWNLOAD_FAILED':
+ case 'GENERAL_ERROR':
+ case 'VERIFICATION_FAILED':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return false;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeButton.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeButton.ts
new file mode 100644
index 0000000000..254aff3521
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeButton.ts
@@ -0,0 +1,23 @@
+import {
+ useAppUpgradeEventType,
+ useHasAppUpgradeError,
+ useHasAppUpgradeInitiated,
+} from '../../../../hooks';
+
+export const useShowUpgradeButton = () => {
+ const appUpgradeEventType = useAppUpgradeEventType();
+ const hasAppUpgradeError = useHasAppUpgradeError();
+ const hasAppUpgradeInitiated = useHasAppUpgradeInitiated();
+
+ if (hasAppUpgradeError) {
+ return false;
+ }
+
+ // If we don't have an event type yet it is because the user has not attempted
+ // an upgrade yet.
+ if (!hasAppUpgradeInitiated || appUpgradeEventType === 'APP_UPGRADE_STATUS_ABORTED') {
+ return true;
+ }
+
+ return false;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeLabel.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeLabel.ts
new file mode 100644
index 0000000000..8ecead9ad4
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/hooks/useShowUpgradeLabel.ts
@@ -0,0 +1,16 @@
+import { useHasAppUpgradeError, useIsAppUpgradePending } from '../../../../hooks';
+import { useConnectionIsBlocked } from '../../../../redux/hooks';
+
+export const useShowUpgradeLabel = () => {
+ const { isBlocked } = useConnectionIsBlocked();
+ const hasAppUpgradeError = useHasAppUpgradeError();
+ const isAppUpgradePending = useIsAppUpgradePending();
+
+ if (isBlocked || hasAppUpgradeError) {
+ return true;
+ }
+
+ const showUpgradeLabel = isAppUpgradePending;
+
+ return showUpgradeLabel;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/index.ts
new file mode 100644
index 0000000000..481653229f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/index.ts
@@ -0,0 +1 @@
+export * from './AppUpgradeView';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts
index 2ff7a5978b..686a4bbbd3 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/index.ts
@@ -1,3 +1,4 @@
export * from './app-info';
+export * from './app-upgrade';
export * from './changelog';
export * from './settings';