feat!: use elysia js intead of koa				#18
		
		
	
					 5 changed files with 41 additions and 92 deletions
				
			
		| 
						 | 
				
			
			@ -11,18 +11,20 @@ interface NavItemProps {
 | 
			
		|||
    icon: React.ReactNode;
 | 
			
		||||
    to: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
    className?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function NavItem({
 | 
			
		||||
    icon,
 | 
			
		||||
    to,
 | 
			
		||||
    name
 | 
			
		||||
    name,
 | 
			
		||||
    className
 | 
			
		||||
}: NavItemProps) {
 | 
			
		||||
    return <Tooltip>
 | 
			
		||||
        <TooltipTrigger asChild>
 | 
			
		||||
            <Link
 | 
			
		||||
                href={to}
 | 
			
		||||
                className={buttonVariants({ variant: "ghost" })}
 | 
			
		||||
                className={buttonVariants({ variant: "ghost", className })}
 | 
			
		||||
            >
 | 
			
		||||
                {icon}
 | 
			
		||||
                <span className="sr-only">{name}</span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { atom, useAtomValue, setAtomValue, getAtomState } from "@/lib/atom";
 | 
			
		||||
import { useLayoutEffect } from "react";
 | 
			
		||||
import { atom, useAtomValue, useSetAtom } from "@/lib/atom";
 | 
			
		||||
import { useLayoutEffect, useRef } from "react";
 | 
			
		||||
 | 
			
		||||
const NavItems = atom<React.ReactNode>("NavItems", null);
 | 
			
		||||
const NavItems = atom<React.ReactNode>(null);
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line react-refresh/only-export-components
 | 
			
		||||
export function useNavItems() {
 | 
			
		||||
| 
						 | 
				
			
			@ -9,14 +9,19 @@ export function useNavItems() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export function PageNavItem({items, children}:{items: React.ReactNode, children: React.ReactNode}) {
 | 
			
		||||
    const currentNavItems = useAtomValue(NavItems);
 | 
			
		||||
    const setNavItems = useSetAtom(NavItems);
 | 
			
		||||
    const prevValueRef = useRef<React.ReactNode>(null);
 | 
			
		||||
    
 | 
			
		||||
    useLayoutEffect(() => {
 | 
			
		||||
        const prev = getAtomState(NavItems).value;
 | 
			
		||||
        const setter = setAtomValue(NavItems);
 | 
			
		||||
        setter(items);
 | 
			
		||||
        // Store current value before setting new one
 | 
			
		||||
        prevValueRef.current = currentNavItems;
 | 
			
		||||
        setNavItems(items);
 | 
			
		||||
        
 | 
			
		||||
        return () => {
 | 
			
		||||
            setter(prev);
 | 
			
		||||
            setNavItems(prevValueRef.current);
 | 
			
		||||
        };
 | 
			
		||||
    }, [items]);
 | 
			
		||||
    }, [items, currentNavItems, setNavItems]);
 | 
			
		||||
 | 
			
		||||
    return children;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,70 +1,2 @@
 | 
			
		|||
import { useEffect, useReducer, useState } from "react";
 | 
			
		||||
 | 
			
		||||
interface AtomState<T> {
 | 
			
		||||
    value: T;
 | 
			
		||||
    listeners: Set<() => void>;
 | 
			
		||||
}
 | 
			
		||||
interface Atom<T> {
 | 
			
		||||
    key: string;
 | 
			
		||||
    default: T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const atomStateMap = new WeakMap<Atom<unknown>, AtomState<unknown>>();
 | 
			
		||||
 | 
			
		||||
export function atom<T>(key: string, defaultVal: T): Atom<T> {
 | 
			
		||||
    return { key, default: defaultVal };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getAtomState<T>(atom: Atom<T>): AtomState<T> {
 | 
			
		||||
    let atomState = atomStateMap.get(atom);
 | 
			
		||||
    if (!atomState) {
 | 
			
		||||
        atomState = {
 | 
			
		||||
            value: atom.default,
 | 
			
		||||
            listeners: new Set(),
 | 
			
		||||
        };
 | 
			
		||||
        atomStateMap.set(atom, atomState);
 | 
			
		||||
    }
 | 
			
		||||
    return atomState as AtomState<T>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAtom<T>(atom: Atom<T>): [T, (val: T) => void] {
 | 
			
		||||
    const state = getAtomState(atom);
 | 
			
		||||
    const [, setState] = useState(state.value);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const listener = () => setState(state.value);
 | 
			
		||||
        state.listeners.add(listener);
 | 
			
		||||
        return () => {
 | 
			
		||||
            state.listeners.delete(listener);
 | 
			
		||||
        };
 | 
			
		||||
    }, [state]);
 | 
			
		||||
    return [
 | 
			
		||||
        state.value as T,
 | 
			
		||||
        (val: T) => {
 | 
			
		||||
            state.value = val;
 | 
			
		||||
            // biome-ignore lint/complexity/noForEach: forEach is used to call each listener
 | 
			
		||||
            state.listeners.forEach((listener) => listener());
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAtomValue<T>(atom: Atom<T>): T {
 | 
			
		||||
    const state = getAtomState(atom);
 | 
			
		||||
    const update = useReducer((x) => x + 1, 0)[1];
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const listener = () => update();
 | 
			
		||||
        state.listeners.add(listener);
 | 
			
		||||
        return () => {
 | 
			
		||||
            state.listeners.delete(listener);
 | 
			
		||||
        };
 | 
			
		||||
    }, [state, update]);
 | 
			
		||||
    return state.value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function setAtomValue<T>(atom: Atom<T>): (val: T) => void {
 | 
			
		||||
    const state = getAtomState(atom);
 | 
			
		||||
    return (val: T) => {
 | 
			
		||||
        state.value = val;
 | 
			
		||||
        // biome-ignore lint/complexity/noForEach: forEach is used to call each listener
 | 
			
		||||
        state.listeners.forEach((listener) => listener());
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
// Re-export jotai functions to maintain compatibility
 | 
			
		||||
export { atom, useAtom, useAtomValue, useSetAtom, useSetAtom as setAtomValue } from 'jotai';
 | 
			
		||||
| 
						 | 
				
			
			@ -146,12 +146,20 @@ export default function ComicPage({
 | 
			
		|||
 | 
			
		||||
    return (
 | 
			
		||||
        <PageNavItem items={<>
 | 
			
		||||
            <NavItem to={`/doc/${params.id}`} name="Back" icon={<ExitIcon />} />
 | 
			
		||||
            <NavItemButton name={isFullScreen ? "Exit Fullscreen" : "Enter Fullscreen"} icon={isFullScreen ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />} onClick={() => {
 | 
			
		||||
                toggleFullScreen();
 | 
			
		||||
            }} />
 | 
			
		||||
            <NavItem
 | 
			
		||||
                className="flex-1"
 | 
			
		||||
                to={`/doc/${params.id}`} name="Back" icon={<ExitIcon />} />
 | 
			
		||||
            <NavItemButton
 | 
			
		||||
                className="flex-1"
 | 
			
		||||
                name={isFullScreen ? "Exit Fullscreen" : "Enter Fullscreen"}
 | 
			
		||||
                icon={isFullScreen ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />}
 | 
			
		||||
                onClick={() => {
 | 
			
		||||
                    toggleFullScreen();
 | 
			
		||||
                }} />
 | 
			
		||||
            <Popover>
 | 
			
		||||
                <PopoverTrigger>
 | 
			
		||||
                <PopoverTrigger
 | 
			
		||||
                    className="flex-1"
 | 
			
		||||
                >
 | 
			
		||||
                    <span className="text-sm text-ellipsis" >{curPage + 1}/{data.additional.page as number}</span>
 | 
			
		||||
                </PopoverTrigger>
 | 
			
		||||
                <PopoverContent className="w-28">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
import { atom, useAtomValue, setAtomValue } from "../lib/atom.ts";
 | 
			
		||||
import { atom, useAtomValue } from "../lib/atom.ts";
 | 
			
		||||
import { createStore } from 'jotai';
 | 
			
		||||
import { LoginRequest } from "dbtype/mod.ts";
 | 
			
		||||
import {
 | 
			
		||||
	ApiError,
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +10,9 @@ import {
 | 
			
		|||
	resetPasswordService,
 | 
			
		||||
} from "./api.ts";
 | 
			
		||||
 | 
			
		||||
// Create a store for setting atom values outside components
 | 
			
		||||
const store = createStore();
 | 
			
		||||
 | 
			
		||||
let localObj: LoginResponse | null = null;
 | 
			
		||||
function getUserSessions() {
 | 
			
		||||
    if (localObj === null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +59,6 @@ export async function refresh() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export const doLogout = async () => {
 | 
			
		||||
	const setVal = setAtomValue(userLoginStateAtom);
 | 
			
		||||
	try {
 | 
			
		||||
		const res = await logoutService();
 | 
			
		||||
		localObj = {
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +67,7 @@ export const doLogout = async () => {
 | 
			
		|||
			permission: res.permission,
 | 
			
		||||
		};
 | 
			
		||||
		window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj));
 | 
			
		||||
		setVal(localObj);
 | 
			
		||||
		store.set(userLoginStateAtom, localObj);
 | 
			
		||||
		return {
 | 
			
		||||
			username: localObj.username,
 | 
			
		||||
			permission: localObj.permission,
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +77,7 @@ export const doLogout = async () => {
 | 
			
		|||
		// Even if logout fails, clear client-side session
 | 
			
		||||
		localObj = { accessExpired: 0, username: "", permission: [] };
 | 
			
		||||
		window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj));
 | 
			
		||||
		setVal(localObj);
 | 
			
		||||
		store.set(userLoginStateAtom, localObj);
 | 
			
		||||
		return {
 | 
			
		||||
			username: "",
 | 
			
		||||
			permission: [],
 | 
			
		||||
| 
						 | 
				
			
			@ -85,9 +88,8 @@ export const doLogout = async () => {
 | 
			
		|||
export const doLogin = async (userLoginInfo: LoginRequest): Promise<string | LoginResponse> => {
 | 
			
		||||
	try {
 | 
			
		||||
		const b = await loginService(userLoginInfo);
 | 
			
		||||
		const setVal = setAtomValue(userLoginStateAtom);
 | 
			
		||||
		localObj = b;
 | 
			
		||||
		setVal(b);
 | 
			
		||||
		store.set(userLoginStateAtom, b);
 | 
			
		||||
		window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj));
 | 
			
		||||
		return b;
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +125,7 @@ export async function getInitialValue() {
 | 
			
		|||
    return refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const userLoginStateAtom = atom("userLoginState", getUserSessions());
 | 
			
		||||
export const userLoginStateAtom = atom(getUserSessions());
 | 
			
		||||
 | 
			
		||||
export function useLogin() {
 | 
			
		||||
    const val = useAtomValue(userLoginStateAtom);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue