mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-02 10:03:28 +00:00
[MOB-77] Mobile Drawer (#2255)
* drawer and navigation * Remove old job manager code * make edit location/tags UX better * hide settings bcs it exist on tabs * we don't do index on mobile for screens + some organization * make max w percantage
This commit is contained in:
parent
08825dd22f
commit
f5c258e627
|
@ -39,7 +39,7 @@ import { useTheme } from './hooks/useTheme';
|
|||
import { changeTwTheme, tw } from './lib/tailwind';
|
||||
import RootNavigator from './navigation';
|
||||
import OnboardingNavigator from './navigation/OnboardingNavigator';
|
||||
import { P2P } from './screens/p2p';
|
||||
import { P2P } from './screens/p2p/P2P';
|
||||
import { currentLibraryStore } from './utils/nav';
|
||||
|
||||
LogBox.ignoreLogs(['Sending `onAnimatedValueUpdate` with no listeners registered.']);
|
||||
|
|
74
apps/mobile/src/components/drawer/DrawerContent.tsx
Normal file
74
apps/mobile/src/components/drawer/DrawerContent.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||
import { DrawerContentComponentProps } from '@react-navigation/drawer/lib/typescript/src/types';
|
||||
import { AppLogo } from '@sd/assets/images';
|
||||
import { CheckCircle, Gear } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Image, Platform, Pressable, Text, View } from 'react-native';
|
||||
import { JobManagerContextProvider, useLibraryQuery } from '@sd/client';
|
||||
import Layout from '~/constants/Layout';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import { PulseAnimation } from '../animation/lottie';
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
import { JobManagerModal } from '../modal/job/JobManagerModal';
|
||||
import DrawerLibraryManager from './DrawerLibraryManager';
|
||||
import DrawerLocations from './DrawerLocations';
|
||||
import DrawerTags from './DrawerTags';
|
||||
|
||||
const drawerHeight = Platform.select({
|
||||
ios: Layout.window.height * 0.85,
|
||||
android: Layout.window.height * 0.9
|
||||
});
|
||||
|
||||
function JobIcon() {
|
||||
const { data: isActive } = useLibraryQuery(['jobs.isActive']);
|
||||
return isActive ? (
|
||||
<PulseAnimation style={tw`h-[24px] w-[32px]`} speed={1.5} />
|
||||
) : (
|
||||
<CheckCircle color="white" size={24} />
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: `navigation` is not typed here...
|
||||
const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => {
|
||||
// const stackName = getStackNameFromState(state);
|
||||
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
return (
|
||||
<DrawerContentScrollView style={tw`flex-1 px-3 py-2`} scrollEnabled={false}>
|
||||
<View style={twStyle('justify-between', { height: drawerHeight })}>
|
||||
<View>
|
||||
<View style={tw`flex flex-row items-center`}>
|
||||
<Image source={AppLogo} style={tw`h-[40px] w-[40px]`} />
|
||||
<Text style={tw`ml-2 text-lg font-bold text-ink`}>Spacedrive</Text>
|
||||
</View>
|
||||
<View style={tw`mt-6`} />
|
||||
{/* Library Manager */}
|
||||
<DrawerLibraryManager />
|
||||
{/* Locations */}
|
||||
<DrawerLocations />
|
||||
{/* Tags */}
|
||||
<DrawerTags />
|
||||
</View>
|
||||
<View style={tw`flex w-full flex-row items-center gap-x-4`}>
|
||||
{/* Settings */}
|
||||
{/* <Pressable
|
||||
onPress={() => navigation.navigate('SettingsStack', { screen: 'Settings' })}
|
||||
>
|
||||
<Gear color="white" size={24} />
|
||||
</Pressable> */}
|
||||
{/* Job Manager */}
|
||||
<JobManagerContextProvider>
|
||||
<Pressable onPress={() => modalRef.current?.present()}>
|
||||
<JobIcon />
|
||||
</Pressable>
|
||||
<JobManagerModal ref={modalRef} />
|
||||
</JobManagerContextProvider>
|
||||
</View>
|
||||
</View>
|
||||
</DrawerContentScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerContent;
|
122
apps/mobile/src/components/drawer/DrawerLibraryManager.tsx
Normal file
122
apps/mobile/src/components/drawer/DrawerLibraryManager.tsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import { useDrawerStatus } from '@react-navigation/drawer';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { MotiView } from 'moti';
|
||||
import { CaretRight, Gear, Lock, Plus } from 'phosphor-react-native';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Alert, Pressable, Text, View } from 'react-native';
|
||||
import { useClientContext } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { currentLibraryStore } from '~/utils/nav';
|
||||
|
||||
import { AnimatedHeight } from '../animation/layout';
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
import CreateLibraryModal from '../modal/CreateLibraryModal';
|
||||
import { Divider } from '../primitive/Divider';
|
||||
|
||||
const DrawerLibraryManager = () => {
|
||||
const [dropdownClosed, setDropdownClosed] = useState(true);
|
||||
|
||||
// Closes the dropdown when the drawer is closed
|
||||
const isDrawerOpen = useDrawerStatus() === 'open';
|
||||
useEffect(() => {
|
||||
if (!isDrawerOpen) setDropdownClosed(true);
|
||||
}, [isDrawerOpen]);
|
||||
|
||||
const { library: currentLibrary, libraries } = useClientContext();
|
||||
|
||||
const navigation = useNavigation();
|
||||
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Pressable onPress={() => setDropdownClosed((v) => !v)}>
|
||||
<View
|
||||
style={twStyle(
|
||||
'flex h-10 w-full flex-row items-center justify-between border bg-sidebar-box px-3 shadow-sm',
|
||||
dropdownClosed
|
||||
? 'rounded-md border-sidebar-line/50'
|
||||
: 'rounded-t-md border-sidebar-line border-b-app-box bg-sidebar-button'
|
||||
)}
|
||||
>
|
||||
<Text style={tw`text-sm font-semibold text-ink`}>
|
||||
{currentLibrary?.config.name}
|
||||
</Text>
|
||||
<MotiView
|
||||
animate={{ rotateZ: dropdownClosed ? '0deg' : '90deg' }}
|
||||
transition={{ type: 'timing', duration: 100 }}
|
||||
>
|
||||
<CaretRight color="white" size={18} weight="bold" />
|
||||
</MotiView>
|
||||
</View>
|
||||
</Pressable>
|
||||
<AnimatedHeight hide={dropdownClosed}>
|
||||
<View style={tw`rounded-b-md border-sidebar-line bg-sidebar-button p-2`}>
|
||||
{/* Libraries */}
|
||||
{libraries.data?.map((library) => {
|
||||
// console.log('library', library);
|
||||
return (
|
||||
<Pressable
|
||||
key={library.uuid}
|
||||
onPress={() => (currentLibraryStore.id = library.uuid)}
|
||||
>
|
||||
<View
|
||||
style={twStyle(
|
||||
'mt-1 p-2',
|
||||
currentLibrary?.uuid === library.uuid && 'rounded bg-accent'
|
||||
)}
|
||||
>
|
||||
<Text
|
||||
style={twStyle(
|
||||
'text-sm font-semibold text-ink',
|
||||
currentLibrary?.uuid === library.uuid && 'text-white'
|
||||
)}
|
||||
>
|
||||
{library.config.name}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
<Divider style={tw`my-2`} />
|
||||
{/* Menu */}
|
||||
{/* Create Library */}
|
||||
<Pressable
|
||||
style={tw`flex flex-row items-center px-1.5 py-[8px]`}
|
||||
onPress={() => modalRef.current?.present()}
|
||||
>
|
||||
<Plus size={18} weight="bold" color="white" style={tw`mr-2`} />
|
||||
<Text style={tw`text-sm font-semibold text-white`}>New Library</Text>
|
||||
</Pressable>
|
||||
<CreateLibraryModal ref={modalRef} />
|
||||
{/* Manage Library */}
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
navigation.navigate('Root', {
|
||||
screen: 'Home',
|
||||
params: {
|
||||
screen: 'SettingsStack',
|
||||
params: { screen: 'LibraryGeneralSettings' }
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
|
||||
<Gear size={18} weight="bold" color="white" style={tw`mr-2`} />
|
||||
<Text style={tw`text-sm font-semibold text-white`}>Manage Library</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
{/* Lock */}
|
||||
<Pressable onPress={() => Alert.alert('TODO')}>
|
||||
<View style={tw`flex flex-row items-center px-1.5 py-[8px]`}>
|
||||
<Lock size={18} weight="bold" color="white" style={tw`mr-2`} />
|
||||
<Text style={tw`text-sm font-semibold text-white`}>Lock</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
</View>
|
||||
</AnimatedHeight>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerLibraryManager;
|
77
apps/mobile/src/components/drawer/DrawerLocations.tsx
Normal file
77
apps/mobile/src/components/drawer/DrawerLocations.tsx
Normal file
|
@ -0,0 +1,77 @@
|
|||
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 { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import FolderIcon from '../icons/FolderIcon';
|
||||
import CollapsibleView from '../layout/CollapsibleView';
|
||||
import ImportModal from '../modal/ImportModal';
|
||||
|
||||
type DrawerLocationItemProps = {
|
||||
folderName: string;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
const DrawerLocationItem: React.FC<DrawerLocationItemProps> = (props) => {
|
||||
const { folderName, onPress } = props;
|
||||
|
||||
return (
|
||||
<Pressable onPress={onPress}>
|
||||
<View style={twStyle('mb-[4px] flex flex-row items-center rounded px-1 py-2')}>
|
||||
<FolderIcon size={20} />
|
||||
<Text style={twStyle('ml-1.5 font-medium text-gray-300')} numberOfLines={1}>
|
||||
{folderName}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
const DrawerLocations = () => {
|
||||
const navigation = useNavigation<DrawerNavigationHelpers>();
|
||||
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
const result = useLibraryQuery(['locations.list'], { keepPreviousData: true });
|
||||
useNodes(result.data?.nodes);
|
||||
const locations = useCache(result.data?.items);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CollapsibleView
|
||||
title="Locations"
|
||||
titleStyle={tw`text-sm font-semibold text-gray-300`}
|
||||
containerStyle={tw`mb-3 ml-1 mt-6`}
|
||||
>
|
||||
<View style={tw`mt-2`}>
|
||||
{locations?.map((location) => (
|
||||
<DrawerLocationItem
|
||||
key={location.id}
|
||||
folderName={location.name ?? ''}
|
||||
onPress={() =>
|
||||
navigation.navigate('BrowseStack', {
|
||||
screen: 'Location',
|
||||
params: { id: location.id }
|
||||
})
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
{/* Add Location */}
|
||||
<Pressable onPress={() => modalRef.current?.present()}>
|
||||
<View style={tw`mt-1 rounded border border-dashed border-app-line/80`}>
|
||||
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>
|
||||
Add Location
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
</CollapsibleView>
|
||||
<ImportModal ref={modalRef} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerLocations;
|
73
apps/mobile/src/components/drawer/DrawerTags.tsx
Normal file
73
apps/mobile/src/components/drawer/DrawerTags.tsx
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useRef } from 'react';
|
||||
import { ColorValue, Pressable, Text, View } from 'react-native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import CollapsibleView from '../layout/CollapsibleView';
|
||||
import CreateTagModal from '../modal/tag/CreateTagModal';
|
||||
|
||||
type DrawerTagItemProps = {
|
||||
tagName: string;
|
||||
tagColor: ColorValue;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
const DrawerTagItem: React.FC<DrawerTagItemProps> = (props) => {
|
||||
const { tagName, tagColor, onPress } = props;
|
||||
return (
|
||||
<Pressable onPress={onPress} testID="drawer-tag">
|
||||
<View style={twStyle('mb-[4px] flex flex-row items-center rounded px-1 py-2')}>
|
||||
<View style={twStyle('h-3.5 w-3.5 rounded-full', { backgroundColor: tagColor })} />
|
||||
<Text style={twStyle('ml-2 text-sm font-medium text-gray-300')} numberOfLines={1}>
|
||||
{tagName}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
const DrawerTags = () => {
|
||||
const navigation = useNavigation<DrawerNavigationHelpers>();
|
||||
|
||||
const tags = useLibraryQuery(['tags.list']);
|
||||
useNodes(tags.data?.nodes);
|
||||
const tagData = useCache(tags.data?.items);
|
||||
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
return (
|
||||
<CollapsibleView
|
||||
title="Tags"
|
||||
titleStyle={tw`text-sm font-semibold text-gray-300`}
|
||||
containerStyle={tw`mb-3 ml-1 mt-6`}
|
||||
>
|
||||
<View style={tw`mt-2`}>
|
||||
{tagData?.map((tag) => (
|
||||
<DrawerTagItem
|
||||
key={tag.id}
|
||||
tagName={tag.name!}
|
||||
onPress={() =>
|
||||
navigation.navigate('BrowseStack', {
|
||||
screen: 'Tag',
|
||||
params: { id: tag.id }
|
||||
})
|
||||
}
|
||||
tagColor={tag.color as ColorValue}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
{/* Add Tag */}
|
||||
<Pressable onPress={() => modalRef.current?.present()}>
|
||||
<View style={tw`mt-1 rounded border border-dashed border-app-line/80`}>
|
||||
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Tag</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
<CreateTagModal ref={modalRef} />
|
||||
</CollapsibleView>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerTags;
|
|
@ -83,7 +83,7 @@ export default function Header({
|
|||
<Pressable
|
||||
hitSlop={24}
|
||||
onPress={() => {
|
||||
navigation.navigate('ExplorerSearch', {
|
||||
navigation.navigate('SearchStack', {
|
||||
screen: 'Search'
|
||||
});
|
||||
}}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { DotsThreeOutlineVertical } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { Swipeable } from 'react-native-gesture-handler';
|
||||
import { arraysEqual, byteSize, Location, useOnlineLocations } from '@sd/client';
|
||||
|
@ -8,20 +9,22 @@ import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
|||
|
||||
import FolderIcon from '../icons/FolderIcon';
|
||||
import Card from '../layout/Card';
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
import RightActions from './RightActions';
|
||||
|
||||
interface ListLocationProps {
|
||||
location: Location;
|
||||
modalRef: React.RefObject<ModalRef>;
|
||||
}
|
||||
|
||||
const ListLocation = ({ location, modalRef }: ListLocationProps) => {
|
||||
const ListLocation = ({ location }: ListLocationProps) => {
|
||||
const swipeRef = useRef<Swipeable>(null);
|
||||
|
||||
const navigation = useNavigation<SettingsStackScreenProps<'LocationSettings'>['navigation']>();
|
||||
const onlineLocations = useOnlineLocations();
|
||||
const online = onlineLocations.some((l) => arraysEqual(location.pub_id, l));
|
||||
|
||||
return (
|
||||
<Swipeable
|
||||
ref={swipeRef}
|
||||
containerStyle={tw`rounded-md border border-app-cardborder bg-app-card`}
|
||||
enableTrackpadTwoFingerGesture
|
||||
renderRightActions={(progress, _, swipeable) => (
|
||||
|
@ -69,7 +72,7 @@ const ListLocation = ({ location, modalRef }: ListLocationProps) => {
|
|||
{`${byteSize(location.size_in_bytes)}`}
|
||||
</Text>
|
||||
</View>
|
||||
<Pressable hitSlop={24} onPress={() => modalRef.current?.present()}>
|
||||
<Pressable hitSlop={24} onPress={() => swipeRef.current?.openRight()}>
|
||||
<DotsThreeOutlineVertical
|
||||
weight="fill"
|
||||
size={20}
|
||||
|
|
|
@ -29,19 +29,21 @@ export const LocationItem = ({
|
|||
onPress={onPress}
|
||||
>
|
||||
{viewStyle === 'grid' ? (
|
||||
<GridLocation location={location} modalRef={modalRef} />
|
||||
<>
|
||||
<GridLocation location={location} modalRef={modalRef} />
|
||||
<LocationModal
|
||||
editLocation={() => {
|
||||
editLocation();
|
||||
modalRef.current?.close();
|
||||
}}
|
||||
locationId={location.id}
|
||||
ref={modalRef}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<ListLocation location={location} modalRef={modalRef} />
|
||||
<ListLocation location={location} />
|
||||
)}
|
||||
</Pressable>
|
||||
<LocationModal
|
||||
editLocation={() => {
|
||||
editLocation();
|
||||
modalRef.current?.close();
|
||||
}}
|
||||
locationId={location.id}
|
||||
ref={modalRef}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Pressable, Text, View } from 'react-native';
|
|||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
|
||||
|
||||
import Fade from '../layout/Fade';
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
|
@ -15,7 +15,7 @@ import OverviewSection from './OverviewSection';
|
|||
import StatCard from './StatCard';
|
||||
|
||||
const Locations = () => {
|
||||
const navigation = useNavigation<BrowseStackScreenProps<'Browse'>['navigation']>();
|
||||
const navigation = useNavigation<OverviewStackScreenProps<'Overview'>['navigation']>();
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
const locationsQuery = useLibraryQuery(['locations.list']);
|
||||
|
@ -60,7 +60,8 @@ const Locations = () => {
|
|||
renderItem={({ item }) => (
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
navigation.navigate('BrowseStack', {
|
||||
navigation.jumpTo('BrowseStack', {
|
||||
initial: false,
|
||||
screen: 'Location',
|
||||
params: { id: item.id }
|
||||
})
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import { DotsThreeOutlineVertical } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { Swipeable } from 'react-native-gesture-handler';
|
||||
import { ClassInput } from 'twrnc';
|
||||
import { Tag } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
import RightActions from './RightActions';
|
||||
|
||||
interface ListTagProps {
|
||||
tag: Tag;
|
||||
tagStyle?: ClassInput;
|
||||
modalRef: React.RefObject<ModalRef>;
|
||||
}
|
||||
|
||||
const ListTag = ({ tag, tagStyle, modalRef }: ListTagProps) => {
|
||||
const ListTag = ({ tag, tagStyle }: ListTagProps) => {
|
||||
const swipeRef = useRef<Swipeable>(null);
|
||||
|
||||
return (
|
||||
<Swipeable
|
||||
ref={swipeRef}
|
||||
containerStyle={tw`rounded-md border border-app-cardborder bg-app-card p-3`}
|
||||
enableTrackpadTwoFingerGesture
|
||||
renderRightActions={(progress, _, swipeable) => (
|
||||
|
@ -39,7 +41,7 @@ const ListTag = ({ tag, tagStyle, modalRef }: ListTagProps) => {
|
|||
{tag.name}
|
||||
</Text>
|
||||
</View>
|
||||
<Pressable hitSlop={24} onPress={() => modalRef.current?.present()}>
|
||||
<Pressable hitSlop={24} onPress={() => swipeRef.current?.openRight()}>
|
||||
<DotsThreeOutlineVertical
|
||||
weight="fill"
|
||||
size={20}
|
||||
|
|
|
@ -24,12 +24,14 @@ export const TagItem = ({ tag, onPress, viewStyle = 'grid' }: TagItemProps) => {
|
|||
testID="browse-tag"
|
||||
>
|
||||
{viewStyle === 'grid' ? (
|
||||
<GridTag tag={tag} modalRef={modalRef} />
|
||||
<>
|
||||
<GridTag tag={tag} modalRef={modalRef} />
|
||||
<TagModal ref={modalRef} tag={tag} />
|
||||
</>
|
||||
) : (
|
||||
<ListTag tag={tag} modalRef={modalRef} />
|
||||
<ListTag tag={tag} />
|
||||
)}
|
||||
</Pressable>
|
||||
<TagModal ref={modalRef} tag={tag} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
39
apps/mobile/src/navigation/DrawerNavigator.tsx
Normal file
39
apps/mobile/src/navigation/DrawerNavigator.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { createDrawerNavigator, DrawerScreenProps } from '@react-navigation/drawer';
|
||||
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import DrawerContent from '~/components/drawer/DrawerContent';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
|
||||
import type { RootStackParamList } from '.';
|
||||
import type { TabParamList } from './TabNavigator';
|
||||
import TabNavigator from './TabNavigator';
|
||||
|
||||
const Drawer = createDrawerNavigator<DrawerNavParamList>();
|
||||
|
||||
export default function DrawerNavigator() {
|
||||
return (
|
||||
<Drawer.Navigator
|
||||
id="drawer"
|
||||
initialRouteName="Home"
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
drawerStyle: { backgroundColor: tw.color('app-darkBox'), width: '70%' },
|
||||
overlayColor: 'transparent',
|
||||
drawerType: 'slide',
|
||||
swipeEdgeWidth: 50
|
||||
}}
|
||||
drawerContent={(props) => <DrawerContent {...(props as any)} />}
|
||||
>
|
||||
<Drawer.Screen name="Home" component={TabNavigator} />
|
||||
</Drawer.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
export type DrawerNavParamList = {
|
||||
Home: NavigatorScreenParams<TabParamList>;
|
||||
};
|
||||
|
||||
export type HomeDrawerScreenProps<Screen extends keyof DrawerNavParamList> = CompositeScreenProps<
|
||||
DrawerScreenProps<DrawerNavParamList, Screen>,
|
||||
NativeStackScreenProps<RootStackParamList, 'Root'>
|
||||
>;
|
|
@ -1,5 +1,5 @@
|
|||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { OnboardingContext, useContextValue } from '~/screens/onboarding/context';
|
||||
import { OnboardingContext, useContextValue } from '~/components/context/OnboardingContext';
|
||||
import CreatingLibraryScreen from '~/screens/onboarding/CreatingLibrary';
|
||||
import GetStartedScreen from '~/screens/onboarding/GetStarted';
|
||||
import NewLibraryScreen from '~/screens/onboarding/NewLibrary';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
import Header from '~/components/header/Header';
|
||||
import SearchScreen from '~/screens/search';
|
||||
import FiltersScreen from '~/screens/search/Filters';
|
||||
import SearchScreen from '~/screens/search/Search';
|
||||
|
||||
const Stack = createNativeStackNavigator<SearchStackParamList>();
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ export default function TabNavigator() {
|
|||
* TouchableWithoutFeedback is used to prevent Android ripple effect
|
||||
* State is being used to control the animation and make Rive work
|
||||
* Tab.Screen listeners are needed because if a user taps on the tab text only, the animation won't play
|
||||
* This may be revisted in the future to update accordingly
|
||||
* This may be revisited in the future to update accordingly
|
||||
*/
|
||||
tabBarIcon: () => (
|
||||
<TouchableWithoutFeedback
|
||||
|
|
|
@ -2,17 +2,21 @@ import { NavigatorScreenParams } from '@react-navigation/native';
|
|||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import NotFoundScreen from '~/screens/NotFound';
|
||||
|
||||
import DrawerNavigator, { DrawerNavParamList } from './DrawerNavigator';
|
||||
import SearchStack, { SearchStackParamList } from './SearchStack';
|
||||
import TabNavigator, { TabParamList } from './TabNavigator';
|
||||
|
||||
const Stack = createNativeStackNavigator<RootStackParamList>();
|
||||
// This is the main navigator we nest everything under.
|
||||
export default function RootNavigator() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Root">
|
||||
<Stack.Screen name="Root" component={TabNavigator} options={{ headerShown: false }} />
|
||||
<Stack.Screen
|
||||
name="ExplorerSearch"
|
||||
name="Root"
|
||||
component={DrawerNavigator}
|
||||
options={{ headerShown: false }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="SearchStack"
|
||||
component={SearchStack}
|
||||
options={{ headerShown: false }}
|
||||
/>
|
||||
|
@ -22,8 +26,8 @@ export default function RootNavigator() {
|
|||
}
|
||||
|
||||
export type RootStackParamList = {
|
||||
Root: NavigatorScreenParams<TabParamList>;
|
||||
ExplorerSearch: NavigatorScreenParams<SearchStackParamList>;
|
||||
Root: NavigatorScreenParams<DrawerNavParamList>;
|
||||
SearchStack: NavigatorScreenParams<SearchStackParamList>;
|
||||
NotFound: undefined;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { CompositeScreenProps } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import BrowseScreen from '~/screens/browse';
|
||||
import BrowseScreen from '~/screens/browse/Browse';
|
||||
import LibraryScreen from '~/screens/browse/Library';
|
||||
import LocationScreen from '~/screens/browse/Location';
|
||||
import LocationsScreen from '~/screens/browse/Locations';
|
||||
import TagScreen from '~/screens/browse/Tag';
|
||||
import TagsScreen from '~/screens/browse/Tags';
|
||||
import LocationScreen from '~/screens/Location';
|
||||
import TagScreen from '~/screens/Tag';
|
||||
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
|
@ -47,7 +47,7 @@ export default function BrowseStack() {
|
|||
name="Tag"
|
||||
component={TagScreen}
|
||||
options={{
|
||||
header: (route) => <Header routeTitle route={route} headerKind="tag" navBack />
|
||||
header: (route) => <Header navBack routeTitle route={route} headerKind="tag" />
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { CompositeScreenProps } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import NetworkScreen from '~/screens/network';
|
||||
import NetworkScreen from '~/screens/network/Network';
|
||||
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CompositeScreenProps } from '@react-navigation/native';
|
|||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import CategoriesScreen from '~/screens/overview/Categories';
|
||||
import OverviewScreen from '~/screens/overview/index';
|
||||
import OverviewScreen from '~/screens/overview/Overview';
|
||||
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ export default function NotFoundScreen({ navigation }: RootStackScreenProps<'Not
|
|||
<TouchableOpacity
|
||||
onPress={() =>
|
||||
navigation.replace('Root', {
|
||||
screen: 'BrowseStack',
|
||||
params: { screen: 'Browse' }
|
||||
screen: 'Home',
|
||||
params: { screen: 'BrowseStack', params: { screen: 'Browse' } }
|
||||
})
|
||||
}
|
||||
style={tw`mt-4 py-4`}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { CheckCircle } from 'phosphor-react-native';
|
||||
import React from 'react';
|
||||
import { Pressable, View } from 'react-native';
|
||||
import { JobManagerContextProvider, useLibraryQuery } from '@sd/client';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { PulseAnimation } from '~/components/animation/lottie';
|
||||
import BrowseCategories from '~/components/browse/BrowseCategories';
|
||||
import BrowseLocations from '~/components/browse/BrowseLocations';
|
||||
|
@ -9,7 +8,6 @@ import BrowseTags from '~/components/browse/BrowseTags';
|
|||
import Jobs from '~/components/browse/Jobs';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { JobManagerModal } from '~/components/modal/job/JobManagerModal';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
|
||||
function JobIcon() {
|
||||
|
@ -30,15 +28,6 @@ export default function BrowseScreen() {
|
|||
<BrowseLocations />
|
||||
<BrowseTags />
|
||||
<Jobs />
|
||||
{/* TODO: Remove this when the new job manager is live, this is here for debugging purposes. */}
|
||||
<View style={tw`w-full flex-row items-center gap-x-4`}>
|
||||
<JobManagerContextProvider>
|
||||
<Pressable onPress={() => modalRef.current?.present()}>
|
||||
<JobIcon />
|
||||
</Pressable>
|
||||
<JobManagerModal ref={modalRef} />
|
||||
</JobManagerContextProvider>
|
||||
</View>
|
||||
</ScreenContainer>
|
||||
);
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import { Controller } from 'react-hook-form';
|
||||
import { Alert, Text, View } from 'react-native';
|
||||
import { useOnboardingContext } from '~/components/context/OnboardingContext';
|
||||
import { Icon } from '~/components/icons/Icon';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { Input } from '~/components/primitive/Input';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
|
||||
|
||||
import { useOnboardingContext } from './context';
|
||||
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './GetStarted';
|
||||
|
||||
const NewLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'NewLibrary'>) => {
|
||||
|
|
|
@ -2,11 +2,10 @@ import { ArrowRight } from 'phosphor-react-native';
|
|||
import React from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Linking, Pressable, Text, View, ViewStyle } from 'react-native';
|
||||
import { useOnboardingContext } from '~/components/context/OnboardingContext';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
|
||||
|
||||
import { useOnboardingContext } from './context';
|
||||
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './GetStarted';
|
||||
|
||||
type RadioButtonProps = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { CaretRight, Pen, Trash } from 'phosphor-react-native';
|
||||
import { DotsThreeOutlineVertical, Pen, Trash } from 'phosphor-react-native';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Animated, FlatList, Text, View } from 'react-native';
|
||||
import { Animated, FlatList, Pressable, Text, View } from 'react-native';
|
||||
import { Swipeable } from 'react-native-gesture-handler';
|
||||
import { LibraryConfigWrapped, useBridgeQuery, useCache, useNodes } from '@sd/client';
|
||||
import Fade from '~/components/layout/Fade';
|
||||
|
@ -49,8 +49,11 @@ function LibraryItem({
|
|||
);
|
||||
};
|
||||
|
||||
const swipeRef = useRef<Swipeable>(null);
|
||||
|
||||
return (
|
||||
<Swipeable
|
||||
ref={swipeRef}
|
||||
containerStyle={twStyle(
|
||||
index !== 0 && 'mt-2',
|
||||
'rounded-lg border border-app-cardborder bg-app-card px-4 py-3'
|
||||
|
@ -63,7 +66,13 @@ function LibraryItem({
|
|||
<Text style={tw`text-md font-semibold text-ink`}>{library.config.name}</Text>
|
||||
<Text style={tw`mt-1 text-xs text-ink-dull`}>{library.uuid}</Text>
|
||||
</View>
|
||||
<CaretRight color={tw.color('ink')} size={20} />
|
||||
<Pressable onPress={() => swipeRef.current?.openRight()}>
|
||||
<DotsThreeOutlineVertical
|
||||
weight="fill"
|
||||
size={20}
|
||||
color={tw.color('ink-dull')}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
</Swipeable>
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue