From e8ddc4e6b9233eef50887b4284975f5ea6394075 Mon Sep 17 00:00:00 2001 From: Wukong Sun Date: Fri, 17 May 2024 10:14:46 +0800 Subject: [PATCH] progressive ui and claim rewards (#20) * fix: progressive ui * feat: claim rewards * chore: prettier --------- Co-authored-by: swkatmask --- cspell.json | 2 +- src/App.tsx | 7 +- src/components/ProgressiveText.tsx | 31 +++++++++ src/components/StakeMaskStatusCard/index.tsx | 2 +- src/components/UserStatus/RewardCard.tsx | 69 ++++++++++++++++---- src/components/UserStatus/StakedMask.tsx | 39 +++++++++-- src/components/UserStatus/index.tsx | 20 +++--- src/helpers/formatNumber.ts | 3 +- src/hooks/useLinkTwitter.ts | 27 ++++---- src/hooks/useUserInfo.ts | 13 ++-- src/locales/en.po | 13 ++-- src/locales/en.ts | 2 +- src/modals/BaseModal.tsx | 21 ++---- src/modals/StakeModal.tsx | 8 ++- src/modals/UITaskManager.tsx | 10 +-- src/styles/index.css | 7 +- src/styles/theme.ts | 6 +- src/types/api.ts | 6 +- 18 files changed, 198 insertions(+), 88 deletions(-) create mode 100644 src/components/ProgressiveText.tsx diff --git a/cspell.json b/cspell.json index 99a786a..09798e6 100644 --- a/cspell.json +++ b/cspell.json @@ -1,7 +1,7 @@ { "version": "0.1", "language": "en", - "words": ["immer", "Merkle", "sepolia", "urlcat", "zustand", "masknet", "unstake"], + "words": ["immer", "masknet", "Merkle", "sepolia", "unstake", "urlcat", "webp", "zustand"], "ignoreWords": [ "Lingui", "lingui", diff --git a/src/App.tsx b/src/App.tsx index 7c20918..cee01ce 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { createRouter, RouterProvider } from '@tanstack/react-router' import { StrictMode } from 'react' -import { ChakraBaseProvider } from '@chakra-ui/react' +import { ChakraBaseProvider, ToastProviderProps } from '@chakra-ui/react' import { i18n } from '@lingui/core' import { I18nProvider } from '@lingui/react' import { QueryClientProvider } from '@tanstack/react-query' @@ -20,11 +20,14 @@ declare module '@tanstack/react-router' { } } +const toastOptions = { + defaultOptions: { position: 'top-right' }, +} satisfies ToastProviderProps export function App() { return ( - + diff --git a/src/components/ProgressiveText.tsx b/src/components/ProgressiveText.tsx new file mode 100644 index 0000000..cbfb037 --- /dev/null +++ b/src/components/ProgressiveText.tsx @@ -0,0 +1,31 @@ +import { Text, TextProps, Skeleton } from '@chakra-ui/react' +import { forwardRef, memo, type ReactNode } from 'react' + +export interface ProgressiveTextProps extends TextProps { + loading?: boolean + skeletonWidth?: string | number + skeletonHeight?: string | number + fallback?: ReactNode + component?: string +} + +export const ProgressiveText = memo( + forwardRef(function ProgressiveText( + { loading, skeletonWidth, skeletonHeight, children, fallback = '--', ...props }, + ref, + ) { + if (loading) { + return ( + + + + ) + } + + return ( + + {children ?? fallback} + + ) + }), +) diff --git a/src/components/StakeMaskStatusCard/index.tsx b/src/components/StakeMaskStatusCard/index.tsx index 5c873d8..42800a7 100644 --- a/src/components/StakeMaskStatusCard/index.tsx +++ b/src/components/StakeMaskStatusCard/index.tsx @@ -170,7 +170,7 @@ export const StakeMaskStatusCard: FC = ({ ...props }) spacing={6} > {pool?.apr ? ( - + @@ -17,12 +25,26 @@ export function RewardCard(props: Props) { - - 70000.00 - - - RSS3 - + + {formatNumber(reward?.amount ? +reward.amount : 0)} + + + {reward?.name} + diff --git a/src/components/UserStatus/StakedMask.tsx b/src/components/UserStatus/StakedMask.tsx index 039b2f6..ecec782 100644 --- a/src/components/UserStatus/StakedMask.tsx +++ b/src/components/UserStatus/StakedMask.tsx @@ -1,20 +1,44 @@ -import { Box, BoxProps, Button, HStack, Icon, Stack, Text } from '@chakra-ui/react' -import { ActionCard } from './ActionCard' +import { Box, BoxProps, Button, HStack, Icon, Stack } from '@chakra-ui/react' import { t } from '@lingui/macro' +import { ActionCard } from './ActionCard' +import { useAccount, useReadContract } from 'wagmi' +import { StakeManagerABI } from '../../abis/stakeManager.ts' import MaskLogo from '../../assets/mask-logo.svg?react' import Question from '../../assets/question.svg?react' +import { formatNumber } from '../../helpers/formatNumber.ts' +import { useUserInfo } from '../../hooks/useUserInfo.ts' +import { ProgressiveText } from '../ProgressiveText.tsx' import { Tooltip } from '../Tooltip.tsx' +import { stakeModal } from '../../modals/index.tsx' export function StakedMask(props: BoxProps) { + const account = useAccount() + const { data: userInfo, isLoading: loadingUserInfo } = useUserInfo() + const { isLoading, data: chainData } = useReadContract({ + abi: StakeManagerABI, + functionName: 'userInfos', + address: import.meta.env.STAKE_MANAGER_CONTRACT_ADDRESS, + args: account.address ? [account.address] : undefined, + }) return ( - - 2.343 - + + {chainData ? chainData[0].toLocaleString() : formatNumber(userInfo?.amount)} + - 2.343 + {t`+${userInfo?.score_per_hour} Points/h`} @@ -30,6 +54,9 @@ export function StakedMask(props: BoxProps) { _hover={{ bg: 'gradient.purple', transform: 'scale(1.01)' }} _active={{ transform: 'scale(0.9)' }} leftIcon={} + onClick={() => { + stakeModal.show() + }} > {t`Stake Mask`} diff --git a/src/components/UserStatus/index.tsx b/src/components/UserStatus/index.tsx index 2e9fbd4..a526945 100644 --- a/src/components/UserStatus/index.tsx +++ b/src/components/UserStatus/index.tsx @@ -1,18 +1,16 @@ import { Grid, GridProps, Skeleton } from '@chakra-ui/react' -import { useAccount } from 'wagmi' -import { UserTotalPoints } from './UserTotalPoint' -import { useUserInfo } from '../../hooks/useUserInfo' -import { usePoolStore } from '../../store/poolStore' import { t } from '@lingui/macro' -import { StakedMask } from './StakedMask' +import { useUserInfo } from '../../hooks/useUserInfo' import { RewardCard } from './RewardCard' +import { StakedMask } from './StakedMask' +import { UserTotalPoints } from './UserTotalPoint' export interface UserStatusProps extends GridProps {} export function UserStatus(props: UserStatusProps) { - const { address } = useAccount() - const store = usePoolStore() - const { data: userInfo } = useUserInfo(address, store.poolId) + const { data: userInfo } = useUserInfo() + const rss3 = userInfo?.reward_pool.find((x) => x.name === 'rss3') + const ton = userInfo?.reward_pool.find((x) => x.name === 'ton') if (!userInfo) return ( @@ -47,9 +45,9 @@ export function UserStatus(props: UserStatusProps) { {...props} > - - - + + + ) } diff --git a/src/helpers/formatNumber.ts b/src/helpers/formatNumber.ts index 7de11b5..280a13f 100644 --- a/src/helpers/formatNumber.ts +++ b/src/helpers/formatNumber.ts @@ -1,4 +1,5 @@ -export function formatNumber(num: number, digits = 2) { +export function formatNumber(num: number | undefined, digits = 2) { + if (!num) return num return num.toLocaleString('en-US', { maximumFractionDigits: digits, }) diff --git a/src/hooks/useLinkTwitter.ts b/src/hooks/useLinkTwitter.ts index fe35b0e..5c4bbd7 100644 --- a/src/hooks/useLinkTwitter.ts +++ b/src/hooks/useLinkTwitter.ts @@ -1,28 +1,23 @@ +import { useToast } from '@chakra-ui/react' import { useAsyncFn } from 'react-use' import urlcat from 'urlcat' -import { useAccount, useClient } from 'wagmi' -import { signMessage } from 'wagmi/actions' -import { config } from '../configs/wagmiClient' +import { UserRejectedRequestError } from 'viem' +import { useAccount, useSignMessage } from 'wagmi' import { FIREFLY_API_ROOT } from '../constants/api' import { fetchJSON } from '../helpers/fetchJSON' import { TwitterAuthorizeResponse } from '../types/api' -import { useToast } from '@chakra-ui/react' -import { UserRejectedRequestError } from 'viem' // Any message is ok. -const message = 'Hello, world!' +const message = 'Stake $MASK' export function useLinkTwitter() { const account = useAccount() - const client = useClient() const toast = useToast() + const { signMessageAsync } = useSignMessage() return useAsyncFn(async () => { - if (!account.address || !client) return + if (!account.address) return try { - const signed = await signMessage(config, { - account: account.address, - message: message, - }) + const signed = await signMessageAsync({ message }) const url = urlcat(FIREFLY_API_ROOT, '/v1/mask_stake/twitter/authorize', { original_message: message, signature_message: signed.slice(2), // omit 0x @@ -31,6 +26,11 @@ export function useLinkTwitter() { const res = await fetchJSON(url) if (res.code !== 200) { console.error('Failed to get twitter authorize', res.message, res.reason) + toast({ + status: 'error', + title: 'Failed to get twitter authorize', + description: res.message, + }) return } location.href = res.data.url @@ -38,12 +38,11 @@ export function useLinkTwitter() { if (err instanceof UserRejectedRequestError) { toast({ status: 'error', - position: 'top-right', title: err.details, }) return } throw err } - }, [account.address, client]) + }, [account.address, signMessageAsync]) } diff --git a/src/hooks/useUserInfo.ts b/src/hooks/useUserInfo.ts index 6565ac6..e3d228e 100644 --- a/src/hooks/useUserInfo.ts +++ b/src/hooks/useUserInfo.ts @@ -3,15 +3,20 @@ import { fetchJSON } from '../helpers/fetchJSON' import { FIREFLY_API_ROOT } from '../constants/api' import { UserInfo, UserInfoResponse } from '../types/api' import urlcat from 'urlcat' +import { useAccount } from 'wagmi' +import { usePoolStore } from '../store/poolStore' -export function useUserInfo(address: string | undefined, pool_id: number | null) { +export function useUserInfo() { + const { address } = useAccount() + const store = usePoolStore() + const poolId = store.poolId return useQuery({ - enabled: !!pool_id && !!address, - queryKey: ['user-info', address, pool_id], + enabled: !!poolId && !!address, + queryKey: ['user-info', address, poolId], queryFn: async () => { const url = urlcat(FIREFLY_API_ROOT, '/v1/mask_stake/user_info', { address, - pool_id: pool_id, + pool_id: poolId, }) return fetchJSON(url) }, diff --git a/src/locales/en.po b/src/locales/en.po index 9166534..fd98909 100644 --- a/src/locales/en.po +++ b/src/locales/en.po @@ -13,6 +13,10 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" +#: src/components/UserStatus/StakedMask.tsx +msgid "+{0} Points/h" +msgstr "" + #: src/components/UserStatus/StakedMask.tsx msgid "1 staked MASK will generate 1 point per hour." msgstr "" @@ -38,10 +42,6 @@ msgstr "" msgid "Claim" msgstr "" -#: src/modals/BaseModal.tsx -msgid "Close" -msgstr "" - #: src/modals/StakeModal.tsx #: src/modals/StakeModal.tsx msgid "Connect Wallet" @@ -137,6 +137,10 @@ msgstr "" msgid "MAX" msgstr "" +#: src/components/UserStatus/RewardCard.tsx +msgid "No match pool found." +msgstr "" + #: src/components/StakeMaskStatusCard/ActivityStatusTag.tsx msgid "Not started" msgstr "" @@ -177,6 +181,7 @@ msgstr "" #: src/components/Nav/index.tsx #: src/modals/StakeModal.tsx +#: src/modals/StakeModal.tsx msgid "Stake" msgstr "" diff --git a/src/locales/en.ts b/src/locales/en.ts index 088b1be..7a8f95d 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -1 +1 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages:Messages=JSON.parse("{\"RZKpwz\":\"1 staked MASK will generate 1 point per hour.\",\"uyJsf6\":\"About\",\"5MojdE\":\"Accept cookies\",\"70wH1Q\":\"APR\",\"nvV/Td\":[\"Balance: <0>\",[\"0\"],\"\"],\"hom7qf\":\"Claim\",\"yz7wBu\":\"Close\",\"CtByM7\":\"Connect Wallet\",\"YcfUZ9\":\"Contact Us\",\"AodcaS\":\"Cookie Policy\",\"QSMBSI\":\"Cookies are small text files that are stored on your computer or mobile device when you visit a website. They are used to store information about you and your internet usage habits to improve your online experience. Cookies enable websites to recognize your device and remember some information about your preferences.\",\"aqK0+O\":\"Estimated Rewards\",\"aJ4pMe\":\"FAQs\",\"INa0YU\":\"How to Control Cookies\",\"5x0Zyr\":\"If you have any questions or concerns about this Cookie Policy, please contact us at:\",\"v5W7I0\":\"info@dimension.im\",\"isuyW5\":\"Introduction\\nMask Network is committed to protecting the privacy of its users. This Privacy Policy outlines how we collect, use, disclose, and safeguard your information when you stake Mask to receive other token rewards through our website. By using our service, you consent to the data practices described in this policy.\\nInformation Collection and Use\\nInformation You Provide to Us\\nWe collect information you provide directly to us when you use our services. This may include:\\nContact information, such as your name, email address, and phone number.\\nBlockchain-related information, such as your wallet addresses.\\nSocial media account information, including but not limited to accounts on platforms like x.com.\\nWhen you choose to link your social media accounts or provide your wallet addresses, we may display this information on our website to enhance the user experience and foster a community environment. We encourage you to review the privacy settings and policies of any social media platforms you interact with to understand how your information may be shared and used.\\nInformation We Collect Automatically\\nWhen you access our website, we may automatically collect information about your device and usage, including:\\nIP address, browser type, and operating system.\\nInformation about your interactions with our website, such as the pages you visit.\\nUse of Your Information\\nWe use the information we collect about you to:\\nProvide, maintain, and improve our services.\\nDisplay your social media profiles and wallet addresses on our website, subject to your preferences and permissions.\\nRespond to your inquiries and provide customer support.\\nMonitor and analyze trends, usage, and activities in connection with our services.\\nSharing of Your Information\\nWe may share your information with third parties in the following situations:\\nWith your consent or at your direction.\\nWith service providers who assist us in our operations, under confidentiality agreements.\\nIf required by law or to protect the rights, property, or safety of [Your Website Name], our users, or others.\\nSecurity\\nWe take reasonable measures to protect your information from unauthorized access, use, alteration, and destruction. However, no internet or email transmission is ever fully secure or error-free.\\nChanges to This Privacy Policy\\nWe may update this Privacy Policy from time to time. We will notify you of any changes by posting the new policy on this page and updating the \\\"Last Updated\\\" date.\\nContact Us\\nIf you have any questions about this Privacy Policy, please contact us at info@dimension.im.\",\"uq8EoS\":\"Last Updated: 03.10 2024\",\"zwWKhA\":\"Learn more\",\"5ZMh6R\":\"Link 𝕏\",\"4HtGBb\":\"MAX\",\"mu1gVr\":\"Not started\",\"uz8dR/\":\"On going\",\"NukQm3\":\"Persistent Cookies: These cookies are stored on your device until they reach their expiration date or are manually deleted. They are used to remember your preferences and provide a personalized experience when you revisit the website.\",\"fE6ylD\":\"Please connect first\",\"OKEQCB\":\"Pool Liquidity\",\"LcET2C\":\"Privacy Policy\",\"BEn/IE\":\"Session Cookies: These cookies exist only during your visit to the website and are automatically deleted when you close your browser. They are used to maintain session state, such as storing the contents of your shopping cart.\",\"F8o9TO\":\"Share of Pool\",\"bdg6QA\":\"Since 2019 to Now | Mask.io\",\"JYKRJS\":\"Stake\",\"tcsT9V\":\"Stake Amount\",\"dy48iv\":\"Stake Mask\",\"hQngH8\":\"Stake Mask Now\",\"NE0dj/\":\"Staked MASK can be unstake after the campaign ends.\",\"ZoLkCW\":\"Staking Ranking\",\"PSGjEt\":\"Terms of Use\",\"8X0GDU\":\"Terms of Use for Mask.io\\nAcceptance of Terms\\nBy accessing and using Mask.io , you agree to be bound by these Terms of Use (\\\"Terms\\\"). If you do not agree to these Terms, you may not use our services.\\nChanges to Terms\\nWe reserve the right to modify these Terms at any time. We will notify you of any changes by posting the new Terms on this page. Your continued use of our services after any changes indicates your acceptance of the new Terms.\\nUse of Our Services\\n[Your Website Name] allows you to stake Mask to receive other token rewards. You agree to use our services only for lawful purposes and in accordance with these Terms.\\nAccount Responsibilities\\nYou are responsible for maintaining the confidentiality of your account information and for all activities that occur under your account.\\nYou agree to notify us immediately of any unauthorized use of your account.\\nProhibited Activities\\nYou agree not to engage in any of the following prohibited activities:\\nUsing our services for any illegal purpose or in violation of any local, state, national, or international law.\\nInfringing on the intellectual property rights of others or the intellectual property rights of Mask.io.\\nAttempting to gain unauthorized access to our services or another user's account.\\nInterfering with the security-related features of our services.\\nIntellectual Property\\nAll content, trademarks, logos, and service marks displayed on Mask.io are our property or the property of other third parties. You are not permitted to use these Marks without our prior written consent or the consent of such third party which may own the Marks.\\nDisclaimer of Warranties\\nOur services are provided on an \\\"as is\\\" and \\\"as available\\\" basis. Mask.io expressly disclaims all warranties of any kind, whether express or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement.\\nLimitation of Liability\\n[Your Website Name] shall not be liable for any indirect, incidental, special, consequential, or punitive damages, including but not limited to, damages for loss of profits, goodwill, use, data, or other intangible losses, resulting from the use or the inability to use our services.\\nIndemnification\\nYou agree to indemnify and hold harmless Mask.io and its officers, directors, employees, and agents from any and all claims, damages, expenses, and liabilities arising out of your use of our services, your violation of these Terms, or your violation of the rights of a third party.\\nGoverning Law\\nThese Terms shall be governed and construed in accordance with the laws of the People's Republic of China, without regard to its conflict of law provisions.\\nContact Us\\nIf you have any questions about these Terms, please contact us at info@dimension.im.\",\"zSbbOZ\":\"The staking addresses need to pass Go+ security check. Note that staking is not available in some restricted regions.<0>More\",\"Pim9Ok\":\"Third-Party Cookies: The Site may use cookies from third-party service providers for traffic analysis and user behavior tracking, as well as to display ads relevant to your interests.\",\"qIYYXT\":\"This is the first phase of the MASK staking event. You will be able to retrieve your staked Mask tokens and reward tokens only after the event concludes.\",\"DYT0zx\":\"This website (hereinafter referred to as \\\"the Site\\\") uses cookies. By using the Site, you consent to the use of cookies as described in this Cookie Policy.\",\"wiEMgy\":[\"Time\",[\"0\"]],\"a7kfrG\":\"Total MASK Staked\",\"yxKbv1\":\"Total points\",\"qmYDVk\":\"Total Rewards\",\"KTFOT8\":\"Types of Cookies We Use\",\"Jbb2+W\":\"Unlock MASK Time\",\"ilbzKn\":\"Updates to the Cookie Policy\",\"9dsM+S\":\"We reserve the right to update this Cookie Policy at any time. The updated Cookie Policy will be posted on this page and will become effective immediately. We recommend checking back regularly for updates.\",\"a1Cyi2\":\"We use our own cookies.\",\"Qgqys4\":\"What Are Cookies?\",\"2z+KTh\":\"You can manage and delete cookies through your browser settings. Please note that disabling cookies may affect your access to and experience on the Site.\",\"gGN2Rp\":\"Your Portal To The New, Open Internet.\"}"); \ No newline at end of file +/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages:Messages=JSON.parse("{\"muaVuc\":[\"+\",[\"0\"],\" Points/h\"],\"RZKpwz\":\"1 staked MASK will generate 1 point per hour.\",\"uyJsf6\":\"About\",\"5MojdE\":\"Accept cookies\",\"70wH1Q\":\"APR\",\"nvV/Td\":[\"Balance: <0>\",[\"0\"],\"\"],\"hom7qf\":\"Claim\",\"CtByM7\":\"Connect Wallet\",\"YcfUZ9\":\"Contact Us\",\"AodcaS\":\"Cookie Policy\",\"QSMBSI\":\"Cookies are small text files that are stored on your computer or mobile device when you visit a website. They are used to store information about you and your internet usage habits to improve your online experience. Cookies enable websites to recognize your device and remember some information about your preferences.\",\"aqK0+O\":\"Estimated Rewards\",\"aJ4pMe\":\"FAQs\",\"INa0YU\":\"How to Control Cookies\",\"5x0Zyr\":\"If you have any questions or concerns about this Cookie Policy, please contact us at:\",\"v5W7I0\":\"info@dimension.im\",\"isuyW5\":\"Introduction\\nMask Network is committed to protecting the privacy of its users. This Privacy Policy outlines how we collect, use, disclose, and safeguard your information when you stake Mask to receive other token rewards through our website. By using our service, you consent to the data practices described in this policy.\\nInformation Collection and Use\\nInformation You Provide to Us\\nWe collect information you provide directly to us when you use our services. This may include:\\nContact information, such as your name, email address, and phone number.\\nBlockchain-related information, such as your wallet addresses.\\nSocial media account information, including but not limited to accounts on platforms like x.com.\\nWhen you choose to link your social media accounts or provide your wallet addresses, we may display this information on our website to enhance the user experience and foster a community environment. We encourage you to review the privacy settings and policies of any social media platforms you interact with to understand how your information may be shared and used.\\nInformation We Collect Automatically\\nWhen you access our website, we may automatically collect information about your device and usage, including:\\nIP address, browser type, and operating system.\\nInformation about your interactions with our website, such as the pages you visit.\\nUse of Your Information\\nWe use the information we collect about you to:\\nProvide, maintain, and improve our services.\\nDisplay your social media profiles and wallet addresses on our website, subject to your preferences and permissions.\\nRespond to your inquiries and provide customer support.\\nMonitor and analyze trends, usage, and activities in connection with our services.\\nSharing of Your Information\\nWe may share your information with third parties in the following situations:\\nWith your consent or at your direction.\\nWith service providers who assist us in our operations, under confidentiality agreements.\\nIf required by law or to protect the rights, property, or safety of [Your Website Name], our users, or others.\\nSecurity\\nWe take reasonable measures to protect your information from unauthorized access, use, alteration, and destruction. However, no internet or email transmission is ever fully secure or error-free.\\nChanges to This Privacy Policy\\nWe may update this Privacy Policy from time to time. We will notify you of any changes by posting the new policy on this page and updating the \\\"Last Updated\\\" date.\\nContact Us\\nIf you have any questions about this Privacy Policy, please contact us at info@dimension.im.\",\"uq8EoS\":\"Last Updated: 03.10 2024\",\"zwWKhA\":\"Learn more\",\"5ZMh6R\":\"Link 𝕏\",\"4HtGBb\":\"MAX\",\"eUt9gV\":\"No match pool found.\",\"mu1gVr\":\"Not started\",\"uz8dR/\":\"On going\",\"NukQm3\":\"Persistent Cookies: These cookies are stored on your device until they reach their expiration date or are manually deleted. They are used to remember your preferences and provide a personalized experience when you revisit the website.\",\"fE6ylD\":\"Please connect first\",\"OKEQCB\":\"Pool Liquidity\",\"LcET2C\":\"Privacy Policy\",\"BEn/IE\":\"Session Cookies: These cookies exist only during your visit to the website and are automatically deleted when you close your browser. They are used to maintain session state, such as storing the contents of your shopping cart.\",\"F8o9TO\":\"Share of Pool\",\"bdg6QA\":\"Since 2019 to Now | Mask.io\",\"JYKRJS\":\"Stake\",\"tcsT9V\":\"Stake Amount\",\"dy48iv\":\"Stake Mask\",\"hQngH8\":\"Stake Mask Now\",\"NE0dj/\":\"Staked MASK can be unstake after the campaign ends.\",\"ZoLkCW\":\"Staking Ranking\",\"PSGjEt\":\"Terms of Use\",\"8X0GDU\":\"Terms of Use for Mask.io\\nAcceptance of Terms\\nBy accessing and using Mask.io , you agree to be bound by these Terms of Use (\\\"Terms\\\"). If you do not agree to these Terms, you may not use our services.\\nChanges to Terms\\nWe reserve the right to modify these Terms at any time. We will notify you of any changes by posting the new Terms on this page. Your continued use of our services after any changes indicates your acceptance of the new Terms.\\nUse of Our Services\\n[Your Website Name] allows you to stake Mask to receive other token rewards. You agree to use our services only for lawful purposes and in accordance with these Terms.\\nAccount Responsibilities\\nYou are responsible for maintaining the confidentiality of your account information and for all activities that occur under your account.\\nYou agree to notify us immediately of any unauthorized use of your account.\\nProhibited Activities\\nYou agree not to engage in any of the following prohibited activities:\\nUsing our services for any illegal purpose or in violation of any local, state, national, or international law.\\nInfringing on the intellectual property rights of others or the intellectual property rights of Mask.io.\\nAttempting to gain unauthorized access to our services or another user's account.\\nInterfering with the security-related features of our services.\\nIntellectual Property\\nAll content, trademarks, logos, and service marks displayed on Mask.io are our property or the property of other third parties. You are not permitted to use these Marks without our prior written consent or the consent of such third party which may own the Marks.\\nDisclaimer of Warranties\\nOur services are provided on an \\\"as is\\\" and \\\"as available\\\" basis. Mask.io expressly disclaims all warranties of any kind, whether express or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement.\\nLimitation of Liability\\n[Your Website Name] shall not be liable for any indirect, incidental, special, consequential, or punitive damages, including but not limited to, damages for loss of profits, goodwill, use, data, or other intangible losses, resulting from the use or the inability to use our services.\\nIndemnification\\nYou agree to indemnify and hold harmless Mask.io and its officers, directors, employees, and agents from any and all claims, damages, expenses, and liabilities arising out of your use of our services, your violation of these Terms, or your violation of the rights of a third party.\\nGoverning Law\\nThese Terms shall be governed and construed in accordance with the laws of the People's Republic of China, without regard to its conflict of law provisions.\\nContact Us\\nIf you have any questions about these Terms, please contact us at info@dimension.im.\",\"zSbbOZ\":\"The staking addresses need to pass Go+ security check. Note that staking is not available in some restricted regions.<0>More\",\"Pim9Ok\":\"Third-Party Cookies: The Site may use cookies from third-party service providers for traffic analysis and user behavior tracking, as well as to display ads relevant to your interests.\",\"qIYYXT\":\"This is the first phase of the MASK staking event. You will be able to retrieve your staked Mask tokens and reward tokens only after the event concludes.\",\"DYT0zx\":\"This website (hereinafter referred to as \\\"the Site\\\") uses cookies. By using the Site, you consent to the use of cookies as described in this Cookie Policy.\",\"wiEMgy\":[\"Time\",[\"0\"]],\"a7kfrG\":\"Total MASK Staked\",\"yxKbv1\":\"Total points\",\"qmYDVk\":\"Total Rewards\",\"KTFOT8\":\"Types of Cookies We Use\",\"Jbb2+W\":\"Unlock MASK Time\",\"ilbzKn\":\"Updates to the Cookie Policy\",\"9dsM+S\":\"We reserve the right to update this Cookie Policy at any time. The updated Cookie Policy will be posted on this page and will become effective immediately. We recommend checking back regularly for updates.\",\"a1Cyi2\":\"We use our own cookies.\",\"Qgqys4\":\"What Are Cookies?\",\"2z+KTh\":\"You can manage and delete cookies through your browser settings. Please note that disabling cookies may affect your access to and experience on the Site.\",\"gGN2Rp\":\"Your Portal To The New, Open Internet.\"}"); \ No newline at end of file diff --git a/src/modals/BaseModal.tsx b/src/modals/BaseModal.tsx index cea5929..9e454da 100644 --- a/src/modals/BaseModal.tsx +++ b/src/modals/BaseModal.tsx @@ -4,9 +4,9 @@ import { DrawerContent, DrawerHeader, DrawerOverlay, - IconButton, Modal, ModalBody, + ModalCloseButton, ModalContent, ModalContentProps, ModalHeader, @@ -16,30 +16,19 @@ import { useBreakpointValue, } from '@chakra-ui/react' -import { t } from '@lingui/macro' import { ReactNode } from 'react' -import Close from '../assets/close.svg?react' interface Props extends ModalProps { title: ReactNode width: ModalContentProps['width'] height: ModalContentProps['height'] } -export function BaseModal({ title, width, height, ...rest }: Props) { +export function BaseModal({ title, width, ...rest }: Props) { const isMobile = useBreakpointValue({ base: true, md: false }) const header = ( <> {title} - } - onClick={rest.onClose} - /> + ) if (isMobile) { @@ -62,8 +51,8 @@ export function BaseModal({ title, width, height, ...rest }: Props) { return ( - - + + {header} diff --git a/src/modals/StakeModal.tsx b/src/modals/StakeModal.tsx index 8efe022..02b7f40 100644 --- a/src/modals/StakeModal.tsx +++ b/src/modals/StakeModal.tsx @@ -107,6 +107,10 @@ export function StakeModal(props: Props) { placeholder={t`Stake Amount`} border="none" outline="none" + fontSize="40px" + fontFamily="input" + fontWeight={700} + autoFocus value={amount} max={1e18} onChange={(e) => { @@ -197,7 +201,9 @@ export function StakeModal(props: Props) { - + ) diff --git a/src/modals/UITaskManager.tsx b/src/modals/UITaskManager.tsx index 2e21cbd..51ded38 100644 --- a/src/modals/UITaskManager.tsx +++ b/src/modals/UITaskManager.tsx @@ -12,8 +12,7 @@ export interface BaseDialogProps extends Pick, Result>( Component: ComponentType, @@ -59,10 +58,10 @@ export const createUITaskManager = , show(options?: Omit, signal?: AbortSignal) { const [promise, resolve, reject] = defer() id += 1 - signal?.addEventListener('abort', function abortHandler() { + function abortHandler() { resolve(null) - signal.removeEventListener('abort', abortHandler) - }) + } + signal?.addEventListener('abort', abortHandler, { once: true }) const newTask: Task = { id, isOpen: true, @@ -73,6 +72,7 @@ export const createUITaskManager = , } setTasks((list) => [...list, newTask]) promise.then(() => { + signal?.removeEventListener('abort', abortHandler) removeTask(newTask.id) }) return promise diff --git a/src/styles/index.css b/src/styles/index.css index 0134147..b223b3c 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -56,11 +56,14 @@ body .purple-gradient-button { background: var(--chakra-colors-gradient-purple); color: var(--chakra-colors-neutrals-8); } -body .purple-gradient-button:hover { +body .purple-gradient-button:disabled { + opacity: 0.8; +} +body .purple-gradient-button:not(:disabled):hover { background: var(--chakra-colors-gradient-purple); transform: scale(1.01); } -body .purple-gradient-button:active { +body .purple-gradient-button:not(:disabled):active { transform: scale(0.95); } diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 1308f27..149b2c1 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -6,8 +6,6 @@ import { Drawer } from './components/Drawer.ts' window.localStorage.setItem('chakra-ui-color-mode', 'dark') -console.log(chakraTheme.components.Tooltip) - export const theme = extendBaseTheme({ components: { ...chakraTheme.components, @@ -59,6 +57,10 @@ export const theme = extendBaseTheme({ shadows: { card: '0px 5px 10px 0px rgba(0, 0, 0, 0.05), 0px 15px 30px 0px rgba(0, 0, 0, 0.05), 0px 20px 40px 0px rgba(0, 0, 0, 0.05)', }, + fonts: { + input: + 'Inter, SFRounded, ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', + }, styles: { global: () => { return { diff --git a/src/types/api.ts b/src/types/api.ts index b20ce15..f82445c 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -22,15 +22,15 @@ export interface UserInfo { twitter_display_name: string twitter_image: string address_type: string - reward_pool: { + reward_pool: Array<{ reward_pool_id: number amount: string big_amount: string /** token name */ name: string /** Merkle tree */ - proof: string[] - } + proof: Array<`0x${string}`> + }> } export type UserInfoResponse = Response