mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-26 13:17:15 -05:00
63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
import { type FetcherWithComponents, useFetcher } from "react-router";
|
|
import { holdRevalidationsDuring } from "~/features/chat/revalidation-scope";
|
|
import {
|
|
type ActionsOf,
|
|
type FieldsOf,
|
|
serializeFieldValue,
|
|
} from "~/utils/action-schemas";
|
|
import type { AnySchema } from "~/utils/schema";
|
|
|
|
interface UseActionSubmitOptions {
|
|
/** Route to submit to. Defaults to the current route. */
|
|
action?: string;
|
|
/** Defaults to "application/x-www-form-urlencoded". */
|
|
encType?: "application/x-www-form-urlencoded" | "application/json";
|
|
/** Fetcher to submit with, e.g. to share submitting state. Defaults to own fetcher. */
|
|
fetcher?: FetcherWithComponents<unknown>;
|
|
}
|
|
|
|
/**
|
|
* Programmatic `<ActionButton>`: submits an `_action` mutation type checked against the route's schema,
|
|
* e.g. `submit("DELETE_FRIEND", { friendshipId })`.
|
|
*/
|
|
export function useActionSubmit<TSchema extends AnySchema>(
|
|
_schema: TSchema,
|
|
opts?: UseActionSubmitOptions,
|
|
) {
|
|
const ownFetcher = useFetcher();
|
|
const fetcher = opts?.fetcher ?? ownFetcher;
|
|
|
|
const submit = <const TAction extends ActionsOf<TSchema>>(
|
|
action: TAction,
|
|
// biome-ignore lint/complexity/noBannedTypes: {} models "branch with no extra fields"
|
|
...rest: {} extends FieldsOf<TSchema, TAction>
|
|
? [fields?: FieldsOf<TSchema, TAction>]
|
|
: [fields: FieldsOf<TSchema, TAction>]
|
|
) => {
|
|
const fields = (rest[0] ?? {}) as Record<string, unknown>;
|
|
|
|
if (opts?.encType === "application/json") {
|
|
void holdRevalidationsDuring(() =>
|
|
fetcher.submit(
|
|
{ _action: action, ...fields } as Parameters<
|
|
typeof fetcher.submit
|
|
>[0],
|
|
{ method: "post", action: opts?.action, encType: "application/json" },
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
const payload: Record<string, string> = { _action: action };
|
|
for (const [name, value] of Object.entries(fields)) {
|
|
if (value === undefined || value === null) continue;
|
|
payload[name] = serializeFieldValue(value);
|
|
}
|
|
void holdRevalidationsDuring(() =>
|
|
fetcher.submit(payload, { method: "post", action: opts?.action }),
|
|
);
|
|
};
|
|
|
|
return { submit, fetcher, state: fetcher.state };
|
|
}
|