From 530e3c8ac8c3059c5b6c136861f4b55507fecc41 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue, 28 May 2024 22:02:50 +0100 Subject: [PATCH] [MOB-99] Job remove & more (#2514) * Clear job and improve job manager design * pullToRefresh, search adjustment, and more * ts * use rspc instead of query client * Update JobManagerModal.tsx --- .../components/explorer/menu/SortByMenu.tsx | 2 + apps/mobile/src/components/header/Header.tsx | 2 +- .../src/components/job/JobContainer.tsx | 4 +- apps/mobile/src/components/job/JobGroup.tsx | 74 +++++++++++++------ apps/mobile/src/components/layout/Empty.tsx | 10 +-- .../src/components/locations/GridLocation.tsx | 2 +- .../src/components/modal/ImportModal.tsx | 2 + .../components/modal/job/JobManagerModal.tsx | 37 ++++++---- .../src/components/primitive/Button.tsx | 4 +- apps/mobile/src/components/primitive/Menu.tsx | 18 +++-- .../mobile/src/components/primitive/Toast.tsx | 8 +- apps/mobile/src/constants/style/Colors.js | 2 +- apps/mobile/src/hooks/useFiltersSearch.ts | 1 - apps/mobile/src/screens/browse/Browse.tsx | 3 +- apps/mobile/src/screens/browse/Location.tsx | 1 - apps/mobile/src/screens/browse/Locations.tsx | 1 - apps/mobile/src/screens/browse/Tag.tsx | 1 - apps/mobile/src/screens/browse/Tags.tsx | 1 - apps/mobile/src/screens/search/Search.tsx | 19 +++-- 19 files changed, 121 insertions(+), 71 deletions(-) diff --git a/apps/mobile/src/components/explorer/menu/SortByMenu.tsx b/apps/mobile/src/components/explorer/menu/SortByMenu.tsx index d5b1cdc4d..8abd4322d 100644 --- a/apps/mobile/src/components/explorer/menu/SortByMenu.tsx +++ b/apps/mobile/src/components/explorer/menu/SortByMenu.tsx @@ -25,6 +25,7 @@ const SortByMenu = () => { return ( } > @@ -40,6 +41,7 @@ const SortByMenu = () => { ))} diff --git a/apps/mobile/src/components/job/JobContainer.tsx b/apps/mobile/src/components/job/JobContainer.tsx index 9e7532cc0..5cceda639 100644 --- a/apps/mobile/src/components/job/JobContainer.tsx +++ b/apps/mobile/src/components/job/JobContainer.tsx @@ -1,8 +1,8 @@ +import { TextItems } from '@sd/client'; import { Image } from 'expo-image'; import { Icon } from 'phosphor-react-native'; import { Fragment } from 'react'; import { Text, View, ViewStyle } from 'react-native'; -import { TextItems } from '@sd/client'; import { styled, tw, twStyle } from '~/lib/tailwind'; type JobContainerProps = { @@ -25,7 +25,7 @@ export default function JobContainer(props: JobContainerProps) { - + ); }; @@ -158,8 +160,23 @@ const toastErrorSuccess = ( }; }; -function Options({ activeJob, group }: { activeJob?: JobReport; group: JobGroup }) { - // const queryClient = useQueryClient(); +interface OptionsProps { + activeJob?: JobReport; + group: JobGroup; + showChildJobs: boolean; + setShowChildJobs: React.Dispatch> +} + +function Options({ activeJob, group, setShowChildJobs, showChildJobs }: OptionsProps) { + + const rspc = useRspcLibraryContext(); + + const clearJob = useLibraryMutation( + ['jobs.clear'], { + onSuccess: () => { + rspc.queryClient.invalidateQueries(['jobs.reports']); + } + }) const resumeJob = useLibraryMutation( ['jobs.resume'], @@ -179,32 +196,45 @@ function Options({ activeJob, group }: { activeJob?: JobReport; group: JobGroup [group.jobs] ); - // const clearJob = useLibraryMutation( - // ['jobs.clear'], - // toastErrorSuccess('failed_to_remove_job', undefined, () => { - // queryClient.invalidateQueries(['jobs.reports']); - // }) - // ); + const clearJobHandler = () => { + group.jobs.forEach((job) => { + clearJob.mutate(job.id); + //only one toast for all jobs + if (job.id === group.id) + toast.success('Job has been removed'); + }); + }; return ( <> {/* Resume */} {(group.status === 'Queued' || group.status === 'Paused' || isJobPaused) && ( - )} {/* TODO: This should remove the job from panel */} {!activeJob !== undefined ? ( - + + } + > + setShowChildJobs(!showChildJobs)} + text="Expand" icon={Eye}/> + + ) : ( - - diff --git a/apps/mobile/src/components/layout/Empty.tsx b/apps/mobile/src/components/layout/Empty.tsx index fec6b282a..2c7ce7ab4 100644 --- a/apps/mobile/src/components/layout/Empty.tsx +++ b/apps/mobile/src/components/layout/Empty.tsx @@ -7,14 +7,14 @@ import { Icon, IconName } from '../icons/Icon'; interface Props { description: string; //description of empty state - icon: IconName; //Spacedrive icon + icon?: IconName; //Spacedrive icon style?: ClassInput; //Tailwind classes iconSize?: number; //Size of the icon - textSize?: ClassInput; //Size of the text + textStyle?: ClassInput; //Size of the text includeHeaderHeight?: boolean; //Height of the header } -const Empty = ({ description, icon, style, includeHeaderHeight = false, textSize = 'text-sm', iconSize = 38 }: Props) => { +const Empty = ({ description, icon, style, includeHeaderHeight = false, textStyle, iconSize = 38 }: Props) => { const headerHeight = useSafeAreaInsets().top; return ( - - + {icon && } + {description} diff --git a/apps/mobile/src/components/locations/GridLocation.tsx b/apps/mobile/src/components/locations/GridLocation.tsx index 47c8e1853..bfc5239ef 100644 --- a/apps/mobile/src/components/locations/GridLocation.tsx +++ b/apps/mobile/src/components/locations/GridLocation.tsx @@ -20,7 +20,7 @@ const GridLocation: React.FC = ({ location, modalRef }: GridL - + ((_, ref) => { //custom message handling if (error.message.startsWith("location already exists")) { return toast.error('This location has already been added'); + } else if (error.message.startsWith("nested location currently")) { + return toast.error('Nested locations are currently not supported'); } switch (error.message) { case 'NEED_RELINK': diff --git a/apps/mobile/src/components/modal/job/JobManagerModal.tsx b/apps/mobile/src/components/modal/job/JobManagerModal.tsx index 7d996b313..2c759e2a1 100644 --- a/apps/mobile/src/components/modal/job/JobManagerModal.tsx +++ b/apps/mobile/src/components/modal/job/JobManagerModal.tsx @@ -1,40 +1,51 @@ -import { forwardRef } from 'react'; -import { FlatList, Text, View } from 'react-native'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; import { useJobProgress, useLibraryQuery } from '@sd/client'; +import { forwardRef, useEffect } from 'react'; import JobGroup from '~/components/job/JobGroup'; +import Empty from '~/components/layout/Empty'; import { Modal, ModalRef } from '~/components/layout/Modal'; +import useForwardedRef from '~/hooks/useForwardedRef'; import { tw } from '~/lib/tailwind'; -// TODO: -// - When there is no job, make modal height smaller -// - Add clear all jobs button +//TODO: Handle data fetching better when modal is opened export const JobManagerModal = forwardRef((_, ref) => { - // const queryClient = useQueryClient(); - + // const rspc = useRspcLibraryContext(); const jobGroups = useLibraryQuery(['jobs.reports']); const progress = useJobProgress(jobGroups.data); + const modalRef = useForwardedRef(ref); + + //TODO: Add clear all jobs button // const clearAllJobs = useLibraryMutation(['jobs.clearAll'], { // onError: () => { - // // TODO: Show error toast + // toast.error('Failed to clear all jobs.'); // }, // onSuccess: () => { // queryClient.invalidateQueries(['jobs.reports ']); // } // }); + useEffect(() => { + if (jobGroups.data?.length === 0) { + modalRef.current?.snapToPosition('20'); + } + }, [jobGroups, modalRef]); + return ( - - + i.id} contentContainerStyle={tw`mt-4`} renderItem={({ item }) => } ListEmptyComponent={ - - No jobs. - + } /> diff --git a/apps/mobile/src/components/primitive/Button.tsx b/apps/mobile/src/components/primitive/Button.tsx index 16b5c0958..7e0cbd893 100644 --- a/apps/mobile/src/components/primitive/Button.tsx +++ b/apps/mobile/src/components/primitive/Button.tsx @@ -11,8 +11,8 @@ const button = cva(['items-center justify-center rounded-md border shadow-sm'], gray: ['border-app-box bg-app shadow-none'], darkgray: ['border-app-box bg-app shadow-none'], accent: ['border-accent-deep bg-accent shadow-md shadow-app-shade/10'], - outline: ['border-app-lightborder bg-black shadow-none'], - transparent: ['border-0 bg-black shadow-none'], + outline: ['border border-app-inputborder bg-transparent shadow-none'], + transparent: ['border-0 bg-transparent shadow-none'], dashed: ['border border-dashed border-app-line bg-transparent shadow-none'] }, size: { diff --git a/apps/mobile/src/components/primitive/Menu.tsx b/apps/mobile/src/components/primitive/Menu.tsx index 3f906f5c2..7f16d870a 100644 --- a/apps/mobile/src/components/primitive/Menu.tsx +++ b/apps/mobile/src/components/primitive/Menu.tsx @@ -14,13 +14,14 @@ type MenuProps = { trigger: React.ReactNode; children: React.ReactNode[] | React.ReactNode; triggerStyle?: ClassInput; + containerStyle?: ClassInput; }; // TODO: Still looks a bit off... export const Menu = (props: MenuProps) => ( {props.trigger} - + {props.children} @@ -28,24 +29,25 @@ export const Menu = (props: MenuProps) => ( type MenuItemProps = { icon?: Icon; + textStyle?: ClassInput; + iconStyle?: ClassInput; + style?: ClassInput; } & MenuOptionProps; -export const MenuItem = ({ icon, ...props }: MenuItemProps) => { +export const MenuItem = ({ icon, textStyle, iconStyle, style, ...props }: MenuItemProps) => { const Icon = icon; return ( - + {Icon && ( - - - + )} ); diff --git a/apps/mobile/src/components/primitive/Toast.tsx b/apps/mobile/src/components/primitive/Toast.tsx index 0ba8d9528..d0da33b3c 100644 --- a/apps/mobile/src/components/primitive/Toast.tsx +++ b/apps/mobile/src/components/primitive/Toast.tsx @@ -5,13 +5,13 @@ import Toast, { ToastConfig } from 'react-native-toast-message'; import { tw } from '~/lib/tailwind'; const baseStyles = 'max-w-[340px] flex-row gap-1 items-center justify-center overflow-hidden rounded-md border p-3 shadow-lg bg-app-input border-app-inputborder'; -const containerStyle = 'flex-row items-start gap-2' +const containerStyle = 'flex-row items-start gap-1.5' const toastConfig: ToastConfig = { success: ({ text1, ...rest }) => ( - + {text1} @@ -21,7 +21,7 @@ const toastConfig: ToastConfig = { error: ({ text1, ...rest }) => ( - + {text1} @@ -31,7 +31,7 @@ const toastConfig: ToastConfig = { info: ({ text1, ...rest }) => ( - + {text1} diff --git a/apps/mobile/src/constants/style/Colors.js b/apps/mobile/src/constants/style/Colors.js index ae080949b..715b96f56 100644 --- a/apps/mobile/src/constants/style/Colors.js +++ b/apps/mobile/src/constants/style/Colors.js @@ -79,7 +79,7 @@ module.exports = { // shadow shade: `hsla(${DARK_HUE}, 15%, 0%, ${ALPHA})`, // menu - menu: `hsla(${DARK_HUE}, 25%, 5%, ${ALPHA})` + menu: `hsla(${DARK_HUE}, 10%, 5%, ${ALPHA})` }, sidebar: { box: `hsla(${DARK_HUE}, 15%, 16%, ${ALPHA})`, diff --git a/apps/mobile/src/hooks/useFiltersSearch.ts b/apps/mobile/src/hooks/useFiltersSearch.ts index a93bd0216..4c953df85 100644 --- a/apps/mobile/src/hooks/useFiltersSearch.ts +++ b/apps/mobile/src/hooks/useFiltersSearch.ts @@ -17,7 +17,6 @@ export function useFiltersSearch(search: string) { const locations = useLibraryQuery(['locations.list'], { keepPreviousData: true, - enabled: (name || ext) ? true : false, }); const filterFactory = (key: SearchFilters, value: Filters[keyof Filters]) => { diff --git a/apps/mobile/src/screens/browse/Browse.tsx b/apps/mobile/src/screens/browse/Browse.tsx index e7a3b39b2..23b867f43 100644 --- a/apps/mobile/src/screens/browse/Browse.tsx +++ b/apps/mobile/src/screens/browse/Browse.tsx @@ -1,4 +1,3 @@ -import BrowseCategories from '~/components/browse/BrowseCategories'; import BrowseLocations from '~/components/browse/BrowseLocations'; import BrowseTags from '~/components/browse/BrowseTags'; import ScreenContainer from '~/components/layout/ScreenContainer'; @@ -6,7 +5,7 @@ import ScreenContainer from '~/components/layout/ScreenContainer'; export default function BrowseScreen() { return ( - + {/* */} diff --git a/apps/mobile/src/screens/browse/Location.tsx b/apps/mobile/src/screens/browse/Location.tsx index 39a767968..e8984552d 100644 --- a/apps/mobile/src/screens/browse/Location.tsx +++ b/apps/mobile/src/screens/browse/Location.tsx @@ -77,7 +77,6 @@ export default function LocationScreen({ navigation, route }: BrowseStackScreenP includeHeaderHeight icon={'FolderNoSpace'} style={tw`flex-1 items-center justify-center border-0`} - textSize="text-md" iconSize={100} description={'No files found'} />} diff --git a/apps/mobile/src/screens/browse/Locations.tsx b/apps/mobile/src/screens/browse/Locations.tsx index 0afe969f8..7dc4327e2 100644 --- a/apps/mobile/src/screens/browse/Locations.tsx +++ b/apps/mobile/src/screens/browse/Locations.tsx @@ -60,7 +60,6 @@ export default function LocationsScreen({ viewStyle }: Props) { diff --git a/apps/mobile/src/screens/browse/Tag.tsx b/apps/mobile/src/screens/browse/Tag.tsx index 012beb9ca..0854c8217 100644 --- a/apps/mobile/src/screens/browse/Tag.tsx +++ b/apps/mobile/src/screens/browse/Tag.tsx @@ -38,7 +38,6 @@ export default function TagScreen({ navigation, route }: BrowseStackScreenProps< includeHeaderHeight icon={'Tags'} style={tw`flex-1 items-center justify-center border-0`} - textSize="text-md" iconSize={100} description={'No items assigned to this tag'} />} {...objects} />; diff --git a/apps/mobile/src/screens/browse/Tags.tsx b/apps/mobile/src/screens/browse/Tags.tsx index c154b7a54..fa89b6d57 100644 --- a/apps/mobile/src/screens/browse/Tags.tsx +++ b/apps/mobile/src/screens/browse/Tags.tsx @@ -65,7 +65,6 @@ export default function TagsScreen({ viewStyle = 'list' }: Props) { diff --git a/apps/mobile/src/screens/search/Search.tsx b/apps/mobile/src/screens/search/Search.tsx index 485c4624a..ef70a107e 100644 --- a/apps/mobile/src/screens/search/Search.tsx +++ b/apps/mobile/src/screens/search/Search.tsx @@ -1,5 +1,5 @@ import { useIsFocused } from '@react-navigation/native'; -import { usePathsExplorerQuery } from '@sd/client'; +import { useLibraryQuery, usePathsExplorerQuery } from '@sd/client'; import { ArrowLeft, DotsThree, FunnelSimple } from 'phosphor-react-native'; import { Suspense, useDeferredValue, useState } from 'react'; import { ActivityIndicator, Platform, Pressable, TextInput, View } from 'react-native'; @@ -23,6 +23,8 @@ const SearchScreen = ({ navigation }: SearchStackScreenProps<'Search'>) => { const deferredSearch = useDeferredValue(search); const order = useSortBy(); + const locations = useLibraryQuery(['locations.list']).data ?? []; + const objects = usePathsExplorerQuery({ order, arg: { @@ -43,6 +45,13 @@ const SearchScreen = ({ navigation }: SearchStackScreenProps<'Search'>) => { const noObjects = objects.items?.length === 0 || !objects.items; const noSearch = deferredSearch.length === 0 && appliedFiltersLength === 0; + const searchIcon = + locations.length > 0 && noObjects && noSearch ? 'FolderNoSpace' : + noSearch && noObjects ? 'Search' : 'FolderNoSpace'; + + const searchDescription = locations.length === 0 ? 'You have not added any locations to search' : noObjects + || noSearch ? 'No files found' : 'No results found for this search'; + return ( ) => { emptyComponent={ + /> } tabHeight={false} />