From 2c6bbf1c3188da28aa283609e0dad097976c6e16 Mon Sep 17 00:00:00 2001 From: raschel <38816784+aymericdelab@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:03:59 +0100 Subject: [PATCH] next into main (#2463) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add confirmation step to destroy building * raise limits and remove chat (#2435) * loading page loading itmes (#2436) * loaf-opti * buildings * load * push config * loading * fix for 0 donkey bridge, base manifest on chain env * tiles * merge * do sync * bank * market * bump * queries * reintroduce syncing and fetching * sync and get happening again * working multi bridge out step 2, fix transaction success states * tiles * remove logging * memo * world * set loading state * chunk * coords * as * bunk * bridge styling * update * update dojo version * update dojo sync * Fix hyperstructures * fix * add back chat * a * llint * ✨ Add sub to all player structures * fix(social): get name from address directly * chore: add has not settled realm yet * undefined if no realm found * pretty * fix: trade history crash * 🐛 Fix * config * add realm select border animation * hovered frag mine * hs cost (#2443) * hs cost * fix deposit arrival * update lock * load * Fix/army stamina (#2448) * Fix current tick values in nextBlockTimestamp * lint * Remove quest UI while syncing (#2449) * Remove quest UI while syncing * Add quest load animation * Raschel-dev (#2447) * hs cost * fix deposit arrival * update lock * fix hs crash * building cost config * fix namespace * remove resource cost syncing * added production * fix costs * add upgrade * trigger * remove events * fix tile * fixes * remove loading * fix: transfer view styling bug (#2451) * Fix social tribe crash (#2450) * fix build * fix market * add lords to controller on landing * feat: optimise event messages (#2455) * optimize events * fix getSyncEvents composite * small fixes * 🐛 Fix mainnet torii cfg * Fix laggy social search * landing page + quest + inventory (#2461) * update inventory * more discrete quest loading * fix landing page sync --------- Co-authored-by: Bob Co-authored-by: tedison <76473430+edisontim@users.noreply.github.com> Co-authored-by: ponderingdemocritus Co-authored-by: tedison Co-authored-by: RedBeardEth <90423049+RedBeardEth@users.noreply.github.com> Co-authored-by: Loaf <90423308+ponderingdemocritus@users.noreply.github.com> Co-authored-by: bal7hazar Co-authored-by: Nasr Co-authored-by: Larko <59736843+Larkooo@users.noreply.github.com> Co-authored-by: 0x1337 Co-authored-by: Bob <80072466+bob0005@users.noreply.github.com> Co-authored-by: zabanyat.eth <4090500+r0man1337@users.noreply.github.com> --- .../resources/InventoryResources.tsx | 75 +++++++++---- .../worldmap/players/PlayersPanel.tsx | 61 ++++++---- .../worldmap/structures/StructureListItem.tsx | 5 +- .../src/ui/modules/navigation/QuestMenu.tsx | 68 +++++++++--- .../modules/navigation/TopLeftNavigation.tsx | 16 +-- contracts/torii.toml | 4 + .../components/modules/bridge-out-step-1.tsx | 2 +- .../components/modules/bridge-out-step-2.tsx | 61 +++++++++- landing/src/components/modules/swap-panel.tsx | 2 + .../providers/cartridge-controller.tsx | 8 -- .../providers/starknet-provider.tsx | 3 + landing/src/dojo/queries.ts | 104 ++++++++++++++++++ landing/src/dojo/setup.ts | 64 ++++++----- landing/src/hooks/helpers/use-sync-entity.tsx | 39 +++++++ landing/src/hooks/helpers/useResources.tsx | 21 ++-- landing/src/hooks/use-structures.tsx | 14 +-- 16 files changed, 411 insertions(+), 136 deletions(-) delete mode 100644 landing/src/components/providers/cartridge-controller.tsx create mode 100644 landing/src/dojo/queries.ts create mode 100644 landing/src/hooks/helpers/use-sync-entity.tsx diff --git a/client/src/ui/components/resources/InventoryResources.tsx b/client/src/ui/components/resources/InventoryResources.tsx index 4412425d6..29ee90e6d 100644 --- a/client/src/ui/components/resources/InventoryResources.tsx +++ b/client/src/ui/components/resources/InventoryResources.tsx @@ -1,8 +1,10 @@ +import { addToSubscription } from "@/dojo/queries"; +import { useDojo } from "@/hooks/context/DojoContext"; import { useResourceBalance, useResourcesUtils } from "@/hooks/helpers/useResources"; import { ResourceCost } from "@/ui/elements/ResourceCost"; import { divideByPrecision } from "@/ui/utils/utils"; import { ID, Resource, ResourcesIds } from "@bibliothecadao/eternum"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; export const InventoryResources = ({ entityId, @@ -19,18 +21,47 @@ export const InventoryResources = ({ resourcesIconSize?: "xs" | "sm" | "md" | "lg"; textSize?: "xxs" | "xs" | "sm" | "md" | "lg"; }) => { + const dojo = useDojo(); + const [showAll, setShowAll] = useState(false); const { useResourcesFromBalance } = useResourcesUtils(); const { getBalance } = useResourceBalance(); const inventoriesResources = useResourcesFromBalance(entityId); - const dynamicResources = dynamic.map( - (resourceId): Resource => ({ resourceId, amount: getBalance(entityId, resourceId).balance }), + const [isSyncing, setIsSyncing] = useState(false); + + const dynamicResources = useMemo( + () => dynamic.map((resourceId): Resource => ({ resourceId, amount: getBalance(entityId, resourceId).balance })), + [dynamic, entityId, getBalance], ); + useEffect(() => { + if (inventoriesResources.length === 0) { + setIsSyncing(true); + const fetch = async () => { + try { + await addToSubscription( + dojo.network.toriiClient, + dojo.network.contractComponents as any, + entityId.toString(), + ); + } catch (error) { + console.error("Fetch failed", error); + } finally { + setIsSyncing(false); + } + }; + fetch(); + } + }, [inventoriesResources.length, entityId]); + const allResources = [...inventoriesResources, ...dynamicResources]; + const sortedResources = useMemo(() => { + return allResources.sort((a, b) => b.amount - a.amount); + }, [allResources]); + const updatedMax = useMemo(() => { if (showAll) return Infinity; return max; @@ -39,25 +70,27 @@ export const InventoryResources = ({ const maxResources = updatedMax - dynamicResources.length; let currentCount = 0; - return allResources.length > 0 ? ( + return isSyncing ? (
- {allResources - .sort((a, b) => b.amount - a.amount) - .map((resource) => { - if (!resource || currentCount >= maxResources) return null; - currentCount++; - return ( - - ); - })} +
Loading resources...
+
+ ) : allResources.length > 0 ? ( +
+ {sortedResources.map((resource) => { + if (!resource || currentCount >= maxResources) return null; + currentCount++; + return ( + + ); + })}
{updatedMax < inventoriesResources.length && !showAll && (
setShowAll(true)}>+{inventoriesResources.length - updatedMax}
diff --git a/client/src/ui/components/worldmap/players/PlayersPanel.tsx b/client/src/ui/components/worldmap/players/PlayersPanel.tsx index 06bef20d7..836ef5afb 100644 --- a/client/src/ui/components/worldmap/players/PlayersPanel.tsx +++ b/client/src/ui/components/worldmap/players/PlayersPanel.tsx @@ -1,12 +1,12 @@ import { useDojo } from "@/hooks/context/DojoContext"; -import { useGetAllPlayers } from "@/hooks/helpers/use-get-all-players"; import { useEntitiesUtils } from "@/hooks/helpers/useEntities"; import { useGuilds } from "@/hooks/helpers/useGuilds"; +import Button from "@/ui/elements/Button"; import TextInput from "@/ui/elements/TextInput"; import { getEntityIdFromKeys, toHexString } from "@/ui/utils/utils"; import { ContractAddress, Player } from "@bibliothecadao/eternum"; import { Has, HasValue, getComponentValue, runQuery } from "@dojoengine/recs"; -import { useMemo, useState } from "react"; +import { KeyboardEvent, useMemo, useState } from "react"; import { PlayerCustom, PlayerList } from "./PlayerList"; export const PlayersPanel = ({ @@ -25,15 +25,12 @@ export const PlayersPanel = ({ } = useDojo(); const { getGuildFromPlayerAddress } = useGuilds(); - + const { getEntityName } = useEntitiesUtils(); const userGuild = getGuildFromPlayerAddress(ContractAddress(account.address)); const [isLoading, setIsLoading] = useState(false); - - const [searchInput, setSearchInput] = useState(""); - - const { getEntityName } = useEntitiesUtils(); - const getPlayers = useGetAllPlayers(); + const [inputValue, setInputValue] = useState(""); + const [searchTerm, setSearchTerm] = useState(""); const playersWithStructures: PlayerCustom[] = useMemo(() => { // Sort players by points in descending order @@ -52,7 +49,7 @@ export const PlayersPanel = ({ const structureName = getEntityName(structure.entity_id); return structureName; }) - .filter((structure) => structure !== undefined); + .filter((structure): structure is string => structure !== undefined); const guild = getGuildFromPlayerAddress(player.address); @@ -73,18 +70,22 @@ export const PlayersPanel = ({ }; }); return playersWithStructures; - }, [getPlayers, isLoading]); + }, [isLoading]); const filteredPlayers = useMemo(() => { - return playersWithStructures.filter( - (player) => - player.name.toLowerCase().includes(searchInput.toLowerCase()) || - player.structures.some( - (structure) => structure && structure.toLowerCase().includes(searchInput.toLowerCase()), - ) || - toHexString(player.address).toLowerCase().includes(searchInput.toLowerCase()), - ); - }, [playersWithStructures, searchInput]); + const term = searchTerm.toLowerCase(); + return searchTerm === "" + ? playersWithStructures + : playersWithStructures.filter((player) => { + const nameMatch = player.name.toLowerCase().includes(term); + if (nameMatch) return true; + + const addressMatch = toHexString(player.address).toLowerCase().includes(term); + if (addressMatch) return true; + + return player.structures.some((structure) => structure && structure.toLowerCase().includes(term)); + }); + }, [playersWithStructures, searchTerm]); const whitelistPlayer = (address: ContractAddress) => { setIsLoading(true); @@ -104,17 +105,31 @@ export const PlayersPanel = ({ }).finally(() => setIsLoading(false)); }; + const handleSearch = () => { + setSearchTerm(inputValue); + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Enter") { + handleSearch(); + } + }; + return (
-
+
setSearchInput(searchInput)} - className="mb-4" + onChange={(value) => setInputValue(value)} + onKeyDown={handleKeyDown} + className="flex-1" /> +
-
+
diff --git a/client/src/ui/modules/navigation/QuestMenu.tsx b/client/src/ui/modules/navigation/QuestMenu.tsx index a44707ecd..7f069fcbe 100644 --- a/client/src/ui/modules/navigation/QuestMenu.tsx +++ b/client/src/ui/modules/navigation/QuestMenu.tsx @@ -2,6 +2,7 @@ import { useDojo } from "@/hooks/context/DojoContext"; import { Prize, QuestStatus, useQuests } from "@/hooks/helpers/useQuests"; import { useRealm } from "@/hooks/helpers/useRealm"; import useUIStore from "@/hooks/store/useUIStore"; +import { useWorldStore } from "@/hooks/store/useWorldLoading"; import { useStartingTutorial } from "@/hooks/use-starting-tutorial"; import { questSteps, useTutorial } from "@/hooks/use-tutorial"; import Button from "@/ui/elements/Button"; @@ -18,6 +19,8 @@ export const QuestsMenu = ({ unclaimedQuestsCount }: { unclaimedQuestsCount: num }, } = useDojo(); + const worldLoading = useWorldStore((state) => state.isWorldLoading); + useStartingTutorial(); const { quests } = useQuests(); @@ -75,6 +78,8 @@ export const QuestsMenu = ({ unclaimedQuestsCount }: { unclaimedQuestsCount: num }; const handleClaimMouseEnter = (e: React.MouseEvent) => { + if (worldLoading) return; + const rect = e.currentTarget.getBoundingClientRect(); const tooltipWidth = 300; @@ -97,20 +102,24 @@ export const QuestsMenu = ({ unclaimedQuestsCount }: { unclaimedQuestsCount: num }; return ( -
+
@@ -120,16 +129,21 @@ export const QuestsMenu = ({ unclaimedQuestsCount }: { unclaimedQuestsCount: num - -
) : ( - )}
diff --git a/client/src/ui/modules/navigation/TopLeftNavigation.tsx b/client/src/ui/modules/navigation/TopLeftNavigation.tsx index 8b4b844e2..c6bccb01a 100644 --- a/client/src/ui/modules/navigation/TopLeftNavigation.tsx +++ b/client/src/ui/modules/navigation/TopLeftNavigation.tsx @@ -4,7 +4,6 @@ import { useEntities, useEntitiesUtils } from "@/hooks/helpers/useEntities"; import { useQuery } from "@/hooks/helpers/useQuery"; import { useUnclaimedQuestsCount } from "@/hooks/helpers/useQuests"; import useUIStore from "@/hooks/store/useUIStore"; -import { useWorldStore } from "@/hooks/store/useWorldLoading"; import useNextBlockTimestamp from "@/hooks/useNextBlockTimestamp"; import { soundSelector, useUiSounds } from "@/hooks/useUISound"; import { Position } from "@/types/Position"; @@ -97,8 +96,6 @@ const WorkersHutTooltipContent = () => { export const TopLeftNavigation = memo(() => { const { setup } = useDojo(); - const worldLoading = useWorldStore((state) => state.isWorldLoading); - const { unclaimedQuestsCount } = useUnclaimedQuestsCount(); const { isMapView, handleUrlChange, hexPosition } = useQuery(); const { playerStructures } = useEntities(); @@ -324,17 +321,10 @@ export const TopLeftNavigation = memo(() => {
- {worldLoading ? ( -
- -
Quests are loading...
+ {unclaimedQuestsCount > 0 && ( +
+
- ) : ( - unclaimedQuestsCount > 0 && ( -
- -
- ) )}
diff --git a/contracts/torii.toml b/contracts/torii.toml index a4a37e46e..b5a9b85a7 100644 --- a/contracts/torii.toml +++ b/contracts/torii.toml @@ -12,3 +12,7 @@ contracts = [ "erc721:0x07ae27a31bb6526e3de9cf02f081f6ce0615ac12a6d7b85ee58b8ad7947a2809", "erc20:0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49", ] + +[events] +raw = true +historical = ["s0_eternum-TrophyCreation", "s0_eternum-TrophyProgression"] diff --git a/landing/src/components/modules/bridge-out-step-1.tsx b/landing/src/components/modules/bridge-out-step-1.tsx index 64a59a56a..7ad062653 100644 --- a/landing/src/components/modules/bridge-out-step-1.tsx +++ b/landing/src/components/modules/bridge-out-step-1.tsx @@ -31,13 +31,13 @@ function formatFee(fee: number) { export const BridgeOutStep1 = () => { const { address } = useAccount(); + const [realmEntityId, setRealmEntityId] = useState(""); const { getRealmNameById } = useRealm(); const { computeTravelTime } = useTravel(); const [isFeesOpen, setIsFeesOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [realmEntityId, setRealmEntityId] = useState(""); const { bridgeStartWithdrawFromRealm } = useBridgeAsset(); const [selectedResourceIds, setSelectedResourceIds] = useState([]); const [selectedResourceAmounts, setSelectedResourceAmounts] = useState<{ [key: string]: number }>({}); diff --git a/landing/src/components/modules/bridge-out-step-2.tsx b/landing/src/components/modules/bridge-out-step-2.tsx index 3f2be3a35..1b0c87640 100644 --- a/landing/src/components/modules/bridge-out-step-2.tsx +++ b/landing/src/components/modules/bridge-out-step-2.tsx @@ -1,9 +1,12 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; +import { useDojo } from "@/hooks/context/DojoContext"; +import { useSyncEntity } from "@/hooks/helpers/use-sync-entity"; import { useEntities } from "@/hooks/helpers/useEntities"; import { useDonkeyArrivals } from "@/hooks/helpers/useResources"; import { useBridgeAsset } from "@/hooks/useBridge"; import { displayAddress } from "@/lib/utils"; import { ADMIN_BANK_ENTITY_ID, RESOURCE_PRECISION, ResourcesIds } from "@bibliothecadao/eternum"; +import { getComponentValue } from "@dojoengine/recs"; import { useAccount } from "@starknet-react/core"; import { ChevronDown, ChevronUp, Loader } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; @@ -19,8 +22,12 @@ import { BridgeFees } from "./bridge-fees"; export const BridgeOutStep2 = () => { const { address } = useAccount(); + const dojo = useDojo(); + const { getOwnerArrivalsAtBank, getDonkeyInfo } = useDonkeyArrivals(); + const [isLoading, setIsLoading] = useState(false); + const [isRefreshing, setIsRefreshing] = useState(false); const [selectedResourceIds, setSelectedResourceIds] = useState([]); const [selectedResourceAmounts, setSelectedResourceAmounts] = useState<{ [key: string]: number }>({}); @@ -41,7 +48,21 @@ export const BridgeOutStep2 = () => { return playerRealms.map((realm) => realm!.entity_id); }, [playerRealms]); - const donkeysArrivals = useMemo(() => getOwnerArrivalsAtBank(realmEntityIds as number[]), [realmEntityIds]); + const [refreshTrigger, setRefreshTrigger] = useState(0); + const donkeysArrivals = useMemo( + () => getOwnerArrivalsAtBank(realmEntityIds as number[]), + [realmEntityIds, refreshTrigger], + ); + + const donkeyArrivalsEntityIds = useMemo(() => { + return donkeysArrivals.map((entity) => { + const position = getComponentValue(dojo.setup.components.Position, entity); + return position?.entity_id; + }); + }, [donkeysArrivals]) as number[]; + + useSyncEntity(donkeyArrivalsEntityIds); + const donkeyInfos = useMemo(() => { return donkeysArrivals.map((donkey) => getDonkeyInfo(donkey)); }, [donkeysArrivals]); @@ -55,6 +76,7 @@ export const BridgeOutStep2 = () => { tokenAddress: resourceAddresses[ResourcesIds[id].toUpperCase() as keyof typeof resourceAddresses][1], from_entity_id: Array.from(selectedDonkeys)[index], })); + try { setIsLoading(true); const tx = await bridgeFinishWithdrawFromRealm(donkeyResources, ADMIN_BANK_ENTITY_ID); @@ -104,6 +126,12 @@ export const BridgeOutStep2 = () => { updateResourcesFromSelectedDonkeys(newSelected); }, [donkeyInfos]); + const handleRefresh = () => { + setIsRefreshing(true); + setRefreshTrigger((prev) => prev + 1); + setTimeout(() => setIsRefreshing(false), 500); // Spin for 500ms + }; + return ( <>
@@ -122,7 +150,35 @@ export const BridgeOutStep2 = () => { Select Donkeys (optional)
- {isTableOpen ? : } +
+ + {isTableOpen ? : } +
@@ -145,7 +201,6 @@ export const BridgeOutStep2 = () => { } }); } - setSe; setSelectedDonkeys(newSelected); updateResourcesFromSelectedDonkeys(newSelected); }} diff --git a/landing/src/components/modules/swap-panel.tsx b/landing/src/components/modules/swap-panel.tsx index a5fb46b7c..62fc18fc0 100644 --- a/landing/src/components/modules/swap-panel.tsx +++ b/landing/src/components/modules/swap-panel.tsx @@ -1,10 +1,12 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { useSyncPlayerRealms } from "@/hooks/helpers/use-sync-entity"; import { BookOpen } from "lucide-react"; import { BridgeIn } from "./bridge-in"; import { BridgeOutStep1 } from "./bridge-out-step-1"; import { BridgeOutStep2 } from "./bridge-out-step-2"; export const SwapPanel = () => { + useSyncPlayerRealms(); return (
diff --git a/landing/src/components/providers/cartridge-controller.tsx b/landing/src/components/providers/cartridge-controller.tsx deleted file mode 100644 index 16ca3a125..000000000 --- a/landing/src/components/providers/cartridge-controller.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import ControllerConnector from "@cartridge/connector/controller"; - -export const cartridgeController = new ControllerConnector({ - rpc: "https://api.cartridge.gg/x/starknet/sepolia", - // Uncomment to use a custom theme - // theme: "dope-wars", - // colorMode: "light" -}); diff --git a/landing/src/components/providers/starknet-provider.tsx b/landing/src/components/providers/starknet-provider.tsx index 208e8b929..970095d81 100644 --- a/landing/src/components/providers/starknet-provider.tsx +++ b/landing/src/components/providers/starknet-provider.tsx @@ -16,6 +16,9 @@ const cartridgeController = new ControllerConnector({ rpc: "https://api.cartridge.gg/x/starknet/" + env.VITE_PUBLIC_CHAIN, theme, colorMode, + tokens: { + erc20: ["0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49"], + }, // namespace, slot, }); diff --git a/landing/src/dojo/queries.ts b/landing/src/dojo/queries.ts new file mode 100644 index 000000000..a805e9763 --- /dev/null +++ b/landing/src/dojo/queries.ts @@ -0,0 +1,104 @@ +// onload -> fetch single key entities + +import { Component, Metadata, Schema } from "@dojoengine/recs"; +import { setEntities } from "@dojoengine/state"; +import { Clause, EntityKeysClause, PatternMatching, ToriiClient } from "@dojoengine/torii-client"; + +// on hexception -> fetch below queries based on entityID + +// background sync after load -> + +export const getEntities = async ( + client: ToriiClient, + clause: Clause | undefined, + components: Component[], + limit: number = 100, + logging: boolean = false, +) => { + if (logging) console.log("Starting getEntities"); + let offset = 0; + let continueFetching = true; + + while (continueFetching) { + const entities = await client.getEntities({ + limit, + offset, + clause, + dont_include_hashed_keys: false, + order_by: [], + }); + + setEntities(entities, components); + + if (Object.keys(entities).length < limit) { + continueFetching = false; + } else { + offset += limit; + } + } +}; + +export const syncEntitiesEternum = async ( + client: ToriiClient, + components: Component[], + entityKeyClause: EntityKeysClause[], + logging: boolean = false, +) => { + // if (logging) console.log("Starting syncEntities"); + return await client.onEntityUpdated(entityKeyClause, (fetchedEntities: any, data: any) => { + // if (logging) console.log("Entity updated", fetchedEntities); + + setEntities({ [fetchedEntities]: data }, components); + }); +}; + +export const addToSubscription = async ( + client: ToriiClient, + components: Component[], + entityID: string, + position?: { x: number; y: number }, +) => { + const positionClause: EntityKeysClause = { + Keys: { + keys: [String(position?.x || 0), String(position?.y || 0), undefined, undefined], + pattern_matching: "FixedLen" as PatternMatching, + models: [], + }, + }; + + await getEntities( + client, + { + Composite: { + operator: "Or", + clauses: [ + positionClause, + { + Keys: { + keys: [entityID], + pattern_matching: "FixedLen", + models: [], + }, + }, + { + Keys: { + keys: [entityID, undefined], + pattern_matching: "FixedLen", + models: [], + }, + }, + { + Keys: { + keys: [entityID, undefined, undefined], + pattern_matching: "FixedLen", + models: [], + }, + }, + ], + }, + }, + components, + 10_000, + false, + ); +}; diff --git a/landing/src/dojo/setup.ts b/landing/src/dojo/setup.ts index 0efd65ea0..310caae86 100644 --- a/landing/src/dojo/setup.ts +++ b/landing/src/dojo/setup.ts @@ -1,6 +1,6 @@ import { WORLD_CONFIG_ID } from "@bibliothecadao/eternum"; import { DojoConfig } from "@dojoengine/core"; -import { getSyncEntities, getSyncEvents, syncEntities } from "@dojoengine/state"; +import { getEvents, getSyncEntities } from "@dojoengine/state"; import { Clause } from "@dojoengine/torii-client"; import { createClientComponents } from "./createClientComponents"; import { createSystemCalls } from "./createSystemCalls"; @@ -15,16 +15,7 @@ export async function setup({ ...config }: DojoConfig) { const components = createClientComponents(network); const systemCalls = createSystemCalls(network); - // Helper function to filter components or events for syncing - const getFilteredComponents = (componentKeys: (keyof typeof network.contractComponents)[]) => { - return componentKeys.map((key) => network.contractComponents[key]); - }; - - const getFilteredEvents = (eventKeys: (keyof (typeof network.contractComponents)["events"])[]) => { - return eventKeys.map((key) => network.contractComponents["events"][key]); - }; - - const filteredComponents = getFilteredComponents([ + const filteredModels = [ "AddressName", "Realm", "Owner", @@ -40,27 +31,17 @@ export async function setup({ ...config }: DojoConfig) { "GuildMember", "EntityName", "Structure", - // todo: these are needed only for the bridge: how to improve this? - "Position", - "WeightConfig", "CapacityConfig", - "EntityOwner", - "ArrivalTime", - "OwnedResourcesTracker", - "Weight", - "Resource", - "SpeedConfig", - ]) as any; + ]; - const filteredEvents = getFilteredEvents([ + const filteredEvents = [ "BurnDonkey", // points "HyperstructureCoOwnersChange", "HyperstructureFinished", "GameEnded", - // count - "FragmentMineDiscovered", - ]) as any; + ]; + const clauses: Clause[] = [ { Keys: { @@ -87,17 +68,42 @@ export async function setup({ ...config }: DojoConfig) { // fetch all existing entities from torii with optional component filtering await getSyncEntities( network.toriiClient, - filteredComponents, + network.contractComponents as any, { Composite: { operator: "Or", clauses } }, [], 10_000, ); - const sync = await syncEntities(network.toriiClient, filteredComponents, [], false); + const sync = await getSyncEntities( + network.toriiClient, + network.contractComponents as any, + { + Keys: { + keys: [undefined], + pattern_matching: "VariableLen", + models: filteredModels.map((model) => `s0_eternum-${model}`), + }, + }, + [], + 10_000, + ); - configManager.setDojo(components); + const eventSync = getEvents( + network.toriiClient, + network.contractComponents.events as any, + undefined, + { + Keys: { + keys: [undefined], + pattern_matching: "VariableLen", + models: filteredEvents.map((event) => `s0_eternum-${event}`), + }, + }, + false, + false, + ); - const eventSync = getSyncEvents(network.toriiClient, filteredEvents, undefined, [], 20_000, false, false); + configManager.setDojo(components); return { network, diff --git a/landing/src/hooks/helpers/use-sync-entity.tsx b/landing/src/hooks/helpers/use-sync-entity.tsx new file mode 100644 index 000000000..e3511daff --- /dev/null +++ b/landing/src/hooks/helpers/use-sync-entity.tsx @@ -0,0 +1,39 @@ +import { addToSubscription } from "@/dojo/queries"; +import { useEffect, useMemo, useState } from "react"; +import { useDojo } from "../context/DojoContext"; +import { useEntities } from "./useEntities"; + +export const useSyncEntity = (entityIds: number | number[]) => { + const dojo = useDojo(); + const [isSyncing, setIsSyncing] = useState(false); + + useEffect(() => { + setIsSyncing(true); + const fetch = async () => { + try { + const ids = Array.isArray(entityIds) ? entityIds : [entityIds]; + await Promise.all( + ids.map((id) => + addToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, id.toString()), + ), + ); + } catch (error) { + console.error("Fetch failed", error); + } finally { + setIsSyncing(false); + } + }; + fetch(); + }, [entityIds]); + + return isSyncing; +}; + +export const useSyncPlayerRealms = () => { + const { playerRealms } = useEntities(); + const realmEntityIds = useMemo(() => { + return playerRealms.map((realm) => realm!.entity_id); + }, [playerRealms]); + + return useSyncEntity(realmEntityIds); +}; diff --git a/landing/src/hooks/helpers/useResources.tsx b/landing/src/hooks/helpers/useResources.tsx index bbef77688..0c374e284 100644 --- a/landing/src/hooks/helpers/useResources.tsx +++ b/landing/src/hooks/helpers/useResources.tsx @@ -32,17 +32,18 @@ export function useDonkeyArrivals() { const arrivals: any[] = []; for (const realmEntityId of realmEntityIds) { - arrivals.push( - ...runQuery([ - HasValue(Position, { x: bankPosition?.x ?? 0, y: bankPosition?.y ?? 0 }), - NotValue(OwnedResourcesTracker, { resource_types: 0n }), - Has(OwnedResourcesTracker), - Has(Weight), - Has(ArrivalTime), - HasValue(EntityOwner, { entity_owner_id: realmEntityId }), - ]), - ); + const res = runQuery([ + HasValue(Position, { x: bankPosition?.x ?? 0, y: bankPosition?.y ?? 0 }), + NotValue(OwnedResourcesTracker, { resource_types: 0n }), + Has(OwnedResourcesTracker), + Has(Weight), + Has(ArrivalTime), + HasValue(EntityOwner, { entity_owner_id: realmEntityId }), + ]); + + arrivals.push(...res); } + return arrivals; }; diff --git a/landing/src/hooks/use-structures.tsx b/landing/src/hooks/use-structures.tsx index 987370ae1..685e0098f 100644 --- a/landing/src/hooks/use-structures.tsx +++ b/landing/src/hooks/use-structures.tsx @@ -1,19 +1,19 @@ +import { StructureType } from "@bibliothecadao/eternum"; import { useEntityQuery } from "@dojoengine/react"; -import { Has } from "@dojoengine/recs"; +import { Has, HasValue } from "@dojoengine/recs"; import { useDojo } from "./context/DojoContext"; export const useStructuresNumber = () => { const { setup: { - components: { - Hyperstructure, - Realm, - events: { FragmentMineDiscovered }, - }, + components: { Hyperstructure, Realm, Structure }, }, } = useDojo(); - const fragmentMinesCount = useEntityQuery([Has(FragmentMineDiscovered)]).length; + const fragmentMinesCount = useEntityQuery([ + HasValue(Structure, { category: StructureType[StructureType.FragmentMine] }), + ]).length; + const hyperstructuresCount = useEntityQuery([Has(Hyperstructure)]).length; const realmsCount = useEntityQuery([Has(Realm)]).length;