diff --git a/package.json b/package.json index 3e24f4d0..841b2b79 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.5.3-7", + "version": "1.0.0", "scripts": { "dev": "node scripts/setup_assets.js --check && vite", "pull": "node scripts/setup_assets.js", diff --git a/src/lib/eventEmitter.ts b/src/lib/eventEmitter.ts index 1ad13148..c54460a9 100644 --- a/src/lib/eventEmitter.ts +++ b/src/lib/eventEmitter.ts @@ -31,3 +31,4 @@ export function internalEmit(ns: string, event: string, ...args: unknown[]) { // - PWA/update // - NewMessages/hide // - NewMessages/mark +// - System/alert diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx index 82711ef6..76535ad3 100644 --- a/src/pages/RevoltApp.tsx +++ b/src/pages/RevoltApp.tsx @@ -2,14 +2,11 @@ import { Docked, OverlappingPanels, ShowIf } from "react-overlapping-panels"; import { Switch, Route, useLocation, Link } from "react-router-dom"; import styled, { css } from "styled-components/macro"; -import { useState } from "preact/hooks"; +import { useEffect, useState } from "preact/hooks"; import ContextMenus from "../lib/ContextMenus"; import { isTouchscreenDevice } from "../lib/isTouchscreenDevice"; -import { useApplicationState } from "../mobx/State"; -import { SIDEBAR_CHANNELS } from "../mobx/stores/Layout"; - import Popovers from "../context/intermediate/Popovers"; import Notifications from "../context/revoltjs/Notifications"; import StateMonitor from "../context/revoltjs/StateMonitor"; @@ -18,6 +15,7 @@ import { Titlebar } from "../components/native/Titlebar"; import BottomNavigation from "../components/navigation/BottomNavigation"; import LeftSidebar from "../components/navigation/LeftSidebar"; import RightSidebar from "../components/navigation/RightSidebar"; +import { useSystemAlert } from "../updateWorker"; import Open from "./Open"; import Channel from "./channels/Channel"; import Developer from "./developer/Developer"; @@ -112,22 +110,35 @@ export default function App() { path.startsWith("/invite") || path.includes("/settings"); + const alert = useSystemAlert(); const [statusBar, setStatusBar] = useState(false); + useEffect(() => setStatusBar(true), [alert]); return ( <> - {statusBar && ( + {alert && statusBar && ( -
Partial outage: CDN
+
{alert.text}
- - -
Updates
+ {alert.actions?.map((action) => + action.type === "internal" ? ( + +
{action.text}
+ + ) : action.type === "external" ? ( +
+
{action.text}
{" "} +
+ ) : null, + )} + {alert.dismissable !== false && ( + setStatusBar(false)}> +
Dismiss
- - setStatusBar(false)}> -
Dismiss
-
+ )}
)} diff --git a/src/updateWorker.ts b/src/updateWorker.ts index b86d52e3..770a2a90 100644 --- a/src/updateWorker.ts +++ b/src/updateWorker.ts @@ -1,8 +1,11 @@ +import isEqual from "lodash.isequal"; import semver from "semver"; import { ulid } from "ulid"; 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"; @@ -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 */ async function checkVersion() { - const { version } = (await fetch("https://api.revolt.chat/release").then( - (res) => res.json(), - )) as { version: string }; + const { version, poll_rate, alert } = (await fetch( + "https://api.revolt.chat/release", + ).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 forceUpdate = true; @@ -59,6 +114,6 @@ async function checkVersion() { if (import.meta.env.VITE_API_URL === "https://api.revolt.chat") { // Check for critical updates hourly + schedule(); checkVersion(); - setInterval(checkVersion, INTERVAL_HOUR); }