feat: support pwa

This commit is contained in:
DIYgod 2023-01-20 23:20:07 +00:00
parent 74fd41a88b
commit 1e28fb9e12
No known key found for this signature in database
13 changed files with 4839 additions and 109 deletions

View File

@ -5,6 +5,9 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
})
const execSync = require("child_process").execSync
const withPWA = require("next-pwa")({
dest: "public",
})
const lastCommitCommand = "git rev-parse HEAD"
@ -28,41 +31,43 @@ class UnoCSS {
}
/** @type {import('next').NextConfig} */
module.exports = withBundleAnalyzer({
env: {
APP_DESCRIPTION: pkg.description,
},
experimental: {
scrollRestoration: true,
},
output: "standalone",
productionBrowserSourceMaps: true,
module.exports = withBundleAnalyzer(
withPWA({
env: {
APP_DESCRIPTION: pkg.description,
},
experimental: {
scrollRestoration: true,
},
output: "standalone",
productionBrowserSourceMaps: true,
webpack(config) {
config.plugins.push(new UnoCSS())
return config
},
webpack(config) {
config.plugins.push(new UnoCSS())
return config
},
images: {
dangerouslyAllowSVG: true,
contentSecurityPolicy:
"default-src 'self'; script-src 'none'; sandbox; style-src 'unsafe-inline';",
remotePatterns: [{ hostname: "**" }],
},
images: {
dangerouslyAllowSVG: true,
contentSecurityPolicy:
"default-src 'self'; script-src 'none'; sandbox; style-src 'unsafe-inline';",
remotePatterns: [{ hostname: "**" }],
},
async generateBuildId() {
return execSync(lastCommitCommand).toString().trim()
},
async generateBuildId() {
return execSync(lastCommitCommand).toString().trim()
},
serverRuntimeConfig: {
ENV_REDIS_URL: process.env.REDIS_URL,
ENV_REDIS_EXPIRE: process.env.REDIS_EXPIRE,
ENV_REDIS_REFRESH: process.env.REDIS_REFRESH,
ENV_MORALIS_WEB3_API_KEY: process.env.MORALIS_WEB3_API_KEY,
ENV_ALCHEMY_ETHEREUM_API_KEY: process.env.ALCHEMY_ETHEREUM_API_KEY,
ENV_ALCHEMY_POLYGON_API_KEY: process.env.ALCHEMY_POLYGON_API_KEY,
ENV_NFTSCAN_API_KEY: process.env.NFTSCAN_API_KEY,
ENV_OPENSEA_API_KEY: process.env.OPENSEA_API_KEY,
ENV_POAP_API_KEY: process.env.POAP_API_KEY,
},
})
serverRuntimeConfig: {
ENV_REDIS_URL: process.env.REDIS_URL,
ENV_REDIS_EXPIRE: process.env.REDIS_EXPIRE,
ENV_REDIS_REFRESH: process.env.REDIS_REFRESH,
ENV_MORALIS_WEB3_API_KEY: process.env.MORALIS_WEB3_API_KEY,
ENV_ALCHEMY_ETHEREUM_API_KEY: process.env.ALCHEMY_ETHEREUM_API_KEY,
ENV_ALCHEMY_POLYGON_API_KEY: process.env.ALCHEMY_POLYGON_API_KEY,
ENV_NFTSCAN_API_KEY: process.env.NFTSCAN_API_KEY,
ENV_OPENSEA_API_KEY: process.env.OPENSEA_API_KEY,
ENV_POAP_API_KEY: process.env.POAP_API_KEY,
},
}),
)

View File

@ -135,6 +135,7 @@
"eslint-config-next": "13.1.2",
"husky": "8.0.3",
"lint-staged": "13.1.0",
"next-pwa": "^5.6.0",
"postcss-import": "15.1.0",
"prettier": "2.8.3",
"tailwindcss": "3.2.4",

File diff suppressed because it is too large Load Diff

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

107
public/sw.js Normal file
View File

@ -0,0 +1,107 @@
/**
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// If the loader is already loaded, just stop.
if (!self.define) {
let registry = {}
// Used for `eval` and `importScripts` where we can't get script URL by other means.
// In both cases, it's safe to use a global var because those functions are synchronous.
let nextDefineUri
const singleRequire = (uri, parentUri) => {
uri = new URL(uri + ".js", parentUri).href
return (
registry[uri] ||
new Promise((resolve) => {
if ("document" in self) {
const script = document.createElement("script")
script.src = uri
script.onload = resolve
document.head.appendChild(script)
} else {
nextDefineUri = uri
importScripts(uri)
resolve()
}
}).then(() => {
let promise = registry[uri]
if (!promise) {
throw new Error(`Module ${uri} didnt register its module`)
}
return promise
})
)
}
self.define = (depsNames, factory) => {
const uri =
nextDefineUri ||
("document" in self ? document.currentScript.src : "") ||
location.href
if (registry[uri]) {
// Module is already loading or loaded.
return
}
let exports = {}
const require = (depUri) => singleRequire(depUri, uri)
const specialDeps = {
module: { uri },
exports,
require,
}
registry[uri] = Promise.all(
depsNames.map((depName) => specialDeps[depName] || require(depName)),
).then((deps) => {
factory(...deps)
return exports
})
}
}
define(["./workbox-212689ce"], function (workbox) {
"use strict"
importScripts()
self.skipWaiting()
workbox.clientsClaim()
workbox.registerRoute(
"/",
new workbox.NetworkFirst({
cacheName: "start-url",
plugins: [
{
cacheWillUpdate: async ({ request, response, event, state }) => {
if (response && response.type === "opaqueredirect") {
return new Response(response.body, {
status: 200,
statusText: "OK",
headers: response.headers,
})
}
return response
},
},
],
}),
"GET",
)
workbox.registerRoute(
/.*/i,
new workbox.NetworkOnly({
cacheName: "dev",
plugins: [],
}),
"GET",
)
})
//# sourceMappingURL=sw.js.map

1
public/sw.js.map Normal file

File diff suppressed because one or more lines are too long

2650
public/workbox-212689ce.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -46,6 +46,7 @@ export const SEOHead: React.FC<{
type="application/feed+json"
></link>
<link rel="icon" href={icon || `${SITE_URL}/logo.svg`}></link>
<link rel="manifest" href="/manifest.json" />
</Head>
)
}

View File

@ -9,6 +9,8 @@ const slugReservedWords = [
"dashboard",
"robots.txt",
"nft",
"sw.js",
"manifest.json",
]
export const checkSlugReservedWords = (word: string) => {

View File

@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server"
import { IS_PROD } from "~/lib/constants"
import { DISCORD_LINK } from "~/lib/env"
import { DISCORD_LINK, IPFS_GATEWAY } from "~/lib/env"
export default async function middleware(req: NextRequest) {
const { pathname } = req.nextUrl
@ -29,8 +29,11 @@ export default async function middleware(req: NextRequest) {
if (
pathname.startsWith("/api/") ||
pathname.startsWith("/dashboard") ||
pathname.startsWith("/_next") ||
pathname === "/ipfs-gateway-sw.js"
pathname.startsWith("/_next/") ||
pathname.match(/^\/(workbox|worker|fallback)-\w+\.js(\.map)?$/) ||
pathname === "/sw.js" ||
pathname === "/sw.js.map" ||
pathname === "/logo.png"
) {
return NextResponse.next()
}

View File

@ -42,18 +42,6 @@ const queryClient = new QueryClient({
const persister = createIDBPersister()
try {
if (typeof window !== "undefined") {
navigator.serviceWorker?.getRegistrations?.().then((registrations) => {
registrations?.forEach((registration) => {
registration.unregister()
})
})
}
} catch (error) {
console.warn(error)
}
function MyApp({ Component, pageProps }: any) {
const getLayout = Component.getLayout ?? ((page: any) => page)
const connectModal = useConnectModal()

View File

@ -0,0 +1,40 @@
import { GetServerSideProps } from "next"
import { fetchGetSite } from "~/queries/site.server"
import { QueryClient } from "@tanstack/react-query"
export const getServerSideProps: GetServerSideProps = async (ctx) => {
ctx.res.setHeader("Content-Type", "application/json")
const queryClient = new QueryClient()
const domainOrSubdomain = ctx.params!.site as string
const site = await fetchGetSite(domainOrSubdomain, queryClient)
ctx.res.write(
JSON.stringify({
name: site.name,
short_name: site.name,
description: site.description,
icons: [
{
src: site.avatars?.[0] || "logo.png",
type: "image/png",
sizes: "any",
},
],
theme_color: "#ffffff",
background_color: "#ffffff",
start_url: "/",
display: "standalone",
orientation: "portrait",
}),
)
ctx.res.end()
return {
props: {},
}
}
const ManifestJson: React.FC = () => null
export default ManifestJson