├── .env ├── .env.staging ├── .env.testnet ├── .github ├── pull_request_template.md └── workflows │ ├── dev-build.yml │ ├── discord.yml │ ├── mainnet-prod-pipeline.yml │ ├── pr-build.yml │ ├── prod-build.yml │ └── staging-build.yml ├── .gitignore ├── .husky ├── .gitignore ├── pre-commit └── pre-push ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── eslint.config.js ├── index.html ├── package.json ├── public ├── consumer-rewards │ └── rewards-period.svg ├── favicon-new.ico ├── favicon.ico ├── logo-new.svg ├── manifest.json ├── robots.txt ├── static │ ├── appIcon.svg │ ├── base.png │ ├── booster.svg │ ├── check.png │ ├── connectWallet.png │ ├── default.project.png │ ├── eth.png │ ├── geo.json │ ├── kepler-logo.svg │ ├── logo.png │ ├── matrix-logo.svg │ ├── metaMask.svg │ ├── metamask.png │ ├── notification.svg │ ├── rainbow.svg │ ├── rocket.svg │ ├── rpcError.svg │ ├── sqt.svg │ ├── sqtoken.png │ ├── switch-network.png │ ├── talisman.png │ ├── thumb.svg │ ├── usdc.png │ ├── walletConnect.svg │ └── x.svg └── sw.js ├── src ├── App.css ├── App.tsx ├── components │ ├── APYTooltip │ │ └── index.tsx │ ├── AccountActions │ │ ├── AccountActions.module.less │ │ ├── AccountActions.tsx │ │ └── index.ts │ ├── AlertBanner │ │ └── AlertBanner.tsx │ ├── AppPageHeader │ │ ├── AppPageHeader.module.css │ │ ├── AppPageHeader.tsx │ │ └── index.tsx │ ├── AppSidebar │ │ ├── AppSidebar.module.css │ │ ├── AppSidebar.tsx │ │ └── index.tsx │ ├── Banner │ │ ├── Banner.module.css │ │ ├── Banner.tsx │ │ └── index.ts │ ├── BarCharts │ │ ├── index.module.less │ │ └── index.tsx │ ├── BillingTransferModal │ │ ├── BillingTransferModal.tsx │ │ └── index.ts │ ├── BreadcrumbNav │ │ ├── BreadcrumbNav.tsx │ │ └── index.tsx │ ├── Button │ │ ├── Button.module.css │ │ ├── Button.tsx │ │ └── index.ts │ ├── Card │ │ ├── Card.module.css │ │ └── index.tsx │ ├── ConnectWallet │ │ ├── ChainStatus.module.css │ │ ├── ChainStatus.tsx │ │ ├── ConnectWallet.module.less │ │ ├── ConnectWallet.tsx │ │ └── index.ts │ ├── Copy │ │ ├── Copy.module.css │ │ ├── Copy.tsx │ │ └── index.ts │ ├── CreateFlexPlan │ │ ├── index.module.less │ │ └── index.tsx │ ├── CurEra │ │ ├── CurEra.module.css │ │ ├── CurEra.tsx │ │ └── index.ts │ ├── DeploymentInfo │ │ ├── DeploymentInfo.module.css │ │ ├── DeploymentInfo.tsx │ │ └── index.ts │ ├── Description │ │ ├── Description.tsx │ │ └── index.tsx │ ├── Detail │ │ ├── Detail.tsx │ │ ├── Details.module.css │ │ └── index.ts │ ├── DoAllocate │ │ ├── DoAllocate.tsx │ │ └── index.module.less │ ├── DoBooster │ │ ├── index.module.less │ │ └── index.tsx │ ├── Dropdown │ │ ├── Dropdown.module.css │ │ ├── Dropdown.tsx │ │ └── index.ts │ ├── EmptyList │ │ ├── EmptyList.module.css │ │ ├── EmptyList.tsx │ │ └── index.ts │ ├── EstimatedNextEraLayout │ │ └── index.tsx │ ├── Expand │ │ ├── Expand.tsx │ │ └── index.module.less │ ├── FTextInput │ │ ├── FTextInput.module.less │ │ └── FTextInput.tsx │ ├── GetEndpoint │ │ ├── index.module.less │ │ └── index.tsx │ ├── GlobalBanner │ │ ├── GlobalBanner.module.css │ │ ├── GlobalBanner.tsx │ │ └── index.ts │ ├── Header │ │ ├── Header.module.less │ │ ├── Header.tsx │ │ └── index.ts │ ├── IPFSImage │ │ ├── IPFSImage.tsx │ │ └── index.ts │ ├── Icons │ │ ├── Icons.module.less │ │ └── Icons.tsx │ ├── ImageInput │ │ ├── ImageInput.module.css │ │ ├── ImageInput.tsx │ │ └── index.ts │ ├── IndexerDetails │ │ ├── IndexerDetails.module.less │ │ ├── IndexerDetails.tsx │ │ ├── IndexerName.tsx │ │ ├── PlansTable.tsx │ │ ├── PriceQueries.tsx │ │ ├── Progress.tsx │ │ ├── Row.tsx │ │ └── index.ts │ ├── IndexerProgress │ │ ├── IndexerProgress.module.css │ │ ├── IndexerProgress.tsx │ │ └── index.ts │ ├── LineChartWithMarkLine │ │ ├── index.module.less │ │ └── index.tsx │ ├── LineCharts │ │ ├── index.module.less │ │ └── index.tsx │ ├── Modal │ │ ├── Modal.module.css │ │ ├── Modal.tsx │ │ └── index.ts │ ├── ModalApproveToken │ │ ├── ModalApproveToken.module.css │ │ ├── ModalApproveToken.tsx │ │ └── index.ts │ ├── ModalClaimIndexerRewards │ │ ├── ModalClaimIndexerRewards.module.css │ │ ├── ModalClaimIndexerRewards.tsx │ │ └── index.ts │ ├── ModalInput │ │ ├── ModalInput.module.css │ │ ├── ModalInput.tsx │ │ └── index.ts │ ├── NormalError │ │ ├── index.module.less │ │ └── index.tsx │ ├── Notification │ │ ├── Notification.tsx │ │ └── index.tsx │ ├── NotificationCentre │ │ ├── index.module.less │ │ ├── index.tsx │ │ ├── useMakeNotification.ts │ │ └── useToastNotificationModal.tsx │ ├── NumberInput │ │ ├── NumberInput.module.less │ │ ├── NumberInput.tsx │ │ └── index.ts │ ├── PasswordField │ │ ├── index.module.less │ │ └── index.tsx │ ├── ProjectCard │ │ ├── ProjectCard.module.css │ │ ├── ProjectCard.tsx │ │ └── index.ts │ ├── ProjectDeployments │ │ ├── ProjectDeployments.module.less │ │ ├── ProjectDeployments.tsx │ │ └── index.ts │ ├── ProjectHeader │ │ ├── ProjectHeader.module.less │ │ ├── ProjectHeader.tsx │ │ └── index.ts │ ├── ProjectOverview │ │ ├── ProjectOverview.module.less │ │ ├── ProjectOverview.tsx │ │ └── index.ts │ ├── RpcError │ │ ├── index.module.less │ │ └── index.tsx │ ├── RpcPlayground │ │ ├── RpcPlayground.tsx │ │ └── index.module.less │ ├── SearchInput │ │ ├── SearchInput.module.css │ │ ├── SearchInput.tsx │ │ └── index.ts │ ├── SeasonProgress │ │ ├── SeasonProgress.module.css │ │ └── SeasonProgress.tsx │ ├── Sidebar │ │ ├── Sidebar.module.css │ │ ├── Sidebar.tsx │ │ └── index.tsx │ ├── Spinner │ │ ├── Spinner.module.css │ │ ├── Spinner.tsx │ │ └── index.ts │ ├── Stat │ │ ├── Stat.module.css │ │ ├── Stat.tsx │ │ └── index.ts │ ├── Status │ │ ├── Status.module.css │ │ ├── Status.tsx │ │ └── index.ts │ ├── StepButton │ │ ├── StepButton.module.css │ │ └── index.tsx │ ├── SummaryList │ │ ├── SummaryList.module.css │ │ ├── SummaryList.tsx │ │ └── index.ts │ ├── TabButton │ │ ├── TabButton.module.less │ │ ├── TabButton.tsx │ │ └── index.tsx │ ├── Table │ │ ├── AntDTable.tsx │ │ └── index.ts │ ├── TableText │ │ ├── TableText.module.css │ │ ├── TableText.tsx │ │ └── index.ts │ ├── TokenAmount │ │ ├── TokenAmount.tsx │ │ └── index.ts │ ├── TokenTooltip │ │ ├── TokenTooltip.tsx │ │ └── index.module.less │ ├── TransactionModal │ │ ├── TransactionModal.module.less │ │ ├── TransactionModal.tsx │ │ └── index.ts │ ├── Typography │ │ ├── Typography.module.css │ │ ├── Typography.tsx │ │ └── index.ts │ ├── UnsafeWarn │ │ └── index.tsx │ └── WalletRoute │ │ ├── WalletRoute.module.css │ │ ├── WalletRoute.tsx │ │ └── index.tsx ├── config │ ├── dayjsConf.ts │ ├── polyfill.ts │ ├── rainbowConf.tsx │ └── sentryConf.ts ├── const │ ├── bridge.ts │ └── const.ts ├── containers │ ├── AppInitialProvider.tsx │ ├── Container.tsx │ ├── IPFS.ts │ ├── ProjectMetadata.ts │ ├── ProjectRegistry.ts │ ├── QueryApolloProvider.tsx │ ├── SQToken.ts │ ├── Web3.tsx │ └── index.ts ├── globalScalars.d.ts ├── hooks │ ├── index.ts │ ├── useAddAllowance.ts │ ├── useApiEndpoint.ts │ ├── useAsyncMemo.tsx │ ├── useCommissionRate.ts │ ├── useConsumerHostServices.tsx │ ├── useCreateDeployment.tsx │ ├── useCreateProject.tsx │ ├── useDelegating.tsx │ ├── useDeploymentMetadata.tsx │ ├── useEra.ts │ ├── useEraValue.tsx │ ├── useEthersProvider.ts │ ├── useFetchMetadata.tsx │ ├── useForumApis.ts │ ├── useGetCapacityFromContract.ts │ ├── useGetDeploymentManifest.tsx │ ├── useGetFlexPlanPrice.tsx │ ├── useGetIfUnsafeDeployment.tsx │ ├── useIPFSMetadata.tsx │ ├── useIndexerGeoInformation.tsx │ ├── useIndexerMetadata.tsx │ ├── useInitContracts.ts │ ├── useIsIndexer.tsx │ ├── useIsLogin.ts │ ├── useIsMobile.ts │ ├── useLocalProjects.ts │ ├── useLockPeriod.tsx │ ├── useMaxUnstakeAmount.tsx │ ├── useMinCommissionRate.ts │ ├── useNetworkClient.ts │ ├── useOnScreen.tsx │ ├── useProject.tsx │ ├── useProjectFromQuery.tsx │ ├── useProjectList.module.less │ ├── useProjectList.tsx │ ├── usePropsValue.ts │ ├── useRequestServiceAgreementToken.ts │ ├── useRewardCollectStatus.tsx │ ├── useRouteQuery.tsx │ ├── useSortedIndexer.tsx │ ├── useSortedIndexerDeployments.tsx │ ├── useSpaceId.tsx │ ├── useSqtPrice.ts │ ├── useStudio.ts │ ├── useUpdateProjectMetadata.ts │ ├── useVerifyDeployment.1.ts │ ├── useVerifyDeployment.ts │ └── useWaitTransactionHandled.ts ├── i18n │ ├── en │ │ ├── consumer.ts │ │ ├── dashboard.ts │ │ ├── delegator.ts │ │ ├── explorer.ts │ │ ├── global.ts │ │ ├── index.ts │ │ ├── indexer.ts │ │ └── scanner.ts │ └── index.ts ├── images │ ├── playground.tsx │ └── rpcPlayground.tsx ├── index.less ├── index.tsx ├── models.tsx ├── pages │ ├── account │ │ ├── Account.module.less │ │ ├── AccountHeaders │ │ │ ├── Header.tsx │ │ │ └── Headers.module.css │ │ ├── Rewards │ │ │ ├── ClaimRewards.module.less │ │ │ ├── ClaimRewards.tsx │ │ │ ├── Rewards.module.css │ │ │ ├── Rewards.tsx │ │ │ └── index.ts │ │ ├── Staking │ │ │ ├── Breakdown.tsx │ │ │ ├── index.module.less │ │ │ └── index.tsx │ │ ├── Withdrawn │ │ │ ├── Locked │ │ │ │ ├── DoWithdraw │ │ │ │ │ ├── DoWithdraw.module.css │ │ │ │ │ ├── DoWithdraw.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Home │ │ │ │ │ ├── Locked.module.less │ │ │ │ │ ├── Locked.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── Withdrawn.module.css │ │ │ ├── Withdrawn.tsx │ │ │ └── index.ts │ │ └── index.tsx │ ├── bridge │ │ ├── index.module.less │ │ ├── index.tsx │ │ └── success │ │ │ ├── index.module.less │ │ │ └── index.tsx │ ├── consumer │ │ ├── MyBoostedProjects │ │ │ ├── MyBoostedProjects.tsx │ │ │ └── index.module.less │ │ ├── MyFlexPlans │ │ │ ├── BillingAction.tsx │ │ │ ├── ClaimFlexPlan.tsx │ │ │ ├── MyFlexPlanTable.tsx │ │ │ ├── MyFlexPlans.module.css │ │ │ ├── MyFlexPlans.tsx │ │ │ ├── MyHostedPlan │ │ │ │ ├── MyHostedPlan.module.less │ │ │ │ └── MyHostedPlan.tsx │ │ │ ├── OngoingFlexPlanActions.tsx │ │ │ ├── apiKeys.module.less │ │ │ ├── apiKeys.tsx │ │ │ └── index.ts │ │ ├── MyOffers │ │ │ ├── CancelOffer.tsx │ │ │ ├── CreateOffer │ │ │ │ ├── ChooseTemplate │ │ │ │ │ ├── ChooseTemplate.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CreateOffer.module.css │ │ │ │ ├── CreateOffer.tsx │ │ │ │ ├── OfferDetails │ │ │ │ │ ├── OfferDetails.module.css │ │ │ │ │ ├── OfferDetails.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SelectDeployment │ │ │ │ │ ├── SelectDeployment.module.css │ │ │ │ │ ├── SelectDeployment.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Summary │ │ │ │ │ ├── Summary.module.css │ │ │ │ │ ├── Summary.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── MyOffers.module.css │ │ │ ├── MyOffers.tsx │ │ │ ├── OfferTable.module.css │ │ │ ├── OfferTable.tsx │ │ │ └── index.ts │ │ ├── OfferMarketplace │ │ │ ├── AcceptOffer.module.css │ │ │ ├── AcceptOffer.tsx │ │ │ ├── CheckList.tsx │ │ │ ├── Marketplace.module.css │ │ │ ├── Marketplace.tsx │ │ │ └── index.ts │ │ ├── Playground │ │ │ ├── AuthPlayground.tsx │ │ │ ├── FlexPlayground.tsx │ │ │ ├── GraphQLQuery.tsx │ │ │ ├── Playground.module.css │ │ │ ├── RequestToken.tsx │ │ │ ├── SAPlayground.tsx │ │ │ └── index.ts │ │ ├── ServiceAgreements │ │ │ ├── ServiceAgreements.module.css │ │ │ ├── ServiceAgreements.tsx │ │ │ ├── ServiceAgreementsTable.tsx │ │ │ └── index.tsx │ │ └── index.tsx │ ├── dashboard │ │ ├── components │ │ │ ├── ActiveCard │ │ │ │ ├── ActiveCard.module.less │ │ │ │ └── ActiveCard.tsx │ │ │ ├── EraCard │ │ │ │ └── EraCard.tsx │ │ │ ├── ForumCard │ │ │ │ └── ForumCard.tsx │ │ │ ├── RewardsLineChart │ │ │ │ └── RewardsLineChart.tsx │ │ │ └── StakeAndDelegationLineChart │ │ │ │ └── StakeAndDelegationLineChart.tsx │ │ ├── index.module.less │ │ └── index.tsx │ ├── delegator │ │ ├── AllIndexers │ │ │ ├── AllIndexers.tsx │ │ │ └── IndexerList │ │ │ │ ├── IndexerList.module.css │ │ │ │ ├── IndexerList.tsx │ │ │ │ └── index.ts │ │ ├── DoDelegate │ │ │ ├── DelegateFrom.tsx │ │ │ ├── DoDelegate.module.less │ │ │ ├── DoDelegate.tsx │ │ │ └── index.ts │ │ ├── DoUndelegate │ │ │ ├── DoUndelegate.module.css │ │ │ ├── DoUndelegate.tsx │ │ │ └── index.ts │ │ ├── IndexerDetails │ │ │ ├── IndexerDetails.module.css │ │ │ └── IndexerDetails.tsx │ │ ├── Indexers.module.css │ │ ├── Indexers.tsx │ │ ├── MyDelegation.module.css │ │ ├── MyDelegation.tsx │ │ ├── TopIndexers │ │ │ ├── TopIndexers.tsx │ │ │ ├── TopIndexersList.module.css │ │ │ └── TopIndexersList.tsx │ │ └── index.tsx │ ├── explorer │ │ ├── Explorer.module.css │ │ ├── FlexPlans │ │ │ ├── CreateHostingPlan │ │ │ │ ├── CreateHostingPlan.tsx │ │ │ │ └── index.module.less │ │ │ ├── FlexPlans.module.less │ │ │ ├── FlexPlans.tsx │ │ │ └── index.ts │ │ ├── Home │ │ │ ├── Home.module.css │ │ │ └── Home.tsx │ │ ├── Project │ │ │ ├── Project.module.less │ │ │ ├── Project.tsx │ │ │ ├── components │ │ │ │ └── DeploymentRewardsChart.tsx │ │ │ └── type.ts │ │ └── index.tsx │ ├── indexer │ │ ├── IndexerProfile │ │ │ ├── IndexerProfile.tsx │ │ │ └── index.module.less │ │ ├── MyDelegators │ │ │ ├── MyDelegators.module.css │ │ │ ├── MyDelegators.tsx │ │ │ ├── OwnDelegator.module.css │ │ │ └── OwnDelegator.tsx │ │ ├── MyPlans │ │ │ ├── Create │ │ │ │ ├── Create.module.less │ │ │ │ ├── Create.tsx │ │ │ │ └── index.ts │ │ │ ├── Default │ │ │ │ ├── Default.tsx │ │ │ │ └── index.ts │ │ │ ├── List │ │ │ │ ├── List.module.less │ │ │ │ ├── List.tsx │ │ │ │ └── index.ts │ │ │ ├── Plans.module.css │ │ │ ├── Plans.tsx │ │ │ └── Specific │ │ │ │ ├── Specific.module.css │ │ │ │ ├── Specific.tsx │ │ │ │ └── index.ts │ │ ├── MyProjects │ │ │ ├── AutoReduceOverAllocation │ │ │ │ ├── index.module.less │ │ │ │ └── index.tsx │ │ │ ├── CurrentEraRewards │ │ │ │ └── index.tsx │ │ │ ├── LastEraBurntReason │ │ │ │ └── index.tsx │ │ │ ├── MyProjects.module.css │ │ │ ├── MyProjects.tsx │ │ │ └── OwnDeployments │ │ │ │ ├── OwnDeployments.module.css │ │ │ │ ├── OwnDeployments.tsx │ │ │ │ └── index.ts │ │ ├── MyStaking │ │ │ ├── DoStake │ │ │ │ ├── DoStake.tsx │ │ │ │ └── index.ts │ │ │ ├── Indexing │ │ │ │ ├── Indexing.module.css │ │ │ │ ├── Indexing.tsx │ │ │ │ └── index.ts │ │ │ ├── MyStaking.module.css │ │ │ ├── MyStaking.tsx │ │ │ └── SetCommissionRate │ │ │ │ ├── SetCommissionRate.tsx │ │ │ │ └── index.ts │ │ └── index.tsx │ └── projects │ │ ├── Create │ │ ├── Create.module.less │ │ ├── Create.tsx │ │ └── index.ts │ │ ├── Home │ │ ├── Home.module.less │ │ ├── Home.tsx │ │ └── index.ts │ │ ├── Project │ │ ├── Deployments.tsx │ │ ├── Project.module.less │ │ ├── Project.tsx │ │ └── index.ts │ │ ├── index.module.css │ │ └── index.tsx ├── react-i18next.d.ts ├── router │ ├── index.tsx │ └── routes.ts ├── stores │ ├── chatbox.ts │ ├── index.ts │ ├── notification.ts │ ├── project.ts │ └── web3Account.ts ├── utils │ ├── USDC │ │ ├── USDC.json │ │ └── index.ts │ ├── colors.ts │ ├── constants.ts │ ├── dateFormatters.ts │ ├── eip712.ts │ ├── eip721SignTokenReq.ts │ ├── eventBus.ts │ ├── fetch.ts │ ├── getDeploymentProgress.ts │ ├── getFlexPlanPrice.ts │ ├── getIndexerStatus.ts │ ├── getOrderedAccounts.ts │ ├── getTrimmedStr.ts │ ├── idleCallback.ts │ ├── index.tsx │ ├── limitation.ts │ ├── links.ts │ ├── localStorage.ts │ ├── numberFormatters.ts │ ├── parseError.ts │ ├── retry.ts │ ├── stringFormatters.ts │ ├── useDebounce.ts │ └── waitForSomething.ts └── vite-env.d.ts ├── tsconfig.json ├── types ├── i18next.d.ts └── react-jazzicon.d.ts ├── vite.config.js └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | VITE_IPFS_GATEWAY=https://unauthipfs.subquery.network/ipfs/api/v0 2 | VITE_QUERY_REGISTRY_PROJECT=https://api.subquery.network/sq/subquery/subquery-mainnet 3 | VITE_TOP_100_INDEXERS=https://lb-api.subquery.network/graphql 4 | VITE_CONSUMER_HOST_ENDPOINT=https://chs.subquery.network 5 | VITE_FORUM_DOMAIN=https://forum.subquery.network 6 | VITE_NETWORK=mainnet 7 | VITE_GQL_PROXY=https://gql-proxy.subquery.network 8 | VITE_NETWORK_DEPLOYMENT_ID=QmQqqmwwaBben8ncfHo3DMnDxyWFk5QcEdTmbevzKj7DBd 9 | VITE_PROXYGATEWAY=https://gateway.subquery.network 10 | VITE_SUBQUERY_OFFICIAL_BASE_RPC=https://base.rpc.subquery.network/public 11 | VITE_SUBQUERY_OFFICIAL_ETH_RPC=https://ethereum.rpc.subquery.network/public 12 | VITE_AI_URL=https://ai-reaction-backend.subquery.network/ai-network/v1/chat/completions 13 | VITE_AI_REACTION_URL=https://ai-reaction-backend.subquery.network 14 | VITE_CONSUMER_CAMPAIGN_URL=https://consumer-campaign-api.subquery.network 15 | -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | VITE_IPFS_GATEWAY=https://unauthipfs.subquery.network/ipfs/api/v0 2 | VITE_QUERY_REGISTRY_PROJECT=https://api.subquery.network/sq/subquery/subquery-mainnet__c3Vic 3 | VITE_TOP_100_INDEXERS=https://lb-api.subquery.network/graphql 4 | VITE_CONSUMER_HOST_ENDPOINT=https://chs.subquery.network 5 | VITE_FORUM_DOMAIN=https://forum.subquery.network 6 | VITE_NETWORK=mainnet 7 | VITE_GQL_PROXY=https://gql-proxy.subquery.network 8 | VITE_NETWORK_DEPLOYMENT_ID=QmZdLhKt44hmW56YS63rwr1NbkyGuG224oHjFypeXUiahW 9 | VITE_PROXYGATEWAY=https://gateway.subquery.network 10 | VITE_SUBQUERY_OFFICIAL_BASE_RPC=https://base.rpc.subquery.network/public 11 | VITE_SUBQUERY_OFFICIAL_ETH_RPC=https://ethereum.rpc.subquery.network/public 12 | VITE_USE_SCANNER=1 13 | -------------------------------------------------------------------------------- /.env.testnet: -------------------------------------------------------------------------------- 1 | VITE_IPFS_GATEWAY=https://unauthipfs.subquery.network/ipfs/api/v0 2 | VITE_QUERY_REGISTRY_PROJECT=https://api.subquery.network/sq/subquery/base-testnet 3 | VITE_TOP_100_INDEXERS=https://leaderboard-api.thechaindata.com/graphql 4 | VITE_CONSUMER_HOST_ENDPOINT=https://dev-chs.thechaindata.com 5 | VITE_NETWORK=testnet 6 | VITE_GQL_PROXY=https://gql-proxy.thechaindata.com 7 | VITE_NETWORK_DEPLOYMENT_ID=QmQDQa6id3QP1Tiznu7gE4ZjNw4urQnS6R9mgfvzANWgqb 8 | VITE_PROXYGATEWAY=https://dev-gateway.thechaindata.com 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Ticket: [#ticket number](https://url-for-ticket) 6 | 7 | ## Test cases: 8 | 9 | Please describe tested cases. 10 | 11 | ## Type of change 12 | 13 | Please delete options that are not relevant. 14 | 15 | - [ ] New feature (non-breaking change which adds functionality) 16 | - [ ] Bug fix (non-breaking change which fixes an issue) 17 | - [ ] Improvements (ie: code cleaning or remove unused codes or performance issue) 18 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 19 | - [ ] This change requires a documentation update 20 | 21 | ## UI Changes 22 | 23 | Add before and after changes for UI if needed. 24 | 25 | |before|after| 26 | |-|-| 27 | |0|![1](https://user-images.githubusercontent.com/8177474/193932711-e13b8cd5-e42b-415d-a37f-b98db2f846d3.jpeg)| 28 | -------------------------------------------------------------------------------- /.github/workflows/dev-build.yml: -------------------------------------------------------------------------------- 1 | name: Mainnet-Dev 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | env: 12 | AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID_V2 }} 13 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_V2 }} 14 | VITE_SENTRY_DSN: ${{ vars.DEV_SENTRY_DSN }} 15 | NODE_OPTIONS: --max-old-space-size=32768 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Setup Node.js environment 20 | uses: actions/setup-node@v2.4.1 21 | with: 22 | node-version: 18 23 | 24 | - run: yarn 25 | 26 | - name: Build 27 | # Disable CI flag so linter warnings aren't treated as errors 28 | run: CI=false yarn build:dev 29 | 30 | - name: Deploy Dev 31 | uses: reggionick/s3-deploy@v3 32 | with: 33 | folder: dist 34 | bucket: ${{ vars.S3_BUCKET_DEV }} 35 | bucket-region: ap-northeast-1 36 | invalidation: /* 37 | dist-id: ${{ vars.CLOUDFRONT_ID_DEV }} 38 | delete-removed: false 39 | private: true 40 | -------------------------------------------------------------------------------- /.github/workflows/discord.yml: -------------------------------------------------------------------------------- 1 | name: discord notification 2 | on: 3 | release: 4 | types: 5 | - published 6 | 7 | jobs: 8 | notify: 9 | name: Discord Notification 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Send release details to Discord 14 | uses: rjstone/discord-webhook-notify@v1 15 | with: 16 | webhookUrl: ${{ secrets.KEPLER_DISCORD_WEBHOOK }} 17 | color: '#6499ff' 18 | avatarUrl: https://github.githubassets.com/images/modules/logos_page/Octocat.png 19 | details: ${{ github.event.release.body }} 20 | description: "[Release] ${{ github.event.release.name }}" 21 | footer: ${{ github.event.release.html_url }} -------------------------------------------------------------------------------- /.github/workflows/mainnet-prod-pipeline.yml: -------------------------------------------------------------------------------- 1 | name: Mainnet-Prod 2 | on: 3 | workflow_dispatch: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | env: 11 | AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID_V2 }} 12 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_V2 }} 13 | VITE_SENTRY_DSN: ${{ vars.DEV_SENTRY_DSN }} 14 | NODE_OPTIONS: --max-old-space-size=32768 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Setup Node.js environment 19 | uses: actions/setup-node@v2.4.1 20 | with: 21 | node-version: 18 22 | 23 | - run: yarn 24 | 25 | - name: Build 26 | # Disable CI flag so linter warnings aren't treated as errors 27 | run: CI=false yarn build 28 | 29 | - name: Deploy Prod 30 | uses: reggionick/s3-deploy@v3 31 | with: 32 | folder: dist 33 | bucket: ${{ vars.S3_BUCKET_PROD }} 34 | bucket-region: ap-northeast-1 35 | invalidation: /* 36 | dist-id: ${{ vars.CLOUDFRONT_ID_PROD }} 37 | delete-removed: false 38 | private: true 39 | -------------------------------------------------------------------------------- /.github/workflows/pr-build.yml: -------------------------------------------------------------------------------- 1 | name: PR - Build 2 | on: 3 | workflow_dispatch: 4 | pull_request: 5 | branches: 6 | - main 7 | - kepler 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | env: 13 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 14 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 15 | VITE_SENTRY_DSN: ${{ vars.DEV_SENTRY_DSN }} 16 | NODE_OPTIONS: --max-old-space-size=32768 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Setup Node.js environment 21 | uses: actions/setup-node@v2.4.1 22 | with: 23 | node-version: 18 24 | 25 | - run: yarn 26 | 27 | - name: Build 28 | # Disable CI flag so linter warnings aren't treated as errors 29 | run: CI=false yarn build:dev 30 | -------------------------------------------------------------------------------- /.github/workflows/prod-build.yml: -------------------------------------------------------------------------------- 1 | name: Kepler-Prod 2 | on: 3 | workflow_dispatch: 4 | branches: 5 | - kepler 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | env: 11 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_PROD }} 12 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }} 13 | VITE_SENTRY_DSN: ${{ vars.DEV_SENTRY_DSN }} 14 | NODE_OPTIONS: --max-old-space-size=32768 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Setup Node.js environment 19 | uses: actions/setup-node@v2.4.1 20 | with: 21 | node-version: 18 22 | 23 | - run: yarn 24 | 25 | - name: Build 26 | # Disable CI flag so linter warnings aren't treated as errors 27 | run: CI=false yarn build 28 | 29 | - name: Deploy Prod 30 | uses: reggionick/s3-deploy@v3 31 | with: 32 | folder: dist 33 | bucket: kepler.subquery.network 34 | bucket-region: ap-southeast-2 35 | invalidation: /* 36 | dist-id: ${{ secrets.CLOUDFRONT_ID_KEPLER_PROD }} 37 | delete-removed: false 38 | no-cache: false 39 | private: true 40 | -------------------------------------------------------------------------------- /.github/workflows/staging-build.yml: -------------------------------------------------------------------------------- 1 | name: Mainnet-Staging 2 | on: 3 | workflow_dispatch: 4 | branches: 5 | - main 6 | - kepler 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | env: 12 | AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID_V2 }} 13 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_V2 }} 14 | VITE_SENTRY_DSN: ${{ vars.DEV_SENTRY_DSN }} 15 | NODE_OPTIONS: --max-old-space-size=32768 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Setup Node.js environment 20 | uses: actions/setup-node@v2.4.1 21 | with: 22 | node-version: 18 23 | 24 | - run: yarn 25 | 26 | - name: Build 27 | # Disable CI flag so linter warnings aren't treated as errors 28 | run: CI=false yarn build:staging 29 | 30 | - name: Deploy Staging 31 | uses: reggionick/s3-deploy@v3 32 | with: 33 | folder: dist 34 | bucket: ${{ vars.S3_BUCKET_STAGING }} 35 | bucket-region: ap-northeast-1 36 | invalidation: /* 37 | dist-id: ${{ secrets.CLOUDFRONT_ID_STAGING }} 38 | delete-removed: false 39 | no-cache: false 40 | private: true 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .idea 8 | .vscode 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.staging.local 21 | .env.test.local 22 | .env.production.local 23 | .env.*.local 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # Generated graphql types 30 | __generated__ 31 | src/globalTypes.ts 32 | .env 33 | dist 34 | 35 | stats.html -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | protected_branch='main' 5 | 6 | # Argument parsing taken from .git/hooks/pre-push.sample 7 | if read local_ref local_sha remote_ref remote_sha; then 8 | if [[ "$remote_ref" == *"$protected_branch"* ]]; then 9 | echo -en "\033[1;33mYou're about to push to main, is that what you intended? [y|n] \033[0m" 10 | echo -en "\033[1m" 11 | read -n 1 -r < /dev/tty 12 | echo -en "\033[0m" 13 | 14 | echo 15 | if echo $REPLY | grep -E '^[Yy]$' > /dev/null; then 16 | exit 0 # push will execute 17 | fi 18 | exit 1 # push will not execute 19 | fi 20 | fi 21 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.18.2 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | 2 | .github -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "printWidth": 120, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "trailingComma": "all", 7 | "proseWrap": "always" 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SubQuery Network App 2 | 3 | dApp for Node Operators, consumers and delegators interact with SubQuery Network. You can find this at 4 | [https://app.subquery.network](https://app.subquery.network/) 5 | -------------------------------------------------------------------------------- /public/favicon-new.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/favicon-new.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/favicon.ico -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "SubQueryNetwork Explorer", 3 | "name": "SubQueryNetwork Explorer", 4 | "description": "SubQueryNetwork Explorer", 5 | "iconPath": "favicon.ico", 6 | "icons": [ 7 | { 8 | "src": "favicon.ico", 9 | "sizes": "64x64 32x32 24x24 16x16", 10 | "type": "image/x-icon" 11 | }, 12 | { 13 | "src": "favicon.ico", 14 | "type": "image/x-icon", 15 | "sizes": "192x192" 16 | }, 17 | { 18 | "src": "favicon.ico", 19 | "type": "image/x-icon", 20 | "sizes": "512x512" 21 | } 22 | ], 23 | "start_url": ".", 24 | "display": "standalone", 25 | "theme_color": "#000000", 26 | "background_color": "#ffffff" 27 | } 28 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/static/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/base.png -------------------------------------------------------------------------------- /public/static/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/check.png -------------------------------------------------------------------------------- /public/static/connectWallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/connectWallet.png -------------------------------------------------------------------------------- /public/static/default.project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/default.project.png -------------------------------------------------------------------------------- /public/static/eth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/eth.png -------------------------------------------------------------------------------- /public/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/logo.png -------------------------------------------------------------------------------- /public/static/metamask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/metamask.png -------------------------------------------------------------------------------- /public/static/notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/static/sqtoken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/sqtoken.png -------------------------------------------------------------------------------- /public/static/switch-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/switch-network.png -------------------------------------------------------------------------------- /public/static/talisman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/talisman.png -------------------------------------------------------------------------------- /public/static/usdc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/public/static/usdc.png -------------------------------------------------------------------------------- /public/static/walletConnect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/static/x.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .Main { 2 | display: flex; 3 | flex-direction: column; 4 | min-height: 100vh; 5 | } 6 | 7 | .scannerMain { 8 | display: flex; 9 | flex-direction: column; 10 | min-height: 100vh; 11 | background: var(--dark-mode-background); 12 | } 13 | 14 | .Header { 15 | flex: 0 1 auto; 16 | } 17 | 18 | .Content { 19 | display: flex; 20 | flex: 1 1 auto; 21 | position: relative; 22 | } 23 | 24 | .content-width { 25 | width: 100%; 26 | padding: 2rem; 27 | } 28 | 29 | .footer { 30 | position: absolute; 31 | bottom: 0; 32 | 33 | /* sidebar width */ 34 | width: 100%; 35 | display: flex; 36 | justify-content: center; 37 | } 38 | 39 | p { 40 | margin-bottom: 0; 41 | } 42 | 43 | .subql-chatbox { 44 | z-index: 999; 45 | } 46 | 47 | path:focus { 48 | outline: none; 49 | } 50 | -------------------------------------------------------------------------------- /src/components/AccountActions/AccountActions.module.less: -------------------------------------------------------------------------------- 1 | .downIcon { 2 | margin-left: 3px; 3 | } 4 | 5 | .address { 6 | padding: 12px; 7 | border: 1px solid var(--gradient-to); 8 | border-radius: 36px; 9 | display: flex; 10 | align-items: center; 11 | cursor: pointer; 12 | } 13 | 14 | .accountActionDropdown { 15 | width: 420px; 16 | 17 | :global { 18 | .ant-dropdown-menu { 19 | padding: 16px 0; 20 | border-radius: 8px; 21 | box-shadow: 0px 9px 28px 8px rgba(0, 0, 0, 0.05), 0px 6px 16px 0px rgba(0, 0, 0, 0.08), 0px 3px 6px -4px rgba(0, 0, 0, 0.12); 22 | 23 | &-item { 24 | padding: 0!important; 25 | 26 | &:hover { 27 | background-color: transparent!important; 28 | } 29 | } 30 | } 31 | } 32 | 33 | .dropdownItemInner { 34 | padding: 8px 16px; 35 | width: 100%; 36 | display: flex; 37 | align-items: center; 38 | &:hover { 39 | background: rgba(67, 136, 221, 0.10); 40 | } 41 | } 42 | } 43 | 44 | @media screen and (max-width: 768px){ 45 | .accountActionDropdown { 46 | width: 100vw; 47 | margin-left: -24px; 48 | :global { 49 | .ant-dropdown-menu { 50 | box-shadow: none; 51 | } 52 | } 53 | 54 | .balanceOnNetworks { 55 | flex-direction: column; 56 | } 57 | } 58 | 59 | :global { 60 | #mobile-dropdown-container { 61 | .ant-dropdown { 62 | position: static; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/AccountActions/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './AccountActions'; 5 | -------------------------------------------------------------------------------- /src/components/AppPageHeader/AppPageHeader.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | color: var(--gray900); 3 | line-height: 68px; 4 | } 5 | 6 | .header { 7 | display: flex; 8 | justify-content: space-between; 9 | align-items: center; 10 | margin: 1rem 0 0.5rem 0; 11 | } 12 | 13 | .title { 14 | font-weight: 600; 15 | } 16 | 17 | .desc { 18 | display: flex; 19 | flex-direction: row; 20 | gap: 8px; 21 | } 22 | 23 | .icon { 24 | padding-top: 0.125rem; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/AppPageHeader/AppPageHeader.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { AiOutlineInfoCircle } from 'react-icons/ai'; 6 | import { Typography } from '@subql/components'; 7 | import { COLORS } from '@utils'; 8 | import { Space } from 'antd'; 9 | import clsx from 'clsx'; 10 | 11 | import { CurEra } from '../CurEra'; 12 | import styles from './AppPageHeader.module.css'; 13 | 14 | type Props = { 15 | title: string | React.ReactNode; 16 | desc?: string | Array; 17 | }; 18 | 19 | export const AppPageHeader: React.FC = ({ title, desc }) => { 20 | const sortedDescriptions = Array.isArray(desc) ? desc : [desc]; 21 | return ( 22 | <> 23 |
24 | {title && typeof title === 'string' ? ( 25 | 26 | {title} 27 | 28 | ) : ( 29 | <>{title} 30 | )} 31 | 32 | 33 |
34 | 35 | {desc && ( 36 |
37 |
38 | 39 |
40 |
41 | {sortedDescriptions.map((description) => ( 42 | 43 | {description} 44 | 45 | ))} 46 |
47 |
48 | )} 49 | 50 | ); 51 | }; 52 | -------------------------------------------------------------------------------- /src/components/AppPageHeader/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './AppPageHeader'; 5 | -------------------------------------------------------------------------------- /src/components/AppSidebar/AppSidebar.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | background-color: white; 4 | width: 100%; 5 | } 6 | 7 | .sidebar { 8 | min-width: 220px; 9 | } 10 | 11 | .mainContent { 12 | min-height: 85%; 13 | display: flex; 14 | flex-direction: column; 15 | } 16 | 17 | .content { 18 | height: 100%; 19 | flex-grow: 1; 20 | padding: 0 2rem; 21 | overflow: scroll; 22 | } 23 | 24 | @media (max-width: 768px) { 25 | .container { 26 | display: block; 27 | } 28 | 29 | .sidebar { 30 | min-width: unset; 31 | display: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/AppSidebar/AppSidebar.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Footer } from '@subql/components'; 6 | 7 | import { Sidebar } from '../Sidebar'; 8 | import styles from './AppSidebar.module.css'; 9 | 10 | type Props = { 11 | list: { 12 | label: string; 13 | link: string; 14 | icon?: React.ReactElement; 15 | }[]; 16 | children: React.ReactNode; 17 | }; 18 | 19 | export const AppSidebar: React.FC = ({ list, children }) => { 20 | return ( 21 |
22 |
23 | 24 |
25 |
26 |
{children}
27 |
28 |
29 |
30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/components/AppSidebar/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './AppSidebar'; 5 | -------------------------------------------------------------------------------- /src/components/Banner/Banner.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: var(--sq-background-gradient); 3 | height: 50px; 4 | border-radius: 16px; 5 | width: 844px; 6 | display: flex; 7 | align-items: center; 8 | justify-content: space-between; 9 | padding: 0 26px; 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Banner/Banner.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | 6 | import styles from './Banner.module.css'; 7 | 8 | type Props = { 9 | text: string; 10 | }; 11 | 12 | const Banner: React.FC = ({ text }) => { 13 | return ( 14 |
15 | {text} 16 |
17 | ); 18 | }; 19 | 20 | export default Banner; 21 | -------------------------------------------------------------------------------- /src/components/Banner/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Banner'; 5 | -------------------------------------------------------------------------------- /src/components/BillingTransferModal/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './BillingTransferModal'; 5 | -------------------------------------------------------------------------------- /src/components/BreadcrumbNav/BreadcrumbNav.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { NavLink } from 'react-router-dom'; 5 | import { Breadcrumb } from 'antd'; 6 | 7 | interface BreadcrumbProps { 8 | backLink: string; 9 | backLinkText: string; 10 | childText: string; 11 | } 12 | 13 | export const BreadcrumbNav: React.FC = ({ backLink, backLinkText, childText }) => { 14 | const items = [ 15 | { 16 | title: {backLinkText}, 17 | }, 18 | { 19 | title: childText, 20 | }, 21 | ]; 22 | return ; 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/BreadcrumbNav/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './BreadcrumbNav'; 5 | -------------------------------------------------------------------------------- /src/components/Button/Button.module.css: -------------------------------------------------------------------------------- 1 | /* TODO: custom antD theme */ 2 | .button { 3 | /* background: var(--gradient-to) !important; */ 4 | } 5 | -------------------------------------------------------------------------------- /src/components/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Button as AntdButton, ButtonProps } from 'antd'; 6 | 7 | import styles from './Button.module.css'; 8 | 9 | interface IButtonProps extends ButtonProps { 10 | children?: React.ReactNode; 11 | onClick?: (any: any) => void; 12 | } 13 | 14 | // custom antD button 15 | export const Button: React.FC = ({ children, onClick, ...buttonProps }) => { 16 | return ( 17 | 25 | {children} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /src/components/Button/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Button'; 5 | -------------------------------------------------------------------------------- /src/components/Card/Card.module.css: -------------------------------------------------------------------------------- 1 | .cardContainer { 2 | display: flex; 3 | width: 260px; 4 | min-width: 260px; 5 | min-height: 120px; 6 | background: white; 7 | border-radius: 4px; 8 | } 9 | 10 | .card { 11 | padding: 1rem 1.2rem; 12 | 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: space-between; 16 | } 17 | 18 | .category { 19 | color: var(--sq-gray500); 20 | line-height: 18px; 21 | } 22 | 23 | .title { 24 | margin-top: 1rem; 25 | color: var(--sq-gray500); 26 | font-weight: 600; 27 | line-height: 18px; 28 | } 29 | 30 | .titleWithCategory { 31 | margin-top: 0rem; 32 | color: var(--sq-gray900); 33 | } 34 | 35 | .value { 36 | font-weight: 600; 37 | color: var(--sq-gray700); 38 | } 39 | 40 | .grayText { 41 | color: var(--gray500); 42 | } 43 | .sCard { 44 | width: 100%; 45 | overflow: auto; 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Card/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Card as SQCard } from '@subql/components'; 6 | import clsx from 'clsx'; 7 | 8 | import styles from './Card.module.css'; 9 | 10 | interface CardProps { 11 | category?: string; 12 | title?: string; 13 | value?: string; 14 | className?: string; 15 | action?: React.ReactNode; 16 | } 17 | 18 | export const Card: React.FC = ({ category, title, value, className, action }) => { 19 | return ( 20 |
21 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/ConnectWallet/ChainStatus.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: var(--sq-background-gradient); 3 | width: 100%; 4 | min-height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | } 10 | 11 | .content { 12 | display: flex; 13 | flex-direction: column; 14 | max-width: 420px; 15 | padding: 24px; 16 | background: white; 17 | border-radius: 8px; 18 | align-items: center; 19 | justify-content: center; 20 | } 21 | 22 | .title { 23 | font-size: 26px; 24 | font-weight: 600; 25 | } 26 | 27 | .switchContainer { 28 | height: 100%; 29 | align-items: center; 30 | margin: 1rem 0; 31 | 32 | display: flex; 33 | flex-direction: column; 34 | gap: 24px; 35 | } 36 | 37 | .description { 38 | font-size: 18px; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/ConnectWallet/ConnectWallet.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | padding: 24px; 6 | background: white; 7 | margin: 1rem 0; 8 | border-radius: 8px; 9 | max-width: 420px; 10 | 11 | box-shadow: 0px 10px 20px 0px rgba(67, 136, 221, 0.06); 12 | } 13 | 14 | .subtitle { 15 | color: var(--gray700); 16 | margin: 1rem 0; 17 | text-align: center; 18 | } 19 | 20 | .connectBtn { 21 | &:hover { 22 | transform: scale(1.03); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/ConnectWallet/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './ConnectWallet'; 5 | export * from './ChainStatus'; 6 | -------------------------------------------------------------------------------- /src/components/Copy/Copy.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 24px; 3 | width: 24px; 4 | margin: 0 6px; 5 | 6 | border-radius: 100px; 7 | background: rgba(67, 136, 221, 0.08); 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | cursor: pointer; 12 | } 13 | 14 | .content { 15 | width: 100%; 16 | } 17 | 18 | .copy { 19 | color: var(--primary); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Copy/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Copy'; 5 | -------------------------------------------------------------------------------- /src/components/CurEra/CurEra.module.css: -------------------------------------------------------------------------------- 1 | .currentEraText { 2 | text-align: right; 3 | } 4 | 5 | .countdownText { 6 | color: var(--gray700); 7 | } 8 | 9 | .eraContainer { 10 | display: flex; 11 | width: 10rem; 12 | flex-direction: column; 13 | } 14 | 15 | .eraProgress { 16 | padding-top: 3px; 17 | padding-right: 8px; 18 | text-align: right; 19 | } 20 | -------------------------------------------------------------------------------- /src/components/CurEra/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './CurEra'; 5 | -------------------------------------------------------------------------------- /src/components/DeploymentInfo/DeploymentInfo.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 1rem 0; 3 | } 4 | 5 | .projectInfo { 6 | display: flex; 7 | height: 5em; 8 | } 9 | 10 | .ipfsImage { 11 | object-fit: cover; 12 | width: 68px; 13 | height: 68px; 14 | border-radius: 8px; 15 | overflow: hidden; 16 | flex-shrink: 0; 17 | } 18 | 19 | .projectTextInfo { 20 | margin: 0 1rem; 21 | } 22 | 23 | .deployment { 24 | display: flex; 25 | flex-direction: column; 26 | justify-content: center; 27 | } 28 | 29 | .text { 30 | color: var(--gray500); 31 | } 32 | 33 | .link { 34 | transition: all 0.3s ease; 35 | cursor: pointer; 36 | &:hover { 37 | color: var(--sq-blue600); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/DeploymentInfo/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './DeploymentInfo'; 5 | -------------------------------------------------------------------------------- /src/components/Description/Description.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { AiOutlineInfoCircle } from 'react-icons/ai'; 6 | import { Space } from 'antd'; 7 | 8 | import { COLORS } from '../../utils'; 9 | import { AppTypography } from '../Typography'; 10 | 11 | type Props = { 12 | desc: string; 13 | icon?: React.ReactNode; 14 | }; 15 | 16 | export const Description: React.FC = ({ desc, icon }) => { 17 | return ( 18 | 19 | {icon ?? } 20 | {desc} 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/Description/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Description'; 5 | -------------------------------------------------------------------------------- /src/components/Detail/Details.module.css: -------------------------------------------------------------------------------- 1 | .detail { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: flex-start; 5 | text-align: left; 6 | padding: 10px 10px 10px 0px; 7 | } 8 | 9 | .value { 10 | text-decoration: none; 11 | color: var(--gray900); 12 | line-height: 24px; 13 | letter-spacing: 0.3px; 14 | font-weight: normal; 15 | } 16 | 17 | .valueCont { 18 | display: flex; 19 | margin-top: 8px; 20 | align-items: center; 21 | } 22 | 23 | .copy { 24 | margin-left: 6px; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Detail/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Detail'; 5 | -------------------------------------------------------------------------------- /src/components/DoAllocate/index.module.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/src/components/DoAllocate/index.module.less -------------------------------------------------------------------------------- /src/components/DoBooster/index.module.less: -------------------------------------------------------------------------------- 1 | .doBooster { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/Dropdown/Dropdown.module.css: -------------------------------------------------------------------------------- 1 | .downIcon { 2 | margin-left: 3px; 3 | } 4 | 5 | .hosted { 6 | margin: 0 1rem; 7 | cursor: pointer; 8 | } 9 | 10 | .menuItem { 11 | line-height: 24px; 12 | margin: 0.25rem 0; 13 | cursor: pointer; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Dropdown/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Dropdown'; 5 | -------------------------------------------------------------------------------- /src/components/EmptyList/EmptyList.module.css: -------------------------------------------------------------------------------- 1 | .emptyListContainer { 2 | padding-top: 4rem; 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | align-items: center; 7 | text-align: center; 8 | } 9 | 10 | .emptyListContent { 11 | width: 50%; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | gap: 1.5rem; 16 | } 17 | 18 | .description { 19 | flex-direction: column; 20 | row-gap: 1rem; 21 | width: 70%; 22 | } 23 | 24 | .infoLink { 25 | margin-top: 2vh; 26 | } 27 | .infoLink a { 28 | color: var(--primary); 29 | } 30 | 31 | .title { 32 | font-size: 24px; 33 | line-height: 36px; 34 | } 35 | 36 | .content { 37 | margin: 2rem 0; 38 | display: flex; 39 | justify-content: center; 40 | } 41 | -------------------------------------------------------------------------------- /src/components/EmptyList/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './EmptyList'; 5 | -------------------------------------------------------------------------------- /src/components/EstimatedNextEraLayout/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { FC } from 'react'; 5 | import { Typography } from '@subql/components'; 6 | import { Tooltip } from 'antd'; 7 | 8 | export const EstimatedNextEraLayout: FC<{ 9 | value: React.ReactNode; 10 | valueTooltip?: string; 11 | tooltip?: string; 12 | size?: 'normal' | 'small'; 13 | }> = ({ value, valueTooltip, tooltip, size = 'normal' }) => { 14 | return ( 15 | 16 | 21 | {value} 22 | 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/Expand/index.module.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/src/components/Expand/index.module.less -------------------------------------------------------------------------------- /src/components/FTextInput/FTextInput.module.less: -------------------------------------------------------------------------------- 1 | .textInput { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 8px; 5 | } -------------------------------------------------------------------------------- /src/components/FTextInput/FTextInput.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | import React, { useEffect, useRef } from 'react'; 4 | import { TextInput, Typography } from '@subql/components'; 5 | import { useField } from 'formik'; 6 | 7 | import styles from './FTextInput.module.less'; 8 | 9 | const FTextInput: React.FC< 10 | Omit, 'error' | 'value' | 'onChange'> & { id: string } 11 | > = ({ id, ...rest }) => { 12 | const [field, meta] = useField(id); 13 | const ref = useRef(null); 14 | 15 | useEffect(() => { 16 | if (meta.touched && meta.error) { 17 | ref.current?.scrollIntoView({ 18 | behavior: 'smooth', 19 | block: 'center', 20 | inline: 'nearest', 21 | }); 22 | } 23 | }, [meta.touched, meta.error]); 24 | 25 | return ( 26 |
27 | 34 | 35 | {meta.touched && meta.error && {meta.touched && meta.error}} 36 |
37 | ); 38 | }; 39 | 40 | export default FTextInput; 41 | -------------------------------------------------------------------------------- /src/components/GetEndpoint/index.module.less: -------------------------------------------------------------------------------- 1 | .radioCard { 2 | border: 1px solid var(--sq-gray300); 3 | border-radius: 8px; 4 | padding: 12px; 5 | display: flex; 6 | flex-direction: column; 7 | gap: 8px; 8 | cursor: pointer; 9 | 10 | &&Selected { 11 | border-color: var(--sq-blue400); 12 | } 13 | } -------------------------------------------------------------------------------- /src/components/GlobalBanner/GlobalBanner.module.css: -------------------------------------------------------------------------------- 1 | .banner { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | background-color: var(--sq-primary-blue); 6 | width: 100%; 7 | height: 90px; 8 | padding: 1rem 3rem; 9 | } 10 | 11 | .left { 12 | height: 100%; 13 | display: flex; 14 | align-items: center; 15 | column-gap: 16px; 16 | } 17 | 18 | .text { 19 | display: flex; 20 | flex-direction: column; 21 | } 22 | 23 | /* TODO: antD color custom */ 24 | .title { 25 | font-size: 22px; 26 | font-weight: 500; 27 | color: white !important; 28 | } 29 | 30 | .subTitle { 31 | font-size: 16px; 32 | color: white !important; 33 | } 34 | 35 | .right { 36 | display: flex; 37 | align-items: center; 38 | column-gap: 24px; 39 | } 40 | 41 | /* @media screen and (max-width: var(--tabletWidth)) { */ 42 | @media screen and (max-width: 980px) { 43 | .bannerContent { 44 | flex-direction: column; 45 | align-items: center; 46 | justify-content: center; 47 | padding-top: 20px; 48 | row-gap: 20px; 49 | } 50 | 51 | .left { 52 | padding: 0; 53 | height: 100%; 54 | padding-left: 24px; 55 | padding-right: 24px; 56 | } 57 | 58 | .right { 59 | padding: 0; 60 | padding-bottom: 24px; 61 | height: 100%; 62 | } 63 | 64 | .text { 65 | text-align: center; 66 | } 67 | 68 | .icon { 69 | display: none; 70 | } 71 | } 72 | 73 | @media screen and (max-width: 800px) { 74 | .text { 75 | padding-bottom: 0px; 76 | } 77 | 78 | .right { 79 | /* flex-direction: column; */ 80 | justify-content: center; 81 | width: 100%; 82 | padding-left: 24px; 83 | padding-right: 24px; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/components/GlobalBanner/GlobalBanner.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Typography } from 'antd'; 6 | 7 | import { Button } from '../Button'; 8 | import styles from './GlobalBanner.module.css'; 9 | 10 | interface GlobalBannerProps { 11 | title: string | React.ReactNode; 12 | subTitle?: string | React.ReactNode; 13 | navigationLink?: string; 14 | navigationText?: string; 15 | } 16 | 17 | export const GlobalBanner: React.FC = ({ title, subTitle, navigationLink, navigationText }) => { 18 | const [show, setShow] = React.useState(true); 19 | 20 | if (!show) return null; 21 | return ( 22 |
23 |
24 | rocket 25 |
26 | {title} 27 | {subTitle && {subTitle}} 28 |
29 |
30 |
31 | {navigationLink && ( 32 | 35 | )} 36 | 37 |
setShow(false)}> 38 | close icon 39 | {/* // todo: if user closes banner, dont show it again on refresh? */} 40 |
41 |
42 |
43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/GlobalBanner/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './GlobalBanner'; 5 | -------------------------------------------------------------------------------- /src/components/Header/Header.module.less: -------------------------------------------------------------------------------- 1 | .header { 2 | :global { 3 | .subql-header { 4 | padding: 0 24px; 5 | display: flex; 6 | align-items: center; 7 | } 8 | } 9 | } 10 | 11 | .scannerHeader { 12 | :global { 13 | .subql-header { 14 | padding: 0 24px; 15 | display: flex; 16 | align-items: center; 17 | } 18 | } 19 | background: var(--dark-mode-card); 20 | } 21 | 22 | @media screen and (max-width: 768px){ 23 | .right { 24 | padding: 20px 24px; 25 | } 26 | } -------------------------------------------------------------------------------- /src/components/Header/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Header'; 5 | -------------------------------------------------------------------------------- /src/components/IPFSImage/IPFSImage.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import clsx from 'clsx'; 6 | 7 | import { useIPFS } from '../../containers'; 8 | import { CIDv0, CIDv1 } from '../../utils'; 9 | 10 | type Props = { 11 | src: string | File | undefined; 12 | renderPlaceholder?: () => React.ReactNode; 13 | } & Omit, 'src'>; 14 | 15 | const IPFSImage: React.FC = ({ src, renderPlaceholder, className, ...rest }) => { 16 | const [source, setSource] = React.useState(); 17 | const { catSingle } = useIPFS(); 18 | 19 | React.useEffect(() => { 20 | if (typeof src === 'string') { 21 | const srcIPFS = src.replace('ipfs://', ''); 22 | if (CIDv0.test(srcIPFS) || CIDv1.test(srcIPFS)) { 23 | catSingle(srcIPFS).then((data) => { 24 | setSource(`data:image/png;base64,${Buffer.from(data).toString('base64')}`); 25 | }); 26 | } else { 27 | setSource(src); 28 | } 29 | } else if (src instanceof File) { 30 | setSource(URL.createObjectURL(src)); 31 | } else { 32 | setSource(undefined); 33 | } 34 | }, [src, catSingle]); 35 | 36 | if (!source && renderPlaceholder) { 37 | return <>{renderPlaceholder()}; 38 | } 39 | 40 | return ; 41 | }; 42 | 43 | export default IPFSImage; 44 | -------------------------------------------------------------------------------- /src/components/IPFSImage/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './IPFSImage'; 5 | -------------------------------------------------------------------------------- /src/components/Icons/Icons.module.less: -------------------------------------------------------------------------------- 1 | .actionWrapper { 2 | transform: rotate(90deg); 3 | cursor: pointer; 4 | width: 32px; 5 | height: 32px; 6 | border-radius: 8px; 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | background: #4388DD0D; 11 | color: var(--sq-blue600); 12 | transition: all .2s linear; 13 | &:hover { 14 | background: #4388DD26; 15 | 16 | color: var(--sq-blue600); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/ImageInput/ImageInput.module.css: -------------------------------------------------------------------------------- 1 | .label { 2 | display: inline-block; 3 | cursor: pointer; 4 | border: 1px solid var(--weak); 5 | border-radius: 8px; 6 | overflow: hidden; 7 | } 8 | 9 | .content { 10 | width: var(--icon-size); 11 | height: var(--icon-size); 12 | } 13 | 14 | .imagePlaceholder { 15 | height: var(--icon-size); 16 | width: var(--icon-size); 17 | position: relative; 18 | } 19 | 20 | .imageOverlay { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | width: 100%; 25 | height: 100%; 26 | background: rgba(0, 0, 0, 0.5); 27 | display: flex; 28 | align-items: center; 29 | justify-content: center; 30 | } 31 | 32 | .imageOverlay:hover { 33 | background: rgba(0, 0, 0, 0.4); 34 | } 35 | 36 | .imageOverlay :is(i, p) { 37 | font-size: 16px; 38 | line-height: 24px; 39 | 40 | /* identical to box height, or 150% */ 41 | letter-spacing: 0.3px; 42 | color: var(--gray300); 43 | } 44 | 45 | .imageOverlay i { 46 | padding-right: 5px; 47 | } 48 | -------------------------------------------------------------------------------- /src/components/ImageInput/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './ImageInput'; 5 | -------------------------------------------------------------------------------- /src/components/IndexerDetails/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './IndexerDetails'; 5 | -------------------------------------------------------------------------------- /src/components/IndexerProgress/IndexerProgress.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | text-align: left; 3 | margin-bottom: 15px; 4 | } 5 | 6 | .status { 7 | font-weight: 600; 8 | font-size: 12px; 9 | line-height: 18px; 10 | color: var(--sq-gray900); 11 | text-transform: uppercase; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/IndexerProgress/IndexerProgress.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { SubqlProgress } from '@subql/components'; 6 | 7 | import styles from './IndexerProgress.module.css'; 8 | 9 | type Status = { 10 | indexer: string; 11 | latestBlock: number; 12 | }; 13 | 14 | type Props = { 15 | indexerStatus: Status[]; 16 | chainBlockHeight: number; 17 | startBlock: number; 18 | containerClassName?: string; 19 | }; 20 | 21 | const IndexerProgress: React.FC = ({ indexerStatus, chainBlockHeight, startBlock, containerClassName }) => { 22 | const maxProgress = React.useMemo(() => { 23 | const greatestBlock = Math.max(0, ...indexerStatus.map((status) => status.latestBlock)); 24 | 25 | return Math.min(Math.max(0, greatestBlock - startBlock) / (chainBlockHeight - startBlock), 1); 26 | }, [startBlock, indexerStatus, chainBlockHeight]); 27 | 28 | const status = React.useMemo(() => { 29 | if (!indexerStatus.length) { 30 | return 'Not indexed'; 31 | } 32 | 33 | if (maxProgress < 0.98) { 34 | return 'Indexing'; 35 | } 36 | 37 | return 'Indexed'; 38 | }, [indexerStatus, maxProgress]); 39 | 40 | return ( 41 |
42 | {status} 43 | 44 |
45 | ); 46 | }; 47 | 48 | export default IndexerProgress; 49 | -------------------------------------------------------------------------------- /src/components/IndexerProgress/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './IndexerProgress'; 5 | -------------------------------------------------------------------------------- /src/components/Modal/Modal.module.css: -------------------------------------------------------------------------------- 1 | .steps { 2 | margin: 0 0 1.25rem 0; 3 | } 4 | 5 | .title { 6 | font-weight: 600; 7 | } 8 | 9 | .description { 10 | font-size: 16px; 11 | } 12 | 13 | .modal { 14 | :gloabl .ant-modal-content { 15 | padding: 32px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Modal/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Modal'; 5 | -------------------------------------------------------------------------------- /src/components/ModalApproveToken/ModalApproveToken.module.css: -------------------------------------------------------------------------------- 1 | .btnContainer { 2 | width: 100%; 3 | display: flex; 4 | justify-content: flex-end; 5 | margin-top: 1rem; 6 | } 7 | 8 | .submitBtn { 9 | background: var(--sq-blue600) !important; 10 | color: white !important; 11 | min-width: 30%; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/ModalApproveToken/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './ModalApproveToken'; 5 | -------------------------------------------------------------------------------- /src/components/ModalClaimIndexerRewards/ModalClaimIndexerRewards.module.css: -------------------------------------------------------------------------------- 1 | .btnContainer { 2 | width: 100%; 3 | display: flex; 4 | justify-content: flex-end; 5 | margin-top: 1rem; 6 | } 7 | 8 | .error { 9 | margin: 1rem 0; 10 | } 11 | -------------------------------------------------------------------------------- /src/components/ModalClaimIndexerRewards/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './ModalClaimIndexerRewards'; 5 | -------------------------------------------------------------------------------- /src/components/ModalInput/ModalInput.module.css: -------------------------------------------------------------------------------- 1 | .input { 2 | margin-top: 0.4rem; 3 | } 4 | 5 | .inputNumber { 6 | width: 100% !important; 7 | } 8 | 9 | .prefix { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .unit { 16 | color: var(--gray500); 17 | margin: 0 0.5rem; 18 | } 19 | 20 | .btnContainer { 21 | width: 100%; 22 | display: flex; 23 | justify-content: flex-end; 24 | margin-top: 1rem; 25 | } 26 | 27 | .inputBottomText { 28 | color: var(--gray500); 29 | } 30 | 31 | .inputError { 32 | margin: 1rem 0; 33 | color: var(--error); 34 | } 35 | 36 | .submitBtn { 37 | background: var(--gradient-to) !important; 38 | color: white !important; 39 | min-width: 30%; 40 | } 41 | 42 | .submitBtn:disabled { 43 | background: var(--gray300) !important; 44 | color: var(--gray500) !important; 45 | } 46 | -------------------------------------------------------------------------------- /src/components/ModalInput/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './ModalInput'; 5 | -------------------------------------------------------------------------------- /src/components/NormalError/index.module.less: -------------------------------------------------------------------------------- 1 | .normalError { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | row-gap: 8px; 7 | } 8 | 9 | .withWrapper { 10 | width: 100%; 11 | height: 100%; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/NormalError/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { FC } from 'react'; 5 | import { Typography } from '@subql/components'; 6 | import { clsx } from 'clsx'; 7 | 8 | import styles from './index.module.less'; 9 | 10 | interface IProps { 11 | size?: 'normal' | 'small'; 12 | children?: React.ReactNode; 13 | withWrapper?: boolean; 14 | } 15 | 16 | const NormalError: FC = (props) => { 17 | const { size = 'normal', children, withWrapper = false } = props; 18 | 19 | return ( 20 |
21 | rpc 29 | 30 | Oops! Something went wrong. 31 | 32 | 33 | {children} 34 | 35 |
36 | ); 37 | }; 38 | export default NormalError; 39 | -------------------------------------------------------------------------------- /src/components/Notification/Notification.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { NotificationProps } from '@subql/components'; 5 | import { openNotification } from '@subql/components'; 6 | 7 | export { openNotification }; 8 | export type { NotificationProps }; 9 | 10 | export enum NotificationType { 11 | INFO = 'info', 12 | SUCCESS = 'success', 13 | ERROR = 'error', 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Notification/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Notification'; 5 | -------------------------------------------------------------------------------- /src/components/NumberInput/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './NumberInput'; 5 | -------------------------------------------------------------------------------- /src/components/PasswordField/index.module.less: -------------------------------------------------------------------------------- 1 | .passwordField { 2 | display: flex; 3 | align-items: center; 4 | 5 | &Eye { 6 | display: flex; 7 | color: var(--primary); 8 | margin-left: 8px; 9 | } 10 | 11 | &Input { 12 | padding: 0; 13 | border: 0; 14 | background: none; 15 | color: var(--sq-gray500); 16 | font-size: 16px; 17 | } 18 | } -------------------------------------------------------------------------------- /src/components/PasswordField/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { FC, useState } from 'react'; 5 | import { BsEye, BsEyeSlash } from 'react-icons/bs'; 6 | import Copy from '@components/Copy'; 7 | 8 | import styles from './index.module.less'; 9 | 10 | interface IProps { 11 | password: string; 12 | } 13 | 14 | const PasswordField: FC = ({ password }) => { 15 | const [showPwd, setShowPwd] = useState(false); 16 | 17 | return ( 18 | 19 |
20 | 26 |
{ 29 | e.stopPropagation(); 30 | setShowPwd(!showPwd); 31 | }} 32 | > 33 | {showPwd ? : } 34 |
35 |
36 |
37 | ); 38 | }; 39 | export default PasswordField; 40 | -------------------------------------------------------------------------------- /src/components/ProjectCard/ProjectCard.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border: 1px solid var(--sq-gray200); 3 | border-radius: 8px; 4 | padding: 16px; 5 | width: 236px; 6 | cursor: pointer; 7 | box-sizing: border-box; 8 | box-shadow: 0px 0px 6px 0px rgba(157, 190, 239, 0.08); 9 | display: flex; 10 | flex-direction: column; 11 | height: 100%; 12 | transition: all 0.3s linear; 13 | } 14 | 15 | .card:hover { 16 | background-color: var(--weak-opac); 17 | } 18 | 19 | .image { 20 | width: 100%; 21 | height: 205px; 22 | border-radius: 8px; 23 | background-color: white; 24 | flex-shrink: 0; 25 | } 26 | 27 | .details { 28 | display: flex; 29 | flex-direction: column; 30 | overflow: auto; 31 | white-space: nowrap; 32 | text-align: center; 33 | } 34 | 35 | .address { 36 | margin: auto; 37 | } 38 | 39 | .name { 40 | text-overflow: ellipsis; 41 | overflow: hidden; 42 | font-size: 18px; 43 | line-height: 30px; 44 | font-weight: 500; 45 | padding-top: 8px; 46 | } 47 | 48 | .line { 49 | width: 100%; 50 | height: 1px; 51 | background: var(--sq-gray200); 52 | margin: 12px 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/components/ProjectCard/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './ProjectCard'; 5 | -------------------------------------------------------------------------------- /src/components/ProjectDeployments/ProjectDeployments.module.less: -------------------------------------------------------------------------------- 1 | .createdDate { 2 | text-transform: capitalize; 3 | } 4 | 5 | .deploymentId { 6 | display: flex; 7 | } 8 | 9 | .copy { 10 | margin-left: 6px; 11 | } 12 | 13 | 14 | @media screen and (max-width: 1599px) { 15 | .descriptionMarkdown { 16 | width: 300px; 17 | } 18 | } 19 | 20 | @media screen and (min-width: 1600px) { 21 | .descriptionMarkdown { 22 | width: auto; 23 | } 24 | } -------------------------------------------------------------------------------- /src/components/ProjectDeployments/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './ProjectDeployments'; 5 | -------------------------------------------------------------------------------- /src/components/ProjectHeader/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './ProjectHeader'; 5 | -------------------------------------------------------------------------------- /src/components/ProjectOverview/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './ProjectOverview'; 5 | -------------------------------------------------------------------------------- /src/components/RpcError/index.module.less: -------------------------------------------------------------------------------- 1 | .rpcError { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | row-gap: 8px; 7 | } -------------------------------------------------------------------------------- /src/components/RpcPlayground/index.module.less: -------------------------------------------------------------------------------- 1 | .rpcPlayground { 2 | display: flex; 3 | padding: 16px; 4 | border-radius: 0px 0px 16px 16px; 5 | background: #2A3546; 6 | 7 | &Editor { 8 | flex: 1; 9 | padding: 16px; 10 | background: #1F2A3B; 11 | border-radius: 10px; 12 | position: relative; 13 | 14 | .rows { 15 | display: flex; 16 | flex-direction: column; 17 | color: var(--sq-gray600); 18 | font-size: 14px; 19 | line-height: 1.5714285714285714; 20 | padding-top: 2px; 21 | } 22 | 23 | :global { 24 | .ant-input { 25 | background: #1F2A3B; 26 | border: 1px solid #1F2A3B; 27 | color: var(--sq-gray500); 28 | padding: 0; 29 | font-family: var(--sq-font-family); 30 | &:focus { 31 | box-shadow: none; 32 | } 33 | 34 | &::placeholder { 35 | color: var(--sq-gary500); 36 | } 37 | } 38 | } 39 | } 40 | 41 | &Response { 42 | flex: 1; 43 | padding: 16px; 44 | 45 | } 46 | } -------------------------------------------------------------------------------- /src/components/SearchInput/SearchInput.module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/src/components/SearchInput/SearchInput.module.css -------------------------------------------------------------------------------- /src/components/SearchInput/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './SearchInput'; 5 | -------------------------------------------------------------------------------- /src/components/SeasonProgress/SeasonProgress.module.css: -------------------------------------------------------------------------------- 1 | .seasonProgress { 2 | flex: 2.5; 3 | padding: 1.2rem 3rem 1rem 1rem; 4 | background: white; 5 | display: flex; 6 | justify-content: space-between; 7 | align-items: center; 8 | } 9 | 10 | .progress { 11 | flex: 0.7; 12 | } 13 | 14 | .description { 15 | flex: 0.3; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/SeasonProgress/SeasonProgress.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { Progress } from 'antd'; 5 | import dayjs from 'dayjs'; 6 | 7 | import { getProgress, getTimeLeft } from '../../utils'; 8 | import styles from './SeasonProgress.module.css'; 9 | 10 | function getStatus(mTo: dayjs.Dayjs, mNow: dayjs.Dayjs): string { 11 | if (mNow.isAfter(mTo)) { 12 | return 'This Season has ended'; 13 | } else { 14 | return 'Current Season ends in'; 15 | } 16 | } 17 | 18 | export const SeasonProgress: React.FC<{ 19 | timePeriod: { 20 | from: Date; 21 | to: Date; 22 | }; 23 | }> = (timePeriod) => { 24 | const now = new Date(); 25 | const { from, to } = timePeriod.timePeriod; 26 | const mTo = dayjs(to); 27 | const mNow = dayjs(now); 28 | 29 | const status = getStatus(mTo, mNow); 30 | const timeLeft = getTimeLeft(mTo, mNow); 31 | const percent_complete = getProgress(now, from, to); 32 | 33 | return ( 34 |
35 |
36 |

{status}

37 |

38 | {timeLeft} 39 |

40 |
41 |
42 | 50 |
51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.module.css: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | width: 100%; 3 | height: 100%; 4 | padding-top: 1rem; 5 | padding-left: 0.5rem; 6 | background: var(--sidebar-background); 7 | border-right: 1px solid var(--gray300); 8 | 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: flex-start; 12 | align-items: flex-start; 13 | } 14 | 15 | .navLink { 16 | display: flex; 17 | align-items: center; 18 | justify-content: space-between; 19 | font-weight: 600; 20 | color: var(--gray800); 21 | row-gap: 0.5rem; 22 | padding: 1rem; 23 | } 24 | 25 | .navLink:hover { 26 | color: var(--gradient-to); 27 | } 28 | 29 | .activeNav { 30 | color: var(--sq-primary-blue); 31 | background: var(--sidebar-background-hover); 32 | width: 100%; 33 | } 34 | 35 | .activeNav > p, 36 | .navLink > p:hover { 37 | color: inherit !important; 38 | } 39 | 40 | .label { 41 | color: inherit; 42 | line-height: 2rem; 43 | } 44 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { NavLink } from 'react-router-dom'; 6 | import { Typography } from '@subql/components'; 7 | import clsx from 'clsx'; 8 | 9 | import styles from './Sidebar.module.css'; 10 | 11 | type Props = { 12 | list: { 13 | label: string; 14 | link: string; 15 | icon?: React.ReactElement; 16 | }[]; 17 | }; 18 | 19 | export const Sidebar: React.FC = ({ list }) => { 20 | return ( 21 |
22 | {list.map((sidebarItem) => ( 23 | clsx(styles.navLink, isActive && styles.activeNav)} 25 | to={sidebarItem.link} 26 | key={sidebarItem.link} 27 | > 28 | {sidebarItem?.icon} 29 | {sidebarItem.label} 30 | 31 | ))} 32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/components/Sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Sidebar'; 5 | -------------------------------------------------------------------------------- /src/components/Spinner/Spinner.module.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | width: 100%; 3 | height: 80%; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Spinner/Spinner.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Spinner as SubqlSpinner } from '@subql/components'; 6 | 7 | import styles from './Spinner.module.css'; 8 | 9 | type Props = { 10 | size?: number; 11 | }; 12 | 13 | const Spinner: React.FC = ({ size }) => { 14 | return ( 15 |
16 | 17 |
18 | ); 19 | }; 20 | 21 | export default Spinner; 22 | -------------------------------------------------------------------------------- /src/components/Spinner/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Spinner'; 5 | -------------------------------------------------------------------------------- /src/components/Stat/Stat.module.css: -------------------------------------------------------------------------------- 1 | .stat { 2 | width: 100%; 3 | padding: 1rem; 4 | border: 1px solid var(--gray300); 5 | border-radius: 6px; 6 | 7 | display: flex; 8 | flex-direction: column; 9 | align-items: start; 10 | } 11 | 12 | .title { 13 | font-weight: 600; 14 | line-height: 18px; 15 | } 16 | 17 | .value { 18 | font-weight: 500; 19 | font-size: 24px; 20 | line-height: 36px; 21 | margin-top: 1rem; 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Stat/Stat.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | 6 | import { AppTypography } from '../Typography'; 7 | import styles from './Stat.module.css'; 8 | 9 | interface CardProps { 10 | title?: string; 11 | tooltip?: string; 12 | value?: string; 13 | } 14 | 15 | export const Stat: React.FC = ({ title, tooltip, value }) => { 16 | return ( 17 |
18 | {title && ( 19 | 20 | {title.toUpperCase()} 21 | 22 | )} 23 | {value && {value}} 24 |
25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/components/Stat/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Stat'; 5 | -------------------------------------------------------------------------------- /src/components/Status/Status.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | border-radius: 4px; 3 | border: 1px solid; 4 | padding: 2px 8px; 5 | display: inline-flex; 6 | align-items: center; 7 | gap: 4px; 8 | } 9 | 10 | .container-blue { 11 | border-color: var(--info); 12 | background: var(--info-opac); 13 | } 14 | 15 | .container-gray { 16 | border-color: var(--weak); 17 | background: var(--weak-opac); 18 | } 19 | 20 | .container-red { 21 | border-color: var(--error); 22 | background: var(--error-opac); 23 | } 24 | 25 | .container-green { 26 | border-color: var(--success); 27 | background: var(--success-opac); 28 | } 29 | 30 | .text { 31 | font-family: Futura; 32 | font-size: 12px; 33 | line-height: 20px; 34 | text-transform: uppercase; 35 | margin-bottom: 0; 36 | } 37 | 38 | .text-blue { 39 | color: var(--info); 40 | } 41 | 42 | .text-gray { 43 | color: var(--weak); 44 | } 45 | 46 | .text-red { 47 | color: var(--error); 48 | } 49 | 50 | .text-green { 51 | color: var(--success); 52 | } 53 | -------------------------------------------------------------------------------- /src/components/Status/Status.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import WarningOutlined from '@ant-design/icons/WarningOutlined'; 6 | import { DeploymentStatus } from '@hooks'; 7 | 8 | import styles from './Status.module.css'; 9 | 10 | export enum StatusColor { 11 | red = 'red', 12 | green = 'green', 13 | gray = 'gray', 14 | blue = 'blue', 15 | } 16 | 17 | export const deploymentStatus: { [key: string]: StatusColor } = { 18 | INDEXING: StatusColor.blue, 19 | STARTED: StatusColor.blue, 20 | READY: StatusColor.green, 21 | NOTINDEXING: StatusColor.gray, 22 | TERMINATED: StatusColor.red, 23 | [DeploymentStatus.Unhealthy]: StatusColor.red, 24 | }; 25 | 26 | type Props = { 27 | text: string; 28 | color?: StatusColor; 29 | }; 30 | 31 | const Status: React.FC = ({ text, color = 'gray' }) => { 32 | return ( 33 |
34 | {text === DeploymentStatus.Unhealthy ? : null} 35 |

{text}

36 |
37 | ); 38 | }; 39 | 40 | export default Status; 41 | -------------------------------------------------------------------------------- /src/components/Status/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Status'; 5 | -------------------------------------------------------------------------------- /src/components/StepButton/StepButton.module.css: -------------------------------------------------------------------------------- 1 | .steps { 2 | margin: 1rem 0; 3 | overflow: scroll; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/StepButton/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | import LeftOutlined from '@ant-design/icons/LeftOutlined'; 7 | import { Button } from '@components/Button'; 8 | import { getCapitalizedStr } from '@utils'; 9 | 10 | import styles from './StepButton.module.css'; 11 | 12 | export enum StepType { 13 | BACK, 14 | NEXT, 15 | } 16 | interface IStepButtons { 17 | curStep: number; 18 | onStepChange: (step: number, type: StepType) => void; 19 | disabled?: boolean; 20 | loading?: boolean; 21 | submitType?: boolean; 22 | } 23 | 24 | export const StepButtons: React.FC = ({ loading, curStep, disabled = false, onStepChange }) => { 25 | const { t } = useTranslation(); 26 | const isFirstStep = curStep === 0; 27 | return ( 28 |
29 | {!isFirstStep && ( 30 | 38 | )} 39 | 40 | 43 |
44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /src/components/SummaryList/SummaryList.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 1rem 0; 3 | } 4 | 5 | .list { 6 | margin: 1rem 0; 7 | } 8 | 9 | .listItem { 10 | display: flex; 11 | align-items: center; 12 | justify-content: space-between; 13 | margin: 16px 0; 14 | line-height: 22px; 15 | } 16 | 17 | .label { 18 | color: var(--sq-gray600); 19 | } 20 | 21 | .strongLabel { 22 | color: var(--sq-gray900); 23 | } 24 | 25 | .value { 26 | margin-left: 8px; 27 | } 28 | 29 | .indexer { 30 | margin-left: 8px; 31 | } 32 | -------------------------------------------------------------------------------- /src/components/SummaryList/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './SummaryList'; 5 | -------------------------------------------------------------------------------- /src/components/TabButton/TabButton.module.less: -------------------------------------------------------------------------------- 1 | .tabContainer { 2 | display: flex; 3 | margin: 0.5rem 0; 4 | position: relative; 5 | width: 100%; 6 | } 7 | 8 | .withUnderline { 9 | &::after { 10 | content: ' '; 11 | position: absolute; 12 | width: 100%; 13 | height: 1px; 14 | background: var(--sq-gray300); 15 | bottom: 0; 16 | } 17 | } 18 | 19 | .whiteTabContainer { 20 | background: white; 21 | } 22 | 23 | .tab { 24 | font-size: 14px; 25 | line-height: 22px; 26 | margin-right: 24px; 27 | padding-bottom: 8px; 28 | text-decoration: none; 29 | color: var(--sq-gray600); 30 | font-family: var(--sq-font-family); 31 | position: relative; 32 | } 33 | 34 | .tab:hover { 35 | text-decoration: none; 36 | } 37 | 38 | .tabSelected { 39 | :global { 40 | .subql-typography { 41 | color: var(--sq-blue600); 42 | } 43 | } 44 | 45 | &::after { 46 | content: ' '; 47 | position: absolute; 48 | width: 100%; 49 | height: 2px; 50 | background: var(--sq-blue600); 51 | bottom: 0; 52 | left: 0; 53 | z-index: 1; 54 | } 55 | } 56 | 57 | .tabContainer { 58 | display: flex; 59 | } 60 | 61 | .whiteTab { 62 | background: white; 63 | min-width: 6rem; 64 | margin: 0 0.6rem; 65 | text-align: center; 66 | } 67 | 68 | .whiteTabLabel { 69 | font-size: 18px; 70 | } 71 | -------------------------------------------------------------------------------- /src/components/TabButton/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './TabButton'; 5 | -------------------------------------------------------------------------------- /src/components/Table/AntDTable.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /** 5 | * NOTE: 6 | * Use graphql Offset and antD table will have empty display issue at larger pagination 7 | * As the data always be filled start from the first page of antTable 8 | */ 9 | 10 | import * as React from 'react'; 11 | import { Pagination, PaginationProps, Table } from 'antd'; 12 | import { TableProps } from 'antd'; 13 | import clsx from 'clsx'; 14 | 15 | interface AntDTableProps { 16 | customPagination?: boolean; 17 | tableProps: TableProps; // TODO use other type in here. 18 | paginationProps?: PaginationProps; 19 | } 20 | 21 | export const AntDTable: React.FC = ({ customPagination = false, paginationProps, tableProps }) => { 22 | // TODO maybe should remove this. can't find any code use this fork. and seems have some bugs in previous. 23 | if (!customPagination) { 24 | return ; 25 | } 26 | 27 | return ( 28 | <> 29 |
33 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/Table/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './AntDTable'; 5 | -------------------------------------------------------------------------------- /src/components/TableText/TableText.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | font-size: 14px; 3 | color: var(--gray700); 4 | } 5 | -------------------------------------------------------------------------------- /src/components/TableText/TableText.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { AppTypography } from '@components/Typography'; 6 | 7 | import styles from './TableText.module.css'; 8 | 9 | /** 10 | * Custom style of table text content using antD. 11 | * Apply for tables of staking dashboard / plan manager.. 12 | */ 13 | 14 | interface TableTextprops { 15 | content?: string | number | React.ReactNode; 16 | className?: string; 17 | tooltip?: string; 18 | children?: string | number | React.ReactNode; 19 | } 20 | 21 | export const TableText: React.FC = (props) => { 22 | return ; 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/TableText/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './TableText'; 5 | -------------------------------------------------------------------------------- /src/components/TokenAmount/TokenAmount.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { TOKEN, truncFormatEtherStr } from '../../utils'; 5 | import { TableText } from '../TableText'; 6 | 7 | export const TokenAmount: React.FC<{ 8 | value: string | number | undefined; 9 | tooltip?: string; 10 | className?: string; 11 | }> = ({ value, tooltip, className }) => { 12 | if (!value) return ; 13 | 14 | const truncatedValue = truncFormatEtherStr(value.toString()); 15 | 16 | return ( 17 | 18 | {truncatedValue} {TOKEN} 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/TokenAmount/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './TokenAmount'; 5 | -------------------------------------------------------------------------------- /src/components/TokenTooltip/index.module.less: -------------------------------------------------------------------------------- 1 | .tokenTooltip { 2 | 3 | :global { 4 | .ant-tooltip-inner { 5 | padding: 16px; 6 | width: 305px; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/components/TransactionModal/TransactionModal.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | } 4 | 5 | .errorButtonIcon { 6 | color: var(--error); 7 | } 8 | -------------------------------------------------------------------------------- /src/components/TransactionModal/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './TransactionModal'; 5 | -------------------------------------------------------------------------------- /src/components/Typography/Typography.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | color: var(--sq-gray600); 3 | font-size: 14px; 4 | } 5 | 6 | .tooltip { 7 | cursor: pointer; 8 | display: inline-block; 9 | } 10 | 11 | .tooltipContent { 12 | display: flex; 13 | align-items: center; 14 | } 15 | 16 | .tooltipIcon { 17 | margin: 0 6px; 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Typography/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Typography'; 5 | -------------------------------------------------------------------------------- /src/components/WalletRoute/WalletRoute.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: var(--sq-background-gradient); 3 | width: 100%; 4 | min-height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | } 10 | 11 | .componentMode { 12 | background: transparent; 13 | } 14 | 15 | .componentModeWallet { 16 | border: 1px solid rgba(223, 227, 232, 0.6); 17 | } 18 | 19 | .error { 20 | margin: 1rem; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/WalletRoute/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './WalletRoute'; 5 | -------------------------------------------------------------------------------- /src/config/dayjsConf.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import dayjs from 'dayjs'; 5 | import advancedFormat from 'dayjs/plugin/advancedFormat'; 6 | import customParseFormat from 'dayjs/plugin/customParseFormat'; 7 | import duration from 'dayjs/plugin/duration'; 8 | import isBetween from 'dayjs/plugin/isBetween'; 9 | import localeData from 'dayjs/plugin/localeData'; 10 | import relativeTime from 'dayjs/plugin/relativeTime'; 11 | import utc from 'dayjs/plugin/utc'; 12 | import weekday from 'dayjs/plugin/weekday'; 13 | import weekOfYear from 'dayjs/plugin/weekOfYear'; 14 | import weekYear from 'dayjs/plugin/weekYear'; 15 | 16 | dayjs.extend(utc); 17 | dayjs.extend(customParseFormat); 18 | dayjs.extend(advancedFormat); 19 | dayjs.extend(weekday); 20 | dayjs.extend(localeData); 21 | dayjs.extend(weekOfYear); 22 | dayjs.extend(weekYear); 23 | dayjs.extend(relativeTime); 24 | dayjs.extend(duration); 25 | dayjs.extend(isBetween); 26 | -------------------------------------------------------------------------------- /src/config/polyfill.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { Buffer } from 'buffer'; 5 | 6 | window.global = window.global ?? window; 7 | window.Buffer = window.Buffer ?? Buffer; 8 | window.process = window.process ?? { env: {} }; 9 | -------------------------------------------------------------------------------- /src/config/sentryConf.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { BrowserTracing, init } from '@sentry/react'; 5 | import { isString } from 'lodash-es'; 6 | 7 | const eventLimiter: { [index: string]: boolean } = {}; 8 | 9 | const filterMsgKey = ['user rejected transaction']; 10 | 11 | init({ 12 | beforeSend: (event, hint) => { 13 | const rawError = hint?.originalException as Error; 14 | if (!rawError) return event; 15 | const msg = isString(rawError) ? rawError : rawError.message; 16 | // do not send event if already sent in last 1 minute 17 | if (msg && msg in eventLimiter) { 18 | return null; 19 | } 20 | 21 | if (filterMsgKey.some((key) => msg.toLowerCase().includes(key))) { 22 | return null; 23 | } 24 | 25 | eventLimiter[msg] = true; 26 | 27 | setTimeout(() => { 28 | delete eventLimiter[msg]; 29 | }, 60 * 1000); 30 | 31 | event.extra = { 32 | ...event.extra, 33 | debugInfo: window.debugInfo, 34 | }; 35 | 36 | return event; 37 | }, 38 | // this env set on Github workflow. 39 | dsn: import.meta.env.VITE_SENTRY_DSN, 40 | integrations: [new BrowserTracing()], 41 | environment: import.meta.env.MODE, 42 | // Set tracesSampleRate to 1.0 to capture 100% 43 | tracesSampleRate: 1.0, 44 | attachStacktrace: true, 45 | }); 46 | -------------------------------------------------------------------------------- /src/const/bridge.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const BRIDGE_URL = 'https://app.subquery.network/bridge'; 5 | -------------------------------------------------------------------------------- /src/containers/AppInitialProvider.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { PropsWithChildren } from 'react'; 5 | import { useInitContracts } from '@hooks'; 6 | import { Spinner } from '@subql/components'; 7 | 8 | /** 9 | * 10 | * This is the App Initial State 11 | * The App will initial ContractSDK, and store it at the global state 12 | */ 13 | export const AppInitProvider: React.FC = ({ children }) => { 14 | const { loading: loadingContract } = useInitContracts(); 15 | 16 | if (loadingContract) { 17 | return ; 18 | } 19 | 20 | return
{children}
; 21 | }; 22 | -------------------------------------------------------------------------------- /src/containers/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { IPFSProvider, useIPFS } from './IPFS'; 5 | export { ProjectMetadataProvider, useProjectMetadata } from './ProjectMetadata'; 6 | export { ProjectRegistryProvider, useProjectRegistry } from './ProjectRegistry'; 7 | export * from './QueryApolloProvider'; 8 | export * from './SQToken'; 9 | export { useWeb3 } from './Web3'; 10 | -------------------------------------------------------------------------------- /src/globalScalars.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Remaps types not resolved by graphql codegen 5 | type GraphQL_Datetime = Date; 6 | type GraphQL_DateTime = Date; 7 | type GraphQL_BigFloat = bigint | string; 8 | type GraphQL_JSON = unknown; 9 | type GraphQL_Date = Date; 10 | 11 | declare interface Window { 12 | config: { 13 | apiUrl: string; 14 | }; 15 | gtag: (event: string, type: string, options?: object) => void; 16 | } 17 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { useAsyncMemo } from './useAsyncMemo'; 5 | export { useCreateDeployment } from './useCreateDeployment'; 6 | export { useCreateProject } from './useCreateProject'; 7 | export { useDeploymentMetadata } from './useDeploymentMetadata'; 8 | export * from './useEra'; 9 | export { useEraValue } from './useEraValue'; 10 | export { useIndexerMetadata } from './useIndexerMetadata'; 11 | export * from './useInitContracts'; 12 | export { useIPFSMetadata } from './useIPFSMetadata'; 13 | export * from './useIsIndexer'; 14 | export * from './useLockPeriod'; 15 | export * from './useNetworkClient'; 16 | export { useOnScreen } from './useOnScreen'; 17 | export { useProject } from './useProject'; 18 | export { useProjectFromQuery } from './useProjectFromQuery'; 19 | export { useRouteQuery } from './useRouteQuery'; 20 | export { useSortedIndexer } from './useSortedIndexer'; 21 | export * from './useSortedIndexerDeployments'; 22 | export * from './useStudio'; 23 | export { useUpdateProjectMetadata } from './useUpdateProjectMetadata'; 24 | -------------------------------------------------------------------------------- /src/hooks/useAddAllowance.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { ApproveContract } from '@components/ModalApproveToken'; 5 | import { openNotification } from '@subql/components'; 6 | 7 | import { useWeb3Store } from 'src/stores'; 8 | 9 | export const useAddAllowance = () => { 10 | const { contracts } = useWeb3Store(); 11 | 12 | const addAllowance = async (contractName: ApproveContract, allowance: string) => { 13 | try { 14 | if (!contracts) throw new Error('Contracts not available'); 15 | openNotification({ 16 | type: 'info', 17 | description: 'Allowance not enough, increase allowance first', 18 | duration: 5, 19 | }); 20 | const tx = await contracts.sqToken.approve(contracts[contractName].address, allowance); 21 | await tx?.wait(); 22 | return tx; 23 | } catch (e) { 24 | console.error(e); 25 | throw e; 26 | } 27 | }; 28 | 29 | const checkAllowanceEnough = async (contractName: ApproveContract, account: string, allowance: string) => { 30 | try { 31 | if (!contracts) throw new Error('Contracts not available'); 32 | const currentAllowance = await contracts.sqToken.allowance(account, contracts[contractName].address); 33 | if (currentAllowance.lt(allowance)) { 34 | return false; 35 | } 36 | return true; 37 | } catch (e) { 38 | console.error(e); 39 | throw e; 40 | } 41 | }; 42 | 43 | return { 44 | addAllowance, 45 | checkAllowanceEnough, 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /src/hooks/useApiEndpoint.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { ApolloClient, gql, InMemoryCache } from '@apollo/client'; 5 | 6 | import { AsyncData } from '../utils'; 7 | import { useAsyncMemo } from './useAsyncMemo'; 8 | 9 | export const GET_PROJECT = gql` 10 | query GetProjectStatus($id: String!) { 11 | project(id: $id) { 12 | id 13 | status 14 | queryEndpoint 15 | } 16 | } 17 | `; 18 | 19 | type Response = { 20 | project: { 21 | id: string; 22 | status: unknown; 23 | queryEndpoint: string; 24 | }; 25 | }; 26 | 27 | // XXXX untested 28 | export function useApiEndpoint(indexerEndpoint?: string, deploymentId?: string): AsyncData { 29 | return useAsyncMemo(async () => { 30 | if (!indexerEndpoint || !deploymentId) return undefined; 31 | 32 | const client = new ApolloClient({ 33 | uri: indexerEndpoint, 34 | cache: new InMemoryCache(), 35 | }); 36 | 37 | const res = await client.query({ 38 | query: GET_PROJECT, 39 | variables: { id: deploymentId }, 40 | }); 41 | 42 | return res.data.project.queryEndpoint; 43 | }, [indexerEndpoint]); 44 | } 45 | -------------------------------------------------------------------------------- /src/hooks/useCommissionRate.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import assert from 'assert'; 5 | import { BigNumber } from 'ethers'; 6 | 7 | import { useWeb3Store } from 'src/stores'; 8 | 9 | import { AsyncMemoReturn, useAsyncMemo } from './useAsyncMemo'; 10 | 11 | // Jun 2022 commission-divUnit = perMil / 100 -> 10,000 12 | export const COMMISSION_DIV_UNIT = 10000; 13 | 14 | export function useCommissionRate(account: string | null | undefined): AsyncMemoReturn<{ 15 | after: BigNumber; 16 | cur: BigNumber; 17 | }> { 18 | const { contracts } = useWeb3Store(); 19 | return useAsyncMemo(async () => { 20 | assert(contracts, 'Contracts not available'); 21 | 22 | const rate = await contracts.indexerRegistry.commissionRates(account || ''); 23 | 24 | return { 25 | after: rate.valueAfter.div(COMMISSION_DIV_UNIT), 26 | cur: rate.valueAt.div(COMMISSION_DIV_UNIT), 27 | }; 28 | }, [contracts]); 29 | } 30 | -------------------------------------------------------------------------------- /src/hooks/useCreateDeployment.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { ContractReceipt } from '@ethersproject/contracts'; 5 | 6 | import { useProjectMetadata, useProjectRegistry } from '../containers'; 7 | import { NewDeployment } from '../models'; 8 | import { useWaitTransactionhandled } from './useWaitTransactionHandled'; 9 | 10 | export function useCreateDeployment( 11 | projectId: string, 12 | ): (deploymentDetails: NewDeployment & { recommended: boolean }) => Promise { 13 | const projectRegistry = useProjectRegistry(); 14 | const { uploadVersionMetadata } = useProjectMetadata(); 15 | const waitTransactionHandled = useWaitTransactionhandled(); 16 | const createDeployment = async (deploymentDetails: NewDeployment & { recommended: boolean }) => { 17 | const versionCid = await uploadVersionMetadata({ 18 | version: deploymentDetails.version, 19 | description: deploymentDetails.description, 20 | }); 21 | 22 | const tx = await projectRegistry.updateDeployment( 23 | projectId, 24 | deploymentDetails.deploymentId, 25 | versionCid, 26 | deploymentDetails.recommended, 27 | ); 28 | 29 | const receipt = await tx.wait(5); 30 | 31 | await waitTransactionHandled(receipt.blockNumber); 32 | return receipt; 33 | }; 34 | 35 | return createDeployment; 36 | } 37 | -------------------------------------------------------------------------------- /src/hooks/useDelegating.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useGetDelegatorQuery } from '@subql/react-hooks'; 5 | import { BigNumber } from 'ethers'; 6 | 7 | import { parseRawEraValue } from './useEraValue'; 8 | import { useAsyncMemo, useEra } from '.'; 9 | 10 | export function useDelegating(address: string) { 11 | const { currentEra } = useEra(); 12 | const ownDelegation = useGetDelegatorQuery({ 13 | variables: { 14 | address: `${address}`, 15 | }, 16 | }); 17 | 18 | return useAsyncMemo(async () => { 19 | const eraIndex = currentEra.data?.index; 20 | const delgationAmount = ownDelegation.data?.delegator?.totalDelegations; 21 | const eraValue = parseRawEraValue(delgationAmount, eraIndex); 22 | return { 23 | curEra: eraValue.current, 24 | nextEra: eraValue.after || BigNumber.from(0), 25 | }; 26 | }, [address, currentEra.data?.index, ownDelegation.data?.delegator?.totalDelegations]); 27 | } 28 | -------------------------------------------------------------------------------- /src/hooks/useDeploymentMetadata.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useEffect } from 'react'; 5 | import { useGetDeploymentLazyQuery } from '@subql/react-hooks'; 6 | 7 | import { useIPFS } from '../containers'; 8 | import { AsyncData, mergeAsyncLast } from '../utils'; 9 | import { useAsyncMemo } from '.'; 10 | 11 | type DeploymentMetadata = { 12 | versionId: string; 13 | version: string; 14 | description: string; 15 | }; 16 | 17 | export async function getDeploymentMetadata( 18 | catSingle: (cid: string) => Promise, 19 | versionId: string | undefined, 20 | ): Promise { 21 | if (!versionId) return undefined; 22 | 23 | const raw = await catSingle(versionId); 24 | 25 | const { version, description } = JSON.parse(Buffer.from(raw).toString('utf8')); 26 | 27 | return { 28 | versionId, 29 | version, 30 | description, 31 | }; 32 | } 33 | 34 | export function useDeploymentMetadata(deploymentId: string | undefined): AsyncData { 35 | const { catSingle } = useIPFS(); 36 | const [loadDeployment, asyncDeployment] = useGetDeploymentLazyQuery(); 37 | const asyncMetadata = useAsyncMemo( 38 | () => getDeploymentMetadata(catSingle, asyncDeployment.data?.deployment?.metadata), 39 | [catSingle, asyncDeployment.data?.deployment?.metadata], 40 | ); 41 | 42 | useEffect(() => { 43 | if (deploymentId) { 44 | loadDeployment({ 45 | variables: { 46 | deploymentId, 47 | }, 48 | }); 49 | } 50 | }, [deploymentId]); 51 | 52 | return mergeAsyncLast(asyncDeployment, asyncMetadata); 53 | } 54 | -------------------------------------------------------------------------------- /src/hooks/useFetchMetadata.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { decodeIpfsRaw, useIPFS } from '@containers/IPFS'; 5 | import { parseError } from '@utils/parseError'; 6 | 7 | import { IndexerDetails } from 'src/models'; 8 | 9 | export const useFetchMetadata = () => { 10 | const { catSingle } = useIPFS(); 11 | 12 | const fetchMetadata = async (indexerCid: string | undefined) => { 13 | if (!indexerCid) 14 | return { 15 | name: '', 16 | description: '', 17 | url: '', 18 | image: '', 19 | }; 20 | try { 21 | const res = await catSingle(indexerCid); 22 | const decodeMetadata = decodeIpfsRaw(res); 23 | return decodeMetadata; 24 | } catch (e) { 25 | parseError(e); 26 | return { 27 | name: '', 28 | description: '', 29 | url: '', 30 | image: '', 31 | }; 32 | } 33 | }; 34 | 35 | return fetchMetadata; 36 | }; 37 | -------------------------------------------------------------------------------- /src/hooks/useForumApis.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import axios from 'axios'; 5 | 6 | const instance = axios.create({ 7 | baseURL: import.meta.env.VITE_FORUM_DOMAIN, 8 | }); 9 | 10 | export const useForumApis = () => { 11 | const getLatestApi = async () => { 12 | const res = await instance.get('/c/subquery-mainnet/17/l/latest.json', { 13 | params: { 14 | filter: 'default', 15 | ascending: 'false', 16 | }, 17 | }); 18 | 19 | if (res.status === 200) { 20 | return res.data.topic_list.topics; 21 | } 22 | 23 | return []; 24 | }; 25 | 26 | return { 27 | getLatestApi, 28 | }; 29 | }; 30 | 31 | export interface IGetLatestTopics { 32 | topic_list: { 33 | topics: { 34 | last_posted_at: string; 35 | created_at: string; 36 | 37 | title: string; 38 | slug: string; 39 | }[]; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/hooks/useGetDeploymentManifest.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useEffect, useState } from 'react'; 5 | import { useIPFS } from '@containers'; 6 | import yaml from 'js-yaml'; 7 | 8 | export enum RPCFAMILY { 9 | EVM = 'evm', 10 | SUBSTRATE = 'substrate', 11 | } 12 | 13 | export type Manifest = { 14 | nodeType?: string; 15 | // subquery 16 | network?: { 17 | chainId: string; 18 | }; 19 | // rpc 20 | rpcFamily?: (string | RPCFAMILY)[]; 21 | client?: { 22 | name: string; 23 | }; 24 | chain?: { 25 | chainId: string; 26 | }; 27 | 28 | // subgraph 29 | dataSources?: { 30 | network: string; 31 | }[]; 32 | }; 33 | 34 | export const useGetDeploymentManifest = (currentDeploymentId?: string) => { 35 | const { catSingle } = useIPFS(); 36 | 37 | const [manifest, setManifest] = useState(); 38 | 39 | const getManifest = async (deploymentId: string) => { 40 | try { 41 | const res = await catSingle(deploymentId); 42 | 43 | const result = Buffer.from(res).toString('utf8'); 44 | 45 | return yaml.load(result) as Manifest; 46 | } catch (e) { 47 | return {} as Manifest; 48 | } 49 | }; 50 | 51 | useEffect(() => { 52 | const inner = async () => { 53 | if (currentDeploymentId) { 54 | const result = await getManifest(currentDeploymentId); 55 | setManifest(result); 56 | } 57 | }; 58 | inner(); 59 | }, [currentDeploymentId]); 60 | 61 | return { 62 | getManifest, 63 | manifest, 64 | }; 65 | }; 66 | -------------------------------------------------------------------------------- /src/hooks/useIPFSMetadata.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import yaml from 'js-yaml'; 5 | 6 | import { useIPFS } from '../containers'; 7 | import { AsyncData } from '../utils'; 8 | import { useAsyncMemo } from '.'; 9 | 10 | export async function fetchIpfsMetadata( 11 | catSingle: (cid: string) => Promise, 12 | cid: string, 13 | ): Promise { 14 | const data = await catSingle(cid); 15 | return yaml.load(Buffer.from(data).toString()) as T; 16 | } 17 | 18 | export function useIPFSMetadata(cid: string | undefined | null): AsyncData { 19 | const { catSingle } = useIPFS(); 20 | 21 | return useAsyncMemo(() => (cid ? fetchIpfsMetadata(catSingle, cid) : undefined), [catSingle, cid]); 22 | } 23 | -------------------------------------------------------------------------------- /src/hooks/useIndexerGeoInformation.tsx: -------------------------------------------------------------------------------- 1 | import { gql, useLazyQuery } from '@apollo/client'; 2 | import { TOP_100_INDEXERS } from '@containers/QueryApolloProvider'; 3 | import { useWhyDidYouUpdate } from 'ahooks'; 4 | 5 | import { useAsyncMemo } from './useAsyncMemo'; 6 | 7 | const useIndexerGeoInformation = (indexers?: string[]) => { 8 | const [fetchGeoInformation] = useLazyQuery<{ 9 | geoips: { 10 | indexer: string; 11 | name: string; 12 | country?: { 13 | names?: { 14 | en?: string; 15 | }; 16 | }; 17 | city?: { 18 | names?: { 19 | en?: string; 20 | }; 21 | }; 22 | location?: { 23 | latitude?: number; 24 | longitude?: number; 25 | }; 26 | }[]; 27 | }>(gql` 28 | query GetGeoInformation($indexers: [String!]!) { 29 | geoips(indexers: $indexers) { 30 | error 31 | indexer 32 | name 33 | country { 34 | names { 35 | en 36 | } 37 | } 38 | city { 39 | names { 40 | en 41 | } 42 | } 43 | location { 44 | latitude 45 | longitude 46 | } 47 | } 48 | } 49 | `); 50 | 51 | const geoInfo = useAsyncMemo(async () => { 52 | if (!indexers || indexers?.length === 0) { 53 | return []; 54 | } 55 | const res = await fetchGeoInformation({ 56 | context: { 57 | clientName: TOP_100_INDEXERS, 58 | }, 59 | variables: { indexers }, 60 | }); 61 | 62 | return res?.data?.geoips; 63 | }, [indexers]); 64 | 65 | return geoInfo; 66 | }; 67 | 68 | export default useIndexerGeoInformation; 69 | -------------------------------------------------------------------------------- /src/hooks/useIsIndexer.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { limitContract, makeCacheKey } from '@utils/limitation'; 5 | import assert from 'assert'; 6 | 7 | import { useWeb3Store } from 'src/stores'; 8 | 9 | import { AsyncData } from '../utils'; 10 | import { useAsyncMemo } from './useAsyncMemo'; 11 | 12 | export function useIsIndexer(account: string | null | undefined): AsyncData { 13 | const { contracts } = useWeb3Store(); 14 | return useAsyncMemo(async () => { 15 | assert(contracts, 'Contracts not available'); 16 | return await limitContract( 17 | () => contracts.indexerRegistry.isIndexer(account || ''), 18 | makeCacheKey(`${account}-isIndexer`), 19 | 0, 20 | ); 21 | }, [account, contracts]); 22 | } 23 | -------------------------------------------------------------------------------- /src/hooks/useIsLogin.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useAccount } from 'wagmi'; 5 | 6 | export const useIsLogin = () => { 7 | const { address } = useAccount(); 8 | 9 | return !!address; 10 | }; 11 | -------------------------------------------------------------------------------- /src/hooks/useIsMobile.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useSize } from 'ahooks'; 5 | 6 | export const useIsMobile = () => { 7 | const size = useSize(document.body); 8 | 9 | return size?.width ? size.width < 768 : false; 10 | }; 11 | -------------------------------------------------------------------------------- /src/hooks/useLockPeriod.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { limitContract, makeCacheKey } from '@utils/limitation'; 5 | import assert from 'assert'; 6 | 7 | import { useWeb3Store } from 'src/stores'; 8 | 9 | import { AsyncData, convertBigNumberToNumber } from '../utils'; 10 | import { useAsyncMemo } from './useAsyncMemo'; 11 | 12 | export function useLockPeriod(): AsyncData { 13 | const { contracts } = useWeb3Store(); 14 | return useAsyncMemo(async () => { 15 | assert(contracts, 'Contracts not available'); 16 | const lockPeriod = await limitContract(() => contracts.staking.lockPeriod(), makeCacheKey('lockPeriod'), 0); 17 | 18 | return convertBigNumberToNumber(lockPeriod); 19 | }, [contracts]); 20 | } 21 | -------------------------------------------------------------------------------- /src/hooks/useMaxUnstakeAmount.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { BigNumber } from 'ethers'; 5 | 6 | import { AsyncMemoReturn } from './useAsyncMemo'; 7 | import { useAsyncMemo, useNetworkClient } from '.'; 8 | 9 | export function useMaxUnstakeAmount(indexer: string, eraNumber?: number): AsyncMemoReturn { 10 | const networkClient = useNetworkClient(); 11 | 12 | return useAsyncMemo(async () => { 13 | const maxUnstakeAmount = await networkClient?.maxUnstakeAmount(indexer ?? '', eraNumber || 0); 14 | return maxUnstakeAmount; 15 | }, [indexer, networkClient]); 16 | } 17 | -------------------------------------------------------------------------------- /src/hooks/useMinCommissionRate.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useCallback } from 'react'; 5 | import { useAsyncMemo } from '@subql/react-hooks'; 6 | import { limitContract } from '@utils/limitation'; 7 | import BigNumber from 'bignumber.js'; 8 | 9 | import { PER_MILL } from 'src/const/const'; 10 | import { useWeb3Store } from 'src/stores'; 11 | 12 | export const useMinCommissionRate = () => { 13 | const { contracts } = useWeb3Store(); 14 | 15 | const minCommission = useAsyncMemo(async () => { 16 | if (!contracts) return 0; 17 | const minConmmissionRate = await limitContract( 18 | () => contracts.indexerRegistry.minimumCommissionRate(), 19 | 'minCommissionRate', 20 | ); 21 | 22 | return BigNumber(minConmmissionRate.toString()).div(PER_MILL).multipliedBy(100).toNumber(); 23 | }, [contracts]); 24 | 25 | const getDisplayedCommission = useCallback( 26 | (commission: string | number): number => { 27 | if (BigNumber(commission).lt(minCommission.data || 0)) { 28 | return minCommission.data || 0; 29 | } 30 | 31 | return BigNumber(commission).toNumber(); 32 | }, 33 | [minCommission.data], 34 | ); 35 | 36 | return { 37 | minCommission: minCommission.data, 38 | loading: minCommission.loading, 39 | getDisplayedCommission, 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /src/hooks/useNetworkClient.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { networkLink } from '@containers/QueryApolloProvider'; 6 | import { SUPPORTED_NETWORK } from '@containers/Web3'; 7 | import { NetworkClient } from '@subql/network-clients'; 8 | 9 | import { useEthersProviderWithPublic, useEthersSigner } from './useEthersProvider'; 10 | 11 | export function useNetworkClient(): NetworkClient | undefined { 12 | const [networkClient, setNetworkClient] = React.useState(); 13 | const { signer } = useEthersSigner(); 14 | const provider = useEthersProviderWithPublic(); 15 | 16 | React.useEffect(() => { 17 | async function getNetworkClient() { 18 | const client = await NetworkClient.create(SUPPORTED_NETWORK, signer || provider, undefined, { 19 | queryClientOptions: { 20 | link: networkLink, 21 | }, 22 | }); 23 | setNetworkClient(client); 24 | } 25 | 26 | getNetworkClient(); 27 | }, []); 28 | 29 | return networkClient; 30 | } 31 | -------------------------------------------------------------------------------- /src/hooks/useOnScreen.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { RefObject, useEffect, useRef, useState } from 'react'; 5 | 6 | export function useOnScreen(ref: RefObject): boolean { 7 | const observerRef = useRef(null); 8 | const [isOnScreen, setIsOnScreen] = useState(false); 9 | 10 | useEffect(() => { 11 | observerRef.current = new IntersectionObserver(([entry]) => setIsOnScreen(entry.isIntersecting)); 12 | }, []); 13 | 14 | useEffect(() => { 15 | if (!ref.current) { 16 | return; 17 | } 18 | 19 | observerRef.current?.observe(ref.current); 20 | 21 | return () => { 22 | observerRef.current?.disconnect(); 23 | }; 24 | }, [ref]); 25 | 26 | return isOnScreen; 27 | } 28 | -------------------------------------------------------------------------------- /src/hooks/useProject.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useCallback, useEffect, useState } from 'react'; 5 | 6 | import { useWeb3Store } from 'src/stores'; 7 | 8 | import { useIPFS, useProjectMetadata, useProjectRegistry } from '../containers'; 9 | import { ProjectDetails } from '../models'; 10 | import { AsyncData } from '../utils'; 11 | import { useAsyncMemo } from '.'; 12 | 13 | export function useProject(id: string): AsyncData { 14 | const { getQuery } = useProjectRegistry(); 15 | const { catSingle } = useIPFS(); 16 | const { getMetadataFromCid } = useProjectMetadata(); 17 | 18 | return useAsyncMemo(async () => { 19 | if (!id) { 20 | return undefined; 21 | } 22 | 23 | const query = await getQuery(id); 24 | console.warn(query); 25 | if (!query) { 26 | return undefined; 27 | } 28 | 29 | const metadata = await getMetadataFromCid(query.metadata); 30 | return { 31 | id, 32 | owner: query.owner, 33 | version: query.version, 34 | metadata, 35 | deploymentId: query.deployment, 36 | type: query.type, 37 | }; 38 | }, [id, catSingle, getMetadataFromCid, getQuery]); 39 | } 40 | -------------------------------------------------------------------------------- /src/hooks/useProjectFromQuery.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useState } from 'react'; 5 | import { ProjectFieldsFragment as Project } from '@subql/network-query'; 6 | import { useGetProjectQuery } from '@subql/react-hooks'; 7 | import dayjs from 'dayjs'; 8 | 9 | import { useProjectMetadata } from '../containers'; 10 | import { ProjectMetadata } from '../models'; 11 | import { AsyncData } from '../utils'; 12 | import { useAsyncMemo } from '.'; 13 | 14 | export type ProjectDetailsQuery = Omit & { 15 | metadata: ProjectMetadata; 16 | }; 17 | 18 | export function useProjectFromQuery(id: string): AsyncData { 19 | const { getMetadataFromCid } = useProjectMetadata(); 20 | const [now] = useState(dayjs().utc(true).toDate()); 21 | const { data, loading, error } = useGetProjectQuery({ 22 | variables: { 23 | id, 24 | now, 25 | }, 26 | }); 27 | 28 | const { 29 | data: project, 30 | loading: loadingData, 31 | error: errorData, 32 | } = useAsyncMemo(async () => { 33 | if (!data?.project) { 34 | return undefined; 35 | } 36 | 37 | const query = data.project; 38 | const metadata = await getMetadataFromCid(query.metadata); 39 | 40 | return { 41 | ...query, 42 | metadata, 43 | }; 44 | }, [data, getMetadataFromCid]); 45 | 46 | return { 47 | data: project, 48 | error: error || errorData, 49 | loading: loading || loadingData, 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/hooks/usePropsValue.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Copy from antd-mobile 5 | import { SetStateAction, useRef } from 'react'; 6 | import { useMemoizedFn, useUpdate } from 'ahooks'; 7 | 8 | type Options = { 9 | value?: T; 10 | defaultValue: T; 11 | onChange?: (v: T) => void; 12 | }; 13 | 14 | export function usePropsValue(options: Options) { 15 | const { value, defaultValue, onChange } = options; 16 | 17 | const update = useUpdate(); 18 | 19 | const stateRef = useRef(value !== undefined ? value : defaultValue); 20 | if (value !== undefined) { 21 | stateRef.current = value; 22 | } 23 | 24 | const setState = useMemoizedFn((v: SetStateAction, forceTrigger = false) => { 25 | // `forceTrigger` means trigger `onChange` even if `v` is the same as `stateRef.current` 26 | const nextValue = typeof v === 'function' ? (v as (prevState: T) => T)(stateRef.current) : v; 27 | if (!forceTrigger && nextValue === stateRef.current) return; 28 | stateRef.current = nextValue; 29 | update(); 30 | return onChange?.(nextValue); 31 | }); 32 | return [stateRef.current, setState] as const; 33 | } 34 | -------------------------------------------------------------------------------- /src/hooks/useRouteQuery.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import { useLocation } from 'react-router'; 6 | 7 | export function useRouteQuery(): URLSearchParams { 8 | const { search } = useLocation(); 9 | 10 | return React.useMemo(() => new URLSearchParams(search), [search]); 11 | } 12 | -------------------------------------------------------------------------------- /src/hooks/useSqtPrice.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useEffect, useState } from 'react'; 5 | import BigNumberJs from 'bignumber.js'; 6 | 7 | export const useSqtPrice = () => { 8 | const [sqtPrice, setSqtPrice] = useState('0'); 9 | 10 | useEffect(() => { 11 | const fetchSqtPrice = async () => { 12 | try { 13 | const response = await fetch('https://sqt.subquery.foundation/price'); 14 | const data = await response.text(); 15 | setSqtPrice(BigNumberJs(data).toString()); 16 | } catch (error) { 17 | // don't care of this 18 | } 19 | }; 20 | fetchSqtPrice(); 21 | }, []); 22 | 23 | return sqtPrice; 24 | }; 25 | -------------------------------------------------------------------------------- /src/hooks/useStudio.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useCallback, useEffect, useState } from 'react'; 5 | import { useAccount } from '@containers/Web3'; 6 | 7 | import { useWeb3Store } from 'src/stores'; 8 | 9 | export function useStudioEnabled() { 10 | const { contracts } = useWeb3Store(); 11 | const { address: account } = useAccount(); 12 | const [studioEnabled, setStudioEnabled] = useState(false); 13 | 14 | const checkStudioEnabled = useCallback(async () => { 15 | if (!contracts || !account) return; 16 | const studioEnabled = await contracts.projectRegistry.creatorWhitelist(account); 17 | setStudioEnabled(studioEnabled); 18 | }, [contracts, account]); 19 | 20 | useEffect(() => { 21 | checkStudioEnabled(); 22 | }, [checkStudioEnabled]); 23 | 24 | return studioEnabled; 25 | } 26 | -------------------------------------------------------------------------------- /src/hooks/useUpdateProjectMetadata.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | 6 | import { useIPFS, useProjectMetadata, useProjectRegistry } from '../containers'; 7 | import { FormProjectMetadata, ProjectMetadata } from '../models'; 8 | 9 | export function useUpdateProjectMetadata(projectId: string): (params: FormProjectMetadata) => Promise { 10 | const { uploadMetadata } = useProjectMetadata(); 11 | const { ipfs } = useIPFS(); 12 | const { updateQueryMetadata } = useProjectRegistry(); 13 | 14 | const updateMetadata = React.useCallback( 15 | async function (project: FormProjectMetadata): Promise { 16 | // Form can give us a File type that doesn't match the schema 17 | if (project.image && typeof project.image !== 'string') { 18 | console.log('Uploading icon...'); 19 | const res = await ipfs.add(project.image); 20 | project.image = res.cid.toString(); 21 | console.log('Uploading icon...DONE'); 22 | } 23 | 24 | const metadata = await uploadMetadata(project as ProjectMetadata); 25 | const tx = await updateQueryMetadata(projectId, metadata); 26 | const receipt = await tx.wait(1); 27 | 28 | if (!receipt.status) { 29 | throw new Error('Create project unsuccessful'); 30 | } 31 | }, 32 | [projectId, ipfs, uploadMetadata, updateQueryMetadata], 33 | ); 34 | 35 | return updateMetadata; 36 | } 37 | -------------------------------------------------------------------------------- /src/hooks/useVerifyDeployment.1.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import { useIPFS } from '@containers/IPFS'; 6 | 7 | export const useVerifyDeployment = () => { 8 | const ipfs = useIPFS(); 9 | const getDployment = React.useCallback( 10 | async (deploymentId: string) => { 11 | const deployment = await ipfs.catSingle(deploymentId); 12 | const result = Buffer.from(deployment).toString('utf8'); 13 | return result; 14 | }, 15 | [ipfs], 16 | ); 17 | 18 | const verifyIfSubQuery = React.useCallback( 19 | async (deploymentId: string) => { 20 | try { 21 | const deployment = await getDployment(deploymentId); 22 | return deployment.includes('@subql'); 23 | } catch { 24 | return false; 25 | } 26 | }, 27 | [getDployment], 28 | ); 29 | 30 | const verifyIfSubGraph = React.useCallback( 31 | async (deploymentId: string) => { 32 | try { 33 | const deployment = await getDployment(deploymentId); 34 | return deployment.includes('wasm/assemblyscript'); 35 | } catch { 36 | return false; 37 | } 38 | }, 39 | [getDployment], 40 | ); 41 | 42 | return { verifyIfSubQuery, verifyIfSubGraph }; 43 | }; 44 | -------------------------------------------------------------------------------- /src/hooks/useVerifyDeployment.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import { useIPFS } from '@containers'; 6 | 7 | export const useVerifyDeployment = () => { 8 | const ipfs = useIPFS(); 9 | const getDployment = React.useCallback( 10 | async (deploymentId: string) => { 11 | const deployment = await ipfs.catSingle(deploymentId); 12 | const result = Buffer.from(deployment).toString('utf8'); 13 | return result; 14 | }, 15 | [ipfs], 16 | ); 17 | 18 | const verifyIfSubQuery = React.useCallback( 19 | async (deploymentId: string) => { 20 | try { 21 | const deployment = await getDployment(deploymentId); 22 | return deployment.includes('@subql') && deployment.includes('dataSources'); 23 | } catch { 24 | return false; 25 | } 26 | }, 27 | [getDployment], 28 | ); 29 | 30 | const verifyIfSubGraph = React.useCallback( 31 | async (deploymentId: string) => { 32 | try { 33 | const deployment = await getDployment(deploymentId); 34 | return deployment.includes('wasm/assemblyscript'); 35 | } catch { 36 | return false; 37 | } 38 | }, 39 | [getDployment], 40 | ); 41 | 42 | return { verifyIfSubQuery, verifyIfSubGraph }; 43 | }; 44 | -------------------------------------------------------------------------------- /src/hooks/useWaitTransactionHandled.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useCallback } from 'react'; 5 | import { gql, useLazyQuery } from '@apollo/client'; 6 | import { waitForSomething } from '@utils/waitForSomething'; 7 | import { isNumber } from 'lodash-es'; 8 | 9 | export const useWaitTransactionhandled = () => { 10 | const [requestLastProcessedHeight] = useLazyQuery(gql` 11 | query { 12 | _metadata { 13 | lastProcessedHeight 14 | } 15 | } 16 | `); 17 | 18 | const waitTransactionHandled = useCallback(async (targetHeight = 0) => { 19 | if (!isNumber(targetHeight)) return true; 20 | 21 | const res = await waitForSomething({ 22 | func: async () => { 23 | const { data } = await requestLastProcessedHeight({ 24 | fetchPolicy: 'network-only', 25 | }); 26 | return data?._metadata?.lastProcessedHeight >= targetHeight; 27 | }, 28 | splitTime: 2000, 29 | timeout: 30_000, 30 | }); 31 | 32 | return res; 33 | }, []); 34 | 35 | return waitTransactionHandled; 36 | }; 37 | -------------------------------------------------------------------------------- /src/i18n/en/dashboard.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const dashboardTrans = { 5 | dashboardHeader: { 6 | title: 'Dashboard', 7 | }, 8 | }; 9 | 10 | export default dashboardTrans; 11 | -------------------------------------------------------------------------------- /src/i18n/en/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import consumerTranslations from './consumer'; 5 | import dashboardTrans from './dashboard'; 6 | import delegatorTranslations from './delegator'; 7 | import explorerTranslations from './explorer'; 8 | import globalTransaltions from './global'; 9 | import indexerTranslations from './indexer'; 10 | import scanner from './scanner'; 11 | 12 | export const en = { 13 | translation: { 14 | ...delegatorTranslations, 15 | ...consumerTranslations, 16 | ...indexerTranslations, 17 | ...globalTransaltions, 18 | ...explorerTranslations, 19 | ...dashboardTrans, 20 | scanner, 21 | }, 22 | } as const; 23 | 24 | export type Translations = typeof en; 25 | 26 | export default en; 27 | -------------------------------------------------------------------------------- /src/i18n/en/scanner.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const translation = { 5 | general: { 6 | operatorsTooltip: 7 | 'The Total Stake allocated to this project, including the additional stake you entered in the Projected Rewards Calculator.', 8 | projectedRewards: 9 | 'The projected Stake Rewards you would have theoretically earned if you ran the project in the selected Era. This is an approximation and does not suggest the same reward level in future Eras', 10 | projectedApy: 11 | 'The projected Stake APY the project would have achieved if you’d staked in the selected Era. This is an approximation and does not suggest the same Stake APYl in future Eras', 12 | }, 13 | } as const; 14 | 15 | export default translation; 16 | -------------------------------------------------------------------------------- /src/i18n/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { initReactI18next } from 'react-i18next'; 5 | import i18n from 'i18next'; 6 | import LanguageDetector from 'i18next-browser-languagedetector'; 7 | 8 | import en from './en'; 9 | 10 | export const resources = { en } as const; 11 | 12 | i18n.use(LanguageDetector).use(initReactI18next).init({ 13 | debug: true, 14 | fallbackLng: 'en', 15 | resources, 16 | defaultNS: 'translation', 17 | returnNull: false, 18 | }); 19 | 20 | type Combine = T extends string ? (P extends '' ? T : `${P}.${T}`) : never; 21 | 22 | type ObjectKeyPaths = K extends string 23 | ? // just check T[K] is a Object type { xxx: 'yyy' } 24 | T[K] extends object 25 | ? ObjectKeyPaths> 26 | : Combine 27 | : never; 28 | 29 | // GetDictValue copy from https://stackoverflow.com/questions/58277973/how-to-type-check-i18n-dictionaries-with-typescript 30 | export type GetDictValue< 31 | T extends string, 32 | O = (typeof resources)['en']['translation'], 33 | > = T extends `${infer A}.${infer B}` 34 | ? A extends keyof O 35 | ? GetDictValue 36 | : never 37 | : T extends keyof O 38 | ? O[T] 39 | : never; 40 | 41 | export type TranslationKeys = ObjectKeyPaths<(typeof resources)['en']['translation']>; 42 | 43 | export default i18n; 44 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // i18n must render before App 5 | // eslint-disable-next-line simple-import-sort/imports 6 | import ReactDOM from 'react-dom/client'; 7 | import TagManager from 'react-gtm-module'; 8 | 9 | import './i18n'; 10 | import '@subql/components/dist/subquery-components.css'; 11 | 12 | import './config/polyfill'; 13 | import './config/sentryConf'; 14 | import './config/dayjsConf'; 15 | import 'reflect-metadata'; 16 | import './index.less'; 17 | 18 | import { App } from './App'; 19 | 20 | const tagManagerArgs = { 21 | gtmId: 'G-DK4PX8F61X', 22 | }; 23 | 24 | const isProd = import.meta.env.PROD; 25 | isProd && TagManager.initialize(tagManagerArgs); 26 | 27 | const cleanExpiryLocalStorage = () => { 28 | try { 29 | Object.keys(localStorage).forEach((key) => { 30 | const item = localStorage.getItem(key); 31 | if (item) { 32 | try { 33 | const parsed = JSON.parse(item); 34 | if (parsed.expiry && parsed.expiry < Date.now()) { 35 | localStorage.removeItem(key); 36 | } 37 | } catch (e) { 38 | // ignore 39 | } 40 | } 41 | }); 42 | } catch (e) { 43 | // ignore 44 | } 45 | }; 46 | 47 | cleanExpiryLocalStorage(); 48 | 49 | const root = ReactDOM.createRoot(document.getElementById('root') as HTMLDivElement); 50 | root.render(); 51 | -------------------------------------------------------------------------------- /src/pages/account/Account.module.less: -------------------------------------------------------------------------------- 1 | .account { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | } 6 | 7 | .page { 8 | min-height: 90vh; 9 | } 10 | 11 | .tab { 12 | margin-top: 24px; 13 | 14 | :global { 15 | .ant-tabs-tab.ant-tabs-tab.ant-tabs-tab { 16 | padding: 0; 17 | line-height: 22px; 18 | margin-bottom: 8px; 19 | 20 | &-btn { 21 | font-family: var(--sq-font-family); 22 | color: var(--sq-gray600); 23 | 24 | &:hover { 25 | color: var(--sq-blue600) 26 | 27 | } 28 | } 29 | 30 | &-active { 31 | .ant-tabs-tab-btn.ant-tabs-tab-btn.ant-tabs-tab-btn { 32 | color: var(--sq-blue600) 33 | } 34 | } 35 | } 36 | 37 | .ant-tabs-ink-bar { 38 | background: var(--sq-blue600); 39 | } 40 | } 41 | } 42 | 43 | .totalRewardsCard{ 44 | margin-right: 24px; 45 | min-height: 426px; 46 | min-width: 304px; 47 | } 48 | 49 | @media (max-width: 768px) { 50 | .dashboard{ 51 | display: flex; 52 | flex-direction: column; 53 | width: 100%; 54 | } 55 | 56 | .totalRewardsCard{ 57 | // TODO: change the subqlCard 58 | width: 100% !important; 59 | margin-right: 0px; 60 | margin-bottom: 24px; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/pages/account/AccountHeaders/Header.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useMemo } from 'react'; 5 | import { CurEra } from '@components/CurEra'; 6 | import { ConnectedIndexer } from '@components/IndexerDetails/IndexerName'; 7 | import { useAccount } from '@containers/Web3'; 8 | import { useIsMobile } from '@hooks/useIsMobile'; 9 | import { Typography } from '@subql/components'; 10 | 11 | import styles from './Headers.module.css'; 12 | 13 | export const AccountHeader: React.FC<{ profileAccount?: string }> = ({ profileAccount }) => { 14 | const { address } = useAccount(); 15 | const isMobile = useIsMobile(); 16 | 17 | const account = useMemo(() => profileAccount || address, [profileAccount, address]); 18 | 19 | return ( 20 |
21 |
22 | 23 | {profileAccount ? 'Profile' : 'My Profile'} 24 | 25 | {!isMobile && account && } 26 | {isMobile && } 27 |
28 | {!isMobile && } 29 | {isMobile && account && ( 30 |
31 | 32 |
33 | )} 34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/pages/account/AccountHeaders/Headers.module.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: var(--sq-gray100); 3 | width: 100%; 4 | } 5 | 6 | .container { 7 | flex-direction: column; 8 | max-width: 1100px; 9 | height: 100vh; 10 | margin: 0 auto; 11 | } 12 | 13 | .statsTiles { 14 | display: flex; 15 | height: 12rem; 16 | padding-bottom: 2rem; 17 | border-bottom: 1px solid var(--gray300); 18 | column-gap: 1rem; 19 | justify-content: space-between; 20 | } 21 | 22 | .statTile { 23 | flex: 1; 24 | overflow: hidden; 25 | } 26 | 27 | .myDelegators { 28 | height: 50vh; 29 | margin-top: 2rem; 30 | padding: 2rem; 31 | background-color: white; 32 | border-radius: 5px; 33 | border: 1px solid var(--gray300); 34 | } 35 | 36 | .delegatorHeader { 37 | display: flex; 38 | justify-content: space-between; 39 | } 40 | 41 | .header, 42 | .accountContainer { 43 | display: flex; 44 | justify-content: space-between; 45 | align-items: center; 46 | } 47 | 48 | .accountContainer > * { 49 | margin: auto; 50 | } 51 | 52 | .account { 53 | padding-left: 1rem; 54 | } 55 | 56 | .link { 57 | padding-top: 5px; 58 | } 59 | 60 | .header { 61 | display: flex; 62 | justify-content: space-between; 63 | align-items: center; 64 | margin: 1rem 0 1rem 0; 65 | } 66 | 67 | @media (max-width: 768px) { 68 | .header { 69 | flex-direction: column; 70 | width: 100%; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/pages/account/Rewards/ClaimRewards.module.less: -------------------------------------------------------------------------------- 1 | .submitBtn { 2 | background: var(--gradient-to) !important; 3 | color: white !important; 4 | min-width: 30%; 5 | } 6 | 7 | .submitBtn:hover { 8 | background: var(--primary-hover) !important; 9 | } 10 | 11 | .btnContainer { 12 | width: 100%; 13 | display: flex; 14 | justify-content: flex-end; 15 | margin-top: 1rem; 16 | } 17 | 18 | .claimButton { 19 | line-height: 16px; 20 | background: var(--sq-blue600); 21 | font-size: 16px; 22 | 23 | :global { 24 | .anticon-spin { 25 | background: var(--sq-blue600); 26 | 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/account/Rewards/Rewards.module.css: -------------------------------------------------------------------------------- 1 | .rewardsContainer { 2 | width: 100%; 3 | } 4 | 5 | .rewardsList { 6 | margin: 8px 0; 7 | } 8 | 9 | .text { 10 | font-size: 14px; 11 | } 12 | 13 | .unClaimedText { 14 | color: var(--primary); 15 | } 16 | 17 | .header { 18 | margin-bottom: 8px; 19 | margin-top: 16px; 20 | } 21 | 22 | .claim { 23 | display: flex; 24 | justify-content: space-between; 25 | align-items: center; 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/account/Rewards/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Rewards'; 5 | -------------------------------------------------------------------------------- /src/pages/account/Staking/Breakdown.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { FC } from 'react'; 5 | import { PieChart } from 'echarts/charts'; 6 | import { LegendComponent, TitleComponent, TooltipComponent } from 'echarts/components'; 7 | import * as echarts from 'echarts/core'; 8 | import { LabelLayout } from 'echarts/features'; 9 | import { SVGRenderer } from 'echarts/renderers'; 10 | import ReactEChartsCore from 'echarts-for-react/lib/core'; 11 | 12 | echarts.use([TitleComponent, TooltipComponent, LegendComponent, PieChart, SVGRenderer, LabelLayout]); 13 | 14 | interface IProps { 15 | data: { 16 | value: number; 17 | }[]; 18 | } 19 | 20 | const Breakdown: FC = (props) => { 21 | return ( 22 |
23 | { 44 | return params.dataIndex === 0 ? '#7BACE7' : '#C7DBF5'; 45 | }, 46 | }, 47 | }, 48 | ], 49 | }} 50 | /> 51 |
52 | ); 53 | }; 54 | export default Breakdown; 55 | -------------------------------------------------------------------------------- /src/pages/account/Staking/index.module.less: -------------------------------------------------------------------------------- 1 | .staking { 2 | display: flex; 3 | flex-direction: column; 4 | margin-top: 24px; 5 | &Breakdown { 6 | padding: 24px; 7 | border-radius: 8px; 8 | border: 1px solid var(--Card-boder, rgba(223, 227, 232, 0.60)); 9 | background: #FFF; 10 | margin-bottom: 24px; 11 | } 12 | 13 | .myDelegators { 14 | margin-top: 24px; 15 | } 16 | } 17 | 18 | .breakdownDashboard{ 19 | margin-top: 8px; 20 | justify-content: center; 21 | } 22 | 23 | .breakdownInfo{ 24 | margin-left: 24px; 25 | } 26 | 27 | @media (max-width: 768px) { 28 | .breakdownDashboard{ 29 | flex-direction: column; 30 | } 31 | .breakdownInfo{ 32 | margin-left: 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Locked/DoWithdraw/DoWithdraw.module.css: -------------------------------------------------------------------------------- 1 | .btnContainer { 2 | margin: 1rem 0; 3 | } 4 | 5 | .unlockedAmount { 6 | margin: 1rem 0; 7 | } 8 | 9 | .withdrawButton { 10 | padding: 16px 32px; 11 | line-height: 16px; 12 | background: var(--sq-blue600); 13 | font-size: 16px; 14 | 15 | :global { 16 | .anticon-spin { 17 | background: var(--sq-blue600); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Locked/DoWithdraw/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './DoWithdraw'; 5 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Locked/Home/Locked.module.less: -------------------------------------------------------------------------------- 1 | .withdrawnContainer { 2 | margin: 1rem 0; 3 | } 4 | 5 | .container { 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | 10 | .header { 11 | display: flex; 12 | justify-content: space-between; 13 | align-items: center; 14 | } 15 | 16 | .title { 17 | margin: 1rem 0; 18 | } 19 | 20 | .tableCell { 21 | color: var(--gray600); 22 | } 23 | 24 | 25 | // TODO: replace it when components finishi 26 | .cancelModal { 27 | :global .ant-modal-content { 28 | .ant-modal-close { 29 | display: none; 30 | } 31 | .ant-modal-header { 32 | display: none; 33 | } 34 | 35 | .modalSteps { 36 | display: none; 37 | } 38 | 39 | .ant-btn { 40 | padding: 10px 16px; 41 | height: auto; 42 | font-family: var(--sq-font-family); 43 | font-size: 14px; 44 | line-height: 1; 45 | height: 38px; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Locked/Home/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Locked'; 5 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Locked/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Home/Locked'; 5 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Withdrawn.module.css: -------------------------------------------------------------------------------- 1 | .rewardsContainer { 2 | width: 100%; 3 | } 4 | 5 | .rewardsList { 6 | margin: 2rem 0; 7 | } 8 | 9 | .text { 10 | font-size: 14px; 11 | } 12 | 13 | .unClaimedText { 14 | color: var(--primary); 15 | } 16 | 17 | .header { 18 | margin-bottom: 2rem; 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/Withdrawn.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | 6 | import { Locked } from './Locked'; 7 | import styles from './Withdrawn.module.css'; 8 | 9 | export const Withdrawn: React.FC = () => { 10 | return ( 11 |
12 | 13 |
14 | ); 15 | }; 16 | 17 | export default Withdrawn; 18 | -------------------------------------------------------------------------------- /src/pages/account/Withdrawn/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Withdrawn'; 5 | -------------------------------------------------------------------------------- /src/pages/bridge/success/index.module.less: -------------------------------------------------------------------------------- 1 | .swapSuccess { 2 | width: 100%; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | 7 | &Inner { 8 | display: flex; 9 | flex-direction: column; 10 | width: 421px; 11 | align-items: center; 12 | gap: 24px; 13 | padding: 24px; 14 | border: 1px solid rgba(223, 227, 232, 0.60); 15 | border-radius: 8px; 16 | } 17 | } -------------------------------------------------------------------------------- /src/pages/bridge/success/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { FC } from 'react'; 5 | import { BsCheckCircleFill, BsLifePreserver } from 'react-icons/bs'; 6 | import { Typography } from '@subql/components'; 7 | 8 | import styles from './index.module.less'; 9 | 10 | const SwapSuccess: FC = () => { 11 | return ( 12 |
13 |
14 | 15 | 16 | Bridge successful 17 | 18 | 19 | Your bridge successfully completed. You can view it on{' '} 20 | 21 | Basescan. 22 | {' '} 23 | and{' '} 24 | 25 | Etherscan. 26 | 27 | 28 | 29 | 30 | 31 | Need help? please 32 | 33 | contact us 34 | 35 | 36 |
37 |
38 | ); 39 | }; 40 | export default SwapSuccess; 41 | -------------------------------------------------------------------------------- /src/pages/consumer/MyBoostedProjects/index.module.less: -------------------------------------------------------------------------------- 1 | .myBoostedProjects { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 24px; 5 | } -------------------------------------------------------------------------------- /src/pages/consumer/MyFlexPlans/BillingAction.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { OutlineDot } from '@components/Icons/Icons'; 6 | import { Dropdown } from 'antd'; 7 | 8 | import { BillingExchangeModal } from '../../../components/BillingTransferModal'; 9 | 10 | export const BillingAction: React.FC = () => { 11 | return ( 12 | , 17 | key: 1, 18 | }, 19 | { 20 | label: , 21 | key: 2, 22 | }, 23 | ], 24 | }} 25 | > 26 | 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/pages/consumer/MyFlexPlans/MyFlexPlans.module.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | } 6 | 7 | .cards { 8 | margin: 1.5rem 0; 9 | display: flex; 10 | } 11 | 12 | .balances { 13 | display: flex; 14 | column-gap: 1rem; 15 | } 16 | 17 | /* ClaimFlexPlan */ 18 | .btnContainer { 19 | width: 100%; 20 | display: flex; 21 | justify-content: flex-end; 22 | margin-top: 3rem; 23 | } 24 | .billingAction { 25 | font-size: larger; 26 | height: fit-content; 27 | padding: 1rem; 28 | } 29 | 30 | /* Terminate Plan */ 31 | .remainDeposit { 32 | margin: 2rem 0; 33 | } 34 | 35 | .terminateError { 36 | margin-bottom: 1rem; 37 | } 38 | 39 | .actionList { 40 | display: flex; 41 | align-items: center; 42 | } 43 | -------------------------------------------------------------------------------- /src/pages/consumer/MyFlexPlans/MyHostedPlan/MyHostedPlan.module.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subquery/network-app/7052848f393ea7026885eb750be50accd8e12426/src/pages/consumer/MyFlexPlans/MyHostedPlan/MyHostedPlan.module.less -------------------------------------------------------------------------------- /src/pages/consumer/MyFlexPlans/apiKeys.module.less: -------------------------------------------------------------------------------- 1 | .apiKeys { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | flex:1; 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/consumer/MyFlexPlans/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './MyFlexPlans'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/ChooseTemplate/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './ChooseTemplate'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/CreateOffer.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | background: white; 3 | width: 64%; 4 | min-height: 420px; 5 | padding: 1rem; 6 | } 7 | 8 | .steps { 9 | margin: 1rem 0; 10 | overflow: auto; 11 | } 12 | 13 | .stepButtons { 14 | margin: 2rem 0; 15 | } 16 | 17 | .back { 18 | display: flex; 19 | align-items: center; 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/OfferDetails/OfferDetails.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | background: white; 3 | width: 70%; 4 | min-height: 420px; 5 | padding: 1rem; 6 | } 7 | 8 | .steps { 9 | margin: 1rem 0; 10 | overflow: scroll; 11 | } 12 | 13 | .tabs { 14 | display: flex; 15 | justify-content: space-between; 16 | align-items: center; 17 | } 18 | 19 | .create { 20 | height: 40px; 21 | } 22 | 23 | .datePickerTitle { 24 | font-family: 'Inter', sans-serif; 25 | font-size: 16px; 26 | color: var(--gray900); 27 | } 28 | 29 | .datePicker { 30 | width: 100%; 31 | } 32 | 33 | .cancelWarning { 34 | background: var(--sq-gray200); 35 | border-radius: 6px; 36 | padding: 1.6rem; 37 | margin: 1rem 0; 38 | display: flex; 39 | justify-content: center; 40 | align-items: center; 41 | } 42 | 43 | .cancelWarningText { 44 | margin: 0 1rem; 45 | } 46 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/OfferDetails/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './OfferDetails'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/SelectDeployment/SelectDeployment.module.css: -------------------------------------------------------------------------------- 1 | .description { 2 | width: 80%; 3 | } 4 | 5 | /* TODO: add custom theme */ 6 | .descriptionBtn { 7 | padding: 0 !important; 8 | } 9 | 10 | .searchDeployment { 11 | margin: 1rem 0; 12 | } 13 | 14 | .deploymentInfoContainer { 15 | margin: 1rem 0; 16 | } 17 | 18 | .deploymentInfo { 19 | margin: 1rem 0; 20 | border: 1px solid var(--gradient-to); 21 | border-radius: 5px; 22 | padding: 1rem; 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/SelectDeployment/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './SelectDeployment'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/Summary/Summary.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | background: white; 3 | width: 70%; 4 | min-height: 420px; 5 | padding: 1rem; 6 | } 7 | 8 | .tabs { 9 | display: flex; 10 | justify-content: space-between; 11 | align-items: center; 12 | } 13 | 14 | .create { 15 | height: 40px; 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/Summary/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Summary'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/CreateOffer/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './CreateOffer'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/MyOffers.module.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | display: flex; 3 | justify-content: space-between; 4 | } 5 | 6 | .content { 7 | margin-top: 2rem; 8 | } 9 | 10 | .create { 11 | height: 40px; 12 | flex-shrink: 0; 13 | } 14 | 15 | .description { 16 | margin-top: 1vh; 17 | } 18 | 19 | .infoLink { 20 | margin-top: 2vh; 21 | } 22 | 23 | .noOffersContainer { 24 | margin: 10vh auto; 25 | height: 50vh; 26 | width: 30vw; 27 | display: flex; 28 | flex-direction: column; 29 | text-align: center; 30 | } 31 | 32 | /* CancelOffer */ 33 | .btnContainer { 34 | width: 100%; 35 | display: flex; 36 | justify-content: flex-end; 37 | margin-top: 1rem; 38 | } 39 | 40 | .cancelOfferSummary { 41 | margin: 2rem 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/OfferTable.module.css: -------------------------------------------------------------------------------- 1 | .description { 2 | white-space: pre-line; 3 | font-size: 16px; 4 | } 5 | 6 | .offerTableHeader { 7 | margin: 1rem 0; 8 | } 9 | 10 | .searchDeployment { 11 | width: 360px; 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/consumer/MyOffers/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './MyOffers'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/OfferMarketplace/AcceptOffer.module.css: -------------------------------------------------------------------------------- 1 | .offerSummary { 2 | margin: 1rem 0; 3 | } 4 | 5 | .requirementCheckContainer { 6 | margin: 1rem 0; 7 | } 8 | 9 | .checkListTitle { 10 | margin-bottom: 6px; 11 | margin-top: 1rem; 12 | font-size: 16px; 13 | font-weight: 500; 14 | } 15 | 16 | .requirementCheckItemContainer { 17 | margin-bottom: 1rem; 18 | } 19 | 20 | .requirementCheckItem { 21 | border: 1px solid var(--gray300); 22 | padding: 0.6rem; 23 | border-radius: 4px; 24 | } 25 | 26 | .requirementText { 27 | width: 25%; 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/consumer/OfferMarketplace/Marketplace.module.css: -------------------------------------------------------------------------------- 1 | .offers { 2 | margin-top: 2rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/consumer/OfferMarketplace/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Marketplace'; 5 | -------------------------------------------------------------------------------- /src/pages/consumer/Playground/Playground.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | margin: 1rem 0; 6 | } 7 | 8 | .title { 9 | font-weight: 600; 10 | font-size: 18px; 11 | line-height: 20px; 12 | } 13 | 14 | .errorText { 15 | margin: 1rem 0; 16 | } 17 | 18 | .content { 19 | background: white; 20 | min-height: 560px; 21 | border-radius: 4px; 22 | padding: 1.6rem; 23 | margin: 1rem 0; 24 | } 25 | 26 | /* RequestToken */ 27 | .requestToken { 28 | display: flex; 29 | flex-direction: column; 30 | align-items: center; 31 | 32 | padding: 5rem 0; 33 | } 34 | 35 | .requestTokenBtn { 36 | margin: 1rem 0; 37 | background: var(--gradient-to) !important; 38 | } 39 | 40 | /* playground */ 41 | .deploymentMetaContainer { 42 | background: white; 43 | border-radius: 4px; 44 | padding: 1rem; 45 | margin: 1rem 0; 46 | } 47 | 48 | .deploymentMeta { 49 | border: 1px solid var(--gray300); 50 | border-radius: 6px; 51 | padding: 1rem; 52 | margin: 1rem 0; 53 | } 54 | 55 | .deploymentTable { 56 | margin-bottom: 1rem; 57 | } 58 | 59 | .playgroundHeader { 60 | display: flex; 61 | justify-content: space-between; 62 | margin: 1rem 0; 63 | } 64 | 65 | .playgroundText { 66 | width: 30%; 67 | } 68 | 69 | .playgroundContainer { 70 | display: flex; 71 | height: 60vh; 72 | flex-direction: column; 73 | } 74 | -------------------------------------------------------------------------------- /src/pages/consumer/Playground/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './SAPlayground'; 5 | export * from './FlexPlayground'; 6 | -------------------------------------------------------------------------------- /src/pages/consumer/ServiceAgreements/ServiceAgreements.module.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | margin-top: 1rem; 6 | } 7 | 8 | .create { 9 | height: 40px; 10 | } 11 | 12 | .url { 13 | white-space: nowrap; 14 | overflow: hidden; 15 | text-overflow: ellipsis; 16 | } 17 | 18 | .addressCont { 19 | display: flex; 20 | max-width: 300px; 21 | } 22 | 23 | .copy { 24 | margin-left: 6px; 25 | flex-shrink: 0; 26 | } 27 | 28 | .description { 29 | margin-top: 2vh; 30 | } 31 | 32 | .infoLink { 33 | margin-top: 2vh; 34 | } 35 | 36 | .noAgreementsContainer { 37 | margin: 10vh auto; 38 | height: 50vh; 39 | width: 33vw; 40 | display: flex; 41 | flex-direction: column; 42 | text-align: center; 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/consumer/ServiceAgreements/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { FC } from 'react'; 5 | 6 | import { ServiceAgreements } from './ServiceAgreements'; 7 | 8 | const IndexerServiceAgreements: FC = () => { 9 | return ; 10 | }; 11 | 12 | export default IndexerServiceAgreements; 13 | export { ServiceAgreements }; 14 | -------------------------------------------------------------------------------- /src/pages/dashboard/components/ActiveCard/ActiveCard.module.less: -------------------------------------------------------------------------------- 1 | .images { 2 | display: flex; 3 | margin-top: 12px; 4 | 5 | .image { 6 | flex-shrink: 0; 7 | width: 32px; 8 | height: 32px; 9 | border-radius: 50%; 10 | overflow: hidden; 11 | cursor: pointer; 12 | margin-left: 0; 13 | transition: all 0.1s linear; 14 | 15 | & + .image { 16 | margin-left: -4px; 17 | } 18 | 19 | &:hover + .image { 20 | margin-left: 4px; 21 | } 22 | } 23 | } 24 | 25 | .explorerButton,.explorerRewardButton { 26 | position: absolute; 27 | right: 0px; 28 | top: 0px; 29 | } 30 | 31 | .explorerRewardButton { 32 | background: #FF4581; 33 | } 34 | 35 | .activeCarousel { 36 | :global { 37 | .slick-dots-bottom { 38 | height: 5px; 39 | li { 40 | background: rgba(67, 136, 221, 0.3); 41 | } 42 | 43 | .slick-active { 44 | background: var(--sq-blue600); 45 | 46 | &::after { 47 | background: transparent; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | .projectsCard { 55 | overflow: hidden; 56 | &::before { 57 | content: ''; 58 | position: absolute; 59 | top: 0; 60 | left: 0; 61 | width: 11px; 62 | height: 100%; 63 | background: var(--sq-blue600); 64 | } 65 | } 66 | 67 | .consumerRewardsCard { 68 | overflow: hidden; 69 | &::before { 70 | content: ''; 71 | position: absolute; 72 | top: 0; 73 | left: 0; 74 | width: 11px; 75 | height: 100%; 76 | background: #FF4581; 77 | } 78 | } 79 | 80 | @media (max-width: 768px) { 81 | .explorerButton { 82 | position: relative; 83 | margin-top: 16px; 84 | width: 100%; 85 | } 86 | } -------------------------------------------------------------------------------- /src/pages/delegator/AllIndexers/IndexerList/IndexerList.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: white; 3 | padding: 1rem; 4 | overflow: auto; 5 | } 6 | 7 | .indexerListHeader { 8 | margin: 1rem 0 2rem 0; 9 | display: flex; 10 | justify-content: space-between; 11 | align-items: flex-start; 12 | } 13 | 14 | .indexerSearch { 15 | width: 360px; 16 | } 17 | 18 | .tipsBanner { 19 | display: flex; 20 | flex-direction: column; 21 | gap: 16px; 22 | padding: 24px; 23 | border-radius: 8px; 24 | background-color: #4388dd26; 25 | background-image: url('/static/check.png'); 26 | background-repeat: no-repeat; 27 | background-position: right bottom; 28 | position: relative; 29 | } 30 | 31 | @media (max-width: 768px) { 32 | .indexerListHeader { 33 | display: block; 34 | } 35 | 36 | .indexerSearch { 37 | width: 100%; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/pages/delegator/AllIndexers/IndexerList/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { IndexerList } from './IndexerList'; 5 | -------------------------------------------------------------------------------- /src/pages/delegator/DoDelegate/DoDelegate.module.less: -------------------------------------------------------------------------------- 1 | .btn { 2 | margin-right: 1rem; 3 | background: var(--gradient-to); 4 | } 5 | 6 | .option { 7 | padding: 0.2rem 0; 8 | gap: 8px; 9 | } 10 | 11 | .description { 12 | color: var(--gray700); 13 | margin: 1rem 0; 14 | } 15 | 16 | .delegatorSelect { 17 | 18 | :global { 19 | // up css priority 20 | .ant-select-selector.ant-select-selector.ant-select-selector.ant-select-selector { 21 | height: 72px; 22 | max-height: 72px; 23 | padding: 12px; 24 | } 25 | 26 | .ant-select-selection-search-input.ant-select-selection-search-input.ant-select-selection-search-input.ant-select-selection-search-input { 27 | margin-top: 5px; 28 | } 29 | 30 | .ant-space { 31 | line-height: 1; 32 | } 33 | } 34 | } 35 | 36 | // TODO: migrate when re-new @subql/components 37 | .alertInfo { 38 | border: 1px solid rgba(58, 160, 255, 0.50); 39 | background: rgba(58, 160, 255, 0.08); 40 | display: flex; 41 | align-items: flex-start; 42 | padding: 9px 16px; 43 | 44 | :global { 45 | .anticon-info-circle { 46 | color: var(--sq-info); 47 | margin-top: 4px; 48 | } 49 | } 50 | } 51 | 52 | .button { 53 | padding: 16px 32px!important; 54 | line-height: 1!important; 55 | height: auto!important; 56 | } 57 | 58 | .disabledButton { 59 | border: none; 60 | background: #F4F6F8; 61 | color: var(--sq-gray400); 62 | } 63 | -------------------------------------------------------------------------------- /src/pages/delegator/DoDelegate/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './DoDelegate'; 5 | -------------------------------------------------------------------------------- /src/pages/delegator/DoUndelegate/DoUndelegate.module.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | margin-right: 1rem; 3 | background: var(--gradient-to); 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/delegator/DoUndelegate/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './DoUndelegate'; 5 | -------------------------------------------------------------------------------- /src/pages/delegator/IndexerDetails/IndexerDetails.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-top: 2rem; 3 | padding: 2rem; 4 | background-color: white; 5 | border-radius: 5px; 6 | border: 1px solid var(--gray300); 7 | } 8 | 9 | .delegatorHeader { 10 | display: flex; 11 | justify-content: space-between; 12 | } 13 | 14 | .header, 15 | .accountContainer { 16 | display: flex; 17 | height: 12vh; 18 | justify-content: space-between; 19 | align-items: center; 20 | } 21 | 22 | .navHeader { 23 | display: flex; 24 | justify-content: space-between; 25 | align-items: center; 26 | margin: 1rem 0; 27 | } 28 | 29 | .title { 30 | font-weight: 600; 31 | font-size: 18px; 32 | line-height: 20px; 33 | } 34 | 35 | .account { 36 | padding-left: 1rem; 37 | } 38 | 39 | .link { 40 | padding-top: 5px; 41 | } 42 | 43 | .delegateActions { 44 | display: flex; 45 | } 46 | -------------------------------------------------------------------------------- /src/pages/delegator/Indexers.module.css: -------------------------------------------------------------------------------- 1 | .tabList { 2 | display: flex; 3 | margin-top: 2rem; 4 | text-align: center; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/delegator/Indexers.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | import { Outlet } from 'react-router'; 7 | import { AppPageHeader } from '@components/AppPageHeader'; 8 | import { ChatBoxPlanTextTrigger, Typography } from '@subql/components'; 9 | 10 | import { useChatBoxStore } from 'src/stores/chatbox'; 11 | 12 | export const Indexers: React.FC = () => { 13 | const { t } = useTranslation(); 14 | const { chatBoxRef } = useChatBoxStore(); 15 | return ( 16 | <> 17 | 20 | {t('indexer.indexers')} 21 | 22 | Which operator should I delegate to? 23 | 24 | 25 | } 26 | desc={t('allIndexers.desc')} 27 | /> 28 | 29 | 30 | ); 31 | }; 32 | 33 | export default Indexers; 34 | -------------------------------------------------------------------------------- /src/pages/delegator/MyDelegation.module.css: -------------------------------------------------------------------------------- 1 | .delegatingCard { 2 | margin: 1rem 0; 3 | width: 20vw; 4 | } 5 | 6 | .doDelegate { 7 | margin: 1rem 0; 8 | } 9 | 10 | .rewardsLineChart { 11 | width: 100%; 12 | } 13 | 14 | .delegationInfo { 15 | margin: 24px 0px; 16 | } 17 | 18 | .newCard { 19 | width: auto; 20 | margin-right: 24px; 21 | min-width: 364px; 22 | height: 340px; 23 | } 24 | 25 | @media (max-width: 768px) { 26 | .rewardsLineChart { 27 | width: 100%; 28 | margin-top: 16px; 29 | } 30 | 31 | .delegationInfo { 32 | margin: 24px 0px; 33 | flex-wrap: wrap; 34 | } 35 | 36 | .newCard { 37 | width: 100% !important; 38 | min-width: 364px; 39 | height: 340px; 40 | margin-right: 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/pages/delegator/TopIndexers/TopIndexers.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | import { AppPageHeader } from '@components/AppPageHeader'; 7 | import { TOP_100_INDEXERS } from '@containers'; 8 | import { Spinner } from '@subql/components'; 9 | import { useGetTopIndexersQuery } from '@subql/react-hooks'; 10 | import { Typography } from 'antd'; 11 | 12 | import { renderAsync } from '../../../utils'; 13 | import { TopIndexerList } from './TopIndexersList'; 14 | 15 | export const TopIndexers: React.FC = () => { 16 | const { t } = useTranslation(); 17 | const topIndexers = useGetTopIndexersQuery({ 18 | context: { clientName: TOP_100_INDEXERS }, 19 | }); 20 | 21 | return ( 22 |
23 | 24 | {renderAsync(topIndexers, { 25 | loading: () => , 26 | error: (error) => ( 27 | <> 28 | Error: {' '} 29 | {`Failed to get top Indexers: ${error.message}`} 30 | 31 | ), 32 | data: (data) => { 33 | const topIndexers = data?.indexerPrograms; 34 | 35 | if (!topIndexers) { 36 | return {t('topIndexers.nonData')}; 37 | } 38 | return ; 39 | }, 40 | })} 41 |
42 | ); 43 | }; 44 | 45 | export default TopIndexers; 46 | -------------------------------------------------------------------------------- /src/pages/delegator/TopIndexers/TopIndexersList.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: white; 3 | padding: 1rem; 4 | overflow: auto; 5 | } 6 | 7 | .indexerListHeader { 8 | margin: 1rem 0 2rem 0; 9 | display: flex; 10 | justify-content: flex-end; 11 | } 12 | 13 | .indexerSearch { 14 | width: 360px; 15 | } 16 | -------------------------------------------------------------------------------- /src/pages/delegator/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Outlet } from 'react-router-dom'; 6 | import { AppSidebar } from '@components/AppSidebar'; 7 | import { DelegatorSidebar } from '@utils/links'; 8 | 9 | const Delegator: React.FC = () => { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | }; 16 | 17 | export default Delegator; 18 | -------------------------------------------------------------------------------- /src/pages/explorer/Explorer.module.css: -------------------------------------------------------------------------------- 1 | .account { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | } 6 | 7 | .page { 8 | position: relative; 9 | height: 100%; 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/explorer/FlexPlans/CreateHostingPlan/index.module.less: -------------------------------------------------------------------------------- 1 | .billBalance { 2 | margin: 1rem 0; 3 | } 4 | 5 | .billingCard { 6 | border-radius: 8px; 7 | border: 1px solid var(--sq-gray200); 8 | width: 100%; 9 | padding: 16px 24px; 10 | margin-bottom: 24px; 11 | } 12 | 13 | .billingButton { 14 | background: var(--sq-blue600); 15 | border-color: var(--sq-blue600); 16 | } 17 | 18 | .createFlexPlanModal { 19 | :global { 20 | .ant-form-item { 21 | margin-bottom: 0; 22 | } 23 | 24 | .ant-form-item-required.ant-form-item-required.ant-form-item-required.ant-form-item-required { 25 | &::before { 26 | display: none; 27 | } 28 | } 29 | 30 | .ant-input-number-group-wrapper { 31 | width: 100%; 32 | 33 | .ant-input-number { 34 | border-right: none; 35 | } 36 | .ant-input-number-group-addon { 37 | background: none; 38 | color: var(--sq-gray500); 39 | } 40 | 41 | &:hover { 42 | .ant-input-number-group-addon { 43 | border-color: #4096ff; 44 | } 45 | } 46 | 47 | .ant-input-number-focused ~ .ant-input-number-group-addon { 48 | border-color: #4096ff; 49 | } 50 | 51 | .ant-input-number-status-error ~ .ant-input-number-group-addon { 52 | border-color: #ff4d4f; 53 | } 54 | } 55 | 56 | .ant-input-number-handler-wrap { 57 | display: none; 58 | } 59 | 60 | .ant-input-number { 61 | width: 100%; 62 | padding: 12px; 63 | font-size: 16px; 64 | .ant-input-number-input { 65 | height: 24px; 66 | padding: 0; 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/pages/explorer/FlexPlans/FlexPlans.module.less: -------------------------------------------------------------------------------- 1 | .starContainer { 2 | width: 1.5rem; 3 | } 4 | 5 | .star { 6 | color: var(--yellow700); 7 | width: 1.2rem; 8 | height: 1.2rem; 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/explorer/FlexPlans/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './FlexPlans'; 5 | -------------------------------------------------------------------------------- /src/pages/explorer/Home/Home.module.css: -------------------------------------------------------------------------------- 1 | .explorer { 2 | padding: 72px 80px; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | flex-direction: column; 10 | margin-bottom: 18px; 11 | align-items: center; 12 | } 13 | 14 | .headerTitle { 15 | font-weight: 500; 16 | font-size: 32px; 17 | line-height: 38px; 18 | } 19 | 20 | .line { 21 | background: var(--sq-gradient); 22 | margin-top: 24px; 23 | width: 48px; 24 | height: 10px; 25 | } 26 | 27 | .typographySecondary { 28 | width: 565px; 29 | margin-top: 16px; 30 | } 31 | 32 | @media (max-width: 768px) { 33 | .typographySecondary { 34 | width: auto; 35 | margin-top: 16px; 36 | } 37 | 38 | .explorer { 39 | padding: 30px; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pages/explorer/Home/Home.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | import { useRouteQuery } from '@hooks'; 7 | import { useProjectList } from '@hooks/useProjectList'; 8 | import { Typography } from '@subql/components'; 9 | import { ProjectType } from '@subql/network-query'; 10 | 11 | import styles from './Home.module.css'; 12 | 13 | // TODO move to components 14 | export const Header: React.FC = () => { 15 | const { t } = useTranslation(); 16 | 17 | return ( 18 |
19 | 20 | {t('explorer.home.header')} 21 | 22 |
23 | 24 | {t('explorer.home.headerDesc')} 25 | 26 |
27 |
28 | ); 29 | }; 30 | 31 | const Home: React.FC = () => { 32 | const query = useRouteQuery(); 33 | 34 | const { listsWithSearch } = useProjectList({ 35 | showTopProject: false, 36 | defaultFilterProjectType: query.get('category') === 'rpc' ? ProjectType.RPC : ProjectType.SUBQUERY, 37 | }); 38 | 39 | return ( 40 |
41 |
42 |
43 | {listsWithSearch} 44 |
45 |
46 | ); 47 | }; 48 | 49 | export default Home; 50 | -------------------------------------------------------------------------------- /src/pages/explorer/Project/Project.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | min-height: 85%; 4 | } 5 | 6 | .progress { 7 | margin: 40px 0; 8 | } 9 | 10 | .upper { 11 | padding: 24px 78px; 12 | } 13 | 14 | .projectHeader { 15 | margin: 24px 0; 16 | } 17 | 18 | .contentOverview{ 19 | padding: 0px 78px 24px; 20 | } 21 | 22 | @media (max-width: 768px) { 23 | .container { 24 | display: flex; 25 | flex-direction: column; 26 | } 27 | .upper { 28 | padding: 24px 25px; 29 | } 30 | .contentOverview{ 31 | padding: 0px 24px 24px; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/explorer/Project/type.ts: -------------------------------------------------------------------------------- 1 | export enum ProjectActionArgv { 2 | BOOST = 'boost', 3 | CREATE_PLAN = 'createPlan', 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/explorer/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Outlet } from 'react-router'; 6 | import { Footer } from '@subql/components'; 7 | 8 | import styles from './Explorer.module.css'; 9 | 10 | const Explorer: React.FC = () => { 11 | return ( 12 |
13 |
14 | 15 |
16 |
17 |
18 | ); 19 | }; 20 | 21 | export default Explorer; 22 | -------------------------------------------------------------------------------- /src/pages/indexer/MyDelegators/MyDelegators.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | background: white; 3 | padding: 2rem; 4 | overflow: auto; 5 | min-height: 80%; 6 | } 7 | 8 | .offers { 9 | margin: 1rem 0; 10 | } 11 | 12 | .noDelegatorsContainer { 13 | margin: 10vh auto; 14 | width: 30vw; 15 | display: flex; 16 | flex-direction: column; 17 | text-align: center; 18 | } 19 | 20 | .description { 21 | margin-top: 3vh; 22 | } 23 | -------------------------------------------------------------------------------- /src/pages/indexer/MyDelegators/OwnDelegator.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 1rem 0; 3 | } 4 | 5 | .text { 6 | color: var(--gray500); 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Create/Create.module.less: -------------------------------------------------------------------------------- 1 | .btns { 2 | margin-top: 2rem; 3 | } 4 | 5 | .btn { 6 | margin: 0 1rem; 7 | } 8 | 9 | .select { 10 | margin: 1rem 0; 11 | } 12 | 13 | .inputTitle { 14 | margin: 6px 0; 15 | color: var(--gray700); 16 | } 17 | 18 | .projectName { 19 | color: var(--gray700); 20 | padding-right: 5px; 21 | } 22 | 23 | .projectDeploymentId { 24 | font-size: 12px; 25 | color: var(--gray500); 26 | } 27 | 28 | .deploymentOption { 29 | padding-top: 0.5rem; 30 | } 31 | 32 | .marginSpace { 33 | margin: 1rem 0; 34 | } 35 | 36 | .templateList { 37 | height: 350px; 38 | overflow: auto; 39 | } 40 | 41 | .project { 42 | display: flex; 43 | flex-direction: column; 44 | } 45 | 46 | .usdcAlert { 47 | padding: 9px 16px; 48 | border-radius: 8px; 49 | border: 1px solid rgba(58, 160, 255, 0.5); 50 | background: rgba(58, 160, 255, 0.08); 51 | align-items: flex-start; 52 | 53 | :global { 54 | .ant-alert-icon.ant-alert-icon { 55 | color: var(--sq-info); 56 | margin-top: 4px; 57 | } 58 | } 59 | } 60 | 61 | .planSelector:global(.ant-select-single.ant-select-lg) { 62 | height: 60px; 63 | } 64 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Create/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Create'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Default/Default.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | import { EmptyList } from '@components/EmptyList'; 7 | import { useWeb3 } from '@containers'; 8 | import { Spinner, Typography } from '@subql/components'; 9 | import { useGetPlansQuery } from '@subql/react-hooks'; 10 | import { mapAsync, notEmpty, renderAsyncArray } from '@utils'; 11 | 12 | import List from '../List'; 13 | 14 | export const Default: React.FC = () => { 15 | const { account } = useWeb3(); 16 | const { t } = useTranslation(); 17 | const plans = useGetPlansQuery({ variables: { address: account ?? '' }, pollInterval: 10000 }); 18 | 19 | return ( 20 |
21 | {renderAsyncArray( 22 | mapAsync((d) => d.plans?.nodes.filter(notEmpty), plans), 23 | { 24 | loading: () => , 25 | error: (e) => {`Error loading plans: ${e}`}, 26 | empty: () => ( 27 | 32 | ), 33 | data: (data) => ( 34 | parseInt(a.id || '0x00', 16) - parseInt(b.id || '0x00', 16))} 36 | onRefresh={() => plans.refetch()} 37 | title={t('plans.default.title')} 38 | /> 39 | ), 40 | }, 41 | )} 42 |
43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Default/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Default'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/List/List.module.less: -------------------------------------------------------------------------------- 1 | .btns { 2 | display: flex; 3 | justify-content: flex-end; 4 | margin-top: 2rem; 5 | } 6 | 7 | .btn { 8 | margin: 0 1rem; 9 | } -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/List/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './List'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Plans.module.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | margin-top: 2vh; 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | } 7 | 8 | .create { 9 | height: 40px; 10 | flex-shrink: 0; 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Specific/Specific.module.css: -------------------------------------------------------------------------------- 1 | .plans { 2 | margin: 16px 0; 3 | } 4 | 5 | .plan { 6 | border: 1px solid var(--gray300); 7 | border-radius: 6px; 8 | padding: 16px; 9 | margin-bottom: 26px; 10 | } 11 | 12 | .header { 13 | margin-bottom: 16px; 14 | } 15 | -------------------------------------------------------------------------------- /src/pages/indexer/MyPlans/Specific/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Specific'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyProjects/AutoReduceOverAllocation/index.module.less: -------------------------------------------------------------------------------- 1 | .autoReduceAllocation { 2 | flex-direction: column; 3 | gap: 4px; 4 | padding: 9px 16px; 5 | margin-bottom: 24px; 6 | 7 | &.enabled { 8 | border-radius: 8px; 9 | border: 1px solid rgba(58, 160, 255, 0.50); 10 | background: rgba(58, 160, 255, 0.08); 11 | } 12 | 13 | &.disabled { 14 | border-radius: 8px; 15 | border: 1px solid rgba(241, 88, 91, 0.50); 16 | background: rgba(241, 88, 91, 0.08); 17 | } 18 | } -------------------------------------------------------------------------------- /src/pages/indexer/MyProjects/MyProjects.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100%; 3 | } 4 | 5 | .content { 6 | background: white; 7 | padding: 2rem; 8 | overflow: auto; 9 | min-height: 80%; 10 | } 11 | 12 | .offers { 13 | margin: 1rem 0; 14 | } 15 | 16 | .noOffersContainer { 17 | margin: 15vh auto; 18 | width: 35vw; 19 | display: flex; 20 | flex-direction: column; 21 | text-align: center; 22 | } 23 | 24 | .description { 25 | margin-top: 3vh; 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/indexer/MyProjects/OwnDeployments/OwnDeployments.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 1rem 0; 3 | } 4 | 5 | .desc { 6 | margin: 1rem 0 1rem -8px; 7 | } 8 | 9 | .progress { 10 | width: 100%; 11 | } 12 | 13 | .info { 14 | display: flex; 15 | gap: 24px; 16 | } 17 | 18 | .totalStake { 19 | box-shadow: none; 20 | margin-bottom: 24px; 21 | flex: 1 1 0%; 22 | } 23 | 24 | .stakeInfo { 25 | display: flex; 26 | gap: 12px; 27 | flex-direction: column; 28 | } 29 | 30 | @media (max-width: 768px) { 31 | .info { 32 | display: flex; 33 | gap: 1px; 34 | flex-wrap: wrap; 35 | width: 100%; 36 | flex-direction: column; 37 | } 38 | 39 | .totalStake { 40 | width: 100%; 41 | box-shadow: none; 42 | margin-bottom: 24px; 43 | flex: 1 1 0%; 44 | } 45 | 46 | .stakeInfo { 47 | display: flex; 48 | gap: 1px; 49 | flex-wrap: wrap; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/indexer/MyProjects/OwnDeployments/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './OwnDeployments'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyStaking/DoStake/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { DoStake } from './DoStake'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyStaking/Indexing/Indexing.module.css: -------------------------------------------------------------------------------- 1 | .nextItem { 2 | display: flex; 3 | } 4 | 5 | .nextIcon { 6 | height: 100%; 7 | margin-right: 6px; 8 | color: var(--gray500); 9 | } 10 | 11 | .btns { 12 | display: flex; 13 | margin: 1rem 0; 14 | } 15 | 16 | .textGroup { 17 | margin: 0.25rem 0; 18 | display: flex; 19 | flex-direction: column; 20 | } 21 | 22 | .grayText { 23 | color: var(--gray500) !important; 24 | } 25 | 26 | .learnMoreContainer { 27 | display: flex; 28 | } 29 | 30 | .learnMoreText { 31 | margin-right: 0.5rem; 32 | } 33 | 34 | .learnMoreBtn { 35 | font-size: 14px; 36 | line-height: 22px; 37 | } 38 | 39 | .notIndexerContainer { 40 | width: 100%; 41 | height: 70%; 42 | display: flex; 43 | justify-content: center; 44 | align-items: center; 45 | } 46 | 47 | .notIndexer { 48 | width: 45%; 49 | display: flex; 50 | flex-direction: column; 51 | justify-content: center; 52 | row-gap: 1.25rem; 53 | } 54 | 55 | .doStakeContainer { 56 | display: flex; 57 | justify-content: center; 58 | margin: 1rem 0; 59 | } 60 | 61 | .doStake { 62 | width: 55%; 63 | text-align: center; 64 | display: flex; 65 | flex-direction: column; 66 | } 67 | 68 | .doStakeTitle { 69 | font-weight: 500; 70 | font-size: 24px; 71 | line-height: 36px; 72 | } 73 | 74 | .doStakeText { 75 | font-size: 16px; 76 | line-height: 24px; 77 | } 78 | -------------------------------------------------------------------------------- /src/pages/indexer/MyStaking/Indexing/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './Indexing'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/MyStaking/MyStaking.module.css: -------------------------------------------------------------------------------- 1 | .grayText { 2 | color: var(--gray500); 3 | } 4 | 5 | .container { 6 | display: flex; 7 | background-color: var(--sq-gray200); 8 | width: 100%; 9 | min-height: 100%; 10 | } 11 | 12 | .sidebar { 13 | width: 25%; 14 | } 15 | 16 | .content { 17 | width: 100%; 18 | padding: 1rem 2rem; 19 | overflow: auto; 20 | } 21 | 22 | .profile { 23 | width: 100%; 24 | height: 100%; 25 | display: flex; 26 | flex-direction: column; 27 | padding: 1rem 0; 28 | } 29 | 30 | .stakingHeader { 31 | margin: 1.25rem 0; 32 | 33 | display: flex; 34 | justify-content: space-between; 35 | align-items: flex-end; 36 | } 37 | 38 | .stakingActions { 39 | display: flex; 40 | } 41 | 42 | .stakingAmount { 43 | width: 280px; 44 | } 45 | 46 | .tabList { 47 | display: flex; 48 | margin-top: 2rem; 49 | text-align: center; 50 | } 51 | 52 | .line { 53 | background: var(--sq-gradient); 54 | height: 2px; 55 | } 56 | -------------------------------------------------------------------------------- /src/pages/indexer/MyStaking/SetCommissionRate/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './SetCommissionRate'; 5 | -------------------------------------------------------------------------------- /src/pages/indexer/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Outlet } from 'react-router'; 6 | import { AppSidebar } from '@components/AppSidebar'; 7 | import { IndexerSidebar } from '@utils/links'; 8 | 9 | const Indexer: React.FC = () => { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | }; 16 | 17 | export default Indexer; 18 | -------------------------------------------------------------------------------- /src/pages/projects/Create/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Create'; 5 | -------------------------------------------------------------------------------- /src/pages/projects/Home/Home.module.less: -------------------------------------------------------------------------------- 1 | .typeCard { 2 | transition: all 0.3s; 3 | cursor: pointer; 4 | &:hover { 5 | border-color: var(--sq-blue600); 6 | } 7 | } 8 | .active { 9 | border-color: var(--sq-blue600); 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/projects/Home/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Home'; 5 | -------------------------------------------------------------------------------- /src/pages/projects/Project/Project.module.less: -------------------------------------------------------------------------------- 1 | .deployments { 2 | padding-top: 32px; 3 | display: flex; 4 | align-items: flex-start; 5 | flex-direction: column; 6 | } 7 | 8 | .deployButton { 9 | margin-top: 32px; 10 | } 11 | 12 | .content { 13 | padding: 0 80px 80px 80px; 14 | display: flex; 15 | flex-direction: column; 16 | gap: 24px; 17 | } 18 | 19 | .tabContainer { 20 | display: flex; 21 | column-gap: 1rem; 22 | margin: 1rem 0; 23 | } 24 | 25 | .tab { 26 | font-size: 18px; 27 | font-weight: 500; 28 | cursor: pointer; 29 | } 30 | 31 | 32 | .markdownWrapper { 33 | :global { 34 | .subql-markdown-preview { 35 | height: 440px; 36 | overflow: auto; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/pages/projects/Project/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default } from './Project'; 5 | -------------------------------------------------------------------------------- /src/pages/projects/index.module.css: -------------------------------------------------------------------------------- 1 | .networkContainer { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | padding: 50px 0; 7 | } 8 | 9 | .networkTitle { 10 | font-family: Futura; 11 | font-weight: 500; 12 | font-size: 24px; 13 | line-height: 36px; 14 | color: var(--gray900); 15 | padding-bottom: 8px; 16 | } 17 | 18 | .networkSubtitle { 19 | font-size: 16px; 20 | line-height: 24px; 21 | 22 | /* identical to box height, or 150% */ 23 | letter-spacing: 0.3px; 24 | padding-bottom: 8px; 25 | } 26 | -------------------------------------------------------------------------------- /src/pages/projects/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import * as React from 'react'; 5 | import { Route, Routes } from 'react-router'; 6 | import { WalletRoute } from '@components/WalletRoute'; 7 | import { Footer } from '@subql/components'; 8 | 9 | import Create from './Create'; 10 | import Home from './Home'; 11 | import Project from './Project'; 12 | 13 | const Studio: React.FC = () => { 14 | return ( 15 | 18 | 19 | } /> 20 | } /> 21 | } /> 22 | 23 | 24 |
25 | 26 | } 27 | >
28 | ); 29 | }; 30 | 31 | export default Studio; 32 | -------------------------------------------------------------------------------- /src/react-i18next.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { resources } from './i18n'; 5 | 6 | declare module 'react-i18next' { 7 | interface CustomTypeOptions { 8 | resources: (typeof resources)['en']; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/stores/chatbox.ts: -------------------------------------------------------------------------------- 1 | import { ChatBoxRef } from '@subql/components'; 2 | import { create } from 'zustand'; 3 | 4 | export type chatBoxStore = { 5 | chatBoxRef?: ChatBoxRef; 6 | setChatBoxRef: (ref: ChatBoxRef) => void; 7 | }; 8 | 9 | export const useChatBoxStore = create((set) => ({ 10 | chatBoxRef: undefined, 11 | setChatBoxRef(ref) { 12 | this.chatBoxRef = ref; 13 | }, 14 | })); 15 | -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './web3Account'; 5 | -------------------------------------------------------------------------------- /src/stores/web3Account.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { RootContractSDK } from '@subql/contract-sdk/rootSdk'; 5 | import { ContractSDK } from '@subql/contract-sdk/sdk'; 6 | import { ContractClient } from '@subql/network-clients'; 7 | import { ethers } from 'ethers'; 8 | import { create } from 'zustand'; 9 | 10 | /** 11 | * 12 | * Web3Account 13 | * 14 | */ 15 | 16 | interface Web3Store { 17 | error?: any; 18 | setError: (error: any) => void; 19 | 20 | isInitialAccount?: boolean; 21 | setIsInitialAccount: (isInitialAccount: boolean) => void; 22 | 23 | contracts?: ContractSDK; 24 | setContracts: (contracts: ContractSDK) => void; 25 | 26 | rootContracts?: RootContractSDK; 27 | setRootContracts: (rootContracts: RootContractSDK) => void; 28 | 29 | contractClient?: ContractClient; 30 | setContractClient: (contracts: ContractClient) => void; 31 | } 32 | 33 | export const useWeb3Store = create()((set) => ({ 34 | contracts: undefined, 35 | isInitialAccount: false, 36 | rootContracts: undefined, 37 | setRootContracts: (rootContracts: RootContractSDK | undefined) => set((state) => ({ ...state, rootContracts })), 38 | 39 | setIsInitialAccount: (isInitialAccount: boolean) => set((state) => ({ ...state, isInitialAccount })), 40 | 41 | setError: (error: Error) => set((state) => ({ ...state, error })), 42 | 43 | setContracts: (contracts: ContractSDK | undefined) => set((state) => ({ ...state, contracts })), 44 | 45 | setContractClient: (contractClient: ContractClient | undefined) => set((state) => ({ ...state, contractClient })), 46 | })); 47 | -------------------------------------------------------------------------------- /src/utils/USDC/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { SUPPORTED_NETWORK } from '@containers/Web3'; 5 | import { STABLE_COIN_ADDRESSES } from '@subql/network-config'; 6 | 7 | export const STABLE_TOKEN_ADDRESS = STABLE_COIN_ADDRESSES[SUPPORTED_NETWORK]; 8 | -------------------------------------------------------------------------------- /src/utils/colors.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /** 5 | * NOTE: Any color variable should be align with the index.css and design 6 | * NOTE: Please avoid hardcode any color value in the Page files. 7 | */ 8 | export const COLORS = { 9 | primary: '#1677ff', 10 | success: '#65cd45', 11 | error: '#f1585b', 12 | 13 | /*New colors*/ 14 | gray200: '#f4f6f8', 15 | gray300: '#dfe3e8', 16 | gray400: '#c4cdd5', 17 | gray500: '#919eab', 18 | gray700: '#454f58', 19 | gray900: '#1a202c', 20 | }; 21 | -------------------------------------------------------------------------------- /src/utils/eip721SignTokenReq.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { FetcherReturnType } from '@graphiql/toolkit'; 5 | 6 | export const defaultQuery = ` 7 | { 8 | _metadata { 9 | indexerHealthy 10 | indexerNodeVersion 11 | } 12 | } 13 | `; 14 | 15 | export async function fetcher(queryUrl: string, graphqlBody: string, sessionToken: string): Promise { 16 | const headers = { 17 | 'Content-Type': 'application/json', 18 | }; 19 | const sortedHeaders = sessionToken ? { ...headers, Authorization: `Bearer ${sessionToken}` } : headers; 20 | const data = await fetch(queryUrl, { 21 | method: 'POST', 22 | headers: sortedHeaders, 23 | body: graphqlBody, 24 | }); 25 | return data.json().catch(() => data.text()); 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/eventBus.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import EventEmitter from 'eventemitter3'; 5 | 6 | export enum EVENT_TYPE { 7 | CREATED_CONSUMER_OFFER = 'CREATED_CONSUMER_OFFER', 8 | } 9 | 10 | const EventBus = new EventEmitter(); 11 | 12 | export { EventBus }; 13 | -------------------------------------------------------------------------------- /src/utils/fetch.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { parseError } from './parseError'; 5 | 6 | interface PostProps { 7 | endpoint: string; 8 | headers?: any; 9 | requestBody: any; 10 | } 11 | 12 | interface PostReturn { 13 | response?: Response; 14 | error?: Error | any; 15 | } 16 | 17 | export const POST = async ({ endpoint, headers, requestBody }: PostProps): Promise => { 18 | let response, error; 19 | try { 20 | response = await fetch(endpoint, { 21 | method: 'POST', 22 | headers: { 'Content-Type': 'application/json', ...headers }, 23 | body: JSON.stringify(requestBody), 24 | }); 25 | } catch (e) { 26 | parseError(e); 27 | error = e; 28 | } 29 | 30 | return { response, error }; 31 | }; 32 | 33 | export const getAuthReqHeader = (authToken: string): { Authorization: string } => ({ 34 | Authorization: `Bearer ${authToken}`, 35 | }); 36 | -------------------------------------------------------------------------------- /src/utils/getFlexPlanPrice.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { BigNumberish } from 'ethers'; 5 | 6 | import { convertStringToNumber, formatEther } from './numberFormatters'; 7 | 8 | export function getFlexPlanPrice(price: BigNumberish): string { 9 | const amount = 1000; 10 | const sortedPrice = `${convertStringToNumber(formatEther(price, 4)) * amount}`; 11 | const sortedRequest = `${amount} requests`; 12 | return `${sortedPrice} / ${sortedRequest}`; 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/getIndexerStatus.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { DeploymentStatus } from '@hooks'; 5 | import { ServiceStatus } from '@subql/network-query'; 6 | 7 | export function getDeploymentStatus(status: ServiceStatus | DeploymentStatus, isOfflineOnContract: boolean): string { 8 | if (status === DeploymentStatus.Unhealthy) { 9 | return DeploymentStatus.Unhealthy; 10 | } 11 | return isOfflineOnContract ? 'OFFLINE' : status; 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/getOrderedAccounts.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const getOrderedAccounts = ( 5 | accounts: T, 6 | key: K, 7 | targetAccount: string | null | undefined, 8 | ): T => { 9 | if (!targetAccount) return accounts; 10 | return accounts.sort((accountA, accountB) => 11 | accountA[key] === targetAccount ? -1 : accountB[key] === targetAccount ? 1 : 0, 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/utils/getTrimmedStr.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const getTrimmedStr = (string: string | undefined, maxLength = 9): string | undefined => { 5 | if (!string) return string; 6 | if (maxLength < 1) return string; 7 | if (string.length <= maxLength) return string; 8 | if (maxLength === 1) return `${string.substring(0, 1)} ...`; 9 | 10 | const midpoint = Math.ceil(string.length / 2); 11 | const toRemove = string.length - maxLength; 12 | const lastRip = Math.ceil(toRemove / 2); 13 | const rstrip = toRemove - lastRip; 14 | return `${string.substring(0, midpoint - lastRip)} ... ${string.substring(midpoint + rstrip)}`; 15 | }; 16 | -------------------------------------------------------------------------------- /src/utils/idleCallback.ts: -------------------------------------------------------------------------------- 1 | export const idleTimeout = (func: () => void) => setTimeout(func, 200); 2 | export const idleCallback = window.requestIdleCallback || idleTimeout; 3 | 4 | export const idleQueue = async (queue: (() => void)[]) => { 5 | const [first, ...rest] = queue; 6 | if (!first) return; 7 | await first(); 8 | idleCallback(() => idleQueue(rest)); 9 | }; 10 | -------------------------------------------------------------------------------- /src/utils/localStorage.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import CryptoJS from 'crypto-js'; 5 | 6 | export const encrypt = (value: string | null, key = 'subquery testnet'): string => 7 | value ? CryptoJS.DES.encrypt(value, key).toString() : ''; 8 | export const decrypt = (value: string | null, key = 'subquery testnet'): string => 9 | value ? CryptoJS.DES.decrypt(value, key).toString(CryptoJS.enc.Utf8) : ''; 10 | 11 | export const setStorage = (key: string, value: string): void => { 12 | localStorage.setItem(key, value); 13 | }; 14 | 15 | export const getStorage = (key: string): string | null => { 16 | return localStorage.getItem(key); 17 | }; 18 | 19 | export const removeStorage = (key: string): void => { 20 | return localStorage.removeItem(key); 21 | }; 22 | 23 | export const setEncryptStorage = (key: string, value: string): void => { 24 | const encryptValue = encrypt(value); 25 | localStorage.setItem(key, encryptValue); 26 | }; 27 | 28 | export const getEncryptStorage = (key: string): string => { 29 | return decrypt(localStorage.getItem(key)); 30 | }; 31 | -------------------------------------------------------------------------------- /src/utils/retry.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const retry = ( 5 | func: () => void, 6 | options?: { 7 | sleepTime?: number; 8 | retryTime?: number; 9 | }, 10 | ) => { 11 | const { sleepTime = 3000, retryTime = 5 } = options || {}; 12 | let times = 0; 13 | 14 | const clear = setInterval(() => { 15 | func(); 16 | times += 1; 17 | if (times === retryTime) { 18 | clearInterval(clear); 19 | } 20 | }, sleepTime); 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/stringFormatters.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const getCapitalizedStr = (str: string): string => { 5 | return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); 6 | }; 7 | -------------------------------------------------------------------------------- /src/utils/useDebounce.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useEffect, useState } from 'react'; 5 | 6 | export function useDebounce(value: T, delay = 5000): T { 7 | const [debouncedValue, setDebouncedValue] = useState(value); 8 | useEffect(() => { 9 | // Update debounced value after delay 10 | const handler = setTimeout(() => { 11 | setDebouncedValue(value); 12 | }, delay); 13 | 14 | return () => { 15 | clearTimeout(handler); 16 | }; 17 | }, [value, delay]); 18 | return debouncedValue; 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/waitForSomething.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { parseError } from '@utils'; 5 | 6 | export const sleep = (time = 2000) => new Promise((resolve) => setTimeout(resolve, time)); 7 | 8 | interface waitForSomethingArg { 9 | func: () => boolean | PromiseLike; 10 | timeout?: number; 11 | splitTime?: number; 12 | } 13 | 14 | export const waitForSomething = async ( 15 | { func, timeout, splitTime = 50 }: waitForSomethingArg, 16 | sleepTime = 0, 17 | ): Promise => { 18 | if (timeout && sleepTime >= timeout) { 19 | return false; 20 | } 21 | try { 22 | const r = await func(); 23 | 24 | if (r) { 25 | return r; 26 | } 27 | } catch (e) { 28 | parseError(e); 29 | } 30 | 31 | await sleep(splitTime); 32 | 33 | return waitForSomething({ func, timeout, splitTime }, splitTime + sleepTime); 34 | }; 35 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import 'vite/client'; 5 | 6 | declare global { 7 | interface Window { 8 | ethereum?: any; 9 | Buffer?: any; 10 | debugInfo?: any; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "strictNullChecks": true, 15 | "strictBindCallApply": true, 16 | "strictPropertyInitialization": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "module": "esnext", 20 | "moduleResolution": "node", 21 | "resolveJsonModule": true, 22 | "isolatedModules": true, 23 | "noEmit": true, 24 | "noImplicitAny": true, 25 | "noImplicitThis": true, 26 | "jsx": "react-jsx", 27 | "plugins": [{ "name": "typescript-plugin-css-modules" }], 28 | "baseUrl": ".", 29 | "paths": { 30 | "@__generated__": ["src/__generated__"], 31 | "@__generated__/*": ["src/__generated__/*"], 32 | "@components": ["./src/components"], 33 | "@components/*": ["./src/components/*"], 34 | "@containers": ["src/containers"], 35 | "@containers/*": ["src/containers/*"], 36 | "@hooks": ["src/hooks"], 37 | "@hooks/*": ["src/hooks/*"], 38 | "@i18n": ["src/i18n"], 39 | "@i18n/*": ["src/i18n/*"], 40 | "@pages": ["src/pages"], 41 | "@pages/*": ["src/pages/*"], 42 | "@utils": ["src/utils"], 43 | "@utils/*": ["src/utils/*"], 44 | } 45 | }, 46 | "include": [ 47 | "src", 48 | "types", 49 | ], 50 | "exclude": [ 51 | "node_modules", 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /types/i18next.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { GetDictValue, TranslationKeys } from '@i18n'; 5 | 6 | import 'i18next'; 7 | 8 | // reference: https://www.i18next.com/overview/typescript#argument-of-type-defaulttfuncreturn-is-not-assignable-to-parameter-of-type-xyz 9 | declare module 'i18next' { 10 | interface CustomTypeOptions { 11 | returnNull: false; 12 | // WARNING 13 | // i18next can config this for tips, but it has bug now. 14 | // https://github.com/i18next/i18next/issues/1956 Don't close for now. 15 | // And the declare in typescript is a deep merge. https://www.typescriptlang.org/docs/handbook/declaration-merging.html 16 | // So the TFunction we override will be merge with the default definition. 17 | // The default is `string`, so add a wrong type definition to override the `string`. 18 | // TODO: If the above issue solved will use this instead of override. 19 | resources: { 'z-dont-use-it': '' }; 20 | } 21 | 22 | interface TFunction { 23 | (key: T, options?: TOptions): GetDictValue; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /types/react-jazzicon.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // https://github.com/marcusmolchany/react-jazzicon 5 | 6 | declare module 'react-jazzicon' { 7 | import * as React from 'react'; 8 | 9 | type JazziconProps = { 10 | diameter?: number; 11 | paperStyles?: React.CSSProperties; 12 | seed?: number; 13 | svgStyles?: React.CSSProperties; 14 | }; 15 | 16 | const Jazzicon: React.FunctionComponent; 17 | 18 | export function jsNumberForAddress(address: string): number; 19 | 20 | export default Jazzicon; 21 | } 22 | --------------------------------------------------------------------------------