From c980c707983de4e2893286c69d8147e5ee1f6c3d Mon Sep 17 00:00:00 2001 From: Ben Phelps Date: Mon, 26 Sep 2022 22:54:12 +0300 Subject: [PATCH] revalidate config changes, check on focus changes --- src/pages/api/hash.js | 27 ++++++++++++++++++++ src/pages/index.jsx | 44 ++++++++++++++++++++++++++++++++- src/utils/hooks/window-focus.js | 26 +++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/pages/api/hash.js create mode 100644 src/utils/hooks/window-focus.js diff --git a/src/pages/api/hash.js b/src/pages/api/hash.js new file mode 100644 index 000000000..c7408e486 --- /dev/null +++ b/src/pages/api/hash.js @@ -0,0 +1,27 @@ +import { join } from "path"; +import { createHash } from "crypto"; +import { readFileSync } from "fs"; + +import checkAndCopyConfig from "utils/config/config"; + +const configs = ["docker.yaml", "settings.yaml", "services.yaml", "bookmarks.yaml"]; + +function hash(buffer) { + const hashSum = createHash("sha256"); + hashSum.update(buffer); + return hashSum.digest("hex"); +} + +export default async function handler(req, res) { + const hashes = configs.map((config) => { + checkAndCopyConfig(config); + const configYaml = join(process.cwd(), "config", config); + return hash(readFileSync(configYaml, "utf8")); + }); + + const combinedHash = hash(hashes.join("")); + + res.send({ + hash: combinedHash, + }); +} diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 25179e80f..4faddd3e5 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -3,7 +3,7 @@ import useSWR, { SWRConfig } from "swr"; import Head from "next/head"; import dynamic from "next/dynamic"; import { useTranslation } from "next-i18next"; -import { useEffect, useContext } from "react"; +import { useEffect, useContext, useState } from "react"; import { BiError } from "react-icons/bi"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; @@ -17,6 +17,7 @@ import { ColorContext } from "utils/contexts/color"; import { ThemeContext } from "utils/contexts/theme"; import { SettingsContext } from "utils/contexts/settings"; import { bookmarksResponse, servicesResponse, widgetsResponse } from "utils/config/api-response"; +import useWindowFocus from "utils/hooks/window-focus"; const ThemeToggle = dynamic(() => import("components/toggles/theme"), { ssr: false, @@ -49,6 +50,7 @@ export async function getStaticProps() { "/api/services": services, "/api/bookmarks": bookmarks, "/api/widgets": widgets, + "/api/hash": false, }, ...(await serverSideTranslations(settings.language ?? "en")), }, @@ -67,7 +69,47 @@ export async function getStaticProps() { } export default function Index({ initialSettings, fallback }) { + const windowFocused = useWindowFocus(); + const [stale, setStale] = useState(false); const { data: errorsData } = useSWR("/api/validate"); + const { data: hashData, mutate: mutateHash } = useSWR("/api/hash"); + + useEffect(() => { + if (windowFocused) { + mutateHash(); + } + }, [windowFocused, mutateHash]); + + useEffect(() => { + if (hashData) { + if (typeof window !== "undefined") { + const previousHash = localStorage.getItem("hash"); + + if (!previousHash) { + localStorage.setItem("hash", hashData.hash); + } + + if (previousHash && previousHash !== hashData.hash) { + setStale(true); + localStorage.setItem("hash", hashData.hash); + + fetch("/api/revalidate").then((res) => { + if (res.ok) { + window.location.reload(); + } + }); + } + } + } + }, [hashData]); + + if (stale) { + return ( +
+
+
+ ); + } if (errorsData && errorsData.length > 0) { return ( diff --git a/src/utils/hooks/window-focus.js b/src/utils/hooks/window-focus.js new file mode 100644 index 000000000..3ad57ad02 --- /dev/null +++ b/src/utils/hooks/window-focus.js @@ -0,0 +1,26 @@ +import { useState, useEffect } from "react"; + +const hasFocus = () => typeof document !== "undefined" && document.hasFocus(); + +const useWindowFocus = () => { + const [focused, setFocused] = useState(hasFocus); + + useEffect(() => { + setFocused(hasFocus()); + + const onFocus = () => setFocused(true); + const onBlur = () => setFocused(false); + + window.addEventListener("focus", onFocus); + window.addEventListener("blur", onBlur); + + return () => { + window.removeEventListener("focus", onFocus); + window.removeEventListener("blur", onBlur); + }; + }, []); + + return focused; +}; + +export default useWindowFocus;