diff --git a/public/locales/af/settings.json b/public/locales/af/settings.json index a5aea1374..79a58462c 100644 --- a/public/locales/af/settings.json +++ b/public/locales/af/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Pas Uitnodigingskode Toe", "cancel": "Cancel", "change-language": "Verander taal", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Gekoppelde Eweknieë", @@ -83,6 +85,11 @@ }, "pool-mining": "Poel Mynbou", "pool-mining-description": "Wanneer geaktiveer, sal u in \"n poel myn en by \"n groep mynwerkers aansluit.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx netwerk hashrate", "refresh-versions": "Verfris weergawes", "release-notes": { @@ -108,6 +115,7 @@ "title": "Outomatiese begin by stelsel opstart" }, "should-use-system-language": "Gebruik stelsel taal", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/public/locales/cn/settings.json b/public/locales/cn/settings.json index 7ef486bde..a25b19c23 100644 --- a/public/locales/cn/settings.json +++ b/public/locales/cn/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "应用邀请代码", "cancel": "取消", "change-language": "更改语言", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "已连接的节点", @@ -83,6 +85,11 @@ }, "pool-mining": "矿池挖矿", "pool-mining-description": "启用后,您将在矿池中挖矿并加入一组矿工(部落)。", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx 网络算力", "refresh-versions": "刷新版本", "release-notes": { @@ -108,6 +115,7 @@ "title": "系统启动时自动启动" }, "should-use-system-language": "使用系统语言", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "空投", diff --git a/public/locales/de/settings.json b/public/locales/de/settings.json index a4cee4eb5..97ce82b9c 100644 --- a/public/locales/de/settings.json +++ b/public/locales/de/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Einladungscode anwenden", "cancel": "Abbrechen", "change-language": "Sprache", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Verbundenen Peers", @@ -83,6 +85,11 @@ }, "pool-mining": "Pool-Mining", "pool-mining-description": "Wenn aktiviert, wirst du in einem Pool minen und einer Gruppe von Minern beitreten.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx-Netzwerk-Hashrate", "refresh-versions": "Versionen aktualisieren", "release-notes": { @@ -108,6 +115,7 @@ "title": "Automatischer Start beim Systemstart" }, "should-use-system-language": "Systemsprache verwenden", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/public/locales/en/settings.json b/public/locales/en/settings.json index 2614b8b1c..c857a8755 100644 --- a/public/locales/en/settings.json +++ b/public/locales/en/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Apply Invite Code", "cancel": "Cancel", "change-language": "Language", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Import new wallet", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?\nYour current wallet will be abandoned.", "connected-peers": "Connected Peers", @@ -83,6 +85,11 @@ }, "pool-mining": "Pool Mining", "pool-mining-description": "When enabled, you will mine in a pool and join a group of miners.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx network hashrate", "refresh-versions": "Refresh versions", "release-notes": { @@ -108,6 +115,7 @@ "title": "Auto-start on system boot" }, "should-use-system-language": "Use system language", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/public/locales/fr/settings.json b/public/locales/fr/settings.json index 89818aa67..093fae309 100644 --- a/public/locales/fr/settings.json +++ b/public/locales/fr/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Appliquer le code d\"invitation", "cancel": "Cancel", "change-language": "Langue", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Pairs connectés", @@ -83,6 +85,11 @@ }, "pool-mining": "Minage en pool", "pool-mining-description": "Lorsqu\"il est activé, vous minerez dans un pool et rejoindrez un groupe de mineurs.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Taux de hachage du réseau Randomx", "refresh-versions": "Rafraîchir les versions", "release-notes": { @@ -108,6 +115,7 @@ "title": "Démarrage automatique au démarrage du système" }, "should-use-system-language": "Utiliser la langue du système", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/public/locales/hi/settings.json b/public/locales/hi/settings.json index 7e13a3503..44135d491 100644 --- a/public/locales/hi/settings.json +++ b/public/locales/hi/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "आमंत्रण कोड लागू करें", "cancel": "रद्द करें", "change-language": "भाषा", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "जुड़े हुए सहकर्मी", @@ -83,6 +85,11 @@ }, "pool-mining": "पूल माइनिंग", "pool-mining-description": "सक्षम होने पर, आप एक पूल में माइन करेंगे और माइनर्स के समूह में शामिल होंगे।", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx नेटवर्क हैशरेट", "refresh-versions": "संस्करण ताज़ा करें", "release-notes": { @@ -108,6 +115,7 @@ "title": "सिस्टम बूट पर स्वचालित शुरूआत" }, "should-use-system-language": "सिस्टम भाषा का उपयोग करें", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "एयरड्रॉप", diff --git a/public/locales/id/settings.json b/public/locales/id/settings.json index 415c9f751..3d8f6559a 100644 --- a/public/locales/id/settings.json +++ b/public/locales/id/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Terapkan Kode Undangan", "cancel": "Batal", "change-language": "Bahasa", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Rekan Terhubung", @@ -83,6 +85,11 @@ }, "pool-mining": "Penambangan Pool", "pool-mining-description": "Ketika diaktifkan, Anda akan menambang dalam pool dan bergabung dengan kelompok penambang.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Hashrate jaringan Randomx", "refresh-versions": "Segarkan versi", "release-notes": { @@ -108,6 +115,7 @@ "title": "Mulai otomatis saat boot sistem" }, "should-use-system-language": "Gunakan bahasa sistem", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/public/locales/ja/settings.json b/public/locales/ja/settings.json index 496bc3c5b..c3f904e13 100644 --- a/public/locales/ja/settings.json +++ b/public/locales/ja/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "招待コードを適用", "cancel": "キャンセル", "change-language": "言語", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "接続されたピア", @@ -83,6 +85,11 @@ }, "pool-mining": "プールマイニング", "pool-mining-description": "有効にすると、プールでマイニングし、マイナーのグループに参加します。", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomxネットワークのハッシュレート", "refresh-versions": "バージョンを更新", "release-notes": { @@ -108,6 +115,7 @@ "title": "システム起動時に自動開始" }, "should-use-system-language": "システム言語を使用", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "エアドロップ", diff --git a/public/locales/ko/settings.json b/public/locales/ko/settings.json index 40da718af..581529585 100644 --- a/public/locales/ko/settings.json +++ b/public/locales/ko/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "초대 코드 적용", "cancel": "취소", "change-language": "언어", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "연결된 피어", @@ -83,6 +85,11 @@ }, "pool-mining": "풀 채굴", "pool-mining-description": "활성화되면 풀에서 채굴하고 마이너 그룹에 참여합니다.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx 네트워크 해시레이트", "refresh-versions": "버전 새로고침", "release-notes": { @@ -108,6 +115,7 @@ "title": "시스템 부팅 시 자동 시작" }, "should-use-system-language": "시스템 언어 사용", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "에어드롭", diff --git a/public/locales/pl/settings.json b/public/locales/pl/settings.json index ec5551010..acd0e61df 100644 --- a/public/locales/pl/settings.json +++ b/public/locales/pl/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Zastosuj Kod Zaproszenia", "cancel": "Anuluj", "change-language": "Zmień język", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Połączone węzły", @@ -85,6 +87,11 @@ }, "pool-mining": "Kopanie w puli", "pool-mining-description": "Po włączeniu tej opcji będziesz wydobywać w puli i dołączać do grupy górników (plemienia).", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Hashrate sieci Randomx", "refresh-versions": "Odśwież wersje", "release-notes": { @@ -110,6 +117,7 @@ "title": "Automatyczne uruchamianie przy starcie systemu" }, "should-use-system-language": "Użyj języka systemowego", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/public/locales/ru/settings.json b/public/locales/ru/settings.json index 779df8e4b..af84d7657 100644 --- a/public/locales/ru/settings.json +++ b/public/locales/ru/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Применить код приглашения", "cancel": "Отмена", "change-language": "Язык", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Подключенные узлы", @@ -83,6 +85,11 @@ }, "pool-mining": "Майнинг в пуле", "pool-mining-description": "При включении вы будете майнить в пуле и присоединитесь к группе майнеров.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Хэшрейт сети Randomx", "refresh-versions": "Обновить версии", "release-notes": { @@ -108,6 +115,7 @@ "title": "Автозапуск при загрузке системы" }, "should-use-system-language": "Использовать системный язык", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Эйрдроп", diff --git a/public/locales/tr/settings.json b/public/locales/tr/settings.json index c1ff96255..9200d6157 100644 --- a/public/locales/tr/settings.json +++ b/public/locales/tr/settings.json @@ -6,6 +6,8 @@ "applyInviteCode": "Davet Kodunu Uygula", "cancel": "İptal", "change-language": "Dil Değiştir", + "confirm": "Confirm", + "confirm-action": "Confirm action", "confirm-import-wallet": "Abandon current wallet?", "confirm-import-wallet-copy": "Are you sure you want to import a new wallet?", "connected-peers": "Bağlı Eşler", @@ -83,6 +85,11 @@ }, "pool-mining": "Havuz Madenciliği", "pool-mining-description": "Etkinleştirildiğinde, bir havuzda madencilik yapacak ve bir grup madenciye (kabileye) katılacaksınız.", + "pre-release": { + "description": "Explore the latest features before they go live and provide feedback", + "title": "Use pre-release version" + }, + "pre-release-note": "You are about to switch to the pre-release version of the application. This version can offer exciting new features and enhancements, with the possibility of encountering areas that may still be under refinement.", "randomx-network-hash-rate": "Randomx ağ hash oranı", "refresh-versions": "Sürümleri yenileyin", "release-notes": { @@ -108,6 +115,7 @@ "title": "Sistem açılışında otomatik başlat" }, "should-use-system-language": "Sistem Dilini Kullan", + "stable-version-note": "By turning off the pre-release version of the application, you will revert to the stable release, which prioritizes reliability and consistency. While you may miss out on the latest features and enhancements.", "stats-server-port": "Stats server port", "tabs": { "airdrop": "Airdrop", diff --git a/src-tauri/src/app_config.rs b/src-tauri/src/app_config.rs index 8ca20cde9..582d11672 100644 --- a/src-tauri/src/app_config.rs +++ b/src-tauri/src/app_config.rs @@ -110,6 +110,8 @@ pub struct AppConfigFromFile { show_experimental_settings: bool, #[serde(default = "default_p2pool_stats_server_port")] p2pool_stats_server_port: Option, + #[serde(default = "default_false")] + pre_release: bool, } impl Default for AppConfigFromFile { @@ -151,6 +153,7 @@ impl Default for AppConfigFromFile { window_settings: default_window_settings(), show_experimental_settings: false, p2pool_stats_server_port: default_p2pool_stats_server_port(), + pre_release: false, } } } @@ -263,6 +266,7 @@ pub(crate) struct AppConfig { window_settings: Option, show_experimental_settings: bool, p2pool_stats_server_port: Option, + pre_release: bool, } impl AppConfig { @@ -307,6 +311,7 @@ impl AppConfig { show_experimental_settings: false, keyring_accessed: false, p2pool_stats_server_port: default_p2pool_stats_server_port(), + pre_release: false, } } @@ -383,6 +388,7 @@ impl AppConfig { self.window_settings = config.window_settings; self.show_experimental_settings = config.show_experimental_settings; self.p2pool_stats_server_port = config.p2pool_stats_server_port; + self.pre_release = config.pre_release; KEYRING_ACCESSED.store( config.keyring_accessed, @@ -694,6 +700,10 @@ impl AppConfig { Ok(()) } + pub fn auto_update(&self) -> bool { + self.auto_update + } + pub async fn set_auto_update(&mut self, auto_update: bool) -> Result<(), anyhow::Error> { self.auto_update = auto_update; self.update_config_file().await?; @@ -724,6 +734,16 @@ impl AppConfig { Ok(()) } + pub fn pre_release(&self) -> bool { + self.pre_release + } + + pub async fn set_pre_release(&mut self, pre_release: bool) -> Result<(), anyhow::Error> { + self.pre_release = pre_release; + self.update_config_file().await?; + Ok(()) + } + // Allow needless update because in future there may be fields that are // missing #[allow(clippy::needless_update)] @@ -770,6 +790,7 @@ impl AppConfig { window_settings: self.window_settings.clone(), show_experimental_settings: self.show_experimental_settings, p2pool_stats_server_port: self.p2pool_stats_server_port, + pre_release: self.pre_release, }; let config = serde_json::to_string(config)?; debug!(target: LOG_TARGET, "Updating config file: {:?} {:?}", file, self.clone()); diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 290f262f9..fc3f05ed3 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1618,3 +1618,91 @@ pub async fn update_applications( Ok(()) } + +#[tauri::command] +pub async fn set_pre_release( + app: tauri::AppHandle, + pre_release: bool, + state: tauri::State<'_, UniverseAppState>, +) -> Result<(), String> { + let timer = Instant::now(); + state + .config + .write() + .await + .set_pre_release(pre_release) + .await + .map_err(|e| e.to_string())?; + + info!(target: LOG_TARGET, "Pre-release set to {}, try_update called", pre_release); + + state + .updates_manager + .try_update(app.clone(), true, !pre_release) + .await + .map_err(|e| e.to_string())?; + + if timer.elapsed() > MAX_ACCEPTABLE_COMMAND_TIME { + warn!(target: LOG_TARGET, "set_pre_release took too long: {:?}", timer.elapsed()); + } + + Ok(()) +} + +#[tauri::command] +pub async fn check_for_updates( + app: tauri::AppHandle, + state: tauri::State<'_, UniverseAppState>, +) -> Result, String> { + let timer = Instant::now(); + + let update = state + .updates_manager + .check_for_update(app.clone(), false) + .await + .map_err(|e| e.to_string())?; + + if timer.elapsed() > MAX_ACCEPTABLE_COMMAND_TIME { + warn!(target: LOG_TARGET, "check_for_updates took too long: {:?}", timer.elapsed()); + } + + Ok(update.map(|u| u.version)) +} + +#[tauri::command] +pub async fn try_update( + force: Option, + app: tauri::AppHandle, + state: tauri::State<'_, UniverseAppState>, +) -> Result<(), String> { + let timer = Instant::now(); + + state + .updates_manager + .try_update(app.clone(), force.unwrap_or(false), false) + .await + .map_err(|e| e.to_string())?; + + if timer.elapsed() > MAX_ACCEPTABLE_COMMAND_TIME { + warn!(target: LOG_TARGET, "check_for_updates took too long: {:?}", timer.elapsed()); + } + + Ok(()) +} + +#[tauri::command] +pub async fn proceed_with_update( + app: tauri::AppHandle, + state: tauri::State<'_, UniverseAppState>, +) -> Result<(), String> { + let timer = Instant::now(); + state + .updates_manager + .proceed_with_update(app.clone()) + .await + .map_err(|e| e.to_string())?; + if timer.elapsed() > MAX_ACCEPTABLE_COMMAND_TIME { + warn!(target: LOG_TARGET, "proceed_with_update took too long: {:?}", timer.elapsed()); + } + Ok(()) +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b922c5904..ba5596c56 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -30,6 +30,7 @@ use log::{debug, error, info, warn}; use p2pool::models::Connections; use std::fs::{create_dir_all, remove_dir_all, remove_file, File}; use tokio::sync::watch::{self}; +use updates_manager::UpdatesManager; use log4rs::config::RawConfig; use serde::Serialize; @@ -112,6 +113,7 @@ mod telemetry_manager; mod tests; mod tor_adapter; mod tor_manager; +mod updates_manager; mod user_listener; mod utils; mod wallet_adapter; @@ -178,6 +180,11 @@ async fn setup_inner( return Ok(()); } + state + .updates_manager + .init_periodic_updates(app.clone()) + .await?; + let data_dir = app .path() .app_local_data_dir() @@ -576,6 +583,7 @@ struct UniverseAppState { airdrop_access_token: Arc>>, p2pool_manager: P2poolManager, tor_manager: TorManager, + updates_manager: UpdatesManager, cached_p2pool_stats: Arc>>>, cached_p2pool_connections: Arc>>>, cached_wallet_details: Arc>>, @@ -644,6 +652,8 @@ fn main() { p2pool_manager.clone(), ); + let updates_manager = UpdatesManager::new(app_config.clone(), shutdown.to_signal()); + let feedback = Feedback::new(app_in_memory_config.clone(), app_config.clone()); let mm_proxy_manager = MmProxyManager::new(); @@ -670,6 +680,7 @@ fn main() { feedback: Arc::new(RwLock::new(feedback)), airdrop_access_token: Arc::new(RwLock::new(None)), tor_manager: TorManager::new(), + updates_manager, cached_p2pool_stats: Arc::new(RwLock::new(None)), cached_p2pool_connections: Arc::new(RwLock::new(None)), cached_wallet_details: Arc::new(RwLock::new(None)), @@ -883,7 +894,10 @@ fn main() { commands::get_p2pool_connections, commands::set_p2pool_stats_server_port, commands::get_used_p2pool_stats_server_port, - commands::get_network + commands::proceed_with_update, + commands::set_pre_release, + commands::check_for_updates, + commands::try_update, ]) .build(tauri::generate_context!()) .inspect_err( diff --git a/src-tauri/src/updates_manager.rs b/src-tauri/src/updates_manager.rs new file mode 100644 index 000000000..7b38e9f35 --- /dev/null +++ b/src-tauri/src/updates_manager.rs @@ -0,0 +1,221 @@ +// Copyright 2024. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::sync::Arc; +use tokio::time; + +use anyhow::anyhow; +use log::{error, info, warn}; + +use serde::{Deserialize, Serialize}; +use tauri::{Emitter, Url}; +use tauri_plugin_updater::{Update, UpdaterExt}; +use tokio::sync::RwLock; + +use crate::app_config::AppConfig; +use tari_shutdown::ShutdownSignal; +use tokio::time::Duration; + +const LOG_TARGET: &str = "tari::universe::updates_manager"; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DownloadProgressPayload { + pub event_type: String, + pub downloaded: u64, + pub total: u64, +} + +impl DownloadProgressPayload { + pub fn new(downloaded: u64, total: u64) -> Self { + Self { + event_type: "download_progress".to_string(), + downloaded, + total, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AskForUpdatePayload { + pub event_type: String, + pub version: String, +} + +impl AskForUpdatePayload { + pub fn new(version: String) -> Self { + Self { + event_type: "ask_for_update".to_string(), + version, + } + } +} + +#[derive(Clone)] +pub struct UpdatesManager { + config: Arc>, + update: Arc>>, + app_shutdown: ShutdownSignal, +} + +impl UpdatesManager { + pub fn new(config: Arc>, app_shutdown: ShutdownSignal) -> Self { + Self { + config, + update: Arc::new(RwLock::new(None)), + app_shutdown, + } + } + + pub async fn init_periodic_updates(&self, app: tauri::AppHandle) -> Result<(), anyhow::Error> { + let app_clone = app.clone(); + let self_clone = self.clone(); + tauri::async_runtime::spawn(async move { + let mut interval = time::interval(Duration::from_secs(3600)); + loop { + if self_clone.app_shutdown.is_triggered() && self_clone.app_shutdown.is_triggered() + { + break; + }; + interval.tick().await; + if let Err(e) = self_clone.try_update(app_clone.clone(), false, false).await { + error!(target: LOG_TARGET, "Error checking for updates: {:?}", e); + } + } + }); + + Ok(()) + } + + pub async fn try_update( + &self, + app: tauri::AppHandle, + force: bool, + enable_downgrade: bool, + ) -> Result<(), anyhow::Error> { + match self.check_for_update(app.clone(), enable_downgrade).await? { + Some(update) => { + let version = update.version.clone(); + info!(target: LOG_TARGET, "try_update: Update available: {:?}", version); + *self.update.write().await = Some(update); + let is_auto_update = self.config.read().await.auto_update(); + + if force { + info!(target: LOG_TARGET, "try_update: Proceeding with force update"); + self.proceed_with_update(app.clone()).await?; + } else if is_auto_update { + info!(target: LOG_TARGET, "try_update: Auto update is enabled. Proceeding with update"); + self.proceed_with_update(app.clone()).await?; + } else { + info!(target: LOG_TARGET, "try_update: Auto update is disabled. Prompting user to update"); + let payload = AskForUpdatePayload { + event_type: "ask_for_update".to_string(), + version, + }; + drop(app.emit("updates_event", payload).inspect_err(|e| { + warn!(target: LOG_TARGET, "Failed to emit 'updates-event' with UpdateAvailablePayload: {}", e); + })); + // proceed_with_update will be trigger by the user + } + } + None => { + info!(target: LOG_TARGET, "No updates available"); + } + } + + Ok(()) + } + + pub async fn check_for_update( + &self, + app: tauri::AppHandle, + enable_downgrade: bool, + ) -> Result, anyhow::Error> { + let is_pre_release = self.config.read().await.pre_release(); + let updates_url = self.get_updates_url(is_pre_release); + + let update = app + .updater_builder() + .version_comparator(move |current, update| { + if enable_downgrade { + // Needed for switching off the pre-release + update.version != current + } else { + update.version > current + } + }) + .endpoints(vec![updates_url]) + .expect("Failed to set update URL") + .build() + .expect("Failed to build updater") + .check() + .await + .expect("Failed to check for updates"); + + Ok(update) + } + + fn get_updates_url(&self, is_pre_release: bool) -> Url { + let updater_filename = if is_pre_release { + "alpha-latest" + } else { + "latest" + }; + + let update_url_string = format!("https://raw.githubusercontent.com/tari-project/universe/main/.updater/{updater_filename}.json"); + Url::parse(&update_url_string).expect("Failed to parse update URL") + } + + pub async fn proceed_with_update(&self, app: tauri::AppHandle) -> Result<(), anyhow::Error> { + let mut downloaded: u64 = 0; + let update = self + .update + .read() + .await + .clone() + .ok_or_else(|| anyhow!("No update available"))?; + + let mut last_emit = std::time::Instant::now(); + update + .download_and_install( + |chunk_length, content_length| { + downloaded += chunk_length as u64; + + let now = std::time::Instant::now(); + let is_last_chunk = content_length.map(|cl| downloaded >= cl).unwrap_or(false); + + if is_last_chunk || now.duration_since(last_emit) >= Duration::from_millis(100) { + last_emit = std::time::Instant::now(); + let payload = DownloadProgressPayload::new(downloaded, content_length.unwrap_or(downloaded)); + drop(app.emit("updates_event", payload).inspect_err(|e| { + warn!(target: LOG_TARGET, "Failed to emit 'updates_event' event: {}", e); + })); + } + }, + || { + app.restart(); + }, + ) + .await?; + + Ok(()) + } +} diff --git a/src/App/AppWrapper.tsx b/src/App/AppWrapper.tsx index 015ff18e2..90ca15271 100644 --- a/src/App/AppWrapper.tsx +++ b/src/App/AppWrapper.tsx @@ -4,14 +4,7 @@ import * as Sentry from '@sentry/react'; import { IGNORE_FETCHING } from '@app/App/sentryIgnore'; import { initSystray } from '@app/utils'; -import { - useCheckUpdate, - useDetectMode, - useDisableRefresh, - useInterval, - useLangaugeResolver, - useListenForExternalDependencies, -} from '@app/hooks'; +import { useDetectMode, useDisableRefresh, useLangaugeResolver, useListenForExternalDependencies } from '@app/hooks'; import packageInfo from '../../package.json'; import { useAppConfigStore } from '../store/useAppConfigStore.ts'; @@ -40,13 +33,10 @@ const sentryOptions = { setupLogger(); -const UPDATE_CHECK_INTERVAL = 1000 * 60 * 60; // 1 hour - export default function AppWrapper() { const allowTelemetry = useAppConfigStore((s) => s.allow_telemetry); const fetchAppConfig = useAppConfigStore((s) => s.fetchAppConfig); const setMiningNetwork = useMiningStore((s) => s.setMiningNetwork); - const checkUpdateTariUniverse = useCheckUpdate(); useDetectMode(); useDisableRefresh(); @@ -57,7 +47,6 @@ export default function AppWrapper() { useEffect(() => { async function initialize() { await fetchAppConfig(); - checkUpdateTariUniverse(); // first check await initSystray(); await setMiningNetwork(); } @@ -73,7 +62,5 @@ export default function AppWrapper() { } }, [allowTelemetry]); - useInterval(() => checkUpdateTariUniverse(), UPDATE_CHECK_INTERVAL); - return ; } diff --git a/src/components/dialogs/ConfirmationDialog.tsx b/src/components/dialogs/ConfirmationDialog.tsx new file mode 100644 index 000000000..382bddad6 --- /dev/null +++ b/src/components/dialogs/ConfirmationDialog.tsx @@ -0,0 +1,62 @@ +import { useTranslation } from 'react-i18next'; +import { Dialog, DialogContent } from '@app/components/elements/dialog/Dialog.tsx'; +import { Stack } from '@app/components/elements/Stack.tsx'; +import { Typography } from '@app/components/elements/Typography.tsx'; +import { Button } from '@app/components/elements/buttons/Button.tsx'; + +import { IoClose } from 'react-icons/io5'; +import { Divider } from '@app/components/elements/Divider.tsx'; +import { TextButton } from '@app/components/elements/buttons/TextButton.tsx'; +import { IconButton } from '@app/components/elements/buttons/IconButton.tsx'; +import { useTheme } from 'styled-components'; + +interface ConfirmationDialogProps { + title?: string; + description: string; + onConfirm: () => void; + onCancel: () => void; +} + +export default function ConfirmationDialog({ + title = 'confirm-action', + description, + onConfirm, + onCancel, +}: ConfirmationDialogProps) { + const { t } = useTranslation('settings', { useSuspense: false }); + const theme = useTheme(); + + return ( + + + + + {t(title)} + + + + + + + + {t(description)} + + + + + + {t('cancel')} + + + + + + + ); +} diff --git a/src/containers/floating/AutoUpdateDialog/AutoUpdateDialog.tsx b/src/containers/floating/AutoUpdateDialog/AutoUpdateDialog.tsx index 0188ba249..fcf0c723b 100644 --- a/src/containers/floating/AutoUpdateDialog/AutoUpdateDialog.tsx +++ b/src/containers/floating/AutoUpdateDialog/AutoUpdateDialog.tsx @@ -1,7 +1,6 @@ import { useTranslation } from 'react-i18next'; import { useUIStore } from '@app/store/useUIStore'; -import { useHandleUpdate } from '@app/hooks'; import { DialogContent, Dialog } from '@app/components/elements/dialog/Dialog'; import { SquaredButton } from '@app/components/elements/buttons/SquaredButton'; @@ -9,41 +8,85 @@ import { Typography } from '@app/components/elements/Typography'; import { UpdatedStatus } from './UpdatedStatus'; import { ButtonsWrapper } from './AutoUpdateDialog.styles'; -import { useEffect, useRef } from 'react'; +import { useCallback, useEffect, useState } from 'react'; +import { listen } from '@tauri-apps/api/event'; +import { invoke } from '@tauri-apps/api/core'; + +interface DownloadProgressPayload { + event_type: 'download_progress'; + downloaded: number; + total: number; +} + +interface AskForUpdatePayload { + event_type: 'ask_for_update'; + version: string; +} export default function AutoUpdateDialog() { - const hasFetched = useRef(false); - const { handleUpdate, fetchUpdate, updateData, isLoading, contentLength, downloaded, handleClose } = - useHandleUpdate(); const { t } = useTranslation('setup-view', { useSuspense: false }); - const open = useUIStore((s) => s.dialogToShow === 'autoUpdate'); + const setDialogToShow = useUIStore((s) => s.setDialogToShow); + const [version, setVersion] = useState(''); + const [downloaded, setDownloaded] = useState(0); + const [contentLength, setContentLength] = useState(0); + + const isDownloading = downloaded > 0; + const isDownloaded = isDownloading && downloaded === contentLength; + const subtitle = isDownloading ? 'installing-latest-version' : 'would-you-like-to-install'; useEffect(() => { - if (hasFetched.current) return; - fetchUpdate().then(() => { - hasFetched.current = true; - }); - }, [fetchUpdate]); + const unlistenPromise = listen( + 'updates_event', + ({ payload }: { payload: AskForUpdatePayload | DownloadProgressPayload }) => { + switch (payload.event_type) { + case 'ask_for_update': + setDialogToShow('autoUpdate'); + setVersion(payload.version); + break; + case 'download_progress': + if (!open) { + // open when auto update is triggered + setDialogToShow('autoUpdate'); + } + setDownloaded(payload.downloaded); + setContentLength(payload.total); + break; + default: + console.warn('Unknown tauri event: ', payload); + break; + } + } + ); + return () => { + unlistenPromise.then((unlisten) => unlisten()); + }; + }, [open, setDialogToShow]); + + const handleClose = useCallback(() => { + console.info('Update declined'); + setDialogToShow(null); + }, [setDialogToShow]); + + const handleUpdate = useCallback(() => { + console.info('Proceed with update'); + invoke('proceed_with_update').catch((e) => console.error('Failed to proceed with update', e)); + }, []); - const subtitle = isLoading ? 'installing-latest-version' : 'would-you-like-to-install'; return ( {t('new-tari-version-available')} - {t(subtitle, { version: updateData?.version })} - {isLoading && } - - {downloaded > 0 && downloaded === contentLength ? ( - {`Update downloaded: Restarting Tari Universe`} - ) : null} + {t(subtitle, { version })} + {isDownloading && } + {isDownloaded && {`Update downloaded: Restarting Tari Universe`}} - {!isLoading && updateData && ( + {!isDownloading && ( <> - handleClose()} color="warning"> + {t('no')} - handleUpdate()} color="green"> + {t('yes')} diff --git a/src/containers/floating/Settings/sections/general/GeneralSettings.tsx b/src/containers/floating/Settings/sections/general/GeneralSettings.tsx index aa123ec43..03b853d77 100644 --- a/src/containers/floating/Settings/sections/general/GeneralSettings.tsx +++ b/src/containers/floating/Settings/sections/general/GeneralSettings.tsx @@ -7,12 +7,14 @@ import LanguageSettings from './LanguageSettings.tsx'; import { ResetSettingsButton } from './ResetSettingsButton.tsx'; import StartApplicationOnBootSettings from './StartApplicationOnBootSettings.tsx'; import AutoUpdate from './AutoUpdate.tsx'; +import PreReleaseSettings from './PreReleaseSettings.tsx'; export const GeneralSettings = () => { return ( <> + diff --git a/src/containers/floating/Settings/sections/general/PreReleaseSettings.tsx b/src/containers/floating/Settings/sections/general/PreReleaseSettings.tsx new file mode 100644 index 000000000..05bf85324 --- /dev/null +++ b/src/containers/floating/Settings/sections/general/PreReleaseSettings.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from 'react-i18next'; +import { ToggleSwitch } from '@app/components/elements/ToggleSwitch'; +import { useAppConfigStore } from '@app/store/useAppConfigStore'; +import { useCallback, useState } from 'react'; +import { Typography } from '@app/components/elements/Typography.tsx'; +import { + SettingsGroupWrapper, + SettingsGroup, + SettingsGroupContent, + SettingsGroupTitle, + SettingsGroupAction, +} from '../../components/SettingsGroup.styles'; +import ConfirmationDialog from '@app/components/dialogs/ConfirmationDialog'; + +export default function PreReleaseSettings() { + const isPreRelease = useAppConfigStore((s) => s.pre_release); + const setPreRelease = useAppConfigStore((s) => s.setPreRelease); + const { t } = useTranslation('settings', { useSuspense: false }); + const [isDialogOpen, setDialogOpen] = useState(false); + + const closeDialog = useCallback(() => { + setDialogOpen(false); + }, [setDialogOpen]); + + const askForConfirmation = useCallback(() => { + setDialogOpen(true); + }, [setDialogOpen]); + + const applyChange = useCallback(() => { + closeDialog(); + setPreRelease(!isPreRelease); + }, [closeDialog, isPreRelease, setPreRelease]); + + return ( + + + + + {t('pre-release.title')} + + {t('pre-release.description')} + + + + + + + {isDialogOpen && ( + + )} + + ); +} diff --git a/src/containers/floating/Settings/sections/releaseNotes/ReleaseNotes.tsx b/src/containers/floating/Settings/sections/releaseNotes/ReleaseNotes.tsx index 47c8488a9..387bc79b8 100644 --- a/src/containers/floating/Settings/sections/releaseNotes/ReleaseNotes.tsx +++ b/src/containers/floating/Settings/sections/releaseNotes/ReleaseNotes.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import ReactMarkdown from 'react-markdown'; import { IconImage, @@ -15,8 +15,7 @@ import { AccordionItem } from './AccordionItem/AccordionItem'; import tariIcon from './tari-icon.png'; import packageInfo from '../../../../../../package.json'; import { useTranslation } from 'react-i18next'; -import { useUIStore } from '@app/store/useUIStore'; -import { check } from '@tauri-apps/plugin-updater'; +import { invoke } from '@tauri-apps/api/core'; const appVersion = packageInfo.version; const versionString = `v${appVersion}`; @@ -46,7 +45,6 @@ interface ReleaseSection { } export const ReleaseNotes = () => { - const { setDialogToShow } = useUIStore(); const [sections, setSections] = useState([]); const [isLoading, setIsLoading] = useState(true); const [openSectionIndex, setOpenSectionIndex] = useState(0); @@ -77,9 +75,10 @@ export const ReleaseNotes = () => { useEffect(() => { const checkForUpdates = async () => { - const update = await check(); - const shouldUpdate = !!update?.available; - setNeedsUpgrade(shouldUpdate); + const version = await invoke('check_for_updates').catch((err) => { + console.error('Error checking for updates:', err); + }); + setNeedsUpgrade(!!version); }; checkForUpdates(); @@ -89,6 +88,12 @@ export const ReleaseNotes = () => { setOpenSectionIndex(openSectionIndex === index ? null : index); }; + const handleUpdate = useCallback(async () => { + invoke('proceed_with_update', { force: true }).catch((err) => { + console.error('Error updating:', err); + }); + }, []); + return ( @@ -101,7 +106,7 @@ export const ReleaseNotes = () => { {needsUpgrade && !isLoading && ( - setDialogToShow('autoUpdate')}> + {t('settings:release-notes.upgrade-available')} )} diff --git a/src/hooks/app/index.ts b/src/hooks/app/index.ts index c1549a44c..1af1019ab 100644 --- a/src/hooks/app/index.ts +++ b/src/hooks/app/index.ts @@ -6,4 +6,3 @@ export * from './useListenForExternalDependencies.ts'; export * from './useSetUp.ts'; export * from './useShuttingDown.ts'; export * from './useSystemTray.ts'; -export * from './useUpdateStatus.ts'; diff --git a/src/hooks/app/useSetUp.ts b/src/hooks/app/useSetUp.ts index c52aca6cf..cbda61289 100644 --- a/src/hooks/app/useSetUp.ts +++ b/src/hooks/app/useSetUp.ts @@ -16,7 +16,6 @@ export function useSetUp() { const adminShow = useUIStore((s) => s.adminShow); const setSetupDetails = useAppStateStore((s) => s.setSetupDetails); const setCriticalError = useAppStateStore((s) => s.setCriticalError); - const isAfterAutoUpdate = useAppStateStore((s) => s.isAfterAutoUpdate); const setSettingUpFinished = useAppStateStore((s) => s.setSettingUpFinished); const fetchApplicationsVersionsWithRetry = useAppStateStore((s) => s.fetchApplicationsVersionsWithRetry); @@ -65,7 +64,7 @@ export function useSetUp() { break; } }); - if (isAfterAutoUpdate && syncedAidropWithBackend && !isInitializingRef.current) { + if (syncedAidropWithBackend && !isInitializingRef.current) { isInitializingRef.current = true; clearStorage(); invoke('setup_application').catch((e) => { @@ -76,13 +75,5 @@ export function useSetUp() { return () => { unlistenPromise.then((unlisten) => unlisten()); }; - }, [ - clearStorage, - handlePostSetup, - isAfterAutoUpdate, - setCriticalError, - setSetupDetails, - adminShow, - syncedAidropWithBackend, - ]); + }, [clearStorage, handlePostSetup, setCriticalError, setSetupDetails, adminShow, syncedAidropWithBackend]); } diff --git a/src/hooks/app/useUpdateStatus.ts b/src/hooks/app/useUpdateStatus.ts deleted file mode 100644 index af53e6811..000000000 --- a/src/hooks/app/useUpdateStatus.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { useCallback, useState } from 'react'; -import { check, Update } from '@tauri-apps/plugin-updater'; -import { relaunch } from '@tauri-apps/plugin-process'; - -import { useAppStateStore } from '@app/store/appStateStore'; -import { useAppConfigStore } from '@app/store/useAppConfigStore'; -import { useUIStore } from '@app/store/useUIStore'; - -export const useHandleUpdate = () => { - const setIsAfterAutoUpdate = useAppStateStore((s) => s.setIsAfterAutoUpdate); - const setError = useAppStateStore((s) => s.setError); - const auto_update = useAppConfigStore((s) => s.auto_update); - const [updateData, setUpdateData] = useState(); - const [isLoading, setIsLoading] = useState(false); - const [contentLength, setContentLength] = useState(0); - const [downloaded, setDownloaded] = useState(0); - const setDialogToShow = useUIStore((s) => s.setDialogToShow); - - const handleClose = useCallback(() => { - setDialogToShow(null); - setIsAfterAutoUpdate(true); - }, [setIsAfterAutoUpdate, setDialogToShow]); - - const handleUpdate = useCallback(async () => { - if (!updateData) return; - setIsLoading(true); - console.info('Installing latest version of Tari Universe'); - - updateData - .downloadAndInstall(async (event) => { - switch (event.event) { - case 'Started': - setContentLength(event.data.contentLength || 0); - break; - case 'Progress': - setDownloaded((c) => c + event.data.chunkLength); - break; - case 'Finished': - console.info('download finished'); - break; - } - }) - .then(async () => { - handleClose(); - await relaunch(); - }) - .catch((e) => { - console.error(e); - setError(e); - }); - }, [handleClose, setError, updateData]); - - const fetchUpdate = useCallback(async () => { - const update = await check(); - if (update) { - setUpdateData(update); - - if (auto_update) { - await handleUpdate(); - } - } - }, [auto_update, handleUpdate]); - - return { - fetchUpdate, - handleUpdate, - updateData, - isLoading, - contentLength, - handleClose, - downloaded, - }; -}; - -export const useCheckUpdate = () => { - const setIsAfterAutoUpdate = useAppStateStore((s) => s.setIsAfterAutoUpdate); - const setDialogToShow = useUIStore((s) => s.setDialogToShow); - - return useCallback(() => { - check() - .then((updateRes) => { - if (updateRes && updateRes.available) { - setDialogToShow('autoUpdate'); - } else { - setIsAfterAutoUpdate(true); - } - }) - .catch(() => setIsAfterAutoUpdate(true)); - }, [setDialogToShow, setIsAfterAutoUpdate]); -}; diff --git a/src/store/appStateStore.ts b/src/store/appStateStore.ts index 1f5869cf2..47065e268 100644 --- a/src/store/appStateStore.ts +++ b/src/store/appStateStore.ts @@ -7,8 +7,6 @@ import { useMiningStore } from './useMiningStore'; import { addToast } from '@app/components/ToastStack/useToastStore'; interface AppState { - isAfterAutoUpdate: boolean; - setIsAfterAutoUpdate: (value: boolean) => void; criticalError?: string; setCriticalError: (value: string | undefined) => void; error?: string; @@ -38,8 +36,6 @@ interface AppState { } export const useAppStateStore = create()((set, getState) => ({ - isAfterAutoUpdate: false, - setIsAfterAutoUpdate: (value: boolean) => set({ isAfterAutoUpdate: value }), criticalError: undefined, setCriticalError: (criticalError) => set({ criticalError }), error: undefined, diff --git a/src/store/useAppConfigStore.ts b/src/store/useAppConfigStore.ts index ece45bcec..adeefed24 100644 --- a/src/store/useAppConfigStore.ts +++ b/src/store/useAppConfigStore.ts @@ -34,6 +34,7 @@ interface Actions { setVisualMode: (enabled: boolean) => void; setShowExperimentalSettings: (showExperimentalSettings: boolean) => Promise; setP2poolStatsServerPort: (port: number | null) => Promise; + setPreRelease: (preRelease: boolean) => Promise; } type AppConfigStoreState = State & Actions; @@ -63,6 +64,7 @@ const initialState: State = { custom_max_gpu_usage: [], show_experimental_settings: false, p2pool_stats_server_port: null, + pre_release: false, }; export const useAppConfigStore = create()((set, getState) => ({ @@ -301,4 +303,13 @@ export const useAppConfigStore = create()((set, getState) = set({ p2pool_stats_server_port: port }); }); }, + setPreRelease: async (preRelease) => { + set({ pre_release: preRelease }); + invoke('set_pre_release', { preRelease }).catch((e) => { + const appStateStore = useAppStateStore.getState(); + console.error('Could not set pre release', e); + appStateStore.setError('Could not change pre release'); + set({ pre_release: !preRelease }); + }); + }, })); diff --git a/src/types/app-status.ts b/src/types/app-status.ts index e6f336e78..84737ed6d 100644 --- a/src/types/app-status.ts +++ b/src/types/app-status.ts @@ -47,6 +47,7 @@ export interface AppConfig { monero_address_is_generated?: boolean; created_at: string; p2pool_stats_server_port: number | null; + pre_release: boolean; } export enum ExternalDependencyStatus { diff --git a/src/types/invoke.ts b/src/types/invoke.ts index d191f663b..4f3041cb4 100644 --- a/src/types/invoke.ts +++ b/src/types/invoke.ts @@ -72,6 +72,10 @@ declare module '@tauri-apps/api/core' { function invoke(param: 'fetch_tor_bridges'): Promise; function invoke(param: 'get_tor_entry_guards'): Promise; function invoke(param: 'set_visual_mode', payload: { enabled: boolean }): Promise; + function invoke(param: 'set_pre_release', payload: { preRelease: boolean }): Promise; + function invoke(param: 'proceed_with_update'): Promise; + function invoke(param: 'check_for_updates'): Promise; + function invoke(param: 'try_update', payload?: { force?: boolean }): Promise; function invoke( param: 'set_show_experimental_settings', payload: { showExperimentalSettings: boolean }