diff --git a/packages/profile/src/components/context/data.tsx b/packages/profile/src/components/context/data.tsx new file mode 100644 index 000000000..f4deaf0cf --- /dev/null +++ b/packages/profile/src/components/context/data.tsx @@ -0,0 +1,32 @@ +import { createContext, useState, ReactNode } from "react"; +import { useAchievements } from "@/hooks/achievements"; + +type DataContextType = { + trophies: ReturnType; + setAccountAddress: (address: string | undefined) => void; +}; + +const initialState: DataContextType = { + trophies: { + achievements: [], + players: [], + isLoading: false, + }, + setAccountAddress: () => {}, +}; + +export const DataContext = createContext(initialState); + +export function DataProvider({ children }: { children: ReactNode }) { + const [accountAddress, setAccountAddress] = useState( + undefined, + ); + + const trophies = useAchievements(accountAddress); + + return ( + + {children} + + ); +} diff --git a/packages/profile/src/components/context/index.tsx b/packages/profile/src/components/context/index.tsx index b51cc7165..0ff06f971 100644 --- a/packages/profile/src/components/context/index.tsx +++ b/packages/profile/src/components/context/index.tsx @@ -1,3 +1,4 @@ export { ConnectionContext } from "./connection"; +export { DataContext } from "./data"; export { ThemeContext } from "./theme"; export { Provider } from "./provider"; diff --git a/packages/profile/src/components/context/provider.tsx b/packages/profile/src/components/context/provider.tsx index ca4dbdfc9..86baaa621 100644 --- a/packages/profile/src/components/context/provider.tsx +++ b/packages/profile/src/components/context/provider.tsx @@ -5,6 +5,7 @@ import { ConnectionProvider } from "./connection"; import { BrowserRouter } from "react-router-dom"; import { CartridgeAPIProvider } from "@cartridge/utils/api/cartridge"; import { IndexerAPIProvider } from "@cartridge/utils/api/indexer"; +import { DataProvider } from "./data"; export function Provider({ children }: PropsWithChildren) { const queryClient = new QueryClient(); @@ -17,7 +18,9 @@ export function Provider({ children }: PropsWithChildren) { > - {children} + + {children} + diff --git a/packages/profile/src/components/trophies/achievements.tsx b/packages/profile/src/components/trophies/achievements.tsx index e988f715f..774a3d4d8 100644 --- a/packages/profile/src/components/trophies/achievements.tsx +++ b/packages/profile/src/components/trophies/achievements.tsx @@ -269,7 +269,7 @@ function Page({ const handleMouseLeave = useCallback(() => { setHover(false); - }, [highlighted]); + }, []); return (
(); const { username } = useUsername({ address: address || self || "" }); - const { achievements, players, isLoading } = useAchievements({ - namespace: namespace ?? "", - address: address || self || "", - }); + const [activeTab, setActiveTab] = useState<"trophies" | "leaderboard">( "trophies", ); @@ -54,6 +54,10 @@ export function Trophies() { return !address || address === self; }, [address, self]); + useEffect(() => { + setAccountAddress(address || self || ""); + }, [address, self, setAccountAddress]); + return ( - {achievements.map((achievement, index) => ( - + {achievements.map((achievement) => ( + ))} {Array.from({ length: 3 - achievements.length }).map((_, index) => ( diff --git a/packages/profile/src/hooks/achievements.ts b/packages/profile/src/hooks/achievements.ts index fcabc337b..cef1edeb5 100644 --- a/packages/profile/src/hooks/achievements.ts +++ b/packages/profile/src/hooks/achievements.ts @@ -1,8 +1,10 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { TROPHY, PROGRESS } from "@/constants"; import { useEvents } from "./events"; import { Trophy, Progress } from "@/models"; import { AchievementTask } from "@/components/trophies/achievement"; +import { useConnection } from "./context"; +import { useAccount } from "./account"; // Number of events to fetch at a time, could be increased if needed const LIMIT = 1000; @@ -37,27 +39,28 @@ export interface Player { timestamp: number; } -export function useAchievements({ - namespace, - address, -}: { - namespace: string; - address: string; -}) { +export function useAchievements(accountAddress?: string) { + const { namespace } = useConnection(); + const { address } = useAccount(); + const [isLoading, setIsLoading] = useState(true); const [achievements, setAchievements] = useState([]); const [players, setPlayers] = useState([]); + const currentAddress = useMemo(() => { + return accountAddress || address; + }, [accountAddress, address]); + const { events: trophies, isFetching: isFetchingTrophiess } = useEvents({ - namespace, + namespace: namespace || "", name: TROPHY, limit: LIMIT, parse: Trophy.parse, }); const { events: progresses, isFetching: isFetchingProgresses } = useEvents({ - namespace, + namespace: namespace || "", name: PROGRESS, limit: LIMIT, parse: Progress.parse, @@ -69,7 +72,7 @@ export function useAchievements({ isFetchingTrophiess || isFetchingProgresses || !trophies.length || - !address + !currentAddress ) return; @@ -136,7 +139,7 @@ export function useAchievements({ trophy.tasks.forEach((task) => { let count = 0; let completion = false; - counters[address]?.[task.id] + counters[currentAddress]?.[task.id] ?.sort((a, b) => a.timestamp - b.timestamp) .forEach( ({ @@ -183,7 +186,7 @@ export function useAchievements({ // Update loading state setIsLoading(false); }, [ - address, + currentAddress, trophies, progresses, isFetchingTrophiess, diff --git a/packages/profile/src/hooks/context.ts b/packages/profile/src/hooks/context.ts index a6d69bffd..ea9576d20 100644 --- a/packages/profile/src/hooks/context.ts +++ b/packages/profile/src/hooks/context.ts @@ -1,5 +1,9 @@ import { useContext } from "react"; -import { ThemeContext, ConnectionContext } from "@/components/context"; +import { + ThemeContext, + ConnectionContext, + DataContext, +} from "@/components/context"; export function useTheme() { return useContext(ThemeContext); @@ -8,3 +12,7 @@ export function useTheme() { export function useConnection() { return useContext(ConnectionContext); } + +export function useData() { + return useContext(DataContext); +} diff --git a/packages/profile/src/hooks/events.ts b/packages/profile/src/hooks/events.ts index 39a4514c8..cce692df3 100644 --- a/packages/profile/src/hooks/events.ts +++ b/packages/profile/src/hooks/events.ts @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { Event, EventNode, useEventsQuery } from "@cartridge/utils/api/indexer"; import { Trophy, Progress } from "@/models"; import { hash, byteArray, ByteArray } from "starknet"; +import { useIndexerAPI } from "@cartridge/utils"; const EVENT_WRAPPER = "EventEmitted"; @@ -46,13 +47,13 @@ export function useEvents({ limit: number; parse: (node: EventNode) => TEvent; }) { + const { indexerUrl } = useIndexerAPI(); const [offset, setOffset] = useState(0); - const [isFetching, setIsFetching] = useState(true); const [nodes, setNodes] = useState<{ [key: string]: boolean }>({}); const [events, setEvents] = useState([]); // Fetch achievement creations from raw events - const { refetch: fetchEvents } = useEventsQuery( + const { refetch: fetchEvents, isFetching } = useEventsQuery( { keys: [ getSelectorFromName(EVENT_WRAPPER), @@ -63,12 +64,11 @@ export function useEvents({ }, { enabled: false, + refetchInterval: 300_000, // Refetch every 5 minutes onSuccess: ({ events }: { events: Event }) => { // Update offset if (events.pageInfo.hasNextPage) { setOffset(offset + limit); - } else { - setIsFetching(false); } // Parse the events const results: TEvent[] = []; @@ -85,8 +85,9 @@ export function useEvents({ ); useEffect(() => { + if (!indexerUrl) return; fetchEvents(); - }, [offset, fetchEvents]); + }, [offset, indexerUrl, fetchEvents]); return { events, isFetching }; }