feat: check IndexedDB support (#1630)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Tue, 23 Jan 2024 13:06:55 +0000 (00:06 +1100)
committerGitHub <noreply@github.com>
Tue, 23 Jan 2024 13:06:55 +0000 (18:36 +0530)
* check indexed db support and display message

* fix typo

src/app/pages/App.tsx
src/app/pages/FeatureCheck.tsx [new file with mode: 0644]
src/app/utils/featureCheck.ts [new file with mode: 0644]

index 6cefe999cb313cfb5d2a351604b11636ae690bdc..62c173f91a250a632ea75b379a19e5f22ad9d81e 100644 (file)
@@ -17,6 +17,7 @@ import { isAuthenticated } from '../../client/state/auth';
 import Client from '../templates/client/Client';
 import { getLoginPath } from './pathUtils';
 import { ConfigConfigError, ConfigConfigLoading } from './ConfigConfig';
+import { FeatureCheck } from './FeatureCheck';
 
 const createRouter = (clientConfig: ClientConfig) => {
   const { hashRouter } = clientConfig;
@@ -62,20 +63,22 @@ const createRouter = (clientConfig: ClientConfig) => {
 // TODO: app crash boundary
 function App() {
   return (
-    <ClientConfigLoader
-      fallback={() => <ConfigConfigLoading />}
-      error={(err, retry, ignore) => (
-        <ConfigConfigError error={err} retry={retry} ignore={ignore} />
-      )}
-    >
-      {(clientConfig) => (
-        <ClientConfigProvider value={clientConfig}>
-          <JotaiProvider>
-            <RouterProvider router={createRouter(clientConfig)} />
-          </JotaiProvider>
-        </ClientConfigProvider>
-      )}
-    </ClientConfigLoader>
+    <FeatureCheck>
+      <ClientConfigLoader
+        fallback={() => <ConfigConfigLoading />}
+        error={(err, retry, ignore) => (
+          <ConfigConfigError error={err} retry={retry} ignore={ignore} />
+        )}
+      >
+        {(clientConfig) => (
+          <ClientConfigProvider value={clientConfig}>
+            <JotaiProvider>
+              <RouterProvider router={createRouter(clientConfig)} />
+            </JotaiProvider>
+          </ClientConfigProvider>
+        )}
+      </ClientConfigLoader>
+    </FeatureCheck>
   );
 }
 
diff --git a/src/app/pages/FeatureCheck.tsx b/src/app/pages/FeatureCheck.tsx
new file mode 100644 (file)
index 0000000..abb366a
--- /dev/null
@@ -0,0 +1,42 @@
+import React, { ReactNode, useEffect } from 'react';
+import { Box, Dialog, Text, config } from 'folds';
+import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback';
+import { checkIndexedDBSupport } from '../utils/featureCheck';
+import { SplashScreen } from '../components/splash-screen';
+
+export function FeatureCheck({ children }: { children: ReactNode }) {
+  const [idbSupportState, checkIDBSupport] = useAsyncCallback(checkIndexedDBSupport);
+
+  useEffect(() => {
+    checkIDBSupport();
+  }, [checkIDBSupport]);
+
+  if (idbSupportState.status === AsyncStatus.Success && idbSupportState.data === false) {
+    return (
+      <SplashScreen>
+        <Box grow="Yes" alignItems="Center" justifyContent="Center">
+          <Dialog>
+            <Box style={{ padding: config.space.S400 }} direction="Column" gap="400">
+              <Text>Missing Browser Feature</Text>
+              <Text size="T300" priority="400">
+                No IndexedDB support found. This application requires IndexedDB to store session
+                data locally. Please make sure your browser support IndexedDB and have it enabled.
+              </Text>
+              <Text size="T200">
+                <a
+                  href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API"
+                  rel="noreferrer noopener"
+                  target="_blank"
+                >
+                  What is IndexedDB?
+                </a>
+              </Text>
+            </Box>
+          </Dialog>
+        </Box>
+      </SplashScreen>
+    );
+  }
+
+  return children;
+}
diff --git a/src/app/utils/featureCheck.ts b/src/app/utils/featureCheck.ts
new file mode 100644 (file)
index 0000000..a9474c3
--- /dev/null
@@ -0,0 +1,21 @@
+export const checkIndexedDBSupport = async (): Promise<boolean> => {
+  const ts = new Date().getTime();
+  const dbName = `checkIndexedDBSupport-${ts}`;
+  return new Promise((resolve) => {
+    let db;
+    try {
+      db = indexedDB.open(dbName);
+    } catch {
+      resolve(false);
+      return;
+    }
+    db.onsuccess = () => {
+      resolve(true);
+      indexedDB.deleteDatabase(dbName);
+    };
+    db.onerror = () => {
+      resolve(false);
+      indexedDB.deleteDatabase(dbName);
+    };
+  });
+};