small improvements

This commit is contained in:
Daniel Bulant 2026-05-12 23:36:42 +02:00
parent 3dfc773590
commit a7a7eeac0c
No known key found for this signature in database
4 changed files with 90 additions and 23 deletions

View file

@ -15,7 +15,8 @@ import {
export function UserInfo() {
const { user } = useUser();
const { party, members, isConnecting, isReconnecting } = useParty();
const { party, members, isConnecting, isReconnecting, resetParty } =
useParty();
return (
<Item>
<ItemMedia>
@ -37,8 +38,15 @@ export function UserInfo() {
</ItemDescription>
</ItemContent>
<ItemActions>
{party && members.length > 1 && (
<Button onClick={() => client.api.party.leave.post()}>Leave</Button>
{party && (
<Button
onClick={async () => {
await client.api.party.leave.post();
resetParty();
}}
>
Leave
</Button>
)}
</ItemActions>
</Item>

View file

@ -1,4 +1,13 @@
import { useCallback, useMemo, useState } from "react";
import {
createContext,
createElement,
type ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import type {
PartySocketEvent,
PartyState,
@ -6,14 +15,34 @@ import type {
import { usePartySocket } from "./use-party-socket";
import { useUser } from "./user";
const emptyPartyState: PartyState = {
party: null,
members: [],
};
type PartyContextValue = PartyState & {
connectionState: "disconnected" | "connecting" | "connected" | "reconnecting";
isConnected: boolean;
isConnecting: boolean;
isReconnecting: boolean;
setPartyState: (state: PartyState) => void;
resetParty: () => void;
};
const PartyContext = createContext<PartyContextValue | null>(null);
function reducePartyState(
state: PartyState,
event: PartySocketEvent,
userId: string,
): PartyState {
switch (event.type) {
case "snapshot":
case "party_status":
case "party_status": {
if (!event.party) return emptyPartyState;
const isMember = event.members.some((member) => member.userId === userId);
if (!isMember) return emptyPartyState;
return { party: event.party, members: event.members };
}
case "member_payload":
case "pong":
case "error":
@ -28,16 +57,18 @@ function getApiUrl(): string | null {
return `${window.location.protocol}//${window.location.host}`;
}
export function useParty() {
const { session } = useUser();
const [state, setState] = useState<PartyState>({
party: null,
members: [],
});
export function PartyProvider({ children }: { children: ReactNode }) {
const { user } = useUser();
const userId = user?.id ?? null;
const [state, setState] = useState<PartyState>(emptyPartyState);
const handleMessage = useCallback((event: PartySocketEvent) => {
setState((prev: PartyState) => reducePartyState(prev, event));
}, []);
const handleMessage = useCallback(
(event: PartySocketEvent) => {
if (!userId) return;
setState((prev: PartyState) => reducePartyState(prev, event, userId));
},
[userId],
);
const apiUrl = useMemo(() => {
const url = getApiUrl();
@ -45,13 +76,36 @@ export function useParty() {
return url;
}, []);
const resetParty = useCallback(() => {
setState(emptyPartyState);
}, []);
useEffect(() => {
if (!userId) resetParty();
}, [resetParty, userId]);
const wsState = usePartySocket({
apiUrl,
onMessage: session ? handleMessage : null,
apiUrl: userId ? apiUrl : null,
onMessage: userId ? handleMessage : null,
});
return {
...state,
...wsState,
};
const value = useMemo(
() => ({
...state,
...wsState,
setPartyState: setState,
resetParty,
}),
[state, wsState, resetParty],
);
return createElement(PartyContext.Provider, { value }, children);
}
export function useParty() {
const context = useContext(PartyContext);
if (!context) {
throw new Error("useParty must be used within PartyProvider");
}
return context;
}

View file

@ -1,4 +1,8 @@
import { treaty } from "@elysiajs/eden";
import type { App } from "../../../api/src/index";
export const client = treaty<App>("aura.rpi1.danbulant.cloud", {});
// export const client = treaty<App>("aura.rpi1.danbulant.cloud", {});
export const client = treaty<App>(
process.env.VITE_BETTER_AUTH_URL || "127.0.0.1:3000",
{},
);

View file

@ -10,6 +10,7 @@ import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
import type * as React from "react";
import { useEffect } from "react";
import { toast } from "sonner";
import { PartyProvider } from "#/hooks/use-party";
import type { AuthSession } from "#/lib/auth.serverfn";
import { fetchSession, sessionQueryKey } from "#/lib/auth-client";
import { client } from "#/lib/eden";
@ -131,7 +132,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
<HeadContent />
</head>
<body className="font-sans antialiased wrap-anywhere dark">
{children}
<PartyProvider>{children}</PartyProvider>
<Toaster />
<TanStackDevtools
config={{