Squash merge feature/library-reorganization
This commit is contained in:
11
frontend/src/hooks/useSession.ts
Normal file
11
frontend/src/hooks/useSession.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { api } from "../api/client";
|
||||
|
||||
export function useSession() {
|
||||
return useQuery({
|
||||
queryKey: ["session"],
|
||||
queryFn: api.getSession,
|
||||
});
|
||||
}
|
||||
|
||||
135
frontend/src/hooks/useWalkupContext.tsx
Normal file
135
frontend/src/hooks/useWalkupContext.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { api } from "../api/client";
|
||||
import type { TeamSnapEvent, TeamSnapMember } from "../api/types";
|
||||
import { queryClient } from "../lib/queryClient";
|
||||
import { findCurrentPlayer, findNextGame, sortGames } from "../lib/teamsnapHelpers";
|
||||
import { teamsnapClient } from "../lib/teamsnap";
|
||||
import { useSession } from "./useSession";
|
||||
|
||||
const TEAM_STORAGE_KEY = "walkup.selectedTeamId";
|
||||
|
||||
function readStoredTeamId(): string {
|
||||
if (typeof window === "undefined") {
|
||||
return "";
|
||||
}
|
||||
return window.localStorage.getItem(TEAM_STORAGE_KEY) ?? "";
|
||||
}
|
||||
|
||||
type WalkupContextValue = ReturnType<typeof useBuildWalkupContext>;
|
||||
|
||||
const WalkupContext = createContext<WalkupContextValue | null>(null);
|
||||
|
||||
function useBuildWalkupContext() {
|
||||
const sessionQuery = useSession();
|
||||
const isTeamSnap = sessionQuery.data?.authenticated === true && sessionQuery.data?.provider === "teamsnap";
|
||||
const [selectedTeamId, setSelectedTeamId] = useState(readStoredTeamId);
|
||||
const teamsQuery = useQuery({
|
||||
queryKey: ["teamsnap", "teams"],
|
||||
queryFn: () => teamsnapClient.loadTeams(),
|
||||
enabled: isTeamSnap,
|
||||
});
|
||||
|
||||
const teams = teamsQuery.data ?? [];
|
||||
const selectedTeam = teams.find((team) => String(team.id) === selectedTeamId) ?? null;
|
||||
const resolvedTeamId = selectedTeam ? String(selectedTeam.id) : "";
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTeamId && !selectedTeam && teams.length) {
|
||||
setSelectedTeamId("");
|
||||
window.localStorage.removeItem(TEAM_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedTeamId) {
|
||||
window.localStorage.removeItem(TEAM_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(TEAM_STORAGE_KEY, selectedTeamId);
|
||||
}, [resolvedTeamId, selectedTeam, selectedTeamId, teams.length]);
|
||||
|
||||
const membersQuery = useQuery({
|
||||
queryKey: ["teamsnap", "members", resolvedTeamId],
|
||||
queryFn: () => teamsnapClient.loadMembers(resolvedTeamId),
|
||||
enabled: isTeamSnap && Boolean(resolvedTeamId),
|
||||
});
|
||||
const eventsQuery = useQuery({
|
||||
queryKey: ["teamsnap", "events", resolvedTeamId],
|
||||
queryFn: () => teamsnapClient.loadEvents(resolvedTeamId),
|
||||
enabled: isTeamSnap && Boolean(resolvedTeamId),
|
||||
});
|
||||
|
||||
const members: TeamSnapMember[] = membersQuery.data ?? [];
|
||||
const games: TeamSnapEvent[] = sortGames(eventsQuery.data ?? []);
|
||||
const currentPlayer = findCurrentPlayer(sessionQuery.data?.external_user_id, members);
|
||||
const nextGame = findNextGame(games);
|
||||
const currentPlayerId =
|
||||
sessionQuery.data?.external_team_id === selectedTeamId && sessionQuery.data?.external_player_id
|
||||
? String(sessionQuery.data.external_player_id)
|
||||
: currentPlayer
|
||||
? String(currentPlayer.id)
|
||||
: "";
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTeamSnap || !resolvedTeamId || !currentPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPlayerId = String(currentPlayer.id);
|
||||
if (
|
||||
sessionQuery.data?.external_team_id === selectedTeamId &&
|
||||
sessionQuery.data?.external_player_id === selectedPlayerId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
void api
|
||||
.updateWalkupSessionSelection({
|
||||
external_team_id: resolvedTeamId,
|
||||
external_player_id: selectedPlayerId,
|
||||
})
|
||||
.then(async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["session"] });
|
||||
})
|
||||
.catch(() => {
|
||||
// Keep the UI working even if the session cache update fails.
|
||||
});
|
||||
}, [currentPlayer, isTeamSnap, resolvedTeamId, selectedTeamId, sessionQuery.data?.external_player_id, sessionQuery.data?.external_team_id]);
|
||||
|
||||
function selectTeam(teamId: string) {
|
||||
setSelectedTeamId(teamId);
|
||||
}
|
||||
|
||||
return {
|
||||
isTeamSnap,
|
||||
sessionQuery,
|
||||
teamsQuery,
|
||||
selectedTeam,
|
||||
selectedTeamId,
|
||||
hasSelectedTeam: Boolean(resolvedTeamId),
|
||||
selectTeam,
|
||||
membersQuery,
|
||||
members,
|
||||
currentPlayer,
|
||||
currentPlayerId,
|
||||
eventsQuery,
|
||||
games,
|
||||
nextGame,
|
||||
};
|
||||
}
|
||||
|
||||
export function WalkupProvider({ children }: { children: ReactNode }) {
|
||||
const value = useBuildWalkupContext();
|
||||
const memoizedValue = useMemo(() => value, [value]);
|
||||
return <WalkupContext.Provider value={memoizedValue}>{children}</WalkupContext.Provider>;
|
||||
}
|
||||
|
||||
export function useWalkupContext() {
|
||||
const value = useContext(WalkupContext);
|
||||
if (!value) {
|
||||
throw new Error("useWalkupContext must be used within WalkupProvider");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user