diff --git a/src/components/common/ColonyActionsTable/FiltersContext/FiltersContext.ts b/src/components/common/ColonyActionsTable/FiltersContext/FiltersContext.ts index bd77e9718e9..375948327ca 100644 --- a/src/components/common/ColonyActionsTable/FiltersContext/FiltersContext.ts +++ b/src/components/common/ColonyActionsTable/FiltersContext/FiltersContext.ts @@ -22,6 +22,7 @@ export interface FiltersContextValue { dateFilters: DateOptions; activeFilters: ActivityFeedFilters; selectedFiltersCount: number; + chainIdFilters: string[]; handleActionTypesFilterChange: ( event: React.ChangeEvent, ) => void; @@ -34,6 +35,9 @@ export interface FiltersContextValue { handleDateFilterChange: (event: React.ChangeEvent) => void; handleCustomDateFilterChange: (date: [Date | null, Date | null]) => void; handleResetFilters: (filter: FiltersValues) => void; + handleChainIdFilterChange: ( + event: React.ChangeEvent, + ) => void; } export const FiltersContext = createContext( diff --git a/src/components/common/ColonyActionsTable/FiltersContext/FiltersContextProvider.tsx b/src/components/common/ColonyActionsTable/FiltersContext/FiltersContextProvider.tsx index 897d1b2c515..019d5d3f107 100644 --- a/src/components/common/ColonyActionsTable/FiltersContext/FiltersContextProvider.tsx +++ b/src/components/common/ColonyActionsTable/FiltersContext/FiltersContextProvider.tsx @@ -15,11 +15,12 @@ import { import useCurrentBlockTime from '~hooks/useCurrentBlockTime.ts'; import { type AnyActionType } from '~types/actions.ts'; import { type MotionState } from '~utils/colonyMotions.ts'; +import { useChainOptions } from '~v5/common/ActionSidebar/partials/ChainSelect/hooks.ts'; import { type DateOptions } from '../partials/ActionsTableFilters/types.ts'; import { getDateFilter } from '../utils.ts'; -import { FiltersContext } from './FiltersContext.ts'; +import { FiltersContext, type FiltersContextValue } from './FiltersContext.ts'; import { FiltersValues } from './types.ts'; const emptyDateFilters: DateOptions = { @@ -39,7 +40,13 @@ const FiltersContextProvider: FC = ({ children }) => { >([]); const [actionTypesFilters, setActionTypesFilters] = useState([]); const [dateFilters, setDateFilters] = useState(emptyDateFilters); + const [chainIdFilters, setChainIdFilters] = useState([]); + const { dateFromCurrentBlockTime } = useCurrentBlockTime(); + const chainOptions = useChainOptions({ + includeDefaultChain: true, + onlyShowActiveChains: true, + }); const handleActionTypesFilterChange = useCallback( (event: React.ChangeEvent) => { @@ -120,10 +127,27 @@ const FiltersContextProvider: FC = ({ children }) => { [], ); + const handleChainIdFilterChange = useCallback( + (event: React.ChangeEvent) => { + const { checked } = event.target; + const { name } = event.target; + + if (checked) { + setChainIdFilters((state) => [...state, name]); + } else { + setChainIdFilters((state) => + state.filter((checkedItem) => checkedItem !== name), + ); + } + }, + [], + ); + const activeFilters: ActivityFeedFilters = useMemo(() => { const date = dateFromCurrentBlockTime ? getDateFilter(dateFilters, dateFromCurrentBlockTime) : null; + const actionTypes = actionTypesFilters.reduce( (result, actionType) => { const apiActionTypes = ACTION_TYPE_TO_API_ACTION_TYPES_MAP[actionType]; @@ -137,6 +161,10 @@ const FiltersContextProvider: FC = ({ children }) => { [], ); + const chainIds = chainOptions + .filter((chainOption) => chainIdFilters.includes(chainOption.label)) + .map((chainOption) => chainOption.value); + return { ...(motionStates.length ? { motionStates } : {}), ...(actionTypes.length ? { actionTypes } : {}), @@ -147,14 +175,17 @@ const FiltersContextProvider: FC = ({ children }) => { } : {}), ...(searchFilter ? { search: searchFilter } : {}), + ...(chainIdFilters.length ? { chainIds } : {}), }; }, [ - actionTypesFilters, dateFromCurrentBlockTime, dateFilters, - decisionMethods, + actionTypesFilters, motionStates, + decisionMethods, searchFilter, + chainIdFilters, + chainOptions, ]); const handleResetFilters = useCallback((filter: FiltersValues) => { @@ -168,6 +199,9 @@ const FiltersContextProvider: FC = ({ children }) => { case FiltersValues.DecisionMethod: { return setDecisionMethods([]); } + case FiltersValues.Chain: { + return setChainIdFilters([]); + } case FiltersValues.Date: case FiltersValues.Custom: { return setDateFilters(emptyDateFilters); @@ -181,10 +215,11 @@ const FiltersContextProvider: FC = ({ children }) => { const selectedFiltersCount = actionTypesFilters.length + motionStates.length + + chainIdFilters.length + decisionMethods.length + Object.values(dateFilters).filter(Boolean).length; - const value = useMemo( + const value = useMemo( () => ({ searchFilter, setSearchFilter, @@ -194,12 +229,14 @@ const FiltersContextProvider: FC = ({ children }) => { dateFilters, activeFilters, selectedFiltersCount, + chainIdFilters, handleActionTypesFilterChange, handleDecisionMethodsFilterChange, handleMotionStatesFilterChange, handleDateFilterChange, handleCustomDateFilterChange, handleResetFilters, + handleChainIdFilterChange, }), [ searchFilter, @@ -209,12 +246,14 @@ const FiltersContextProvider: FC = ({ children }) => { dateFilters, activeFilters, selectedFiltersCount, + chainIdFilters, handleActionTypesFilterChange, handleDecisionMethodsFilterChange, handleMotionStatesFilterChange, handleDateFilterChange, handleCustomDateFilterChange, handleResetFilters, + handleChainIdFilterChange, ], ); diff --git a/src/components/common/ColonyActionsTable/FiltersContext/types.ts b/src/components/common/ColonyActionsTable/FiltersContext/types.ts index 138e79ce691..abdd9aa6af9 100644 --- a/src/components/common/ColonyActionsTable/FiltersContext/types.ts +++ b/src/components/common/ColonyActionsTable/FiltersContext/types.ts @@ -4,4 +4,5 @@ export enum FiltersValues { DecisionMethod = 'decisionMethod', Date = 'date', Custom = 'custom', + Chain = 'chain', } diff --git a/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/consts.tsx b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/consts.tsx index 4e4031cde44..e2f6ad38faa 100644 --- a/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/consts.tsx +++ b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/consts.tsx @@ -1,9 +1,16 @@ -import { Calendar, FilePlus, FlagPennant, Scales } from '@phosphor-icons/react'; +import { + Calendar, + CubeFocus, + FilePlus, + FlagPennant, + Scales, +} from '@phosphor-icons/react'; import React from 'react'; import { formatText } from '~utils/intl.ts'; import ActionTypeFilters from './partials/filters/ActionTypeFilters/index.ts'; +import ChainFilters from './partials/filters/ChainFilters/ChainFilters.tsx'; import DateFilters from './partials/filters/DateFilters/index.ts'; import DecisionMethodFilters from './partials/filters/DecisionMethodFilters/index.ts'; import StatusFilters from './partials/filters/StatusFilters/index.ts'; @@ -33,4 +40,10 @@ export const filterItems = [ name: 'decisionMethod', children: , }, + { + icon: CubeFocus, + label: formatText({ id: 'activityFeedTable.filters.chain' }), + name: 'chain', + children: , + }, ]; diff --git a/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/ActiveFiltersList/hooks.ts b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/ActiveFiltersList/hooks.ts index d1075c05788..44c43130eae 100644 --- a/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/ActiveFiltersList/hooks.ts +++ b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/ActiveFiltersList/hooks.ts @@ -17,6 +17,7 @@ export const useActiveFilters = () => { decisionMethods, motionStates, handleResetFilters, + chainIdFilters, } = useFiltersContext(); const activeFiltersToDisplay = useMemo(() => { @@ -87,8 +88,25 @@ export const useActiveFilters = () => { }, ] : []), + ...(chainIdFilters.length + ? [ + { + filter: FiltersValues.Chain, + category: formatText({ + id: 'activityFeedTable.filters.chain', + }), + items: chainIdFilters, + }, + ] + : []), ]; - }, [actionTypesFilters, dateFilters, decisionMethods, motionStates]); + }, [ + actionTypesFilters, + chainIdFilters, + dateFilters, + decisionMethods, + motionStates, + ]); return { activeFiltersToDisplay, handleResetFilters }; }; diff --git a/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/filters/ChainFilters/ChainFilters.tsx b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/filters/ChainFilters/ChainFilters.tsx new file mode 100644 index 00000000000..ca539c57662 --- /dev/null +++ b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/filters/ChainFilters/ChainFilters.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +import { useFiltersContext } from '~common/ColonyActionsTable/FiltersContext/FiltersContext.ts'; +import { formatText } from '~utils/intl.ts'; +import { useChainOptions } from '~v5/common/ActionSidebar/partials/ChainSelect/hooks.ts'; +import Checkbox from '~v5/common/Checkbox/index.ts'; + +const ChainFilters = () => { + const { chainIdFilters, handleChainIdFilterChange } = useFiltersContext(); + + const chainOptions = useChainOptions({ + includeDefaultChain: true, + onlyShowActiveChains: true, + }); + + return ( +
+
+ {formatText({ id: 'balancePage.filter.availableChains' })} +
+
    + {chainOptions.map(({ label, icon: Icon, value }) => { + const isChecked = chainIdFilters.includes(label); + + return ( +
  • + + {Icon && } + {label} + +
  • + ); + })} +
+
+ ); +}; + +export default ChainFilters; diff --git a/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/filters/ChainFilters/index.ts b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/filters/ChainFilters/index.ts new file mode 100644 index 00000000000..d18b1b36e66 --- /dev/null +++ b/src/components/common/ColonyActionsTable/partials/ActionsTableFilters/partials/filters/ChainFilters/index.ts @@ -0,0 +1 @@ +export { default } from './ChainFilters.tsx'; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/BalanceFilters.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/BalanceFilters.tsx index a1bf834fa5b..ffed1f935dd 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/BalanceFilters.tsx +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/BalanceFilters.tsx @@ -15,7 +15,7 @@ import { useFiltersContext } from '../FiltersContext/FiltersContext.ts'; import ActiveFiltersList from '../partials/ActiveFiltersList/ActiveFiltersList.tsx'; import BalanceTableFiltersItem from '../partials/BalanceTableFiltersItem/BalanceTableFiltersItem.tsx'; -import { filterItems } from './consts.tsx'; +import { filterItems } from './consts.ts'; interface BalanceFiltersProps { toggleAddFundsModalOn: () => void; @@ -42,10 +42,10 @@ const BalanceFilters: FC = ({ toggleAddFundsModalOn }) => { {formatText({ id: isMobile ? 'filterAndSort' : 'filters' })}
    - {filterItems.map(({ icon, label, children }) => ( + {filterItems.map(({ icon, label, FiltersContent }) => (
  • - {children} +
  • ))} diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/consts.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/consts.ts new file mode 100644 index 00000000000..92274143efc --- /dev/null +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/consts.ts @@ -0,0 +1,30 @@ +import { + CoinVertical, + CubeFocus, + FolderSimpleLock, +} from '@phosphor-icons/react'; + +import { formatText } from '~utils/intl.ts'; + +import { BalancePageFilter } from '../partials/filters/index.ts'; + +export const filterItems = [ + { + icon: CoinVertical, + label: formatText({ id: 'balancePage.filter.tokenType' }), + name: 'token', + FiltersContent: BalancePageFilter.Token, + }, + { + icon: FolderSimpleLock, + label: formatText({ id: 'balancePage.filter.attributes' }), + name: 'attribute', + FiltersContent: BalancePageFilter.Attribute, + }, + { + icon: CubeFocus, + label: formatText({ id: 'balancePage.filter.chain' }), + name: 'chain', + FiltersContent: BalancePageFilter.Chain, + }, +]; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/consts.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/consts.tsx deleted file mode 100644 index 47a76479fbd..00000000000 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/consts.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { CoinVertical, FolderSimpleLock } from '@phosphor-icons/react'; -import React from 'react'; - -import { formatText } from '~utils/intl.ts'; - -import AttributeFilters from '../partials/filters/AttributeFilters/index.ts'; -import TokenFilters from '../partials/filters/TokenFilters/index.ts'; - -export const filterItems = [ - { - icon: CoinVertical, - label: formatText({ id: 'balancePage.filter.tokenType' }), - name: 'actionType', - children: , - }, - { - icon: FolderSimpleLock, - label: formatText({ id: 'balancePage.filter.attributes' }), - name: 'status', - children: , - }, -]; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/index.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/index.ts index e69de29bb2d..5ab2d5cd3df 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/index.ts +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/BalanceFilters/index.ts @@ -0,0 +1 @@ +export { default } from './BalanceFilters.tsx'; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContext.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContext.ts index 94d1030a060..d497cc8c9c3 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContext.ts +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContext.ts @@ -1,28 +1,23 @@ import { createContext, useContext } from 'react'; import { - type AttributeFilters, type FiltersValues, - type TokenTypes, - type BalanceTableFilters, + type TBalanceTableFilters, + type TBalanceTableFilterKey, } from './types.ts'; import type React from 'react'; -interface FiltersContextValue { +export interface FiltersContextValue { searchFilter: string; setSearchFilter: (searchValue: string) => void; - tokenTypes: TokenTypes; - attributeFilters: AttributeFilters; - activeFilters: BalanceTableFilters; selectedFiltersCount: number; - handleAttributeFilterChange: ( - event: React.ChangeEvent, - ) => void; - handleTokenTypesFilterChange: ( + handleResetFilters: (filter: FiltersValues) => void; + filters: TBalanceTableFilters; + handleFiltersChange: ( event: React.ChangeEvent, + type: TBalanceTableFilterKey, ) => void; - handleResetFilters: (filter: FiltersValues) => void; } export const FiltersContext = createContext( diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContextProvider.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContextProvider.tsx index 9644d3a8cc8..6d3cad7ece3 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContextProvider.tsx +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/FiltersContextProvider.tsx @@ -6,68 +6,66 @@ import React, { useCallback, } from 'react'; -import { FiltersContext } from './FiltersContext.ts'; +import { getObjectValues } from '~utils/objects/index.ts'; + +import { defaultBalanceTableFilters } from './consts.ts'; +import { FiltersContext, type FiltersContextValue } from './FiltersContext.ts'; import { - type AttributeFilters, FiltersValues, - type TokenTypes, - type BalanceTableFilters, + type TBalanceTableFilters, + type TBalanceTableFilterKey, } from './types.ts'; const FilterContextProvider: FC = ({ children }) => { const [searchFilter, setSearchFilter] = useState(''); - const [tokenTypes, setTokenTypes] = useState({}); - const [attributeFilters, setAttributeFilters] = useState({ - native: false, - reputation: false, - }); - - const handleTokenTypesFilterChange = useCallback( - (event: React.ChangeEvent) => { - const isChecked = event.target.checked; - const name = event.target.name as keyof TokenTypes; - if (isChecked) { - setTokenTypes({ ...tokenTypes, [name]: true }); - } else { - setTokenTypes({ ...tokenTypes, [name]: false }); - } - }, - [tokenTypes], - ); + const [filters, setFilters] = useState({ + attribute: defaultBalanceTableFilters.attribute, + token: defaultBalanceTableFilters.token, + chain: defaultBalanceTableFilters.chain, + }); - const handleAttributeFilterChange = useCallback( - (event: React.ChangeEvent) => { + const handleFiltersChange = useCallback( + ( + event: React.ChangeEvent, + type: TBalanceTableFilterKey, + ) => { const isChecked = event.target.checked; - const name = event.target.name as keyof AttributeFilters; + const { name, id } = event.target; - if (isChecked) { - setAttributeFilters({ ...attributeFilters, [name]: true }); - } else { - setAttributeFilters({ ...attributeFilters, [name]: false }); - } + setFilters((state) => ({ + ...state, + [type]: { + ...state[type], + [name]: { + isChecked, + id, + }, + }, + })); }, - [attributeFilters], + [], ); - const activeFilters: BalanceTableFilters = useMemo(() => { - return { - ...(Object.keys(tokenTypes).length ? { tokenTypes } : {}), - ...(Object.keys(attributeFilters).length ? { attributeFilters } : {}), - ...(searchFilter ? { search: searchFilter } : {}), - }; - }, [tokenTypes, attributeFilters, searchFilter]); - const handleResetFilters = useCallback((filter: FiltersValues) => { switch (filter) { case FiltersValues.TokenType: { - return setTokenTypes({}); + return setFilters((state) => ({ + ...state, + token: defaultBalanceTableFilters.token, + })); } case FiltersValues.Attributes: { - return setAttributeFilters({ - native: false, - reputation: false, - }); + return setFilters((state) => ({ + ...state, + attribute: defaultBalanceTableFilters.attribute, + })); + } + case FiltersValues.Chain: { + return setFilters((state) => ({ + ...state, + chain: defaultBalanceTableFilters.chain, + })); } default: { return undefined; @@ -76,30 +74,26 @@ const FilterContextProvider: FC = ({ children }) => { }, []); const selectedFiltersCount = - Object.values(tokenTypes).filter((value) => value === true).length + - Object.values(attributeFilters).filter((value) => value === true).length; + getObjectValues(filters.token).filter(({ isChecked }) => isChecked).length + + getObjectValues(filters.attribute).filter(({ isChecked }) => isChecked) + .length + + getObjectValues(filters.chain).filter(({ isChecked }) => isChecked).length; - const value = useMemo( + const value = useMemo( () => ({ searchFilter, setSearchFilter, - tokenTypes, - attributeFilters, - activeFilters, selectedFiltersCount, - handleAttributeFilterChange, - handleTokenTypesFilterChange, handleResetFilters, + filters, + handleFiltersChange, }), [ searchFilter, - tokenTypes, - attributeFilters, - activeFilters, selectedFiltersCount, - handleAttributeFilterChange, - handleTokenTypesFilterChange, handleResetFilters, + filters, + handleFiltersChange, ], ); diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/consts.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/consts.ts new file mode 100644 index 00000000000..8563edd14ce --- /dev/null +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/consts.ts @@ -0,0 +1,14 @@ +import { type TBalanceTableFilters } from './types.ts'; + +export const defaultBalanceTableFilters: TBalanceTableFilters = { + attribute: { + native: { + isChecked: false, + }, + reputation: { + isChecked: false, + }, + }, + chain: {}, + token: {}, +}; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/types.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/types.ts index 6fd31548f2d..93e4628400c 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/types.ts +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/types.ts @@ -1,18 +1,27 @@ export enum FiltersValues { TokenType = 'tokenType', Attributes = 'attributes', + Chain = 'chain', } export interface TokenTypes { [key: string]: boolean; } -export interface AttributeFilters { - native: boolean; - reputation: boolean; -} +type TFilterConfig = { + isChecked: boolean; + id?: string; +}; -export interface BalanceTableFilters { - tokenTypes?: TokenTypes; - attributeFilters?: AttributeFilters; -} +type TAttributeFilter = { + native: TFilterConfig; + reputation: TFilterConfig; +}; + +export type TBalanceTableFilters = { + attribute: TAttributeFilter; + chain: Record; + token: Record; +}; + +export type TBalanceTableFilterKey = keyof TBalanceTableFilters; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/ActiveFiltersList/hooks.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/ActiveFiltersList/hooks.ts index fa7f94ebc48..f9881d11a09 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/ActiveFiltersList/hooks.ts +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/ActiveFiltersList/hooks.ts @@ -3,23 +3,27 @@ import { useMemo } from 'react'; import { useFiltersContext } from '~frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/index.ts'; import { FiltersValues } from '~frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/types.ts'; import { formatText } from '~utils/intl.ts'; +import { getObjectKeys } from '~utils/objects/index.ts'; import { ATTRIBUTE_FILTERS } from '../filters/AttributeFilters/consts.ts'; import { useGetTokenTypeFilters } from '../filters/TokenFilters/hooks.ts'; +// The hook used to show enabled filters via the blue filter pills export const useActiveFilters = () => { - const { attributeFilters, tokenTypes, handleResetFilters } = - useFiltersContext(); + const { + handleResetFilters, + filters: { attribute, token, chain }, + } = useFiltersContext(); const tokenTypesFilters = useGetTokenTypeFilters(); - const tokenItems = tokenTypesFilters.map(({ token }) => ({ - symbol: token.symbol, - name: token?.tokenAddress || '', + const tokenItems = tokenTypesFilters.map(({ token: tokenTypeFilter }) => ({ + symbol: tokenTypeFilter.symbol, + name: tokenTypeFilter?.tokenAddress || '', })); const activeFiltersToDisplay = useMemo(() => { return [ - ...(Object.values(tokenTypes).some((value) => value === true) + ...(Object.values(token).some(({ isChecked }) => isChecked) ? [ { filter: FiltersValues.TokenType, @@ -27,12 +31,12 @@ export const useActiveFilters = () => { id: 'balancePage.filter.type', }), items: tokenItems - .filter(({ name }) => tokenTypes[name]) + .filter(({ name }) => token[name]?.isChecked) .map(({ symbol }) => symbol), }, ] : []), - ...(Object.values(attributeFilters).some((value) => value === true) + ...(Object.values(attribute).some(({ isChecked }) => isChecked) ? [ { filter: FiltersValues.Attributes, @@ -40,13 +44,26 @@ export const useActiveFilters = () => { id: 'balancePage.filter.attributes', }), items: ATTRIBUTE_FILTERS.filter( - ({ name }) => attributeFilters[name], + ({ name }) => attribute[name]?.isChecked, ).map(({ label }) => label), }, ] : []), + ...(Object.values(chain).some(({ isChecked }) => isChecked) + ? [ + { + filter: FiltersValues.Chain, + category: formatText({ + id: 'balancePage.filter.chain', + }), + items: getObjectKeys(chain).filter( + (chainKey) => chain[chainKey]?.isChecked, + ), + }, + ] + : []), ]; - }, [attributeFilters, tokenTypes, tokenItems]); + }, [token, tokenItems, attribute, chain]); return { activeFiltersToDisplay, handleResetFilters }; }; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/AttributeFilters/AttributeFilters.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/AttributeFilters/AttributeFilters.tsx index 42839001b97..e03a5fc550b 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/AttributeFilters/AttributeFilters.tsx +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/AttributeFilters/AttributeFilters.tsx @@ -7,7 +7,7 @@ import Checkbox from '~v5/common/Checkbox/index.ts'; import { ATTRIBUTE_FILTERS } from './consts.ts'; const AttributeFilters: FC = () => { - const { attributeFilters, handleAttributeFilterChange } = useFiltersContext(); + const { handleFiltersChange, filters } = useFiltersContext(); return (
    @@ -16,14 +16,16 @@ const AttributeFilters: FC = () => {
      {ATTRIBUTE_FILTERS.map(({ label, name }) => { - const isChecked = attributeFilters[name]; + const { isChecked } = filters.attribute[name] ?? {}; return (
    • { + handleFiltersChange(event, 'attribute'); + }} isChecked={isChecked} > {label} diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/ChainFilters/ChainFilters.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/ChainFilters/ChainFilters.tsx new file mode 100644 index 00000000000..18cbccd4f04 --- /dev/null +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/ChainFilters/ChainFilters.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +import { useFiltersContext } from '~frame/v5/pages/BalancePage/partials/BalanceTable/Filters/FiltersContext/index.ts'; +import { formatText } from '~utils/intl.ts'; +import { useChainOptions } from '~v5/common/ActionSidebar/partials/ChainSelect/hooks.ts'; +import Checkbox from '~v5/common/Checkbox/index.ts'; + +const ChainFilters = () => { + const { filters, handleFiltersChange } = useFiltersContext(); + + const chainOptions = useChainOptions({ + includeDefaultChain: true, + onlyShowActiveChains: true, + }); + + return ( +
      +
      + {formatText({ id: 'balancePage.filter.availableChains' })} +
      +
        + {chainOptions.map(({ label, icon: Icon, value }) => { + const { isChecked } = filters.chain[label] ?? {}; + + return ( +
      • + handleFiltersChange(event, 'chain')} + isChecked={isChecked} + > + {Icon && } + {label} + +
      • + ); + })} +
      +
      + ); +}; + +export default ChainFilters; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/ChainFilters/index.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/ChainFilters/index.ts new file mode 100644 index 00000000000..d18b1b36e66 --- /dev/null +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/ChainFilters/index.ts @@ -0,0 +1 @@ +export { default } from './ChainFilters.tsx'; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/TokenFilters/TokenFilters.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/TokenFilters/TokenFilters.tsx index bc90067d372..e5da128dd84 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/TokenFilters/TokenFilters.tsx +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/TokenFilters/TokenFilters.tsx @@ -9,23 +9,39 @@ import { TokenAvatar } from '~v5/shared/TokenAvatar/TokenAvatar.tsx'; import { useGetTokenTypeFilters } from './hooks.ts'; const TokenFilters: FC = () => { - const { tokenTypes, handleTokenTypesFilterChange } = useFiltersContext(); + const { + filters: { token: tokenFilters }, + handleFiltersChange, + } = useFiltersContext(); + const tokenTypesFilters = useGetTokenTypeFilters(); - const tokenItems = tokenTypesFilters.map(({ token }) => ({ - symbol: token.symbol, - label: ( -
      - - {multiLineTextEllipsis(token.symbol, 5)} -
      - ), - name: token?.tokenAddress || '', - })); + + const tokenItems = tokenTypesFilters.map( + ({ + token: { + symbol, + name, + tokenAddress, + avatar, + chainMetadata: { chainId }, + }, + }) => ({ + symbol, + label: ( +
      + + {multiLineTextEllipsis(symbol, 5)} +
      + ), + name: tokenAddress || '', + key: `${chainId}-${symbol}`, + }), + ); return (
      @@ -33,15 +49,15 @@ const TokenFilters: FC = () => { {formatText({ id: 'balancePage.filter.approvedTokenTypes' })}
        - {tokenItems.map(({ label, name }) => { - const isChecked = tokenTypes[name]; + {tokenItems.map(({ label, name, key }) => { + const { isChecked } = tokenFilters[name] ?? {}; return ( -
      • +
      • handleFiltersChange(event, 'token')} isChecked={isChecked} > {label} diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/index.ts b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/index.ts new file mode 100644 index 00000000000..1c10083b371 --- /dev/null +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/Filters/partials/filters/index.ts @@ -0,0 +1,9 @@ +import AttributeFilters from './AttributeFilters/index.ts'; +import ChainFilters from './ChainFilters/index.ts'; +import TokenFilters from './TokenFilters/index.ts'; + +export const BalancePageFilter = { + Token: TokenFilters, + Attribute: AttributeFilters, + Chain: ChainFilters, +}; diff --git a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/hooks.tsx b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/hooks.tsx index 702802c0906..2e0ce11f941 100644 --- a/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/hooks.tsx +++ b/src/components/frame/v5/pages/BalancePage/partials/BalanceTable/hooks.tsx @@ -16,6 +16,7 @@ import Numeral from '~shared/Numeral/index.ts'; import { type NativeTokenStatus, type Token } from '~types/graphql.ts'; import { notNull } from '~utils/arrays/index.ts'; import { formatText } from '~utils/intl.ts'; +import { getObjectValues } from '~utils/objects/index.ts'; import { multiLineTextEllipsis } from '~utils/strings.ts'; import { getBalanceForTokenAndDomain, @@ -37,10 +38,16 @@ export const useBalancesData = (): BalanceTableFieldModel[] => { colony: { tokens: colonyTokens, balances, nativeToken }, } = useColonyContext(); const selectedDomain = useGetSelectedDomainFilter(); - const { attributeFilters, tokenTypes, searchFilter } = useFiltersContext(); + const { filters, searchFilter } = useFiltersContext(); const { balancesByToken: expenditureBalances, loading } = useColonyExpenditureBalances(); + const { + attribute: attributeFilters, + token: tokenFilters, + chain: chainFilters, + } = filters; + const tokensData = useMemo( () => colonyTokens?.items.filter(notNull).map((item) => { @@ -77,30 +84,34 @@ export const useBalancesData = (): BalanceTableFieldModel[] => { ], ); - const filteredTokens = tokensData?.filter((token) => { - if (attributeFilters.native) { - if ( - Object.values(tokenTypes).some((tokenTypeFilter) => tokenTypeFilter) - ) { + const filteredTokens = tokensData?.filter(({ token: { tokenAddress } }) => { + if (attributeFilters.native.isChecked) { + if (getObjectValues(tokenFilters).some(({ isChecked }) => isChecked)) { return ( - token.token?.tokenAddress === nativeToken.tokenAddress && - tokenTypes[token.token?.tokenAddress || 0] + tokenAddress === nativeToken.tokenAddress && + tokenFilters[tokenAddress || 0] ); } - return token.token?.tokenAddress === nativeToken.tokenAddress; + return tokenAddress === nativeToken.tokenAddress; } - if (Object.values(tokenTypes).some((tokenTypeFilter) => tokenTypeFilter)) { - return tokenTypes[token.token?.tokenAddress || 0]; + if (getObjectValues(tokenFilters).some(({ isChecked }) => isChecked)) { + return tokenFilters[tokenAddress]?.isChecked; } return true; }); - const searchedTokens = filteredTokens?.filter( - ({ token }) => - token?.name.toLowerCase().includes(searchFilter.toLowerCase()) || - token?.symbol.toLowerCase().includes(searchFilter.toLowerCase()), + const searchedChainIds = getObjectValues(chainFilters) + .filter(({ isChecked }) => isChecked) + .map(({ id }) => id); + + const searchedTokens = filteredTokens?.filter(({ token }) => + searchedChainIds.length + ? searchedChainIds.includes(token.chainMetadata.chainId) + : true && + (token?.name.toLowerCase().includes(searchFilter.toLowerCase()) || + token?.symbol.toLowerCase().includes(searchFilter.toLowerCase())), ); const sortedTokens = useMemo( diff --git a/src/components/frame/v5/pages/FundsPage/partials/Filter/Filter.tsx b/src/components/frame/v5/pages/FundsPage/partials/Filter/Filter.tsx index 07230657eac..f24c29352af 100644 --- a/src/components/frame/v5/pages/FundsPage/partials/Filter/Filter.tsx +++ b/src/components/frame/v5/pages/FundsPage/partials/Filter/Filter.tsx @@ -55,19 +55,22 @@ function Filter({ [onSearch], ); - const RootItems = rootItems.map(({ icon, items, label, name, title }) => ( - - )); + const RootItems = rootItems.map( + ({ icon, items, label, name, title, containerClassName }) => ( + + ), + ); const filterCount = Object.values(value).reduce((acc, obj) => { return acc + Object.values(obj).filter((item) => item === true).length; diff --git a/src/components/frame/v5/pages/FundsPage/partials/Filter/FilterItem.tsx b/src/components/frame/v5/pages/FundsPage/partials/Filter/FilterItem.tsx index b41d1de5a8f..920061ff55f 100644 --- a/src/components/frame/v5/pages/FundsPage/partials/Filter/FilterItem.tsx +++ b/src/components/frame/v5/pages/FundsPage/partials/Filter/FilterItem.tsx @@ -1,4 +1,5 @@ import { CaretDown } from '@phosphor-icons/react'; +import clsx from 'clsx'; import React from 'react'; import { usePopperTooltip } from 'react-popper-tooltip'; @@ -18,6 +19,7 @@ function FilterItem({ path, value, title, + containerClassName, }: RootFilterProps) { const { getTooltipProps, setTooltipRef, setTriggerRef, visible } = usePopperTooltip({ @@ -77,7 +79,10 @@ function FilterItem({ hasShadow: true, className: 'py-6 px-2', }} - className="mr-2 w-full sm:max-w-[13.25rem]" + className={clsx( + 'mr-2 w-full sm:max-w-[13.25rem]', + containerClassName, + )} > <> diff --git a/src/components/frame/v5/pages/FundsPage/partials/Filter/types.ts b/src/components/frame/v5/pages/FundsPage/partials/Filter/types.ts index 39e5dae12c1..f3f2ddbb6b5 100644 --- a/src/components/frame/v5/pages/FundsPage/partials/Filter/types.ts +++ b/src/components/frame/v5/pages/FundsPage/partials/Filter/types.ts @@ -10,6 +10,7 @@ export type FilterValue = { export interface NestedItem { label: ReactNode; name: KeysAtLevel; + icon?: Icon; symbol?: string; // @ts-ignore items?: NestedItem>[]; @@ -22,6 +23,7 @@ export interface RootItem { icon: Icon; title: ReactNode; filterName: string; + containerClassName?: string; } export interface FilterProps { diff --git a/src/components/frame/v5/pages/FundsPage/partials/FundsTable/hooks.tsx b/src/components/frame/v5/pages/FundsPage/partials/FundsTable/hooks.tsx index 8f170d837ed..89140afae55 100644 --- a/src/components/frame/v5/pages/FundsPage/partials/FundsTable/hooks.tsx +++ b/src/components/frame/v5/pages/FundsPage/partials/FundsTable/hooks.tsx @@ -1,4 +1,4 @@ -import { CoinVertical, ShieldCheck } from '@phosphor-icons/react'; +import { CoinVertical, CubeFocus, ShieldCheck } from '@phosphor-icons/react'; import { type ColumnDef, createColumnHelper } from '@tanstack/react-table'; import { isEqual } from 'lodash'; import React, { useMemo, useState } from 'react'; @@ -10,6 +10,7 @@ import { notNull } from '~utils/arrays/index.ts'; import { formatText } from '~utils/intl.ts'; import { multiLineTextEllipsis } from '~utils/strings.ts'; import { formatMessage } from '~utils/yup/tests/helpers.ts'; +import { useChainOptions } from '~v5/common/ActionSidebar/partials/ChainSelect/hooks.ts'; import { TokenAvatar } from '~v5/shared/TokenAvatar/TokenAvatar.tsx'; import { type FilterProps } from '../Filter/types.ts'; @@ -49,6 +50,12 @@ export const useFundsTableColumns = (): ColumnDef< export const useFundsTable = (): UseFundsTableProps => { const { colony } = useColonyContext(); const claims = useColonyFundsClaims(); + + const validChainFilters = useChainOptions({ + includeDefaultChain: true, + onlyShowActiveChains: true, + }); + const colonyTokens = useMemo( () => colony.tokens?.items.filter(notNull).sort((a, b) => { @@ -149,11 +156,16 @@ export const useFundsTable = (): UseFundsTableProps => { return true; }); - const searchedTokens = visibleTokens.filter( - ({ token }) => - token?.name.toLowerCase().includes(searchValue.toLowerCase()) || - token?.symbol.toLowerCase().includes(searchValue.toLowerCase()), - ); + const chainFilters = validChainFilters.map(({ icon: Icon, label }) => ({ + name: label, + symbol: label, + label: ( +
        + {Icon && } + {label} +
        + ), + })); const tokenTypeFilters = colonyTokens.map(({ token }) => ({ name: token.tokenAddress, @@ -214,6 +226,15 @@ export const useFundsTable = (): UseFundsTableProps => { }, ], }, + { + name: 'chain', + filterName: formatText({ id: 'incomingFundsPage.filter.chain' }), + label: formatText({ id: 'incomingFundsPage.filter.chain' }), + icon: CubeFocus, + title: formatText({ id: 'incomingFundsPage.filter.availableChains' }), + items: chainFilters, + containerClassName: 'sm:max-w-full', + }, ], }; @@ -244,6 +265,30 @@ export const useFundsTable = (): UseFundsTableProps => { }) .filter(Boolean); + const searchedTokens = useMemo(() => { + const searchedChains = activeFilters.find( + (activeFilter) => activeFilter?.filterName === 'Chain', + )?.filters; + + const searchedChainIds = validChainFilters + .filter(({ label }) => searchedChains?.includes(label)) + ?.map(({ value }) => value); + + const searchedTokensByChainIds = visibleTokens.filter(({ token }) => + searchedChainIds?.length && token + ? searchedChainIds.includes(token?.chainMetadata.chainId) + : true, + ); + + const searchResults = searchedTokensByChainIds.filter( + ({ token }) => + token?.name.toLowerCase().includes(searchValue.toLowerCase()) || + token?.symbol.toLowerCase().includes(searchValue.toLowerCase()), + ); + + return searchResults; + }, [activeFilters, searchValue, validChainFilters, visibleTokens]); + return { filters, searchedTokens, diff --git a/src/components/frame/v5/pages/FundsPage/partials/FundsTable/types.ts b/src/components/frame/v5/pages/FundsPage/partials/FundsTable/types.ts index 64aa5b89dea..caf502745d1 100644 --- a/src/components/frame/v5/pages/FundsPage/partials/FundsTable/types.ts +++ b/src/components/frame/v5/pages/FundsPage/partials/FundsTable/types.ts @@ -20,6 +20,9 @@ export type FundsTableFilters = { type: { [key: string]: boolean; }; + chain: { + [key: string]: boolean; + }; }; export interface UseFundsTableProps { diff --git a/src/components/v5/common/ActionSidebar/partials/ChainSelect/ChainSelect.tsx b/src/components/v5/common/ActionSidebar/partials/ChainSelect/ChainSelect.tsx index ee30ece4043..366cf47c405 100644 --- a/src/components/v5/common/ActionSidebar/partials/ChainSelect/ChainSelect.tsx +++ b/src/components/v5/common/ActionSidebar/partials/ChainSelect/ChainSelect.tsx @@ -9,12 +9,8 @@ import { formatText } from '~utils/intl.ts'; import ChainBadge from '~v5/common/Pills/ChainBadge/ChainBadge.tsx'; import { renderIconOption } from '~v5/shared/SearchSelect/partials/OptionRenderer/IconOptionRenderer.tsx'; import SearchSelect from '~v5/shared/SearchSelect/SearchSelect.tsx'; -import { - type SearchSelectOption, - type IconOption, -} from '~v5/shared/SearchSelect/types.ts'; -import { useChainOptions } from './hooks.ts'; +import { type IUseChainOptions, useChainOptions } from './hooks.ts'; const displayName = 'v5.common.ActionsContent.partials.ChainSelect'; @@ -22,7 +18,7 @@ interface ChainSelectProps { name: string; disabled?: boolean; readOnly?: boolean; - filterOptionsFn?: (option: SearchSelectOption) => boolean; + filterOptionsFn?: IUseChainOptions['filterOptionsFn']; } const ChainSelect: FC = ({ @@ -39,7 +35,7 @@ const ChainSelect: FC = ({ }); const fieldValue = field.value; const isError = !!error; - const chainOptions = useChainOptions(filterOptionsFn); + const chainOptions = useChainOptions({ filterOptionsFn }); const { readonly } = useAdditionalFormOptionsContext(); const isReadOnly = readonly || readOnlyProp; diff --git a/src/components/v5/common/ActionSidebar/partials/ChainSelect/hooks.ts b/src/components/v5/common/ActionSidebar/partials/ChainSelect/hooks.ts index 7cdbcb696b7..e7091124301 100644 --- a/src/components/v5/common/ActionSidebar/partials/ChainSelect/hooks.ts +++ b/src/components/v5/common/ActionSidebar/partials/ChainSelect/hooks.ts @@ -1,33 +1,73 @@ import { useMemo } from 'react'; +import { DEFAULT_CHAIN_CONFIG, type IChainConfig } from '~constants'; import { SUPPORTED_CHAINS } from '~constants/proxyColonies.ts'; import { useGetSupportedChainsQuery } from '~gql'; -import GanacheIcon from '~icons/GanacheIcon.tsx'; +import { useDeployedChainIds } from '~hooks/proxyColonies/useDeployedChainIds.ts'; import { notNull } from '~utils/arrays/index.ts'; +import { + type IconOption, + type SearchSelectOption, +} from '~v5/shared/SearchSelect/types.ts'; + +export interface IUseChainOptions { + filterOptionsFn?: (options: SearchSelectOption) => boolean; + onlyShowActiveChains?: boolean; + includeDefaultChain?: boolean; +} + +export const useChainOptions = (args: IUseChainOptions = {}) => { + const { filterOptionsFn, includeDefaultChain, onlyShowActiveChains } = args; + + const deployedChainIds = useDeployedChainIds({ + filterFn: (deployedProxyColony) => deployedProxyColony?.isActive, + skip: !onlyShowActiveChains, + }); -export const useChainOptions = (filterOptionsFn) => { const { data } = useGetSupportedChainsQuery(); + const supportedChains = data?.listSupportedChains?.items; + const chainOptions = useMemo(() => { - return (data?.listSupportedChains?.items ?? []) + if (!supportedChains) { + return []; + } + + let options: IChainConfig[] = supportedChains .map((chain) => { const chainConfig = SUPPORTED_CHAINS.find( (supportedChain) => supportedChain.chainId === chain?.id, ); + if (!chainConfig) { return null; } return { - icon: chainConfig.icon ?? GanacheIcon, + icon: chainConfig.icon ?? DEFAULT_CHAIN_CONFIG.icon, isDisabled: !chain?.isActive, value: chainConfig.chainId, label: chainConfig.shortName, }; }) - .filter(notNull) - .filter((option) => filterOptionsFn && filterOptionsFn(option)); - }, [data, filterOptionsFn]); + .filter(notNull); + + if (onlyShowActiveChains) { + options = options.filter(({ value }) => deployedChainIds.includes(value)); + } + + if (filterOptionsFn) { + options = options.filter((option) => filterOptionsFn(option)); + } + + return [...options, ...(includeDefaultChain ? [DEFAULT_CHAIN_CONFIG] : [])]; + }, [ + deployedChainIds, + filterOptionsFn, + includeDefaultChain, + onlyShowActiveChains, + supportedChains, + ]); return chainOptions; }; diff --git a/src/components/v5/common/Checkbox/Checkbox.tsx b/src/components/v5/common/Checkbox/Checkbox.tsx index f2752055662..6c338efe8f3 100644 --- a/src/components/v5/common/Checkbox/Checkbox.tsx +++ b/src/components/v5/common/Checkbox/Checkbox.tsx @@ -16,7 +16,7 @@ const Checkbox: FC> = ({ label = '', onChange, className, - isChecked, + isChecked = false, children, }) => { const generatedId = useId(); diff --git a/src/constants/index.ts b/src/constants/index.ts index 4c19ad8e77b..abc966d2275 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -309,6 +309,20 @@ export const DEFAULT_NETWORK_TOKEN = TOKEN_DATA[DEFAULT_NETWORK]; export const DEFAULT_NETWORK_INFO = NETWORK_DATA[DEFAULT_NETWORK]; +export interface IChainConfig { + icon: Icon; + value: string; + label: string; + isDisabled: boolean; +} + +export const DEFAULT_CHAIN_CONFIG: IChainConfig = { + icon: DEFAULT_NETWORK_INFO.icon ?? GanacheIcon, + value: DEFAULT_NETWORK_INFO.chainId, + label: DEFAULT_NETWORK_INFO.shortName, + isDisabled: false, +}; + /* * List all networks that curently support metatransactions */ diff --git a/src/graphql/generated.ts b/src/graphql/generated.ts index b146bbed30c..bf13c3549d0 100644 --- a/src/graphql/generated.ts +++ b/src/graphql/generated.ts @@ -650,8 +650,6 @@ export enum ColonyActionType { AddVerifiedMembers = 'ADD_VERIFIED_MEMBERS', AddVerifiedMembersMotion = 'ADD_VERIFIED_MEMBERS_MOTION', AddVerifiedMembersMultisig = 'ADD_VERIFIED_MEMBERS_MULTISIG', - /** An action related to arbitrary transaction */ - ArbitraryTx = 'ARBITRARY_TX', /** An action related to canceling an expenditure */ CancelExpenditure = 'CANCEL_EXPENDITURE', /** An action related to a motion to cancel an expenditure */ diff --git a/src/hooks/proxyColonies/useDeployedChainIds.ts b/src/hooks/proxyColonies/useDeployedChainIds.ts index 359c6cc25c3..00b5c9fffcc 100644 --- a/src/hooks/proxyColonies/useDeployedChainIds.ts +++ b/src/hooks/proxyColonies/useDeployedChainIds.ts @@ -1,11 +1,13 @@ import { useColonyContext } from '~context/ColonyContext/ColonyContext.ts'; -import { type ProxyColony, useGetProxyColoniesQuery } from '~gql'; +import { useGetProxyColoniesQuery, type ProxyColony } from '~gql'; import { notNull } from '~utils/arrays/index.ts'; export const useDeployedChainIds = ({ filterFn = notNull, + skip = false, }: { filterFn: (x: ProxyColony) => boolean; + skip?: boolean; }) => { const { colony } = useColonyContext(); const { data } = useGetProxyColoniesQuery({ @@ -13,6 +15,7 @@ export const useDeployedChainIds = ({ colonyAddress: colony.colonyAddress, }, fetchPolicy: 'network-only', + skip, }); const deployedProxyColonies = data?.getProxyColoniesByColonyAddress?.items || []; diff --git a/src/hooks/useActivityFeed/helpers.ts b/src/hooks/useActivityFeed/helpers.ts index 0a8d997d273..33834645bc2 100644 --- a/src/hooks/useActivityFeed/helpers.ts +++ b/src/hooks/useActivityFeed/helpers.ts @@ -166,7 +166,13 @@ export const getBaseSearchActionsFilterVariable = ( export const getSearchActionsFilterVariable = ( colonyAddress: string, - { dateFrom, dateTo, decisionMethods, teamId }: ActivityFeedFilters = {}, + { + dateFrom, + dateTo, + decisionMethods, + teamId, + chainIds, + }: ActivityFeedFilters = {}, ): SearchActionsFilterVariable => { const dateFilter = dateFrom && dateTo @@ -191,6 +197,7 @@ export const getSearchActionsFilterVariable = ( } : {}), }; + const decisionMethodFilter = decisionMethods && !!decisionMethods.length ? { @@ -229,11 +236,20 @@ export const getSearchActionsFilterVariable = ( } : undefined; + const chainFilters = chainIds?.length + ? { + or: chainIds.map((chainId) => ({ + targetChainId: { eq: Number(chainId) }, + })), + } + : {}; + return { ...getBaseSearchActionsFilterVariable(colonyAddress), ...(teamId !== undefined ? { fromDomainId: { eq: teamId } } : {}), ...dateFilter, ...(decisionMethodFilter || {}), + ...chainFilters, }; }; diff --git a/src/hooks/useActivityFeed/types.ts b/src/hooks/useActivityFeed/types.ts index 4539195d506..f3a9b61ca1e 100644 --- a/src/hooks/useActivityFeed/types.ts +++ b/src/hooks/useActivityFeed/types.ts @@ -21,6 +21,7 @@ export interface ActivityFeedFilters { dateTo?: Date; decisionMethods?: ActivityDecisionMethod[]; search?: string; + chainIds?: string[]; } export type ActivityFeedSort = SearchActionsQueryVariables['sort']; diff --git a/src/hooks/useActivityFeed/useActivityFeed.ts b/src/hooks/useActivityFeed/useActivityFeed.ts index 11dc9283499..3161adaa7ba 100644 --- a/src/hooks/useActivityFeed/useActivityFeed.ts +++ b/src/hooks/useActivityFeed/useActivityFeed.ts @@ -56,6 +56,8 @@ const useActivityFeed = ( setPageNumber(1); }, [stringifiedFilters]); + const filter = getSearchActionsFilterVariable(colonyAddress, filters); + const { data, fetchMore, @@ -63,7 +65,7 @@ const useActivityFeed = ( refetch: refetchActions, } = useSearchActionsQuery({ variables: { - filter: getSearchActionsFilterVariable(colonyAddress, filters), + filter, sort: sort || { field: SearchableColonyActionSortableFields.CreatedAt, direction: SearchableSortDirection.Desc, diff --git a/src/i18n/en.json b/src/i18n/en.json index 0c80fa8f21a..95f9d180c29 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -30,7 +30,6 @@ "navigation.finances.balance": "Balance", "navigation.finances.balances": "Balances", "navigation.finances.incoming": "Incoming", - "navigation.finances.incomingFunds": "Incoming funds", "navigation.finances.transactions": "Transactions", "navigation.finances.streamingPayments": "Streaming payments", "navigation.agreements": "Agreements", @@ -1574,11 +1573,13 @@ "incomingFundsPage.table.claim": "Claim", "incomingFundsPage.table.accepted": "Accepted", "incomingFundsPage.table.new": "New", + "incomingFundsPage.filter.chain": "Chain", "incomingFundsPage.table.claimAllFunds": "Accept all available funds", "incomingFundsPage.filter.tokenStatus": "Token status", "incomingFundsPage.filter.tokenType": "Token type", "incomingFundsPage.filter.approvedTokens": "approved token types", "incomingFundsPage.filter.approved": "Approved", + "incomingFundsPage.filter.availableChains": "Available chains", "incomingFundsPage.filter.unapproved": "Unapproved", "incomingFundsPage.filter.status": "Status", "incomingFundsPage.filter.type": "Type", @@ -1727,6 +1728,8 @@ "balancePage.table.addFunds": "Add funds to the Colony", "balancePage.table.emptyTitle": "No results to display", "balancePage.table.emptyDescription": "There are no tokens in the Colony that match your search. Try searching again", + "balancePage.filter.availableChains": "Available chains", + "balancePage.filter.chain": "Chain", "balancePage.filter.searchModalTitle": "Search balance activity", "balancePage.filter.search": "Search tokens", "balancePage.filter.tokenType": "Token type", @@ -1774,6 +1777,8 @@ "activityFeedTable.table.date": "

        Date:

        {date}", "activityFeedTable.table.status": "

        Status:

        {statusBadge}", "activityFeedTable.filters.actionType": "Action type", + "activityFeedTable.filters.availableChains": "Available chains", + "activityFeedTable.filters.chain": "Chain", "activityFeedTable.filters.date": "Date", "activityFeedTable.filters.date.pastHour": "Past hour", "activityFeedTable.filters.date.pastDay": "Past 24 hours", diff --git a/src/utils/objects/index.ts b/src/utils/objects/index.ts index badd003fe82..81ae6077a15 100644 --- a/src/utils/objects/index.ts +++ b/src/utils/objects/index.ts @@ -20,6 +20,12 @@ export const getObjectKeys = (obj: T): Array => { return Object.keys(obj) as Array; }; +export const getObjectValues = ( + obj: T, +): Array => { + return Object.values(obj) as Array; +}; + /** * Removes specified fields from an object and returns the updated object. *