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
14 changes: 14 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ export default function RootLayout({
suppressHydrationWarning
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<head>
<link
rel="preconnect"
href="https://basemaps.cartocdn.com"
crossOrigin="anonymous"
/>
<link
rel="preconnect"
href="https://tiles.basemaps.cartocdn.com"
crossOrigin="anonymous"
/>
<link rel="dns-prefetch" href="https://basemaps.cartocdn.com" />
<link rel="dns-prefetch" href="https://tiles.basemaps.cartocdn.com" />
</head>
<body className="flex min-h-full flex-col">
<ThemeProvider
attribute="class"
Expand Down
55 changes: 30 additions & 25 deletions src/components/address-page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
"use client";

import { useEffect, useState } from "react";
import dynamic from "next/dynamic";
import { useEffect, useState, type ComponentType } from "react";
import type { City } from "@/lib/types";
import { useAddressState } from "@/hooks/use-address-state";
import { useMediaQuery } from "@/hooks/use-media-query";
import { AddressForm } from "@/components/address-form";

const TaiwanMap = dynamic(
() => import("@/components/taiwan-map").then((m) => m.TaiwanMap),
{
ssr: false,
loading: () => (
<div className="border-border/50 bg-muted/30 h-full w-full animate-pulse rounded-xl border" />
),
},
);
// Module-level: start downloading the map chunk during hydration (before any
// useEffect fires). Only on desktop to avoid wasting mobile bandwidth.
// Also prime the HTTP cache for counties data in parallel.
const isDesktopAtLoad =
typeof window !== "undefined" &&
window.matchMedia("(min-width: 1024px)").matches;

const mapModulePromise = isDesktopAtLoad
? import("@/components/taiwan-map")
: null;

if (isDesktopAtLoad) {
void fetch("/data/map/counties-10t.json");
}

const MapPlaceholder = () => (
<div className="border-border/50 bg-muted/30 h-full w-full animate-pulse rounded-xl border" />
Expand All @@ -28,25 +32,26 @@ interface AddressPageProps {
export function AddressPage({ cities }: AddressPageProps) {
const addressState = useAddressState();
const isDesktop = useMediaQuery("(min-width: 1024px)");
const [mapReady, setMapReady] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [TaiwanMap, setTaiwanMap] = useState<ComponentType<any> | null>(null);

useEffect(() => {
if (!isDesktop) return;

if (typeof requestIdleCallback === "function") {
const id = requestIdleCallback(() => {
setMapReady(true);
});
return () => {
cancelIdleCallback(id);
};
}
let cancelled = false;

// If the module-level preload started, reuse that promise;
// otherwise start a fresh download (e.g. viewport was resized to desktop).
const promise = mapModulePromise ?? import("@/components/taiwan-map");

void promise.then((mod) => {
if (!cancelled) {
setTaiwanMap(() => mod.TaiwanMap);
}
});

const id = setTimeout(() => {
setMapReady(true);
}, 0);
return () => {
clearTimeout(id);
cancelled = true;
};
}, [isDesktop]);

Expand All @@ -56,7 +61,7 @@ export function AddressPage({ cities }: AddressPageProps) {
{isDesktop && (
<div>
<div className="sticky top-16 h-[calc(100vh-8rem)]">
{mapReady ? (
{TaiwanMap ? (
<TaiwanMap
city={addressState.state.city}
district={addressState.state.district}
Expand Down
Loading