diff --git a/components/mod-event/EventList.tsx b/components/mod-event/EventList.tsx
index fe7e18f0..091cb9ff 100644
--- a/components/mod-event/EventList.tsx
+++ b/components/mod-event/EventList.tsx
@@ -64,9 +64,7 @@ export const ModEventList = (
) => {
const {
types,
- setTypes,
includeAllUserRecords,
- setIncludeAllUserRecords,
modEvents,
fetchMoreModEvents,
hasMoreModEvents,
@@ -76,15 +74,12 @@ export const ModEventList = (
toggleCommentFilter,
setCommentFilterKeyword,
createdBy,
- setCreatedBy,
subject,
- setSubject,
oldestFirst,
- setOldestFirst,
createdAfter,
- setCreatedAfter,
createdBefore,
- setCreatedBefore,
+ changeListFilter,
+ resetListFilters
} = useModEventList(props)
const [showFiltersPanel, setShowFiltersPanel] = useState(false)
@@ -100,7 +95,11 @@ export const ModEventList = (
{...{
subjectTitle,
includeAllUserRecords,
- setIncludeAllUserRecords,
+ setIncludeAllUserRecords: (value) =>
+ changeListFilter({
+ field: 'includeAllUserRecords',
+ value,
+ }),
isShowingEventsByCreator,
}}
/>
@@ -126,7 +125,9 @@ export const ModEventList = (
+ changeListFilter({ field: 'types', value })
+ }
/>
@@ -188,7 +189,12 @@ export const ModEventList = (
className="block w-full"
disabled={!!props.createdBy}
value={createdBy || ''}
- onChange={(ev) => setCreatedBy(ev.target.value)}
+ onChange={(ev) =>
+ changeListFilter({
+ field: 'createdBy',
+ value: ev.target.value,
+ })
+ }
autoComplete="off"
/>
@@ -206,7 +212,12 @@ export const ModEventList = (
placeholder="DID or AT-URI"
className="block w-full"
value={subject || ''}
- onChange={(ev) => setSubject(ev.target.value)}
+ onChange={(ev) =>
+ changeListFilter({
+ field: 'subject',
+ value: ev.target.value,
+ })
+ }
autoComplete="off"
/>
@@ -222,7 +233,12 @@ export const ModEventList = (
name="createdAfter"
className="block w-full"
value={createdAfter}
- onChange={(ev) => setCreatedAfter(ev.target.value)}
+ onChange={(ev) =>
+ changeListFilter({
+ field: 'createdAfter',
+ value: ev.target.value,
+ })
+ }
autoComplete="off"
min={FIRST_EVENT_TIMESTAMP}
max={new Date().toISOString().split('.')[0]}
@@ -240,7 +256,12 @@ export const ModEventList = (
name="createdBefore"
className="block w-full"
value={createdBefore}
- onChange={(ev) => setCreatedBefore(ev.target.value)}
+ onChange={(ev) =>
+ changeListFilter({
+ field: 'createdBefore',
+ value: ev.target.value,
+ })
+ }
autoComplete="off"
min={FIRST_EVENT_TIMESTAMP}
max={new Date().toISOString().split('.')[0]}
@@ -256,7 +277,9 @@ export const ModEventList = (
name="sortDirection"
className="flex items-center"
checked={oldestFirst}
- onChange={() => setOldestFirst((current) => !current)}
+ onChange={() =>
+ changeListFilter({ field: 'oldestFirst', value: !oldestFirst })
+ }
label="Show oldest events first (default: newest first)"
/>
@@ -269,7 +292,11 @@ export const ModEventList = (
No moderation events found.
{!!types.length && (
- setTypes([])}>
+ resetListFilters()}
+ >
Clear all filters
{' '}
to see all events
diff --git a/components/mod-event/useModEventList.tsx b/components/mod-event/useModEventList.tsx
index 837a90ac..a086effd 100644
--- a/components/mod-event/useModEventList.tsx
+++ b/components/mod-event/useModEventList.tsx
@@ -1,6 +1,6 @@
import { useInfiniteQuery } from '@tanstack/react-query'
import client from '@/lib/client'
-import { useContext, useEffect, useState } from 'react'
+import { useContext, useEffect, useReducer, useState } from 'react'
import { AuthContext } from '@/shell/AuthContext'
import { ComAtprotoAdminQueryModerationEvents } from '@atproto/api'
import { MOD_EVENT_TITLES } from './constants'
@@ -18,58 +18,95 @@ type CommentFilter = {
export const FIRST_EVENT_TIMESTAMP = '2022-11-01T00:00'
const allTypes = Object.keys(MOD_EVENT_TITLES)
+const initialListState = {
+ types: allTypes,
+ includeAllUserRecords: false,
+ commentFilter: { enabled: false, keyword: '' },
+ createdBy: undefined,
+ subject: undefined,
+ oldestFirst: false,
+ createdBefore: new Date().toISOString().split('.')[0],
+ createdAfter: FIRST_EVENT_TIMESTAMP,
+}
+
+// The 2 fields need overriding because in the initialState, they are set as undefined so the alternative string type is not accepted without override
+type EventListState = Omit & {
+ subject?: string
+ createdBy?: string
+}
+
+type EventListFilterPayload =
+ | { field: 'types'; value: string[] }
+ | { field: 'includeAllUserRecords'; value: boolean }
+ | { field: 'commentFilter'; value: CommentFilter }
+ | { field: 'createdBy'; value: string | undefined }
+ | { field: 'subject'; value: string | undefined }
+ | { field: 'oldestFirst'; value: boolean }
+ | { field: 'createdBefore'; value: string }
+ | { field: 'createdAfter'; value: string }
+
+type EventListAction =
+ | {
+ type: 'SET_FILTER'
+ payload: EventListFilterPayload
+ }
+ | {
+ type: 'RESET'
+ }
+
+const eventListReducer = (state: EventListState, action: EventListAction) => {
+ switch (action.type) {
+ case 'SET_FILTER':
+ return { ...state, [action.payload.field]: action.payload.value }
+ case 'RESET':
+ return initialListState
+ default:
+ return state
+ }
+}
export const useModEventList = (
props: { subject?: string; createdBy?: string } & ModEventListQueryOptions,
) => {
const { isLoggedIn } = useContext(AuthContext)
- const [createdAfter, setCreatedAfter] = useState(
- FIRST_EVENT_TIMESTAMP,
- )
- const [createdBefore, setCreatedBefore] = useState(
- new Date().toISOString().split('.')[0],
- )
- const [subject, setSubject] = useState(props.subject)
- const [createdBy, setCreatedBy] = useState(
- props.createdBy,
- )
- const [types, setTypes] = useState(allTypes)
- const [oldestFirst, setOldestFirst] = useState(false)
- const [commentFilter, setCommentFilter] = useState({
- enabled: false,
- keyword: '',
- })
- const [includeAllUserRecords, setIncludeAllUserRecords] =
- useState(false)
+ const [listState, dispatch] = useReducer(eventListReducer, initialListState)
+
+ const setCommentFilter = (value: CommentFilter) => {
+ dispatch({ type: 'SET_FILTER', payload: { field: 'commentFilter', value } })
+ }
useEffect(() => {
- if (props.subject !== subject) {
- setSubject(props.subject)
+ if (props.subject !== listState.subject) {
+ dispatch({
+ type: 'SET_FILTER',
+ payload: { field: 'subject', value: props.subject },
+ })
}
}, [props.subject])
useEffect(() => {
- if (props.createdBy !== createdBy) {
- setCreatedBy(props.createdBy)
+ if (props.createdBy !== listState.createdBy) {
+ dispatch({
+ type: 'SET_FILTER',
+ payload: { field: 'createdBy', value: props.createdBy },
+ })
}
}, [props.createdBy])
const results = useInfiniteQuery({
enabled: isLoggedIn,
- queryKey: [
- 'modEventList',
- {
- createdBy,
- subject,
+ queryKey: ['modEventList', { listState }],
+ queryFn: async ({ pageParam }) => {
+ const {
types,
includeAllUserRecords,
commentFilter,
+ createdBy,
+ subject,
oldestFirst,
- createdAfter,
createdBefore,
- },
- ],
- queryFn: async ({ pageParam }) => {
+ createdAfter,
+ } = listState
const queryParams: ComAtprotoAdminQueryModerationEvents.QueryParams = {
cursor: pageParam,
includeAllUserRecords,
@@ -93,7 +130,7 @@ export const useModEventList = (
const filterTypes = types.filter(Boolean)
if (filterTypes.length < allTypes.length && filterTypes.length > 0) {
- queryParams.types = allTypes
+ queryParams.types = filterTypes
}
if (oldestFirst) {
@@ -115,47 +152,41 @@ export const useModEventList = (
})
const hasFilter =
- (types.length > 0 &&
- types.length !== Object.keys(MOD_EVENT_TITLES).length) ||
- includeAllUserRecords ||
- commentFilter.enabled ||
- createdBy ||
- subject ||
- oldestFirst
+ (listState.types.length > 0 &&
+ listState.types.length !== allTypes.length) ||
+ listState.includeAllUserRecords ||
+ listState.commentFilter.enabled ||
+ listState.createdBy ||
+ listState.subject ||
+ listState.oldestFirst
return {
- types,
- setTypes,
- includeAllUserRecords,
- setIncludeAllUserRecords,
+ // Data from react-query
modEvents: results.data?.pages.map((page) => page.events).flat() || [],
fetchMoreModEvents: results.fetchNextPage,
hasMoreModEvents: results.hasNextPage,
refetchModEvents: results.refetch,
isInitialLoadingModEvents: results.isInitialLoading,
- hasFilter,
- commentFilter,
+
+ // Helper functions to mutate state
toggleCommentFilter: () => {
- setCommentFilter((prev) => {
- if (prev.enabled) {
- return { enabled: false, keyword: '' }
- }
- return { enabled: true, keyword: '' }
- })
+ if (listState.commentFilter.enabled) {
+ return setCommentFilter({ enabled: false, keyword: '' })
+ }
+ return setCommentFilter({ enabled: true, keyword: '' })
},
setCommentFilterKeyword: (keyword: string) => {
setCommentFilter({ enabled: true, keyword })
},
- createdBy,
- setCreatedBy,
- subject,
- setSubject,
- setOldestFirst,
- oldestFirst,
- createdBefore,
- setCreatedBefore,
- createdAfter,
- setCreatedAfter,
+ changeListFilter: (payload: EventListFilterPayload) =>
+ dispatch({ type: 'SET_FILTER', payload: payload }),
+ resetListFilters: () => dispatch({ type: 'RESET' }),
+
+ // State data
+ ...listState,
+
+ // Derived data from state
+ hasFilter,
}
}