adding functions for loading indicator

This commit is contained in:
Miguel Ocampo Paniagua 2026-03-01 22:47:21 -08:00
parent 0708cfbc02
commit 48abcc33ff
11 changed files with 8059 additions and 829 deletions

View file

@ -0,0 +1,13 @@
export default function LoadingIndicator({ label = "Loading" }: { label?: string }) {
return (
<div
role="status"
aria-label={label}
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
>
// change the size of the indicater and color
<div className="h-12 w-12 animate-spin rounded-full border-4 border-white border-t-transparent" />
<span className="sr-only">{label}</span>
</div>
);
}

View file

@ -0,0 +1,61 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import React from "react";
vi.mock("api/api", () => ({
getCfg: vi.fn(),
}));
function deferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>((res) => {
resolve = res;
});
return { promise, resolve };
}
describe("AppProvider loading indicator", () => {
beforeEach(() => {vi.resetAllMocks();}
);
afterEach(() => {vi.unstubAllGlobals();});
it("Loading indicator while fetching", async () => {
const userDef = deferred<any>();
const cfgDef = deferred<any>();
vi.stubGlobal("fetch", vi.fn((input: any) => {
const url = String(input);
if (url.includes("/apis/web/v1/user/me")) {
return userDef.promise.then((data) => ({
ok: true,
json: async () => data,
})) as any;
}
throw new Error(`Unexpected fetch: ${url}`);
}));
const { getCfg } = await import("api/api");
(getCfg as any).mockReturnValue(cfgDef.promise);
const { AppProvider } = await import("./AppProvider");
render(
<AppProvider>
<div>APP CONTENT</div>
</AppProvider>
);
expect(screen.getByRole("status", { name: /loading/i })).toBeInTheDocument();
expect(screen.queryByText("APP CONTENT")).not.toBeInTheDocument();
userDef.resolve({ username: "test-user" });
cfgDef.resolve({ default_theme: "testing" });
expect(await screen.findByText("APP CONTENT")).toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByRole("status", { name: /loading/i })).not.toBeInTheDocument();
});
});
});

View file

@ -1,5 +1,6 @@
import { getCfg, type User } from "api/api";
import { createContext, useContext, useEffect, useState } from "react";
import LoadingIndicator from "~/components/LoadingIndicator";
interface AppContextType {
user: User | null | undefined;
@ -26,8 +27,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
const [defaultTheme, setDefaultTheme] = useState<string | undefined>(
undefined
);
const [configurableHomeActivity, setConfigurableHomeActivity] =
useState<boolean>(false);
const [configurableHomeActivity, setConfigurableHomeActivity] = useState<boolean>(false);
const [homeItems, setHomeItems] = useState<number>(0);
const setUsername = (value: string) => {
@ -53,14 +53,15 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
if (cfg.default_theme !== "") {
setDefaultTheme(cfg.default_theme);
} else {
setDefaultTheme("yuu");
setDefaultTheme("Testing");
}
});
}, []);
// Block rendering the app until config is loaded
if (user === undefined || defaultTheme === undefined) {
return null;
const isLoading = user === undefined || defaultTheme === undefined;
if (isLoading) {
return <LoadingIndicator label="Loading app" />;
}
const contextValue: AppContextType = {

6981
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,9 @@
"build": "react-router build",
"dev": "react-router dev",
"start": "react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc"
"typecheck": "react-router typegen && tsc",
"test": "vitest"
},
"dependencies": {
"@radix-ui/react-tabs": "^1.1.12",

1
client/test/setup.ts Normal file
View file

@ -0,0 +1 @@
import "@testing-library/jest-dom/vitest";

View file

@ -15,7 +15,8 @@
"rootDirs": [".", "./.react-router/types"],
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
"~/*": ["./app/*"],
"api/*": ["./api/*"]
},
"esModuleInterop": true,
"verbatimModuleSyntax": true,
@ -24,4 +25,4 @@
"skipLibCheck": true,
"strict": true
}
}
}

View file

@ -1,3 +1,4 @@
import path from "node:path";
import { reactRouter } from "@react-router/dev/vite";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
@ -7,7 +8,12 @@ import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'
const isDocker = process.env.BUILD_TARGET === 'docker';
export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths(), vanillaExtractPlugin()],
plugins: [
tailwindcss(),
reactRouter(),
tsconfigPaths({ projects: ["./tsconfig.json"] }),
vanillaExtractPlugin(),
],
server: {
proxy: {
'/apis': {
@ -24,7 +30,8 @@ export default defineConfig({
alias: {
...(isDocker
? { 'react-dom/server': 'react-dom/server.node' }
: {}),
},
},
: {}),
api: path.resolve(__dirname, "api"),
},
},
});

16
client/vitest.config.ts Normal file
View file

@ -0,0 +1,16 @@
import { defineConfig } from "vitest/config";
import tsconfigPaths from "vite-tsconfig-paths";
import path from "node:path";
export default defineConfig({
plugins: [tsconfigPaths({ projects: ["./tsconfig.json"] })],
resolve: {
alias: {
api: path.resolve(__dirname, "api"),
},
},
test: {
environment: "jsdom",
setupFiles: ["./test/setup.ts"],
},
});

File diff suppressed because it is too large Load diff

2
go.mod
View file

@ -1,6 +1,6 @@
module github.com/gabehf/koito
go 1.24.2
go 1.24
require (
github.com/go-chi/chi/v5 v5.2.1