mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-05-24 10:29:11 +00:00
fix: minor PWA fixes and implement offline support
Minor fixes such as setting the start_url to `/editor`. Also add complete offline support by updating configuration. This is especially nice for mobile users. I've had some slight issues running this via `npm run dev`. However, `npm run build && npm run preview` seems to work fine.
This commit is contained in:
parent
18b4267047
commit
22a7603c21
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ lerna-debug.log*
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
dev-dist
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
|
3
package-lock.json
generated
3
package-lock.json
generated
@ -49,7 +49,8 @@
|
|||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.3.6",
|
||||||
"vite": "^5.0.11",
|
"vite": "^5.0.11",
|
||||||
"vite-plugin-pwa": "^0.20.0"
|
"vite-plugin-pwa": "^0.20.0",
|
||||||
|
"workbox-window": "^7.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aashutoshrathi/word-wrap": {
|
"node_modules/@aashutoshrathi/word-wrap": {
|
||||||
|
@ -51,7 +51,8 @@
|
|||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.3.6",
|
||||||
"vite": "^5.0.11",
|
"vite": "^5.0.11",
|
||||||
"vite-plugin-pwa": "^0.20.0"
|
"vite-plugin-pwa": "^0.20.0",
|
||||||
|
"workbox-window": "^7.1.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"follow-redirects": "^1.15.4"
|
"follow-redirects": "^1.15.4"
|
||||||
|
@ -9,6 +9,7 @@ import LandingPage from "./pages/LandingPage";
|
|||||||
import SettingsContextProvider from "./context/SettingsContext";
|
import SettingsContextProvider from "./context/SettingsContext";
|
||||||
import useSettings from "./hooks/useSettings";
|
import useSettings from "./hooks/useSettings";
|
||||||
import NotFound from "./pages/NotFound";
|
import NotFound from "./pages/NotFound";
|
||||||
|
import { PwaUpdatePrompt } from "./components/PwaUpdatePrompt";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@ -53,6 +54,7 @@ export default function App() {
|
|||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
<PwaUpdatePrompt />
|
||||||
</SettingsContextProvider>
|
</SettingsContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useRegisterSW } from "virtual:pwa-register/react";
|
||||||
import {
|
import {
|
||||||
IconCaretdown,
|
IconCaretdown,
|
||||||
|
IconCloud,
|
||||||
IconChevronRight,
|
IconChevronRight,
|
||||||
IconChevronUp,
|
IconChevronUp,
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
@ -18,6 +20,7 @@ import {
|
|||||||
InputNumber,
|
InputNumber,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Spin,
|
Spin,
|
||||||
|
Tag,
|
||||||
Toast,
|
Toast,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
} from "@douyinfe/semi-ui";
|
} from "@douyinfe/semi-ui";
|
||||||
@ -1287,9 +1290,13 @@ export default function ControlPanel({
|
|||||||
});
|
});
|
||||||
useHotkeys("ctrl+alt+w, meta+alt+w", fitWindow, { preventDefault: true });
|
useHotkeys("ctrl+alt+w, meta+alt+w", fitWindow, { preventDefault: true });
|
||||||
|
|
||||||
|
const {
|
||||||
|
offlineReady: [isOfflineReady],
|
||||||
|
} = useRegisterSW();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{layout.header && header()}
|
{layout.header && header(isOfflineReady)}
|
||||||
{layout.toolbar && toolbar()}
|
{layout.toolbar && toolbar()}
|
||||||
<Modal
|
<Modal
|
||||||
modal={modal}
|
modal={modal}
|
||||||
@ -1498,10 +1505,10 @@ export default function ControlPanel({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function header() {
|
function header(isOfflineReady) {
|
||||||
return (
|
return (
|
||||||
<nav className="flex justify-between pt-1 items-center whitespace-nowrap">
|
<nav className="flex justify-between pt-1 items-center whitespace-nowrap">
|
||||||
<div className="flex justify-start items-center">
|
<div className="flex justify-start items-center grow">
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<img
|
<img
|
||||||
width={54}
|
width={54}
|
||||||
@ -1510,7 +1517,7 @@ export default function ControlPanel({
|
|||||||
className="ms-8 min-w-[54px]"
|
className="ms-8 min-w-[54px]"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="ms-1 mt-1">
|
<div className="ms-1 sm:me-1 xl:me-6 mt-1 grow">
|
||||||
<div className="flex items-center ms-3 gap-2">
|
<div className="flex items-center ms-3 gap-2">
|
||||||
{databases[database].image && (
|
{databases[database].image && (
|
||||||
<img
|
<img
|
||||||
@ -1535,7 +1542,7 @@ export default function ControlPanel({
|
|||||||
</div>
|
</div>
|
||||||
{(showEditName || modal === MODAL.RENAME) && <IconEdit />}
|
{(showEditName || modal === MODAL.RENAME) && <IconEdit />}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-start items-center">
|
||||||
<div className="flex justify-start text-md select-none me-2">
|
<div className="flex justify-start text-md select-none me-2">
|
||||||
{Object.keys(menu).map((category) => (
|
{Object.keys(menu).map((category) => (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@ -1642,6 +1649,13 @@ export default function ControlPanel({
|
|||||||
>
|
>
|
||||||
{getState()}
|
{getState()}
|
||||||
</Button>
|
</Button>
|
||||||
|
{isOfflineReady && (
|
||||||
|
<span className="ms-auto">
|
||||||
|
<Tag prefixIcon={<IconCloud />} size="large">
|
||||||
|
{t("available_offline")}
|
||||||
|
</Tag>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
34
src/components/PwaUpdatePrompt.jsx
Normal file
34
src/components/PwaUpdatePrompt.jsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { useRegisterSW } from "virtual:pwa-register/react";
|
||||||
|
import { Typography, Toast } from "@douyinfe/semi-ui";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
export function PwaUpdatePrompt() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
needRefresh: [isRefreshNeeded],
|
||||||
|
updateServiceWorker,
|
||||||
|
} = useRegisterSW();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isRefreshNeeded) return;
|
||||||
|
|
||||||
|
Toast.info({
|
||||||
|
duration: 0, // indefinite
|
||||||
|
content: (
|
||||||
|
<div>
|
||||||
|
<h5>{t("update_available")}</h5>
|
||||||
|
<p className="text-xs">{t("reload_page_to_update")}</p>
|
||||||
|
<div className="mt-2">
|
||||||
|
<Text link onClick={updateServiceWorker}>
|
||||||
|
{t("reload_now")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [isRefreshNeeded, updateServiceWorker, t]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}
|
@ -6,6 +6,10 @@ const english = {
|
|||||||
|
|
||||||
const en = {
|
const en = {
|
||||||
translation: {
|
translation: {
|
||||||
|
available_offline: "Available offline",
|
||||||
|
update_available: "An updated version of drawDB is available!",
|
||||||
|
reload_page_to_update: "Reload the page to update",
|
||||||
|
reload_now: "Reload now",
|
||||||
report_bug: "Report a bug",
|
report_bug: "Report a bug",
|
||||||
import: "Import",
|
import: "Import",
|
||||||
file: "File",
|
file: "File",
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import { VitePWA } from "vite-plugin-pwa";
|
import { VitePWA } from "vite-plugin-pwa";
|
||||||
const manifestForPlugIn = {
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
VitePWA({
|
||||||
|
workbox: {
|
||||||
|
globPatterns: ["**/*"],
|
||||||
|
maximumFileSizeToCacheInBytes: 15_000_000,
|
||||||
|
},
|
||||||
|
includeAssets: ["**/*"],
|
||||||
registerType: "prompt",
|
registerType: "prompt",
|
||||||
includeAssests: ["favicon.ico", "apple-touc-icon.png"],
|
|
||||||
manifest: {
|
manifest: {
|
||||||
name: "DrawDB",
|
name: "DrawDB",
|
||||||
short_name: "DrawDB",
|
short_name: "DrawDB",
|
||||||
@ -33,6 +42,7 @@ const manifestForPlugIn = {
|
|||||||
purpose: "maskable",
|
purpose: "maskable",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
scope: "/",
|
||||||
start_url: "/editor",
|
start_url: "/editor",
|
||||||
display: "standalone",
|
display: "standalone",
|
||||||
background_color: "#14475b",
|
background_color: "#14475b",
|
||||||
@ -40,8 +50,6 @@ const manifestForPlugIn = {
|
|||||||
description:
|
description:
|
||||||
"Free, simple, and intuitive database design tool and SQL generator.",
|
"Free, simple, and intuitive database design tool and SQL generator.",
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
// https://vitejs.dev/config/
|
],
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react(), VitePWA(manifestForPlugIn)],
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user