Skip to content

Commit

Permalink
polling hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
bergeron committed Nov 4, 2024
1 parent 651d035 commit ddfcc07
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 3 deletions.
9 changes: 6 additions & 3 deletions app/components/Views/Root/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useAppTheme, ThemeContext } from '../../../util/theme';
import { ToastContextWrapper } from '../../../component-library/components/Toast';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { isTest } from '../../../util/test/utils';
import { AssetPollingProvider } from '../../hooks/AssetPolling/AssetPollingProvider';

/**
* Top level of the component hierarchy
Expand Down Expand Up @@ -85,9 +86,11 @@ const ConnectedRoot = () => {
<SafeAreaProvider>
<ThemeContext.Provider value={theme}>
<ToastContextWrapper>
<ErrorBoundary view="Root">
<App />
</ErrorBoundary>
<AssetPollingProvider>
<ErrorBoundary view="Root">
<App />
</ErrorBoundary>
</AssetPollingProvider>
</ToastContextWrapper>
</ThemeContext.Provider>
</SafeAreaProvider>
Expand Down
11 changes: 11 additions & 0 deletions app/components/hooks/AssetPolling/AssetPollingProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React, { ReactNode } from 'react';
import useCurrencyRatePolling from './useCurrencyRatePolling';

// This provider is a step towards making controller polling fully UI based.
// Eventually, individual UI components will call the use*Polling hooks to
// poll and return particular data. This polls globally in the meantime.
export const AssetPollingProvider = ({ children }: { children: ReactNode }) => {
useCurrencyRatePolling();

return <>{children}</>;
};
32 changes: 32 additions & 0 deletions app/components/hooks/AssetPolling/useCurrencyRatePolling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

import { useSelector } from 'react-redux';
import usePolling from '../usePolling';
import { selectNetworkConfigurations } from '../../../selectors/networkController';
import Engine from '../../../core/Engine';

// Polls native currency prices across networks.
//
// Note: Don't check this in until assets controllers version 41,
// where the input changes to an array of tickers.
const useCurrencyRatePolling = () => {

const networkConfigurations = useSelector(selectNetworkConfigurations);

const networkClientIds =
Object.values(networkConfigurations).map(networkConfiguration => ({
networkClientId: networkConfiguration.rpcEndpoints[
networkConfiguration.defaultRpcEndpointIndex].networkClientId
}));

const { CurrencyRateController } = Engine.context;

usePolling({
startPolling:
CurrencyRateController.startPolling.bind(CurrencyRateController),
stopPollingByPollingToken:
CurrencyRateController.stopPollingByPollingToken.bind(CurrencyRateController),
input: networkClientIds,
});
};

export default useCurrencyRatePolling;
58 changes: 58 additions & 0 deletions app/components/hooks/usePolling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect, useState } from 'react';

interface UsePollingOptions<PollingInput> {
startPolling: (input: PollingInput) => string;
stopPollingByPollingToken: (pollingToken: string) => void;
input: PollingInput[];
}

// A hook that manages multiple polling loops of a polling controller.
// Callers provide an array of inputs, and the hook manages starting
// and stopping polling loops for each input.
const usePolling = <PollingInput>(
usePollingOptions: UsePollingOptions<PollingInput>,
) => {
const [polls, setPolls] = useState(new Map());

useEffect(() => {
// start new polls
for (const input of usePollingOptions.input) {
const key = JSON.stringify(input);
if (!polls.has(key)) {
const token = usePollingOptions.startPolling(input);
setPolls((prevPolls) => new Map(prevPolls).set(key, token));
}
}

// stop existing polls
for (const [inputKey, token] of polls.entries()) {
const exists = usePollingOptions.input.some(
(i) => inputKey === JSON.stringify(i),
);

if (!exists) {
usePollingOptions.stopPollingByPollingToken(token);
setPolls((prevPolls) => {
const newPolls = new Map(prevPolls);
newPolls.delete(inputKey);
return newPolls;
});
}
}
},
// stringified for deep equality
// eslint-disable-next-line react-hooks/exhaustive-deps
[usePollingOptions.input && JSON.stringify(usePollingOptions.input)]);

// stop all polling on dismount
useEffect(() => () => {
for (const token of polls.values()) {
usePollingOptions.stopPollingByPollingToken(token);
}
},
// Intentionally empty to trigger on dismount
// eslint-disable-next-line react-hooks/exhaustive-deps
[]);
};

export default usePolling;

0 comments on commit ddfcc07

Please sign in to comment.