mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-05-30 11:59:29 +02:00
fix: treat 'twilight' release as alpha and rolling
This commit is contained in:
+5
-4
@@ -7,15 +7,16 @@
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "biome check src/ && tsc --noEmit && next lint",
|
||||
"lint": "pnpm lint:js && pnpm lint:rust",
|
||||
"lint:js": "biome check src/ && tsc --noEmit && next lint",
|
||||
"lint:rust": "cd src-tauri && cargo clippy --all-targets --all-features -- -D warnings -D clippy::all && cargo fmt --all",
|
||||
"tauri": "tauri",
|
||||
"shadcn:add": "pnpm dlx shadcn@latest add",
|
||||
"prepare": "husky",
|
||||
"format:js": "biome check src/ --fix",
|
||||
"format:rust": "cd src-tauri && cargo clippy --fix --allow-dirty --all-targets --all-features -- -D warnings -D clippy::all && cargo fmt --all",
|
||||
"format:biome": "biome check src/ --fix",
|
||||
"format": "pnpm format:js && pnpm format:rust"
|
||||
"format:js": "biome check src/ --fix",
|
||||
"format": "pnpm format:js && pnpm format:rust",
|
||||
"cargo": "cd src-tauri && cargo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
|
||||
Generated
+470
-623
File diff suppressed because it is too large
Load Diff
+188
-67
@@ -36,12 +36,16 @@ impl VersionComponent {
|
||||
let version = version.trim();
|
||||
|
||||
// Handle special case for Zen Browser twilight releases
|
||||
if version.to_lowercase().contains("twilight") {
|
||||
if version.to_lowercase() == "twilight" {
|
||||
// Pure twilight release without base version
|
||||
return VersionComponent {
|
||||
major: u32::MAX,
|
||||
minor: u32::MAX,
|
||||
patch: u32::MAX,
|
||||
pre_release: None,
|
||||
major: 999, // High major version to indicate it's a rolling release
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
pre_release: Some(PreRelease {
|
||||
kind: PreReleaseKind::Alpha,
|
||||
number: Some(999), // High number to indicate it's a rolling release
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -140,6 +144,38 @@ impl Ord for VersionComponent {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// Check for twilight versions
|
||||
let self_is_twilight = self
|
||||
.pre_release
|
||||
.as_ref()
|
||||
.map(|pr| pr.kind == PreReleaseKind::Alpha && pr.number == Some(999))
|
||||
.unwrap_or(false);
|
||||
let other_is_twilight = other
|
||||
.pre_release
|
||||
.as_ref()
|
||||
.map(|pr| pr.kind == PreReleaseKind::Alpha && pr.number == Some(999))
|
||||
.unwrap_or(false);
|
||||
|
||||
// If one is twilight and the other isn't, twilight always has priority
|
||||
if self_is_twilight && !other_is_twilight {
|
||||
return Ordering::Greater; // twilight > non-twilight
|
||||
}
|
||||
if !self_is_twilight && other_is_twilight {
|
||||
return Ordering::Less; // non-twilight < twilight
|
||||
}
|
||||
|
||||
// Both are twilight or both are not twilight - use normal comparison
|
||||
match (self_is_twilight, other_is_twilight) {
|
||||
(true, true) => {
|
||||
// Both are twilight, compare by base version
|
||||
return (self.major, self.minor, self.patch).cmp(&(other.major, other.minor, other.patch));
|
||||
}
|
||||
(false, false) => {
|
||||
// Neither is twilight, continue with normal comparison
|
||||
}
|
||||
_ => unreachable!(), // Already handled above
|
||||
}
|
||||
|
||||
// Compare major.minor.patch first
|
||||
match (self.major, self.minor, self.patch).cmp(&(other.major, other.minor, other.patch)) {
|
||||
Ordering::Equal => {
|
||||
@@ -193,6 +229,12 @@ pub fn is_alpha_version(version: &str) -> bool {
|
||||
version_comp.pre_release.is_some()
|
||||
}
|
||||
|
||||
// Browser-specific alpha version detection for Zen Browser
|
||||
pub fn is_zen_alpha_version(version: &str) -> bool {
|
||||
// For Zen Browser, only "twilight" is considered alpha/pre-release
|
||||
version.to_lowercase() == "twilight"
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct FirefoxRelease {
|
||||
pub build_number: u32,
|
||||
@@ -273,7 +315,7 @@ impl ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cache_dir() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
fn get_cache_dir() -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let base_dirs = BaseDirs::new().ok_or("Failed to get base directories")?;
|
||||
let app_name = if cfg!(debug_assertions) {
|
||||
"DonutBrowserDev"
|
||||
@@ -343,7 +385,7 @@ impl ApiClient {
|
||||
&self,
|
||||
browser: &str,
|
||||
versions: &[String],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let cache_dir = Self::get_cache_dir()?;
|
||||
let cache_file = cache_dir.join(format!("{browser}_versions.json"));
|
||||
|
||||
@@ -378,7 +420,7 @@ impl ApiClient {
|
||||
&self,
|
||||
browser: &str,
|
||||
releases: &[GithubRelease],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let cache_dir = Self::get_cache_dir()?;
|
||||
let cache_file = cache_dir.join(format!("{browser}_github.json"));
|
||||
|
||||
@@ -569,13 +611,6 @@ impl ApiClient {
|
||||
Ok(releases)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn fetch_mullvad_releases(
|
||||
&self,
|
||||
) -> Result<Vec<GithubRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
self.fetch_mullvad_releases_with_caching(false).await
|
||||
}
|
||||
|
||||
pub async fn fetch_mullvad_releases_with_caching(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
@@ -622,13 +657,6 @@ impl ApiClient {
|
||||
Ok(releases)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn fetch_zen_releases(
|
||||
&self,
|
||||
) -> Result<Vec<GithubRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
self.fetch_zen_releases_with_caching(false).await
|
||||
}
|
||||
|
||||
pub async fn fetch_zen_releases_with_caching(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
@@ -654,7 +682,25 @@ impl ApiClient {
|
||||
.json::<Vec<GithubRelease>>()
|
||||
.await?;
|
||||
|
||||
// Sort releases using the new version sorting system (twilight releases will be at top)
|
||||
// Check for twilight updates and mark alpha releases
|
||||
for release in &mut releases {
|
||||
// Use browser-specific alpha detection for Zen Browser
|
||||
release.is_alpha = is_zen_alpha_version(&release.tag_name) || release.prerelease;
|
||||
|
||||
// Check for twilight update if this is a twilight release
|
||||
if release.tag_name.to_lowercase() == "twilight" {
|
||||
if let Ok(has_update) = self.check_twilight_update(release).await {
|
||||
if has_update {
|
||||
println!(
|
||||
"Detected update for Zen twilight release: {}",
|
||||
release.tag_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort releases using the new version sorting system
|
||||
sort_github_releases(&mut releases);
|
||||
|
||||
// Cache the results (unless bypassing cache)
|
||||
@@ -667,13 +713,6 @@ impl ApiClient {
|
||||
Ok(releases)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn fetch_brave_releases(
|
||||
&self,
|
||||
) -> Result<Vec<GithubRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
self.fetch_brave_releases_with_caching(false).await
|
||||
}
|
||||
|
||||
pub async fn fetch_brave_releases_with_caching(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
@@ -935,6 +974,64 @@ impl ApiClient {
|
||||
// Check if there's a macOS DMG file in this version directory
|
||||
Ok(html.contains("tor-browser-macos-") && html.contains(".dmg"))
|
||||
}
|
||||
|
||||
/// Check if a Zen twilight release has been updated by comparing file size
|
||||
pub async fn check_twilight_update(
|
||||
&self,
|
||||
release: &GithubRelease,
|
||||
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if release.tag_name.to_lowercase() != "twilight" {
|
||||
return Ok(false); // Not a twilight release
|
||||
}
|
||||
|
||||
// Find the macOS universal DMG asset
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == "zen.macos-universal.dmg")
|
||||
.ok_or("No macOS universal asset found for twilight release")?;
|
||||
|
||||
// Check if we have cached file size information
|
||||
let cache_dir = Self::get_cache_dir()?;
|
||||
let twilight_cache_file = cache_dir.join("zen_twilight_info.json");
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct TwilightInfo {
|
||||
file_size: u64,
|
||||
last_updated: u64,
|
||||
download_url: String,
|
||||
}
|
||||
|
||||
let current_info = TwilightInfo {
|
||||
file_size: asset.size,
|
||||
last_updated: Self::get_current_timestamp(),
|
||||
download_url: asset.browser_download_url.clone(),
|
||||
};
|
||||
|
||||
if !twilight_cache_file.exists() {
|
||||
// No cache exists, save current info and return true (new)
|
||||
let content = serde_json::to_string_pretty(¤t_info)?;
|
||||
fs::write(&twilight_cache_file, content)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let cached_content = fs::read_to_string(&twilight_cache_file)?;
|
||||
let cached_info: TwilightInfo = serde_json::from_str(&cached_content)?;
|
||||
|
||||
// Check if file size has changed
|
||||
if cached_info.file_size != current_info.file_size {
|
||||
// File size changed, update cache and return true
|
||||
let content = serde_json::to_string_pretty(¤t_info)?;
|
||||
fs::write(&twilight_cache_file, content)?;
|
||||
println!(
|
||||
"Zen twilight release updated: file size changed from {} to {}",
|
||||
cached_info.file_size, current_info.file_size
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false) // No update detected
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -989,10 +1086,14 @@ mod tests {
|
||||
assert_eq!(pre.number, Some(5));
|
||||
|
||||
// Test twilight version (Zen Browser)
|
||||
let v4 = VersionComponent::parse("1.0.0-twilight");
|
||||
assert_eq!(v4.major, u32::MAX);
|
||||
assert_eq!(v4.minor, u32::MAX);
|
||||
assert_eq!(v4.patch, u32::MAX);
|
||||
let v4 = VersionComponent::parse("twilight");
|
||||
assert_eq!(v4.major, 999);
|
||||
assert_eq!(v4.minor, 0);
|
||||
assert_eq!(v4.patch, 0);
|
||||
assert!(v4.pre_release.is_some());
|
||||
let pre = v4.pre_release.unwrap();
|
||||
assert_eq!(pre.kind, PreReleaseKind::Alpha);
|
||||
assert_eq!(pre.number, Some(999));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1022,10 +1123,15 @@ mod tests {
|
||||
let v10 = VersionComponent::parse("137.0b5");
|
||||
assert!(v10 > v9); // b5 > b4
|
||||
|
||||
// Test twilight version (should be highest)
|
||||
let v11 = VersionComponent::parse("1.0.0-twilight");
|
||||
let v12 = VersionComponent::parse("999.999.999");
|
||||
assert!(v11 > v12);
|
||||
// Test twilight version (should have highest priority)
|
||||
let v11 = VersionComponent::parse("twilight");
|
||||
let v12 = VersionComponent::parse("1.0.0");
|
||||
assert!(v11 > v12); // twilight > stable due to high major version
|
||||
|
||||
// Test twilight vs other pre-releases
|
||||
let v13 = VersionComponent::parse("twilight");
|
||||
let v14 = VersionComponent::parse("1.0.0a1");
|
||||
assert!(v13 > v14); // twilight > a1 due to high major version
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1037,14 +1143,14 @@ mod tests {
|
||||
"137.0b4".to_string(),
|
||||
"137.0b5".to_string(),
|
||||
"137.0".to_string(),
|
||||
"1.0.0-twilight".to_string(),
|
||||
"twilight".to_string(),
|
||||
"2.0.0a1".to_string(),
|
||||
];
|
||||
|
||||
sort_versions(&mut versions);
|
||||
|
||||
// Expected order: twilight, 137.0, 137.0b5, 137.0b4, 2.0.0a1, 1.12.6b, 1.10.0, 1.9.9b
|
||||
assert_eq!(versions[0], "1.0.0-twilight");
|
||||
// Expected order with twilight priority: twilight first due to high major version (999), then normal semantic versioning
|
||||
assert_eq!(versions[0], "twilight");
|
||||
assert_eq!(versions[1], "137.0");
|
||||
assert_eq!(versions[2], "137.0b5");
|
||||
assert_eq!(versions[3], "137.0b4");
|
||||
@@ -1054,6 +1160,31 @@ mod tests {
|
||||
assert_eq!(versions[7], "1.9.9b");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_versions_comprehensive() {
|
||||
let mut versions = vec![
|
||||
"1.0.0".to_string(),
|
||||
"1.0.1".to_string(),
|
||||
"1.1.0".to_string(),
|
||||
"2.0.0a1".to_string(),
|
||||
"2.0.0b1".to_string(),
|
||||
"2.0.0rc1".to_string(),
|
||||
"2.0.0".to_string(),
|
||||
"10.0.0".to_string(),
|
||||
"twilight".to_string(),
|
||||
];
|
||||
|
||||
sort_versions(&mut versions);
|
||||
|
||||
// Expected order with twilight priority: twilight first due to high major version (999), then normal semantic versioning
|
||||
assert_eq!(versions[0], "twilight");
|
||||
assert_eq!(versions[1], "10.0.0");
|
||||
assert_eq!(versions[2], "2.0.0");
|
||||
assert_eq!(versions[3], "2.0.0rc1");
|
||||
assert_eq!(versions[4], "2.0.0b1");
|
||||
assert_eq!(versions[5], "2.0.0a1");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_firefox_api() {
|
||||
let server = setup_mock_server().await;
|
||||
@@ -1167,7 +1298,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "mullvad-browser-macos-14.5a6.dmg",
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.dmg"
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.dmg",
|
||||
"size": 100000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1200,14 +1332,15 @@ mod tests {
|
||||
|
||||
let mock_response = r#"[
|
||||
{
|
||||
"tag_name": "1.0.0-twilight",
|
||||
"tag_name": "twilight",
|
||||
"name": "Zen Browser Twilight",
|
||||
"prerelease": false,
|
||||
"published_at": "2024-01-15T10:00:00Z",
|
||||
"assets": [
|
||||
{
|
||||
"name": "zen.macos-universal.dmg",
|
||||
"browser_download_url": "https://example.com/zen-twilight.dmg"
|
||||
"browser_download_url": "https://example.com/zen-twilight.dmg",
|
||||
"size": 120000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1229,7 +1362,7 @@ mod tests {
|
||||
assert!(result.is_ok());
|
||||
let releases = result.unwrap();
|
||||
assert!(!releases.is_empty());
|
||||
assert_eq!(releases[0].tag_name, "1.0.0-twilight");
|
||||
assert_eq!(releases[0].tag_name, "twilight");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1246,7 +1379,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "brave-v1.81.9-universal.dmg",
|
||||
"browser_download_url": "https://example.com/brave-1.81.9-universal.dmg"
|
||||
"browser_download_url": "https://example.com/brave-1.81.9-universal.dmg",
|
||||
"size": 200000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1472,28 +1606,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_versions_comprehensive() {
|
||||
let mut versions = vec![
|
||||
"1.0.0".to_string(),
|
||||
"1.0.1".to_string(),
|
||||
"1.1.0".to_string(),
|
||||
"2.0.0a1".to_string(),
|
||||
"2.0.0b1".to_string(),
|
||||
"2.0.0rc1".to_string(),
|
||||
"2.0.0".to_string(),
|
||||
"10.0.0".to_string(),
|
||||
"1.0.0-twilight".to_string(),
|
||||
];
|
||||
fn test_is_zen_alpha_version() {
|
||||
// Only "twilight" should be considered alpha for Zen Browser
|
||||
assert!(is_zen_alpha_version("twilight"));
|
||||
assert!(is_zen_alpha_version("TWILIGHT")); // Case insensitive
|
||||
|
||||
sort_versions(&mut versions);
|
||||
|
||||
// Twilight should be first, then normal semantic versioning
|
||||
assert_eq!(versions[0], "1.0.0-twilight");
|
||||
assert_eq!(versions[1], "10.0.0");
|
||||
assert_eq!(versions[2], "2.0.0");
|
||||
assert_eq!(versions[3], "2.0.0rc1");
|
||||
assert_eq!(versions[4], "2.0.0b1");
|
||||
assert_eq!(versions[5], "2.0.0a1");
|
||||
// Versions with "b" should NOT be considered alpha for Zen Browser
|
||||
assert!(!is_zen_alpha_version("1.12.8b"));
|
||||
assert!(!is_zen_alpha_version("1.0.0b1"));
|
||||
assert!(!is_zen_alpha_version("2.0.0"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -303,6 +303,8 @@ pub struct GithubRelease {
|
||||
pub struct GithubAsset {
|
||||
pub name: String,
|
||||
pub browser_download_url: String,
|
||||
#[serde(default)]
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -695,7 +695,7 @@ mod tests {
|
||||
async fn setup_zen_mocks(server: &MockServer) {
|
||||
let mock_response = r#"[
|
||||
{
|
||||
"tag_name": "1.0.0-twilight",
|
||||
"tag_name": "twilight",
|
||||
"name": "Zen Browser Twilight",
|
||||
"prerelease": false,
|
||||
"published_at": "2024-01-15T10:00:00Z",
|
||||
|
||||
@@ -144,6 +144,10 @@ impl Downloader {
|
||||
.resolve_download_url(browser_type.clone(), version, download_info)
|
||||
.await?;
|
||||
|
||||
// Check if this is a twilight release for special handling
|
||||
let is_twilight =
|
||||
browser_type == BrowserType::Zen && version.to_lowercase().contains("twilight");
|
||||
|
||||
// Emit initial progress
|
||||
let progress = DownloadProgress {
|
||||
browser: browser_type.as_str().to_string(),
|
||||
@@ -153,7 +157,11 @@ impl Downloader {
|
||||
percentage: 0.0,
|
||||
speed_bytes_per_sec: 0.0,
|
||||
eta_seconds: None,
|
||||
stage: "downloading".to_string(),
|
||||
stage: if is_twilight {
|
||||
"downloading (twilight rolling release)".to_string()
|
||||
} else {
|
||||
"downloading".to_string()
|
||||
},
|
||||
};
|
||||
|
||||
let _ = app_handle.emit("download-progress", &progress);
|
||||
@@ -205,6 +213,12 @@ impl Downloader {
|
||||
None
|
||||
};
|
||||
|
||||
let stage_description = if is_twilight {
|
||||
"downloading (twilight rolling release)".to_string()
|
||||
} else {
|
||||
"downloading".to_string()
|
||||
};
|
||||
|
||||
let progress = DownloadProgress {
|
||||
browser: browser_type.as_str().to_string(),
|
||||
version: version.to_string(),
|
||||
@@ -213,7 +227,7 @@ impl Downloader {
|
||||
percentage,
|
||||
speed_bytes_per_sec: speed,
|
||||
eta_seconds: eta,
|
||||
stage: "downloading".to_string(),
|
||||
stage: stage_description,
|
||||
};
|
||||
|
||||
let _ = app_handle.emit("download-progress", &progress);
|
||||
@@ -267,7 +281,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "brave-v1.81.9-universal.dmg",
|
||||
"browser_download_url": "https://example.com/brave-1.81.9-universal.dmg"
|
||||
"browser_download_url": "https://example.com/brave-1.81.9-universal.dmg",
|
||||
"size": 200000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -314,7 +329,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "zen.macos-universal.dmg",
|
||||
"browser_download_url": "https://example.com/zen-1.11b-universal.dmg"
|
||||
"browser_download_url": "https://example.com/zen-1.11b-universal.dmg",
|
||||
"size": 120000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -361,7 +377,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "mullvad-browser-macos-14.5a6.dmg",
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.dmg"
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.dmg",
|
||||
"size": 100000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -471,7 +488,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "brave-v1.81.8-universal.dmg",
|
||||
"browser_download_url": "https://example.com/brave-1.81.8-universal.dmg"
|
||||
"browser_download_url": "https://example.com/brave-1.81.8-universal.dmg",
|
||||
"size": 200000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -520,7 +538,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "zen.linux-universal.tar.bz2",
|
||||
"browser_download_url": "https://example.com/zen-1.11b-linux.tar.bz2"
|
||||
"browser_download_url": "https://example.com/zen-1.11b-linux.tar.bz2",
|
||||
"size": 150000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -663,7 +682,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "mullvad-browser-linux-14.5a6.tar.xz",
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.tar.xz"
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.tar.xz",
|
||||
"size": 80000000
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -712,7 +732,8 @@ mod tests {
|
||||
"assets": [
|
||||
{
|
||||
"name": "brave-v1.81.9-universal.dmg",
|
||||
"browser_download_url": "https://example.com/brave-1.81.9-universal.dmg"
|
||||
"browser_download_url": "https://example.com/brave-1.81.9-universal.dmg",
|
||||
"size": 200000000
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ pub struct DownloadedBrowserInfo {
|
||||
pub file_path: PathBuf,
|
||||
pub verified: bool,
|
||||
pub actual_version: Option<String>, // For browsers like Chromium where we track the actual version
|
||||
pub file_size: Option<u64>, // For tracking file size changes (useful for rolling releases)
|
||||
#[serde(default)] // Add default value (false) for backwards compatibility
|
||||
pub is_rolling_release: bool, // True for Zen's twilight releases and other rolling releases
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
@@ -98,6 +101,7 @@ impl DownloadedBrowsersRegistry {
|
||||
}
|
||||
|
||||
pub fn mark_download_started(&mut self, browser: &str, version: &str, file_path: PathBuf) {
|
||||
let is_rolling = Self::is_rolling_release(browser, version);
|
||||
let info = DownloadedBrowserInfo {
|
||||
browser: browser.to_string(),
|
||||
version: version.to_string(),
|
||||
@@ -108,6 +112,8 @@ impl DownloadedBrowsersRegistry {
|
||||
file_path,
|
||||
verified: false,
|
||||
actual_version: None,
|
||||
file_size: None,
|
||||
is_rolling_release: is_rolling,
|
||||
};
|
||||
self.add_browser(info);
|
||||
}
|
||||
@@ -131,6 +137,11 @@ impl DownloadedBrowsersRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_rolling_release(browser: &str, version: &str) -> bool {
|
||||
// Check if this is a rolling release like twilight
|
||||
browser == "zen" && version.to_lowercase() == "twilight"
|
||||
}
|
||||
|
||||
pub fn cleanup_failed_download(
|
||||
&mut self,
|
||||
browser: &str,
|
||||
@@ -186,6 +197,8 @@ mod tests {
|
||||
file_path: PathBuf::from("/test/path"),
|
||||
verified: true,
|
||||
actual_version: None,
|
||||
file_size: None,
|
||||
is_rolling_release: false,
|
||||
};
|
||||
|
||||
registry.add_browser(info.clone());
|
||||
@@ -206,6 +219,8 @@ mod tests {
|
||||
file_path: PathBuf::from("/test/path1"),
|
||||
verified: true,
|
||||
actual_version: None,
|
||||
file_size: None,
|
||||
is_rolling_release: false,
|
||||
};
|
||||
|
||||
let info2 = DownloadedBrowserInfo {
|
||||
@@ -215,6 +230,8 @@ mod tests {
|
||||
file_path: PathBuf::from("/test/path2"),
|
||||
verified: false, // Not verified, should not be included
|
||||
actual_version: None,
|
||||
file_size: None,
|
||||
is_rolling_release: false,
|
||||
};
|
||||
|
||||
let info3 = DownloadedBrowserInfo {
|
||||
@@ -224,6 +241,8 @@ mod tests {
|
||||
file_path: PathBuf::from("/test/path3"),
|
||||
verified: true,
|
||||
actual_version: None,
|
||||
file_size: None,
|
||||
is_rolling_release: false,
|
||||
};
|
||||
|
||||
registry.add_browser(info1);
|
||||
@@ -266,6 +285,8 @@ mod tests {
|
||||
file_path: PathBuf::from("/test/path"),
|
||||
verified: true,
|
||||
actual_version: None,
|
||||
file_size: None,
|
||||
is_rolling_release: false,
|
||||
};
|
||||
|
||||
registry.add_browser(info);
|
||||
@@ -275,4 +296,17 @@ mod tests {
|
||||
assert!(removed.is_some());
|
||||
assert!(!registry.is_browser_downloaded("firefox", "139.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_twilight_rolling_release() {
|
||||
let mut registry = DownloadedBrowsersRegistry::new();
|
||||
|
||||
// Mark twilight download started
|
||||
registry.mark_download_started("zen", "twilight", PathBuf::from("/test/zen-twilight"));
|
||||
|
||||
// Check that it's marked as rolling release
|
||||
let zen_versions = ®istry.browsers["zen"];
|
||||
let twilight_info = &zen_versions["twilight"];
|
||||
assert!(twilight_info.is_rolling_release);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,12 @@ interface ErrorToastProps extends BaseToastProps {
|
||||
|
||||
interface DownloadToastProps extends BaseToastProps {
|
||||
type: "download";
|
||||
stage?: "downloading" | "extracting" | "verifying" | "completed";
|
||||
stage?:
|
||||
| "downloading"
|
||||
| "extracting"
|
||||
| "verifying"
|
||||
| "completed"
|
||||
| "downloading (twilight rolling release)";
|
||||
progress?: {
|
||||
percentage: number;
|
||||
speed?: string;
|
||||
@@ -93,13 +98,20 @@ interface FetchingToastProps extends BaseToastProps {
|
||||
browserName?: string;
|
||||
}
|
||||
|
||||
interface TwilightUpdateToastProps extends BaseToastProps {
|
||||
type: "twilight-update";
|
||||
browserName?: string;
|
||||
hasUpdate?: boolean;
|
||||
}
|
||||
|
||||
type ToastProps =
|
||||
| LoadingToastProps
|
||||
| SuccessToastProps
|
||||
| ErrorToastProps
|
||||
| DownloadToastProps
|
||||
| VersionUpdateToastProps
|
||||
| FetchingToastProps;
|
||||
| FetchingToastProps
|
||||
| TwilightUpdateToastProps;
|
||||
|
||||
function getToastIcon(type: ToastProps["type"], stage?: string) {
|
||||
switch (type) {
|
||||
@@ -122,6 +134,10 @@ function getToastIcon(type: ToastProps["type"], stage?: string) {
|
||||
return (
|
||||
<LuRefreshCw className="h-4 w-4 text-blue-500 animate-spin flex-shrink-0" />
|
||||
);
|
||||
case "twilight-update":
|
||||
return (
|
||||
<LuRefreshCw className="h-4 w-4 text-purple-500 animate-spin flex-shrink-0" />
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-2 border-blue-500 border-t-transparent flex-shrink-0" />
|
||||
@@ -186,6 +202,22 @@ export function UnifiedToast(props: ToastProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Twilight update progress */}
|
||||
{type === "twilight-update" && (
|
||||
<div className="mt-2">
|
||||
<p className="text-xs text-gray-600 dark:text-gray-300">
|
||||
{"hasUpdate" in props && props.hasUpdate
|
||||
? "New twilight build available for download"
|
||||
: "Checking for twilight updates..."}
|
||||
</p>
|
||||
{props.browserName && (
|
||||
<p className="text-xs text-purple-600 dark:text-purple-400 mt-1">
|
||||
{props.browserName} • Rolling Release
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description */}
|
||||
{description && (
|
||||
<p className="mt-1 text-xs text-gray-600 dark:text-gray-300 leading-tight">
|
||||
@@ -206,6 +238,11 @@ export function UnifiedToast(props: ToastProps) {
|
||||
Verifying installation...
|
||||
</p>
|
||||
)}
|
||||
{stage === "downloading (twilight rolling release)" && (
|
||||
<p className="mt-1 text-xs text-purple-600 dark:text-purple-400">
|
||||
Downloading rolling release build...
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ interface UpdateNotification {
|
||||
affected_profiles: string[];
|
||||
is_stable_update: boolean;
|
||||
timestamp: number;
|
||||
is_rolling_release: boolean;
|
||||
}
|
||||
|
||||
export function useUpdateNotifications() {
|
||||
|
||||
Reference in New Issue
Block a user