mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-26 22:03:28 -05:00
fix(updater): check for updates in Flatpak too and say how to update
"No Update Available" must only mean the server has nothing newer. Flatpak installs were skipped before the check, so they always saw that alert. Replace the installable flag with an install method (integrated | flatpak | manual). Flatpak still never self-installs, but now gets a toast telling the user to run `flatpak update`; manual installs keep the download link. The X-Install-Mode header reports "flatpak" for those installs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8a029db38a
commit
9c3a4eeb67
@@ -12,7 +12,7 @@ import type {
|
||||
UpdateResponse,
|
||||
YaakNotification,
|
||||
} from "@yaakapp-internal/tauri-client";
|
||||
import { HStack, Icon, VStack } from "@yaakapp-internal/ui";
|
||||
import { HStack, Icon, InlineCode, VStack } from "@yaakapp-internal/ui";
|
||||
import { openSettings } from "../commands/openSettings";
|
||||
import { Button } from "../components/core/Button";
|
||||
import { ButtonInfiniteLoading } from "../components/core/ButtonInfiniteLoading";
|
||||
@@ -180,9 +180,9 @@ function showUpdateInstalledToast(version: string) {
|
||||
|
||||
async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
|
||||
const UPDATE_TOAST_ID = "update-info";
|
||||
const { version, replyEventId, downloaded, installable } = updateInfo;
|
||||
const { version, replyEventId, downloaded, install } = updateInfo;
|
||||
|
||||
jotaiStore.set(updateAvailableAtom, { version, downloaded, installable });
|
||||
jotaiStore.set(updateAvailableAtom, { version, downloaded, install });
|
||||
|
||||
const whatsNewButton = (
|
||||
<Button
|
||||
@@ -198,8 +198,9 @@ async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
|
||||
</Button>
|
||||
);
|
||||
|
||||
if (!installable) {
|
||||
// Nothing to reply to here; the backend only told us so we can point the user at the download
|
||||
if (install !== "integrated") {
|
||||
// Nothing to reply to here; the backend only told us so we can say how to update
|
||||
const flatpak = install === "flatpak";
|
||||
showToast({
|
||||
id: UPDATE_TOAST_ID,
|
||||
color: "info",
|
||||
@@ -208,22 +209,31 @@ async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
|
||||
<VStack>
|
||||
<h2 className="font-semibold">Yaak {version} is available</h2>
|
||||
<p className="text-text-subtle text-sm">
|
||||
This install can't update itself. Download the new version to upgrade.
|
||||
{flatpak ? (
|
||||
<>
|
||||
This install is managed by Flatpak. Update it with{" "}
|
||||
<InlineCode>flatpak update</InlineCode> or your software center.
|
||||
</>
|
||||
) : (
|
||||
"This install can't update itself. Download the new version to upgrade."
|
||||
)}
|
||||
</p>
|
||||
</VStack>
|
||||
),
|
||||
action: () => (
|
||||
<HStack space={1.5}>
|
||||
<Button
|
||||
size="xs"
|
||||
color="info"
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
onClick={async () => {
|
||||
await platform.openUrl("https://yaak.app/download");
|
||||
}}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
{!flatpak && (
|
||||
<Button
|
||||
size="xs"
|
||||
color="info"
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
onClick={async () => {
|
||||
await platform.openUrl("https://yaak.app/download");
|
||||
}}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
)}
|
||||
{whatsNewButton}
|
||||
</HStack>
|
||||
),
|
||||
|
||||
+8
-3
@@ -9,12 +9,17 @@ export type UpdateInfo = {
|
||||
version: string;
|
||||
downloaded: boolean;
|
||||
/**
|
||||
* Whether the app can download and install this update itself. When false, the
|
||||
* user has to go download the new version (e.g. Linux .deb/.rpm installs).
|
||||
* How this update gets applied. Anything but `Integrated` means the app can't do it
|
||||
* itself and the user is told how to update instead.
|
||||
*/
|
||||
installable: boolean;
|
||||
install: UpdateInstall;
|
||||
};
|
||||
|
||||
/**
|
||||
* How an update can be applied to this install.
|
||||
*/
|
||||
export type UpdateInstall = "integrated" | "flatpak" | "manual";
|
||||
|
||||
export type UpdateResponse = { type: "ack" } | { type: "action"; action: UpdateResponseAction };
|
||||
|
||||
export type UpdateResponseAction = "install" | "skip";
|
||||
|
||||
@@ -76,13 +76,6 @@ impl YaakUpdater {
|
||||
auto_download: bool,
|
||||
update_trigger: UpdateTrigger,
|
||||
) -> Result<bool> {
|
||||
// Flatpak installs (e.g. FlatPark) are updated by flatpak from their remote; the
|
||||
// in-app updater can't write inside the sandbox and must not try
|
||||
#[cfg(target_os = "linux")]
|
||||
if std::env::var_os("FLATPAK_ID").is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let settings = window.db().get_settings();
|
||||
let update_key = format!("{:x}", md5::compute(settings.id));
|
||||
self.last_check = Some(Instant::now());
|
||||
@@ -129,11 +122,15 @@ impl YaakUpdater {
|
||||
Some(update) => {
|
||||
let w = window.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// This install can't apply the update itself (e.g. a .deb or .rpm), so
|
||||
// just point the user at the download instead of failing to install it
|
||||
if !can_install_update(&update) {
|
||||
info!("{} available but must be installed manually", update.version);
|
||||
notify_manual_update(&w, &update);
|
||||
// Only hand the artifact to the updater plugin when this install can
|
||||
// apply it itself; otherwise tell the user how to update instead
|
||||
let install = update_install_method(&update);
|
||||
if install != UpdateInstall::Integrated {
|
||||
info!(
|
||||
"{} available, but this install updates via {install:?}",
|
||||
update.version
|
||||
);
|
||||
notify_external_update(&w, &update, install);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -214,9 +211,23 @@ struct UpdateInfo {
|
||||
reply_event_id: String,
|
||||
version: String,
|
||||
downloaded: bool,
|
||||
/// Whether the app can download and install this update itself. When false, the
|
||||
/// user has to go download the new version (e.g. Linux .deb/.rpm installs).
|
||||
installable: bool,
|
||||
/// How this update gets applied. Anything but `Integrated` means the app can't do it
|
||||
/// itself and the user is told how to update instead.
|
||||
install: UpdateInstall,
|
||||
}
|
||||
|
||||
/// How an update can be applied to this install.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Default, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "index.ts")]
|
||||
enum UpdateInstall {
|
||||
/// The app downloads and installs it itself
|
||||
#[default]
|
||||
Integrated,
|
||||
/// Flatpak install: updated by `flatpak update` from its remote (e.g. FlatPark)
|
||||
Flatpak,
|
||||
/// Nothing can install it in-app (distro package, Nix, unknown); download by hand
|
||||
Manual,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, TS)]
|
||||
@@ -285,7 +296,7 @@ async fn start_integrated_update<R: Runtime>(
|
||||
let info = UpdateInfo {
|
||||
version: update.version.to_string(),
|
||||
downloaded,
|
||||
installable: true,
|
||||
install: UpdateInstall::Integrated,
|
||||
reply_event_id: reply_id,
|
||||
};
|
||||
window
|
||||
@@ -320,13 +331,17 @@ async fn start_integrated_update<R: Runtime>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Tell the frontend about an update this install can't apply itself, so the user can go
|
||||
/// download it. Unlike the integrated flow, there is nothing to reply to.
|
||||
fn notify_manual_update<R: Runtime>(window: &WebviewWindow<R>, update: &Update) {
|
||||
/// Tell the frontend about an update this install can't apply itself, so the user can be
|
||||
/// told how to get it. Unlike the integrated flow, there is nothing to reply to.
|
||||
fn notify_external_update<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
update: &Update,
|
||||
install: UpdateInstall,
|
||||
) {
|
||||
let info = UpdateInfo {
|
||||
version: update.version.to_string(),
|
||||
downloaded: false,
|
||||
installable: false,
|
||||
install,
|
||||
reply_event_id: generate_id(),
|
||||
};
|
||||
if let Err(e) = window.emit_to(window.label(), "update_available", &info) {
|
||||
@@ -404,7 +419,19 @@ fn detect_install_mode() -> Option<&'static str> {
|
||||
return Some("nsis");
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
if cfg!(target_os = "linux") { linux_installer() } else { None }
|
||||
if !cfg!(target_os = "linux") {
|
||||
None
|
||||
} else if is_flatpak() {
|
||||
Some("flatpak")
|
||||
} else {
|
||||
linux_installer()
|
||||
}
|
||||
}
|
||||
|
||||
/// Flatpak installs (e.g. FlatPark) are updated by flatpak from their remote; the in-app
|
||||
/// updater can't write inside the sandbox and must not try.
|
||||
fn is_flatpak() -> bool {
|
||||
std::env::var_os("FLATPAK_ID").is_some()
|
||||
}
|
||||
|
||||
/// How Yaak was installed on Linux, as far as the updater plugin can install into it.
|
||||
@@ -442,15 +469,22 @@ fn package_manager_owns_exe(cmd: &str, query_arg: &str) -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Whether the updater plugin can install the artifact the server returned. On Linux the
|
||||
/// server may hand back a different package format than the one installed (it currently
|
||||
/// serves the AppImage for every Linux install), and unknown install methods (distro
|
||||
/// packages, Nix, ...) can't be updated in-app at all.
|
||||
fn can_install_update(update: &Update) -> bool {
|
||||
/// How the artifact the server returned can be applied to this install. On Linux the
|
||||
/// server may hand back a different package format than the one installed, Flatpak can't
|
||||
/// be written from inside the sandbox, and unknown install methods (distro packages,
|
||||
/// Nix, ...) can't be updated in-app at all.
|
||||
fn update_install_method(update: &Update) -> UpdateInstall {
|
||||
if !cfg!(target_os = "linux") {
|
||||
return true;
|
||||
return UpdateInstall::Integrated;
|
||||
}
|
||||
if is_flatpak() {
|
||||
return UpdateInstall::Flatpak;
|
||||
}
|
||||
if artifact_matches_installer(linux_installer(), update.download_url.path()) {
|
||||
UpdateInstall::Integrated
|
||||
} else {
|
||||
UpdateInstall::Manual
|
||||
}
|
||||
artifact_matches_installer(linux_installer(), update.download_url.path())
|
||||
}
|
||||
|
||||
/// Whether the artifact at `url_path` is in the package format `installer` can install.
|
||||
|
||||
Reference in New Issue
Block a user