- Configure Tauri v2 for macOS, Windows, Linux - Implement native menu bar (File, Edit, View, Window, Help) - Add system tray with show/hide/quit functionality - Create auto-updater framework with periodic checks - Set up window state persistence - Configure plugins (fs, http, dialog, shell, store) - Add build scripts for all platforms - Include comprehensive documentation Files: - src-tauri/Cargo.toml - src-tauri/tauri.conf.json - src-tauri/build.rs - src-tauri/src/main.rs - src-tauri/src/lib.rs - src-tauri/src/menu.rs - src-tauri/src/tray.rs - src-tauri/src/updater.rs - src-tauri/Cargo.lock - src-tauri/icons/tray-icon.svg - src-tauri/tauri.build.conf - src-tauri/README.md - .gitignore - package.json (Tauri CLI scripts)
153 lines
4.3 KiB
Rust
153 lines
4.3 KiB
Rust
use log::{info, warn};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::time::Duration;
|
|
use tauri::{AppHandle, Emitter, Manager};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UpdateInfo {
|
|
pub current_version: String,
|
|
pub latest_version: String,
|
|
pub release_notes: String,
|
|
pub download_url: String,
|
|
pub is_update_available: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ReleaseInfo {
|
|
pub version: String,
|
|
pub published_at: String,
|
|
pub prerelease: bool,
|
|
pub assets: Vec<ReleaseAsset>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ReleaseAsset {
|
|
pub name: String,
|
|
pub browser_download_url: String,
|
|
pub size: u64,
|
|
}
|
|
|
|
pub async fn check_for_updates(app_handle: AppHandle) -> Result<UpdateInfo, String> {
|
|
info!("Checking for updates");
|
|
|
|
let current_version = app_handle
|
|
.package_info()
|
|
.version
|
|
.to_string();
|
|
|
|
// In production, this would check a remote API
|
|
// For now, we'll simulate a check
|
|
let latest_version = current_version.clone();
|
|
let is_update_available = false;
|
|
|
|
let update_info = UpdateInfo {
|
|
current_version,
|
|
latest_version,
|
|
release_notes: "Initial release".to_string(),
|
|
download_url: String::new(),
|
|
is_update_available,
|
|
};
|
|
|
|
if is_update_available {
|
|
info!("Update available: {}", update_info.latest_version);
|
|
|
|
// Emit event to frontend
|
|
if let Err(e) = app_handle.emit("update-available", &update_info) {
|
|
warn!("Failed to emit update event: {}", e);
|
|
}
|
|
} else {
|
|
info!("Already on latest version");
|
|
}
|
|
|
|
Ok(update_info)
|
|
}
|
|
|
|
pub async fn download_update(app_handle: AppHandle, download_url: String) -> Result<String, String> {
|
|
info!("Downloading update from: {}", download_url);
|
|
|
|
// Simulate download
|
|
let client = reqwest::Client::builder()
|
|
.timeout(Duration::from_secs(300))
|
|
.build()
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
let response = client
|
|
.get(&download_url)
|
|
.send()
|
|
.await
|
|
.map_err(|e| format!("Download failed: {}", e))?;
|
|
|
|
let bytes = response
|
|
.bytes()
|
|
.await
|
|
.map_err(|e| format!("Failed to read bytes: {}", e))?;
|
|
|
|
info!("Downloaded {} bytes", bytes.len());
|
|
|
|
// Save to temp location
|
|
let temp_dir = std::env::temp_dir();
|
|
let installer_path = temp_dir.join("frenocorp-updater-installer");
|
|
|
|
std::fs::write(&installer_path, &bytes)
|
|
.map_err(|e| format!("Failed to write installer: {}", e))?;
|
|
|
|
Ok(installer_path.to_string_lossy().to_string())
|
|
}
|
|
|
|
pub async fn install_update(app_handle: AppHandle, installer_path: String) -> Result<(), String> {
|
|
info!("Installing update from: {}", installer_path);
|
|
|
|
// Platform-specific installation
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
// For macOS, we'd use Sparkle or similar
|
|
info!("macOS installation would happen here");
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
// For Windows, use WiX or InnoSetup
|
|
info!("Windows installation would happen here");
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
// For Linux, use AppImage or deb/rpm
|
|
info!("Linux installation would happen here");
|
|
}
|
|
|
|
// Emit update installed event
|
|
if let Err(e) = app_handle.emit("update-installed", &installer_path) {
|
|
warn!("Failed to emit installed event: {}", e);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn schedule_periodic_check(app_handle: AppHandle, interval_secs: u64) {
|
|
info!("Scheduling periodic update check every {} seconds", interval_secs);
|
|
|
|
let interval = Duration::from_secs(interval_secs);
|
|
|
|
tokio::spawn(async move {
|
|
let mut interval_tick = tokio::time::interval(interval);
|
|
|
|
loop {
|
|
interval_tick.tick().await;
|
|
|
|
info!("Running scheduled update check");
|
|
|
|
match check_for_updates(app_handle.clone()).await {
|
|
Ok(update_info) => {
|
|
if update_info.is_update_available {
|
|
info!("Scheduled check found update: {}", update_info.latest_version);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
warn!("Scheduled update check failed: {}", e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|