small improvements
This commit is contained in:
parent
3dfc773590
commit
a7a7eeac0c
4 changed files with 90 additions and 23 deletions
|
|
@ -15,7 +15,8 @@ import {
|
||||||
|
|
||||||
export function UserInfo() {
|
export function UserInfo() {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { party, members, isConnecting, isReconnecting } = useParty();
|
const { party, members, isConnecting, isReconnecting, resetParty } =
|
||||||
|
useParty();
|
||||||
return (
|
return (
|
||||||
<Item>
|
<Item>
|
||||||
<ItemMedia>
|
<ItemMedia>
|
||||||
|
|
@ -37,8 +38,15 @@ export function UserInfo() {
|
||||||
</ItemDescription>
|
</ItemDescription>
|
||||||
</ItemContent>
|
</ItemContent>
|
||||||
<ItemActions>
|
<ItemActions>
|
||||||
{party && members.length > 1 && (
|
{party && (
|
||||||
<Button onClick={() => client.api.party.leave.post()}>Leave</Button>
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
await client.api.party.leave.post();
|
||||||
|
resetParty();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Leave
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ItemActions>
|
</ItemActions>
|
||||||
</Item>
|
</Item>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,13 @@
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
createElement,
|
||||||
|
type ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import type {
|
import type {
|
||||||
PartySocketEvent,
|
PartySocketEvent,
|
||||||
PartyState,
|
PartyState,
|
||||||
|
|
@ -6,14 +15,34 @@ import type {
|
||||||
import { usePartySocket } from "./use-party-socket";
|
import { usePartySocket } from "./use-party-socket";
|
||||||
import { useUser } from "./user";
|
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(
|
function reducePartyState(
|
||||||
state: PartyState,
|
state: PartyState,
|
||||||
event: PartySocketEvent,
|
event: PartySocketEvent,
|
||||||
|
userId: string,
|
||||||
): PartyState {
|
): PartyState {
|
||||||
switch (event.type) {
|
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 };
|
return { party: event.party, members: event.members };
|
||||||
|
}
|
||||||
case "member_payload":
|
case "member_payload":
|
||||||
case "pong":
|
case "pong":
|
||||||
case "error":
|
case "error":
|
||||||
|
|
@ -28,16 +57,18 @@ function getApiUrl(): string | null {
|
||||||
return `${window.location.protocol}//${window.location.host}`;
|
return `${window.location.protocol}//${window.location.host}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useParty() {
|
export function PartyProvider({ children }: { children: ReactNode }) {
|
||||||
const { session } = useUser();
|
const { user } = useUser();
|
||||||
const [state, setState] = useState<PartyState>({
|
const userId = user?.id ?? null;
|
||||||
party: null,
|
const [state, setState] = useState<PartyState>(emptyPartyState);
|
||||||
members: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleMessage = useCallback((event: PartySocketEvent) => {
|
const handleMessage = useCallback(
|
||||||
setState((prev: PartyState) => reducePartyState(prev, event));
|
(event: PartySocketEvent) => {
|
||||||
}, []);
|
if (!userId) return;
|
||||||
|
setState((prev: PartyState) => reducePartyState(prev, event, userId));
|
||||||
|
},
|
||||||
|
[userId],
|
||||||
|
);
|
||||||
|
|
||||||
const apiUrl = useMemo(() => {
|
const apiUrl = useMemo(() => {
|
||||||
const url = getApiUrl();
|
const url = getApiUrl();
|
||||||
|
|
@ -45,13 +76,36 @@ export function useParty() {
|
||||||
return url;
|
return url;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const resetParty = useCallback(() => {
|
||||||
|
setState(emptyPartyState);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!userId) resetParty();
|
||||||
|
}, [resetParty, userId]);
|
||||||
|
|
||||||
const wsState = usePartySocket({
|
const wsState = usePartySocket({
|
||||||
apiUrl,
|
apiUrl: userId ? apiUrl : null,
|
||||||
onMessage: session ? handleMessage : null,
|
onMessage: userId ? handleMessage : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
const value = useMemo(
|
||||||
...state,
|
() => ({
|
||||||
...wsState,
|
...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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import { treaty } from "@elysiajs/eden";
|
import { treaty } from "@elysiajs/eden";
|
||||||
import type { App } from "../../../api/src/index";
|
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",
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { PartyProvider } from "#/hooks/use-party";
|
||||||
import type { AuthSession } from "#/lib/auth.serverfn";
|
import type { AuthSession } from "#/lib/auth.serverfn";
|
||||||
import { fetchSession, sessionQueryKey } from "#/lib/auth-client";
|
import { fetchSession, sessionQueryKey } from "#/lib/auth-client";
|
||||||
import { client } from "#/lib/eden";
|
import { client } from "#/lib/eden";
|
||||||
|
|
@ -131,7 +132,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
<HeadContent />
|
<HeadContent />
|
||||||
</head>
|
</head>
|
||||||
<body className="font-sans antialiased wrap-anywhere dark">
|
<body className="font-sans antialiased wrap-anywhere dark">
|
||||||
{children}
|
<PartyProvider>{children}</PartyProvider>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<TanStackDevtools
|
<TanStackDevtools
|
||||||
config={{
|
config={{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue