fix client imports to prevent multiple react context's

This commit is contained in:
dswbx
2025-11-25 16:16:10 +01:00
parent 5e5dc62304
commit 0cf1d86213
25 changed files with 59 additions and 49 deletions

View File

@@ -186,6 +186,8 @@ async function buildUiElements() {
outDir: "dist/ui/elements",
external: [
"ui/client",
"bknd",
/^bknd\/.*/,
"react",
"react-dom",
"react/jsx-runtime",

View File

@@ -9,7 +9,7 @@ import {
useState,
type ReactNode,
} from "react";
import { useApi } from "ui/client";
import { useApi } from "bknd/client";
import { type TSchemaActions, getSchemaActions } from "./schema/actions";
import { AppReduced } from "./utils/AppReduced";
import { Message } from "ui/components/display/Message";

View File

@@ -14,18 +14,20 @@ const ClientContext = createContext<BkndClientContext>(undefined!);
export type ClientProviderProps = {
children?: ReactNode;
baseUrl?: string;
api?: Api;
} & ApiOptions;
export const ClientProvider = ({
children,
host,
baseUrl: _baseUrl = host,
api: _api,
...props
}: ClientProviderProps) => {
const winCtx = useBkndWindowContext();
const _ctx = useClientContext();
let actualBaseUrl = _baseUrl ?? _ctx?.baseUrl ?? "";
let user: any = undefined;
let user: any;
if (winCtx) {
user = winCtx.user;
@@ -40,6 +42,7 @@ export const ClientProvider = ({
const apiProps = { user, ...props, host: actualBaseUrl };
const api = useMemo(
() =>
_api ??
new Api({
...apiProps,
verbose: isDebug(),
@@ -50,7 +53,7 @@ export const ClientProvider = ({
}
},
}),
[JSON.stringify(apiProps)],
[_api, JSON.stringify(apiProps)],
);
const [authState, setAuthState] = useState<Partial<AuthState> | undefined>(api.getAuthState());
@@ -64,6 +67,10 @@ export const ClientProvider = ({
export const useApi = (host?: ApiOptions["host"]): Api => {
const context = useContext(ClientContext);
if (!context) {
throw new Error("useApi must be used within a ClientProvider");
}
if (!context?.api || (host && host.length > 0 && host !== context.baseUrl)) {
return new Api({ host: host ?? "" });
}

View File

@@ -2,7 +2,7 @@ import type { Api } from "Api";
import { FetchPromise, type ModuleApi, type ResponseObject } from "modules/ModuleApi";
import useSWR, { type SWRConfiguration, useSWRConfig, type Middleware, type SWRHook } from "swr";
import useSWRInfinite from "swr/infinite";
import { useApi } from "ui/client";
import { useApi } from "../ClientProvider";
import { useState } from "react";
export const useApiQuery = <

View File

@@ -10,7 +10,7 @@ import type {
import { objectTransform, encodeSearch } from "bknd/utils";
import type { Insertable, Selectable, Updateable, Generated } from "kysely";
import useSWR, { type SWRConfiguration, type SWRResponse, mutate } from "swr";
import { type Api, useApi } from "ui/client";
import { type Api, useApi } from "bknd/client";
export class UseEntityApiError<Payload = any> extends Error {
constructor(

View File

@@ -4,6 +4,7 @@ export {
type ClientProviderProps,
useApi,
useBaseUrl,
useClientContext
} from "./ClientProvider";
export * from "./api/use-api";

View File

@@ -1,7 +1,6 @@
import type { AuthState } from "Api";
import type { AuthResponse } from "bknd";
import { useApi, useInvalidate } from "ui/client";
import { useClientContext } from "ui/client/ClientProvider";
import { useApi, useInvalidate, useClientContext } from "bknd/client";
type LoginData = {
email: string;

View File

@@ -1,9 +1,11 @@
import type { AppAuthSchema } from "auth/auth-schema";
import { useEffect, useState } from "react";
import { useApi } from "ui/client";
import { useApi } from "bknd/client";
type AuthStrategyData = Pick<AppAuthSchema, "strategies" | "basepath">;
export const useAuthStrategies = (options?: { baseUrl?: string }): Partial<AuthStrategyData> & {
export const useAuthStrategies = (options?: {
baseUrl?: string;
}): Partial<AuthStrategyData> & {
loading: boolean;
} => {
const [data, setData] = useState<AuthStrategyData>();

View File

@@ -14,7 +14,7 @@ import { isFileAccepted } from "bknd/utils";
import { type FileWithPath, useDropzone } from "./use-dropzone";
import { checkMaxReached } from "./helper";
import { DropzoneInner } from "./DropzoneInner";
import { createDropzoneStore } from "ui/elements/media/dropzone-state";
import { createDropzoneStore } from "./dropzone-state";
import { useStore } from "zustand";
export type FileState = {

View File

@@ -1,9 +1,8 @@
import type { Api } from "bknd/client";
import type { PrimaryFieldType, RepoQueryIn } from "bknd";
import type { MediaFieldSchema } from "media/AppMedia";
import type { TAppMediaConfig } from "media/media-schema";
import { useId, useEffect, useRef, useState } from "react";
import { useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "bknd/client";
import { type Api, useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "bknd/client";
import { useEvent } from "ui/hooks/use-event";
import { Dropzone, type DropzoneProps } from "./Dropzone";
import { mediaItemsToFileStates } from "./helper";
@@ -132,26 +131,24 @@ export function DropzoneContainer({
}
return (
<>
<Dropzone
key={key}
getUploadInfo={getUploadInfo}
handleDelete={handleDelete}
autoUpload
initialItems={_initialItems}
footer={
infinite &&
"setSize" in $q && (
<Footer
items={_initialItems.length}
length={placeholderLength}
onFirstVisible={() => $q.setSize($q.size + 1)}
/>
)
}
{...props}
/>
</>
<Dropzone
key={key}
getUploadInfo={getUploadInfo}
handleDelete={handleDelete}
autoUpload
initialItems={_initialItems}
footer={
infinite &&
"setSize" in $q && (
<Footer
items={_initialItems.length}
length={placeholderLength}
onFirstVisible={() => $q.setSize($q.size + 1)}
/>
)
}
{...props}
/>
);
}

View File

@@ -19,8 +19,8 @@ import {
} from "react-icons/tb";
import { Dropdown, type DropdownItem } from "ui/components/overlay/Dropdown";
import { IconButton } from "ui/components/buttons/IconButton";
import { formatNumber } from "core/utils";
import type { DropzoneRenderProps, FileState } from "ui/elements";
import { formatNumber } from "bknd/utils";
import type { DropzoneRenderProps, FileState } from "./Dropzone";
import { useDropzoneFileState, useDropzoneState } from "./Dropzone";
function handleUploadError(e: unknown) {

View File

@@ -10,7 +10,7 @@ import {
TbUser,
TbX,
} from "react-icons/tb";
import { useAuth, useBkndWindowContext } from "ui/client";
import { useAuth, useBkndWindowContext } from "bknd/client";
import { useBknd } from "ui/client/bknd";
import { useTheme } from "ui/client/use-theme";
import { Button } from "ui/components/buttons/Button";

View File

@@ -1,6 +1,6 @@
import type { ContextModalProps } from "@mantine/modals";
import { type ReactNode, useEffect, useMemo, useState } from "react";
import { useEntityQuery } from "ui/client";
import { useEntityQuery } from "bknd/client";
import { type FileState, Media } from "ui/elements";
import { autoFormatString, datetimeStringLocal, formatNumber } from "core/utils";
import { twMerge } from "tailwind-merge";

View File

@@ -1,4 +1,4 @@
import { useApi, useInvalidate } from "ui/client";
import { useApi, useInvalidate } from "bknd/client";
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
import { routes, useNavigate } from "ui/lib/routes";
import { bkndModals } from "ui/modals";

View File

@@ -4,7 +4,7 @@ import type { EntityData } from "bknd";
import type { RelationField } from "data/relations";
import { useEffect, useRef, useState } from "react";
import { TbEye } from "react-icons/tb";
import { useEntityQuery } from "ui/client";
import { useEntityQuery } from "bknd/client";
import { useBknd } from "ui/client/bknd";
import { Button } from "ui/components/buttons/Button";
import * as Formy from "ui/components/form/Formy";

View File

@@ -1,6 +1,6 @@
import clsx from "clsx";
import { TbArrowRight, TbCircle, TbCircleCheckFilled, TbFingerprint } from "react-icons/tb";
import { useApiQuery } from "ui/client";
import { useApiQuery } from "bknd/client";
import { useBknd } from "ui/client/bknd";
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
import { ButtonLink, type ButtonLinkProps } from "ui/components/buttons/Button";

View File

@@ -35,7 +35,7 @@ import { SegmentedControl, Tooltip } from "@mantine/core";
import { Popover } from "ui/components/overlay/Popover";
import { cn } from "ui/lib/utils";
import { JsonViewer } from "ui/components/code/JsonViewer";
import { mountOnce, useApiQuery } from "ui/client";
import { mountOnce, useApiQuery } from "bknd/client";
import { CodePreview } from "ui/components/code/CodePreview";
import type { JsonError } from "json-schema-library";
import { Alert } from "ui/components/display/Alert";

View File

@@ -3,7 +3,7 @@ import { ucFirst } from "bknd/utils";
import type { Entity, EntityData, EntityRelation } from "bknd";
import { Fragment, useState } from "react";
import { TbDots } from "react-icons/tb";
import { useApiQuery, useEntityQuery } from "ui/client";
import { useApiQuery, useEntityQuery } from "bknd/client";
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
import { Button } from "ui/components/buttons/Button";
import { IconButton } from "ui/components/buttons/IconButton";

View File

@@ -1,6 +1,6 @@
import type { EntityData } from "bknd";
import { useState } from "react";
import { useEntityMutate } from "ui/client";
import { useEntityMutate } from "bknd/client";
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
import { Button } from "ui/components/buttons/Button";
import { Message } from "ui/components/display/Message";

View File

@@ -2,7 +2,7 @@ import type { Entity } from "bknd";
import { repoQuery } from "data/server/query";
import { Fragment } from "react";
import { TbDots } from "react-icons/tb";
import { useApiQuery } from "ui/client";
import { useApiQuery } from "bknd/client";
import { useBknd } from "ui/client/bknd";
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
import { Button } from "ui/components/buttons/Button";

View File

@@ -11,7 +11,7 @@ import SettingsRoutes from "./settings";
import { FlashMessage } from "ui/modules/server/FlashMessage";
import { AuthRegister } from "ui/routes/auth/auth.register";
import { BkndModalsProvider } from "ui/modals";
import { useBkndWindowContext } from "ui/client";
import { useBkndWindowContext } from "bknd/client";
import ToolsRoutes from "./tools";
// @ts-ignore

View File

@@ -1,11 +1,12 @@
import { IconPhoto } from "@tabler/icons-react";
import { useBknd } from "ui/client/BkndProvider";
import { Empty } from "ui/components/display/Empty";
import { type FileState, Media } from "ui/elements";
import { useBrowserTitle } from "ui/hooks/use-browser-title";
import * as AppShell from "ui/layouts/AppShell/AppShell";
import { useLocation } from "wouter";
import { bkndModals } from "ui/modals";
import { DropzoneContainer } from "ui/elements/media/DropzoneContainer";
import type { FileState } from "ui/elements/media/Dropzone";
export function MediaIndex() {
const { config } = useBknd();
@@ -35,7 +36,7 @@ export function MediaIndex() {
return (
<AppShell.Scrollable>
<div className="flex flex-1 p-3">
<Media.Dropzone onClick={onClick} infinite query={{ sort: "-id" }} />
<DropzoneContainer onClick={onClick} query={{ sort: "-id" }} />
</div>
</AppShell.Scrollable>
);

View File

@@ -1,6 +1,6 @@
import { IconHome } from "@tabler/icons-react";
import { useEffect } from "react";
import { useAuth } from "ui/client";
import { useAuth } from "bknd/client";
import { useEffectOnce } from "ui/hooks/use-effect";
import { Empty } from "../components/display/Empty";
import { useBrowserTitle } from "../hooks/use-browser-title";

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { useApi, useApiQuery } from "ui/client";
import { useApi, useApiQuery } from "bknd/client";
import { Scrollable } from "ui/layouts/AppShell/AppShell";
function Bla() {

View File

@@ -35,7 +35,8 @@
"bknd/adapter": ["./src/adapter/index.ts"],
"bknd/adapter/*": ["./src/adapter/*/index.ts"],
"bknd/client": ["./src/ui/client/index.ts"],
"bknd/modes": ["./src/modes/index.ts"]
"bknd/modes": ["./src/modes/index.ts"],
"bknd/elements": ["./src/ui/elements/index.ts"]
}
},
"include": [