Switch admin log to react-query
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
f06ec3469b
commit
771c9643dc
9 changed files with 71 additions and 118 deletions
|
@ -55,10 +55,6 @@ const ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST = 'ADMIN_STATUS_TOGGLE_SENSITIVITY
|
||||||
const ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS';
|
const ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS';
|
||||||
const ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL';
|
const ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL';
|
||||||
|
|
||||||
const ADMIN_LOG_FETCH_REQUEST = 'ADMIN_LOG_FETCH_REQUEST';
|
|
||||||
const ADMIN_LOG_FETCH_SUCCESS = 'ADMIN_LOG_FETCH_SUCCESS';
|
|
||||||
const ADMIN_LOG_FETCH_FAIL = 'ADMIN_LOG_FETCH_FAIL';
|
|
||||||
|
|
||||||
const ADMIN_USERS_TAG_REQUEST = 'ADMIN_USERS_TAG_REQUEST';
|
const ADMIN_USERS_TAG_REQUEST = 'ADMIN_USERS_TAG_REQUEST';
|
||||||
const ADMIN_USERS_TAG_SUCCESS = 'ADMIN_USERS_TAG_SUCCESS';
|
const ADMIN_USERS_TAG_SUCCESS = 'ADMIN_USERS_TAG_SUCCESS';
|
||||||
const ADMIN_USERS_TAG_FAIL = 'ADMIN_USERS_TAG_FAIL';
|
const ADMIN_USERS_TAG_FAIL = 'ADMIN_USERS_TAG_FAIL';
|
||||||
|
@ -423,19 +419,6 @@ const toggleStatusSensitivity = (id: string, sensitive: boolean) =>
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchModerationLog = (params?: Record<string, any>) =>
|
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
|
||||||
dispatch({ type: ADMIN_LOG_FETCH_REQUEST });
|
|
||||||
return api(getState)
|
|
||||||
.get('/api/v1/pleroma/admin/moderation_log', { params })
|
|
||||||
.then(({ data }) => {
|
|
||||||
dispatch({ type: ADMIN_LOG_FETCH_SUCCESS, items: data.items, total: data.total });
|
|
||||||
return data;
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch({ type: ADMIN_LOG_FETCH_FAIL, error });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const tagUsers = (accountIds: string[], tags: string[]) =>
|
const tagUsers = (accountIds: string[], tags: string[]) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = accountIdsToAccts(getState(), accountIds);
|
const nicknames = accountIdsToAccts(getState(), accountIds);
|
||||||
|
@ -707,9 +690,6 @@ export {
|
||||||
ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST,
|
ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST,
|
||||||
ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS,
|
ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS,
|
||||||
ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL,
|
ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL,
|
||||||
ADMIN_LOG_FETCH_REQUEST,
|
|
||||||
ADMIN_LOG_FETCH_SUCCESS,
|
|
||||||
ADMIN_LOG_FETCH_FAIL,
|
|
||||||
ADMIN_USERS_TAG_REQUEST,
|
ADMIN_USERS_TAG_REQUEST,
|
||||||
ADMIN_USERS_TAG_SUCCESS,
|
ADMIN_USERS_TAG_SUCCESS,
|
||||||
ADMIN_USERS_TAG_FAIL,
|
ADMIN_USERS_TAG_FAIL,
|
||||||
|
@ -757,7 +737,6 @@ export {
|
||||||
approveUsers,
|
approveUsers,
|
||||||
deleteStatus,
|
deleteStatus,
|
||||||
toggleStatusSensitivity,
|
toggleStatusSensitivity,
|
||||||
fetchModerationLog,
|
|
||||||
tagUsers,
|
tagUsers,
|
||||||
untagUsers,
|
untagUsers,
|
||||||
setTags,
|
setTags,
|
||||||
|
|
|
@ -3,6 +3,7 @@ export { useCreateRelay } from './useCreateRelay';
|
||||||
export { useDeleteDomain } from './useDeleteDomain';
|
export { useDeleteDomain } from './useDeleteDomain';
|
||||||
export { useDeleteRelay } from './useDeleteRelay';
|
export { useDeleteRelay } from './useDeleteRelay';
|
||||||
export { useDomains } from './useDomains';
|
export { useDomains } from './useDomains';
|
||||||
|
export { useModerationLog } from './useModerationLog';
|
||||||
export { useRelays } from './useRelays';
|
export { useRelays } from './useRelays';
|
||||||
export { useSuggest } from './useSuggest';
|
export { useSuggest } from './useSuggest';
|
||||||
export { useUpdateDomain } from './useUpdateDomain';
|
export { useUpdateDomain } from './useUpdateDomain';
|
||||||
|
|
42
src/api/hooks/admin/useModerationLog.ts
Normal file
42
src/api/hooks/admin/useModerationLog.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useApi } from 'soapbox/hooks';
|
||||||
|
import { moderationLogEntrySchema, type ModerationLogEntry } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
interface ModerationLogResult {
|
||||||
|
items: ModerationLogEntry[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const flattenPages = (pages?: ModerationLogResult[]): ModerationLogEntry[] => (pages || []).map(({ items }) => items).flat();
|
||||||
|
|
||||||
|
const useModerationLog = () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const getModerationLog = async (page: number): Promise<ModerationLogResult> => {
|
||||||
|
const { data } = await api.get<ModerationLogResult>('/api/v1/pleroma/admin/moderation_log', { params: { page } });
|
||||||
|
|
||||||
|
const normalizedData = data.items.map((domain) => moderationLogEntrySchema.parse(domain));
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: normalizedData,
|
||||||
|
total: data.total,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryInfo = useInfiniteQuery({
|
||||||
|
queryKey: ['moderation_log'],
|
||||||
|
queryFn: ({ pageParam }) => getModerationLog(pageParam),
|
||||||
|
initialPageParam: 1,
|
||||||
|
getNextPageParam: (page, allPages) => flattenPages(allPages)!.length >= page.total ? undefined : allPages.length + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = flattenPages(queryInfo.data?.pages);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...queryInfo,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useModerationLog };
|
|
@ -1,11 +1,11 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, FormattedDate, useIntl } from 'react-intl';
|
import { defineMessages, FormattedDate, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { fetchModerationLog } from 'soapbox/actions/admin';
|
import { useModerationLog } from 'soapbox/api/hooks/admin';
|
||||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||||
import { Column, Stack, Text } from 'soapbox/components/ui';
|
import { Column, Stack, Text } from 'soapbox/components/ui';
|
||||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
|
||||||
import { AdminLog } from 'soapbox/types/entities';
|
import type { ModerationLogEntry } from 'soapbox/schemas';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' },
|
heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' },
|
||||||
|
@ -14,37 +14,18 @@ const messages = defineMessages({
|
||||||
|
|
||||||
const ModerationLog = () => {
|
const ModerationLog = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const items = useAppSelector((state) => {
|
const {
|
||||||
return state.admin_log.index.map((i) => state.admin_log.items.get(String(i)));
|
data,
|
||||||
});
|
hasNextPage,
|
||||||
|
isLoading,
|
||||||
|
fetchNextPage,
|
||||||
|
} = useModerationLog();
|
||||||
|
|
||||||
const hasMore = useAppSelector((state) => state.admin_log.total - state.admin_log.index.count() > 0);
|
const showLoading = isLoading && data.length === 0;
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [lastPage, setLastPage] = useState(0);
|
|
||||||
|
|
||||||
const showLoading = isLoading && items.count() === 0;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(fetchModerationLog())
|
|
||||||
.then(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
setLastPage(1);
|
|
||||||
})
|
|
||||||
.catch(() => { });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
const handleLoadMore = () => {
|
||||||
const page = lastPage + 1;
|
fetchNextPage();
|
||||||
|
|
||||||
setIsLoading(true);
|
|
||||||
dispatch(fetchModerationLog({ page }))
|
|
||||||
.then(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
setLastPage(page);
|
|
||||||
}).catch(() => { });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -54,11 +35,11 @@ const ModerationLog = () => {
|
||||||
showLoading={showLoading}
|
showLoading={showLoading}
|
||||||
scrollKey='moderation-log'
|
scrollKey='moderation-log'
|
||||||
emptyMessage={intl.formatMessage(messages.emptyMessage)}
|
emptyMessage={intl.formatMessage(messages.emptyMessage)}
|
||||||
hasMore={hasMore}
|
hasMore={hasNextPage}
|
||||||
onLoadMore={handleLoadMore}
|
onLoadMore={handleLoadMore}
|
||||||
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
||||||
>
|
>
|
||||||
{items.map(item => item && (
|
{data.map(item => item && (
|
||||||
<LogItem key={item.id} log={item} />
|
<LogItem key={item.id} log={item} />
|
||||||
))}
|
))}
|
||||||
</ScrollableList>
|
</ScrollableList>
|
||||||
|
@ -67,7 +48,7 @@ const ModerationLog = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ILogItem {
|
interface ILogItem {
|
||||||
log: AdminLog;
|
log: ModerationLogEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogItem: React.FC<ILogItem> = ({ log }) => {
|
const LogItem: React.FC<ILogItem> = ({ log }) => {
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
import {
|
|
||||||
Map as ImmutableMap,
|
|
||||||
Record as ImmutableRecord,
|
|
||||||
OrderedSet as ImmutableOrderedSet,
|
|
||||||
} from 'immutable';
|
|
||||||
|
|
||||||
import { ADMIN_LOG_FETCH_SUCCESS } from 'soapbox/actions/admin';
|
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
|
||||||
import type { APIEntity } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
export const LogEntryRecord = ImmutableRecord({
|
|
||||||
data: ImmutableMap<string, any>(),
|
|
||||||
id: 0,
|
|
||||||
message: '',
|
|
||||||
time: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
|
||||||
items: ImmutableMap<string, LogEntry>(),
|
|
||||||
index: ImmutableOrderedSet<number>(),
|
|
||||||
total: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
type LogEntry = ReturnType<typeof LogEntryRecord>;
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
|
||||||
type APIEntities = Array<APIEntity>;
|
|
||||||
|
|
||||||
const parseItems = (items: APIEntities) => {
|
|
||||||
const ids: Array<number> = [];
|
|
||||||
const map: Record<string, LogEntry> = {};
|
|
||||||
|
|
||||||
items.forEach(item => {
|
|
||||||
ids.push(item.id);
|
|
||||||
map[item.id] = LogEntryRecord(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
return { ids: ids, map: map };
|
|
||||||
};
|
|
||||||
|
|
||||||
const importItems = (state: State, items: APIEntities, total: number) => {
|
|
||||||
const { ids, map } = parseItems(items);
|
|
||||||
|
|
||||||
return state.withMutations(state => {
|
|
||||||
state.update('index', v => v.union(ids));
|
|
||||||
state.update('items', v => v.merge(map));
|
|
||||||
state.set('total', total);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function admin_log(state = ReducerRecord(), action: AnyAction) {
|
|
||||||
switch (action.type) {
|
|
||||||
case ADMIN_LOG_FETCH_SUCCESS:
|
|
||||||
return importItems(state, action.items, action.total);
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ import entities from 'soapbox/entity-store/reducer';
|
||||||
import accounts_meta from './accounts-meta';
|
import accounts_meta from './accounts-meta';
|
||||||
import admin from './admin';
|
import admin from './admin';
|
||||||
import admin_announcements from './admin-announcements';
|
import admin_announcements from './admin-announcements';
|
||||||
import admin_log from './admin-log';
|
|
||||||
import admin_user_index from './admin-user-index';
|
import admin_user_index from './admin-user-index';
|
||||||
import aliases from './aliases';
|
import aliases from './aliases';
|
||||||
import announcements from './announcements';
|
import announcements from './announcements';
|
||||||
|
@ -69,7 +68,6 @@ const reducers = {
|
||||||
accounts_meta,
|
accounts_meta,
|
||||||
admin,
|
admin,
|
||||||
admin_announcements,
|
admin_announcements,
|
||||||
admin_log,
|
|
||||||
admin_user_index,
|
admin_user_index,
|
||||||
aliases,
|
aliases,
|
||||||
announcements,
|
announcements,
|
||||||
|
|
|
@ -12,6 +12,7 @@ export { groupRelationshipSchema, type GroupRelationship } from './group-relatio
|
||||||
export { groupTagSchema, type GroupTag } from './group-tag';
|
export { groupTagSchema, type GroupTag } from './group-tag';
|
||||||
export { instanceSchema, type Instance } from './instance';
|
export { instanceSchema, type Instance } from './instance';
|
||||||
export { mentionSchema, type Mention } from './mention';
|
export { mentionSchema, type Mention } from './mention';
|
||||||
|
export { moderationLogEntrySchema, type ModerationLogEntry } from './moderation-log-entry';
|
||||||
export { notificationSchema, type Notification } from './notification';
|
export { notificationSchema, type Notification } from './notification';
|
||||||
export { patronUserSchema, type PatronUser } from './patron';
|
export { patronUserSchema, type PatronUser } from './patron';
|
||||||
export { pollSchema, type Poll, type PollOption } from './poll';
|
export { pollSchema, type Poll, type PollOption } from './poll';
|
||||||
|
|
12
src/schemas/moderation-log-entry.ts
Normal file
12
src/schemas/moderation-log-entry.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import z from 'zod';
|
||||||
|
|
||||||
|
const moderationLogEntrySchema = z.object({
|
||||||
|
id: z.coerce.string(),
|
||||||
|
data: z.record(z.string(), z.any()).catch({}),
|
||||||
|
time: z.number().catch(0),
|
||||||
|
message: z.string().catch(''),
|
||||||
|
});
|
||||||
|
|
||||||
|
type ModerationLogEntry = z.infer<typeof moderationLogEntrySchema>
|
||||||
|
|
||||||
|
export { moderationLogEntrySchema, type ModerationLogEntry };
|
|
@ -20,14 +20,12 @@ import {
|
||||||
StatusRecord,
|
StatusRecord,
|
||||||
TagRecord,
|
TagRecord,
|
||||||
} from 'soapbox/normalizers';
|
} from 'soapbox/normalizers';
|
||||||
import { LogEntryRecord } from 'soapbox/reducers/admin-log';
|
|
||||||
import { Account as SchemaAccount } from 'soapbox/schemas';
|
import { Account as SchemaAccount } from 'soapbox/schemas';
|
||||||
|
|
||||||
import type { Record as ImmutableRecord } from 'immutable';
|
import type { Record as ImmutableRecord } from 'immutable';
|
||||||
import type { LegacyMap } from 'soapbox/utils/legacy';
|
import type { LegacyMap } from 'soapbox/utils/legacy';
|
||||||
|
|
||||||
type AdminAccount = ReturnType<typeof AdminAccountRecord>;
|
type AdminAccount = ReturnType<typeof AdminAccountRecord>;
|
||||||
type AdminLog = ReturnType<typeof LogEntryRecord>;
|
|
||||||
type AdminReport = ReturnType<typeof AdminReportRecord>;
|
type AdminReport = ReturnType<typeof AdminReportRecord>;
|
||||||
type Announcement = ReturnType<typeof AnnouncementRecord>;
|
type Announcement = ReturnType<typeof AnnouncementRecord>;
|
||||||
type AnnouncementReaction = ReturnType<typeof AnnouncementReactionRecord>;
|
type AnnouncementReaction = ReturnType<typeof AnnouncementReactionRecord>;
|
||||||
|
@ -62,7 +60,6 @@ type EmbeddedEntity<T extends object> = null | string | ReturnType<ImmutableReco
|
||||||
export {
|
export {
|
||||||
Account,
|
Account,
|
||||||
AdminAccount,
|
AdminAccount,
|
||||||
AdminLog,
|
|
||||||
AdminReport,
|
AdminReport,
|
||||||
Announcement,
|
Announcement,
|
||||||
AnnouncementReaction,
|
AnnouncementReaction,
|
||||||
|
|
Loading…
Reference in a new issue