Update scanner (frontend)

This commit is contained in:
jeffvli
2022-10-29 19:13:40 -07:00
parent 0200b92860
commit 19090a0ed8
16 changed files with 411 additions and 38 deletions
+3
View File
@@ -0,0 +1,3 @@
export * from './queries/task-list';
export * from './mutations/cancel-all-tasks';
export * from './mutations/cancel-task';
@@ -0,0 +1,39 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { api } from '@/renderer/api';
import { queryKeys } from '@/renderer/api/query-keys';
import { TaskListResponse } from '@/renderer/api/tasks.api';
import { ApiError, NullResponse } from '@/renderer/api/types';
export const useCancelAllTasks = () => {
const queryClient = useQueryClient();
const mutation = useMutation<
NullResponse,
AxiosError<ApiError>,
null,
{ previous: TaskListResponse | undefined }
>({
mutationFn: () => api.tasks.cancelAllTasks(),
onError: (_err, _variables, context) => {
if (!context?.previous) return;
queryClient.setQueryData(queryKeys.servers.list(), context.previous);
},
onMutate: () => {
const queryKey = queryKeys.tasks.list();
queryClient.cancelQueries(queryKey);
const previous = queryClient.getQueryData<TaskListResponse>(queryKey);
if (!previous) return undefined;
queryClient.setQueryData(queryKey, { ...previous, data: [] });
return { previous };
},
onSuccess: () => {
queryClient.invalidateQueries(queryKeys.tasks.list());
},
});
return mutation;
};
@@ -0,0 +1,39 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { api } from '@/renderer/api';
import { queryKeys } from '@/renderer/api/query-keys';
import { TaskListResponse, TaskResponse } from '@/renderer/api/tasks.api';
import { ApiError } from '@/renderer/api/types';
export const useCancelTask = () => {
const queryClient = useQueryClient();
const mutation = useMutation<
TaskResponse,
AxiosError<ApiError>,
{ query: { taskId: string } },
{ previous: TaskListResponse | undefined }
>({
mutationFn: ({ query }) => api.tasks.cancelTask(query),
onError: (_err, _variables, context) => {
if (!context?.previous) return;
queryClient.setQueryData(queryKeys.servers.list(), context.previous);
},
onMutate: () => {
const queryKey = queryKeys.tasks.list();
queryClient.cancelQueries(queryKey);
const previous = queryClient.getQueryData<TaskListResponse>(queryKey);
if (!previous) return undefined;
queryClient.setQueryData(queryKey, { ...previous, data: [] });
return { previous };
},
onSuccess: () => {
queryClient.invalidateQueries(queryKeys.tasks.list());
},
});
return mutation;
};
@@ -0,0 +1,22 @@
import { useCallback } from 'react';
import { useQuery } from '@tanstack/react-query';
import { api } from '@/renderer/api';
import { queryKeys } from '@/renderer/api/query-keys';
import { TaskListResponse } from '@/renderer/api/tasks.api';
import { QueryOptions } from '@/renderer/lib/react-query';
export const useTaskList = (options?: QueryOptions<TaskListResponse>) => {
return useQuery({
queryFn: ({ signal }) => api.tasks.getActiveTasks(signal),
queryKey: queryKeys.tasks.list(),
select: useCallback((data: TaskListResponse) => {
return {
...data,
data: data.data.map((task) => {
return { ...task, note: `${task.server?.name} - ${task.message}` };
}),
};
}, []),
...options,
});
};
@@ -1,8 +1,8 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { Group } from '@mantine/core';
import { FiActivity } from 'react-icons/fi';
import { Button, Text } from '@/renderer/components';
import { Text } from '@/renderer/components';
import { ActivityMenu } from '@/renderer/features/titlebar/components/activity-menu';
import { AppMenu } from '@/renderer/features/titlebar/components/app-menu';
import { useAuthStore } from '@/renderer/store';
import { Font } from '@/renderer/styles';
@@ -69,14 +69,7 @@ export const Titlebar = ({ children }: TitlebarProps) => {
<Group spacing="xs">
{isAuthenticated && (
<>
<Button
px={5}
size="xs"
sx={{ color: 'var(--titlebar-fg)' }}
variant="subtle"
>
<FiActivity size={15} />
</Button>
<ActivityMenu />
<AppMenu />
</>
)}
@@ -0,0 +1,93 @@
import { useEffect, useState } from 'react';
import styled from '@emotion/styled';
import { Group } from '@mantine/core';
import { FiActivity } from 'react-icons/fi';
import { RiRefreshLine } from 'react-icons/ri';
import { socket } from '@/renderer/api';
import { Button, Popover, Text } from '@/renderer/components';
import { useTaskList } from '@/renderer/features/tasks';
import { rotating } from '@/renderer/styles';
const StyledActivitySvg = styled(RiRefreshLine)`
${rotating}
animation: rotating 1s linear infinite;
`;
export const ActivityMenu = () => {
const [isTaskRunning, setIsTaskRunning] = useState(false);
const { data: tasks, refetch } = useTaskList({
onSuccess: (data) => {
if (data.data.length === 0) {
return setIsTaskRunning(false);
}
return setIsTaskRunning(true);
},
refetchInterval: isTaskRunning ? 5000 : undefined,
});
// const cancelTask = useCancelTask();
// const cancelAllTasks = useCancelAllTasks();
// const handleCancelTask = (taskId: string) => {
// cancelTask.mutate(
// { query: { taskId } },
// {
// onSuccess: () => {
// toast.info({ message: 'Task cancelled' });
// },
// }
// );
// };
// const handleCancelAllTasks = (taskId: string) => {
// cancelAllTasks.mutate(null, {
// onSuccess: () => {
// toast.info({ message: 'All tasks cancelled' });
// },
// });
// };
useEffect(() => {
socket.on('task:started', () => {
setTimeout(() => refetch(), 1000);
setIsTaskRunning(true);
});
return () => {
socket.off('task:started');
};
}, [refetch]);
return (
<>
<Popover withArrow withinPortal>
<Popover.Target>
<Button
px={5}
size="xs"
sx={{ color: 'var(--titlebar-fg)' }}
variant="subtle"
>
{isTaskRunning ? (
<StyledActivitySvg size={15} />
) : (
<FiActivity size={15} />
)}
</Button>
</Popover.Target>
<Popover.Dropdown>
{isTaskRunning ? (
tasks?.data?.map((task) => (
<Group key={task.id} position="apart">
<Text>{task.note}</Text>
</Group>
))
) : (
<Text>No tasks running</Text>
)}
</Popover.Dropdown>
</Popover>
</>
);
};
@@ -1,5 +1,11 @@
import { Group } from '@mantine/core';
import { openModal, closeAllModals } from '@mantine/modals';
import { RiArrowLeftLine, RiLogoutBoxLine, RiMenu3Fill } from 'react-icons/ri';
import {
RiArrowLeftLine,
RiLock2Line,
RiLogoutBoxLine,
RiMenu3Fill,
} from 'react-icons/ri';
import { useNavigate } from 'react-router';
import { Button, DropdownMenu } from '@/renderer/components';
import {
@@ -15,11 +21,16 @@ export const AppMenu = () => {
const logout = useAuthStore((state) => state.logout);
const currentServer = useAuthStore((state) => state.currentServer);
const setCurrentServer = useAuthStore((state) => state.setCurrentServer);
const serverCredentials = useAuthStore((state) => state.serverCredentials);
const permissions = usePermissions();
const { data: servers } = useServerList();
const serverList =
servers?.data?.map((s) => ({ id: s.id, label: `${s.name}` })) ?? [];
servers?.data?.map((s) => ({
id: s.id,
label: `${s.name}`,
noCredential: s.noCredential,
})) ?? [];
const handleLogout = () => {
logout();
@@ -49,7 +60,7 @@ export const AppMenu = () => {
};
return (
<DropdownMenu withinPortal position="bottom" width={200}>
<DropdownMenu withArrow withinPortal position="bottom" width={200}>
<DropdownMenu.Target>
<Button
px={5}
@@ -62,24 +73,38 @@ export const AppMenu = () => {
</DropdownMenu.Target>
<DropdownMenu.Dropdown>
<DropdownMenu.Label>Server switcher</DropdownMenu.Label>
{serverList.map((s) => (
<DropdownMenu.Item
key={`server-${s.id}`}
rightSection={
s.id === currentServer?.id ? <RiArrowLeftLine /> : undefined
}
sx={{
color:
s.id === currentServer?.id ? 'var(--primary-color)' : undefined,
}}
onClick={() => handleSetCurrentServer(s.id)}
>
{s.label}
</DropdownMenu.Item>
))}
{serverList.map((s) => {
const requiresCredential = !serverCredentials.some(
(c) => c.serverId === s.id && c.enabled
);
return (
<DropdownMenu.Item
key={`server-${s.id}`}
disabled={requiresCredential}
rightSection={
s.id === currentServer?.id ? <RiArrowLeftLine /> : undefined
}
sx={{
color:
s.id === currentServer?.id
? 'var(--primary-color)'
: undefined,
}}
onClick={() => handleSetCurrentServer(s.id)}
>
<Group>
{requiresCredential && (
<RiLock2Line color="var(--danger-color)" />
)}
{s.label}
</Group>
</DropdownMenu.Item>
);
})}
<DropdownMenu.Divider />
<DropdownMenu.Item disabled>Search</DropdownMenu.Item>
<DropdownMenu.Item>Configure</DropdownMenu.Item>
<DropdownMenu.Item>Settings</DropdownMenu.Item>
<DropdownMenu.Divider />
{permissions.createServer && (
<DropdownMenu.Item onClick={handleAddServerModal}>