소스 검색

revalidate config changes, check on focus changes

Ben Phelps 2 년 전
부모
커밋
c980c70798
3개의 변경된 파일96개의 추가작업 그리고 1개의 파일을 삭제
  1. 27 0
      src/pages/api/hash.js
  2. 43 1
      src/pages/index.jsx
  3. 26 0
      src/utils/hooks/window-focus.js

+ 27 - 0
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,
+  });
+}

+ 43 - 1
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 (
+      <div className="flex items-center justify-center h-screen">
+        <div className="w-24 h-24 border-2 border-theme-400 border-solid rounded-full animate-spin border-t-transparent" />
+      </div>
+    );
+  }
 
   if (errorsData && errorsData.length > 0) {
     return (

+ 26 - 0
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;