Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 53 additions & 10 deletions frontend/src/routes/downloads.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,56 @@ import { MarketingHeader } from "@/components/MarketingHeader";
import { Monitor, Terminal, Globe, Smartphone } from "lucide-react";
import { Apple } from "@/components/icons/Apple";
import { Android } from "@/components/icons/Android";
import { useState, useEffect } from "react";
import { getLatestDownloadInfo } from "@/utils/githubRelease";
import packageJson from "../../package.json";

// Get version from package.json
const APP_VERSION = packageJson.version;
const CURRENT_VERSION = `v${APP_VERSION}`;
const BASE_DOWNLOAD_URL = `https://github.com/OpenSecretCloud/Maple/releases/download/${CURRENT_VERSION}`;
interface DownloadUrls {
macOS: string;
linuxAppImage: string;
linuxDeb: string;
linuxRpm: string;
}

// Fallback to package.json version if GitHub API fails
const FALLBACK_VERSION = packageJson.version;
const FALLBACK_TAG = `v${FALLBACK_VERSION}`;
const FALLBACK_BASE_URL = `https://github.com/OpenSecretCloud/Maple/releases/download/${FALLBACK_TAG}`;
const FALLBACK_URLS: DownloadUrls = {
macOS: `${FALLBACK_BASE_URL}/Maple_${FALLBACK_VERSION}_universal.dmg`,
linuxAppImage: `${FALLBACK_BASE_URL}/Maple_${FALLBACK_VERSION}_amd64.AppImage`,
linuxDeb: `${FALLBACK_BASE_URL}/Maple_${FALLBACK_VERSION}_amd64.deb`,
linuxRpm: `${FALLBACK_BASE_URL}/Maple-${FALLBACK_VERSION}-1.x86_64.rpm`,
};

function DownloadPage() {
const [downloadUrls, setDownloadUrls] = useState<DownloadUrls>(FALLBACK_URLS);
const [currentVersion, setCurrentVersion] = useState<string>(FALLBACK_VERSION);
const [releaseUrl, setReleaseUrl] = useState<string>(
`https://github.com/OpenSecretCloud/Maple/releases/tag/${FALLBACK_TAG}`
);
const [isLoading, setIsLoading] = useState<boolean>(true);

useEffect(() => {
async function loadLatestRelease() {
try {
const downloadInfo = await getLatestDownloadInfo();
if (downloadInfo) {
setDownloadUrls(downloadInfo.downloadUrls);
setCurrentVersion(downloadInfo.version);
setReleaseUrl(downloadInfo.releaseUrl);
}
} catch (error) {
console.error("Failed to load latest release info:", error);
// Keep fallback values
} finally {
setIsLoading(false);
}
}

loadLatestRelease();
}, []);
Comment thread
AnthonyRonning marked this conversation as resolved.

return (
<>
<TopNav />
Expand Down Expand Up @@ -51,7 +93,7 @@ function DownloadPage() {
</p>
<div className="flex flex-col gap-3">
<a
href={`${BASE_DOWNLOAD_URL}/Maple_${APP_VERSION}_universal.dmg`}
href={downloadUrls.macOS}
Comment thread
AnthonyRonning marked this conversation as resolved.
className="py-3 rounded-lg text-center font-medium transition-all duration-300
dark:bg-white/90 dark:text-black dark:hover:bg-[hsl(var(--purple))]/80 dark:hover:text-[hsl(var(--foreground))] dark:active:bg-white/80
bg-background text-foreground hover:bg-[hsl(var(--purple))] hover:text-[hsl(var(--foreground))] active:bg-background/80
Expand All @@ -76,7 +118,7 @@ function DownloadPage() {
</p>
<div className="flex flex-col gap-3">
<a
href={`${BASE_DOWNLOAD_URL}/Maple_${APP_VERSION}_amd64.AppImage`}
href={downloadUrls.linuxAppImage}
className="py-3 rounded-lg text-center font-medium transition-all duration-300
dark:bg-white/90 dark:text-black dark:hover:bg-[hsl(var(--purple))]/80 dark:hover:text-[hsl(var(--foreground))] dark:active:bg-white/80
bg-background text-foreground hover:bg-[hsl(var(--purple))] hover:text-[hsl(var(--foreground))] active:bg-background/80
Expand All @@ -87,7 +129,7 @@ function DownloadPage() {
</a>
<div className="grid grid-cols-2 gap-2">
<a
href={`${BASE_DOWNLOAD_URL}/Maple_${APP_VERSION}_amd64.deb`}
href={downloadUrls.linuxDeb}
className="py-2 rounded-lg text-center text-sm font-medium transition-all duration-300
dark:bg-white/90 dark:text-black dark:hover:bg-[hsl(var(--purple))]/80 dark:hover:text-[hsl(var(--foreground))] dark:active:bg-white/80
bg-background text-foreground hover:bg-[hsl(var(--purple))] hover:text-[hsl(var(--foreground))] active:bg-background/80
Expand All @@ -96,7 +138,7 @@ function DownloadPage() {
.deb
</a>
<a
href={`${BASE_DOWNLOAD_URL}/Maple-${APP_VERSION}-1.x86_64.rpm`}
href={downloadUrls.linuxRpm}
className="py-2 rounded-lg text-center text-sm font-medium transition-all duration-300
dark:bg-white/90 dark:text-black dark:hover:bg-[hsl(var(--purple))]/80 dark:hover:text-[hsl(var(--foreground))] dark:active:bg-white/80
bg-background text-foreground hover:bg-[hsl(var(--purple))] hover:text-[hsl(var(--foreground))] active:bg-background/80
Expand All @@ -118,9 +160,10 @@ function DownloadPage() {
for full functionality.
</p>
<p className="text-sm">
Current version: <span className="font-mono text-foreground">{APP_VERSION}</span> •{" "}
Current version: <span className="font-mono text-foreground">{currentVersion}</span>
{isLoading && <span className="text-[hsl(var(--marketing-text-muted))]"> (loading...)</span>} •{" "}
<a
href={`https://github.com/OpenSecretCloud/Maple/releases/tag/${CURRENT_VERSION}`}
href={releaseUrl}
className="text-[hsl(var(--purple))] hover:underline"
target="_blank"
rel="noopener noreferrer"
Expand Down
79 changes: 79 additions & 0 deletions frontend/src/utils/githubRelease.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Utility for fetching the latest GitHub release information
*/

interface GitHubRelease {
tag_name: string;
name: string;
published_at: string;
html_url: string;
}

interface DownloadInfo {
version: string;
tagName: string;
downloadUrls: {
macOS: string;
linuxAppImage: string;
linuxDeb: string;
linuxRpm: string;
};
releaseUrl: string;
}

/**
* Fetches the latest release from GitHub
*/
export async function fetchLatestRelease(): Promise<GitHubRelease | null> {
try {
const response = await fetch(
"https://api.github.com/repos/OpenSecretCloud/Maple/releases/latest"
);
Comment thread
AnthonyRonning marked this conversation as resolved.

if (!response.ok) {
console.error("Failed to fetch latest release:", response.status);
return null;
}

const data = await response.json();
if (!data?.tag_name || !data?.name || !data?.published_at || !data?.html_url) {
console.error("Invalid release data format from GitHub API");
return null;
}
const release: GitHubRelease = data;
return release;
} catch (error) {
console.error("Error fetching latest release:", error);
return null;
Comment thread
AnthonyRonning marked this conversation as resolved.
}
}

/**
* Gets download information for the latest release
*/
export async function getLatestDownloadInfo(): Promise<DownloadInfo | null> {
const release = await fetchLatestRelease();

if (!release) {
return null;
}

// Extract version number from tag (remove 'v' prefix if present)
const version = release.tag_name.startsWith("v")
? release.tag_name.slice(1)
: release.tag_name;

const baseDownloadUrl = `https://github.com/OpenSecretCloud/Maple/releases/download/${release.tag_name}`;

return {
version,
tagName: release.tag_name,
downloadUrls: {
macOS: `${baseDownloadUrl}/Maple_${version}_universal.dmg`,
linuxAppImage: `${baseDownloadUrl}/Maple_${version}_amd64.AppImage`,
linuxDeb: `${baseDownloadUrl}/Maple_${version}_amd64.deb`,
linuxRpm: `${baseDownloadUrl}/Maple-${version}-1.x86_64.rpm`,
},
releaseUrl: release.html_url,
};
}
Loading