From 9e9f3ec5b7b53366c9f311d1fc1297b96bdc28dd Mon Sep 17 00:00:00 2001 From: moreal Date: Thu, 16 May 2024 04:20:51 +0900 Subject: [PATCH] feat(examples): fetch data from mimir --- examples/daily-reward-dapp/src/Agent.tsx | 60 +++++--------- examples/daily-reward-dapp/src/App.tsx | 18 +++- .../daily-reward-dapp/src/mimir-client.ts | 83 +++++++++++++++++++ 3 files changed, 121 insertions(+), 40 deletions(-) create mode 100644 examples/daily-reward-dapp/src/mimir-client.ts diff --git a/examples/daily-reward-dapp/src/Agent.tsx b/examples/daily-reward-dapp/src/Agent.tsx index 0b9c7a4..1c965b2 100644 --- a/examples/daily-reward-dapp/src/Agent.tsx +++ b/examples/daily-reward-dapp/src/Agent.tsx @@ -1,13 +1,13 @@ import { Buffer } from "buffer"; import { BencodexDictionary } from "@planetarium/bencodex"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { v4 as uuidv4 } from "uuid"; import { - useGetAvatarsWithTipQuery, useStageTransactionMutation, } from "./generated/graphql"; import { getChronoSdk } from "@planetarium/chrono-sdk"; import { Address } from "@planetarium/account"; +import { GetAvatarsResponse, getAvatars, getTip } from "./mimir-client"; interface RefillButtonProps { signer: Address; @@ -106,62 +106,46 @@ function RefillButton({ signer, avatarAddress }: RefillButtonProps) { } interface AgentProps { + network: "odin" | "heimdall"; agentAddress: Address; } -function Agent({ agentAddress }: AgentProps) { - const { data, loading, error } = useGetAvatarsWithTipQuery({ - variables: { - agentAddress: agentAddress.toString(), - }, - pollInterval: 500, - }); +function Agent({ network, agentAddress }: AgentProps) { + const [agent, setAgent] = useState(); + const [tip, setTip] = useState(); - if (loading) { - return

Loading

; - } - - if (error) { - return ( -

Failed to fetch agent-related states.

- ); - } - - if (data === undefined) { - return ( -

Unexpected failure while fetching data.

- ); - } + useEffect(() => { + setInterval(() => { + getAvatars(network, agentAddress.toString()).then(setAgent); + getTip(network).then(setTip); + }, 500); + }, [network, agentAddress]); - if (data.stateQuery.agent === null || data.stateQuery.agent === undefined) { + if (agent === undefined || tip === undefined) { return ( -

- There is no such agent. (address: {agentAddress.toString()}) -

+

Loading or unexpected failure while fetching data.

); } - const agent = data.stateQuery.agent; - if (agent.avatarStates === null || agent.avatarStates === undefined) { + if (agent.avatars.length < 1) { return ( -

The agent may not have avatar states.

+

The agent may not have any avatars.

); } - const avatarStates = agent.avatarStates; - const tipIndex = data.nodeStatus.tip.index; + const avatars = agent.avatars; return (
- {avatarStates.map((avatar) => ( -
+ {avatars.map(({ avatarAddress, avatarName, actionPoint, dailyRewardReceivedIndex }) => ( +
- {avatar.name} ({avatar.actionPoint} / 120) + {avatarName} ({actionPoint} / 120) - {tipIndex - avatar.dailyRewardReceivedIndex > 2550 ? ( + {tip - dailyRewardReceivedIndex > 2550 ? ( ) : ( <> diff --git a/examples/daily-reward-dapp/src/App.tsx b/examples/daily-reward-dapp/src/App.tsx index 8680c0d..6d46e8c 100644 --- a/examples/daily-reward-dapp/src/App.tsx +++ b/examples/daily-reward-dapp/src/App.tsx @@ -1,5 +1,5 @@ import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import Agent from "./Agent"; import { getChronoSdk } from "@planetarium/chrono-sdk"; import { Address } from "@planetarium/account"; @@ -14,8 +14,18 @@ function App() { const [isConnected, setConnected] = useState(false); const [currentNetwork, setCurrentNetwork] = useState<{ gqlEndpoint: string, + genesisHash: string, id: string, } | null>(null); + const guessedNetworkName = useMemo<"odin" | "heimdall" | "unknown">(() => { + if (currentNetwork?.genesisHash?.toLowerCase() === "4582250d0da33b06779a8475d283d5dd210c683b9b999d74d03fac4f58fa6bce") { + return "odin"; + } else if (currentNetwork?.genesisHash?.toLowerCase() === "729fa26958648a35b53e8e3905d11ec53b1b4929bf5f499884aed7df616f5913") { + return "heimdall"; + } else { + return "unknown"; + } + }, [currentNetwork]); const chronoWallet = getChronoSdk(); @@ -88,6 +98,10 @@ function App() { return <>Loading... (network) } + if (guessedNetworkName === "unknown") { + return <>Unknown network (genesis hash: {currentNetwork.genesisHash}) + } + const client = new ApolloClient({ uri: currentNetwork.gqlEndpoint, cache: new InMemoryCache(), @@ -107,7 +121,7 @@ function App() { ))} - +
); diff --git a/examples/daily-reward-dapp/src/mimir-client.ts b/examples/daily-reward-dapp/src/mimir-client.ts new file mode 100644 index 0000000..bec24a4 --- /dev/null +++ b/examples/daily-reward-dapp/src/mimir-client.ts @@ -0,0 +1,83 @@ +export const BASE_URL = "https://mimir.nine-chronicles.dev/"; + +const defaultHeaders: HeadersInit = { + "Content-Type": "application/json", +}; + +interface FetchOptions { + method?: "GET" | "POST"; + body?: TBody; + headers?: HeadersInit; +} + +async function fetchAPI( + endpoint: string, + options: FetchOptions = {} +): Promise { + try { + const { method = "GET", body, headers } = options; + + const config: RequestInit = { + method, + headers: { ...defaultHeaders, ...headers }, + body: body ? JSON.stringify(body) : null, + }; + + const url = `${BASE_URL}${endpoint}`; + + const response = await fetch(url, config); + + if (!response.ok) { + throw new Error(`Error: ${response.status}`); + } + + const contentType = response.headers.get("Content-Type") || ""; + if (contentType.includes("application/json")) { + return (await response.json()) as TResponse; + } else if (contentType.includes("text/csv")) { + return (await response.text()) as unknown as TResponse; + } else { + throw new Error(`Unsupported response format: ${contentType}`); + } + } catch (error) { + console.error("Error fetching data:", error); + throw error; + } +} + +export async function getSheetNames(network: string): Promise { + return await fetchAPI(`${network}/sheets/names`); +} + +export async function getSheet(network: string, name: string): Promise { + return await fetchAPI(`${network}/sheets/${name}`, { + headers: { accept: "text/csv" }, + }); +} + +interface Avatar { + avatarAddress: string; + avatarName: string; + level: number; + actionPoint: number; + dailyRewardReceivedIndex: number; +} + +export interface GetAvatarsResponse { + avatars: Avatar[]; +} + +export async function getAvatars(network: string, agentAddress: string): Promise { + return await fetchAPI(`${network}/agent/${agentAddress}/avatars`, { + headers: { accept: "application/json" }, + }); +} + +export async function getTip(network: string): Promise { + const resp = await fetchAPI<{ + index: number; + }>(`${network}/tip`, { + headers: { accept: "application/json" }, + }); + return resp.index; +}