feat: 添加 PWA 支持 — 可安装到主屏幕、离线缓存、刘海屏适配
This commit is contained in:
@@ -2,6 +2,7 @@ import type { Metadata, Viewport } from "next";
|
||||
import { Geist } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import GlobalUserBadge from "@/components/GlobalUserBadge";
|
||||
import ServiceWorkerRegistrar from "@/components/ServiceWorkerRegistrar";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -19,6 +20,8 @@ export const viewport: Viewport = {
|
||||
initialScale: 1,
|
||||
maximumScale: 1,
|
||||
userScalable: false,
|
||||
viewportFit: "cover",
|
||||
themeColor: "#10b981",
|
||||
};
|
||||
|
||||
const themeScript = `(function(){try{var t=localStorage.getItem("nowhatever-theme")||"system";var r=t;if(t==="system")r=window.matchMedia("(prefers-color-scheme:light)").matches?"light":"dark";document.documentElement.setAttribute("data-theme",r)}catch(e){}})()`;
|
||||
@@ -32,8 +35,10 @@ export default function RootLayout({
|
||||
<html lang="zh-CN" suppressHydrationWarning>
|
||||
<head>
|
||||
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
</head>
|
||||
<body className={`${geistSans.variable} font-sans antialiased`}>
|
||||
<ServiceWorkerRegistrar />
|
||||
<GlobalUserBadge />
|
||||
{children}
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: "NoWhatever — 别说随便",
|
||||
short_name: "NoWhatever",
|
||||
description: "像 Tinder 一样滑卡片,和朋友一起决定去哪吃!",
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: "#030712",
|
||||
theme_color: "#10b981",
|
||||
orientation: "portrait",
|
||||
icons: [
|
||||
{
|
||||
src: "/icon-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "/icon-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "/icon-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
purpose: "maskable",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { WifiOff } from "lucide-react";
|
||||
|
||||
export default function OfflinePage() {
|
||||
return (
|
||||
<div className="min-h-dvh flex flex-col items-center justify-center px-6 bg-background text-foreground">
|
||||
<div className="w-16 h-16 rounded-2xl bg-surface flex items-center justify-center mb-6">
|
||||
<WifiOff className="w-8 h-8 text-muted" />
|
||||
</div>
|
||||
<h1 className="text-xl font-bold text-heading mb-2">没有网络连接</h1>
|
||||
<p className="text-secondary text-center mb-8">
|
||||
请检查你的网络设置,然后重试
|
||||
</p>
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="px-6 py-3 bg-accent text-white rounded-xl font-medium active:scale-95 transition-transform"
|
||||
>
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function ServiceWorkerRegistrar() {
|
||||
useEffect(() => {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("/sw.js").catch(() => {});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user