mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-22 07:00:58 -05:00
feat: add auto-update and out-of-date indicator
This commit is contained in:
parent
7680931f5f
commit
a404ff7fe0
10 changed files with 145 additions and 28 deletions
|
@ -53,7 +53,7 @@ You can now access the client at http://local.revolt.chat:3000.
|
||||||
## CLI Commands
|
## CLI Commands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| ------------------- | -------------------------------------------- |
|
| --------------------------------------- | -------------------------------------------- |
|
||||||
| `yarn pull` | Setup assets required for Revite. |
|
| `yarn pull` | Setup assets required for Revite. |
|
||||||
| `yarn dev` | Start the Revolt client in development mode. |
|
| `yarn dev` | Start the Revolt client in development mode. |
|
||||||
| `yarn build` | Build the Revolt client. |
|
| `yarn build` | Build the Revolt client. |
|
||||||
|
@ -63,6 +63,7 @@ You can now access the client at http://local.revolt.chat:3000.
|
||||||
| `yarn typecheck` | Run TypeScript type checking on the client. |
|
| `yarn typecheck` | Run TypeScript type checking on the client. |
|
||||||
| `yarn start` | Start a local sirv server with built client. |
|
| `yarn start` | Start a local sirv server with built client. |
|
||||||
| `yarn start:inject` | Inject a given API URL and start server. |
|
| `yarn start:inject` | Inject a given API URL and start server. |
|
||||||
|
| `yarn lint \| egrep "no-literals" -B 1` | Scan for untranslated strings. |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit a38d0dc72a39ea2b5a6f54c1c999f2021b899e50
|
Subproject commit 309648592801a3bb5c1fa1702753f8dadde56cae
|
|
@ -38,7 +38,8 @@
|
||||||
{
|
{
|
||||||
"varsIgnorePattern": "^_"
|
"varsIgnorePattern": "^_"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"react/jsx-no-literals": "warn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -92,6 +93,7 @@
|
||||||
"@types/react-helmet": "^6.1.1",
|
"@types/react-helmet": "^6.1.1",
|
||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"@types/react-scroll": "^1.8.2",
|
"@types/react-scroll": "^1.8.2",
|
||||||
|
"@types/semver": "^7",
|
||||||
"@types/styled-components": "^5.1.10",
|
"@types/styled-components": "^5.1.10",
|
||||||
"@types/twemoji": "^12.1.1",
|
"@types/twemoji": "^12.1.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||||
|
@ -132,6 +134,7 @@
|
||||||
"revolt.js": "6.0.3",
|
"revolt.js": "6.0.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sass": "^1.35.1",
|
"sass": "^1.35.1",
|
||||||
|
"semver": "^7.3.7",
|
||||||
"shade-blend-color": "^1.0.0",
|
"shade-blend-color": "^1.0.0",
|
||||||
"stacktrace-js": "^2.0.2",
|
"stacktrace-js": "^2.0.2",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { internalSubscribe } from "../../lib/eventEmitter";
|
||||||
|
|
||||||
import { useApplicationState } from "../../mobx/State";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
|
|
||||||
import { updateSW } from "../../main";
|
import { updateSW } from "../../updateWorker";
|
||||||
import Tooltip from "./Tooltip";
|
import Tooltip from "./Tooltip";
|
||||||
|
|
||||||
let pendingUpdate = false;
|
let pendingUpdate = false;
|
||||||
|
|
47
src/context/modals/components/OutOfDate.tsx
Normal file
47
src/context/modals/components/OutOfDate.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
|
import { Modal } from "@revoltchat/ui";
|
||||||
|
|
||||||
|
import { noop, noopTrue } from "../../../lib/js";
|
||||||
|
|
||||||
|
import { APP_VERSION } from "../../../version";
|
||||||
|
import { ModalProps } from "../types";
|
||||||
|
|
||||||
|
export default function OutOfDate({
|
||||||
|
onClose,
|
||||||
|
version,
|
||||||
|
}: ModalProps<"out_of_date">) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={<Text id="app.special.modals.out_of_date.title" />}
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
<Text id="app.special.modals.out_of_date.description" />
|
||||||
|
<br />
|
||||||
|
<Text
|
||||||
|
id="app.special.modals.out_of_date.version"
|
||||||
|
fields={{ client: APP_VERSION, server: version }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
palette: "plain",
|
||||||
|
onClick: noop,
|
||||||
|
children: (
|
||||||
|
<Text id="app.special.modals.out_of_date.attempting" />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
palette: "plain-secondary",
|
||||||
|
onClick: noopTrue,
|
||||||
|
children: (
|
||||||
|
<Text id="app.special.modals.out_of_date.ignore" />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onClose={onClose}
|
||||||
|
nonDismissable
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import { ulid } from "ulid";
|
||||||
import MFAEnableTOTP from "./components/MFAEnableTOTP";
|
import MFAEnableTOTP from "./components/MFAEnableTOTP";
|
||||||
import MFAFlow from "./components/MFAFlow";
|
import MFAFlow from "./components/MFAFlow";
|
||||||
import MFARecovery from "./components/MFARecovery";
|
import MFARecovery from "./components/MFARecovery";
|
||||||
|
import OutOfDate from "./components/OutOfDate";
|
||||||
import Test from "./components/Test";
|
import Test from "./components/Test";
|
||||||
import { Modal } from "./types";
|
import { Modal } from "./types";
|
||||||
|
|
||||||
|
@ -120,5 +121,6 @@ export const modalController = new ModalControllerExtended({
|
||||||
mfa_flow: MFAFlow,
|
mfa_flow: MFAFlow,
|
||||||
mfa_recovery: MFARecovery,
|
mfa_recovery: MFARecovery,
|
||||||
mfa_enable_totp: MFAEnableTOTP,
|
mfa_enable_totp: MFAEnableTOTP,
|
||||||
|
out_of_date: OutOfDate,
|
||||||
test: Test,
|
test: Test,
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,6 +24,10 @@ export type Modal = {
|
||||||
secret: string;
|
secret: string;
|
||||||
callback: (code?: string) => void;
|
callback: (code?: string) => void;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: "out_of_date";
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: "test";
|
type: "test";
|
||||||
}
|
}
|
||||||
|
|
15
src/main.tsx
15
src/main.tsx
|
@ -1,21 +1,8 @@
|
||||||
import { registerSW } from "virtual:pwa-register";
|
|
||||||
|
|
||||||
import "./styles/index.scss";
|
import "./styles/index.scss";
|
||||||
import { render } from "preact";
|
import { render } from "preact";
|
||||||
|
|
||||||
import { internalEmit } from "./lib/eventEmitter";
|
|
||||||
|
|
||||||
import { App } from "./pages/app";
|
import { App } from "./pages/app";
|
||||||
|
import "./updateWorker";
|
||||||
export const updateSW = registerSW({
|
|
||||||
onNeedRefresh() {
|
|
||||||
internalEmit("PWA", "update");
|
|
||||||
},
|
|
||||||
onOfflineReady() {
|
|
||||||
console.info("Ready to work offline.");
|
|
||||||
// show a ready to work offline to user
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
render(<App />, document.getElementById("app")!);
|
render(<App />, document.getElementById("app")!);
|
||||||
|
|
64
src/updateWorker.ts
Normal file
64
src/updateWorker.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import semver from "semver";
|
||||||
|
import { ulid } from "ulid";
|
||||||
|
import { registerSW } from "virtual:pwa-register";
|
||||||
|
|
||||||
|
import { internalEmit } from "./lib/eventEmitter";
|
||||||
|
|
||||||
|
import { modalController } from "./context/modals";
|
||||||
|
|
||||||
|
import { APP_VERSION } from "./version";
|
||||||
|
|
||||||
|
const INTERVAL_HOUR = 36e5;
|
||||||
|
|
||||||
|
let forceUpdate = false;
|
||||||
|
let registration: ServiceWorkerRegistration | undefined;
|
||||||
|
|
||||||
|
export const updateSW = registerSW({
|
||||||
|
onNeedRefresh() {
|
||||||
|
if (forceUpdate) {
|
||||||
|
updateSW(true);
|
||||||
|
} else {
|
||||||
|
internalEmit("PWA", "update");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onOfflineReady() {
|
||||||
|
console.info("Ready to work offline.");
|
||||||
|
// show a ready to work offline to user
|
||||||
|
},
|
||||||
|
onRegistered(r) {
|
||||||
|
registration = r;
|
||||||
|
|
||||||
|
// Check for updates every hour
|
||||||
|
setInterval(() => r!.update(), INTERVAL_HOUR);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the client is out of date
|
||||||
|
*/
|
||||||
|
async function checkVersion() {
|
||||||
|
const { version } = (await fetch("https://api.revolt.chat/release").then(
|
||||||
|
(res) => res.json(),
|
||||||
|
)) as { version: string };
|
||||||
|
|
||||||
|
if (!semver.satisfies(APP_VERSION, version)) {
|
||||||
|
// Let the worker know we should immediately refresh
|
||||||
|
forceUpdate = true;
|
||||||
|
|
||||||
|
// Prompt service worker to update
|
||||||
|
registration?.update();
|
||||||
|
|
||||||
|
// Push information that the client is out of date
|
||||||
|
modalController.push({
|
||||||
|
key: ulid(),
|
||||||
|
type: "out_of_date",
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.env.VITE_API_URL === "https://api.revolt.chat") {
|
||||||
|
// Check for critical updates hourly
|
||||||
|
checkVersion();
|
||||||
|
setInterval(checkVersion, INTERVAL_HOUR);
|
||||||
|
}
|
|
@ -2730,6 +2730,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/semver@npm:^7":
|
||||||
|
version: 7.3.9
|
||||||
|
resolution: "@types/semver@npm:7.3.9"
|
||||||
|
checksum: 60bfcfdfa7f937be2c6f4b37ddb6714fb0f27b05fe4cbdfdd596a97d35ed95d13ee410efdd88e72a66449d0384220bf20055ab7d6b5df10de4990fbd20e5cbe0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/styled-components@npm:^5.1.10":
|
"@types/styled-components@npm:^5.1.10":
|
||||||
version: 5.1.13
|
version: 5.1.13
|
||||||
resolution: "@types/styled-components@npm:5.1.13"
|
resolution: "@types/styled-components@npm:5.1.13"
|
||||||
|
@ -3551,6 +3558,7 @@ __metadata:
|
||||||
"@types/react-helmet": ^6.1.1
|
"@types/react-helmet": ^6.1.1
|
||||||
"@types/react-router-dom": ^5.1.7
|
"@types/react-router-dom": ^5.1.7
|
||||||
"@types/react-scroll": ^1.8.2
|
"@types/react-scroll": ^1.8.2
|
||||||
|
"@types/semver": ^7
|
||||||
"@types/styled-components": ^5.1.10
|
"@types/styled-components": ^5.1.10
|
||||||
"@types/twemoji": ^12.1.1
|
"@types/twemoji": ^12.1.1
|
||||||
"@typescript-eslint/eslint-plugin": ^4.27.0
|
"@typescript-eslint/eslint-plugin": ^4.27.0
|
||||||
|
@ -3593,6 +3601,7 @@ __metadata:
|
||||||
revolt.js: 6.0.3
|
revolt.js: 6.0.3
|
||||||
rimraf: ^3.0.2
|
rimraf: ^3.0.2
|
||||||
sass: ^1.35.1
|
sass: ^1.35.1
|
||||||
|
semver: ^7.3.7
|
||||||
shade-blend-color: ^1.0.0
|
shade-blend-color: ^1.0.0
|
||||||
sirv-cli: ^1.0.14
|
sirv-cli: ^1.0.14
|
||||||
stacktrace-js: ^2.0.2
|
stacktrace-js: ^2.0.2
|
||||||
|
|
Loading…
Reference in a new issue