summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar <oskar@mullvad.net>2025-07-04 15:27:29 +0200
committerOskar <oskar@mullvad.net>2025-07-04 15:27:29 +0200
commit3e982cca836db5b7e859f59589b280afeb98bd3a (patch)
tree0036d05dfa1d6d68d8d52e04c3d4fa6c287b3c58
parent4fb0191a259a62efc1264ae71135a9114ce99fd7 (diff)
parentb16514e001a7d51772a36f3fd929f471124334ec (diff)
downloadmullvadvpn-3e982cca836db5b7e859f59589b280afeb98bd3a.tar.xz
mullvadvpn-3e982cca836db5b7e859f59589b280afeb98bd3a.zip
Merge branch 'add-info-icon-with-information-about-where-the-installer-is-des-2293'
-rw-r--r--desktop/packages/mullvad-vpn/locales/messages.pot4
-rw-r--r--desktop/packages/mullvad-vpn/src/main/app-upgrade.ts2
-rw-r--r--desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/app.tsx1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/UpgradeDetails.tsx22
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useCacheDir.ts29
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts1
-rw-r--r--mullvad-daemon/src/lib.rs24
-rw-r--r--mullvad-daemon/src/management_interface.rs17
-rw-r--r--mullvad-daemon/src/version/router.rs23
-rw-r--r--mullvad-management-interface/proto/management_interface.proto1
12 files changed, 127 insertions, 3 deletions
diff --git a/desktop/packages/mullvad-vpn/locales/messages.pot b/desktop/packages/mullvad-vpn/locales/messages.pot
index 324264959d..b21fd8317c 100644
--- a/desktop/packages/mullvad-vpn/locales/messages.pot
+++ b/desktop/packages/mullvad-vpn/locales/messages.pot
@@ -749,6 +749,10 @@ msgctxt "app-upgrade-view"
msgid "Installer quit unexpectedly, please try again or send a problem report."
msgstr ""
+msgctxt "app-upgrade-view"
+msgid "Installer will be downloaded to <b>%(cacheDir)s</b>."
+msgstr ""
+
#. Text displayed when there are no updates for this platform in the next app version
msgctxt "app-upgrade-view"
msgid "No updates or changes were made in this release for this platform."
diff --git a/desktop/packages/mullvad-vpn/src/main/app-upgrade.ts b/desktop/packages/mullvad-vpn/src/main/app-upgrade.ts
index ff50218d5e..3dbe7df04f 100644
--- a/desktop/packages/mullvad-vpn/src/main/app-upgrade.ts
+++ b/desktop/packages/mullvad-vpn/src/main/app-upgrade.ts
@@ -30,6 +30,8 @@ export default class AppUpgrade {
);
}
});
+
+ IpcMainEventChannel.app.handleGetUpgradeCacheDir(() => this.daemonRpc.getAppUpgradeCacheDir());
}
public subscribeEvents() {
diff --git a/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts b/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts
index 35c1f775f4..aed89bb3c9 100644
--- a/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts
+++ b/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts
@@ -148,6 +148,11 @@ export class DaemonRpc extends GrpcClient {
void this.callEmpty(this.client.appUpgradeAbort);
}
+ public async getAppUpgradeCacheDir(): Promise<string> {
+ const response = await this.callEmpty<StringValue>(this.client.getAppUpgradeCacheDir);
+ return response.getValue();
+ }
+
public unsubscribeAppUpgradeEventListener(listener: SubscriptionListener<DaemonAppUpgradeEvent>) {
const id = listener.subscriptionId;
if (id !== undefined) {
diff --git a/desktop/packages/mullvad-vpn/src/renderer/app.tsx b/desktop/packages/mullvad-vpn/src/renderer/app.tsx
index e705f76661..368654e475 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/app.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/app.tsx
@@ -445,6 +445,7 @@ export default class AppRenderer {
public daemonPrepareRestart = (shutdown: boolean): void => {
IpcRendererEventChannel.daemon.prepareRestart(shutdown);
};
+ public getAppUpgradeCacheDir = () => IpcRendererEventChannel.app.getUpgradeCacheDir();
public tryStartDaemon = () => {
if (window.env.platform === 'win32') IpcRendererEventChannel.daemon.tryStart();
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
index cac6f59b9c..4d227496de 100644
--- 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
@@ -1,17 +1,22 @@
+import { sprintf } from 'sprintf-js';
+
import { messages } from '../../../../../../shared/gettext';
import { Container, Flex, TitleBig, TitleLarge } from '../../../../../lib/components';
+import { formatHtml } from '../../../../../lib/html-formatter';
import { AppNavigationHeader } from '../../../../app-navigation-header';
import { ChangelogList } from '../../../../changelog-list';
import { SettingsContainer } from '../../../../Layout';
import { NavigationContainer } from '../../../../NavigationContainer';
import { NavigationScrollbars } from '../../../../NavigationScrollbars';
+import { HeaderSubTitle } from '../../../../SettingsHeader';
import { NoChangelogUpdates } from './components';
-import { useChangelog, useShowChangelogList, useTitle } from './hooks';
+import { useCacheDir, useChangelog, useShowChangelogList, useTitle } from './hooks';
export function UpgradeDetails() {
const changelog = useChangelog();
const showChangelogList = useShowChangelogList();
const title = useTitle();
+ const cacheDir = useCacheDir();
return (
<SettingsContainer>
@@ -24,13 +29,26 @@ export function UpgradeDetails() {
/>
<NavigationScrollbars>
<Flex $flexDirection="column" $gap="large" $padding={{ bottom: 'medium' }}>
- <Container size="4">
+ <Container size="4" $flexDirection="column" $gap="small">
<TitleBig as="h2">
{
// TRANSLATORS: Main title for the update available view
messages.pgettext('app-upgrade-view', 'Update available')
}
</TitleBig>
+ {cacheDir !== undefined && (
+ <HeaderSubTitle>
+ {formatHtml(
+ sprintf(
+ messages.pgettext(
+ 'app-upgrade-view',
+ 'Installer will be downloaded to <b>%(cacheDir)s</b>.',
+ ),
+ { cacheDir },
+ ),
+ )}
+ </HeaderSubTitle>
+ )}
</Container>
<Flex $flexDirection="column" $gap="small">
<Container size="4">
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
index 41cfba9f0a..158c43eeac 100644
--- 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
@@ -1,3 +1,4 @@
export * from './useChangelog';
export * from './useShowChangelogList';
+export * from './useCacheDir';
export * from './useTitle';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useCacheDir.ts b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useCacheDir.ts
new file mode 100644
index 0000000000..296b33e999
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/app-upgrade/components/upgrade-details/hooks/useCacheDir.ts
@@ -0,0 +1,29 @@
+import { useEffect, useState } from 'react';
+
+import log from '../../../../../../../shared/logging';
+import { useAppContext } from '../../../../../../context';
+import { useMounted } from '../../../../../../lib/utility-hooks';
+
+export function useCacheDir() {
+ const { getAppUpgradeCacheDir } = useAppContext();
+ const [cacheDir, setCacheDir] = useState<string>();
+ const isMounted = useMounted();
+
+ useEffect(() => {
+ const fetchCacheDir = async () => {
+ try {
+ const cacheDir = await getAppUpgradeCacheDir();
+ if (isMounted()) {
+ setCacheDir(cacheDir);
+ }
+ } catch (e) {
+ const error = e as Error;
+ log.warn(`Failed to fetch cache dir: ${error.message}`);
+ }
+ };
+
+ void fetchCacheDir();
+ }, [getAppUpgradeCacheDir, isMounted]);
+
+ return cacheDir;
+}
diff --git a/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts b/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts
index 4f2c9da0fd..48148d9498 100644
--- a/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/ipc-schema.ts
@@ -176,6 +176,7 @@ export const ipcSchema = {
upgradeEvent: notifyRenderer<AppUpgradeEvent>(),
upgradeError: notifyRenderer<AppUpgradeError>(),
upgradeInstallerStart: send<string>(),
+ getUpgradeCacheDir: invoke<void, string>(),
},
tunnel: {
'': notifyRenderer<TunnelState>(),
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 5550ef3a8a..efdbea5cc6 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -427,6 +427,8 @@ pub enum DaemonCommand {
AppUpgrade(ResponseTx<(), version::Error>),
/// Prompt the daemon to abort the current upgrade.
AppUpgradeAbort(ResponseTx<(), version::Error>),
+ /// Return the storage path for the installers during in-app upgrades.
+ GetAppUpgradeCacheDir(ResponseTx<PathBuf, version::Error>),
}
/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
@@ -1511,6 +1513,7 @@ impl Daemon {
EnableRelay { relay, tx } => self.on_toggle_relay(relay, true, tx),
AppUpgrade(tx) => self.on_app_upgrade(tx).await,
AppUpgradeAbort(tx) => self.on_app_upgrade_abort(tx).await,
+ GetAppUpgradeCacheDir(tx) => self.on_get_app_upgrade_cache_dir(tx).await,
}
}
@@ -3326,6 +3329,27 @@ impl Daemon {
};
}
+ #[cfg_attr(not(in_app_upgrade), allow(clippy::unused_async))]
+ async fn on_get_app_upgrade_cache_dir(&self, tx: ResponseTx<PathBuf, version::Error>) {
+ #[cfg(in_app_upgrade)]
+ {
+ let result = self.version_handle.get_cache_dir().await;
+ Self::oneshot_send(tx, result, "on_get_app_upgrade_cache_dir response");
+ }
+ #[cfg(not(in_app_upgrade))]
+ {
+ log::warn!(
+ "Can't get cache dir for app upgrades since in-app upgrades are disabled on this OS"
+ );
+
+ Self::oneshot_send(
+ tx,
+ Ok(PathBuf::new()),
+ "on_get_app_upgrade_cache_dir response",
+ )
+ };
+ }
+
/// Set the target state of the client. If it changed trigger the operations needed to
/// progress towards that state.
/// Returns a bool representing whether a state change was initiated.
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index aef1d17c45..a578b67610 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -1217,6 +1217,23 @@ impl ManagementService for ManagementServiceImpl {
Box::new(upgrade_event_stream) as Self::AppUpgradeEventsListenStream
))
}
+
+ async fn get_app_upgrade_cache_dir(&self, _: Request<()>) -> ServiceResult<String> {
+ log::debug!("get_app_upgrade_cache_dir");
+
+ let (tx, rx) = oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::GetAppUpgradeCacheDir(tx))?;
+
+ let path = self
+ .wait_for_result(rx)
+ .await?
+ .map_err(map_version_check_error)?;
+
+ path.into_os_string()
+ .into_string()
+ .map_err(|_| Status::internal("Failed to convert OsString to String"))
+ .map(Response::new)
+ }
}
impl ManagementServiceImpl {
diff --git a/mullvad-daemon/src/version/router.rs b/mullvad-daemon/src/version/router.rs
index 7a10c90bcf..a1b81ebabe 100644
--- a/mullvad-daemon/src/version/router.rs
+++ b/mullvad-daemon/src/version/router.rs
@@ -67,6 +67,15 @@ impl VersionRouterHandle {
.map_err(|_| Error::VersionRouterClosed)?;
result_rx.await.map_err(|_| Error::VersionRouterClosed)
}
+
+ #[cfg(in_app_upgrade)]
+ pub async fn get_cache_dir(&self) -> Result<PathBuf> {
+ let (result_tx, result_rx) = oneshot::channel();
+ self.tx
+ .send(Message::GetCacheDir { result_tx })
+ .map_err(|_| Error::VersionRouterClosed)?;
+ result_rx.await.map_err(|_| Error::VersionRouterClosed)
+ }
}
// These wrapper traits and type aliases exist to help feature gate the module
@@ -106,6 +115,8 @@ struct VersionRouter<S = DaemonEventSender<AppVersionInfo>, D = DefaultDownloade
/// Broadcast channel for app upgrade events
#[cfg(in_app_upgrade)]
app_upgrade_broadcast: AppUpgradeBroadcast,
+ #[cfg(in_app_upgrade)]
+ cache_dir: PathBuf,
/// Type used to spawn the downloader task, replaced when testing
_phantom: std::marker::PhantomData<D>,
}
@@ -124,6 +135,9 @@ enum Message {
/// Cancel the ongoing update
#[cfg(in_app_upgrade)]
CancelUpdate { result_tx: oneshot::Sender<()> },
+ /// Get the cache dir
+ #[cfg(in_app_upgrade)]
+ GetCacheDir { result_tx: oneshot::Sender<PathBuf> },
}
#[derive(Debug)]
@@ -215,7 +229,7 @@ pub(crate) fn spawn_version_router(
VersionUpdater::spawn(
api_handle,
availability_handle,
- cache_dir,
+ cache_dir.clone(),
new_version_tx,
refresh_version_check_rx,
)
@@ -230,6 +244,8 @@ pub(crate) fn spawn_version_router(
version_request_channels: vec![],
#[cfg(in_app_upgrade)]
app_upgrade_broadcast,
+ #[cfg(in_app_upgrade)]
+ cache_dir,
refresh_version_check_tx,
_phantom: std::marker::PhantomData::<DefaultDownloader>,
}
@@ -298,6 +314,10 @@ where
self.cancel_upgrade();
let _ = result_tx.send(());
}
+ #[cfg(in_app_upgrade)]
+ Message::GetCacheDir { result_tx } => {
+ let _ = result_tx.send(self.cache_dir.clone());
+ }
}
}
@@ -723,6 +743,7 @@ mod test {
version_request_channels: vec![],
app_upgrade_broadcast,
refresh_version_check_tx,
+ cache_dir: PathBuf::new(),
_phantom: std::marker::PhantomData::<D>,
},
VersionRouterChannels {
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 14a11fff4e..6d0577ebf0 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -140,6 +140,7 @@ service ManagementService {
rpc AppUpgrade(google.protobuf.Empty) returns (google.protobuf.Empty) {}
rpc AppUpgradeAbort(google.protobuf.Empty) returns (google.protobuf.Empty) {}
rpc AppUpgradeEventsListen(google.protobuf.Empty) returns (stream AppUpgradeEvent) {}
+ rpc GetAppUpgradeCacheDir(google.protobuf.Empty) returns (google.protobuf.StringValue) {}
}
message AppUpgradeEvent {