Revert "refactor: Improved platform detection, UI enhancements, and overall code quality"
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
import { addDownload } from "@/lib/db";
|
||||||
import { incrementDownloadCount } from "@/lib/db";
|
import { useState, useEffect } from "react";
|
||||||
import { useState, useEffect, useCallback } from "react";
|
|
||||||
import styled, { keyframes } from "styled-components";
|
import styled, { keyframes } from "styled-components";
|
||||||
import { ny } from "@/lib/utils";
|
import { ny } from "@/lib/utils";
|
||||||
import { Checkbox } from "./ui/checkbox";
|
import { Checkbox } from "./ui/checkbox";
|
||||||
@@ -9,36 +8,50 @@ import { ChevronLeft } from "lucide-react";
|
|||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import Particles from "./ui/particles";
|
import Particles from "./ui/particles";
|
||||||
import confetti from "canvas-confetti";
|
import confetti from "canvas-confetti";
|
||||||
import type {
|
|
||||||
Architecture,
|
|
||||||
LinuxDownloadType,
|
|
||||||
Platform,
|
|
||||||
WindowsDownloadType,
|
|
||||||
} from "@/lib/releases";
|
|
||||||
import { releases, releaseTree } from "@/lib/releases";
|
import { releases, releaseTree } from "@/lib/releases";
|
||||||
import { InfoCircledIcon } from "@radix-ui/react-icons";
|
import { InfoCircledIcon } from "@radix-ui/react-icons";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import SparklesText from "./ui/sparkles-text";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
|
|
||||||
const BASE_URL =
|
const BASE_URL =
|
||||||
"https://github.com/zen-browser/desktop/releases/latest/download";
|
"https://github.com/zen-browser/desktop/releases/latest/download";
|
||||||
|
|
||||||
|
import SparklesText from "./ui/sparkles-text";
|
||||||
const field_enter = keyframes`
|
const field_enter = keyframes`
|
||||||
0% { opacity: 0; transform: scale(0.9); filter: blur(10px); }
|
0% {
|
||||||
1% { max-height: 100%; }
|
opacity: 0;
|
||||||
100% { opacity: 1; transform: scale(1); filter: blur(0); }
|
transform: scale(0.9);
|
||||||
|
filter: blur(10px);
|
||||||
|
}
|
||||||
|
1% {
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
filter: blur(0);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const field_exit = keyframes`
|
const field_exit = keyframes`
|
||||||
from { display: flex; opacity: 1; transform: scale(1); filter: blur(0); }
|
from {
|
||||||
99% { opacity: 0; transform: scale(0.9); filter: blur(10px); }
|
display: flex;
|
||||||
100% { display: none; }
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
filter: blur(0);
|
||||||
|
}
|
||||||
|
99% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
filter: blur(10px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FormField = styled.div<{ enter: boolean; out: boolean }>`
|
const FormField = styled.div<{ enter: boolean; out: boolean }>`
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin-top: 3rem;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
animation: 0.2s ease-in-out forwards
|
animation: 0.2s ease-in-out forwards
|
||||||
@@ -46,59 +59,53 @@ const FormField = styled.div<{ enter: boolean; out: boolean }>`
|
|||||||
animation-delay: ${({ enter }) => (enter ? "0.4s" : "0s")};
|
animation-delay: ${({ enter }) => (enter ? "0.4s" : "0s")};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FieldTitle = ({
|
const FieldTitle = styled.div`
|
||||||
children,
|
font-size: 1.35rem;
|
||||||
className,
|
font-weight: 500;
|
||||||
}: React.PropsWithChildren & React.HTMLAttributes<HTMLHeadElement>) => (
|
`;
|
||||||
<h2 className={ny("text-xl font-medium", className)}>{children}</h2>
|
|
||||||
);
|
|
||||||
|
|
||||||
const FieldDescription = ({
|
const FieldDescription = styled.div`
|
||||||
children,
|
font-size: 1rem;
|
||||||
className,
|
color: #666;
|
||||||
}: React.PropsWithChildren & React.HTMLAttributes<HTMLDivElement>) => (
|
margin-bottom: 1rem;
|
||||||
<p className={ny("text-base text-[#666] mb-4", className)}>{children}</p>
|
`;
|
||||||
);
|
|
||||||
|
|
||||||
const platforms = {
|
|
||||||
Windows: { color: "blue", icon: "windows8" },
|
|
||||||
Linux: { color: "yellow", icon: "linux" },
|
|
||||||
MacOS: { color: "purple", icon: "apple" },
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function DownloadPage() {
|
export default function DownloadPage() {
|
||||||
const router = useRouter();
|
const [platform, setPlatform] = useState<string | null>(null);
|
||||||
|
const [architecture, setArchitecture] = useState<string | null>(null);
|
||||||
const [platform, setPlatform] = useState<Platform | null>(null);
|
const [windowsDownloadType, setWindowsDownloadType] = useState<string | null>(
|
||||||
const [selectedPlatform, setSelectedPlatform] = useState<Platform | null>(
|
null
|
||||||
|
);
|
||||||
|
const [linuxDownloadType, setLinuxDownloadType] = useState<string | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
const [selectedArchitecture, setSelectedArchitecture] =
|
const [selectedPlatform, setSelectedPlatform] = useState("");
|
||||||
useState<Architecture>("specific");
|
const [selectedArchitecture, setSelectedArchitecture] = useState("specific");
|
||||||
const [selectedWindowsDownloadType, setSelectedWindowsDownloadType] =
|
const [selectedWindowsDownloadType, setSelectedWindowsDownloadType] =
|
||||||
useState<WindowsDownloadType>("installer");
|
useState("installer");
|
||||||
const [selectedLinuxDownloadType, setSelectedLinuxDownloadType] =
|
const [selectedLinuxDownloadType, setSelectedLinuxDownloadType] =
|
||||||
useState<LinuxDownloadType>("portable");
|
useState("portable");
|
||||||
|
|
||||||
const [hasDownloaded, setHasDownloaded] = useState<boolean>(false);
|
const [hasDownloaded, setHasDownloaded] = useState(false);
|
||||||
const [flowIndex, setFlowIndex] = useState<number>(0);
|
|
||||||
|
|
||||||
const detectPlatform = useCallback(() => {
|
const [flowIndex, setFlowIndex] = useState(0);
|
||||||
if (typeof window === "undefined") return;
|
useEffect(() => {
|
||||||
|
let userAgent: string = "";
|
||||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
if (typeof window !== "undefined") {
|
||||||
|
userAgent = window.navigator.userAgent;
|
||||||
if (userAgent.includes("win")) {
|
}
|
||||||
|
if (userAgent.includes("Win")) {
|
||||||
setSelectedPlatform("Windows");
|
setSelectedPlatform("Windows");
|
||||||
} else if (userAgent.includes("mac")) {
|
}
|
||||||
|
if (userAgent.includes("Mac")) {
|
||||||
setSelectedPlatform("MacOS");
|
setSelectedPlatform("MacOS");
|
||||||
} else if (userAgent.includes("linux")) {
|
}
|
||||||
|
if (userAgent.includes("Linux")) {
|
||||||
setSelectedPlatform("Linux");
|
setSelectedPlatform("Linux");
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
const throwConfetti = () => {
|
||||||
const throwConfetti = useCallback(() => {
|
|
||||||
const end = Date.now() + 3 * 1000; // 3 seconds
|
const end = Date.now() + 3 * 1000; // 3 seconds
|
||||||
const colors = ["#a786ff", "#fd8bbc", "#eca184", "#f8deb1"];
|
const colors = ["#a786ff", "#fd8bbc", "#eca184", "#f8deb1"];
|
||||||
const frame = () => {
|
const frame = () => {
|
||||||
@@ -123,75 +130,68 @@ export default function DownloadPage() {
|
|||||||
requestAnimationFrame(frame);
|
requestAnimationFrame(frame);
|
||||||
};
|
};
|
||||||
frame();
|
frame();
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
const getReleaseTarget = useCallback(():
|
const startDownload = () => {
|
||||||
| keyof typeof releases
|
let releaseTarget: string;
|
||||||
| undefined => {
|
|
||||||
if (selectedLinuxDownloadType === "flatpak") {
|
if (selectedLinuxDownloadType === "flatpak") {
|
||||||
router.push(
|
window.open(
|
||||||
"https://dl.flathub.org/repo/appstream/io.github.zen_browser.zen.flatpakref"
|
"https://dl.flathub.org/repo/appstream/io.github.zen_browser.zen.flatpakref"
|
||||||
);
|
);
|
||||||
return;
|
releaseTarget = "flatpak";
|
||||||
|
} else {
|
||||||
|
const platform = releaseTree[selectedPlatform.toLowerCase()];
|
||||||
|
let arch: string = selectedArchitecture;
|
||||||
|
if (selectedPlatform === "MacOS") {
|
||||||
|
releaseTarget = platform[arch];
|
||||||
|
} else {
|
||||||
|
releaseTarget =
|
||||||
|
platform[arch][
|
||||||
|
selectedPlatform === "Windows"
|
||||||
|
? (selectedWindowsDownloadType as string)
|
||||||
|
: (selectedLinuxDownloadType as string)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
console.log("Downloading: ");
|
||||||
|
console.log("platform: ", selectedPlatform);
|
||||||
|
console.log("compat: ", arch);
|
||||||
|
window.location.replace(`${BASE_URL}/${releases[releaseTarget]}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedPlatform) return;
|
|
||||||
|
|
||||||
const platformReleases = releaseTree[selectedPlatform.toLowerCase()];
|
|
||||||
|
|
||||||
if (selectedPlatform === "MacOS")
|
|
||||||
return platformReleases[selectedArchitecture];
|
|
||||||
|
|
||||||
return platformReleases[selectedArchitecture][
|
|
||||||
selectedPlatform === "Windows"
|
|
||||||
? selectedWindowsDownloadType
|
|
||||||
: selectedLinuxDownloadType
|
|
||||||
];
|
|
||||||
}, [
|
|
||||||
router,
|
|
||||||
selectedArchitecture,
|
|
||||||
selectedLinuxDownloadType,
|
|
||||||
selectedPlatform,
|
|
||||||
selectedWindowsDownloadType,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const startDownload = useCallback(() => {
|
|
||||||
const releaseTarget = getReleaseTarget();
|
|
||||||
|
|
||||||
if (!releaseTarget) return;
|
|
||||||
|
|
||||||
router.push(`${BASE_URL}/${releases[releaseTarget]}`);
|
|
||||||
|
|
||||||
setHasDownloaded(true);
|
setHasDownloaded(true);
|
||||||
incrementDownloadCount(releaseTarget);
|
addDownload(releaseTarget);
|
||||||
throwConfetti();
|
throwConfetti();
|
||||||
}, [getReleaseTarget, router, throwConfetti]);
|
};
|
||||||
|
|
||||||
const continueFlow = useCallback(() => {
|
const continueFlow = () => {
|
||||||
if (flowIndex === 0) setPlatform(selectedPlatform);
|
if (flowIndex === 0) setPlatform(selectedPlatform);
|
||||||
|
if (flowIndex === 1) setArchitecture(selectedArchitecture);
|
||||||
if (flowIndex === 2 || (flowIndex === 1 && platform === "MacOS")) {
|
if (flowIndex === 2 || (flowIndex === 1 && platform === "MacOS")) {
|
||||||
|
setWindowsDownloadType(selectedWindowsDownloadType);
|
||||||
|
setLinuxDownloadType(selectedLinuxDownloadType);
|
||||||
startDownload();
|
startDownload();
|
||||||
}
|
}
|
||||||
setFlowIndex(flowIndex + 1);
|
setFlowIndex(flowIndex + 1);
|
||||||
}, [flowIndex, platform, selectedPlatform, startDownload]);
|
};
|
||||||
|
|
||||||
const goBackFlow = useCallback(() => {
|
const goBackFlow = () => {
|
||||||
if (flowIndex === 1) setPlatform(null);
|
if (flowIndex === 1) {
|
||||||
else if (flowIndex === 3) {
|
setPlatform(null);
|
||||||
|
} else if (flowIndex === 2) {
|
||||||
|
setArchitecture(null);
|
||||||
|
} else if (flowIndex === 3) {
|
||||||
|
setWindowsDownloadType(null);
|
||||||
setSelectedWindowsDownloadType("installer");
|
setSelectedWindowsDownloadType("installer");
|
||||||
|
setLinuxDownloadType(null);
|
||||||
setSelectedLinuxDownloadType("portable");
|
setSelectedLinuxDownloadType("portable");
|
||||||
}
|
}
|
||||||
if (flowIndex > 0) setFlowIndex(flowIndex - 1);
|
if (flowIndex > 0) setFlowIndex(flowIndex - 1);
|
||||||
}, [flowIndex]);
|
};
|
||||||
|
|
||||||
const changeToFlatpak = useCallback(() => {
|
const changeToFlatpak = () => {
|
||||||
if (selectedArchitecture !== "specific") return;
|
if (selectedArchitecture === "specific") {
|
||||||
setSelectedLinuxDownloadType("flatpak");
|
setSelectedLinuxDownloadType("flatpak");
|
||||||
}, [selectedArchitecture]);
|
}
|
||||||
|
};
|
||||||
useEffect(() => {
|
|
||||||
detectPlatform();
|
|
||||||
}, [detectPlatform]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -244,52 +244,63 @@ export default function DownloadPage() {
|
|||||||
<Button
|
<Button
|
||||||
className="mt-5"
|
className="mt-5"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(
|
(window.location.href =
|
||||||
"https://docs.zen-browser.app/guides/install-macos"
|
"https://docs.zen-browser.app/guides/install-macos")
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Read Installation Instructions
|
Download Zen for MacOS
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)) || (
|
)) || (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-6xl font-bold flex flex-col lg:flex-row">
|
<h1 className="text-6xl font-bold flex flex-col lg:flex-row">Download <SparklesText className="mx-2" text="Zen" /></h1>
|
||||||
Download <SparklesText className="mx-2" text="Zen" />
|
|
||||||
</h1>
|
|
||||||
<p className="text-muted-foreground mt-3">
|
<p className="text-muted-foreground mt-3">
|
||||||
We're thrilled for you to experience Zen Browser. First, let us
|
We're thrilled for you to experience Zen Browser. First, let us know which device you're using. This will only take a moment, we promise.
|
||||||
know which device you're using. This will only take a moment, we
|
|
||||||
promise.
|
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="relative w-full pt-12">
|
<div className="relative w-full">
|
||||||
{/* Platform */}
|
{platform === null && (
|
||||||
{!platform && (
|
<FormField enter={platform === null} out={platform !== null}>
|
||||||
<FormField enter={!platform} out={!platform}>
|
|
||||||
<FieldTitle>Platform</FieldTitle>
|
<FieldTitle>Platform</FieldTitle>
|
||||||
<FieldDescription>
|
<FieldDescription>
|
||||||
Choose the platform you want to download Zen for.
|
Choose the platform you want to download Zen for.
|
||||||
</FieldDescription>
|
</FieldDescription>
|
||||||
{Object.entries(platforms).map(([plat, { color, icon }]) => (
|
<div
|
||||||
<div
|
onClick={() => setSelectedPlatform("Windows")}
|
||||||
key={plat}
|
className={ny(
|
||||||
onClick={() => setSelectedPlatform(plat as Platform)}
|
"select-none mb-2 px-4 py-3 flex items-center rounded-lg bg-background cursor-pointer border",
|
||||||
className={ny(
|
selectedPlatform === "Windows" ? "border-blue-400" : ""
|
||||||
"select-none mb-2 px-4 py-3 flex items-center rounded-lg bg-background cursor-pointer border",
|
)}
|
||||||
selectedPlatform === plat && `border-${color}-400`
|
>
|
||||||
)}
|
<Checkbox checked={selectedPlatform === "Windows"} />
|
||||||
>
|
<i className="devicon-windows8-original ml-3 p-2 border border-blue-400 rounded-lg"></i>
|
||||||
<Checkbox checked={selectedPlatform === plat} />
|
<div className="ml-2">Windows</div>
|
||||||
<i
|
</div>
|
||||||
className={`devicon-${icon}-plain ml-3 p-2 border border-${color}-400 rounded-lg`}
|
<div
|
||||||
></i>
|
onClick={() => setSelectedPlatform("Linux")}
|
||||||
<div className="ml-2">{plat}</div>
|
className={ny(
|
||||||
</div>
|
"select-none mb-2 px-4 py-3 flex items-center rounded-lg bg-background cursor-pointer border",
|
||||||
))}
|
selectedPlatform === "Linux" ? "border-yellow-400" : ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Checkbox checked={selectedPlatform === "Linux"} />
|
||||||
|
<i className="devicon-linux-plain ml-3 p-2 border border-yellow-400 rounded-lg"></i>
|
||||||
|
<div className="ml-2">Linux</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() => setSelectedPlatform("MacOS")}
|
||||||
|
className={ny(
|
||||||
|
"select-none mb-2 px-4 py-3 flex items-center rounded-lg bg-background cursor-pointer border",
|
||||||
|
selectedPlatform === "MacOS" ? "border-purple-400" : ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Checkbox checked={selectedPlatform === "MacOS"} />
|
||||||
|
<i className="devicon-apple-original p-2 border border-purple-400 ml-3 rounded-lg"></i>
|
||||||
|
<div className="ml-2 font-bold">MacOS</div>
|
||||||
|
</div>
|
||||||
</FormField>
|
</FormField>
|
||||||
)}
|
)}
|
||||||
{/* Architecture */}
|
{/* Architecture */}
|
||||||
@@ -311,30 +322,38 @@ export default function DownloadPage() {
|
|||||||
Choose the architecture of your device, either optimized or
|
Choose the architecture of your device, either optimized or
|
||||||
generic.
|
generic.
|
||||||
</FieldDescription>
|
</FieldDescription>
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center">
|
||||||
<div
|
<div
|
||||||
onClick={() => setSelectedArchitecture("specific")}
|
onClick={() => setSelectedArchitecture("specific")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedArchitecture === "specific" && "border-blue-400"
|
selectedArchitecture === "specific"
|
||||||
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl my-2">🚀</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold my-2">Optimized</h1>
|
🚀
|
||||||
<p className="text-muted-foreground mx-auto text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Optimized</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Blazing fast and compatible with modern devices
|
Blazing fast and compatible with modern devices
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
onClick={() => setSelectedArchitecture("generic")}
|
onClick={() => setSelectedArchitecture("generic")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
"select-none w-full h-full mb-2 ml-10 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedArchitecture === "generic" && "border-blue-400"
|
selectedArchitecture === "generic"
|
||||||
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl my-2">👴</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold my-2">Generic</h1>
|
👴
|
||||||
<p className="text-muted-foreground mx-auto text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Generic</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Slow but compatible with older devices.
|
Slow but compatible with older devices.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -350,33 +369,39 @@ export default function DownloadPage() {
|
|||||||
<FieldDescription>
|
<FieldDescription>
|
||||||
Click the button below to download Zen for MacOS.
|
Click the button below to download Zen for MacOS.
|
||||||
</FieldDescription>
|
</FieldDescription>
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center">
|
||||||
<button
|
<div
|
||||||
onClick={() => setSelectedArchitecture("specific")}
|
onClick={() => setSelectedArchitecture("specific")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedArchitecture === "specific" && "border-blue-400"
|
selectedArchitecture === "specific"
|
||||||
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl my-2">🍏</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold my-2">aarch64</h1>
|
🍏
|
||||||
<p className="text-muted-foreground mx-auto text-center text-balance text-sm">
|
</h1>
|
||||||
64-bit ARM architecture, for Apple's M Series Chips
|
<h1 className="text-2xl font-semibold my-2">aarch64</h1>
|
||||||
</p>
|
<p className="text-muted-foreground mx-auto text-center">64-bit ARM architecture, for Apple's M Series Chips</p>
|
||||||
</button>
|
</div>
|
||||||
<button
|
<div
|
||||||
onClick={() => setSelectedArchitecture("generic")}
|
onClick={() => setSelectedArchitecture("generic")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
"select-none w-full h-full mb-2 ml-10 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedArchitecture === "generic" && "border-blue-400"
|
selectedArchitecture === "generic"
|
||||||
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl font-bold my-2">x64</h1>
|
<h1 className="text-5xl font-bold my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold my-2">Intel</h1>
|
x64
|
||||||
<p className="text-muted-foreground mx-auto text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Intel</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
64-bit Intel architecture, for older Macs
|
64-bit Intel architecture, for older Macs
|
||||||
</p>
|
</p>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FormField>
|
</FormField>
|
||||||
)}
|
)}
|
||||||
@@ -391,35 +416,41 @@ export default function DownloadPage() {
|
|||||||
<FieldDescription>
|
<FieldDescription>
|
||||||
Choose the type of download you want for Zen for Windows.
|
Choose the type of download you want for Zen for Windows.
|
||||||
</FieldDescription>
|
</FieldDescription>
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center">
|
||||||
<button
|
<div
|
||||||
onClick={() => setSelectedWindowsDownloadType("installer")}
|
onClick={() => setSelectedWindowsDownloadType("installer")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedWindowsDownloadType === "installer" &&
|
selectedWindowsDownloadType === "installer"
|
||||||
"border-blue-400"
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl my-2">🚀</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold my-2">Installer</h1>
|
🚀
|
||||||
<p className="text-muted-foreground mx-auto text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Installer</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Install Zen with a setup wizard
|
Install Zen with a setup wizard
|
||||||
</p>
|
</p>
|
||||||
</button>
|
</div>
|
||||||
<button
|
<div
|
||||||
onClick={() => setSelectedWindowsDownloadType("portable")}
|
onClick={() => setSelectedWindowsDownloadType("portable")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
"select-none w-full h-full mb-2 ml-10 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedWindowsDownloadType === "portable" &&
|
selectedWindowsDownloadType === "portable"
|
||||||
"border-blue-400"
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl my-2">📦</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold my-2">Portable</h1>
|
📦
|
||||||
<p className="text-muted-foreground mx-auto text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Portable</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Download Zen as a ZIP file
|
Download Zen as a ZIP file
|
||||||
</p>
|
</p>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FormField>
|
</FormField>
|
||||||
)}
|
)}
|
||||||
@@ -434,51 +465,61 @@ export default function DownloadPage() {
|
|||||||
<FieldDescription>
|
<FieldDescription>
|
||||||
Choose the type of download you want for Zen for Linux.
|
Choose the type of download you want for Zen for Linux.
|
||||||
</FieldDescription>
|
</FieldDescription>
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center">
|
||||||
<article
|
<div
|
||||||
onClick={() => setSelectedLinuxDownloadType("appimage")}
|
onClick={() => setSelectedLinuxDownloadType("appimage")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border space-y-2",
|
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedLinuxDownloadType === "appimage" &&
|
selectedLinuxDownloadType === "appimage"
|
||||||
"border-blue-400"
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl">🚀</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold">AppImage</h1>
|
🚀
|
||||||
<p className="text-muted-foreground text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">AppImage</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Install Zen with a setup wizard
|
Install Zen with a setup wizard
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</div>
|
||||||
<article
|
<div
|
||||||
onClick={() => setSelectedLinuxDownloadType("portable")}
|
onClick={() => setSelectedLinuxDownloadType("portable")}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border space-y-2",
|
"select-none w-full h-full mb-2 ml-5 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedLinuxDownloadType === "portable" &&
|
selectedLinuxDownloadType === "portable"
|
||||||
"border-blue-400"
|
? "border-blue-400"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl">📦</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold">Portable</h1>
|
📦
|
||||||
<p className="text-muted-foreground text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Portable</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Download Zen as a ZIP file
|
Download Zen as a ZIP file
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</div>
|
||||||
<article
|
<div
|
||||||
onClick={changeToFlatpak}
|
onClick={() => changeToFlatpak()}
|
||||||
className={ny(
|
className={ny(
|
||||||
"select-none w-full h-full mb-2 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border space-y-2",
|
"select-none w-full h-full mb-2 ml-5 p-5 flex flex-col items-center rounded-lg bg-background cursor-pointer border",
|
||||||
selectedLinuxDownloadType === "flatpak" &&
|
selectedLinuxDownloadType === "flatpak"
|
||||||
"border-blue-400",
|
? "border-blue-400"
|
||||||
selectedArchitecture === "generic" &&
|
: "",
|
||||||
"opacity-50 cursor-not-allowed"
|
selectedArchitecture === "generic"
|
||||||
|
? "opacity-50 cursor-not-allowed"
|
||||||
|
: ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h1 className="text-5xl">🧑💻</h1>
|
<h1 className="text-5xl my-2 opacity-40 dark:opacity-20">
|
||||||
<h1 className="text-xl font-semibold">Flatpak</h1>
|
🧑💻
|
||||||
<p className="text-muted-foreground text-center text-balance text-sm">
|
</h1>
|
||||||
|
<h1 className="text-2xl font-semibold my-2">Flatpak</h1>
|
||||||
|
<p className="text-muted-foreground mx-auto text-center">
|
||||||
Install Zen from the Flatpak repository.
|
Install Zen from the Flatpak repository.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FormField>
|
</FormField>
|
||||||
)}
|
)}
|
||||||
@@ -487,13 +528,19 @@ export default function DownloadPage() {
|
|||||||
<div className="mt-5 flex items-center justify-between">
|
<div className="mt-5 flex items-center justify-between">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={goBackFlow}
|
onClick={() => goBackFlow()}
|
||||||
className={ny("opacity-70", !platform && "invisible")}
|
className={ny(
|
||||||
|
"opacity-70",
|
||||||
|
platform === null ? "invisible" : ""
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ChevronLeft className="size-4" />
|
<ChevronLeft className="size-4" />
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={continueFlow} disabled={!selectedPlatform}>
|
<Button
|
||||||
|
onClick={() => continueFlow()}
|
||||||
|
disabled={selectedPlatform === null}
|
||||||
|
>
|
||||||
{(flowIndex === 1 && platform === "MacOS") || flowIndex === 2
|
{(flowIndex === 1 && platform === "MacOS") || flowIndex === 2
|
||||||
? "Download 🥳"
|
? "Download 🥳"
|
||||||
: "Continue"}
|
: "Continue"}
|
||||||
@@ -502,7 +549,7 @@ export default function DownloadPage() {
|
|||||||
)}
|
)}
|
||||||
{(platform === "Linux" || platform === "Windows") &&
|
{(platform === "Linux" || platform === "Windows") &&
|
||||||
flowIndex === 1 && (
|
flowIndex === 1 && (
|
||||||
<div className="mt-5 flex items-center justify-center">
|
<div className="mt-5 flex items-center">
|
||||||
<InfoCircledIcon className="size-4 mr-2" />
|
<InfoCircledIcon className="size-4 mr-2" />
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Confused about which build to choose?{" "}
|
Confused about which build to choose?{" "}
|
||||||
|
|||||||
@@ -1,49 +1,31 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { createClient } from "@supabase/supabase-js";
|
import { createClient } from '@supabase/supabase-js'
|
||||||
import { releases } from "./releases";
|
|
||||||
|
|
||||||
const supabaseUrl = "https://dmthyedfjzcysoekmyns.supabase.co";
|
const supabaseUrl = 'https://dmthyedfjzcysoekmyns.supabase.co'
|
||||||
const supabaseKey = process.env.SUPABASE_KEY as string;
|
const supabaseKey = process.env.SUPABASE_KEY as string;
|
||||||
const supabase = createClient(supabaseUrl, supabaseKey);
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
||||||
|
|
||||||
const DOWNLOADS_TABLE = "downloads";
|
export async function addDownload(platform: string) {
|
||||||
const PLATFORM_COLUMN = "platform";
|
// Check if the download count for the platform exists
|
||||||
const COUNT_COLUMN = "count";
|
|
||||||
|
|
||||||
export async function incrementDownloadCount(platform: keyof typeof releases) {
|
|
||||||
try {
|
|
||||||
//? Check if the download count for the platform exists
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from(DOWNLOADS_TABLE)
|
.from('downloads')
|
||||||
.select(COUNT_COLUMN)
|
.select('count')
|
||||||
.eq(PLATFORM_COLUMN, platform);
|
.eq('platform', platform)
|
||||||
|
// If it doesn't exist, create it
|
||||||
if (error) throw new Error("Error fetching download count");
|
console.log(data)
|
||||||
|
if (data?.length === 0 || data === null) {
|
||||||
if (!data || data.length === 0) {
|
const {data, error} = await supabase
|
||||||
//? If it doesn't exist, create it
|
.from('downloads')
|
||||||
const { data: insertData, error: insertError } = await supabase
|
.insert([{ platform, count: 1 }]);
|
||||||
.from(DOWNLOADS_TABLE)
|
if (error) {
|
||||||
.insert([{ platform, count: 1 }]);
|
console.error(error)
|
||||||
|
}
|
||||||
if (insertError) throw new Error("Error inserting download count");
|
|
||||||
|
|
||||||
return insertData;
|
|
||||||
} else {
|
} else {
|
||||||
//? If it exists, increment the count
|
// If it exists, increment the count
|
||||||
const newCount = data![0][COUNT_COLUMN] + 1;
|
await supabase
|
||||||
const { data: updateData, error: updateError } = await supabase
|
.from('downloads')
|
||||||
.from(DOWNLOADS_TABLE)
|
.update({ count: data![0].count + 1 })
|
||||||
.update({ count: newCount })
|
.eq('platform', platform)
|
||||||
.eq(PLATFORM_COLUMN, platform);
|
|
||||||
|
|
||||||
if (updateError) throw new Error("Error updating download count");
|
|
||||||
|
|
||||||
return updateData;
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error("Unexpected error in addDownload:", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,46 @@
|
|||||||
export const releases = {
|
export const releases: any = {
|
||||||
WindowsInstaller: "zen.installer.exe",
|
WindowsInstaller: "zen.installer.exe",
|
||||||
WindowsInstallerGeneric: "zen.installer-generic.exe",
|
WindowsInstallerGeneric: "zen.installer-generic.exe",
|
||||||
|
|
||||||
|
WindowsZip: "zen.win-specific.zip",
|
||||||
|
WindowsZipGeneric: "zen.win-generic.zip",
|
||||||
|
|
||||||
WindowsZip: "zen.win-specific.zip",
|
MacOS: "zen.macos-aarch64.dmg",
|
||||||
WindowsZipGeneric: "zen.win-generic.zip",
|
MacOSIntel: "zen.macos-x64.dmg",
|
||||||
|
|
||||||
MacOS: "zen.macos-aarch64.dmg",
|
Linux: "zen.linux-specific.tar.bz2",
|
||||||
MacOSIntel: "zen.macos-x64.dmg",
|
LinuxGeneric: "zen.linux-generic.tar.bz2",
|
||||||
|
|
||||||
Linux: "zen.linux-specific.tar.bz2",
|
LinuxAppImage: "zen-specific.AppImage",
|
||||||
LinuxGeneric: "zen.linux-generic.tar.bz2",
|
LinuxAppImageGeneric: "zen-generic.AppImage",
|
||||||
|
};
|
||||||
LinuxAppImage: "zen-specific.AppImage",
|
|
||||||
LinuxAppImageGeneric: "zen-generic.AppImage",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// platform
|
// platform
|
||||||
// -> arch
|
// -> arch
|
||||||
// -> file
|
// -> file
|
||||||
export const releaseTree: any = {
|
export const releaseTree: any = {
|
||||||
windows: {
|
windows: {
|
||||||
specific: {
|
specific: {
|
||||||
installer: "WindowsInstaller",
|
installer: "WindowsInstaller",
|
||||||
portable: "WindowsZip",
|
portable: "WindowsZip",
|
||||||
|
},
|
||||||
|
generic: {
|
||||||
|
installer: "WindowsInstallerGeneric",
|
||||||
|
portable: "WindowsZipGeneric",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
generic: {
|
macos: {
|
||||||
installer: "WindowsInstallerGeneric",
|
generic: "MacOSIntel",
|
||||||
portable: "WindowsZipGeneric",
|
specific: "MacOS",
|
||||||
},
|
},
|
||||||
},
|
linux: {
|
||||||
macos: {
|
specific: {
|
||||||
generic: "MacOSIntel",
|
portable: "Linux",
|
||||||
specific: "MacOS",
|
appimage: "LinuxAppImage",
|
||||||
},
|
},
|
||||||
linux: {
|
generic: {
|
||||||
specific: {
|
portable: "LinuxGeneric",
|
||||||
portable: "Linux",
|
appimage: "LinuxAppImageGeneric",
|
||||||
appimage: "LinuxAppImage",
|
},
|
||||||
},
|
},
|
||||||
generic: {
|
|
||||||
portable: "LinuxGeneric",
|
|
||||||
appimage: "LinuxAppImageGeneric",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Platform = "Windows" | "MacOS" | "Linux";
|
|
||||||
type Architecture = "specific" | "generic";
|
|
||||||
|
|
||||||
type WindowsDownloadType = "installer" | "portable";
|
|
||||||
type LinuxDownloadType = "portable" | "appimage" | "flatpak";
|
|
||||||
|
|
||||||
export type { Platform, Architecture, WindowsDownloadType, LinuxDownloadType };
|
|
||||||
|
|||||||
Reference in New Issue
Block a user