From 23bb607e9b69a600b3209739287680f378aae9cf Mon Sep 17 00:00:00 2001 From: bu3alwa <3469062+bu3alwa@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:32:13 -0400 Subject: [PATCH] refactor activity page to use react query requests --- src/apps/dashboard/routes/activity.tsx | 118 +++++++++---------------- src/hooks/useGetLogEntries.tsx | 32 +++++++ src/hooks/useGetUsers.tsx | 32 +++++++ 3 files changed, 108 insertions(+), 74 deletions(-) create mode 100644 src/hooks/useGetLogEntries.tsx create mode 100644 src/hooks/useGetUsers.tsx diff --git a/src/apps/dashboard/routes/activity.tsx b/src/apps/dashboard/routes/activity.tsx index e0fd4ccd8a83..5b3d5a36d696 100644 --- a/src/apps/dashboard/routes/activity.tsx +++ b/src/apps/dashboard/routes/activity.tsx @@ -1,6 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { getActivityLogApi } from '@jellyfin/sdk/lib/utils/api/activity-log-api'; -import { getUserApi } from '@jellyfin/sdk/lib/utils/api/user-api'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry'; import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto'; import PermMedia from '@mui/icons-material/PermMedia'; @@ -14,7 +12,6 @@ import { Link, useSearchParams } from 'react-router-dom'; import Page from 'components/Page'; import UserAvatar from 'components/UserAvatar'; -import { useApi } from 'hooks/useApi'; import { parseISO8601Date, toLocaleDateString, toLocaleTimeString } from 'scripts/datetime'; import globalize from 'scripts/globalize'; import { toBoolean } from 'utils/string'; @@ -22,6 +19,8 @@ import { toBoolean } from 'utils/string'; import LogLevelChip from '../components/activityTable/LogLevelChip'; import OverviewCell from '../components/activityTable/OverviewCell'; import GridActionsCellLink from '../components/dataGrid/GridActionsCellLink'; +import { useGetLogEntires } from 'hooks/useGetLogEntries'; +import { useGetUsers } from 'hooks/useGetUsers'; const DEFAULT_PAGE_SIZE = 25; const VIEW_PARAM = 'useractivity'; @@ -41,26 +40,49 @@ const getActivityView = (param: string | null) => { const getRowId = (row: ActivityLogEntry) => row.Id ?? -1; const Activity = () => { - const { api } = useApi(); - const [ searchParams, setSearchParams ] = useSearchParams(); + const [searchParams, setSearchParams] = useSearchParams(); - const [ activityView, setActivityView ] = useState( + const [activityView, setActivityView] = useState( getActivityView(searchParams.get(VIEW_PARAM))); - const [ isLoading, setIsLoading ] = useState(true); - const [ paginationModel, setPaginationModel ] = useState({ + + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: DEFAULT_PAGE_SIZE }); - const [ rowCount, setRowCount ] = useState(0); - const [ rows, setRows ] = useState([]); - const [ users, setUsers ] = useState>({}); + + const { data: usersData, isLoading: isUsersLoading } = useGetUsers(); + + type UsersRecords = Record; + const users: UsersRecords = useMemo(() => { + if (!usersData) return {}; + + return usersData.reduce((acc, user) => { + const userId = user.Id; + if (!userId) return acc; + + return { + ...acc, + [userId]: user + }; + }, {}); + }, [usersData]); + + const activityParams = useMemo(() => ({ + startIndex: paginationModel.page * paginationModel.pageSize, + limit: paginationModel.pageSize, + hasUserId: activityView !== ActivityView.All ? activityView === ActivityView.User : undefined + }), [activityView, paginationModel.page, paginationModel.pageSize]); + + const { data: logEntries, isLoading: isLogEntriesLoading } = useGetLogEntires(activityParams); + + const isLoading = isUsersLoading || isLogEntriesLoading; const userColDef: GridColDef[] = activityView !== ActivityView.System ? [ { field: 'User', headerName: globalize.translate('LabelUser'), width: 60, - valueGetter: ( value, row ) => users[row.UserId]?.Name, + valueGetter: (value, row) => users[row.UserId]?.Name, renderCell: ({ row }) => ( { headerName: globalize.translate('LabelDate'), width: 90, type: 'date', - valueGetter: ( value ) => parseISO8601Date(value), - valueFormatter: ( value ) => toLocaleDateString(value) + valueGetter: (value) => parseISO8601Date(value), + valueFormatter: (value) => toLocaleDateString(value) }, { field: 'Time', headerName: globalize.translate('LabelTime'), width: 100, type: 'dateTime', - valueGetter: ( value, row ) => parseISO8601Date(row.Date), - valueFormatter: ( value ) => toLocaleTimeString(value) + valueGetter: (value, row) => parseISO8601Date(row.Date), + valueFormatter: (value) => toLocaleTimeString(value) }, { field: 'Severity', @@ -113,7 +135,7 @@ const Activity = () => { field: 'Overview', headerName: globalize.translate('LabelOverview'), width: 200, - valueGetter: ( value, row ) => row.ShortOverview ?? row.Overview, + valueGetter: (value, row) => row.ShortOverview ?? row.Overview, renderCell: ({ row }) => ( ) @@ -153,58 +175,6 @@ const Activity = () => { } }, []); - useEffect(() => { - if (api) { - const fetchUsers = async () => { - const { data } = await getUserApi(api).getUsers(); - const usersById: Record = {}; - data.forEach(user => { - if (user.Id) { - usersById[user.Id] = user; - } - }); - - setUsers(usersById); - }; - - fetchUsers() - .catch(err => { - console.error('[activity] failed to fetch users', err); - }); - } - }, [ api ]); - - useEffect(() => { - if (api) { - const fetchActivity = async () => { - const params: { - startIndex: number, - limit: number, - hasUserId?: boolean - } = { - startIndex: paginationModel.page * paginationModel.pageSize, - limit: paginationModel.pageSize - }; - if (activityView !== ActivityView.All) { - params.hasUserId = activityView === ActivityView.User; - } - - const { data } = await getActivityLogApi(api) - .getLogEntries(params); - - setRowCount(data.TotalRecordCount ?? 0); - setRows(data.Items ?? []); - setIsLoading(false); - }; - - setIsLoading(true); - fetchActivity() - .catch(err => { - console.error('[activity] failed to fetch activity log entries', err); - }); - } - }, [ activityView, api, paginationModel ]); - useEffect(() => { const currentViewParam = getActivityView(searchParams.get(VIEW_PARAM)); if (currentViewParam !== activityView) { @@ -215,7 +185,7 @@ const Activity = () => { } setSearchParams(searchParams); } - }, [ activityView, searchParams, setSearchParams ]); + }, [activityView, searchParams, setSearchParams]); return ( { { + const { api } = currentApi; + + if (!api) return; + + const response = await getActivityLogApi(api).getLogEntries(requestParams, { + signal: options?.signal + }); + + return response.data; +}; + +export const useGetLogEntires = ( + requestParams: ActivityLogApiGetLogEntriesRequest +) => { + const currentApi = useApi(); + return useQuery({ + queryKey: ['LogEntries', requestParams], + queryFn: ({ signal }) => + fetchGetLogEntries(currentApi, requestParams, { signal }) + }); +}; diff --git a/src/hooks/useGetUsers.tsx b/src/hooks/useGetUsers.tsx new file mode 100644 index 000000000000..98cb9e03148c --- /dev/null +++ b/src/hooks/useGetUsers.tsx @@ -0,0 +1,32 @@ +import type { AxiosRequestConfig } from 'axios'; +import { type JellyfinApiContext, useApi } from './useApi'; +import type { + UserApiGetUsersRequest +} from '@jellyfin/sdk/lib/generated-client'; +import { getUserApi } from '@jellyfin/sdk/lib/utils/api/user-api'; +import { useQuery } from '@tanstack/react-query'; + +export const fetchGetUsers = async ( + currentApi: JellyfinApiContext, + requestParams?: UserApiGetUsersRequest, + options?: AxiosRequestConfig +) => { + const { api } = currentApi; + + if (!api) return; + + const response = await getUserApi(api).getUsers(requestParams, { + signal: options?.signal + }); + + return response.data; +}; + +export const useGetUsers = (requestParams?: UserApiGetUsersRequest) => { + const currentApi = useApi(); + return useQuery({ + queryKey: ['Users'], + queryFn: ({ signal }) => + fetchGetUsers(currentApi, requestParams, { signal }) + }); +};