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 e862a2c908
commit 52ab0e5526
10 changed files with 145 additions and 28 deletions

View File

@@ -9,7 +9,7 @@ import { internalSubscribe } from "../../lib/eventEmitter";
import { useApplicationState } from "../../mobx/State";
import { updateSW } from "../../main";
import { updateSW } from "../../updateWorker";
import Tooltip from "./Tooltip";
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 MFAFlow from "./components/MFAFlow";
import MFARecovery from "./components/MFARecovery";
import OutOfDate from "./components/OutOfDate";
import Test from "./components/Test";
import { Modal } from "./types";
@@ -120,5 +121,6 @@ export const modalController = new ModalControllerExtended({
mfa_flow: MFAFlow,
mfa_recovery: MFARecovery,
mfa_enable_totp: MFAEnableTOTP,
out_of_date: OutOfDate,
test: Test,
});

View File

@@ -24,6 +24,10 @@ export type Modal = {
secret: string;
callback: (code?: string) => void;
}
| {
type: "out_of_date";
version: string;
}
| {
type: "test";
}

View File

@@ -1,21 +1,8 @@
import { registerSW } from "virtual:pwa-register";
import "./styles/index.scss";
import { render } from "preact";
import { internalEmit } from "./lib/eventEmitter";
import { App } from "./pages/app";
export const updateSW = registerSW({
onNeedRefresh() {
internalEmit("PWA", "update");
},
onOfflineReady() {
console.info("Ready to work offline.");
// show a ready to work offline to user
},
});
import "./updateWorker";
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
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);
}