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
3 changes: 1 addition & 2 deletions app/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import supportedByEN from "../public/locales/en/supportedBy.json";
import supportedByES from "../public/locales/es/supportedBy.json";
Expand All @@ -21,8 +20,8 @@ import footerES from "../public/locales/es/footer.json";

i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
lng: "en",
resources: {
en: {
navbar: navbarEN,
Expand Down
10 changes: 10 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client"

import { useEffect } from "react"
import i18n from "i18next"
import { Navbar } from "@/components/navbar"
import { HeroSection } from "@/components/hero-section"
import { FeaturesSection } from "@/components/features-section"
Expand All @@ -11,7 +13,15 @@ import { ContactSection } from "@/components/contact-section"
import { Footer } from "@/components/footer"
import "./i18n"

const SUPPORTED_LANGS = ["en", "es"]

export default function Home() {
useEffect(() => {
const detected = navigator.language ?? "en"
const match = SUPPORTED_LANGS.find((l) => detected.startsWith(l)) ?? "en"
if (match !== i18n.language) i18n.changeLanguage(match)
}, [])

return (
<main className="min-h-screen">
<Navbar />
Expand Down
73 changes: 55 additions & 18 deletions components/download-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,53 @@
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Download, Package } from "lucide-react"
import { useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
import { findAsset, formatBytes, getLatestRelease } from "@/lib/github"

const downloads = [
const SOURCEFORGE_URL = "https://sourceforge.net/projects/dashai/files/latest/download"

type DownloadEntry = {
id: "mac_intel" | "mac_arm" | "windows"
icon: typeof Package
key: string
defaultPlatform: string
version: string
size: string
defaultFormat: string
link: string
}

const FALLBACK_DOWNLOADS: DownloadEntry[] = [
{
id: "mac_intel",
icon: Package,
key: "macIntel",
defaultPlatform: "macOS Intel processors",
version: "v0.1.15",
size: "487 MB",
defaultFormat: "binary",
link: "https://dashai.nyc3.cdn.digitaloceanspaces.com/executables/DashAI-launcher-cpu-x86_64",
version: "",
size: "",
defaultFormat: ".dmg",
link: SOURCEFORGE_URL,
},
{
id: "mac_arm",
icon: Package,
key: "macArm",
defaultPlatform: "macOS ARM processors",
version: "v0.1.15",
size: "381 MB",
defaultFormat: "binary",
link: "https://dashai.nyc3.cdn.digitaloceanspaces.com/executables/DashAI-launcher-cpu-arm64",
version: "",
size: "",
defaultFormat: ".dmg",
link: SOURCEFORGE_URL,
},
{
id: "windows",
icon: Package,
key: "windows",
defaultPlatform: "Windows",
version: "v0.1.15",
size: "434 MB",
version: "",
size: "",
defaultFormat: ".exe",
link: "https://dashai.nyc3.cdn.digitaloceanspaces.com/executables/DashAI-launcher-cpu.exe",
link: SOURCEFORGE_URL,
},
]

Expand All @@ -53,6 +68,26 @@ async function trackClick(buttonId: string) {

export function DownloadSection() {
const { t } = useTranslation()
const [downloads, setDownloads] = useState<DownloadEntry[]>(FALLBACK_DOWNLOADS)

useEffect(() => {
getLatestRelease().then((release) => {
if (!release) return
setDownloads(
FALLBACK_DOWNLOADS.map((entry) => {
const asset = findAsset(release.assets, entry.id)
if (!asset) return entry
return {
...entry,
version: release.tag_name,
size: formatBytes(asset.size),
link: asset.browser_download_url,
}
})
)
})
}, [])

return (
<section id="download" className="py-24 px-4 bg-secondary/30">
<div className="container mx-auto">
Expand All @@ -66,7 +101,7 @@ export function DownloadSection() {
<p className="text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed">
{t("download:description", {
defaultValue:
"This early version lets you explore DashAIs main features. We appreciate your feedback to help us improve before the official release.",
"This early version lets you explore DashAI's main features. We appreciate your feedback to help us improve before the official release.",
})}
</p>
</div>
Expand All @@ -92,17 +127,19 @@ export function DownloadSection() {
</div>
<h3 className="text-xl font-semibold mb-2">{platform}</h3>
<div className="space-y-1 mb-4">
<p className="text-sm text-muted-foreground">
{download.version} • {download.size}
</p>
{download.version && (
<p className="text-sm text-muted-foreground">
{download.version} • {download.size}
</p>
)}
<p className="text-xs text-muted-foreground font-mono">{format}</p>
</div>
<Button
className="w-full bg-primary hover:bg-primary/90 text-primary-foreground cursor-pointer"
size="sm"
asChild
>
<a href={download.link} download onClick={() => trackClick(download.id)}>
<a href={download.link} onClick={() => trackClick(download.id)}>
<Download className="mr-2 h-4 w-4" />
{t("download:button", { defaultValue: "Download" })}
</a>
Expand Down Expand Up @@ -140,4 +177,4 @@ export function DownloadSection() {
</div>
</section>
)
}
}
51 changes: 51 additions & 0 deletions lib/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export interface ReleaseAsset {
name: string
browser_download_url: string
size: number
}

export interface LatestRelease {
tag_name: string
assets: ReleaseAsset[]
}

export async function getLatestRelease(): Promise<LatestRelease | null> {
try {
const res = await fetch(
"https://api.github.com/repos/DashAISoftware/DashAI/releases/latest",
{ headers: { Accept: "application/vnd.github+json" } }
)
if (!res.ok) return null
return res.json()
} catch {
return null
}
}

export function formatBytes(bytes: number): string {
const mb = bytes / (1024 * 1024)
return `${Math.round(mb)} MB`
}

export function findAsset(
assets: ReleaseAsset[],
platform: "windows" | "mac_intel" | "mac_arm"
): ReleaseAsset | undefined {
return assets.find(({ name }) => {
const n = name.toLowerCase()
switch (platform) {
case "windows":
return n.includes("windows")
case "mac_intel":
return (
(n.includes("x64") || n.includes("intel")) &&
(n.includes("osx") || n.includes("mac") || n.includes("darwin"))
)
case "mac_arm":
return (
(n.includes("arm") || n.includes("aarch")) &&
(n.includes("osx") || n.includes("mac") || n.includes("darwin"))
)
}
})
}
Loading