websocket, polling rate, and component updates

This commit is contained in:
Nishad 2020-08-30 19:49:45 +08:00
parent c79765dd33
commit c5d8fef7d6
5 changed files with 108 additions and 61 deletions

View File

@ -75,10 +75,10 @@ export default function TradeForm({ style, setChangeOrderRef }) {
setChangeOrderRef && setChangeOrderRef(doChangeOrder); setChangeOrderRef && setChangeOrderRef(doChangeOrder);
}, [setChangeOrderRef]); }, [setChangeOrderRef]);
useEffect(() => { // useEffect(() => {
sizeFraction && onSliderChange(sizeFraction); // sizeFraction && onSliderChange(sizeFraction);
// eslint-disable-next-line react-hooks/exhaustive-deps // // eslint-disable-next-line react-hooks/exhaustive-deps
}, [side, sizeFraction]); // }, [side, sizeFraction]);
const doChangeOrder = ({ size, price }) => { const doChangeOrder = ({ size, price }) => {
size && setSize(size); size && setSize(size);
@ -203,12 +203,12 @@ export default function TradeForm({ style, setChangeOrderRef }) {
step={market?.minOrderSize || 1} step={market?.minOrderSize || 1}
onChange={(e) => setSize(e.target.value)} onChange={(e) => setSize(e.target.value)}
/> />
<Slider {/*<Slider*/}
value={sizeFraction} {/* value={sizeFraction}*/}
tipFormatter={(value) => `${value}%`} {/* tipFormatter={(value) => `${value}%`}*/}
marks={sliderMarks} {/* marks={sliderMarks}*/}
onChange={onSliderChange} {/* onChange={onSliderChange}*/}
/> {/*/>*/}
<div style={{ paddingTop: 18 }}> <div style={{ paddingTop: 18 }}>
{'POST '} {'POST '}
<Switch <Switch

View File

@ -34,30 +34,30 @@ export function ConnectionProvider({ children }) {
// The websocket library solana/web3.js uses closes its websocket connection when the subscription list // The websocket library solana/web3.js uses closes its websocket connection when the subscription list
// is empty after opening its first time, preventing subsequent subscriptions from receiving responses. // is empty after opening its first time, preventing subsequent subscriptions from receiving responses.
// This is a hack to prevent the list from every getting empty // This is a hack to prevent the list from every getting empty
useEffect(() => { // useEffect(() => {
const id = connection.onSignature( // const id = connection.onSignature(
'do not worry, this is expected to yield warning logs', // 'do not worry, this is expected to yield warning logs',
(result) => { // (result) => {
console.log( // console.log(
'Received onSignature responses from does-not-exist', // 'Received onSignature responses from does-not-exist',
result, // result,
); // );
}, // },
); // );
return () => connection.removeSignatureListener(id); // return () => connection.removeSignatureListener(id);
}, [connection]); // }, [connection]);
useEffect(() => { // useEffect(() => {
const id = sendConnection.onSignature( // const id = sendConnection.onSignature(
'do not worry, this is expected to yield warning logs', // 'do not worry, this is expected to yield warning logs',
(result) => { // (result) => {
console.log( // console.log(
'Received onSignature responses from does-not-exist', // 'Received onSignature responses from does-not-exist',
result, // result,
); // );
}, // },
); // );
return () => sendConnection.removeSignatureListener(id); // return () => sendConnection.removeSignatureListener(id);
}, [sendConnection]); // }, [sendConnection]);
return ( return (
<ConnectionContext.Provider <ConnectionContext.Provider
@ -89,37 +89,52 @@ export function useAccountInfo(publicKey) {
cacheKey, cacheKey,
{ refreshInterval: 60000000 }, { refreshInterval: 60000000 },
); );
let id = publicKey?.toBase58();
useEffect(() => { useEffect(() => {
if (!publicKey) { if (!publicKey) {
return () => {}; return () => {};
} }
if (accountListenerCount.has(cacheKey)) { if (accountListenerCount.has(cacheKey)) {
let currentCount = accountListenerCount.get(cacheKey); let currentItem = accountListenerCount.get(cacheKey);
accountListenerCount.set(cacheKey, currentCount + 1); console.log('Incrementing', id, currentItem.count + 1);
return () => {}; accountListenerCount.set(cacheKey, {
} count: currentItem.count + 1,
let previousData = null; subscriptionId: currentItem.subscriptionId,
const id = connection.onAccountChange(publicKey, (e) => { });
if (e.data) { } else {
if (!previousData || !previousData.equals(e.data)) { let previousData = null;
setCache(cacheKey, e); console.log('Subscribing to ', id);
const subscriptionId = connection.onAccountChange(publicKey, (e) => {
if (e.data) {
if (!previousData || !previousData.equals(e.data)) {
console.log('Passing along new data', id);
setCache(cacheKey, e);
} else {
console.log('Skipping no-op update', id);
}
previousData = e.data;
} }
previousData = e.data; });
} console.log('Setting cache', id);
}); accountListenerCount.set(cacheKey, { count: 1, subscriptionId });
accountListenerCount.set(cacheKey, 1); }
return () => { return () => {
let currentCount = accountListenerCount.get(cacheKey); let currentItem = accountListenerCount.get(cacheKey);
if (currentCount === 1) { let nextCount = currentItem.count - 1;
// last listener, safe to unsubscribe if (nextCount <= 0) {
connection.removeAccountChangeListener(id); console.log('Removing cache', id);
connection.removeAccountChangeListener(currentItem.subscriptionId);
accountListenerCount.delete(cacheKey); accountListenerCount.delete(cacheKey);
} else { } else {
accountListenerCount.set(cacheKey, currentCount - 1); console.log('Decrementing', id, nextCount);
accountListenerCount.set(cacheKey, {
count: nextCount,
subscriptionId: currentItem.subscriptionId,
});
} }
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [connection, publicKey?.toBase58(), cacheKey]); }, [cacheKey]);
return [accountInfo, loaded]; return [accountInfo, loaded];
} }

View File

@ -7,10 +7,11 @@ const pageLoadTime = new Date();
const globalCache = new Map(); const globalCache = new Map();
class FetchLoopListener { class FetchLoopListener {
constructor(cacheKey, fn, refreshInterval, callback) { constructor(cacheKey, fn, refreshInterval, refreshIntervalOnError, callback) {
this.cacheKey = cacheKey; this.cacheKey = cacheKey;
this.fn = fn; this.fn = fn;
this.refreshInterval = refreshInterval; this.refreshInterval = refreshInterval;
this.refreshIntervalOnError = refreshIntervalOnError;
this.callback = callback; this.callback = callback;
} }
} }
@ -30,6 +31,14 @@ class FetchLoopInternal {
); );
} }
get refreshIntervalOnError() {
return Math.min(
...[...this.listeners]
.map((listener) => listener.refreshIntervalOnError)
.filter((x) => x),
);
}
get stopped() { get stopped() {
return this.listeners.size === 0; return this.listeners.size === 0;
} }
@ -65,6 +74,7 @@ class FetchLoopInternal {
return; return;
} }
let errored = false;
try { try {
const data = await this.fn(); const data = await this.fn();
globalCache.set(this.cacheKey, data); globalCache.set(this.cacheKey, data);
@ -74,9 +84,17 @@ class FetchLoopInternal {
} catch (error) { } catch (error) {
++this.errors; ++this.errors;
console.warn(error); console.warn(error);
errored = true;
} finally { } finally {
if (!this.timeoutId && !this.stopped) { if (!this.timeoutId && !this.stopped) {
let waitTime = this.refreshInterval; let waitTime = this.refreshInterval;
if (
errored &&
this.refreshIntervalOnError &&
this.refreshIntervalOnError > 0
) {
waitTime = this.refreshIntervalOnError;
}
// Back off on errors. // Back off on errors.
if (this.errors > 0) { if (this.errors > 0) {
@ -141,7 +159,7 @@ const globalLoops = new FetchLoops();
export function useAsyncData( export function useAsyncData(
asyncFn, asyncFn,
cacheKey, cacheKey,
{ refreshInterval = 60000 } = {}, { refreshInterval = 60000, refreshIntervalOnError = null } = {},
) { ) {
const [, rerender] = useReducer((i) => i + 1, 0); const [, rerender] = useReducer((i) => i + 1, 0);
@ -154,6 +172,7 @@ export function useAsyncData(
cacheKey, cacheKey,
asyncFn, asyncFn,
refreshInterval, refreshInterval,
refreshIntervalOnError,
rerender, rerender,
); );
globalLoops.addListener(listener); globalLoops.addListener(listener);

View File

@ -131,12 +131,12 @@ export function useAllMarkets() {
const MarketContext = React.createContext(null); const MarketContext = React.createContext(null);
// For things that don't really change // For things that don't really change
const _SLOW_REFRESH_INTERVAL = 1000 * 1000; const _SLOW_REFRESH_INTERVAL = 5 * 1000;
// For things that change frequently // For things that change frequently
const _FAST_REFRESH_INTERVAL = 5 * 1000; const _FAST_REFRESH_INTERVAL = 1000;
const _MEDIUM_REFRESH_INTERVAL = 5 * 1000; const _MEDIUM_REFRESH_INTERVAL = 3 * 1000;
export function MarketProvider({ children }) { export function MarketProvider({ children }) {
const [marketName, setMarketName] = useLocalStorageState( const [marketName, setMarketName] = useLocalStorageState(
@ -601,9 +601,12 @@ export function useBalances() {
openOrdersAccount && openOrdersAccount &&
openOrdersAccount.quoteTokenTotal && openOrdersAccount.quoteTokenTotal &&
openOrdersAccount.quoteTokenFree; openOrdersAccount.quoteTokenFree;
if (baseCurrency === 'UNKNOWN' || quoteCurrency === 'UNKNOWN') {
return [];
}
return [ return [
{ {
key: baseCurrency, key: `${baseCurrency}${quoteCurrency}${baseCurrency}`,
coin: baseCurrency, coin: baseCurrency,
wallet: baseCurrencyBalances, wallet: baseCurrencyBalances,
orders: orders:
@ -620,7 +623,7 @@ export function useBalances() {
: null, : null,
}, },
{ {
key: quoteCurrency, key: `${quoteCurrency}${baseCurrency}${quoteCurrency}`,
coin: quoteCurrency, coin: quoteCurrency,
wallet: quoteCurrencyBalances, wallet: quoteCurrencyBalances,
orders: orders:

View File

@ -19,7 +19,17 @@ export async function settleFunds({
!baseCurrencyAccount || !baseCurrencyAccount ||
!quoteCurrencyAccount !quoteCurrencyAccount
) { ) {
notify({ message: 'Not connected' }); if (
(baseCurrencyAccount && !quoteCurrencyAccount) ||
(quoteCurrencyAccount && !baseCurrencyAccount)
) {
notify({
message: 'Add token account',
description: 'Add accounts for both currencies on sollet.io',
});
} else {
notify({ message: 'Not connected' });
}
return; return;
} }
const transaction = await market.makeSettleFundsTransaction( const transaction = await market.makeSettleFundsTransaction(