-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: track unread counts more accurately (#1382)
- Loading branch information
Showing
7 changed files
with
261 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { useEffect, useRef } from 'react'; | ||
|
||
import { selectRawIndexerHeightData } from '@/abacus-ts/selectors/base'; | ||
import { shallowEqual } from 'react-redux'; | ||
|
||
import { getUserWalletAddress } from '@/state/accountSelectors'; | ||
import { setSeenFills, setSeenOpenOrders, setSeenOrderHistory } from '@/state/accountUiMemory'; | ||
import { getSelectedNetwork } from '@/state/appSelectors'; | ||
import { useAppDispatch, useAppSelector } from '@/state/appTypes'; | ||
|
||
export function useViewPanel( | ||
market: string | undefined, | ||
kind: 'fills' | 'openOrders' | 'orderHistory' | ||
) { | ||
const networkId = useAppSelector(getSelectedNetwork); | ||
const walletId = useAppSelector(getUserWalletAddress); | ||
const height = useAppSelector(selectRawIndexerHeightData); | ||
const lastSetCore = useRef<any[]>([]); | ||
|
||
const dispatch = useAppDispatch(); | ||
const actionCreator = ( | ||
{ | ||
fills: setSeenFills, | ||
openOrders: setSeenOpenOrders, | ||
orderHistory: setSeenOrderHistory, | ||
} as const | ||
)[kind]; | ||
|
||
const componentWillUnmount = useComponentWillUnmount(); | ||
|
||
useEffect(() => { | ||
if (height != null && walletId != null) { | ||
// only set once for a given set of configurations | ||
// effectively, view on mount as soon as we load height | ||
const thisCore = [market, actionCreator, networkId, walletId]; | ||
if (!shallowEqual(lastSetCore.current, thisCore)) { | ||
lastSetCore.current = thisCore; | ||
dispatch(actionCreator({ scope: { networkId, walletId }, market, height })); | ||
} | ||
} | ||
return () => { | ||
if (componentWillUnmount.current) { | ||
if (height != null && walletId != null) { | ||
dispatch(actionCreator({ scope: { networkId, walletId }, market, height })); | ||
} | ||
} | ||
}; | ||
}, [market, actionCreator, networkId, walletId, height, dispatch, componentWillUnmount]); | ||
} | ||
|
||
function useComponentWillUnmount() { | ||
const componentWillUnmount = useRef(false); | ||
useEffect(() => { | ||
componentWillUnmount.current = false; | ||
return () => { | ||
componentWillUnmount.current = true; | ||
}; | ||
}, []); | ||
return componentWillUnmount; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { HeightResponse } from '@dydxprotocol/v4-client-js'; | ||
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; | ||
|
||
import { DydxNetwork } from '@/constants/networks'; | ||
|
||
// NOTE: This app slice is persisted via redux-persist. Changes to this type may require migrations. | ||
|
||
export const ALL_MARKETS_STRING = '___ALL_MARKETS___'; | ||
|
||
// TODO: seen notifications belong in here too | ||
export interface AccountUiMemoryBase { | ||
seenFills: { [marketIdOrAllMarkets: string]: HeightResponse }; | ||
seenOpenOrders: { [marketIdOrAllMarkets: string]: HeightResponse }; | ||
seenOrderHistory: { [marketIdOrAllMarkets: string]: HeightResponse }; | ||
} | ||
|
||
type SeenHeightPayload = { height: HeightResponse; market: string | undefined }; | ||
|
||
export type AccountUiMemoryState = { | ||
[walletId: string]: { | ||
[networkId: string]: AccountUiMemoryBase; | ||
}; | ||
}; | ||
|
||
type AccountScope = { | ||
walletId: string; | ||
networkId: DydxNetwork; | ||
}; | ||
type ScopePayload = { scope: AccountScope }; | ||
|
||
export const initialState: AccountUiMemoryState = {}; | ||
|
||
function ensureScopePresent(state: AccountUiMemoryState, scope: ScopePayload): AccountUiMemoryBase { | ||
state[scope.scope.walletId] ??= {}; | ||
state[scope.scope.walletId]![scope.scope.networkId] ??= { | ||
seenFills: {}, | ||
seenOpenOrders: {}, | ||
seenOrderHistory: {}, | ||
}; | ||
return state[scope.scope.walletId]![scope.scope.networkId]!; | ||
} | ||
|
||
function setSeen( | ||
state: AccountUiMemoryBase, | ||
key: 'seenFills' | 'seenOpenOrders' | 'seenOrderHistory', | ||
payload: SeenHeightPayload | ||
) { | ||
const maybeAll = state[key][ALL_MARKETS_STRING]; | ||
const maybeUs = state[key][payload.market ?? ALL_MARKETS_STRING]; | ||
// make sure we are more than existing and more than base | ||
if ( | ||
(maybeAll == null || maybeAll.height < payload.height.height) && | ||
(maybeUs == null || maybeUs.height < payload.height.height) | ||
) { | ||
state[key][payload.market ?? ALL_MARKETS_STRING] = payload.height; | ||
} | ||
// if all markets, remove all smaller | ||
if (payload.market == null) { | ||
Object.keys(state[key]).forEach((marketOrAll) => { | ||
if (marketOrAll === ALL_MARKETS_STRING) { | ||
return; | ||
} | ||
if (state[key][marketOrAll]!.height < payload.height.height) { | ||
delete state[key][marketOrAll]; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
export const accountUiMemorySlice = createSlice({ | ||
name: 'accountUiMemory', | ||
initialState, | ||
reducers: { | ||
setSeenFills: ( | ||
state: AccountUiMemoryState, | ||
{ payload }: PayloadAction<SeenHeightPayload & ScopePayload> | ||
) => { | ||
const thisState = ensureScopePresent(state, payload); | ||
setSeen(thisState, 'seenFills', payload); | ||
}, | ||
setSeenOpenOrders: ( | ||
state: AccountUiMemoryState, | ||
{ payload }: PayloadAction<SeenHeightPayload & ScopePayload> | ||
) => { | ||
const thisState = ensureScopePresent(state, payload); | ||
setSeen(thisState, 'seenOpenOrders', payload); | ||
}, | ||
setSeenOrderHistory: ( | ||
state: AccountUiMemoryState, | ||
{ payload }: PayloadAction<SeenHeightPayload & ScopePayload> | ||
) => { | ||
const thisState = ensureScopePresent(state, payload); | ||
setSeen(thisState, 'seenOrderHistory', payload); | ||
}, | ||
}, | ||
}); | ||
|
||
export const { setSeenFills, setSeenOpenOrders, setSeenOrderHistory } = | ||
accountUiMemorySlice.actions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.