From 212adc7fd17906f84a73ded4e9d0042d47d333ae Mon Sep 17 00:00:00 2001 From: sokolova-an Date: Thu, 4 Jan 2024 11:37:28 +0100 Subject: [PATCH 1/3] add gidt details page --- src/common/balances/BalanceProvider.tsx | 26 ++++++- src/common/routing/paths.ts | 1 + src/common/types/index.ts | 11 +++ src/common/utils/gift.ts | 25 +++++++ src/components/GiftPlate/GiftPlate.tsx | 26 +++++++ src/components/Layout/Layout.tsx | 7 +- src/components/index.ts | 2 + src/pages/_app.tsx | 2 +- src/pages/dashboard/index.tsx | 19 ++--- src/pages/gifts/index.tsx | 77 ++++++++++++++++++++ src/pages/index.tsx | 11 ++- src/pages/transfer/amount.tsx | 4 +- src/pages/transfer/create-gift.tsx | 4 +- src/screens/dashboard/main/DashboardMain.tsx | 18 +++-- tsconfig.json | 2 +- 15 files changed, 207 insertions(+), 28 deletions(-) create mode 100644 src/common/utils/gift.ts create mode 100644 src/components/GiftPlate/GiftPlate.tsx create mode 100644 src/pages/gifts/index.tsx diff --git a/src/common/balances/BalanceProvider.tsx b/src/common/balances/BalanceProvider.tsx index 6a3137ad..d02e8175 100644 --- a/src/common/balances/BalanceProvider.tsx +++ b/src/common/balances/BalanceProvider.tsx @@ -1,7 +1,7 @@ import { createContext, PropsWithChildren, useContext, useRef } from 'react'; import { useChainRegistry } from '@common/chainRegistry'; import { IAssetBalance } from '@common/balances/types'; -import { chainAssetAccountIdToString, ChainAssetAccount } from '@common/types'; +import { chainAssetAccountIdToString, ChainAssetAccount, ChainId, Gift } from '@common/types'; import { useNumId } from '@common/utils/NumId'; import { SubscriptionState } from '@common/subscription/types'; import { createBalanceService } from '@common/balances/BalanceService'; @@ -14,6 +14,7 @@ type UpdaterCallbackStore = Record; type BalanceProviderContextProps = { subscribeBalance: (account: ChainAssetAccount, onUpdate: UpdateCallback) => number; unsubscribeBalance: (unsubscribeId: number) => void; + getGiftsBalance: (accounts: Gift[], chainId: ChainId) => Promise<[Gift[], Gift[]]>; }; const BalanceProviderContext = createContext({} as BalanceProviderContextProps); @@ -152,8 +153,29 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { } } + async function getGiftsBalance(accounts: Gift[], chainId: ChainId): Promise<[Gift[], Gift[]]> { + const connection = await getConnection(chainId); + const balances = await connection.api.query.system.account.multi(accounts.map((i) => i.address)); + const chain = await getChain(chainId); + + const unclaimed = [] as Gift[]; + const claimed = [] as Gift[]; + + balances.forEach((d, idx) => + d.data.free.isEmpty + ? claimed.push({ + ...accounts[idx], + chainAsset: chain?.assets[0], + status: 'Claimed', + }) + : unclaimed.push({ ...accounts[idx], chainAsset: chain?.assets[0], status: 'Unclaimed' }), + ); + + return [unclaimed, claimed]; + } + return ( - + {children} ); diff --git a/src/common/routing/paths.ts b/src/common/routing/paths.ts index 9fd0a772..97450d68 100644 --- a/src/common/routing/paths.ts +++ b/src/common/routing/paths.ts @@ -7,6 +7,7 @@ export const Paths = { ONBOARDING_IMPORT_WALLET: '/onboarding/import-wallet', DASHBOARD: '/dashboard', + GIFTS: '/gifts', TRANSFER: '/transfer', TRANSFER_SELECT_TOKEN: '/transfer/select-token', diff --git a/src/common/types/index.ts b/src/common/types/index.ts index a39746d8..b6d1e733 100644 --- a/src/common/types/index.ts +++ b/src/common/types/index.ts @@ -1,3 +1,4 @@ +import { Asset } from './../chainRegistry/types/index'; export type HexString = `0x${string}`; import { u8aToHex, hexToU8a } from '@polkadot/util'; @@ -42,6 +43,16 @@ export type TrasferAsset = AssetAccount & { }; export type StateResolution = { resolve: (value: T) => void; reject: () => void }; +export type Gift = { + timestamp: number; + address: string; + secret: string; + balance: string; + chainId: ChainId; + status: 'Unclaimed' | 'Claimed'; + chainAsset?: Asset; +}; + export function chainAssetIdToString(value: ChainAssetId): string { return `${value.chainId} - ${value.assetId}`; } diff --git a/src/common/utils/gift.ts b/src/common/utils/gift.ts new file mode 100644 index 00000000..d1b04a2a --- /dev/null +++ b/src/common/utils/gift.ts @@ -0,0 +1,25 @@ +import secureLocalStorage from 'react-secure-storage'; +import { ChainId, Gift } from '../types'; + +export const backupGifts = (address: string, secret: string, chainId: ChainId, balance: string): void => { + const gift = { timestamp: Date.now(), address, secret, chainId, balance }; + + secureLocalStorage.setItem( + 'GIFT_STORE', + secureLocalStorage.getItem('GIFT_STORE') + ? JSON.stringify([...JSON.parse(secureLocalStorage.getItem('GIFT_STORE') as string), gift]) + : JSON.stringify([gift]), + ); +}; + +export const getGifts = (): Map | null => { + const gifts = JSON.parse(secureLocalStorage.getItem('GIFT_STORE') as string); + if (!gifts) return null; + + const map = new Map(); + gifts.forEach((gift: Gift) => { + map.set(gift.chainId, [...(map.get(gift.chainId) || []), gift]); + }); + + return map; +}; diff --git a/src/components/GiftPlate/GiftPlate.tsx b/src/components/GiftPlate/GiftPlate.tsx new file mode 100644 index 00000000..2cd9b855 --- /dev/null +++ b/src/components/GiftPlate/GiftPlate.tsx @@ -0,0 +1,26 @@ +import { CaptionText, HelpText, Icon, Plate } from '@/components'; +import { Gift } from '@/common/types'; + +const GiftPlate = ({ gift }: { gift: Gift }) => { + const date = new Date(gift.timestamp).toLocaleString(); + + return ( + + +
+ + {gift.balance} {gift.chainAsset?.symbol} + + + Created: {date} + + + {gift.status} + +
+ {gift.status === 'Unclaimed' && } +
+ ); +}; + +export default GiftPlate; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 1b003a53..60104c92 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,12 +1,15 @@ import { PropsWithChildren } from 'react'; -import { ExtrinsicProvider } from '@/common/extrinsicService/ExtrinsicProvider'; import { ChainRegistry } from '@/common/chainRegistry'; +import { ExtrinsicProvider } from '@/common/extrinsicService/ExtrinsicProvider'; +import { BalanceProvider } from '@/common/balances/BalanceProvider'; export default function Layout({ children }: PropsWithChildren) { return ( -
{children}
+ +
{children}
+
); diff --git a/src/components/index.ts b/src/components/index.ts index 18a06434..02aff5a5 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -18,6 +18,7 @@ import PasswordForm from './PasswordForm/PasswordForm'; import Shimmering from './Shimmering/Shimmering'; import Identicon from './Identicon/Identicon'; import Layout from './Layout/Layout'; +import GiftPlate from './GiftPlate/GiftPlate'; export { TextBase, @@ -39,4 +40,5 @@ export { Shimmering, Identicon, Layout, + GiftPlate, }; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index b5c9534e..96ba2731 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,10 +1,10 @@ import { AppProps } from 'next/app'; +import { NextPage } from 'next'; import { ReactElement, ReactNode, useEffect, useState } from 'react'; import { NextUIProvider } from '@nextui-org/react'; import { TelegramProvider } from '@common/providers/telegramProvider'; import { GlobalStateProvider } from '@/common/providers/contextProvider'; import './globals.css'; -import { NextPage } from 'next'; export type NextPageWithLayout

= NextPage & { getLayout?: (page: ReactElement) => ReactNode; diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index 71ff3bfb..f966d548 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -1,16 +1,11 @@ -import { BalanceProvider } from '@/common/balances/BalanceProvider'; -import { ChainRegistry } from '@/common/chainRegistry'; -import { ExtrinsicProvider } from '@/common/extrinsicService/ExtrinsicProvider'; +import { ReactElement } from 'react'; +import { Layout } from '@/components'; import { DashboardMain } from '@/screens/dashboard'; export default function DashboardMainPage() { - return ( - - - - - - - - ); + return ; } + +DashboardMainPage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/src/pages/gifts/index.tsx b/src/pages/gifts/index.tsx new file mode 100644 index 00000000..cd6859d0 --- /dev/null +++ b/src/pages/gifts/index.tsx @@ -0,0 +1,77 @@ +'use client'; +import { ReactElement, useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; + +import { useTelegram } from '@common/providers/telegramProvider'; +import { Paths } from '@/common/routing'; +import { BodyText, GiftPlate, Layout, TitleText } from '@/components'; +import { useBalances } from '@/common/balances/BalanceProvider'; +import { getGifts } from '@/common/utils/gift'; +import { Gift } from '@/common/types'; + +// TODO add loading state for unclaimed and claimed +export default function GiftPage() { + const router = useRouter(); + const { BackButton, MainButton } = useTelegram(); + const { getGiftsBalance } = useBalances(); + const [unclaimedGifts, setUnclaimedGifts] = useState([]); + const [claimedGifts, setClaimedGifts] = useState([]); + + useEffect(() => { + BackButton?.show(); + MainButton?.hide(); + + const callback = async () => { + router.push(Paths.DASHBOARD); + }; + BackButton?.onClick(callback); + + const mapGifts = getGifts(); + if (!mapGifts) return; + + for (const [key, value] of mapGifts) { + (async function () { + const [unclaimed, claimed] = await getGiftsBalance(value, key); + setUnclaimedGifts((prev) => [...prev, ...unclaimed]); + setClaimedGifts((prev) => [...prev, ...claimed]); + })(); + } + + return () => { + BackButton?.hide(); + BackButton?.offClick(callback); + }; + }, []); + + return ( +

+ + Gifts + + {!!unclaimedGifts.length && ( + <> + + Unclaimed + + {unclaimedGifts.map((gift) => ( + + ))} + + )} + {!!claimedGifts.length && ( + <> + + Claimed + + {claimedGifts.map((gift) => ( + + ))} + + )} +
+ ); +} + +GiftPage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4b948bdb..87ca30d8 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,8 +1,9 @@ import React, { useEffect } from 'react'; +import { useGlobalContext } from '@/common/providers/contextProvider'; import { getWallet } from '@common/wallet'; +import { Layout } from '@/components'; import OnboardingStartPage from './onboarding'; import DashboardMainPage from './dashboard'; -import { useGlobalContext } from '@/common/providers/contextProvider'; export default function App() { const { setPublicKey } = useGlobalContext(); @@ -12,5 +13,11 @@ export default function App() { setPublicKey(wallet?.publicKey); }, []); - return wallet ? : ; + return wallet ? ( + + + + ) : ( + + ); } diff --git a/src/pages/transfer/amount.tsx b/src/pages/transfer/amount.tsx index 103e249a..5927612d 100644 --- a/src/pages/transfer/amount.tsx +++ b/src/pages/transfer/amount.tsx @@ -66,12 +66,12 @@ export default function AmountPage() { }, [BackButton, MainButton]); useEffect(() => { + if (!isAmountValid || !Number(amount)) return; + const callback = () => { setSelectedAsset((prev) => ({ ...prev!, transferAll, amount })); router.push(selectedAsset?.isGift ? Paths.TRANSFER_CREATE_GIFT : Paths.TRANSFER_CONFIRMATION); }; - - if (!isAmountValid || !Number(amount)) return; MainButton?.enable(); MainButton?.onClick(callback); diff --git a/src/pages/transfer/create-gift.tsx b/src/pages/transfer/create-gift.tsx index 32b9b550..14ee71a4 100644 --- a/src/pages/transfer/create-gift.tsx +++ b/src/pages/transfer/create-gift.tsx @@ -11,10 +11,11 @@ import { Paths } from '@/common/routing'; import { BodyText, HeadlineText, Layout } from '@/components'; import { useExtrinsicProvider } from '@/common/extrinsicService/ExtrinsicProvider'; import { handleSend } from '@/common/utils/balance'; -import { TrasferAsset } from '@/common/types'; +import { ChainId, TrasferAsset } from '@/common/types'; import { createGiftWallet } from '@/common/wallet'; import { createTgLink, navigateTranferById } from '@/common/telegram'; import { TgLink } from '@/common/telegram/types'; +import { backupGifts } from '@/common/utils/gift'; export default function CreateGiftPage() { const router = useRouter(); @@ -38,6 +39,7 @@ export default function CreateGiftPage() { const wallet = createGiftWallet(selectedAsset.addressPrefix as number); (async function () { await handleSend(submitExtrinsic, selectedAsset as TrasferAsset, wallet.address).then(() => { + backupGifts(wallet.address, wallet.secret, selectedAsset.chainId as ChainId, selectedAsset.amount as string); setLink(createTgLink(wallet.secret, selectedAsset.symbol as string)); setLoading(false); MainButton?.show(); diff --git a/src/screens/dashboard/main/DashboardMain.tsx b/src/screens/dashboard/main/DashboardMain.tsx index 4f1d3e59..bc05fe1f 100644 --- a/src/screens/dashboard/main/DashboardMain.tsx +++ b/src/screens/dashboard/main/DashboardMain.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import { encodeAddress } from '@polkadot/util-crypto'; -import { Avatar } from '@nextui-org/react'; +import { Avatar, Button } from '@nextui-org/react'; import { useExtrinsicProvider } from '@/common/extrinsicService/ExtrinsicProvider'; import { useGlobalContext } from '@/common/providers/contextProvider'; @@ -29,18 +29,19 @@ export const DashboardMain = () => { return; } const [seed, symbol] = webApp.initDataUnsafe.start_param.split('_'); - (async () => { const chain = await getAssetBySymbol(symbol); const address = encodeAddress(publicKey, chain.chain.addressPrefix); claimGift(seed, address, chain.chain.chainId, submitExtrinsic) .then(() => { - setIsGiftClaimed(true); alert('Gift claimed successfully!'); }) .catch(() => { alert('Failed to claim gift'); + }) + .finally(() => { + setIsGiftClaimed(true); }); })(); }, [webApp, publicKey, isGiftClaimed]); @@ -73,7 +74,6 @@ export const DashboardMain = () => { subscribeBalance(account, (balance: IAssetBalance) => { console.info(`${address} ${chain.name} => balance: ${balance.total().toString()}`); - setAssets((prevAssets) => updateAssetsBalance(prevAssets, chain, balance)); }); } @@ -103,7 +103,15 @@ export const DashboardMain = () => { router.push(Paths.TRANSFER)} /> - + + Assets diff --git a/tsconfig.json b/tsconfig.json index a48fc7e8..fe99d7e2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, From 489c5ea7da9d661b2ff09a1864964a1b36d53d7e Mon Sep 17 00:00:00 2001 From: sokolova-an Date: Thu, 4 Jan 2024 16:35:36 +0100 Subject: [PATCH 2/3] Feat: details page for unclaimed gifts --- src/common/routing/paths.ts | 2 + src/pages/gifts/gift-details.tsx | 42 ++++++++++++++++ src/pages/gifts/index.tsx | 53 ++++++++++---------- src/pages/transfer/create-gift.tsx | 19 ++----- src/screens/dashboard/main/DashboardMain.tsx | 2 +- src/screens/gifts/GiftDetails.tsx | 30 +++++++++++ 6 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 src/pages/gifts/gift-details.tsx create mode 100644 src/screens/gifts/GiftDetails.tsx diff --git a/src/common/routing/paths.ts b/src/common/routing/paths.ts index 97450d68..6f756183 100644 --- a/src/common/routing/paths.ts +++ b/src/common/routing/paths.ts @@ -7,7 +7,9 @@ export const Paths = { ONBOARDING_IMPORT_WALLET: '/onboarding/import-wallet', DASHBOARD: '/dashboard', + GIFTS: '/gifts', + GIFT_DETAILS: '/gifts/gift-details', TRANSFER: '/transfer', TRANSFER_SELECT_TOKEN: '/transfer/select-token', diff --git a/src/pages/gifts/gift-details.tsx b/src/pages/gifts/gift-details.tsx new file mode 100644 index 00000000..6ce2bd23 --- /dev/null +++ b/src/pages/gifts/gift-details.tsx @@ -0,0 +1,42 @@ +'use client'; +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; +import { WebApp } from '@twa-dev/types'; + +import { useTelegram } from '@common/providers/telegramProvider'; +import { Paths } from '@/common/routing'; +import { createTgLink } from '@/common/telegram'; +import GiftDetails from '@/screens/gifts/GiftDetails'; +import { TgLink } from '@/common/telegram/types'; +import Image from 'next/image'; + +export default function GiftPage() { + const router = useRouter(); + const { BackButton, MainButton, webApp } = useTelegram(); + const [link, setLink] = useState(null); + const searchParams = router.query; + + useEffect(() => { + BackButton?.show(); + MainButton?.hide(); + + const callback = async () => { + router.push(Paths.GIFTS); + }; + BackButton?.onClick(callback); + setLink(createTgLink(searchParams.seed as string, searchParams.symbol as string)); + + return () => { + BackButton?.hide(); + BackButton?.offClick(callback); + }; + }, []); + + // TODO change image + return ( +
+ gift + +
+ ); +} diff --git a/src/pages/gifts/index.tsx b/src/pages/gifts/index.tsx index cd6859d0..70412a78 100644 --- a/src/pages/gifts/index.tsx +++ b/src/pages/gifts/index.tsx @@ -1,21 +1,23 @@ 'use client'; import { ReactElement, useEffect, useState } from 'react'; import { useRouter } from 'next/router'; +import Link from 'next/link'; import { useTelegram } from '@common/providers/telegramProvider'; import { Paths } from '@/common/routing'; -import { BodyText, GiftPlate, Layout, TitleText } from '@/components'; +import { BodyText, GiftPlate, Layout, Shimmering, TitleText } from '@/components'; import { useBalances } from '@/common/balances/BalanceProvider'; import { getGifts } from '@/common/utils/gift'; import { Gift } from '@/common/types'; -// TODO add loading state for unclaimed and claimed +// TODO improve loading state for unclaimed and claimed export default function GiftPage() { const router = useRouter(); const { BackButton, MainButton } = useTelegram(); const { getGiftsBalance } = useBalances(); const [unclaimedGifts, setUnclaimedGifts] = useState([]); const [claimedGifts, setClaimedGifts] = useState([]); + const [loading, setLoading] = useState(true); useEffect(() => { BackButton?.show(); @@ -29,13 +31,14 @@ export default function GiftPage() { const mapGifts = getGifts(); if (!mapGifts) return; - for (const [key, value] of mapGifts) { + mapGifts.forEach((value, key) => { (async function () { const [unclaimed, claimed] = await getGiftsBalance(value, key); setUnclaimedGifts((prev) => [...prev, ...unclaimed]); setClaimedGifts((prev) => [...prev, ...claimed]); + setLoading(false); })(); - } + }); return () => { BackButton?.hide(); @@ -44,31 +47,29 @@ export default function GiftPage() { }, []); return ( -
+ <> Gifts - {!!unclaimedGifts.length && ( - <> - - Unclaimed - - {unclaimedGifts.map((gift) => ( - - ))} - - )} - {!!claimedGifts.length && ( - <> - - Claimed - - {claimedGifts.map((gift) => ( - - ))} - - )} -
+ + Unclaimed + + {loading && } + {!!unclaimedGifts.length && + unclaimedGifts.map((gift) => ( + + + + ))} + + Claimed + + {loading && } + {!!claimedGifts.length && claimedGifts.map((gift) => )} + ); } diff --git a/src/pages/transfer/create-gift.tsx b/src/pages/transfer/create-gift.tsx index 14ee71a4..d7329791 100644 --- a/src/pages/transfer/create-gift.tsx +++ b/src/pages/transfer/create-gift.tsx @@ -2,18 +2,18 @@ import { ReactElement, useEffect, useState } from 'react'; import { useRouter } from 'next/router'; import { Player } from '@lottiefiles/react-lottie-player'; -import { Button } from '@nextui-org/react'; import { WebApp } from '@twa-dev/types'; import { useTelegram } from '@common/providers/telegramProvider'; import { useGlobalContext } from '@/common/providers/contextProvider'; import { Paths } from '@/common/routing'; -import { BodyText, HeadlineText, Layout } from '@/components'; +import { HeadlineText, Layout } from '@/components'; +import GiftDetails from '@/screens/gifts/GiftDetails'; import { useExtrinsicProvider } from '@/common/extrinsicService/ExtrinsicProvider'; import { handleSend } from '@/common/utils/balance'; import { ChainId, TrasferAsset } from '@/common/types'; import { createGiftWallet } from '@/common/wallet'; -import { createTgLink, navigateTranferById } from '@/common/telegram'; +import { createTgLink } from '@/common/telegram'; import { TgLink } from '@/common/telegram/types'; import { backupGifts } from '@/common/utils/gift'; @@ -61,18 +61,7 @@ export default function CreateGiftPage() { Creating your gift.. ) : ( - <> - - - Now you can send this link anyone who you needed to claim funds. When they will open it, the gift will - marked as claimed - - - + )} ); diff --git a/src/screens/dashboard/main/DashboardMain.tsx b/src/screens/dashboard/main/DashboardMain.tsx index bc05fe1f..249bfefa 100644 --- a/src/screens/dashboard/main/DashboardMain.tsx +++ b/src/screens/dashboard/main/DashboardMain.tsx @@ -88,7 +88,7 @@ export const DashboardMain = () => { } return ( -
+
Hello, {user?.first_name || 'friend'} diff --git a/src/screens/gifts/GiftDetails.tsx b/src/screens/gifts/GiftDetails.tsx new file mode 100644 index 00000000..8fbd17b2 --- /dev/null +++ b/src/screens/gifts/GiftDetails.tsx @@ -0,0 +1,30 @@ +import { Button, Snippet } from '@nextui-org/react'; +import { WebApp } from '@twa-dev/types'; + +import { BodyText } from '@/components'; +import { navigateTranferById } from '@/common/telegram'; +import { TgLink } from '@/common/telegram/types'; + +type GiftDetailsProps = { + link: TgLink | null; + webApp: WebApp; +}; + +export default function GiftDetails({ link, webApp }: GiftDetailsProps) { + if (!link) return; + + return ( + <> + + {link.url} + + + Now you can send this link anyone who you needed to claim funds. When they will open it, the gift will marked as + claimed + + + + ); +} From 4e109dbdfa5c215fabcfe2695bc97ff01427fdec Mon Sep 17 00:00:00 2001 From: sokolova-an Date: Fri, 5 Jan 2024 15:01:28 +0100 Subject: [PATCH 3/3] cleanup types --- src/common/balances/BalanceProvider.tsx | 19 +++++++++++++------ src/common/types/index.ts | 12 ++++++++++-- src/common/utils/gift.ts | 15 ++++++--------- src/components/GiftPlate/GiftPlate.tsx | 6 +++--- src/pages/gifts/index.tsx | 4 ++-- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/common/balances/BalanceProvider.tsx b/src/common/balances/BalanceProvider.tsx index d02e8175..934a332f 100644 --- a/src/common/balances/BalanceProvider.tsx +++ b/src/common/balances/BalanceProvider.tsx @@ -1,7 +1,14 @@ import { createContext, PropsWithChildren, useContext, useRef } from 'react'; import { useChainRegistry } from '@common/chainRegistry'; import { IAssetBalance } from '@common/balances/types'; -import { chainAssetAccountIdToString, ChainAssetAccount, ChainId, Gift } from '@common/types'; +import { + chainAssetAccountIdToString, + ChainAssetAccount, + ChainId, + Gift, + PersistentGift, + GiftStatus, +} from '@common/types'; import { useNumId } from '@common/utils/NumId'; import { SubscriptionState } from '@common/subscription/types'; import { createBalanceService } from '@common/balances/BalanceService'; @@ -14,7 +21,7 @@ type UpdaterCallbackStore = Record; type BalanceProviderContextProps = { subscribeBalance: (account: ChainAssetAccount, onUpdate: UpdateCallback) => number; unsubscribeBalance: (unsubscribeId: number) => void; - getGiftsBalance: (accounts: Gift[], chainId: ChainId) => Promise<[Gift[], Gift[]]>; + getGiftsState: (accounts: Gift[], chainId: ChainId) => Promise<[Gift[], Gift[]]>; }; const BalanceProviderContext = createContext({} as BalanceProviderContextProps); @@ -153,7 +160,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { } } - async function getGiftsBalance(accounts: Gift[], chainId: ChainId): Promise<[Gift[], Gift[]]> { + async function getGiftsState(accounts: PersistentGift[], chainId: ChainId): Promise<[Gift[], Gift[]]> { const connection = await getConnection(chainId); const balances = await connection.api.query.system.account.multi(accounts.map((i) => i.address)); const chain = await getChain(chainId); @@ -166,16 +173,16 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { ? claimed.push({ ...accounts[idx], chainAsset: chain?.assets[0], - status: 'Claimed', + status: GiftStatus.CLAIMED, }) - : unclaimed.push({ ...accounts[idx], chainAsset: chain?.assets[0], status: 'Unclaimed' }), + : unclaimed.push({ ...accounts[idx], chainAsset: chain?.assets[0], status: GiftStatus.UNCLAIMED }), ); return [unclaimed, claimed]; } return ( - + {children} ); diff --git a/src/common/types/index.ts b/src/common/types/index.ts index b6d1e733..b1cee702 100644 --- a/src/common/types/index.ts +++ b/src/common/types/index.ts @@ -43,13 +43,21 @@ export type TrasferAsset = AssetAccount & { }; export type StateResolution = { resolve: (value: T) => void; reject: () => void }; -export type Gift = { +export const enum GiftStatus { + CLAIMED = 'Claimed', + UNCLAIMED = 'Unclaimed', +} + +export type PersistentGift = { timestamp: number; address: string; secret: string; balance: string; chainId: ChainId; - status: 'Unclaimed' | 'Claimed'; + status: GiftStatus; +}; + +export type Gift = PersistentGift & { chainAsset?: Asset; }; diff --git a/src/common/utils/gift.ts b/src/common/utils/gift.ts index d1b04a2a..6d7a696e 100644 --- a/src/common/utils/gift.ts +++ b/src/common/utils/gift.ts @@ -1,23 +1,20 @@ import secureLocalStorage from 'react-secure-storage'; -import { ChainId, Gift } from '../types'; +import { ChainId, PersistentGift } from '../types'; export const backupGifts = (address: string, secret: string, chainId: ChainId, balance: string): void => { const gift = { timestamp: Date.now(), address, secret, chainId, balance }; + const storedGifts = secureLocalStorage.getItem('GIFT_STORE') as string; + const backup = storedGifts ? [...JSON.parse(storedGifts), gift] : [gift]; - secureLocalStorage.setItem( - 'GIFT_STORE', - secureLocalStorage.getItem('GIFT_STORE') - ? JSON.stringify([...JSON.parse(secureLocalStorage.getItem('GIFT_STORE') as string), gift]) - : JSON.stringify([gift]), - ); + secureLocalStorage.setItem('GIFT_STORE', JSON.stringify(backup)); }; -export const getGifts = (): Map | null => { +export const getGifts = (): Map | null => { const gifts = JSON.parse(secureLocalStorage.getItem('GIFT_STORE') as string); if (!gifts) return null; const map = new Map(); - gifts.forEach((gift: Gift) => { + gifts.forEach((gift: PersistentGift) => { map.set(gift.chainId, [...(map.get(gift.chainId) || []), gift]); }); diff --git a/src/components/GiftPlate/GiftPlate.tsx b/src/components/GiftPlate/GiftPlate.tsx index 2cd9b855..c05dca6c 100644 --- a/src/components/GiftPlate/GiftPlate.tsx +++ b/src/components/GiftPlate/GiftPlate.tsx @@ -1,5 +1,5 @@ import { CaptionText, HelpText, Icon, Plate } from '@/components'; -import { Gift } from '@/common/types'; +import { Gift, GiftStatus } from '@/common/types'; const GiftPlate = ({ gift }: { gift: Gift }) => { const date = new Date(gift.timestamp).toLocaleString(); @@ -14,11 +14,11 @@ const GiftPlate = ({ gift }: { gift: Gift }) => { Created: {date} - + {gift.status}
- {gift.status === 'Unclaimed' && } + {gift.status === GiftStatus.UNCLAIMED && } ); }; diff --git a/src/pages/gifts/index.tsx b/src/pages/gifts/index.tsx index 70412a78..28f19e9c 100644 --- a/src/pages/gifts/index.tsx +++ b/src/pages/gifts/index.tsx @@ -14,7 +14,7 @@ import { Gift } from '@/common/types'; export default function GiftPage() { const router = useRouter(); const { BackButton, MainButton } = useTelegram(); - const { getGiftsBalance } = useBalances(); + const { getGiftsState } = useBalances(); const [unclaimedGifts, setUnclaimedGifts] = useState([]); const [claimedGifts, setClaimedGifts] = useState([]); const [loading, setLoading] = useState(true); @@ -33,7 +33,7 @@ export default function GiftPage() { mapGifts.forEach((value, key) => { (async function () { - const [unclaimed, claimed] = await getGiftsBalance(value, key); + const [unclaimed, claimed] = await getGiftsState(value, key); setUnclaimedGifts((prev) => [...prev, ...unclaimed]); setClaimedGifts((prev) => [...prev, ...claimed]); setLoading(false);