Merge pull request #165 from ZauberNerd/service-worker
Add service worker and make PWA installablemain
commit
c75663bf90
|
@ -3,33 +3,24 @@
|
|||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
|
||||
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
|
||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
|
||||
<link rel="preconnect" crossorigin="anonymous" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" crossorigin="anonymous" href="https://cdnjs.cloudflare.com">
|
||||
<link rel="preconnect" crossorigin="anonymous" href="https://cdn.jsdelivr.net">
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<!-- mobile app viewport -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="An open source ChatGPT app with a voice." />
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<!--
|
||||
Notice the use of in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Chat with GPT | Unofficial ChatGPT app</title>
|
||||
<link rel="stylesheet" media="all" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" />
|
||||
<link rel="stylesheet" media="all" href="https://fonts.googleapis.com/css?family=Open+Sans:100,400,300,500,700,800" />
|
||||
<link rel="stylesheet" media="all" href="https://fonts.googleapis.com/css?family=Fira+Code:100,400,300,500,700,800" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Work+Sans:300,400,500,600,700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css"
|
||||
integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC" crossorigin="anonymous">
|
||||
<link rel="stylesheet" crossorigin="anonymous" media="all" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" />
|
||||
<link rel="stylesheet" crossorigin="anonymous" media="all" href="https://fonts.googleapis.com/css?family=Open+Sans:100,400,300,500,700,800" />
|
||||
<link rel="stylesheet" crossorigin="anonymous" media="all" href="https://fonts.googleapis.com/css?family=Fira+Code:100,400,300,500,700,800" />
|
||||
<link rel="stylesheet" crossorigin="anonymous" href="https://fonts.googleapis.com/css?family=Work+Sans:300,400,500,600,700&display=swap">
|
||||
<link rel="stylesheet" crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css" integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC">
|
||||
<link rel="stylesheet" href="/prose.css" />
|
||||
<link rel="canonical" href="https://www.chatwithgpt.ai" />
|
||||
<style>
|
||||
|
|
|
@ -69,6 +69,8 @@
|
|||
"@vitejs/plugin-react": "^4.0.2",
|
||||
"babel-plugin-formatjs": "^10.5.3",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.4.1"
|
||||
"vite": "^4.4.1",
|
||||
"vite-plugin-pwa": "^0.16.4",
|
||||
"workbox-window": "^7.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"short_name": "Chat with GPT",
|
||||
"name": "Chat with GPT",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -4,6 +4,7 @@ import { useChatSpotlightProps } from '../spotlight';
|
|||
import { LoginModal, CreateAccountModal } from './auth-modals';
|
||||
import Header, { HeaderProps, SubHeader } from './header';
|
||||
import MessageInput from './input';
|
||||
import { InstallUpdateNotification } from './pwa-notifications';
|
||||
import SettingsDrawer from './settings';
|
||||
import Sidebar from './sidebar';
|
||||
import AudioControls from './tts-controls';
|
||||
|
@ -90,6 +91,7 @@ export function Page(props: {
|
|||
<SettingsDrawer />
|
||||
<LoginModal />
|
||||
<CreateAccountModal />
|
||||
<InstallUpdateNotification />
|
||||
</Main>
|
||||
</Container>
|
||||
</SpotlightProvider>;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { Button, Notification } from "@mantine/core";
|
||||
import { useCallback } from "react";
|
||||
import { useRegisterSW } from "virtual:pwa-register/react";
|
||||
|
||||
export function InstallUpdateNotification() {
|
||||
const {
|
||||
offlineReady: [_, setOfflineReady],
|
||||
needRefresh: [needRefresh, setNeedRefresh],
|
||||
updateServiceWorker,
|
||||
} = useRegisterSW({
|
||||
onRegistered(r) {
|
||||
console.log("SW Registered:", r);
|
||||
},
|
||||
onRegisterError(error) {
|
||||
console.log("SW registration error", error);
|
||||
},
|
||||
});
|
||||
|
||||
const onClose = () => {
|
||||
setOfflineReady(false);
|
||||
setNeedRefresh(false);
|
||||
};
|
||||
|
||||
const onUpdate = useCallback(async () => {
|
||||
updateServiceWorker(true);
|
||||
}, []);
|
||||
|
||||
return needRefresh ? (
|
||||
<Notification title="Update available!" onClose={onClose}>
|
||||
Click{" "}
|
||||
<Button compact onClick={onUpdate}>
|
||||
Update now
|
||||
</Button>{" "}
|
||||
to get the latest version.
|
||||
</Notification>
|
||||
) : null;
|
||||
}
|
|
@ -86,4 +86,4 @@ async function bootstrapApplication() {
|
|||
);
|
||||
}
|
||||
|
||||
bootstrapApplication();
|
||||
bootstrapApplication();
|
||||
|
|
|
@ -31,8 +31,6 @@ const persistMessageConfig = {
|
|||
storage,
|
||||
}
|
||||
|
||||
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
message: persistReducer<ReturnType<typeof messageReducer>>(persistMessageConfig, messageReducer),
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
/// <reference types="vite-plugin-comlink/client" />
|
||||
/// <reference types="vite-plugin-pwa/client" />
|
|
@ -1,5 +1,6 @@
|
|||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
|
@ -14,10 +15,11 @@ export default defineConfig(() => {
|
|||
},
|
||||
build: {
|
||||
outDir: "build",
|
||||
target: "es2020"
|
||||
target: "es2020",
|
||||
sourcemap: true,
|
||||
},
|
||||
esbuild: {
|
||||
target: "es2020"
|
||||
target: "es2020",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
@ -38,6 +40,90 @@ export default defineConfig(() => {
|
|||
],
|
||||
},
|
||||
}),
|
||||
VitePWA({
|
||||
registerType: "autoUpdate",
|
||||
includeAssets: ["favicon.ico", "lang/*.json"],
|
||||
manifest: {
|
||||
short_name: "Chat with GPT",
|
||||
name: "Chat with GPT",
|
||||
start_url: ".",
|
||||
display: "standalone",
|
||||
theme_color: "#000000",
|
||||
background_color: "#ffffff",
|
||||
icons: [
|
||||
{
|
||||
src: "logo192.png",
|
||||
type: "image/png",
|
||||
sizes: "192x192",
|
||||
},
|
||||
{
|
||||
src: "logo512.png",
|
||||
type: "image/png",
|
||||
sizes: "512x512",
|
||||
},
|
||||
],
|
||||
},
|
||||
workbox: {
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
|
||||
handler: "CacheFirst",
|
||||
options: {
|
||||
cacheName: "google-fonts-cache",
|
||||
expiration: {
|
||||
maxEntries: 10,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
|
||||
handler: "CacheFirst",
|
||||
options: {
|
||||
cacheName: "gstatic-fonts-cache",
|
||||
expiration: {
|
||||
maxEntries: 10,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /^https:\/\/cdnjs\.cloudflare\.com\/.*/i,
|
||||
handler: "CacheFirst",
|
||||
options: {
|
||||
cacheName: "cloudflare-js-cdn",
|
||||
expiration: {
|
||||
maxEntries: 10,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /^https:\/\/cdn\.jsdelivr\.net\/.*/i,
|
||||
handler: "CacheFirst",
|
||||
options: {
|
||||
cacheName: "jsdelivr-cdn",
|
||||
expiration: {
|
||||
maxEntries: 10,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue