diff --git a/app/javascript/hooks/useSearchParam.ts b/app/javascript/hooks/useSearchParam.ts new file mode 100644 index 00000000000..2df8c0b3a9e --- /dev/null +++ b/app/javascript/hooks/useSearchParam.ts @@ -0,0 +1,31 @@ +import { useMemo, useCallback } from 'react'; + +import { useLocation, useHistory } from 'react-router'; + +export function useSearchParams() { + const { search } = useLocation(); + + return useMemo(() => new URLSearchParams(search), [search]); +} + +export function useSearchParam(name: string, defaultValue?: string) { + const searchParams = useSearchParams(); + const history = useHistory(); + + const value = searchParams.get(name) ?? defaultValue; + + const setValue = useCallback( + (value: string | null) => { + if (value === null) { + searchParams.delete(name); + } else { + searchParams.set(name, value); + } + + history.push({ search: searchParams.toString() }); + }, + [history, name, searchParams], + ); + + return [value, setValue] as const; +} diff --git a/app/javascript/mastodon/features/directory/index.tsx b/app/javascript/mastodon/features/directory/index.tsx index 51d283a482d..d0e57600bb8 100644 --- a/app/javascript/mastodon/features/directory/index.tsx +++ b/app/javascript/mastodon/features/directory/index.tsx @@ -1,5 +1,5 @@ import type { ChangeEventHandler } from 'react'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -23,6 +23,8 @@ import { RadioButton } from 'mastodon/components/radio_button'; import ScrollContainer from 'mastodon/containers/scroll_container'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; +import { useSearchParam } from '../../../hooks/useSearchParam'; + import { AccountCard } from './components/account_card'; const messages = defineMessages({ @@ -47,18 +49,19 @@ export const Directory: React.FC<{ const intl = useIntl(); const dispatch = useAppDispatch(); - const [state, setState] = useState<{ - order: string | null; - local: boolean | null; - }>({ - order: null, - local: null, - }); - const column = useRef(null); - const order = state.order ?? params?.order ?? 'active'; - const local = state.local ?? params?.local ?? false; + const [orderParam, setOrderParam] = useSearchParam('order'); + const [localParam, setLocalParam] = useSearchParam('local'); + + let localParamBool: boolean | undefined; + + if (localParam === 'false') { + localParamBool = false; + } + + const order = orderParam ?? params?.order ?? 'active'; + const local = localParamBool ?? params?.local ?? true; const handlePin = useCallback(() => { if (columnId) { @@ -101,10 +104,10 @@ export const Directory: React.FC<{ if (columnId) { dispatch(changeColumnParams(columnId, ['order'], e.target.value)); } else { - setState((s) => ({ order: e.target.value, local: s.local })); + setOrderParam(e.target.value); } }, - [dispatch, columnId], + [dispatch, columnId, setOrderParam], ); const handleChangeLocal = useCallback>( @@ -113,11 +116,13 @@ export const Directory: React.FC<{ dispatch( changeColumnParams(columnId, ['local'], e.target.value === '1'), ); + } else if (e.target.value === '1') { + setLocalParam('true'); } else { - setState((s) => ({ local: e.target.value === '1', order: s.order })); + setLocalParam('false'); } }, - [dispatch, columnId], + [dispatch, columnId, setLocalParam], ); const handleLoadMore = useCallback(() => {