From ae03e4ffc6a25c8a3e3c61701180fdc1ea194141 Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Thu, 19 Sep 2024 17:34:08 +0200
Subject: [PATCH] Update directory page options to use URL params (#31977)

---
 app/javascript/hooks/useSearchParam.ts        | 31 ++++++++++++++++
 .../mastodon/features/directory/index.tsx     | 35 +++++++++++--------
 2 files changed, 51 insertions(+), 15 deletions(-)
 create mode 100644 app/javascript/hooks/useSearchParam.ts

diff --git a/app/javascript/hooks/useSearchParam.ts b/app/javascript/hooks/useSearchParam.ts
new file mode 100644
index 000000000..2df8c0b3a
--- /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 51d283a48..d0e57600b 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<Column>(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<ChangeEventHandler<HTMLInputElement>>(
@@ -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(() => {