diff --git a/packages/page-parachains/src/Crowdloan/Funds.tsx b/packages/page-parachains/src/Crowdloan/Funds.tsx index 032602d001..f3c7765150 100644 --- a/packages/page-parachains/src/Crowdloan/Funds.tsx +++ b/packages/page-parachains/src/Crowdloan/Funds.tsx @@ -27,8 +27,14 @@ function extractLists (value: Campaign[] | null, leasePeriod?: LeasePeriod): [Ca let allIds: ParaId[] | null = null; if (value && currentPeriod) { - active = value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => !(isCapped || isEnded || isWinner) && currentPeriod.lte(firstSlot)); - ended = value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => (isCapped || isEnded || isWinner) || currentPeriod.gt(firstSlot)); + active = value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => + !(isCapped || isEnded || isWinner) && + currentPeriod.lte(firstSlot) + ); + ended = value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => + (isCapped || isEnded || isWinner) || + currentPeriod.gt(firstSlot) + ); allIds = value.map(({ paraId }) => paraId); } diff --git a/packages/page-parachains/src/Crowdloan/useContributions.ts b/packages/page-parachains/src/Crowdloan/useContributions.ts index 1cd8a63619..9770fd5ae5 100644 --- a/packages/page-parachains/src/Crowdloan/useContributions.ts +++ b/packages/page-parachains/src/Crowdloan/useContributions.ts @@ -6,7 +6,7 @@ import type { Balance, ParaId } from '@polkadot/types/interfaces'; import { useEffect, useState } from 'react'; -import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useCall, useNamedHook } from '@polkadot/react-hooks'; import { encodeAddress } from '@polkadot/util-crypto'; interface Result extends DeriveContributions { @@ -25,7 +25,7 @@ const NO_CONTRIB: Result = { myContributions: {} }; -export default function useContributions (paraId: ParaId): Result { +function useContributionsImpl (paraId: ParaId): Result { const { api } = useApi(); const { allAccountsHex } = useAccounts(); const [state, setState] = useState(() => NO_CONTRIB); @@ -57,3 +57,7 @@ export default function useContributions (paraId: ParaId): Result { return state; } + +export default function useContributions (paraId: ParaId): Result { + return useNamedHook('useContributions', useContributionsImpl, paraId); +} diff --git a/packages/page-parachains/src/Proposals/useProposal.ts b/packages/page-parachains/src/Proposals/useProposal.ts index c6a7eb7a91..4d26b18aba 100644 --- a/packages/page-parachains/src/Proposals/useProposal.ts +++ b/packages/page-parachains/src/Proposals/useProposal.ts @@ -7,9 +7,9 @@ import type { ProposalExt, ScheduledProposals } from '../types'; import { useMemo } from 'react'; -import { useApi, useCall } from '@polkadot/react-hooks'; +import { useApi, useCall, useNamedHook } from '@polkadot/react-hooks'; -export default function useProposal (id: ParaId, approvedIds: ParaId[], scheduled: ScheduledProposals[]): ProposalExt { +function useProposalImpl (id: ParaId, approvedIds: ParaId[], scheduled: ScheduledProposals[]): ProposalExt { const { api } = useApi(); const opt = useCall>(api.query.proposeParachain.proposals, [id]); @@ -25,3 +25,7 @@ export default function useProposal (id: ParaId, approvedIds: ParaId[], schedule [approvedIds, id, opt, scheduled] ); } + +export default function useProposal (id: ParaId, approvedIds: ParaId[], scheduled: ScheduledProposals[]): ProposalExt { + return useNamedHook('useProposal', useProposalImpl, id, approvedIds, scheduled); +} diff --git a/packages/page-parachains/src/useActionsQueue.ts b/packages/page-parachains/src/useActionsQueue.ts index 84a191cd39..9f00fa813c 100644 --- a/packages/page-parachains/src/useActionsQueue.ts +++ b/packages/page-parachains/src/useActionsQueue.ts @@ -7,7 +7,7 @@ import type { QueuedAction } from './types'; import { useMemo } from 'react'; -import { useApi, useCall } from '@polkadot/react-hooks'; +import { useApi, useCall, useNamedHook } from '@polkadot/react-hooks'; import { BN_EIGHT, BN_FIVE, BN_FOUR, BN_NINE, BN_ONE, BN_SEVEN, BN_SIX, BN_TEN, BN_THREE, BN_TWO } from '@polkadot/util'; const INC = [BN_ONE, BN_TWO, BN_THREE, BN_FOUR, BN_FIVE, BN_SIX, BN_SEVEN, BN_EIGHT, BN_NINE, BN_TEN]; @@ -16,7 +16,7 @@ const callOpts = { withParams: true }; -export default function useActionsQueue (): QueuedAction[] { +function useActionsQueueImpl (): QueuedAction[] { const { api } = useApi(); const currentIndex = useCall(api.query.session.currentIndex); const queryIndexes = useMemo(() => currentIndex && INC.map((i) => currentIndex.add(i)), [currentIndex]); @@ -35,3 +35,7 @@ export default function useActionsQueue (): QueuedAction[] { [nextActions] ); } + +export default function useActionsQueue (): QueuedAction[] { + return useNamedHook('useActionsQueue', useActionsQueueImpl); +} diff --git a/packages/page-parachains/src/useAuctionInfo.tsx b/packages/page-parachains/src/useAuctionInfo.tsx index 29b99c9480..8efc66bbbf 100644 --- a/packages/page-parachains/src/useAuctionInfo.tsx +++ b/packages/page-parachains/src/useAuctionInfo.tsx @@ -6,7 +6,7 @@ import type { AuctionIndex, BlockNumber, LeasePeriodOf } from '@polkadot/types/i import type { ITuple } from '@polkadot/types/types'; import type { AuctionInfo } from './types'; -import { useApi, useCallMulti } from '@polkadot/react-hooks'; +import { useApi, useCallMulti, useNamedHook } from '@polkadot/react-hooks'; const optionsMulti = { transform: ([numAuctions, optInfo]: [AuctionIndex, Option>]): AuctionInfo => { @@ -20,7 +20,7 @@ const optionsMulti = { } }; -export default function useAuctionInfo (): AuctionInfo | undefined { +function useAuctionInfoImpl (): AuctionInfo | undefined { const { api } = useApi(); return useCallMulti([ @@ -28,3 +28,7 @@ export default function useAuctionInfo (): AuctionInfo | undefined { api.query.auctions?.auctionInfo ], optionsMulti); } + +export default function useAuctionInfo (): AuctionInfo | undefined { + return useNamedHook('useAuctionInfo', useAuctionInfoImpl); +} diff --git a/packages/page-parachains/src/useFunds.ts b/packages/page-parachains/src/useFunds.ts index 55e296107a..173ba78f7c 100644 --- a/packages/page-parachains/src/useFunds.ts +++ b/packages/page-parachains/src/useFunds.ts @@ -10,7 +10,7 @@ import type { Campaign, Campaigns } from './types'; import BN from 'bn.js'; import { useEffect, useState } from 'react'; -import { useApi, useBestNumber, useCall, useEventTrigger, useIsMountedRef, useMapKeys } from '@polkadot/react-hooks'; +import { useApi, useBestNumber, useCall, useEventTrigger, useIsMountedRef, useMapKeys, useNamedHook } from '@polkadot/react-hooks'; import { BN_ZERO, u8aConcat } from '@polkadot/util'; import { encodeAddress } from '@polkadot/util-crypto'; @@ -152,7 +152,7 @@ function extractFundIds (keys: StorageKey<[ParaId]>[]): ParaId[] { return keys.map(({ args: [paraId] }) => paraId); } -export default function useFunds (): Campaigns { +function useFundsImpl (): Campaigns { const { api } = useApi(); const bestNumber = useBestNumber(); const mountedRef = useIsMountedRef(); @@ -171,3 +171,7 @@ export default function useFunds (): Campaigns { return result; } + +export default function useFunds (): Campaigns { + return useNamedHook('useFunds', useFundsImpl); +} diff --git a/packages/page-parachains/src/useLeasePeriod.ts b/packages/page-parachains/src/useLeasePeriod.ts index 90cbde3971..d48bc8a620 100644 --- a/packages/page-parachains/src/useLeasePeriod.ts +++ b/packages/page-parachains/src/useLeasePeriod.ts @@ -6,10 +6,10 @@ import type { LeasePeriod } from './types'; import { useMemo } from 'react'; -import { useApi, useBestNumber } from '@polkadot/react-hooks'; +import { useApi, useBestNumber, useNamedHook } from '@polkadot/react-hooks'; import { BN_ZERO } from '@polkadot/util'; -export default function useLeasePeriod (): LeasePeriod | undefined { +function useLeasePeriodImpl (): LeasePeriod | undefined { const { api } = useApi(); const bestNumber = useBestNumber(); @@ -30,3 +30,7 @@ export default function useLeasePeriod (): LeasePeriod | undefined { }; }, [api, bestNumber]); } + +export default function useLeasePeriod (): LeasePeriod | undefined { + return useNamedHook('useLeasePeriod', useLeasePeriodImpl); +} diff --git a/packages/page-parachains/src/useLeaseRanges.ts b/packages/page-parachains/src/useLeaseRanges.ts index fad42b4a7d..1af6224ec6 100644 --- a/packages/page-parachains/src/useLeaseRanges.ts +++ b/packages/page-parachains/src/useLeaseRanges.ts @@ -6,7 +6,7 @@ import type { u32 } from '@polkadot/types'; import BN from 'bn.js'; import { useMemo } from 'react'; -import { useApi } from '@polkadot/react-hooks'; +import { useApi, useNamedHook } from '@polkadot/react-hooks'; const RANGES_DEFAULT: [number, number][] = [ [0, 0], [0, 1], [0, 2], [0, 3], @@ -19,7 +19,7 @@ function isU32 (leasePeriodsPerSlot: unknown): leasePeriodsPerSlot is u32 { return !!leasePeriodsPerSlot; } -export function useLeaseRanges (): [number, number][] { +function useLeaseRangesImpl (): [number, number][] { const { api } = useApi(); return useMemo( @@ -42,7 +42,11 @@ export function useLeaseRanges (): [number, number][] { ); } -export function useLeaseRangeMax (): BN { +export function useLeaseRanges (): [number, number][] { + return useNamedHook('useLeaseRanges', useLeaseRangesImpl); +} + +function useLeaseRangeMaxImpl (): BN { const ranges = useLeaseRanges(); return useMemo( @@ -50,3 +54,7 @@ export function useLeaseRangeMax (): BN { [ranges] ); } + +export function useLeaseRangeMax (): BN { + return useNamedHook('useLeaseRangeMax', useLeaseRangeMaxImpl); +} diff --git a/packages/page-parachains/src/useOwnedIds.ts b/packages/page-parachains/src/useOwnedIds.ts index 1939969bad..152c221a3b 100644 --- a/packages/page-parachains/src/useOwnedIds.ts +++ b/packages/page-parachains/src/useOwnedIds.ts @@ -8,7 +8,7 @@ import type { OwnedId, OwnedIdPartial } from './types'; import { useMemo } from 'react'; -import { useAccounts, useApi, useCall, useEventTrigger, useMapEntries } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useCall, useEventTrigger, useMapEntries, useNamedHook } from '@polkadot/react-hooks'; interface CodeHash { hash: Hash | null; @@ -52,7 +52,7 @@ const hashesOption = { withParamsTransform: true }; -export default function useOwnedIds (): OwnedId[] { +function useOwnedIdsImpl (): OwnedId[] { const { api } = useApi(); const { allAccounts } = useAccounts(); const trigger = useEventTrigger([api.events.registrar.Registered, api.events.registrar.Reserved]); @@ -71,3 +71,7 @@ export default function useOwnedIds (): OwnedId[] { [allAccounts, hashes, unfiltered] ); } + +export default function useOwnedIds (): OwnedId[] { + return useNamedHook('useOwnedIds', useOwnedIdsImpl); +} diff --git a/packages/page-parachains/src/useProposals.ts b/packages/page-parachains/src/useProposals.ts index 9c17cdc3c7..f3d7df8723 100644 --- a/packages/page-parachains/src/useProposals.ts +++ b/packages/page-parachains/src/useProposals.ts @@ -7,7 +7,7 @@ import type { Proposals } from './types'; import { useMemo } from 'react'; -import { useApi, useCallMulti, useEventTrigger, useIsMountedRef, useMapEntries, useMapKeys } from '@polkadot/react-hooks'; +import { useApi, useCallMulti, useEventTrigger, useIsMountedRef, useMapEntries, useMapKeys, useNamedHook } from '@polkadot/react-hooks'; type MultiQuery = [SessionIndex | undefined, ParaId[] | undefined]; @@ -31,7 +31,7 @@ function extractScheduled (entries: [StorageKey<[SessionIndex]>, ParaId[]][]): S })); } -export default function useProposals (): Proposals | undefined { +function useProposalsImpl (): Proposals | undefined { const { api } = useApi(); const mountedRef = useIsMountedRef(); const trigger = useEventTrigger([api.events.proposeParachain?.ProposeParachain]); @@ -53,3 +53,7 @@ export default function useProposals (): Proposals | undefined { [approvedIds, mountedRef, proposalIds, sessionIndex, scheduled] ); } + +export default function useProposals (): Proposals | undefined { + return useNamedHook('useProposals', useProposalsImpl); +} diff --git a/packages/page-parachains/src/useUpcomingIds.ts b/packages/page-parachains/src/useUpcomingIds.ts index 33e457d637..5bb4011f97 100644 --- a/packages/page-parachains/src/useUpcomingIds.ts +++ b/packages/page-parachains/src/useUpcomingIds.ts @@ -5,7 +5,7 @@ import type { Option, StorageKey } from '@polkadot/types'; import type { ParaId } from '@polkadot/types/interfaces'; import type { PolkadotRuntimeParachainsParasParaLifecycle } from '@polkadot/types/lookup'; -import { useApi, useEventTrigger, useMapEntries } from '@polkadot/react-hooks'; +import { useApi, useEventTrigger, useMapEntries, useNamedHook } from '@polkadot/react-hooks'; function extractIds (entries: [StorageKey<[ParaId]>, Option][]): ParaId[] { return entries @@ -25,7 +25,7 @@ function extractIds (entries: [StorageKey<[ParaId]>, Option a.cmp(b)); } -export default function useUpomingIds (): ParaId[] | undefined { +function useUpomingIdsImpl (): ParaId[] | undefined { const { api } = useApi(); const trigger = useEventTrigger([ api.events.session.NewSession, @@ -37,3 +37,7 @@ export default function useUpomingIds (): ParaId[] | undefined { transform: extractIds }); } + +export default function useUpomingIds (): ParaId[] | undefined { + return useNamedHook('useUpomingIds', useUpomingIdsImpl); +} diff --git a/packages/page-parachains/src/useWinningData.ts b/packages/page-parachains/src/useWinningData.ts index f491d1ad94..263a232f33 100644 --- a/packages/page-parachains/src/useWinningData.ts +++ b/packages/page-parachains/src/useWinningData.ts @@ -8,7 +8,7 @@ import type { AuctionInfo, WinnerData, Winning } from './types'; import BN from 'bn.js'; import { useEffect, useRef, useState } from 'react'; -import { useApi, useBestNumber, useCall, useEventTrigger, useIsMountedRef } from '@polkadot/react-hooks'; +import { useApi, useBestNumber, useCall, useEventTrigger, useIsMountedRef, useNamedHook } from '@polkadot/react-hooks'; import { BN_ONE, BN_ZERO, u8aEq } from '@polkadot/util'; import { CROWD_PREFIX } from './constants'; @@ -121,7 +121,7 @@ function mergeFirst (ranges: [number, number][], auctionInfo: AuctionInfo, prev: return prev; } -export default function useWinningData (auctionInfo?: AuctionInfo): Winning[] | undefined { +function useWinningDataImpl (auctionInfo?: AuctionInfo): Winning[] | undefined { const { api } = useApi(); const mountedRef = useIsMountedRef(); const ranges = useLeaseRanges(); @@ -168,3 +168,7 @@ export default function useWinningData (auctionInfo?: AuctionInfo): Winning[] | return result; } + +export default function useWinningData (auctionInfo?: AuctionInfo): Winning[] | undefined { + return useNamedHook('useWinningData', useWinningDataImpl, auctionInfo); +} diff --git a/packages/react-components/src/ErrorBoundary.tsx b/packages/react-components/src/ErrorBoundary.tsx index 845ceb99c9..4c21d8a74a 100644 --- a/packages/react-components/src/ErrorBoundary.tsx +++ b/packages/react-components/src/ErrorBoundary.tsx @@ -54,9 +54,9 @@ class ErrorBoundary extends React.Component { return displayError ? (
- {t('Uncaught error. Something went wrong with the query and rendering of this component. {{message}}', { - replace: { message: displayError.message } - })} +

{t('Uncaught error. Something went wrong with the query and rendering of this component. Please supply all the details below when logging an issue, it may help in tracing the cause.')}

+

{displayError.message}

+

{displayError.stack}

) : children; diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts index 5db4255f5c..1737f2f066 100644 --- a/packages/react-hooks/src/index.ts +++ b/packages/react-hooks/src/index.ts @@ -34,6 +34,7 @@ export { useLoadingDelay } from './useLoadingDelay'; export { useMapEntries } from './useMapEntries'; export { useMapKeys } from './useMapKeys'; export { useModal } from './useModal'; +export { useNamedHook } from './useNamedHook'; export { useNonEmptyString } from './useNonEmptyString'; export { useNonZeroBn } from './useNonZeroBn'; export { useOwnEraRewards } from './useOwnEraRewards'; diff --git a/packages/react-hooks/src/useCall.ts b/packages/react-hooks/src/useCall.ts index b6f55705d1..3b03a44cdb 100644 --- a/packages/react-hooks/src/useCall.ts +++ b/packages/react-hooks/src/useCall.ts @@ -1,7 +1,7 @@ // Copyright 2017-2021 @polkadot/react-hooks authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { RpcPromiseResult } from '@polkadot/api/types'; +import type { PromiseResult, QueryableStorageEntry } from '@polkadot/api/types'; import type { StorageEntryTypeLatest } from '@polkadot/types/interfaces'; import type { AnyFunction, Codec } from '@polkadot/types/types'; import type { CallOptions, CallParam, CallParams } from './types'; @@ -34,10 +34,16 @@ interface QueryMapFn extends QueryTrackFn { }; } -export type TrackFn = RpcPromiseResult | QueryTrackFn; +type QueryFn = + QueryableStorageEntry<'promise', []> | + QueryableStorageEntry<'promise', []>['entries'] | + QueryableStorageEntry<'promise', []>['keys'] | + QueryableStorageEntry<'promise', []>['multi']; type CallFn = (...params: unknown[]) => Promise; +export type TrackFn = PromiseResult | QueryFn; + export interface Tracker { isActive: boolean; serialized: string | null; @@ -77,6 +83,10 @@ export function unsubscribe (tracker: TrackerRef): void { } } +function isQuery (fn: TrackFn): fn is QueryableStorageEntry<'promise', []> { + return !isUndefined((fn as QueryableStorageEntry<'promise', []>).creator); +} + // subscribe, trying to play nice with the browser threads function subscribe (mountedRef: MountedRef, tracker: TrackerRef, fn: TrackFn | undefined, params: CallParams, setValue: (value: any) => void, { transform = transformIdentity, withParams, withParamsTransform }: CallOptions = {}): void { const validParams = params.filter((p) => !isUndefined(p)); @@ -97,13 +107,23 @@ function subscribe (mountedRef: MountedRef, tracker: TrackerRef, fn: TrackFn tracker.current.subscriber = (fn as CallFn)(...params, (value: Codec): void => { // we use the isActive flag here since .subscriber may not be set on immediate callback) if (mountedRef.current && tracker.current.isActive) { - mountedRef.current && tracker.current.isActive && setValue( - withParams - ? [params, transform(value)] - : withParamsTransform - ? transform([params, value]) - : transform(value) - ); + try { + setValue( + withParams + ? [params, transform(value)] + : withParamsTransform + ? transform([params, value]) + : transform(value) + ); + } catch (error) { + let extra = '...'; + + if (isQuery(fn)) { + extra = `${fn.creator.section}.${fn.creator.method}`; + } + + throw new Error(`useCall(${extra}):: ${(error as Error).message}:: ${(error as Error).stack || ''}`); + } } }); } else { diff --git a/packages/react-hooks/src/useCallMulti.ts b/packages/react-hooks/src/useCallMulti.ts index 15b8faf3e0..1aab46dc04 100644 --- a/packages/react-hooks/src/useCallMulti.ts +++ b/packages/react-hooks/src/useCallMulti.ts @@ -39,11 +39,21 @@ function subscribe (api: ApiPromise, mountedRef: MountedRef, tracker: Tracke if (mountedRef.current && tracker.current.isActive) { let valueIndex = -1; - mountedRef.current && tracker.current.isActive && setValue( - transform( - calls.map((_, index) => included[index] ? value[++valueIndex] : undefined) - ) - ); + if (mountedRef.current && tracker.current.isActive) { + try { + setValue( + transform( + calls.map((_, index) => + included[index] + ? value[++valueIndex] + : undefined + ) + ) + ); + } catch (error) { + throw new Error(`useCallMulti(...):: ${(error as Error).message}:: ${(error as Error).stack || ''}`); + } + } } }); } else { diff --git a/packages/react-hooks/src/useNamedHook.ts b/packages/react-hooks/src/useNamedHook.ts new file mode 100644 index 0000000000..7a5550968f --- /dev/null +++ b/packages/react-hooks/src/useNamedHook.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2021 @polkadot/react-hooks authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export function useNamedHook (name: string, fn: (...args: any[]) => T, ...args: unknown[]): T { + try { + return fn(...args); + } catch (error) { + throw new Error(`${name}:: ${(error as Error).message}:: ${(error as Error).stack || ''}`); + } +}