mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-16 20:39:05 -04:00
[MOB-98] Rename, delete, & more (#2506)
* rename and delete support, browse design improvement * update toast styling * Update RenameModal.tsx * fix test * fix warning message on initial render, add-tag test, and remove solid js references * ci * Add feedback toast for location delete and tag delete
This commit is contained in:
@@ -4,6 +4,19 @@ import {
|
||||
NavigationContainer,
|
||||
useNavigationContainerRef
|
||||
} from '@react-navigation/native';
|
||||
import {
|
||||
ClientContextProvider,
|
||||
LibraryContextProvider,
|
||||
P2PContextProvider,
|
||||
RspcProvider,
|
||||
initPlausible,
|
||||
useBridgeQuery,
|
||||
useClientContext,
|
||||
useInvalidateQuery,
|
||||
usePlausibleEvent,
|
||||
usePlausiblePageViewMonitor,
|
||||
usePlausiblePingMonitor
|
||||
} from '@sd/client';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
import dayjs from 'dayjs';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
@@ -17,19 +30,6 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { MenuProvider } from 'react-native-popup-menu';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import {
|
||||
ClientContextProvider,
|
||||
initPlausible,
|
||||
LibraryContextProvider,
|
||||
P2PContextProvider,
|
||||
RspcProvider,
|
||||
useBridgeQuery,
|
||||
useClientContext,
|
||||
useInvalidateQuery,
|
||||
usePlausibleEvent,
|
||||
usePlausiblePageViewMonitor,
|
||||
usePlausiblePingMonitor
|
||||
} from '@sd/client';
|
||||
|
||||
import { GlobalModals } from './components/modal/GlobalModals';
|
||||
import { Toast, toastConfig } from './components/primitive/Toast';
|
||||
@@ -55,13 +55,17 @@ function AppNavigation() {
|
||||
const plausibleEvent = usePlausibleEvent();
|
||||
const buildInfo = useBridgeQuery(['buildInfo']);
|
||||
|
||||
initPlausible({ platformType: 'mobile', buildInfo: buildInfo?.data });
|
||||
|
||||
const navRef = useNavigationContainerRef();
|
||||
const routeNameRef = useRef<string>();
|
||||
|
||||
const [currentPath, setCurrentPath] = useState<string>('/');
|
||||
|
||||
useEffect(() => {
|
||||
if (buildInfo?.data) {
|
||||
initPlausible({ platformType: 'mobile', buildInfo: buildInfo.data });
|
||||
}
|
||||
}, [buildInfo]);
|
||||
|
||||
usePlausiblePageViewMonitor({ currentPath });
|
||||
usePlausiblePingMonitor({ currentPath });
|
||||
|
||||
@@ -73,9 +77,11 @@ function AppNavigation() {
|
||||
return () => clearInterval(interval);
|
||||
}, [plausibleEvent]);
|
||||
|
||||
if (library === null && libraries.data) {
|
||||
currentLibraryStore.id = libraries.data[0]?.uuid ?? null;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (library === null && libraries.data) {
|
||||
currentLibraryStore.id = libraries.data[0]?.uuid ?? null;
|
||||
}
|
||||
}, [library, libraries]);
|
||||
|
||||
return (
|
||||
<NavigationContainer
|
||||
|
||||
@@ -31,17 +31,17 @@ export const CATEGORIES_LIST = [
|
||||
const BrowseCategories = () => {
|
||||
const navigation = useNavigation<BrowseStackScreenProps<'Browse'>['navigation']>();
|
||||
return (
|
||||
<View style={tw`gap-5 px-6`}>
|
||||
<View style={tw`gap-5 px-5`}>
|
||||
<View style={tw`flex-row items-center justify-between`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>Library</Text>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.navigate('Library');
|
||||
}}
|
||||
style={tw`h-9 w-9 rounded-full`}
|
||||
style={tw`h-8 w-8 rounded-full`}
|
||||
variant="gray"
|
||||
>
|
||||
<DotsThree weight="bold" size={20} color={'white'} />
|
||||
<DotsThree weight="bold" size={18} color={'white'} />
|
||||
</Button>
|
||||
</View>
|
||||
<View style={tw`flex-row flex-wrap gap-2`}>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { DotsThree, Plus } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { useRef, useState } from 'react';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
||||
|
||||
import { Plus } from 'phosphor-react-native';
|
||||
import Empty from '../layout/Empty';
|
||||
import Fade from '../layout/Fade';
|
||||
import { LocationItem } from '../locations/LocationItem';
|
||||
import ImportModal from '../modal/ImportModal';
|
||||
import { Button } from '../primitive/Button';
|
||||
@@ -20,54 +21,64 @@ const BrowseLocations = () => {
|
||||
>();
|
||||
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const result = useLibraryQuery(['locations.list'], { keepPreviousData: true });
|
||||
const locations = result.data;
|
||||
|
||||
return (
|
||||
<View style={tw`gap-5 px-6`}>
|
||||
<View style={tw`w-full flex-row items-center justify-between`}>
|
||||
<View style={tw`gap-5`}>
|
||||
<View style={tw`flex-row items-center justify-between px-5`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>Locations</Text>
|
||||
<View style={tw`flex-row gap-3`}>
|
||||
<Button
|
||||
style={tw`h-9 w-9 rounded-full`}
|
||||
variant="dashed"
|
||||
onPress={() => modalRef.current?.present()}
|
||||
style={twStyle(`rounded-full`, {
|
||||
borderColor: showAll ? tw.color('accent') : tw.color('border-app-lightborder')
|
||||
})}
|
||||
variant="outline"
|
||||
onPress={() => setShowAll((prev) => !prev)}
|
||||
>
|
||||
<Plus weight="bold" size={16} style={tw`text-ink`} />
|
||||
<Text style={tw`text-xs text-ink`}>{showAll ? 'Show less' : 'Show all'} ({locations?.length})</Text>
|
||||
</Button>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.navigate('Locations');
|
||||
}}
|
||||
style={tw`h-9 w-9 rounded-full`}
|
||||
onPress={() => modalRef.current?.present()}
|
||||
style={tw`flex-row gap-1 rounded-full`}
|
||||
variant="gray"
|
||||
>
|
||||
<DotsThree weight="bold" size={20} color={'white'} />
|
||||
<Plus size={10} weight="bold" style={tw`text-white`} />
|
||||
<Text style={tw`text-xs text-ink`}>Add</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<View style={tw`flex-row flex-wrap gap-2`}>
|
||||
{locations?.length === 0 ? (
|
||||
<Empty description="You have not added any locations" icon="Folder" />
|
||||
) : (
|
||||
<>
|
||||
{locations?.slice(0, 3).map((location) => (
|
||||
<View style={tw`relative -m-1`}>
|
||||
<Fade color='black' width={30} height="100%">
|
||||
<FlatList
|
||||
data={locations}
|
||||
ListEmptyComponent={<Empty description="You have not added any locations" icon="Folder" />}
|
||||
numColumns={showAll ? 3 : 1}
|
||||
horizontal={showAll ? false : true}
|
||||
contentContainerStyle={twStyle(locations?.length === 0 && 'w-full','px-5')}
|
||||
key={showAll ? '_locations' : 'alllocationcols'}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
scrollEnabled={showAll ? false : true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
renderItem={({ item }) => {
|
||||
return (
|
||||
<LocationItem
|
||||
key={location.id}
|
||||
location={location}
|
||||
location={item}
|
||||
style={twStyle(showAll && 'max-w-[31%] flex-1')}
|
||||
editLocation={() =>
|
||||
navigation.navigate('SettingsStack', {
|
||||
screen: 'EditLocationSettings',
|
||||
params: { id: location.id },
|
||||
params: { id: item.id },
|
||||
initial: false
|
||||
})
|
||||
}
|
||||
onPress={() => navigation.navigate('Location', { id: location.id })}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
onPress={() => navigation.navigate('Location', { id: item.id })}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
/>
|
||||
</Fade>
|
||||
</View>
|
||||
<ImportModal ref={modalRef} />
|
||||
</View>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { DotsThree, Plus } from 'phosphor-react-native';
|
||||
import React, { useRef } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Plus } from 'phosphor-react-native';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
||||
import Empty from '../layout/Empty';
|
||||
import Fade from '../layout/Fade';
|
||||
import CreateTagModal from '../modal/tag/CreateTagModal';
|
||||
import { Button } from '../primitive/Button';
|
||||
import { TagItem } from '../tags/TagItem';
|
||||
@@ -19,47 +20,58 @@ const BrowseTags = () => {
|
||||
const tagData = tags.data;
|
||||
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
|
||||
return (
|
||||
<View style={tw`gap-5 px-6`}>
|
||||
<View style={tw`w-full flex-row items-center justify-between`}>
|
||||
<View style={tw`gap-5`}>
|
||||
<View style={tw`w-full flex-row items-center justify-between px-5`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>Tags</Text>
|
||||
<View style={tw`flex-row gap-3`}>
|
||||
<Button
|
||||
style={tw`h-9 w-9 rounded-full`}
|
||||
variant="dashed"
|
||||
onPress={() => modalRef.current?.present()}
|
||||
testID='show-all-tags-button'
|
||||
style={twStyle(`rounded-full`, {
|
||||
borderColor: showAll ? tw.color('accent') : tw.color('border-app-lightborder')
|
||||
})}
|
||||
variant="outline"
|
||||
onPress={() => setShowAll((prev) => !prev)}
|
||||
>
|
||||
<Plus weight="bold" size={16} style={tw`text-ink`} />
|
||||
<Text style={tw`text-xs text-ink`}>{showAll ? 'Show less' : 'Show all'} ({tagData?.length})</Text>
|
||||
</Button>
|
||||
<Button
|
||||
testID="navigate-tags-screen"
|
||||
onPress={() => {
|
||||
navigation.navigate('Tags');
|
||||
}}
|
||||
style={tw`w-9 rounded-full`}
|
||||
testID='create-tag-button'
|
||||
onPress={() => modalRef.current?.present()}
|
||||
style={tw`flex-row gap-1 rounded-full`}
|
||||
variant="gray"
|
||||
>
|
||||
<DotsThree weight="bold" size={20} color={'white'} />
|
||||
<Plus size={10} weight="bold" style={tw`text-white`} />
|
||||
<Text style={tw`text-xs text-ink`}>Add</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<View style={tw`flex-row flex-wrap gap-2`}>
|
||||
{tagData?.length === 0 ? (
|
||||
<Empty description="You have not created any tags" icon="Tags" />
|
||||
) : (
|
||||
tagData
|
||||
?.slice(0, 3)
|
||||
.map((tag) => (
|
||||
<View style={tw`relative -m-1`}>
|
||||
<Fade color='black' width={20} height="100%">
|
||||
<FlatList
|
||||
data={tagData}
|
||||
ListEmptyComponent={<Empty description="You have not created any tags" icon="Tags" />}
|
||||
numColumns={showAll ? 3 : 1}
|
||||
contentContainerStyle={twStyle(tagData?.length === 0 && 'w-full','px-5')}
|
||||
horizontal={showAll ? false : true}
|
||||
key={showAll ? '_tags' : 'alltagcols'}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
scrollEnabled={showAll ? false : true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
renderItem={({ item }) => (
|
||||
<TagItem
|
||||
key={tag.id}
|
||||
tag={tag}
|
||||
onPress={() =>
|
||||
navigation.navigate('Tag', { id: tag.id, color: tag.color! })
|
||||
}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
style={twStyle(showAll && 'max-w-[31%] flex-1')}
|
||||
key={item.id}
|
||||
tag={item}
|
||||
onPress={() =>
|
||||
navigation.navigate('Tag', { id: item.id, color: item.color! })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Fade>
|
||||
</View>
|
||||
<CreateTagModal ref={modalRef} />
|
||||
</View>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||
import { DrawerContentComponentProps } from '@react-navigation/drawer/lib/typescript/src/types';
|
||||
import { AppLogo } from '@sd/assets/images';
|
||||
import { JobManagerContextProvider, useLibraryQuery } from '@sd/client';
|
||||
import { Image } from 'expo-image';
|
||||
import { CheckCircle } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Platform, Pressable, Text, View } from 'react-native';
|
||||
import { JobManagerContextProvider, useLibraryQuery } from '@sd/client';
|
||||
import Layout from '~/constants/Layout';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import {
|
||||
Location,
|
||||
arraysEqual,
|
||||
humanizeSize,
|
||||
Location,
|
||||
useLibraryQuery,
|
||||
useOnlineLocations
|
||||
} from '@sd/client';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
@@ -49,8 +49,8 @@ const DrawerLocationItem: React.FC<DrawerLocationItemProps> = ({
|
||||
{location.name ?? ''}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={tw`rounded-md border border-app-lightborder bg-app-box px-1 py-0.5`}>
|
||||
<Text style={tw`text-[11px] font-medium text-ink-dull`} numberOfLines={1}>
|
||||
<View style={tw`rounded-md border border-app-box/70 bg-app/70 px-1 py-0.5`}>
|
||||
<Text style={tw`text-[11px] font-bold text-ink-dull`} numberOfLines={1}>
|
||||
{`${humanizeSize(location.size_in_bytes)}`}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -27,7 +27,7 @@ const FileRow = ({ data }: FileRowProps) => {
|
||||
height: getExplorerStore().listItemSize
|
||||
})}
|
||||
>
|
||||
<FileThumb data={data} size={0.6} />
|
||||
<FileThumb data={data} size={0.5} />
|
||||
<View style={tw`mx-2 flex-1 flex-row items-center justify-between border-b border-white/10 pb-3`}>
|
||||
<View style={tw`max-w-[80%]`}>
|
||||
<Text numberOfLines={1} style={tw`text-center text-sm font-medium text-ink`}>
|
||||
|
||||
@@ -6,10 +6,10 @@ import {
|
||||
BottomSheetHandleProps,
|
||||
BottomSheetModal,
|
||||
BottomSheetModalProps,
|
||||
BottomSheetScrollView
|
||||
BottomSheetScrollView,
|
||||
} from '@gorhom/bottom-sheet';
|
||||
import { X } from 'phosphor-react-native';
|
||||
import { forwardRef, ReactNode } from 'react';
|
||||
import { ReactNode, forwardRef } from 'react';
|
||||
import { Platform, Pressable, Text, View } from 'react-native';
|
||||
import useForwardedRef from '~/hooks/useForwardedRef';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Location, arraysEqual, humanizeSize, useOnlineLocations } from '@sd/client';
|
||||
import { DotsThreeOutlineVertical } from 'phosphor-react-native';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { arraysEqual, humanizeSize, Location, useOnlineLocations } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import FolderIcon from '../icons/FolderIcon';
|
||||
@@ -16,7 +16,7 @@ const GridLocation: React.FC<GridLocationProps> = ({ location, modalRef }: GridL
|
||||
const onlineLocations = useOnlineLocations();
|
||||
const online = onlineLocations.some((l) => arraysEqual(location.pub_id, l));
|
||||
return (
|
||||
<Card style={'h-auto flex-col justify-center gap-3'}>
|
||||
<Card style={'h-auto flex-col items-start justify-center gap-3'}>
|
||||
<View style={tw`w-full flex-col justify-between gap-1`}>
|
||||
<View style={tw`flex-row items-center justify-between`}>
|
||||
<View style={tw`relative`}>
|
||||
@@ -46,9 +46,11 @@ const GridLocation: React.FC<GridLocationProps> = ({ location, modalRef }: GridL
|
||||
{location.path}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={tw`text-left text-[13px] font-bold text-ink-dull`} numberOfLines={1}>
|
||||
<View style={tw`rounded-md border border-app-box/70 bg-app/70 px-1 py-0.5`}>
|
||||
<Text style={tw`text-xs font-bold text-ink-dull`} numberOfLines={1}>
|
||||
{`${humanizeSize(location.size_in_bytes)}`}
|
||||
</Text>
|
||||
</View>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -61,10 +61,10 @@ const ListLocation = ({ location }: ListLocationProps) => {
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={tw`flex-row items-center gap-3`}>
|
||||
<View style={tw`rounded-md border border-app-box bg-app p-1.5`}>
|
||||
<View style={tw`flex-row items-center gap-1.5`}>
|
||||
<View style={tw`rounded-md border border-app-box/70 bg-app/70 px-1.5 py-1`}>
|
||||
<Text
|
||||
style={tw`text-left text-xs font-medium text-ink-dull`}
|
||||
style={tw`text-xs font-bold text-ink-dull`}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{`${humanizeSize(location.size_in_bytes)}`}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Location } from '@sd/client';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable } from 'react-native';
|
||||
import { Location } from '@sd/client';
|
||||
import { twStyle } from '~/lib/tailwind';
|
||||
|
||||
import { ClassInput } from 'twrnc';
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
import { LocationModal } from '../modal/location/LocationModal';
|
||||
import GridLocation from './GridLocation';
|
||||
@@ -13,19 +14,21 @@ type LocationItemProps = {
|
||||
onPress: () => void;
|
||||
viewStyle?: 'grid' | 'list';
|
||||
editLocation: () => void;
|
||||
style?: ClassInput;
|
||||
};
|
||||
|
||||
export const LocationItem = ({
|
||||
location,
|
||||
onPress,
|
||||
editLocation,
|
||||
viewStyle = 'grid'
|
||||
viewStyle = 'grid',
|
||||
style
|
||||
}: LocationItemProps) => {
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
style={twStyle(viewStyle === 'grid' ? `w-[31.5%]` : `flex-1`)}
|
||||
style={twStyle(viewStyle === 'grid' ? `m-1 w-[112px]` : `flex-1`, style)}
|
||||
onPress={onPress}
|
||||
>
|
||||
{viewStyle === 'grid' ? (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Tag, getItemObject, useLibraryMutation, useLibraryQuery, useRspcContext } from "@sd/client";
|
||||
import { Tag, getItemObject, useLibraryMutation, useLibraryQuery, useRspcLibraryContext } from "@sd/client";
|
||||
import { CaretLeft, Plus } from "phosphor-react-native";
|
||||
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { FlatList, NativeScrollEvent, Pressable, Text, View } from "react-native";
|
||||
@@ -24,7 +24,7 @@ const AddTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||
const [startedScrolling, setStartedScrolling] = useState(false);
|
||||
const [reachedBottom, setReachedBottom] = useState(true); // needs to be set to true for initial rendering fade to be correct
|
||||
|
||||
const rspc = useRspcContext();
|
||||
const rspc = useRspcLibraryContext();
|
||||
const tagsQuery = useLibraryQuery(['tags.list']);
|
||||
const tagsObjectQuery = useLibraryQuery(['tags.getForObject', objectData?.id ?? -1]);
|
||||
const mutation = useLibraryMutation(['tags.assign'], {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import * as RNFS from '@dr.pogodin/react-native-fs';
|
||||
import { useLibraryMutation, useRspcLibraryContext } from '@sd/client';
|
||||
import { forwardRef, useCallback } from 'react';
|
||||
import { Alert, Platform, Text, View } from 'react-native';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import { useLibraryMutation, useRspcLibraryContext } from '@sd/client';
|
||||
import { Modal, ModalRef } from '~/components/layout/Modal';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import useForwardedRef from '~/hooks/useForwardedRef';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
|
||||
import { Icon } from '../icons/Icon';
|
||||
import { toast } from '../primitive/Toast';
|
||||
|
||||
// import * as ML from 'expo-media-library';
|
||||
|
||||
@@ -22,17 +23,27 @@ const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||
|
||||
const createLocation = useLibraryMutation('locations.create', {
|
||||
onError: (error, variables) => {
|
||||
modalRef.current?.close();
|
||||
//custom message handling
|
||||
if (error.message.startsWith("location already exists")) {
|
||||
return toast.error('This location has already been added');
|
||||
}
|
||||
switch (error.message) {
|
||||
case 'NEED_RELINK':
|
||||
if (!variables.dry_run) relinkLocation.mutate(variables.path);
|
||||
toast.info('Please relink the location');
|
||||
break;
|
||||
case 'ADD_LIBRARY':
|
||||
addLocationToLibrary.mutate(variables);
|
||||
break;
|
||||
default:
|
||||
toast.error(error.message);
|
||||
throw new Error('Unimplemented custom remote error handling');
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success('Location added successfully');
|
||||
},
|
||||
onSettled: () => {
|
||||
rspc.queryClient.invalidateQueries(['locations.list']);
|
||||
modalRef.current?.close();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRef } from 'react';
|
||||
import { useLibraryMutation, usePlausibleEvent, useRspcLibraryContext } from '@sd/client';
|
||||
import { useRef } from 'react';
|
||||
import { ConfirmModal, ModalRef } from '~/components/layout/Modal';
|
||||
import { toast } from '~/components/primitive/Toast';
|
||||
|
||||
type Props = {
|
||||
locationId: number;
|
||||
@@ -20,6 +21,10 @@ const DeleteLocationModal = ({ trigger, onSubmit, locationId, triggerStyle }: Pr
|
||||
onSuccess: () => {
|
||||
submitPlausibleEvent({ event: { type: 'locationDelete' } });
|
||||
onSubmit?.();
|
||||
toast.success('Location deleted successfully');
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message);
|
||||
},
|
||||
onSettled: () => {
|
||||
modalRef.current?.close();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRef } from 'react';
|
||||
import { useLibraryMutation, usePlausibleEvent, useRspcLibraryContext } from '@sd/client';
|
||||
import { useRef } from 'react';
|
||||
import { ConfirmModal, ModalRef } from '~/components/layout/Modal';
|
||||
import { toast } from '~/components/primitive/Toast';
|
||||
|
||||
type Props = {
|
||||
tagId: number;
|
||||
@@ -19,6 +20,7 @@ const DeleteTagModal = ({ trigger, onSubmit, tagId, triggerStyle }: Props) => {
|
||||
submitPlausibleEvent({ event: { type: 'tagDelete' } });
|
||||
onSubmit?.();
|
||||
rspc.queryClient.invalidateQueries(['tags.list']);
|
||||
toast.success('Tag deleted successfully');
|
||||
},
|
||||
onSettled: () => {
|
||||
modalRef.current?.close();
|
||||
|
||||
@@ -3,7 +3,8 @@ import {
|
||||
getItemObject,
|
||||
humanizeSize,
|
||||
useLibraryMutation,
|
||||
useLibraryQuery
|
||||
useLibraryQuery,
|
||||
useRspcContext
|
||||
} from '@sd/client';
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
@@ -28,6 +29,7 @@ import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { useActionsModalStore } from '~/stores/modalStore';
|
||||
|
||||
import FileInfoModal from './FileInfoModal';
|
||||
import RenameModal from './RenameModal';
|
||||
|
||||
type ActionsContainerProps = PropsWithChildren<{
|
||||
style?: ViewStyle;
|
||||
@@ -65,8 +67,10 @@ const ActionDivider = () => <View style={tw`my-3.5 h-[0.5px] bg-app-box`} />;
|
||||
|
||||
export const ActionsModal = () => {
|
||||
const fileInfoRef = useRef<ModalRef>(null);
|
||||
const renameRef = useRef<ModalRef>(null);
|
||||
|
||||
const { modalRef, data } = useActionsModalStore();
|
||||
const rspc = useRspcContext();
|
||||
|
||||
const objectData = data && getItemObject(data);
|
||||
const filePath = data && getIndexedItemFilePath(data);
|
||||
@@ -77,6 +81,13 @@ export const ActionsModal = () => {
|
||||
enabled: filePath != null
|
||||
});
|
||||
|
||||
const deleteFile = useLibraryMutation('files.deleteFiles', {
|
||||
onSuccess: () => {
|
||||
rspc.queryClient.invalidateQueries(['search.paths'])
|
||||
modalRef.current?.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
async function handleOpen() {
|
||||
const absolutePath = queriedFullPath.data;
|
||||
if (!absolutePath) return;
|
||||
@@ -141,7 +152,9 @@ export const ActionsModal = () => {
|
||||
/>
|
||||
</ActionsContainer>
|
||||
<ActionsContainer style={tw`mt-2`}>
|
||||
<ActionsItem icon={Pencil} title="Rename" />
|
||||
<ActionsItem onPress={() => {
|
||||
renameRef.current?.present();
|
||||
}} icon={Pencil} title="Rename" />
|
||||
<ActionDivider />
|
||||
<ActionsItem icon={Copy} title="Duplicate" />
|
||||
<ActionDivider />
|
||||
@@ -154,11 +167,19 @@ export const ActionsModal = () => {
|
||||
<ActionDivider />
|
||||
<ActionsItem icon={Package} title="Compress" />
|
||||
<ActionDivider />
|
||||
<ActionsItem icon={TrashSimple} title="Delete" isDanger />
|
||||
<ActionsItem icon={TrashSimple} title="Delete" isDanger onPress={async () => {
|
||||
if (filePath && filePath.location_id) {
|
||||
await deleteFile.mutateAsync({
|
||||
location_id: filePath.location_id,
|
||||
file_path_ids: [filePath.id]
|
||||
});
|
||||
}
|
||||
}} />
|
||||
</ActionsContainer>
|
||||
</View>
|
||||
)}
|
||||
</Modal>
|
||||
<RenameModal objectName={filePath?.name ?? ''} ref={renameRef} />
|
||||
<FileInfoModal ref={fileInfoRef} data={data} />
|
||||
</>
|
||||
);
|
||||
|
||||
90
apps/mobile/src/components/modal/inspector/RenameModal.tsx
Normal file
90
apps/mobile/src/components/modal/inspector/RenameModal.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { getIndexedItemFilePath, useLibraryMutation, useRspcLibraryContext } from '@sd/client';
|
||||
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { TextInput } from 'react-native-gesture-handler';
|
||||
import { Modal, ModalRef } from '~/components/layout/Modal';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { ModalInput } from '~/components/primitive/Input';
|
||||
import { toast } from '~/components/primitive/Toast';
|
||||
import useForwardedRef from '~/hooks/useForwardedRef';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { useActionsModalStore } from '~/stores/modalStore';
|
||||
|
||||
interface Props {
|
||||
objectName: string;
|
||||
}
|
||||
|
||||
const RenameModal = forwardRef<ModalRef, Props>((props, ref) => {
|
||||
const modalRef = useForwardedRef(ref);
|
||||
const [newName, setNewName] = useState('');
|
||||
const rspc = useRspcLibraryContext();
|
||||
const { data } = useActionsModalStore();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
|
||||
const filePathData = data && getIndexedItemFilePath(data);
|
||||
|
||||
const renameFile = useLibraryMutation(['files.renameFile'], {
|
||||
onSuccess: () => {
|
||||
modalRef.current?.dismiss();
|
||||
rspc.queryClient.invalidateQueries(['search.paths']);
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to rename object');
|
||||
}
|
||||
});
|
||||
|
||||
// set input value to object name on initial render
|
||||
useEffect(() => {
|
||||
setNewName(props.objectName);
|
||||
}, [props.objectName]);
|
||||
|
||||
const textRenameHandler = async () => {
|
||||
switch (data?.type) {
|
||||
case 'Path':
|
||||
case 'Object': {
|
||||
if (!filePathData) throw new Error('Failed to get file path object');
|
||||
|
||||
const { id, location_id } = filePathData;
|
||||
|
||||
if (!location_id) throw new Error('Missing location id');
|
||||
|
||||
await renameFile.mutateAsync({
|
||||
location_id: location_id,
|
||||
kind: {
|
||||
One: {
|
||||
from_file_path_id: id,
|
||||
to: newName
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={modalRef}
|
||||
title="Rename"
|
||||
onDismiss={() => setNewName(props.objectName)}
|
||||
enableContentPanningGesture={false}
|
||||
enablePanDownToClose={false}
|
||||
snapPoints={['20']}
|
||||
>
|
||||
<View style={tw`mt-2 flex-col gap-2 px-6`}>
|
||||
<ModalInput
|
||||
ref={inputRef}
|
||||
autoFocus
|
||||
onFocus={() => inputRef.current?.setSelection(0, newName.length)}
|
||||
value={newName}
|
||||
onChangeText={(t) => setNewName(t)}
|
||||
/>
|
||||
<Button disabled={newName.length === 0 || props.objectName === newName} onPress={textRenameHandler} variant="accent">
|
||||
<Text style={tw`font-medium text-ink`}>Save</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
export default RenameModal;
|
||||
@@ -1,16 +1,17 @@
|
||||
import { forwardRef, useEffect, useState } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import ColorPicker from 'react-native-wheel-color-picker';
|
||||
import {
|
||||
ToastDefautlColor,
|
||||
useLibraryMutation,
|
||||
usePlausibleEvent,
|
||||
useRspcLibraryContext
|
||||
} from '@sd/client';
|
||||
import { forwardRef, useEffect, useState } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import ColorPicker from 'react-native-wheel-color-picker';
|
||||
import { FadeInAnimation } from '~/components/animation/layout';
|
||||
import { Modal, ModalRef } from '~/components/layout/Modal';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { ModalInput } from '~/components/primitive/Input';
|
||||
import { toast } from '~/components/primitive/Toast';
|
||||
import useForwardedRef from '~/hooks/useForwardedRef';
|
||||
import { useKeyboard } from '~/hooks/useKeyboard';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
@@ -36,8 +37,12 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||
|
||||
rspc.queryClient.invalidateQueries(['tags.list']);
|
||||
|
||||
toast.success('Tag created successfully');
|
||||
submitPlausibleEvent({ event: { type: 'tagCreate' } });
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message);
|
||||
},
|
||||
onSettled: () => {
|
||||
// Close modal
|
||||
modalRef.current?.dismiss();
|
||||
@@ -57,7 +62,7 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||
return (
|
||||
<Modal
|
||||
ref={modalRef}
|
||||
snapPoints={['25']}
|
||||
snapPoints={['22']}
|
||||
title="Create Tag"
|
||||
onDismiss={() => {
|
||||
// Resets form onDismiss
|
||||
@@ -94,7 +99,7 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||
<Button
|
||||
variant="accent"
|
||||
onPress={() => createTag({ color: tagColor, name: tagName })}
|
||||
style={tw`mt-6`}
|
||||
style={tw`mt-2`}
|
||||
disabled={tagName.length === 0}
|
||||
>
|
||||
<Text style={tw`text-sm font-medium text-white`}>Create</Text>
|
||||
|
||||
@@ -14,17 +14,17 @@ export default function CategoriesScreen() {
|
||||
const kinds = useLibraryQuery(['library.kindStatistics']);
|
||||
const navigation = useNavigation<OverviewStackScreenProps<'Overview'>['navigation']>();
|
||||
return (
|
||||
<View style={tw`px-6`}>
|
||||
<View style={tw`px-5`}>
|
||||
<View style={tw`flex-row items-center justify-between pb-5`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>Categories</Text>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.navigate('Categories');
|
||||
}}
|
||||
style={tw`h-9 w-9 rounded-full`}
|
||||
style={tw`h-8 w-8 rounded-full`}
|
||||
variant="gray"
|
||||
>
|
||||
<DotsThree weight='bold' size={20} color={'white'} />
|
||||
<DotsThree weight='bold' size={18} color={'white'} />
|
||||
</Button>
|
||||
</View>
|
||||
<View style={tw`flex-row flex-wrap gap-2`}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import React, { useRef } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
|
||||
|
||||
@@ -25,10 +25,11 @@ const Locations = () => {
|
||||
<>
|
||||
<OverviewSection title="Locations" count={locations?.length}>
|
||||
<View style={tw`flex-row items-center`}>
|
||||
<Fade height={'100%'} width={30} color="black">
|
||||
<FlatList
|
||||
horizontal
|
||||
data={locations}
|
||||
contentContainerStyle={tw`px-7`}
|
||||
contentContainerStyle={tw`px-6`}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
keyExtractor={(location) => location.id.toString()}
|
||||
ItemSeparatorComponent={() => <View style={tw`w-2`} />}
|
||||
@@ -73,6 +74,7 @@ const Locations = () => {
|
||||
</Pressable>
|
||||
)}
|
||||
/>
|
||||
</Fade>
|
||||
</View>
|
||||
</OverviewSection>
|
||||
<ImportModal ref={modalRef} />
|
||||
|
||||
@@ -37,7 +37,7 @@ const StatItem = ({ title, bytes, isLoading, style }: StatItemProps) => {
|
||||
hidden: isLoading
|
||||
})}
|
||||
>
|
||||
<Text style={tw`text-sm font-bold text-zinc-400`}>{title}</Text>
|
||||
<Text style={tw`text-xs font-bold text-zinc-400`}>{title}</Text>
|
||||
<View style={tw`mt-1 flex-row items-baseline`}>
|
||||
<Text style={twStyle('text-xl font-bold tabular-nums text-white')}>{count}</Text>
|
||||
<Text style={tw`ml-1 text-sm text-zinc-400`}>{unit}</Text>
|
||||
@@ -104,7 +104,7 @@ const OverviewStats = ({ stats }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={tw`px-6`}>
|
||||
<View style={tw`px-5`}>
|
||||
<Text style={tw`pb-3 text-lg font-bold text-white`}>Statistics</Text>
|
||||
<View style={tw`flex-row gap-2`}>
|
||||
<View style={tw`h-full flex-1 flex-col gap-2`}>
|
||||
|
||||
@@ -8,10 +8,10 @@ const button = cva(['items-center justify-center rounded-md border shadow-sm'],
|
||||
variants: {
|
||||
variant: {
|
||||
danger: ['border-red-800 bg-red-600 shadow-none'],
|
||||
gray: ['border-app-lightborder bg-app-button shadow-none'],
|
||||
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-sidebar-line/60 bg-black shadow-none'],
|
||||
outline: ['border-app-lightborder bg-black shadow-none'],
|
||||
transparent: ['border-0 bg-black shadow-none'],
|
||||
dashed: ['border border-dashed border-app-line bg-transparent shadow-none']
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BottomSheetTextInput } from '@gorhom/bottom-sheet';
|
||||
import { cva, VariantProps } from 'class-variance-authority';
|
||||
import { Eye, EyeSlash } from 'phosphor-react-native';
|
||||
import { useState } from 'react';
|
||||
import { forwardRef, useState } from 'react';
|
||||
import { Pressable, TextInputProps as RNTextInputProps, TextInput, View } from 'react-native';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
@@ -23,28 +23,32 @@ const input = cva(['rounded-md border text-sm leading-tight shadow-sm'], {
|
||||
|
||||
type InputProps = VariantProps<typeof input> & RNTextInputProps;
|
||||
|
||||
export const Input = ({ variant, size, ...props }: InputProps) => {
|
||||
const { style, ...otherProps } = props;
|
||||
export const Input = forwardRef<TextInput, InputProps>((props, ref) => {
|
||||
const { style, variant, size, ...otherProps } = props;
|
||||
return (
|
||||
<TextInput
|
||||
ref={ref}
|
||||
selectionColor={tw.color('accent')}
|
||||
placeholderTextColor={tw.color('ink-faint')}
|
||||
style={twStyle(input({ variant, size }), style as string)}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
})
|
||||
|
||||
// To use in modals (for keyboard handling)
|
||||
export const ModalInput = ({ variant, size, ...props }: InputProps) => {
|
||||
const { style, ...otherProps } = props;
|
||||
export const ModalInput = forwardRef<any, InputProps>((props, ref) => {
|
||||
const { style, variant, size, ...otherProps } = props;
|
||||
return (
|
||||
<BottomSheetTextInput
|
||||
ref={ref}
|
||||
selectionColor={tw.color('accent')}
|
||||
placeholderTextColor={tw.color('ink-faint')}
|
||||
style={twStyle(input({ variant, size }), style as string)}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
})
|
||||
|
||||
// Same as Input but configured with password props & show/hide password button
|
||||
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import { CheckCircle, Info, WarningCircle } from 'phosphor-react-native';
|
||||
import { Text, View } from 'react-native';
|
||||
import Toast, { ToastConfig } from 'react-native-toast-message';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
|
||||
const baseStyles = 'w-[340px] flex-row overflow-hidden rounded-md border p-3 shadow-lg';
|
||||
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 toastConfig: ToastConfig = {
|
||||
success: ({ text1, ...rest }) => (
|
||||
<View style={tw.style(baseStyles, 'border-app-line bg-app-darkBox/90 ')}>
|
||||
<Text style={tw`text-sm font-medium text-ink`} numberOfLines={3}>
|
||||
<View style={tw.style(baseStyles)}>
|
||||
<View style={tw.style(containerStyle)}>
|
||||
<CheckCircle size={24} weight="fill" color={tw.color("text-green-500")} />
|
||||
<Text style={tw`self-center text-left text-sm font-medium text-ink`} numberOfLines={3}>
|
||||
{text1}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
error: ({ text1, ...rest }) => (
|
||||
<View style={tw.style(baseStyles, 'border-red-500 bg-red-500/90')}>
|
||||
<Text style={tw`text-sm font-medium text-ink`} numberOfLines={3}>
|
||||
<View style={tw.style(baseStyles)}>
|
||||
<View style={tw.style(containerStyle)}>
|
||||
<WarningCircle size={24} weight="fill" color={tw.color("text-red-500")} />
|
||||
<Text style={tw`self-center text-left text-sm font-medium text-ink`} numberOfLines={3}>
|
||||
{text1}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
info: ({ text1, ...rest }) => (
|
||||
<View style={tw.style(baseStyles, 'border-app-line bg-app-darkBox/90')}>
|
||||
<Text style={tw`text-sm font-medium text-ink`} numberOfLines={3}>
|
||||
<View style={tw.style(baseStyles)}>
|
||||
<View style={tw.style(containerStyle)}>
|
||||
<Info size={24} weight="fill" color={tw.color("text-accent")} />
|
||||
<Text style={tw`self-center text-left text-sm font-medium text-ink`} numberOfLines={3}>
|
||||
{text1}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ const GridTag = ({ tag, modalRef }: GridTagProps) => {
|
||||
<Card style={twStyle(`h-auto w-full flex-col justify-center gap-3`)}>
|
||||
<View style={tw`flex-row items-center justify-between`}>
|
||||
<View
|
||||
style={twStyle('h-[28px] w-[28px] rounded-full', {
|
||||
style={twStyle('h-5 w-5 rounded-full', {
|
||||
backgroundColor: tag.color!
|
||||
})}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Tag } from '@sd/client';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable } from 'react-native';
|
||||
import { Tag } from '@sd/client';
|
||||
import { twStyle } from '~/lib/tailwind';
|
||||
|
||||
import { ClassInput } from 'twrnc';
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
import { TagModal } from '../modal/tag/TagModal';
|
||||
import GridTag from './GridTag';
|
||||
@@ -12,14 +13,15 @@ type TagItemProps = {
|
||||
tag: Tag;
|
||||
onPress: () => void;
|
||||
viewStyle?: 'grid' | 'list';
|
||||
style?: ClassInput;
|
||||
};
|
||||
|
||||
export const TagItem = ({ tag, onPress, viewStyle = 'grid' }: TagItemProps) => {
|
||||
export const TagItem = ({ tag, onPress, style, viewStyle = 'grid' }: TagItemProps) => {
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
style={twStyle(viewStyle === 'grid' ? `w-[31.5%]` : `flex-1`)}
|
||||
style={twStyle(viewStyle === 'grid' ? `m-1 w-[112px]` : `flex-1`, style)}
|
||||
onPress={onPress}
|
||||
testID="browse-tag"
|
||||
>
|
||||
|
||||
@@ -60,7 +60,7 @@ export function useFiltersSearch(search: string) {
|
||||
const filters = [] as SearchFilterArgs[];
|
||||
|
||||
//It's a global search if no locations have been selected
|
||||
if (searchStore.filters.locations.length === 0 || !name || !ext) {
|
||||
if (searchStore.filters.locations.length === 0) {
|
||||
const locationIds = locations.data?.map((l) => l.id);
|
||||
if (locationIds) filters.push({ filePath: { locations: { in: locationIds } } });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useLibraryQuery, usePathsExplorerQuery } from '@sd/client';
|
||||
import { useLibraryQuery, useLibrarySubscription, usePathsExplorerQuery } from '@sd/client';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import Explorer from '~/components/explorer/Explorer';
|
||||
import Empty from '~/components/layout/Empty';
|
||||
@@ -19,6 +19,13 @@ export default function LocationScreen({ navigation, route }: BrowseStackScreenP
|
||||
.pop();
|
||||
}, [path])
|
||||
|
||||
// makes sure that the location shows newest/modified objects
|
||||
// when a location is opened
|
||||
useLibrarySubscription(
|
||||
['locations.quickRescan', { sub_path: path ?? '', location_id: id }],
|
||||
{ onData() {} }
|
||||
);
|
||||
|
||||
const paths = usePathsExplorerQuery({
|
||||
arg: {
|
||||
filters: [
|
||||
|
||||
@@ -5,10 +5,10 @@ appId: com.spacedrive.app
|
||||
id: 'browse-tab'
|
||||
- waitForAnimationToEnd
|
||||
- tapOn:
|
||||
id: 'navigate-tags-screen'
|
||||
- tapOn:
|
||||
id: 'create-tag-modal'
|
||||
id: 'create-tag-button'
|
||||
- inputText: 'MyTag'
|
||||
- tapOn:
|
||||
text: 'Create'
|
||||
- tapOn:
|
||||
id: 'show-all-tags-button'
|
||||
- assertVisible: 'MyTag'
|
||||
|
||||
BIN
pnpm-lock.yaml
generated
BIN
pnpm-lock.yaml
generated
Binary file not shown.
Reference in New Issue
Block a user