feat: handle system alerts and poll rate changes

This commit is contained in:
Paul Makles 2022-06-18 17:03:04 +01:00
parent 03e177f865
commit f185dec461
4 changed files with 87 additions and 20 deletions

View file

@ -1,5 +1,5 @@
{ {
"version": "0.5.3-7", "version": "1.0.0",
"scripts": { "scripts": {
"dev": "node scripts/setup_assets.js --check && vite", "dev": "node scripts/setup_assets.js --check && vite",
"pull": "node scripts/setup_assets.js", "pull": "node scripts/setup_assets.js",

View file

@ -31,3 +31,4 @@ export function internalEmit(ns: string, event: string, ...args: unknown[]) {
// - PWA/update // - PWA/update
// - NewMessages/hide // - NewMessages/hide
// - NewMessages/mark // - NewMessages/mark
// - System/alert

View file

@ -2,14 +2,11 @@ import { Docked, OverlappingPanels, ShowIf } from "react-overlapping-panels";
import { Switch, Route, useLocation, Link } from "react-router-dom"; import { Switch, Route, useLocation, Link } from "react-router-dom";
import styled, { css } from "styled-components/macro"; import styled, { css } from "styled-components/macro";
import { useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import ContextMenus from "../lib/ContextMenus"; import ContextMenus from "../lib/ContextMenus";
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
import { useApplicationState } from "../mobx/State";
import { SIDEBAR_CHANNELS } from "../mobx/stores/Layout";
import Popovers from "../context/intermediate/Popovers"; import Popovers from "../context/intermediate/Popovers";
import Notifications from "../context/revoltjs/Notifications"; import Notifications from "../context/revoltjs/Notifications";
import StateMonitor from "../context/revoltjs/StateMonitor"; import StateMonitor from "../context/revoltjs/StateMonitor";
@ -18,6 +15,7 @@ import { Titlebar } from "../components/native/Titlebar";
import BottomNavigation from "../components/navigation/BottomNavigation"; import BottomNavigation from "../components/navigation/BottomNavigation";
import LeftSidebar from "../components/navigation/LeftSidebar"; import LeftSidebar from "../components/navigation/LeftSidebar";
import RightSidebar from "../components/navigation/RightSidebar"; import RightSidebar from "../components/navigation/RightSidebar";
import { useSystemAlert } from "../updateWorker";
import Open from "./Open"; import Open from "./Open";
import Channel from "./channels/Channel"; import Channel from "./channels/Channel";
import Developer from "./developer/Developer"; import Developer from "./developer/Developer";
@ -112,22 +110,35 @@ export default function App() {
path.startsWith("/invite") || path.startsWith("/invite") ||
path.includes("/settings"); path.includes("/settings");
const alert = useSystemAlert();
const [statusBar, setStatusBar] = useState(false); const [statusBar, setStatusBar] = useState(false);
useEffect(() => setStatusBar(true), [alert]);
return ( return (
<> <>
{statusBar && ( {alert && statusBar && (
<StatusBar> <StatusBar>
<div className="title">Partial outage: CDN</div> <div className="title">{alert.text}</div>
<div className="actions"> <div className="actions">
<Link to="/invite/Testers"> {alert.actions?.map((action) =>
<a> action.type === "internal" ? (
<div className="button">Updates</div> <Link to={action.href}>
</a> <div className="button">{action.text}</div>
</Link> </Link>
) : action.type === "external" ? (
<a
href={action.href}
target="_blank"
rel="noreferrer">
<div className="button">{action.text}</div>{" "}
</a>
) : null,
)}
{alert.dismissable !== false && (
<a onClick={() => setStatusBar(false)}> <a onClick={() => setStatusBar(false)}>
<div className="button">Dismiss</div> <div className="button">Dismiss</div>
</a> </a>
)}
</div> </div>
</StatusBar> </StatusBar>
)} )}

View file

@ -1,8 +1,11 @@
import isEqual from "lodash.isequal";
import semver from "semver"; import semver from "semver";
import { ulid } from "ulid"; import { ulid } from "ulid";
import { registerSW } from "virtual:pwa-register"; import { registerSW } from "virtual:pwa-register";
import { internalEmit } from "./lib/eventEmitter"; import { useEffect, useState } from "preact/hooks";
import { internalEmit, internalSubscribe } from "./lib/eventEmitter";
import { modalController } from "./context/modals"; import { modalController } from "./context/modals";
@ -33,15 +36,67 @@ export const updateSW = registerSW({
}, },
}); });
let currentPollRate: number;
let scheduledTask: number;
/**
* Schedule version checker
* @param poll_rate Set poll rate in milliseconds
*/
function schedule(poll_rate = INTERVAL_HOUR) {
if (poll_rate !== currentPollRate) {
currentPollRate = poll_rate;
clearInterval(scheduledTask);
scheduledTask = setInterval(
checkVersion,
poll_rate,
) as unknown as number;
}
}
let currentAlert: SystemAlert | undefined;
type SystemAlert = {
text: string;
dismissable?: boolean;
actions?: {
text: string;
type: "internal" | "external";
href: string;
}[];
};
/**
* Get the current system alert
*/
export function useSystemAlert() {
const [alert, setAlert] = useState(currentAlert);
useEffect(() => internalSubscribe("System", "alert", setAlert as any), []);
return alert;
}
/** /**
* Check whether the client is out of date * Check whether the client is out of date
*/ */
async function checkVersion() { async function checkVersion() {
const { version } = (await fetch("https://api.revolt.chat/release").then( const { version, poll_rate, alert } = (await fetch(
(res) => res.json(), "https://api.revolt.chat/release",
)) as { version: string }; ).then((res) => res.json())) as {
version: string;
poll_rate?: number;
alert?: SystemAlert;
};
if (!semver.satisfies(APP_VERSION, version) && APP_VERSION !== version) { // Re-schedule if necessary
schedule(poll_rate);
// Apply any active alerts
if (!isEqual(alert, currentAlert)) {
currentAlert = alert;
internalEmit("System", "alert", alert);
}
// Check if we need to update
if (version !== "0.5.3-7" && !semver.satisfies(APP_VERSION, version)) {
// Let the worker know we should immediately refresh // Let the worker know we should immediately refresh
forceUpdate = true; forceUpdate = true;
@ -59,6 +114,6 @@ async function checkVersion() {
if (import.meta.env.VITE_API_URL === "https://api.revolt.chat") { if (import.meta.env.VITE_API_URL === "https://api.revolt.chat") {
// Check for critical updates hourly // Check for critical updates hourly
schedule();
checkVersion(); checkVersion();
setInterval(checkVersion, INTERVAL_HOUR);
} }