mirror of
https://github.com/Unkn0wnCat/data-toolbox-site.git
synced 2025-06-25 10:46:56 +02:00
Add Settings and Licenses
This commit is contained in:
parent
6a7e2276f6
commit
87f685c5d7
13 changed files with 21214 additions and 12 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "kevins-data-toolbox",
|
||||
"version": "2.4.7",
|
||||
"version": "2.5.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@loadable/component": "^5.15.0",
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"title": "Kevins Datenkasten",
|
||||
"navigation": {
|
||||
"tools": "Werkzeuge",
|
||||
"about": "Über"
|
||||
"about": "Über",
|
||||
"settings": "Einstellungen"
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
|
@ -95,5 +96,18 @@
|
|||
"description": "Im Werkzeug ist ein fatales Problem aufgetreten und die Aufgabe konnte nicht abgeschlossen werden. Bitte erneut versuchen."
|
||||
}
|
||||
}
|
||||
},
|
||||
"licenses": {
|
||||
"title": "Quellcode-Lizenzen"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
"currentVersion": "Aktuelle Version",
|
||||
"offlineUnavailable": "Offline-Installation in deinem Browser nicht verfügbar.",
|
||||
"checkingForUpdate": "Prüfe auf Aktualisierungen...",
|
||||
"checkForUpdate": "Prüfe auf Aktualisierungen",
|
||||
"version": "Version",
|
||||
"about": "Über",
|
||||
"source": "Quellcode"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"title": "Kevins Data-Toolbox",
|
||||
"navigation": {
|
||||
"tools": "Tools",
|
||||
"about": "About"
|
||||
"about": "About",
|
||||
"settings": "Settings"
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
|
@ -95,5 +96,18 @@
|
|||
"description": "The tool encountered a fatal error and was unable to complete its task. Please retry."
|
||||
}
|
||||
}
|
||||
},
|
||||
"licenses": {
|
||||
"title": "Open-Source-Licenses"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"currentVersion": "Current Version",
|
||||
"offlineUnavailable": "Offline-installation not available on your browser.",
|
||||
"checkingForUpdate": "Checking for updates...",
|
||||
"checkForUpdate": "Check for Updates",
|
||||
"version": "Version",
|
||||
"about": "About",
|
||||
"source": "Source Code"
|
||||
}
|
||||
}
|
||||
|
|
17
src/App.tsx
17
src/App.tsx
|
@ -9,21 +9,22 @@ import Navigation from "./components/Navigation";
|
|||
import * as styles from "./App.module.scss";
|
||||
import NotFoundPage from "./pages/NotFound";
|
||||
import ToolLoader from "./tools/ToolLoader";
|
||||
import prerenderedLoadable from "./helpers/prerenderedLoadable";
|
||||
import {version} from "../package.json"
|
||||
|
||||
const HomePage = prerenderedLoadable(() => import('./pages/Home'));
|
||||
const ToolsPage = prerenderedLoadable(() => import('./pages/Tools'));
|
||||
const AboutPage = prerenderedLoadable(() => import('./pages/About'));
|
||||
const HomePage = React.lazy(() => import('./pages/Home'));
|
||||
const ToolsPage = React.lazy(() => import('./pages/Tools'));
|
||||
const AboutPage = React.lazy(() => import('./pages/About'));
|
||||
const SettingsPage = React.lazy(() => import('./pages/Settings'));
|
||||
const LicensesPage = React.lazy(() => import('./pages/Licenses'));
|
||||
|
||||
function App() {
|
||||
|
||||
return (
|
||||
<Suspense fallback="Kevin's Data-Toolbox is loading...">
|
||||
<Suspense fallback={<h1>Kevin's Data-Toolbox is loading...</h1>}>
|
||||
<Router>
|
||||
<div className={styles.appContainer}>
|
||||
<Navigation/>
|
||||
<Suspense fallback="Kevin's Data-Toolbox is loading...">
|
||||
<Suspense fallback={<div className={styles.layoutBox}><h1>Kevin's Data-Toolbox is loading...</h1></div>}>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage/>} />
|
||||
|
||||
|
@ -33,6 +34,10 @@ function App() {
|
|||
<Route path="/tool/:tool" element={<ToolLoader/>} />
|
||||
|
||||
<Route path="/about" element={<AboutPage/>} />
|
||||
|
||||
<Route path="/settings" element={<SettingsPage/>} />
|
||||
|
||||
<Route path="/licenses" element={<LicensesPage/>} />
|
||||
|
||||
<Route path="*" element={<NotFoundPage/>} />
|
||||
</Routes>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import * as styles from "./Navigation.module.scss";
|
||||
import { Globe, Info, List, RefreshCw } from "lucide-react";
|
||||
import { Globe, List, RefreshCw, Wrench } from "lucide-react";
|
||||
import LanguageChooser from "./LanguageChooser";
|
||||
import ServiceWorkerAPI from "../services/serviceWorkers"
|
||||
|
||||
|
@ -20,7 +20,7 @@ const Navigation = () => {
|
|||
<Link to={"/"}>{t("site.title")}</Link>
|
||||
<span className={styles.spacer}></span>
|
||||
<Link to={"/tools"} title={t("site.navigation.tools")}><List/></Link>
|
||||
<Link to={"/about"} title={t("site.navigation.about")}><Info/></Link>
|
||||
<Link to={"/settings"} title={t("site.navigation.settings")}><Wrench/></Link>
|
||||
<Link to={"#"} onClick={() => {setLangChooserActive(true)}} title="Change Language"><Globe/></Link>
|
||||
{updateAvailable && <Link to={"#"} onClick={() => {ServiceWorkerAPI.forceUpdate()}} title="Update Available"><RefreshCw /></Link>}
|
||||
|
||||
|
|
21010
src/licenses.ts
Normal file
21010
src/licenses.ts
Normal file
File diff suppressed because one or more lines are too long
1
src/pages/Licenses.module.scss
Normal file
1
src/pages/Licenses.module.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "../common";
|
4
src/pages/Licenses.module.scss.d.ts
vendored
Normal file
4
src/pages/Licenses.module.scss.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
export const center: string;
|
||||
export const flexList: string;
|
||||
export const layoutBox: string;
|
||||
export const title: string;
|
27
src/pages/Licenses.tsx
Normal file
27
src/pages/Licenses.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
|
||||
import * as styles from "./Licenses.module.scss";
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import text from "../licenses";
|
||||
|
||||
const LicensesPage = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return <>
|
||||
<Helmet><title>{t("licenses.title")} | {t("site.title")}</title></Helmet>
|
||||
<main>
|
||||
<div className={styles.layoutBox}>
|
||||
<h1>{t("licenses.title")}</h1>
|
||||
|
||||
<pre style={{wordWrap: "break-word", overflow: "hidden", lineBreak: "anywhere", whiteSpace: "break-spaces"}}>
|
||||
{text}
|
||||
</pre>
|
||||
</div>
|
||||
</main>
|
||||
</>;
|
||||
}
|
||||
|
||||
export default LicensesPage;
|
42
src/pages/Settings.module.scss
Normal file
42
src/pages/Settings.module.scss
Normal file
|
@ -0,0 +1,42 @@
|
|||
@use "sass:math";
|
||||
@import "../common";
|
||||
|
||||
.settingsSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $layoutPadding 0;
|
||||
border-bottom: thin solid rgba(0, 0, 0, .1);
|
||||
|
||||
@media(prefers-color-scheme: dark) {
|
||||
border-bottom: thin solid rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
> .sectionHeader {
|
||||
font-weight: 700;
|
||||
margin-bottom: $layoutPadding;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-top: thin solid rgba(0, 0, 0, .1);
|
||||
border-bottom: thin solid rgba(0, 0, 0, .1);
|
||||
padding: math.div($layoutPadding, 2);
|
||||
margin: 0 math.div($layoutPadding, 2);
|
||||
|
||||
@media(prefers-color-scheme: dark) {
|
||||
border-top: thin solid rgba(255, 255, 255, .1);
|
||||
border-bottom: thin solid rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
> span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> a ~ a {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
6
src/pages/Settings.module.scss.d.ts
vendored
Normal file
6
src/pages/Settings.module.scss.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
export const center: string;
|
||||
export const flexList: string;
|
||||
export const layoutBox: string;
|
||||
export const sectionHeader: string;
|
||||
export const settingsSection: string;
|
||||
export const title: string;
|
44
src/pages/Settings.tsx
Normal file
44
src/pages/Settings.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { ChevronRight } from "lucide-react";
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { version } from "../../package.json";
|
||||
|
||||
import ServiceWorkerAPI from "../services/serviceWorkers";
|
||||
|
||||
import * as styles from "./Settings.module.scss";
|
||||
|
||||
const Settings = () => {
|
||||
const {t} = useTranslation();
|
||||
const {checkingForUpdate, checkForUpdate} = ServiceWorkerAPI.useCheckUpdate()
|
||||
|
||||
return <>
|
||||
<Helmet><title>{t("settings.title")} | {t("site.title")}</title></Helmet>
|
||||
<div className={styles.layoutBox}>
|
||||
<h1>{t("settings.title")}</h1>
|
||||
|
||||
<div className={styles.settingsSection}>
|
||||
<span className={styles.sectionHeader}>{t("settings.version")}</span>
|
||||
|
||||
<div>
|
||||
<span>{t("settings.currentVersion")}: {version}</span>{" "}
|
||||
{!ServiceWorkerAPI.serviceWorkerAvailable() && <span>{t("settings.offlineUnavailable")}</span>}
|
||||
{ServiceWorkerAPI.serviceWorkerAvailable() && (<button onClick={() => {checkForUpdate()}} disabled={checkingForUpdate ? true : undefined}>{checkingForUpdate ? t("settings.checkingForUpdate") : t("settings.checkForUpdate")}</button>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.settingsSection}>
|
||||
<span className={styles.sectionHeader}>{t("settings.about")}</span>
|
||||
|
||||
<Link to={"/about"}><span>{t("about.title")}</span><ChevronRight/></Link>
|
||||
|
||||
<Link to={"/licenses"}><span>{t("licenses.title")}</span><ChevronRight/></Link>
|
||||
|
||||
<a href={"https://github.com/Unkn0wnCat/data-toolbox-site"} target={"_blank"} rel="noreferrer"><span>{t("settings.source")}</span><ChevronRight/></a>
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
||||
export default Settings;
|
|
@ -98,13 +98,48 @@ const useUpdatePending = () => {
|
|||
return updatePending
|
||||
}
|
||||
|
||||
const serviceWorkerAvailable = () => {
|
||||
return 'serviceWorker' in navigator;
|
||||
}
|
||||
|
||||
const checkUpdate = async (): Promise<boolean> => {
|
||||
if('serviceWorker' in navigator) {
|
||||
try {
|
||||
const reg = await navigator.serviceWorker.getRegistration()
|
||||
|
||||
if(!reg) return false;
|
||||
|
||||
await reg.update();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const useCheckUpdate = () => {
|
||||
const [checkingForUpdate, setChecking] = useState(false);
|
||||
|
||||
const checkForUpdate = async () => {
|
||||
setChecking(true);
|
||||
|
||||
await checkUpdate();
|
||||
|
||||
setChecking(false);
|
||||
}
|
||||
|
||||
return {checkingForUpdate, checkForUpdate};
|
||||
}
|
||||
|
||||
const ServiceWorkerAPI = {
|
||||
on,
|
||||
off,
|
||||
forceUpdate,
|
||||
isUpdatePending,
|
||||
events,
|
||||
useUpdatePending
|
||||
useUpdatePending,
|
||||
serviceWorkerAvailable,
|
||||
useCheckUpdate
|
||||
}
|
||||
|
||||
export default ServiceWorkerAPI
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue