diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8eb50b6eb..26f3c2d6ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -140,6 +140,12 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NO_STRIP: ${{ startsWith(inputs.platform, 'ubuntu') }} + # In-app updater signing. Empty until the maintainer adds these repo + # secrets and enables createUpdaterArtifacts + plugins.updater (pubkey) + # in tauri.conf.json. While unset, tauri-action builds exactly as + # before (no updater artifacts). + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: args: --verbose ${{ inputs.build-args }} ${{ inputs.target != '' && '--target' || '' }} ${{ inputs.target }} assetNamePattern: ${{ steps.patch-release-name.outputs.platform }} diff --git a/package-lock.json b/package-lock.json index de0828ed60..b18c942e2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@tauri-apps/plugin-os": "^2.3.2", "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.9.0", "@uiw/color-convert": "^2.10.1", "@uiw/react-color-wheel": "^2.10.1", "clsx": "^2.1.1", @@ -2391,6 +2392,15 @@ "@tauri-apps/api": "^2.10.1" } }, + "node_modules/@tauri-apps/plugin-updater": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.10.1.tgz", + "integrity": "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.10.1" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", diff --git a/package.json b/package.json index 12548e3b78..b5d86ead67 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@tauri-apps/plugin-os": "^2.3.2", "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.9.0", "@uiw/color-convert": "^2.10.1", "@uiw/react-color-wheel": "^2.10.1", "clsx": "^2.1.1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c86cdba8b3..ebe62391c6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -70,6 +70,7 @@ sysinfo = "0.39.3" [target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies] trash = "5.2.6" tauri-plugin-single-instance = "2.4.2" +tauri-plugin-updater = "2" reqwest = { version = "0.13", default-features = false, features = ["json", "multipart", "rustls"] } [target.'cfg(target_os = "macos")'.dependencies] diff --git a/src-tauri/capabilities/updater.json b/src-tauri/capabilities/updater.json new file mode 100644 index 0000000000..ea4712b132 --- /dev/null +++ b/src-tauri/capabilities/updater.json @@ -0,0 +1,8 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "updater", + "description": "Allow checking for and installing in-app updates (desktop only).", + "platforms": ["macOS", "windows", "linux"], + "windows": ["main"], + "permissions": ["updater:default"] +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 597d74c1b0..affa7e850b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1892,6 +1892,7 @@ pub fn run() { #[cfg(not(any(target_os = "android", target_os = "ios")))] { + builder = builder.plugin(tauri_plugin_updater::Builder::new().build()); builder = builder.plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| { log::info!( "New instance launched with args: {:?}. Focusing main window.", diff --git a/src/components/panel/MainLibrary.tsx b/src/components/panel/MainLibrary.tsx index 7b8e57c125..fd004895cf 100644 --- a/src/components/panel/MainLibrary.tsx +++ b/src/components/panel/MainLibrary.tsx @@ -1,6 +1,8 @@ import React, { useState, useEffect, useMemo } from 'react'; import { getVersion } from '@tauri-apps/api/app'; import { open } from '@tauri-apps/plugin-shell'; +import { check } from '@tauri-apps/plugin-updater'; +import { relaunch } from '@tauri-apps/plugin-process'; import { AlertTriangle, Check, @@ -37,6 +39,8 @@ import { useLibraryStore } from '../../store/useLibraryStore'; import LibraryGrid from './library/LibraryGrid'; import { SearchInput, ViewOptionsDropdown } from './library/LibraryHeader'; +const RELEASES_URL = 'https://github.com/CyberTimon/RapidRAW/releases/latest'; + interface MainLibraryProps { activePath: string | null; aiModelDownloadStatus: string | null; @@ -93,6 +97,7 @@ export default function MainLibrary(props: MainLibraryProps) { const [appVersion, setAppVersion] = useState(''); const [isUpdateAvailable, setIsUpdateAvailable] = useState(false); const [latestVersion, setLatestVersion] = useState(''); + const [isUpdating, setIsUpdating] = useState(false); const [isBusyDelayed, setIsBusyDelayed] = useState(false); const [isProgressHovered, setIsProgressHovered] = useState(false); @@ -222,6 +227,28 @@ export default function MainLibrary(props: MainLibraryProps) { checkVersion(); }, []); + // In-app update if the maintainer has enabled signed updater artifacts, else manual download. + const handleUpdateClick = async () => { + if (!isUpdateAvailable || isUpdating) { + return; + } + try { + const update = await check(); + if (update?.available) { + setIsUpdating(true); + await update.downloadAndInstall(); + await relaunch(); + return; + } + await open(RELEASES_URL); + } catch (error) { + console.error('In-app update unavailable, falling back to manual download:', error); + await open(RELEASES_URL); + } finally { + setIsUpdating(false); + } + }; + if (!props.rootPaths || props.rootPaths.length === 0) { if (!props.appSettings) { return null; @@ -353,24 +380,24 @@ export default function MainLibrary(props: MainLibraryProps) { { - if (isUpdateAvailable) { - open('https://github.com/CyberTimon/RapidRAW/releases/latest'); - } - }} + onClick={handleUpdateClick} data-tooltip={ - isUpdateAvailable - ? t('library.splash.downloadVersion', { version: latestVersion }) - : t('library.splash.latestVersion') + isUpdating + ? t('library.splash.downloadingUpdate') + : isUpdateAvailable + ? t('library.splash.downloadVersion', { version: latestVersion }) + : t('library.splash.latestVersion') } > - - {t('library.splash.version', { version: appVersion })} + + {isUpdating + ? t('library.splash.downloadingUpdate') + : t('library.splash.version', { version: appVersion })} - {isUpdateAvailable && ( + {isUpdateAvailable && !isUpdating && ( {t('library.splash.newVersionAvailable')} diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index f2555f9c06..fdb448b719 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -954,6 +954,7 @@ "descriptionDesktop": "Ein blitzschneller, GPU-beschleunigter RAW-Bildeditor. Öffnen Sie einen Ordner, um zu beginnen.", "donate": "Spenden auf Ko-Fi", "downloadVersion": "Klicken, um Version {{version}} herunterzuladen", + "downloadingUpdate": "Aktualisierung wird heruntergeladen…", "goToSettings": "Zu den Einstellungen", "imagesBy": "Bilder von", "latestVersion": "Sie haben die neueste Version", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 67c9ed68e2..722cce2d2b 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -954,6 +954,7 @@ "descriptionDesktop": "A blazingly fast, GPU-accelerated RAW image editor. Open a folder to begin.", "donate": "Donate on Ko-Fi", "downloadVersion": "Click to download version {{version}}", + "downloadingUpdate": "Downloading update…", "goToSettings": "Go to Settings", "imagesBy": "Images by", "latestVersion": "You are on the latest version", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index e95875f3d2..4aec536117 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -954,6 +954,7 @@ "descriptionDesktop": "Un editor de imágenes RAW increíblemente rápido y acelerado por GPU. Abre una carpeta para comenzar.", "donate": "Donar en Ko-Fi", "downloadVersion": "Haz clic para descargar la versión {{version}}", + "downloadingUpdate": "Descargando actualización…", "goToSettings": "Ir a Ajustes", "imagesBy": "Imágenes por", "latestVersion": "Estás en la versión más reciente", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index bee664d005..0a0a7a778a 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -954,6 +954,7 @@ "descriptionDesktop": "Un éditeur d'images RAW ultra-rapide, accéléré par GPU. Ouvrez un dossier pour commencer.", "donate": "Faire un don sur Ko-Fi", "downloadVersion": "Cliquez pour télécharger la version {{version}}", + "downloadingUpdate": "Téléchargement de la mise à jour…", "goToSettings": "Aller aux paramètres", "imagesBy": "Images par", "latestVersion": "Vous utilisez la dernière version", diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 24620fe1eb..6141016243 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -954,6 +954,7 @@ "descriptionDesktop": "Un editor di immagini RAW velocissimo e con accelerazione GPU. Apri una cartella per iniziare.", "donate": "Dona su Ko-Fi", "downloadVersion": "Clicca per scaricare la versione {{version}}", + "downloadingUpdate": "Download dell’aggiornamento…", "goToSettings": "Vai alle Impostazioni", "imagesBy": "Immagini di", "latestVersion": "Hai l'ultima versione installata", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 490323d7c7..dd152d62b2 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -953,6 +953,7 @@ "descriptionDesktop": "圧倒的に高速な、GPUアクセラレーション対応RAW画像エディタ。フォルダを開いて始めましょう。", "donate": "Ko-fiで寄付する", "downloadVersion": "クリックしてバージョン {{version}} をダウンロード", + "downloadingUpdate": "アップデートをダウンロード中…", "goToSettings": "設定へ移動", "imagesBy": "写真提供:", "latestVersion": "最新バージョンを使用しています", diff --git a/src/i18n/locales/ko.json b/src/i18n/locales/ko.json index b2e5dddf32..62fb1f794f 100644 --- a/src/i18n/locales/ko.json +++ b/src/i18n/locales/ko.json @@ -954,6 +954,7 @@ "descriptionDesktop": "매우 빠른 GPU 가속 RAW 이미지 편집기입니다. 폴더를 열어 시작하세요.", "donate": "Ko-Fi에서 후원하기", "downloadVersion": "클릭하여 {{version}} 버전 다운로드", + "downloadingUpdate": "업데이트 다운로드 중…", "goToSettings": "설정으로 이동", "imagesBy": "이미지 제공", "latestVersion": "최신 버전을 사용 중입니다", diff --git a/src/i18n/locales/pl.json b/src/i18n/locales/pl.json index e1613f03d3..6596e059bd 100644 --- a/src/i18n/locales/pl.json +++ b/src/i18n/locales/pl.json @@ -998,6 +998,7 @@ "descriptionDesktop": "Niewiarygodnie szybki, akcelerowany przez GPU edytor obrazów RAW. Otwórz folder, aby rozpocząć.", "donate": "Przekaż darowiznę na Ko-Fi", "downloadVersion": "Kliknij, aby pobrać wersję {{version}}", + "downloadingUpdate": "Pobieranie aktualizacji…", "goToSettings": "Przejdź do ustawień", "imagesBy": "Obrazy autorstwa", "latestVersion": "Posiadasz najnowszą wersję", diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index c512a1f39f..5de9b5076b 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -954,6 +954,7 @@ "descriptionDesktop": "Um editor de imagens RAW ultrarrápido e acelerado por GPU. Abra uma pasta para começar.", "donate": "Doar no Ko-Fi", "downloadVersion": "Clique para baixar a versão {{version}}", + "downloadingUpdate": "Baixando atualização…", "goToSettings": "Ir para Configurações", "imagesBy": "Imagens por", "latestVersion": "Você está na versão mais recente", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index da613c7ecc..edad7f0330 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -953,6 +953,7 @@ "descriptionDesktop": "Невероятно быстрый редактор RAW-файлов с GPU-ускорением. Откройте папку, чтобы начать.", "donate": "Поддержать на Ko-Fi", "downloadVersion": "Нажмите, чтобы загрузить версию {{version}}", + "downloadingUpdate": "Загрузка обновления…", "goToSettings": "Перейти в настройки", "imagesBy": "Фото от", "latestVersion": "У вас установлена последняя версия", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 8c2900df8d..18ea39b9dc 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -954,6 +954,7 @@ "descriptionDesktop": "一款极速、GPU 加速的 RAW 图像编辑器。打开文件夹以开始。", "donate": "在 Ko-Fi 上捐赠", "downloadVersion": "点击下载版本 {{version}}", + "downloadingUpdate": "正在下载更新…", "goToSettings": "前往设置", "imagesBy": "图像来源", "latestVersion": "您已是最新版本", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 274a7b909c..cd5868abb4 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -954,6 +954,7 @@ "descriptionDesktop": "一款極速、GPU 加速的 RAW 影像編輯器。開啟資料夾以開始。", "donate": "在 Ko-Fi 上捐贈", "downloadVersion": "點選下載版本 {{version}}", + "downloadingUpdate": "正在下載更新…", "goToSettings": "前往設定", "imagesBy": "影像來源", "latestVersion": "您已是最新版本",