Skip to content

Commit

Permalink
fix landing page sync
Browse files Browse the repository at this point in the history
  • Loading branch information
aymericdelab committed Dec 12, 2024
1 parent a6663dd commit a8422ff
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 50 deletions.
2 changes: 1 addition & 1 deletion landing/src/components/modules/bridge-out-step-1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ function formatFee(fee: number) {

export const BridgeOutStep1 = () => {
const { address } = useAccount();
const [realmEntityId, setRealmEntityId] = useState<string>("");

const { getRealmNameById } = useRealm();
const { computeTravelTime } = useTravel();
const [isFeesOpen, setIsFeesOpen] = useState(false);

const [isLoading, setIsLoading] = useState(false);
const [realmEntityId, setRealmEntityId] = useState<string>("");
const { bridgeStartWithdrawFromRealm } = useBridgeAsset();
const [selectedResourceIds, setSelectedResourceIds] = useState([]);
const [selectedResourceAmounts, setSelectedResourceAmounts] = useState<{ [key: string]: number }>({});
Expand Down
61 changes: 58 additions & 3 deletions landing/src/components/modules/bridge-out-step-2.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 }>({});
Expand All @@ -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]);
Expand All @@ -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);
Expand Down Expand Up @@ -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 (
<>
<div className="max-w-md flex flex-col gap-3 max-h-[calc(75vh-100px)] overflow-y-auto p-3">
Expand All @@ -122,7 +150,35 @@ export const BridgeOutStep2 = () => {
<span className="font-semibold">Select Donkeys</span>
<span className="text-muted-foreground text-sm ml-2"> (optional)</span>
</div>
{isTableOpen ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
<div className="flex items-center gap-2">
<Button
variant="outline"
size="icon"
onClick={(e) => {
e.stopPropagation();
handleRefresh();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={isRefreshing ? "animate-spin" : ""}
>
<path d="M21 2v6h-6"></path>
<path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path>
<path d="M3 22v-6h6"></path>
<path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path>
</svg>
</Button>
{isTableOpen ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
</div>
</CollapsibleTrigger>

<CollapsibleContent>
Expand All @@ -145,7 +201,6 @@ export const BridgeOutStep2 = () => {
}
});
}
setSe;
setSelectedDonkeys(newSelected);
updateResourcesFromSelectedDonkeys(newSelected);
}}
Expand Down
2 changes: 2 additions & 0 deletions landing/src/components/modules/swap-panel.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<Tabs defaultValue="in">
Expand Down
104 changes: 104 additions & 0 deletions landing/src/dojo/queries.ts
Original file line number Diff line number Diff line change
@@ -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 <S extends Schema>(
client: ToriiClient,
clause: Clause | undefined,
components: Component<S, Metadata, undefined>[],
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 <S extends Schema>(
client: ToriiClient,
components: Component<S, Metadata, undefined>[],
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 <S extends Schema>(
client: ToriiClient,
components: Component<S, Metadata, undefined>[],
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,
);
};
64 changes: 35 additions & 29 deletions landing/src/dojo/setup.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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",
Expand All @@ -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: {
Expand All @@ -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,
Expand Down
39 changes: 39 additions & 0 deletions landing/src/hooks/helpers/use-sync-entity.tsx
Original file line number Diff line number Diff line change
@@ -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);
};
Loading

0 comments on commit a8422ff

Please sign in to comment.