[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:
Utku 2024-04-01 03:41:00 -07:00 committed by GitHub
parent 08825dd22f
commit f5c258e627
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 456 additions and 60 deletions

View file

@ -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.']);

View 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;

View 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;

View 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;

View 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;

View file

@ -83,7 +83,7 @@ export default function Header({
<Pressable
hitSlop={24}
onPress={() => {
navigation.navigate('ExplorerSearch', {
navigation.navigate('SearchStack', {
screen: 'Search'
});
}}

View file

@ -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}

View file

@ -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}
/>
</>
);
};

View file

@ -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 }
})

View file

@ -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}

View file

@ -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} />
</>
);
};

View 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'>
>;

View file

@ -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';

View file

@ -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>();

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -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';

View file

@ -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';

View file

@ -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`}

View file

@ -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>
);
}

View file

@ -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'>) => {

View file

@ -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 = {

View file

@ -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>
);