fix(website): hydration issues due to rendering on the server of client-side logic

This commit is contained in:
Elian Doran 2025-09-28 00:58:15 +03:00
parent bd2eb6fdbb
commit 5b673e753b
No known key found for this signature in database
4 changed files with 22 additions and 13 deletions

View File

@ -1,16 +1,18 @@
import { getRecommendedDownload } from "../download-helper.js"; import { getRecommendedDownload, RecommendedDownload } from "../download-helper.js";
import "./DownloadButton.css"; import "./DownloadButton.css";
import Button from "./Button.js"; import Button from "./Button.js";
import downloadIcon from "../assets/boxicons/bx-arrow-in-down-square-half.svg?raw"; import downloadIcon from "../assets/boxicons/bx-arrow-in-down-square-half.svg?raw";
import packageJson from "../../../../package.json" with { type: "json" }; import packageJson from "../../../../package.json" with { type: "json" };
import { useEffect, useState } from "preact/hooks";
interface DownloadButtonProps { interface DownloadButtonProps {
big?: boolean; big?: boolean;
} }
const recommendedDownload = getRecommendedDownload();
export default function DownloadButton({ big }: DownloadButtonProps) { export default function DownloadButton({ big }: DownloadButtonProps) {
const [ recommendedDownload, setRecommendedDownload ] = useState<RecommendedDownload | null>();
useEffect(() => setRecommendedDownload(getRecommendedDownload()), []);
return (recommendedDownload && return (recommendedDownload &&
<Button <Button
className={`download-button desktop-only ${big ? "big" : ""}`} className={`download-button desktop-only ${big ? "big" : ""}`}

View File

@ -22,6 +22,13 @@ export interface DownloadMatrixEntry {
quickStartCode?: string; quickStartCode?: string;
} }
export interface RecommendedDownload {
architecture: Architecture;
platform: Platform;
url: string;
name: string;
}
type DownloadMatrix = Record<App, { [ P in Platform ]?: DownloadMatrixEntry }>; type DownloadMatrix = Record<App, { [ P in Platform ]?: DownloadMatrixEntry }>;
// Keep compatibility info inline with https://github.com/electron/electron/blob/main/README.md#platform-support. // Keep compatibility info inline with https://github.com/electron/electron/blob/main/README.md#platform-support.
@ -205,7 +212,7 @@ export function getPlatform(): Platform | null {
} }
} }
export function getRecommendedDownload() { export function getRecommendedDownload(): RecommendedDownload | null {
if (typeof window === "undefined") return null; if (typeof window === "undefined") return null;
const architecture = getArchitecture(); const architecture = getArchitecture();

View File

@ -11,11 +11,11 @@ export function usePageTitle(title: string) {
} }
export function useColorScheme() { export function useColorScheme() {
if (typeof window === "undefined") return; const defaultValue = (typeof window !== "undefined" && (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches));
const [ prefersDark, setPrefersDark ] = useState(defaultValue);
const [ prefersDark, setPrefersDark ] = useState((window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches));
useEffect(() => { useEffect(() => {
if (typeof window === "undefined") return;
const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)"); const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
const listener = () => setPrefersDark((window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)); const listener = () => setPrefersDark((window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches));

View File

@ -30,7 +30,7 @@ import tableIcon from "../../assets/boxicons/bx-table.svg?raw";
import boardIcon from "../../assets/boxicons/bx-columns-3.svg?raw"; import boardIcon from "../../assets/boxicons/bx-columns-3.svg?raw";
import geomapIcon from "../../assets/boxicons/bx-map.svg?raw"; import geomapIcon from "../../assets/boxicons/bx-map.svg?raw";
import { getPlatform } from '../../download-helper.js'; import { getPlatform } from '../../download-helper.js';
import { useState } from 'preact/hooks'; import { useEffect, useState } from 'preact/hooks';
export function Home() { export function Home() {
usePageTitle(""); usePageTitle("");
@ -51,22 +51,22 @@ export function Home() {
function HeroSection() { function HeroSection() {
const platform = getPlatform(); const platform = getPlatform();
let screenshotUrl: string | null = null;
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
const [ screenshotUrl, setScreenshotUrl ] = useState<string>();
if (colorScheme) { useEffect(() => {
switch (platform) { switch (platform) {
case "macos": case "macos":
screenshotUrl = `/screenshot_desktop_mac_${colorScheme}.webp`; setScreenshotUrl(`/screenshot_desktop_mac_${colorScheme}.webp`);
break; break;
case "linux": case "linux":
break; break;
case "windows": case "windows":
default: default:
screenshotUrl = `/screenshot_desktop_win_${colorScheme}.webp`; setScreenshotUrl(`/screenshot_desktop_win_${colorScheme}.webp`);
break; break;
} }
} }, [ colorScheme ]);
return ( return (
<Section className="hero-section"> <Section className="hero-section">