feat: add auto-update and out-of-date indicator

This commit is contained in:
Paul Makles 2022-06-12 21:16:42 +01:00
parent 7680931f5f
commit a404ff7fe0
10 changed files with 145 additions and 28 deletions

View file

@ -52,17 +52,18 @@ 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. |
| `yarn preview` | Start a local server with the built client. | | `yarn preview` | Start a local server with the built client. |
| `yarn lint` | Run ESLint on the client. | | `yarn lint` | Run ESLint on the client. |
| `yarn fmt` | Run Prettier on the client. | | `yarn fmt` | Run Prettier on the client. |
| `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

@ -1 +1 @@
Subproject commit a38d0dc72a39ea2b5a6f54c1c999f2021b899e50 Subproject commit 309648592801a3bb5c1fa1702753f8dadde56cae

View file

@ -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",

View file

@ -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;

View 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
/>
);
}

View file

@ -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,
}); });

View file

@ -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";
} }

View file

@ -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
View 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);
}

View file

@ -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