Skip to content

Commit

Permalink
feat: add refresh activity button (#320)
Browse files Browse the repository at this point in the history
* fix: correct block explorers and indexers used when switching networks

* refactor: update explorer/indexers to use a customizable name and id

* refactor: overhaul of the account updating

fix: accounts not longer send multiple requests when loading and changing network
feat: updating accounts is ignored if account is being already updated while polling

* feat: update whats new modal

* feat: add refresh activity button

* chore: squash
  • Loading branch information
kieranroneill authored Sep 19, 2024
1 parent 6ac65e2 commit c3650ec
Show file tree
Hide file tree
Showing 65 changed files with 749 additions and 775 deletions.
4 changes: 4 additions & 0 deletions src/common/types/TPartialExcept.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type TPartialExcept<Type, Property extends keyof Type> = Partial<Type> &
Pick<Type, Property>;

export default TPartialExcept;
1 change: 1 addition & 0 deletions src/common/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export type { default as ILogger } from './ILogger';
export type { default as ILogLevel } from './ILogLevel';
export type { default as IPRFExtensionOutput } from './IPRFExtensionOutput';
export type { default as IPRFExtensionResults } from './IPRFExtensionResults';
export type { default as TPartialExcept } from './TPartialExcept';
export type { default as TProviderMessages } from './TProviderMessages';
29 changes: 3 additions & 26 deletions src/extension/apps/background/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import React, { FC, useEffect } from 'react';
import React, { type FC, useEffect } from 'react';
import { useDispatch } from 'react-redux';

// features
import { fetchAccountsFromStorageThunk } from '@extension/features/accounts';
import { fetchActiveThunk as fetchCredentialsLockActiveThunk } from '@extension/features/credential-lock';
import { handleNewEventByIdThunk } from '@extension/features/events';
import { closeCurrentWindowThunk } from '@extension/features/layout';
import { fetchFromStorageThunk as fetchPasskeyCredentialFromStorageThunk } from '@extension/features/passkeys';
import { fetchSessionsThunk } from '@extension/features/sessions';
import { fetchFromStorageThunk as fetchSettingsFromStorageThunk } from '@extension/features/settings';
import { fetchStandardAssetsFromStorageThunk } from '@extension/features/standard-assets';
import { fetchFromStorageThunk as fetchSystemInfoFromStorageThunk } from '@extension/features/system';

// hooks
import useOnAppStartup from '@extension/hooks/useOnAppStartup';
import useOnDebugLogging from '@extension/hooks/useOnDebugLogging';

// modals
Expand All @@ -24,9 +18,6 @@ import SignTransactionsModal from '@extension/modals/SignTransactionsModal';
// pages
import SplashPage from '@extension/pages/SplashPage';

// selectors
import { useSelectSettingsSelectedNetwork } from '@extension/selectors';

// types
import type { IAppThunkDispatch, IBackgroundRootState } from '@extension/types';

Expand All @@ -35,35 +26,21 @@ import decodeURLSearchParam from '@extension/utils/decodeURLSearchParam';

const Root: FC = () => {
const dispatch = useDispatch<IAppThunkDispatch<IBackgroundRootState>>();
// selectors
const network = useSelectSettingsSelectedNetwork();
// misc
const url = new URL(window.location.href);
const eventId = decodeURLSearchParam('eventId', url.searchParams);
// handlers
const handleModalClose = () => dispatch(closeCurrentWindowThunk());

useOnAppStartup();
useEffect(() => {
// if we don't have the necessary information, close this window
if (!eventId) {
dispatch(closeCurrentWindowThunk());

return;
}

dispatch(fetchCredentialsLockActiveThunk());
dispatch(fetchPasskeyCredentialFromStorageThunk());
dispatch(fetchSystemInfoFromStorageThunk());
dispatch(fetchSettingsFromStorageThunk());
dispatch(fetchSessionsThunk());
dispatch(fetchStandardAssetsFromStorageThunk());
}, []);
// fetch accounts when the selected network has been found
useEffect(() => {
if (network) {
dispatch(fetchAccountsFromStorageThunk());
}
}, [network]);
useEffect(() => {
if (eventId) {
dispatch(handleNewEventByIdThunk(eventId));
Expand Down
54 changes: 6 additions & 48 deletions src/extension/apps/main/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,23 @@ import MainLayout from '@extension/components/MainLayout';

// features
import { reset as resetAddAsset } from '@extension/features/add-assets';
import {
fetchAccountsFromStorageThunk,
startPollingForAccountsThunk,
} from '@extension/features/accounts';
import { startPollingForAccountsThunk } from '@extension/features/accounts';
import { fetchARC0072AssetsFromStorageThunk } from '@extension/features/arc0072-assets';
import { fetchARC0200AssetsFromStorageThunk } from '@extension/features/arc0200-assets';
import { fetchActiveThunk as fetchCredentialLockActiveThunk } from '@extension/features/credential-lock';
import {
setConfirmModal,
setScanQRCodeModal,
setWhatsNewModal,
} from '@extension/features/layout';
import {
fetchFromStorageThunk as fetchNetworksFromStorageThunk,
startPollingForTransactionsParamsThunk,
updateTransactionParamsForSelectedNetworkThunk,
} from '@extension/features/networks';
import { startPollingForTransactionsParamsThunk } from '@extension/features/networks';
import { setShowingConfetti } from '@extension/features/notifications';
import { fetchFromStorageThunk as fetchPasskeyCredentialFromStorageThunk } from '@extension/features/passkeys';
import { reset as resetReKeyAccount } from '@extension/features/re-key-account';
import { reset as resetRemoveAssets } from '@extension/features/remove-assets';
import { reset as resetSendAsset } from '@extension/features/send-assets';
import { fetchSessionsThunk } from '@extension/features/sessions';
import { fetchFromStorageThunk as fetchSettingsFromStorageThunk } from '@extension/features/settings';
import { fetchStandardAssetsFromStorageThunk } from '@extension/features/standard-assets';
import {
fetchFromStorageThunk as fetchSystemInfoFromStorageThunk,
startPollingForNetworkConnectivityThunk,
} from '@extension/features/system';
import { startPollingForNetworkConnectivityThunk } from '@extension/features/system';

// hooks
import useOnAppStartup from '@extension/hooks/useOnAppStartup';
import useOnDebugLogging from '@extension/hooks/useOnDebugLogging';
import useOnMainAppMessage from '@extension/hooks/useOnMainAppMessage';
import useOnNewAssets from '@extension/hooks/useOnNewAssets';
Expand All @@ -62,9 +48,7 @@ import WhatsNewModal from '@extension/modals/WhatsNewModal';

// selectors
import {
useSelectAccounts,
useSelectNotificationsShowingConfetti,
useSelectSettingsSelectedNetwork,
useSelectSystemWhatsNewInfo,
} from '@extension/selectors';

Expand All @@ -74,8 +58,6 @@ import type { IAppThunkDispatch, IMainRootState } from '@extension/types';
const Root: FC = () => {
const dispatch = useDispatch<IAppThunkDispatch<IMainRootState>>();
// selectors
const accounts = useSelectAccounts();
const network = useSelectSettingsSelectedNetwork();
const showingConfetti = useSelectNotificationsShowingConfetti();
const whatsNewInfo = useSelectSystemWhatsNewInfo();
// handlers
Expand All @@ -88,41 +70,17 @@ const Root: FC = () => {
const handleSendAssetModalClose = () => dispatch(resetSendAsset());
const handleWhatsNewModalClose = () => dispatch(setWhatsNewModal(false));

// 1. fetch the required data
useOnAppStartup();
useEffect(() => {
// general
dispatch(fetchSettingsFromStorageThunk());
dispatch(fetchNetworksFromStorageThunk());
dispatch(fetchSystemInfoFromStorageThunk());
dispatch(fetchCredentialLockActiveThunk());
dispatch(fetchPasskeyCredentialFromStorageThunk());
dispatch(fetchSessionsThunk());
// assets
dispatch(fetchStandardAssetsFromStorageThunk());
dispatch(fetchARC0072AssetsFromStorageThunk());
dispatch(fetchARC0200AssetsFromStorageThunk());
// polling
dispatch(startPollingForAccountsThunk());
dispatch(startPollingForTransactionsParamsThunk());
dispatch(startPollingForNetworkConnectivityThunk());
}, []);
// 2. when the selected network has been updated, fetch the account data and transaction params
useEffect(() => {
if (network) {
// fetch accounts when no accounts exist
if (accounts.length < 1) {
dispatch(
fetchAccountsFromStorageThunk({
updateInformation: true,
updateTransactions: true,
})
);
}

// fetch the most recent transaction params for the selected network
dispatch(updateTransactionParamsForSelectedNetworkThunk());
}
}, [network]);

// if the saved what's new version is null or less than the current version (and the update message is not disabled), display the modal
useEffect(() => {
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ const ARC0300KeyRegistrationTransactionSendModalContent: FC<
// force update the account information as we spent fees and refresh all the new transactions
dispatch(
updateAccountsThunk({
accountIds: [account.id],
accountIDs: [account.id],
forceInformationUpdate: true,
refreshTransactions: true,
})
Expand Down
3 changes: 3 additions & 0 deletions src/extension/components/ActivityTab/ActivityTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const ActivityTab: FC<IProps> = ({
accounts,
fetching,
network,
onRefreshClick,
onScrollEnd,
}) => {
const { t } = useTranslation();
Expand All @@ -39,6 +40,7 @@ const ActivityTab: FC<IProps> = ({
AccountService.extractAccountTransactionsForNetwork(account, network)
?.transactions || null;
// handlers
const handleOnRefreshClick = () => onRefreshClick();
const handleScrollEnd = () => onScrollEnd();
// renders
const renderContent = () => {
Expand Down Expand Up @@ -107,6 +109,7 @@ const ActivityTab: FC<IProps> = ({
buttons={[]}
isLoading={updatingActiveAccountTransactions}
loadingTooltipLabel={t<string>('captions.updatingTransactions')}
onRefresh={handleOnRefreshClick}
/>

{renderContent()}
Expand Down
1 change: 1 addition & 0 deletions src/extension/components/ActivityTab/types/IProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface IProps extends IPropsWithContext {
accounts: IAccountWithExtendedProps[];
fetching: boolean;
network: INetworkWithTransactionParams;
onRefreshClick: () => void;
onScrollEnd: () => void;
}

Expand Down
42 changes: 33 additions & 9 deletions src/extension/components/TabControlBar/TabControlBar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { HStack, Spacer, Spinner, Tooltip } from '@chakra-ui/react';
import React, { type FC } from 'react';
import React, { type FC, ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import { IoReloadOutline } from 'react-icons/io5';

// components
import IconButton from '@extension/components/IconButton';
Expand All @@ -19,21 +21,43 @@ const TabControlBar: FC<IProps> = ({
buttons,
isLoading = false,
loadingTooltipLabel,
onRefresh,
}) => {
const { t } = useTranslation();
// hooks
const itemBorderColor = useItemBorderColor();
const primaryColor = usePrimaryColor();
// renders
const renderLoader = () => {
const node = (
<Spinner thickness="1px" speed="0.65s" color={primaryColor} size="sm" />
);
let node: ReactElement;

if (!loadingTooltipLabel) {
return node;
if (isLoading) {
node = (
<Spinner thickness="1px" speed="0.65s" color={primaryColor} size="sm" />
);

if (!loadingTooltipLabel) {
return node;
}

return <Tooltip label={loadingTooltipLabel}>{node}</Tooltip>;
}

if (onRefresh) {
return (
<Tooltip label={t<string>('buttons.refresh')}>
<IconButton
aria-label={t<string>('buttons.refresh')}
icon={IoReloadOutline}
onClick={onRefresh}
size="sm"
variant="ghost"
/>
</Tooltip>
);
}

return <Tooltip label={loadingTooltipLabel}>{node}</Tooltip>;
return null;
};

return (
Expand All @@ -48,8 +72,8 @@ const TabControlBar: FC<IProps> = ({
spacing={1}
w="full"
>
{/*spinner*/}
{isLoading && renderLoader()}
{/*refresh button*/}
{renderLoader()}

<Spacer minH={DEFAULT_GAP + 2} />

Expand Down
1 change: 1 addition & 0 deletions src/extension/components/TabControlBar/types/IProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IProps extends IPropsWithContext {
buttons: ITabControlBarButtonProps[];
isLoading?: boolean;
loadingTooltipLabel?: string;
onRefresh?: () => void;
}

export default IProps;
40 changes: 31 additions & 9 deletions src/extension/config/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ const networks: INetwork[] = [
canonicalName: 'Voi',
chakraTheme: 'voi',
blockExplorers: [
new VoiObserverBlockExplorer('https://explorer.voi.network/explorer'),
new VoiObserverBlockExplorer({
baseURL: 'https://explorer.voi.network/explorer',
canonicalName: 'Voi Network',
id: 'voi-network',
}),
],
feeSunkAddress:
'TBEIGCNK4UCN3YDP2NODK3MJHTUZMYS3TABRM2MVSI2MPUR2V36E5JYHSY',
Expand Down Expand Up @@ -83,14 +87,22 @@ const networks: INetwork[] = [
},
],
arc0072Indexers: [
new NautilusARC0072Indexer('https://arc72-idx.nautilus.sh'),
new NFTNavigatorARC0072Indexer('https://arc72-idx.nftnavigator.xyz'),
new NautilusARC0072Indexer({
baseURL: 'https://arc72-idx.nautilus.sh',
}),
new NFTNavigatorARC0072Indexer({
baseURL: 'https://arc72-idx.nftnavigator.xyz',
}),
],
canonicalName: 'Voi',
chakraTheme: 'voi',
blockExplorers: [
new VoiObserverBlockExplorer('https://voi.observer/explorer'),
new AVMExplorerBlockExplorer('https://avmexplorer.com'),
new VoiObserverBlockExplorer({
baseURL: 'https://voi.observer/explorer',
}),
new AVMExplorerBlockExplorer({
baseURL: 'https://avmexplorer.com',
}),
],
feeSunkAddress:
'FEES3ZW52HQ7U7LB3OGLUFQX2DCCWPJ2LIMXAH75KYROBZBQRN3Q5OR3GI',
Expand Down Expand Up @@ -119,7 +131,11 @@ const networks: INetwork[] = [
type: AssetTypeEnum.Native,
verified: true,
},
nftExplorers: [new NFTNavigatorNFTExplorer()],
nftExplorers: [
new NFTNavigatorNFTExplorer({
baseURL: 'https://arc72-idx.nftnavigator.xyz',
}),
],
type: NetworkTypeEnum.Test,
},
/**
Expand All @@ -139,8 +155,12 @@ const networks: INetwork[] = [
canonicalName: 'Algorand',
chakraTheme: 'algorand',
blockExplorers: [
new PeraBlockExplorer('https://explorer.perawallet.app'),
new AlloBlockExplorer('https://allo.info'),
new PeraBlockExplorer({
baseURL: 'https://explorer.perawallet.app',
}),
new AlloBlockExplorer({
baseURL: 'https://allo.info',
}),
],
feeSunkAddress:
'Y76M3MSY6DKBRHBL7C3NNDXGS5IIMQVQVUAB6MP4XEMMGVF2QWNPL226CA',
Expand Down Expand Up @@ -230,7 +250,9 @@ const networks: INetwork[] = [
canonicalName: 'Algorand',
chakraTheme: 'algorand',
blockExplorers: [
new PeraBlockExplorer('https://testnet.explorer.perawallet.app'),
new PeraBlockExplorer({
baseURL: 'https://testnet.explorer.perawallet.app',
}),
],
feeSunkAddress:
'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE',
Expand Down
Loading

0 comments on commit c3650ec

Please sign in to comment.