feat: support pwa

This commit is contained in:
Ou 2024-10-20 01:30:10 +08:00
parent 55b6710e49
commit 6d07702faf
14 changed files with 2332 additions and 31 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ dist/
.wrangler
.env
.env.*
dev-dist

View File

@ -1,27 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script>
function safeParseString(str) {
try {
return JSON.parse(str)
} catch {
return ""
}
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Elegant reading of real-time and hottest news" />
<meta name="theme-color" content="#F14D42" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
<script>
function safeParseString(str) {
try {
return JSON.parse(str)
} catch {
return ""
}
const theme = safeParseString(localStorage.getItem("color-scheme")) || "auto"
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches
if (theme === "auto" ? isDark : theme === "dark") {
document.documentElement.classList.add("dark")
}
</script>
<title>NewsNow</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/app.tsx"></script>
</body>
}
const theme = safeParseString(localStorage.getItem("color-scheme")) || "auto"
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches
if (theme === "auto" ? isDark : theme === "dark") {
document.documentElement.classList.add("dark")
}
</script>
<title>NewsNow</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/app.tsx"></script>
</body>
</html>

View File

@ -3,6 +3,7 @@
"type": "module",
"version": "0.0.1",
"private": true,
"commit-id": "",
"packageManager": "pnpm@9.12.1",
"author": {
"url": "https://github.com/ourongxing/",
@ -88,9 +89,11 @@
"unocss": "^0.63.4",
"unplugin-auto-import": "^0.18.3",
"vite": "^5.4.8",
"vite-plugin-pwa": "^0.20.5",
"vite-plugin-with-nitro": "0.0.2",
"vite-tsconfig-paths": "^5.0.1",
"vitest": "^2.1.2",
"workbox-window": "^7.1.0",
"wrangler": "^3.80.3"
},
"pnpm": {

2214
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
public/pwa-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
public/pwa-512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

2
public/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Allow: /

View File

@ -5,7 +5,7 @@ export function Toast() {
return (
<Toaster
toastOptions={{
duration: 10000000,
duration: 5000,
unstyled: true,
classNames: {
toast: clsx(
@ -17,10 +17,10 @@ export function Toast() {
"data-[type=warning]:(bg-yellow)",
),
icon: "text-white ml-1 dark:text-dark-600 text-op-80!",
content: "bg-base bg-op-70! p-2 rounded-lg color-base w-full backdrop-blur-md",
content: "bg-base bg-op-70! p-2 rounded-lg color-base w-full backdrop-blur-xl",
title: "font-normal text-base",
description: "color-base text-op-80! text-sm",
actionButton: "bg-base bg-op-70! rounded-lg py-2 w-4em backdrop-blur-md hover:(bg-base bg-op-60!)",
actionButton: "bg-base bg-op-70! rounded-lg py-2 w-4em backdrop-blur-lg hover:(bg-base bg-op-60!)",
closeButton: "bg-base bg-op-50! border-0 hover:(bg-base bg-op-70!)",
},
}}

View File

@ -62,7 +62,7 @@ export function Header() {
</p>
</span>
</Link>
<a target="_blank" href={`${Homepage}/release/tag/${Version}`} className="btn text-sm ml-1 font-mono">
<a target="_blank" href={`${Homepage}/releases/tag/v${Version}`} className="btn text-sm ml-1 font-mono">
{`v${Version}`}
</a>
</span>

28
src/hooks/usePWA.ts Normal file
View File

@ -0,0 +1,28 @@
import { useEffect } from "react"
import { toast } from "sonner"
import { useRegisterSW } from "virtual:pwa-register/react"
export function usePWA() {
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW()
useEffect(() => {
if (offlineReady) {
toast.info("PWA 准备好了")
} else if (needRefresh) {
toast("有更新,点击更新", {
action: {
label: "更新",
onClick: () => updateServiceWorker(true),
},
onDismiss: () => {
setOfflineReady(false)
setNeedRefresh(false)
},
})
}
}, [offlineReady, needRefresh, updateServiceWorker, setOfflineReady, setNeedRefresh])
}

View File

@ -11,6 +11,7 @@ import { GlobalOverlayScrollbar } from "~/components/common/overlay-scrollbar"
import { useSync } from "~/hooks/useSync"
import { Footer } from "~/components/footer"
import { Toast } from "~/components/common/toast"
import { usePWA } from "~/hooks/usePWA"
export const Route = createRootRouteWithContext<{
queryClient: QueryClient
@ -37,6 +38,7 @@ function NotFoundComponent() {
function RootComponent() {
useOnReload()
useSync()
usePWA()
return (
<>
<GlobalOverlayScrollbar className={clsx([

3
src/vite-env.d.ts vendored
View File

@ -1,3 +1,6 @@
/// <reference types="vite/client" />
/// <reference types="vite-plugin-pwa/react" />
/// <reference types="vite-plugin-pwa/info" />
/// <reference lib="webworker" />
declare const __G_CLIENT_ID__: string
declare const __ENABLE_LOGIN__: boolean

View File

@ -7,15 +7,56 @@ import { TanStackRouterVite } from "@tanstack/router-plugin/vite"
import tsconfigPath from "vite-tsconfig-paths"
import unocss from "unocss/vite"
import dotenv from "dotenv"
import type { VitePWAOptions } from "vite-plugin-pwa"
import { VitePWA } from "vite-plugin-pwa"
import { projectDir } from "./shared/dir"
const isCF = process.env.CF_PAGES
dotenv.config({
path: join(projectDir, ".env.server"),
})
const pwaOption: Partial<VitePWAOptions> = {
includeAssets: ["icon.svg", "apple-touch-icon.png"],
manifest: {
name: "NewsNow",
short_name: "NewsNow",
description: "Elegant reading of real-time and hottest news",
theme_color: "#F14D42",
icons: [
{
src: "pwa-192x192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "pwa-512x512.png",
sizes: "512x512",
type: "image/png",
},
{
src: "pwa-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
{
src: "pwa-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable",
},
],
},
devOptions: {
enabled: process.env.SW_DEV === "true",
type: "module",
navigateFallback: "index.html",
},
}
export default defineConfig({
define: {
__G_CLIENT_ID__: `"${process.env.G_CLIENT_ID}"`,
@ -28,6 +69,7 @@ export default defineConfig({
}),
unocss(),
react(),
VitePWA(pwaOption),
nitro({
experimental: {
database: true,