Revert "[MOB-85] Better headers" (#2376)

Revert "[MOB-85] Better headers (#2375)"

This reverts commit 6a556a457d.
This commit is contained in:
Utku 2024-04-23 19:20:59 -04:00 committed by GitHub
parent 6a556a457d
commit e009a0478c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 285 additions and 460 deletions

View file

@ -1,8 +1,8 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { DotsThreeOutline, Plus } from 'phosphor-react-native'; import { DotsThreeOutline, Plus } from 'phosphor-react-native';
import { useRef } from 'react'; import { useRef } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { ModalRef } from '~/components/layout/Modal'; import { ModalRef } from '~/components/layout/Modal';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack'; import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
@ -64,7 +64,7 @@ const BrowseLocations = () => {
initial: false initial: false
}) })
} }
onPress={() => navigation.navigate('Location', { id: location.id, title: location.name })} onPress={() => navigation.navigate('Location', { id: location.id })}
/> />
))} ))}
</> </>

View file

@ -1,8 +1,8 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { DotsThreeOutline, Plus } from 'phosphor-react-native'; import { DotsThreeOutline, Plus } from 'phosphor-react-native';
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { ModalRef } from '~/components/layout/Modal'; import { ModalRef } from '~/components/layout/Modal';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack'; import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
@ -57,7 +57,7 @@ const BrowseTags = () => {
key={tag.id} key={tag.id}
tag={tag} tag={tag}
onPress={() => onPress={() =>
navigation.navigate('Tag', { id: tag.id, color: tag.color!, title: tag.name }) navigation.navigate('Tag', { id: tag.id, color: tag.color! })
} }
/> />
)) ))

View file

@ -1,22 +1,19 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { NativeStackHeaderProps } from '@react-navigation/native-stack';
import { SearchData, isPath, type ExplorerItem } from '@sd/client';
import { FlashList } from '@shopify/flash-list'; import { FlashList } from '@shopify/flash-list';
import { UseInfiniteQueryResult } from '@tanstack/react-query'; import { UseInfiniteQueryResult } from '@tanstack/react-query';
import { ActivityIndicator, Pressable } from 'react-native'; import { ActivityIndicator, Pressable } from 'react-native';
import { SharedValue } from 'react-native-reanimated'; import { isPath, SearchData, type ExplorerItem } from '@sd/client';
import Layout from '~/constants/Layout'; import Layout from '~/constants/Layout';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack'; import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
import { useExplorerStore } from '~/stores/explorerStore'; import { useExplorerStore } from '~/stores/explorerStore';
import { useActionsModalStore } from '~/stores/modalStore'; import { useActionsModalStore } from '~/stores/modalStore';
import { HeaderProps } from '../header/Header';
import ScreenContainer from '../layout/ScreenContainer'; import ScreenContainer from '../layout/ScreenContainer';
import FileItem from './FileItem'; import FileItem from './FileItem';
import FileRow from './FileRow'; import FileRow from './FileRow';
import Menu from './menu/Menu'; import Menu from './menu/Menu';
type ExplorerProps = { type ExplorerProps = {
tabHeight?: boolean; tabHeight?: boolean;
items: ExplorerItem[] | null; items: ExplorerItem[] | null;
@ -24,15 +21,13 @@ type ExplorerProps = {
loadMore: () => void; loadMore: () => void;
query: UseInfiniteQueryResult<SearchData<ExplorerItem>>; query: UseInfiniteQueryResult<SearchData<ExplorerItem>>;
count?: number; count?: number;
scrollY?: SharedValue<number>;
route?: NativeStackHeaderProps['route'];
headerKind?: HeaderProps['headerKind'];
hideHeader?: boolean;
}; };
const Explorer = (props: ExplorerProps) => { const Explorer = (props: ExplorerProps) => {
const navigation = useNavigation<BrowseStackScreenProps<'Location'>['navigation']>(); const navigation = useNavigation<BrowseStackScreenProps<'Location'>['navigation']>();
const store = useExplorerStore(); const store = useExplorerStore();
const { modalRef, setData } = useActionsModalStore(); const { modalRef, setData } = useActionsModalStore();
function handlePress(data: ExplorerItem) { function handlePress(data: ExplorerItem) {
@ -48,20 +43,8 @@ const Explorer = (props: ExplorerProps) => {
} }
return ( return (
<ScreenContainer <ScreenContainer tabHeight={props.tabHeight} scrollview={false} style={'gap-0 py-0'}>
hideHeader={props.hideHeader} <Menu />
header={{
scrollY: props.scrollY,
route: props.route,
headerKind: props.headerKind,
showSearch: true,
navBack: true,
}}
tabHeight={props.tabHeight}
scrollview={false}
style={'gap-0 py-0'}
>
<Menu/>
{/* Items */} {/* Items */}
<FlashList <FlashList
key={store.layoutMode} key={store.layoutMode}
@ -83,11 +66,6 @@ const Explorer = (props: ExplorerProps) => {
)} )}
</Pressable> </Pressable>
)} )}
scrollEventThrottle={1}
onScroll={(e) => {
if (!props.scrollY) return;
props.scrollY.value = e.nativeEvent.contentOffset.y;
}}
contentContainerStyle={tw`px-2 py-5`} contentContainerStyle={tw`px-2 py-5`}
extraData={store.layoutMode} extraData={store.layoutMode}
estimatedItemSize={ estimatedItemSize={

View file

@ -9,6 +9,7 @@ import SortByMenu from './SortByMenu';
const Menu = () => { const Menu = () => {
const store = useExplorerStore(); const store = useExplorerStore();
return ( return (
<AnimatePresence> <AnimatePresence>
{store.toggleMenu && ( {store.toggleMenu && (

View file

@ -2,42 +2,32 @@ import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { NativeStackHeaderProps } from '@react-navigation/native-stack'; import { NativeStackHeaderProps } from '@react-navigation/native-stack';
import { ArrowLeft, DotsThreeOutline, List, MagnifyingGlass } from 'phosphor-react-native'; import { ArrowLeft, DotsThreeOutline, List, MagnifyingGlass } from 'phosphor-react-native';
import React from 'react'; import { Platform, Pressable, Text, View } from 'react-native';
import { Platform, Pressable, View } from 'react-native';
import Animated, {
Extrapolation,
SharedValue,
interpolate,
useAnimatedStyle
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import { getExplorerStore, useExplorerStore } from '~/stores/explorerStore'; import { getExplorerStore, useExplorerStore } from '~/stores/explorerStore';
import { Icon } from '../icons/Icon'; import { Icon } from '../icons/Icon';
import { AnimatedPressable } from '../reanimated/components';
import Search from '../search/Search'; import Search from '../search/Search';
type HeaderProps = {
type Props = {
title?: string; //title of the page title?: string; //title of the page
showSearch?: boolean; //show the search button showSearch?: boolean; //show the search button
showDrawer?: boolean; //show the drawer button showDrawer?: boolean; //show the drawer button
searchType?: 'location' | 'categories' | 'tags'; //Temporary searchType?: 'explorer' | 'location' | 'categories'; //Temporary
navBack?: boolean; //navigate back to the previous screen navBack?: boolean; //navigate back to the previous screen
headerKind?: 'default' | 'location' | 'tag'; //kind of header headerKind?: 'default' | 'location' | 'tag'; //kind of header
route?: never; route?: never;
scrollY?: SharedValue<number>; //scrollY of screen routeTitle?: never;
}; };
//you can pass in a routeTitle only if route is passed in //you can pass in a routeTitle only if route is passed in
export type HeaderProps = type Props =
| Props | HeaderProps
| ({ | ({
route: NativeStackHeaderProps['route']; route: NativeStackHeaderProps;
routeTitle?: boolean; routeTitle?: boolean;
} & Omit<Props, 'route' | 'routeTitle'>); } & Omit<HeaderProps, 'route' | 'routeTitle'>);
// Default header with search bar and button to open drawer // Default header with search bar and button to open drawer
export default function Header({ export default function Header({
@ -45,94 +35,55 @@ export default function Header({
searchType, searchType,
navBack, navBack,
route, route,
routeTitle,
headerKind = 'default', headerKind = 'default',
showDrawer = false, showDrawer = false,
showSearch = false, showSearch = true
scrollY }: Props) {
}: HeaderProps) {
const navigation = useNavigation<DrawerNavigationHelpers>(); const navigation = useNavigation<DrawerNavigationHelpers>();
const explorerStore = useExplorerStore(); const explorerStore = useExplorerStore();
const routeParams = route?.params as any; const routeParams = route?.route.params as any;
const headerSafeArea = useSafeAreaInsets(); const headerHeight = useSafeAreaInsets().top;
const isAndroid = Platform.OS === 'android'; const isAndroid = Platform.OS === 'android';
const scrollYTitle = useAnimatedStyle(() => {
return {
fontSize: interpolate(scrollY?.value || 0, [0, 50], [20, 16], Extrapolation.CLAMP)
};
});
const scrollYHeader = useAnimatedStyle(() => {
// this makes sure the header looks good on different devices
const outputRange = [headerSafeArea.top + (isAndroid ? 56 : 40), headerSafeArea.top + (isAndroid ? 44 : 32)];
return {
height: interpolate(
scrollY?.value || 0,
[0, 50],
outputRange,
Extrapolation.CLAMP
)
};
});
const scrollYIcon = useAnimatedStyle(() => {
return {
transform: [
{
scale: interpolate(scrollY?.value || 0, [0, 50], [1, 0.95], Extrapolation.CLAMP)
}
]
};
});
return ( return (
<Animated.View <View
style={[ style={twStyle('relative h-auto w-full border-b border-app-cardborder bg-app-header', {
twStyle('mt-0 w-full border-b border-app-cardborder bg-app-header', { paddingTop: headerHeight + (isAndroid ? 15 : 0)
paddingTop: headerSafeArea.top + (isAndroid ? 15 : 5), })}
}),
scrollYHeader
]}
> >
<View style={tw`mx-auto h-auto w-full justify-center px-5 pb-6`}> <View style={tw`mx-auto h-auto w-full justify-center px-5 pb-4`}>
<View style={tw`w-full flex-row items-center justify-between`}> <View style={tw`w-full flex-row items-center justify-between`}>
<View style={tw`flex-row items-center`}> <View style={tw`flex-row items-center gap-3`}>
{navBack && ( {navBack && (
<AnimatedPressable <Pressable
style={scrollYIcon}
hitSlop={24} hitSlop={24}
onPress={() => navigation.goBack()} onPress={() => {
navigation.goBack();
}}
> >
<ArrowLeft size={23} color={tw.color('ink')} /> <ArrowLeft size={23} color={tw.color('ink')} />
</AnimatedPressable> </Pressable>
)} )}
<View style={tw`flex-row items-center gap-1.5`}> <View style={tw`flex-row items-center gap-2`}>
<Animated.View style={scrollYIcon}> <HeaderIconKind headerKind={headerKind} routeParams={routeParams} />
<HeaderIconKind headerKind={headerKind} routeParams={routeParams} />
</Animated.View>
{showDrawer && ( {showDrawer && (
<AnimatedPressable <Pressable onPress={() => navigation.openDrawer()}>
style={scrollYIcon} <List size={24} color={tw.color('text-zinc-300')} />
onPress={() => navigation.openDrawer()} </Pressable>
>
<List style={twStyle({
top: isAndroid ? 2 : 0 //fixes the icon alignment on android
})} size={24} color={tw.color('text-zinc-300')} />
</AnimatedPressable>
)} )}
<Animated.Text <Text
numberOfLines={1} numberOfLines={1}
style={[twStyle('max-w-[200px] text-md font-bold text-ink'), scrollYTitle]} style={tw`max-w-[200px] text-xl font-bold text-white`}
> >
{title || routeParams?.title} {title || (routeTitle && route?.options.title)}
</Animated.Text> </Text>
</View> </View>
</View> </View>
<View style={tw`relative flex-row items-center gap-3`}> <View style={tw`relative flex-row items-center gap-3`}>
{showSearch && ( {showSearch && (
<View style={tw`flex-row items-center gap-2`}> <View style={tw`flex-row items-center gap-2`}>
<AnimatedPressable <Pressable
style={scrollYIcon}
hitSlop={24} hitSlop={24}
onPress={() => { onPress={() => {
navigation.navigate('SearchStack', { navigation.navigate('SearchStack', {
@ -141,10 +92,11 @@ export default function Header({
}} }}
> >
<MagnifyingGlass <MagnifyingGlass
size={24}
weight="bold" weight="bold"
color={tw.color('text-zinc-300')} color={tw.color('text-zinc-300')}
/> />
</AnimatedPressable> </Pressable>
</View> </View>
)} )}
{(headerKind === 'location' || headerKind === 'tag') && ( {(headerKind === 'location' || headerKind === 'tag') && (
@ -166,7 +118,7 @@ export default function Header({
</View> </View>
{searchType && <HeaderSearchType searchType={searchType} />} {searchType && <HeaderSearchType searchType={searchType} />}
</View> </View>
</Animated.View> </View>
); );
} }
@ -176,10 +128,10 @@ interface HeaderSearchTypeProps {
const HeaderSearchType = ({ searchType }: HeaderSearchTypeProps) => { const HeaderSearchType = ({ searchType }: HeaderSearchTypeProps) => {
switch (searchType) { switch (searchType) {
case 'explorer':
return 'Explorer'; //TODO
case 'location': case 'location':
return <Search placeholder="Location name..." />; return <Search placeholder="Location name..." />;
case 'tags':
return <Search placeholder="Tag name..." />;
case 'categories': case 'categories':
return <Search placeholder="Category name..." />; return <Search placeholder="Category name..." />;
default: default:
@ -195,11 +147,11 @@ interface HeaderIconKindProps {
const HeaderIconKind = ({ headerKind, routeParams }: HeaderIconKindProps) => { const HeaderIconKind = ({ headerKind, routeParams }: HeaderIconKindProps) => {
switch (headerKind) { switch (headerKind) {
case 'location': case 'location':
return <Icon style={tw`ml-3`} size={30} name="Folder" />; return <Icon size={30} name="Folder" />;
case 'tag': case 'tag':
return ( return (
<View <View
style={twStyle('ml-3 h-[24px] w-[24px] rounded-full', { style={twStyle('h-[30px] w-[30px] rounded-full', {
backgroundColor: routeParams.color backgroundColor: routeParams.color
})} })}
/> />

View file

@ -1,7 +1,9 @@
import { useRoute } from '@react-navigation/native';
import { DimensionValue, Platform } from 'react-native'; import { DimensionValue, Platform } from 'react-native';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import { ClassInput } from 'twrnc'; import { ClassInput } from 'twrnc';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import { useExplorerStore } from '~/stores/explorerStore';
interface Props { interface Props {
children: React.ReactNode; // children of fade children: React.ReactNode; // children of fade
@ -11,6 +13,7 @@ interface Props {
orientation?: 'horizontal' | 'vertical'; // orientation of fade orientation?: 'horizontal' | 'vertical'; // orientation of fade
fadeSides?: 'left-right' | 'top-bottom'; // which sides to fade fadeSides?: 'left-right' | 'top-bottom'; // which sides to fade
screenFade?: boolean; // if true, the fade will consider the bottom tab bar height screenFade?: boolean; // if true, the fade will consider the bottom tab bar height
noConditions?: boolean; // if true, the fade will be rendered as is
bottomFadeStyle?: ClassInput; // tailwind style for bottom fade bottomFadeStyle?: ClassInput; // tailwind style for bottom fade
topFadeStyle?: ClassInput; // tailwind style for top fade topFadeStyle?: ClassInput; // tailwind style for top fade
} }
@ -22,15 +25,20 @@ const Fade = ({
height, height,
bottomFadeStyle, bottomFadeStyle,
topFadeStyle, topFadeStyle,
noConditions = false,
screenFade = false, screenFade = false,
fadeSides = 'left-right', fadeSides = 'left-right',
orientation = 'horizontal' orientation = 'horizontal'
}: Props) => { }: Props) => {
const route = useRoute();
const { toggleMenu } = useExplorerStore();
const bottomTabBarHeight = Platform.OS === 'ios' ? 80 : 60; const bottomTabBarHeight = Platform.OS === 'ios' ? 80 : 60;
const gradientStartEndMap = { const gradientStartEndMap = {
'left-right': { start: { x: 0, y: 0 }, end: { x: 1, y: 0 } }, 'left-right': { start: { x: 0, y: 0 }, end: { x: 1, y: 0 } },
'top-bottom': { start: { x: 0, y: 1 }, end: { x: 0, y: 0 } } 'top-bottom': { start: { x: 0, y: 1 }, end: { x: 0, y: 0 } }
}; };
const menuHeight = 57; // height of the explorer menu
const routesWithMenu = ['Location', 'Search', 'Tag']; // routes that are associated with the explorer
return ( return (
<> <>
<LinearGradient <LinearGradient
@ -38,7 +46,10 @@ const Fade = ({
width: orientation === 'vertical' ? height : width, width: orientation === 'vertical' ? height : width,
height: orientation === 'vertical' ? width : height, height: orientation === 'vertical' ? width : height,
position: 'absolute', position: 'absolute',
top: 0, top:
!noConditions && toggleMenu && routesWithMenu.includes(route.name)
? menuHeight
: 0,
alignSelf: 'center', alignSelf: 'center',
left: fadeSides === 'left-right' ? 0 : undefined, left: fadeSides === 'left-right' ? 0 : undefined,
transform: fadeSides === 'left-right' ? undefined : [{ rotate: '180deg' }], transform: fadeSides === 'left-right' ? undefined : [{ rotate: '180deg' }],

View file

@ -1,12 +1,9 @@
import { useNavigation } from '@react-navigation/native'; import { ReactNode, useRef } from 'react';
import { ReactNode, useEffect, useRef } from 'react'; import { Platform, ScrollView, View } from 'react-native';
import { Platform, View } from 'react-native';
import Animated, { SharedValue, useAnimatedScrollHandler } from 'react-native-reanimated';
import { AnimatedScrollView } from 'react-native-reanimated/lib/typescript/reanimated2/component/ScrollView';
import { ClassInput } from 'twrnc/dist/esm/types'; import { ClassInput } from 'twrnc/dist/esm/types';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import Header, { HeaderProps } from '../header/Header'; import Fade from './Fade';
interface Props { interface Props {
children: ReactNode; children: ReactNode;
@ -19,60 +16,37 @@ interface Props {
/** Styling of both side fades */ /** Styling of both side fades */
topFadeStyle?: string; topFadeStyle?: string;
bottomFadeStyle?: string; bottomFadeStyle?: string;
scrollY?: SharedValue<number>;
/* Header properties */
header?: HeaderProps;
hideHeader?: boolean; // Hide the header
} }
const ScreenContainer = ({ const ScreenContainer = ({
children, children,
style, style,
header, topFadeStyle,
scrollY, bottomFadeStyle,
hideHeader = false,
scrollview = true, scrollview = true,
tabHeight = true, tabHeight = true,
scrollToBottomOnChange = false, scrollToBottomOnChange = false
}: Props) => { }: Props) => {
const ref = useRef<AnimatedScrollView>(null); const ref = useRef<ScrollView>(null);
const bottomTabBarHeight = Platform.OS === 'ios' ? 80 : 60; const bottomTabBarHeight = Platform.OS === 'ios' ? 80 : 60;
const scrollHandler = useAnimatedScrollHandler((e) => {
if (scrollY) scrollY.value = e.contentOffset.y;
});
const navigation = useNavigation();
// Reset scroll position to 0 whenever the tab blurs or focuses
useEffect(() => {
const resetScroll = () => {
ref.current?.scrollTo({ y: 0, animated: false });
if (scrollY) scrollY.value = 0;
};
// Subscribe to blur and focus events
const unsubscribeBlur = navigation.addListener('blur', resetScroll);
const unsubscribeFocus = navigation.addListener('focus', resetScroll);
// Cleanup function to remove event listeners
return () => {
unsubscribeBlur();
unsubscribeFocus();
};
}, [navigation, scrollY]);
return scrollview ? ( return scrollview ? (
<View style={tw`relative flex-1`}> <View style={tw`relative flex-1`}>
{!hideHeader && <Header {...header} scrollY={scrollY} />} <Fade
<Animated.ScrollView topFadeStyle={topFadeStyle}
bottomFadeStyle={bottomFadeStyle}
screenFade
fadeSides="top-bottom"
orientation="vertical"
color="black"
width={30}
height="100%"
>
<ScrollView
ref={ref} ref={ref}
onContentSizeChange={() => { onContentSizeChange={() => {
if (!scrollToBottomOnChange) return; if (!scrollToBottomOnChange) return;
ref.current?.scrollToEnd({ animated: true }); ref.current?.scrollToEnd({ animated: true });
}} }}
scrollEventThrottle={1}
onScroll={scrollHandler}
contentContainerStyle={twStyle('justify-between gap-10 py-6', style)} contentContainerStyle={twStyle('justify-between gap-10 py-6', style)}
style={twStyle( style={twStyle(
'flex-1 bg-black', 'flex-1 bg-black',
@ -80,11 +54,21 @@ useEffect(() => {
)} )}
> >
{children} {children}
</Animated.ScrollView> </ScrollView>
</Fade>
</View> </View>
) : ( ) : (
<View style={tw`relative flex-1`}> <View style={tw`relative flex-1`}>
{!hideHeader && <Header {...header} />} <Fade
topFadeStyle={topFadeStyle}
bottomFadeStyle={bottomFadeStyle}
screenFade
fadeSides="top-bottom"
orientation="vertical"
color="black"
width={30}
height="100%"
>
<View <View
style={twStyle( style={twStyle(
'flex-1 justify-between gap-10 bg-black py-6', 'flex-1 justify-between gap-10 bg-black py-6',
@ -94,6 +78,7 @@ useEffect(() => {
> >
{children} {children}
</View> </View>
</Fade>
</View> </View>
); );
}; };

View file

@ -1,6 +1,6 @@
import { Location } from '@sd/client';
import { useRef } from 'react'; import { useRef } from 'react';
import { Pressable } from 'react-native'; import { Pressable } from 'react-native';
import { Location } from '@sd/client';
import { twStyle } from '~/lib/tailwind'; import { twStyle } from '~/lib/tailwind';
import { ModalRef } from '../layout/Modal'; import { ModalRef } from '../layout/Modal';
@ -23,6 +23,7 @@ export const LocationItem = ({
}: LocationItemProps) => { }: LocationItemProps) => {
const modalRef = useRef<ModalRef>(null); const modalRef = useRef<ModalRef>(null);
return ( return (
<>
<Pressable <Pressable
style={twStyle(viewStyle === 'grid' ? `w-[31.5%]` : `flex-1`)} style={twStyle(viewStyle === 'grid' ? `w-[31.5%]` : `flex-1`)}
onPress={onPress} onPress={onPress}
@ -43,5 +44,6 @@ export const LocationItem = ({
<ListLocation location={location} /> <ListLocation location={location} />
)} )}
</Pressable> </Pressable>
</>
); );
}; };

View file

@ -1,8 +1,8 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { Pressable, Text, View } from 'react-native'; import { Pressable, Text, View } from 'react-native';
import { FlatList } from 'react-native-gesture-handler'; import { FlatList } from 'react-native-gesture-handler';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack'; import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
@ -63,7 +63,7 @@ const Locations = () => {
navigation.jumpTo('BrowseStack', { navigation.jumpTo('BrowseStack', {
initial: false, initial: false,
screen: 'Location', screen: 'Location',
params: { id: item.id, title: item.name } params: { id: item.id }
}) })
} }
> >

View file

@ -14,7 +14,7 @@ const OverviewSection = ({ title, count, children }: Props) => {
<View style={tw`flex-row items-center gap-3 px-6 pb-3`}> <View style={tw`flex-row items-center gap-3 px-6 pb-3`}>
<Text style={tw`text-lg font-bold text-white`}>{title}</Text> <Text style={tw`text-lg font-bold text-white`}>{title}</Text>
<View <View
style={tw`flex h-[24px] w-[24px] items-center justify-center rounded-full border border-app-button px-1`} style={tw`flex h-[24px] w-[24px] items-center justify-center rounded-full border border-app-button/40 px-1`}
> >
<Text style={tw`text-xs text-ink`}>{count}</Text> <Text style={tw`text-xs text-ink`}>{count}</Text>
</View> </View>

View file

@ -1,4 +0,0 @@
import { Pressable } from 'react-native';
import Animated from 'react-native-reanimated';
export const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

View file

@ -18,12 +18,12 @@ export default function Search({ placeholder }: Props) {
}, [searchStore]); }, [searchStore]);
return ( return (
<View <View
style={tw`mt-3 flex h-auto w-full flex-row items-center justify-between rounded-md border border-app-inputborder bg-app-input px-3 py-2 shadow-sm`} style={tw`mt-4 flex h-11 w-full flex-row items-center justify-between rounded-md border border-app-inputborder bg-app-input px-3 shadow-sm`}
> >
<TextInput <TextInput
onChangeText={(text) => searchStore.setSearch(text)} onChangeText={(text) => searchStore.setSearch(text)}
placeholderTextColor={tw.color('text-ink-dull')} placeholderTextColor={tw.color('text-ink-dull')}
style={tw`leading-0 w-[90%] text-sm text-white`} style={tw`w-[90%] text-white`}
placeholder={placeholder} placeholder={placeholder}
/> />
<MagnifyingGlass size={20} weight="bold" color={tw.color('text-ink-dull')} /> <MagnifyingGlass size={20} weight="bold" color={tw.color('text-ink-dull')} />

View file

@ -11,6 +11,7 @@ import {
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { FlatList, Pressable, Text, View } from 'react-native'; import { FlatList, Pressable, Text, View } from 'react-native';
import { Icon } from '~/components/icons/Icon'; import { Icon } from '~/components/icons/Icon';
import Fade from '~/components/layout/Fade';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import { SearchStackScreenProps } from '~/navigation/SearchStack'; import { SearchStackScreenProps } from '~/navigation/SearchStack';
@ -46,6 +47,7 @@ const FiltersBar = () => {
<Plus weight="bold" size={20} color={tw.color('text-ink-dull')} /> <Plus weight="bold" size={20} color={tw.color('text-ink-dull')} />
</Button> </Button>
<View style={tw`relative flex-1`}> <View style={tw`relative flex-1`}>
<Fade noConditions height={'100%'} width={30} color="app-header">
<FlatList <FlatList
ref={flatListRef} ref={flatListRef}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
@ -58,6 +60,7 @@ const FiltersBar = () => {
)} )}
contentContainerStyle={tw`flex-row gap-2 pl-4 pr-4`} contentContainerStyle={tw`flex-row gap-2 pl-4 pr-4`}
/> />
</Fade>
</View> </View>
</View> </View>
); );

View file

@ -1,6 +1,6 @@
import { Tag } from '@sd/client';
import { DotsThreeOutlineVertical } from 'phosphor-react-native'; import { DotsThreeOutlineVertical } from 'phosphor-react-native';
import { Pressable, Text, View } from 'react-native'; import { Pressable, Text, View } from 'react-native';
import { Tag } from '@sd/client';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import Card from '../layout/Card'; import Card from '../layout/Card';
@ -20,7 +20,7 @@ const GridTag = ({ tag, modalRef }: GridTagProps) => {
backgroundColor: tag.color! backgroundColor: tag.color!
})} })}
/> />
<Pressable hitSlop={12} onPress={() => modalRef.current?.present()}> <Pressable hitSlop={24} onPress={() => modalRef.current?.present()}>
<DotsThreeOutlineVertical <DotsThreeOutlineVertical
weight="fill" weight="fill"
size={20} size={20}

View file

@ -1,5 +1,6 @@
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import React from 'react'; import React from 'react';
import Header from '~/components/header/Header';
import FiltersScreen from '~/screens/search/Filters'; import FiltersScreen from '~/screens/search/Filters';
import SearchScreen from '~/screens/search/Search'; import SearchScreen from '~/screens/search/Search';
@ -7,18 +8,22 @@ const Stack = createNativeStackNavigator<SearchStackParamList>();
export default function SearchStack() { export default function SearchStack() {
return ( return (
<Stack.Navigator <Stack.Navigator initialRouteName="Search">
screenOptions={{
headerShown: false
}}
initialRouteName="Search">
<Stack.Screen <Stack.Screen
name="Search" name="Search"
component={SearchScreen} component={SearchScreen}
options={{
headerShown: false
}}
/> />
<Stack.Screen <Stack.Screen
name="Filters" name="Filters"
component={FiltersScreen} component={FiltersScreen}
options={{
header: () => {
return <Header navBack showSearch={false} title="Search filters" />;
}
}}
/> />
</Stack.Navigator> </Stack.Navigator>
); );

View file

@ -1,5 +1,6 @@
import { CompositeScreenProps } from '@react-navigation/native'; import { CompositeScreenProps } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import Header from '~/components/header/Header';
import BrowseScreen from '~/screens/browse/Browse'; import BrowseScreen from '~/screens/browse/Browse';
import LibraryScreen from '~/screens/browse/Library'; import LibraryScreen from '~/screens/browse/Library';
import LocationScreen from '~/screens/browse/Location'; import LocationScreen from '~/screens/browse/Location';
@ -13,35 +14,48 @@ const Stack = createNativeStackNavigator<BrowseStackParamList>();
export default function BrowseStack() { export default function BrowseStack() {
return ( return (
<Stack.Navigator <Stack.Navigator initialRouteName="Browse">
screenOptions={{
headerShown: false
}}
initialRouteName="Browse">
<Stack.Screen <Stack.Screen
name="Browse" name="Browse"
component={BrowseScreen} component={BrowseScreen}
options={{ header: () => <Header showDrawer title="Browse" /> }}
/> />
<Stack.Screen <Stack.Screen
name="Location" name="Location"
> component={LocationScreen}
{(props) => <LocationScreen {...props}/>} options={{
</Stack.Screen> header: (route) => (
<Header route={route} headerKind="location" routeTitle navBack />
)
}}
/>
<Stack.Screen <Stack.Screen
name="Tags" name="Tags"
component={TagsScreen} component={TagsScreen}
options={{
header: () => <Header navBack title="Tags" />
}}
/> />
<Stack.Screen <Stack.Screen
name="Locations" name="Locations"
component={LocationsScreen}/> component={LocationsScreen}
options={{
header: () => <Header navBack searchType="location" title="Locations" />
}}
/>
<Stack.Screen <Stack.Screen
name="Tag" name="Tag"
> component={TagScreen}
{(props) => <TagScreen {...props} />} options={{
</Stack.Screen> header: (route) => <Header navBack routeTitle route={route} headerKind="tag" />
}}
/>
<Stack.Screen <Stack.Screen
name="Library" name="Library"
component={LibraryScreen} component={LibraryScreen}
options={{
header: () => <Header navBack title="Library" />
}}
/> />
</Stack.Navigator> </Stack.Navigator>
); );
@ -49,9 +63,9 @@ export default function BrowseStack() {
export type BrowseStackParamList = { export type BrowseStackParamList = {
Browse: undefined; Browse: undefined;
Location: { id: number; path?: string, title?: string | null }; Location: { id: number; path?: string };
Locations: undefined; Locations: undefined;
Tag: { id: number; color: string, title?: string | null }; Tag: { id: number; color: string };
Tags: undefined; Tags: undefined;
Library: undefined; Library: undefined;
}; };

View file

@ -1,5 +1,6 @@
import { CompositeScreenProps } from '@react-navigation/native'; import { CompositeScreenProps } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import Header from '~/components/header/Header';
import NetworkScreen from '~/screens/network/Network'; import NetworkScreen from '~/screens/network/Network';
import { TabScreenProps } from '../TabNavigator'; import { TabScreenProps } from '../TabNavigator';
@ -8,12 +9,11 @@ const Stack = createNativeStackNavigator<NetworkStackParamList>();
export default function NetworkStack() { export default function NetworkStack() {
return ( return (
<Stack.Navigator screenOptions={{ <Stack.Navigator initialRouteName="Network">
headerShown: false
}} initialRouteName="Network">
<Stack.Screen <Stack.Screen
name="Network" name="Network"
component={NetworkScreen} component={NetworkScreen}
options={{ header: () => <Header showDrawer title="Network" /> }}
/> />
</Stack.Navigator> </Stack.Navigator>
); );

View file

@ -1,24 +1,27 @@
import { CompositeScreenProps } from '@react-navigation/native'; import { CompositeScreenProps } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import Header from '~/components/header/Header';
import CategoriesScreen from '~/screens/overview/Categories'; import CategoriesScreen from '~/screens/overview/Categories';
import OverviewScreen from '~/screens/overview/Overview'; import OverviewScreen from '~/screens/overview/Overview';
import { TabScreenProps } from '../TabNavigator'; import { TabScreenProps } from '../TabNavigator';
const Stack = createNativeStackNavigator<OverviewStackParamList>(); const Stack = createNativeStackNavigator<OverviewStackParamList>();
export default function OverviewStack() { export default function OverviewStack() {
return ( return (
<Stack.Navigator <Stack.Navigator initialRouteName="Overview">
screenOptions={{
headerShown: false
}}>
<Stack.Screen <Stack.Screen
name="Overview" name="Overview"
component={OverviewScreen} component={OverviewScreen}
options={{ header: () => <Header showDrawer title="Overview" /> }}
/> />
<Stack.Screen <Stack.Screen
name="Categories" name="Categories"
component={CategoriesScreen} component={CategoriesScreen}
options={{
header: () => <Header searchType="categories" navBack title="Categories" />
}}
/> />
</Stack.Navigator> </Stack.Navigator>
); );

View file

@ -2,7 +2,7 @@ import { CompositeScreenProps } from '@react-navigation/native';
// import KeysSettingsScreen from '~/screens/settings/library/KeysSettings'; // import KeysSettingsScreen from '~/screens/settings/library/KeysSettings';
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import LocationsScreen from '~/screens/browse/Locations'; import Header from '~/components/header/Header';
import AppearanceSettingsScreen from '~/screens/settings/client/AppearanceSettings'; import AppearanceSettingsScreen from '~/screens/settings/client/AppearanceSettings';
import ExtensionsSettingsScreen from '~/screens/settings/client/ExtensionsSettings'; import ExtensionsSettingsScreen from '~/screens/settings/client/ExtensionsSettings';
import GeneralSettingsScreen from '~/screens/settings/client/GeneralSettings'; import GeneralSettingsScreen from '~/screens/settings/client/GeneralSettings';
@ -13,6 +13,7 @@ import DebugScreen from '~/screens/settings/info/Debug';
import SupportScreen from '~/screens/settings/info/Support'; import SupportScreen from '~/screens/settings/info/Support';
import EditLocationSettingsScreen from '~/screens/settings/library/EditLocationSettings'; import EditLocationSettingsScreen from '~/screens/settings/library/EditLocationSettings';
import LibraryGeneralSettingsScreen from '~/screens/settings/library/LibraryGeneralSettings'; import LibraryGeneralSettingsScreen from '~/screens/settings/library/LibraryGeneralSettings';
import LocationSettingsScreen from '~/screens/settings/library/LocationSettings';
import NodesSettingsScreen from '~/screens/settings/library/NodesSettings'; import NodesSettingsScreen from '~/screens/settings/library/NodesSettings';
import TagsSettingsScreen from '~/screens/settings/library/TagsSettings'; import TagsSettingsScreen from '~/screens/settings/library/TagsSettings';
import SettingsScreen from '~/screens/settings/Settings'; import SettingsScreen from '~/screens/settings/Settings';
@ -23,59 +24,65 @@ const Stack = createNativeStackNavigator<SettingsStackParamList>();
export default function SettingsStack() { export default function SettingsStack() {
return ( return (
<Stack.Navigator <Stack.Navigator initialRouteName="Settings">
screenOptions={{
headerShown: false
}}
initialRouteName="Settings">
<Stack.Screen <Stack.Screen
name="Settings" name="Settings"
> component={SettingsScreen}
{(props) => <SettingsScreen {...props}/>} options={{ header: () => <Header showDrawer title="Settings" /> }}
</Stack.Screen> />
{/* Client */} {/* Client */}
<Stack.Screen <Stack.Screen
name="GeneralSettings" name="GeneralSettings"
component={GeneralSettingsScreen} component={GeneralSettingsScreen}
options={{ header: () => <Header navBack title="General" /> }}
/> />
<Stack.Screen <Stack.Screen
name="LibrarySettings" name="LibrarySettings"
> component={LibrarySettingsScreen}
{(props) => <LibrarySettingsScreen {...props} />} options={{ header: () => <Header navBack title="Libraries" /> }}
</Stack.Screen> />
<Stack.Screen <Stack.Screen
name="AppearanceSettings" name="AppearanceSettings"
component={AppearanceSettingsScreen} component={AppearanceSettingsScreen}
options={{ header: () => <Header navBack title="Appearance" /> }}
/> />
<Stack.Screen <Stack.Screen
name="PrivacySettings" name="PrivacySettings"
component={PrivacySettingsScreen} component={PrivacySettingsScreen}
options={{ header: () => <Header navBack title="Privacy" /> }}
/> />
<Stack.Screen <Stack.Screen
name="ExtensionsSettings" name="ExtensionsSettings"
component={ExtensionsSettingsScreen} component={ExtensionsSettingsScreen}
options={{ header: () => <Header navBack title="Extensions" /> }}
/> />
{/* Library */} {/* Library */}
<Stack.Screen <Stack.Screen
name="LibraryGeneralSettings" name="LibraryGeneralSettings"
component={LibraryGeneralSettingsScreen} component={LibraryGeneralSettingsScreen}
options={{ header: () => <Header navBack title="Library Settings" /> }}
/> />
<Stack.Screen <Stack.Screen
name="LocationSettings" name="LocationSettings"
component={LocationsScreen} component={LocationSettingsScreen}
options={{
header: () => <Header searchType="location" navBack title="Locations" />
}}
/> />
<Stack.Screen <Stack.Screen
name="EditLocationSettings" name="EditLocationSettings"
> component={EditLocationSettingsScreen}
{(props) => <EditLocationSettingsScreen {...props} />} options={{ header: () => <Header navBack title="Edit Location" /> }}
</Stack.Screen> />
<Stack.Screen <Stack.Screen
name="NodesSettings" name="NodesSettings"
component={NodesSettingsScreen} component={NodesSettingsScreen}
options={{ header: () => <Header navBack title="Nodes" /> }}
/> />
<Stack.Screen <Stack.Screen
name="TagsSettings" name="TagsSettings"
component={TagsSettingsScreen} component={TagsSettingsScreen}
options={{ header: () => <Header navBack title="Tags" /> }}
/> />
{/* <Stack.Screen {/* <Stack.Screen
name="KeysSettings" name="KeysSettings"
@ -86,14 +93,17 @@ export default function SettingsStack() {
<Stack.Screen <Stack.Screen
name="About" name="About"
component={AboutScreen} component={AboutScreen}
/> options={{ header: () => <Header navBack title="About" /> }}
/>
<Stack.Screen <Stack.Screen
name="Support" name="Support"
component={SupportScreen} component={SupportScreen}
options={{ header: () => <Header navBack title="Support" /> }}
/> />
<Stack.Screen <Stack.Screen
name="Debug" name="Debug"
component={DebugScreen} component={DebugScreen}
options={{ header: () => <Header navBack title="Debug" /> }}
/> />
</Stack.Navigator> </Stack.Navigator>
); );

View file

@ -1,18 +1,11 @@
import { useSharedValue } from 'react-native-reanimated';
import BrowseCategories from '~/components/browse/BrowseCategories'; import BrowseCategories from '~/components/browse/BrowseCategories';
import BrowseLocations from '~/components/browse/BrowseLocations'; import BrowseLocations from '~/components/browse/BrowseLocations';
import BrowseTags from '~/components/browse/BrowseTags'; import BrowseTags from '~/components/browse/BrowseTags';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
export default function BrowseScreen() { export default function BrowseScreen() {
const scrollY = useSharedValue(0);
return ( return (
<ScreenContainer header={{ <ScreenContainer>
scrollY: scrollY,
showSearch: true,
showDrawer: true,
title: 'Browse',
}}>
<BrowseCategories /> <BrowseCategories />
<BrowseLocations /> <BrowseLocations />
<BrowseTags /> <BrowseTags />

View file

@ -6,13 +6,7 @@ import { tw } from '~/lib/tailwind';
export default function LibraryScreen() { export default function LibraryScreen() {
return ( return (
<ScreenContainer <ScreenContainer style={tw`px-6 py-0`} scrollview={false}>
header={{
title: 'Library',
navBack: true,
showSearch: true,
}}
style={tw`px-6 py-0`} scrollview={false}>
<FlatList <FlatList
data={CATEGORIES_LIST} data={CATEGORIES_LIST}
contentContainerStyle={tw`py-6`} contentContainerStyle={tw`py-6`}

View file

@ -1,20 +1,12 @@
import { RouteProp } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useCache, useLibraryQuery, useNodes, usePathsExplorerQuery } from '@sd/client';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSharedValue } from 'react-native-reanimated'; import { useCache, useLibraryQuery, useNodes, usePathsExplorerQuery } from '@sd/client';
import Explorer from '~/components/explorer/Explorer'; import Explorer from '~/components/explorer/Explorer';
import { BrowseStackParamList } from '~/navigation/tabs/BrowseStack'; import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
import { getExplorerStore } from '~/stores/explorerStore'; import { getExplorerStore } from '~/stores/explorerStore';
interface Props { export default function LocationScreen({ navigation, route }: BrowseStackScreenProps<'Location'>) {
route: RouteProp<BrowseStackParamList, 'Location'>;
navigation: NativeStackNavigationProp<BrowseStackParamList, 'Location'>;
}
export default function LocationScreen({ navigation, route }: Props) {
const { id, path } = route.params; const { id, path } = route.params;
const scrollY = useSharedValue(0);
const location = useLibraryQuery(['locations.get', route.params.id]); const location = useLibraryQuery(['locations.get', route.params.id]);
useNodes(location.data?.nodes); useNodes(location.data?.nodes);
const locationData = useCache(location.data?.item); const locationData = useCache(location.data?.item);
@ -67,7 +59,5 @@ export default function LocationScreen({ navigation, route }: Props) {
getExplorerStore().path = path ?? ''; getExplorerStore().path = path ?? '';
}, [id, path]); }, [id, path]);
return ( return <Explorer {...paths} />;
<Explorer headerKind='location' route={route} scrollY={scrollY} {...paths} />
);
} }

View file

@ -1,10 +1,9 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { Plus } from 'phosphor-react-native'; import { Plus } from 'phosphor-react-native';
import { useMemo, useRef } from 'react'; import { useMemo, useRef } from 'react';
import { Pressable, View } from 'react-native'; import { FlatList, Pressable, View } from 'react-native';
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
import { useDebounce } from 'use-debounce'; import { useDebounce } from 'use-debounce';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import Empty from '~/components/layout/Empty'; import Empty from '~/components/layout/Empty';
import { ModalRef } from '~/components/layout/Modal'; import { ModalRef } from '~/components/layout/Modal';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
@ -20,7 +19,6 @@ interface Props {
} }
export default function LocationsScreen({ viewStyle }: Props) { export default function LocationsScreen({ viewStyle }: Props) {
const scrollY = useSharedValue(0);
const locationsQuery = useLibraryQuery(['locations.list']); const locationsQuery = useLibraryQuery(['locations.list']);
useNodes(locationsQuery.data?.nodes); useNodes(locationsQuery.data?.nodes);
const locations = useCache(locationsQuery.data?.items); const locations = useCache(locationsQuery.data?.items);
@ -39,17 +37,8 @@ export default function LocationsScreen({ viewStyle }: Props) {
BrowseStackScreenProps<'Browse'>['navigation'] & BrowseStackScreenProps<'Browse'>['navigation'] &
SettingsStackScreenProps<'Settings'>['navigation'] SettingsStackScreenProps<'Settings'>['navigation']
>(); >();
const scrollHandler = useAnimatedScrollHandler((e) => {
scrollY.value = e.contentOffset.y;
});
return ( return (
<ScreenContainer <ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
header={{
title: 'Locations',
navBack: true,
searchType: 'location',
}}
scrollview={false} style={tw`relative px-6 py-0`}>
<Pressable <Pressable
style={tw`absolute bottom-7 right-7 z-10 h-12 w-12 items-center justify-center rounded-full bg-accent`} style={tw`absolute bottom-7 right-7 z-10 h-12 w-12 items-center justify-center rounded-full bg-accent`}
onPress={() => { onPress={() => {
@ -58,13 +47,12 @@ export default function LocationsScreen({ viewStyle }: Props) {
> >
<Plus size={20} weight="bold" style={tw`text-ink`} /> <Plus size={20} weight="bold" style={tw`text-ink`} />
</Pressable> </Pressable>
<Animated.FlatList <FlatList
data={filteredLocations} data={filteredLocations}
contentContainerStyle={twStyle( contentContainerStyle={twStyle(
`py-6`, `py-6`,
filteredLocations.length === 0 && 'h-full items-center justify-center' filteredLocations.length === 0 && 'h-full items-center justify-center'
)} )}
onScroll={scrollHandler}
keyExtractor={(location) => location.id.toString()} keyExtractor={(location) => location.id.toString()}
ItemSeparatorComponent={() => <View style={tw`h-2`} />} ItemSeparatorComponent={() => <View style={tw`h-2`} />}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
@ -83,7 +71,7 @@ export default function LocationsScreen({ viewStyle }: Props) {
onPress={() => onPress={() =>
navigation.navigate('BrowseStack', { navigation.navigate('BrowseStack', {
screen: 'Location', screen: 'Location',
params: { id: item.id, title: item.name } params: { id: item.id }
}) })
} }
editLocation={() => editLocation={() =>

View file

@ -1,21 +1,11 @@
import { useCache, useLibraryQuery, useNodes, useObjectsExplorerQuery } from '@sd/client';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSharedValue } from 'react-native-reanimated'; import { useCache, useLibraryQuery, useNodes, useObjectsExplorerQuery } from '@sd/client';
import Explorer from '~/components/explorer/Explorer'; import Explorer from '~/components/explorer/Explorer';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack'; import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
export default function TagScreen({ navigation, route }: BrowseStackScreenProps<'Tag'>) {
interface Props {
route: BrowseStackScreenProps<'Tag'>['route'];
navigation: BrowseStackScreenProps<'Tag'>['navigation'];
}
export default function TagScreen({
navigation,
route,
}: Props) {
const { id } = route.params; const { id } = route.params;
const scrollY = useSharedValue(0);
const tag = useLibraryQuery(['tags.get', id]); const tag = useLibraryQuery(['tags.get', id]);
useNodes(tag.data?.nodes); useNodes(tag.data?.nodes);
const tagData = useCache(tag.data?.item); const tagData = useCache(tag.data?.item);
@ -32,7 +22,5 @@ export default function TagScreen({
}); });
}, [tagData?.name, navigation]); }, [tagData?.name, navigation]);
return ( return <Explorer {...objects} />;
<Explorer headerKind='tag' route={route} scrollY={scrollY} {...objects} />
);
} }

View file

@ -1,18 +1,17 @@
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { Plus } from 'phosphor-react-native'; import { Plus } from 'phosphor-react-native';
import { useMemo, useRef } from 'react'; import { useRef } from 'react';
import { Pressable, View } from 'react-native'; import { Pressable, View } from 'react-native';
import Animated from 'react-native-reanimated'; import { FlatList } from 'react-native-gesture-handler';
import { useDebounce } from 'use-debounce'; import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import Empty from '~/components/layout/Empty'; import Empty from '~/components/layout/Empty';
import Fade from '~/components/layout/Fade';
import { ModalRef } from '~/components/layout/Modal'; import { ModalRef } from '~/components/layout/Modal';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import CreateTagModal from '~/components/modal/tag/CreateTagModal'; import CreateTagModal from '~/components/modal/tag/CreateTagModal';
import { TagItem } from '~/components/tags/TagItem'; import { TagItem } from '~/components/tags/TagItem';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack'; import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
import { useSearchStore } from '~/stores/searchStore';
interface Props { interface Props {
viewStyle?: 'grid' | 'list'; viewStyle?: 'grid' | 'list';
@ -21,32 +20,13 @@ interface Props {
export default function TagsScreen({ viewStyle = 'list' }: Props) { export default function TagsScreen({ viewStyle = 'list' }: Props) {
const navigation = useNavigation<BrowseStackScreenProps<'Browse'>['navigation']>(); const navigation = useNavigation<BrowseStackScreenProps<'Browse'>['navigation']>();
const modalRef = useRef<ModalRef>(null); const modalRef = useRef<ModalRef>(null);
const { search } = useSearchStore();
const tags = useLibraryQuery(['tags.list']); const tags = useLibraryQuery(['tags.list']);
useNodes(tags.data?.nodes); useNodes(tags.data?.nodes);
const tagData = useCache(tags.data?.items); const tagData = useCache(tags.data?.items);
const [debouncedSearch] = useDebounce(search, 200);
const filteredTags = useMemo(
() =>
tagData?.filter((tag) =>
tag.name?.toLowerCase().includes(debouncedSearch.toLowerCase())
) ?? [],
[debouncedSearch, tagData]
);
return ( return (
<ScreenContainer <ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
header={{
title: 'Tags',
showSearch: false,
navBack: true,
searchType: 'tags',
}}
scrollview={false}
style={tw`relative px-6 py-0`}>
<Pressable <Pressable
style={tw`absolute bottom-7 right-7 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-accent`} style={tw`absolute bottom-7 right-7 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-accent`}
testID="create-tag-modal" testID="create-tag-modal"
@ -56,9 +36,15 @@ export default function TagsScreen({ viewStyle = 'list' }: Props) {
> >
<Plus size={20} weight="bold" style={tw`text-ink`} /> <Plus size={20} weight="bold" style={tw`text-ink`} />
</Pressable> </Pressable>
<Animated.FlatList <Fade
data={filteredTags} fadeSides="top-bottom"
scrollEventThrottle={1} orientation="vertical"
color="black"
width={30}
height="100%"
>
<FlatList
data={tagData}
renderItem={({ item }) => ( renderItem={({ item }) => (
<TagItem <TagItem
viewStyle={viewStyle} viewStyle={viewStyle}
@ -90,6 +76,7 @@ export default function TagsScreen({ viewStyle = 'list' }: Props) {
tagData.length === 0 && 'h-full items-center justify-center' tagData.length === 0 && 'h-full items-center justify-center'
)} )}
/> />
</Fade>
<CreateTagModal ref={modalRef} /> <CreateTagModal ref={modalRef} />
</ScreenContainer> </ScreenContainer>
); );

View file

@ -6,11 +6,7 @@ import { NetworkStackScreenProps } from '~/navigation/tabs/NetworkStack';
export default function NetworkScreen({ navigation }: NetworkStackScreenProps<'Network'>) { export default function NetworkScreen({ navigation }: NetworkStackScreenProps<'Network'>) {
return ( return (
<ScreenContainer header={{ <ScreenContainer scrollview={false} style={tw`items-center justify-center gap-0`}>
showDrawer: true,
title: 'Network',
showSearch: true,
}} scrollview={false} style={tw`items-center justify-center gap-0`}>
<Icon name="Globe" size={128} /> <Icon name="Globe" size={128} />
<Text style={tw`mt-4 text-lg font-bold text-white`}>Your Local Network</Text> <Text style={tw`mt-4 text-lg font-bold text-white`}>Your Local Network</Text>
<Text style={tw`mt-1 max-w-sm text-center text-sm text-ink-dull`}> <Text style={tw`mt-1 max-w-sm text-center text-sm text-ink-dull`}>

View file

@ -1,8 +1,7 @@
import { useLibraryQuery } from '@sd/client';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { View } from 'react-native'; import { FlatList, View } from 'react-native';
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
import { useDebounce } from 'use-debounce'; import { useDebounce } from 'use-debounce';
import { useLibraryQuery } from '@sd/client';
import { IconName } from '~/components/icons/Icon'; import { IconName } from '~/components/icons/Icon';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import CategoryItem from '~/components/overview/CategoryItem'; import CategoryItem from '~/components/overview/CategoryItem';
@ -10,7 +9,6 @@ import { tw } from '~/lib/tailwind';
import { useSearchStore } from '~/stores/searchStore'; import { useSearchStore } from '~/stores/searchStore';
const CategoriesScreen = () => { const CategoriesScreen = () => {
const scrollY = useSharedValue(0);
const kinds = useLibraryQuery(['library.kindStatistics']); const kinds = useLibraryQuery(['library.kindStatistics']);
const { search } = useSearchStore(); const { search } = useSearchStore();
const [debouncedSearch] = useDebounce(search, 200); const [debouncedSearch] = useDebounce(search, 200);
@ -21,26 +19,13 @@ const CategoriesScreen = () => {
) ?? [], ) ?? [],
[debouncedSearch, kinds] [debouncedSearch, kinds]
); );
const scrollHandler = useAnimatedScrollHandler((e) => {
scrollY.value = e.contentOffset.y;
});
return ( return (
<ScreenContainer <ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
header={{ <FlatList
title: 'Categories',
searchType: 'categories',
navBack: true,
}}
scrollY={scrollY}
scrollview={false}
style={tw`relative px-6 py-0`}>
<Animated.FlatList
data={filteredKinds?.sort((a, b) => b.count - a.count).filter((i) => i.kind !== 0)} data={filteredKinds?.sort((a, b) => b.count - a.count).filter((i) => i.kind !== 0)}
numColumns={3} numColumns={3}
onScroll={scrollHandler}
contentContainerStyle={tw`py-6`} contentContainerStyle={tw`py-6`}
keyExtractor={(item) => item.name} keyExtractor={(item) => item.name}
scrollEventThrottle={1}
ItemSeparatorComponent={() => <View style={tw`h-2`} />} ItemSeparatorComponent={() => <View style={tw`h-2`} />}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}

View file

@ -1,5 +1,4 @@
import { useBridgeQuery, useLibraryQuery } from '@sd/client'; import { useBridgeQuery, useLibraryQuery } from '@sd/client';
import { useSharedValue } from 'react-native-reanimated';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import Categories from '~/components/overview/Categories'; import Categories from '~/components/overview/Categories';
import Cloud from '~/components/overview/Cloud'; import Cloud from '~/components/overview/Cloud';
@ -21,19 +20,13 @@ const EMPTY_STATISTICS = {
export default function OverviewScreen() { export default function OverviewScreen() {
const { data: node } = useBridgeQuery(['nodeState']); const { data: node } = useBridgeQuery(['nodeState']);
const scrollY = useSharedValue(0);
const stats = useLibraryQuery(['library.statistics'], { const stats = useLibraryQuery(['library.statistics'], {
initialData: { ...EMPTY_STATISTICS } initialData: { ...EMPTY_STATISTICS }
}); });
return ( return (
<ScreenContainer <ScreenContainer>
header={{
title: 'Overview',
showSearch: true,
showDrawer: true,
}}
scrollY={scrollY}>
<OverviewStats stats={stats} /> <OverviewStats stats={stats} />
<Categories /> <Categories />
<Devices stats={stats} node={node} /> <Devices stats={stats} node={node} />

View file

@ -5,13 +5,7 @@ import SaveAdd from '~/components/search/filters/SaveAdd';
const FiltersScreen = () => { const FiltersScreen = () => {
return ( return (
<> <>
<ScreenContainer <ScreenContainer bottomFadeStyle="bottom-0" tabHeight={false}>
header={{
title: 'Filters',
showSearch: false,
navBack: true,
}}
tabHeight={false}>
<FiltersList /> <FiltersList />
</ScreenContainer> </ScreenContainer>
<SaveAdd /> <SaveAdd />

View file

@ -1,8 +1,8 @@
import { SearchFilterArgs, useObjectsExplorerQuery } from '@sd/client';
import { ArrowLeft, DotsThreeOutline, FunnelSimple, MagnifyingGlass } from 'phosphor-react-native'; import { ArrowLeft, DotsThreeOutline, FunnelSimple, MagnifyingGlass } from 'phosphor-react-native';
import { Suspense, useDeferredValue, useMemo, useState } from 'react'; import { Suspense, useDeferredValue, useMemo, useState } from 'react';
import { ActivityIndicator, Platform, Pressable, TextInput, View } from 'react-native'; import { ActivityIndicator, Platform, Pressable, TextInput, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { SearchFilterArgs, useObjectsExplorerQuery } from '@sd/client';
import Explorer from '~/components/explorer/Explorer'; import Explorer from '~/components/explorer/Explorer';
import FiltersBar from '~/components/search/filters/FiltersBar'; import FiltersBar from '~/components/search/filters/FiltersBar';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
@ -124,7 +124,7 @@ const SearchScreen = ({ navigation }: SearchStackScreenProps<'Search'>) => {
{/* Content */} {/* Content */}
<View style={tw`flex-1`}> <View style={tw`flex-1`}>
<Suspense fallback={<ActivityIndicator />}> <Suspense fallback={<ActivityIndicator />}>
<Explorer hideHeader {...objects} tabHeight={false} /> <Explorer {...objects} tabHeight={false} />
</Suspense> </Suspense>
</View> </View>
</View> </View>

View file

@ -1,4 +1,3 @@
import { DebugState, useDebugState, useDebugStateEnabler } from '@sd/client';
import { import {
Books, Books,
FlyingSaucer, FlyingSaucer,
@ -13,8 +12,9 @@ import {
ShieldCheck, ShieldCheck,
TagSimple TagSimple
} from 'phosphor-react-native'; } from 'phosphor-react-native';
import { Platform, Text, TouchableWithoutFeedback, View } from 'react-native'; import React from 'react';
import { useSharedValue } from 'react-native-reanimated'; import { Platform, SectionList, Text, TouchableWithoutFeedback, View } from 'react-native';
import { DebugState, useDebugState, useDebugStateEnabler } from '@sd/client';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { SettingsItem } from '~/components/settings/SettingsItem'; import { SettingsItem } from '~/components/settings/SettingsItem';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
@ -129,7 +129,7 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
<Text <Text
style={twStyle( style={twStyle(
'mb-3 text-lg font-bold text-ink', 'mb-3 text-lg font-bold text-ink',
section.title === 'Client' ? 'mt-0' : 'mt-5' section.title === 'Client' ? 'mt-2' : 'mt-5'
)} )}
> >
{section.title} {section.title}
@ -137,34 +137,28 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
); );
} }
export default function SettingsScreen({ export default function SettingsScreen({ navigation }: SettingsStackScreenProps<'Settings'>) {
navigation,
}: SettingsStackScreenProps<'Settings'>) {
const debugState = useDebugState(); const debugState = useDebugState();
const scrollY = useSharedValue(0);
return ( return (
<ScreenContainer <ScreenContainer tabHeight={false} scrollview={false} style={tw`gap-0 px-6 py-0`}>
header={{ <SectionList
title: 'Settings', sections={sections(debugState)}
showSearch: true, contentContainerStyle={tw`h-auto pb-5 pt-3`}
showDrawer: true, renderItem={({ item }) => (
}} <SettingsItem
scrollY={scrollY} tabHeight={false} style={tw`gap-0 px-6`}> title={item.title}
{sections(debugState).map((section, i) => ( leftIcon={item.icon}
<View key={i}> onPress={() => navigation.navigate(item.navigateTo as any)}
{renderSectionHeader({ section })} rounded={item.rounded}
{section.data.map((item, i) => ( />
<SettingsItem )}
key={i} renderSectionHeader={renderSectionHeader}
title={item.title} ListFooterComponent={<FooterComponent />}
leftIcon={item.icon} showsVerticalScrollIndicator={false}
onPress={() => navigation.navigate(item.navigateTo as any)} stickySectionHeadersEnabled={false}
rounded={item.rounded} initialNumToRender={50}
/> />
))}
</View>
))}
<FooterComponent />
</ScreenContainer> </ScreenContainer>
); );
} }

View file

@ -1,7 +1,7 @@
import { Themes, useThemeStore } from '@sd/client';
import { CheckCircle } from 'phosphor-react-native'; import { CheckCircle } from 'phosphor-react-native';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { ColorValue, Pressable, ScrollView, Text, View, ViewStyle } from 'react-native'; import { ColorValue, Pressable, ScrollView, Text, View, ViewStyle } from 'react-native';
import { Themes, useThemeStore } from '@sd/client';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { SettingsTitle } from '~/components/settings/SettingsContainer'; import { SettingsTitle } from '~/components/settings/SettingsContainer';
import Colors from '~/constants/style/Colors'; import Colors from '~/constants/style/Colors';
@ -126,10 +126,7 @@ const AppearanceSettingsScreen = ({
// TODO: Hook this up to the theme store once light theme is fixed. // TODO: Hook this up to the theme store once light theme is fixed.
return ( return (
<ScreenContainer header={{ <ScreenContainer scrollview={false} style={tw`gap-2 px-6`}>
navBack: true,
title: 'Appearance'
}} scrollview={false} style={tw`gap-2 px-6`}>
<SettingsTitle>Theme</SettingsTitle> <SettingsTitle>Theme</SettingsTitle>
<ScrollView <ScrollView
horizontal horizontal

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Text } from 'react-native'; import { Text, View } from 'react-native';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack'; import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
@ -8,11 +8,7 @@ const ExtensionsSettingsScreen = ({
navigation navigation
}: SettingsStackScreenProps<'ExtensionsSettings'>) => { }: SettingsStackScreenProps<'ExtensionsSettings'>) => {
return ( return (
<ScreenContainer header={{ <ScreenContainer style={tw`px-6`}>
title: 'Extensions',
navBack: true,
}}
style={tw`px-6`}>
<Text style={tw`text-ink`}>TODO</Text> <Text style={tw`text-ink`}>TODO</Text>
</ScreenContainer> </ScreenContainer>
); );

View file

@ -1,13 +1,14 @@
import { useBridgeQuery, useDebugState } from '@sd/client';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { useBridgeQuery, useDebugState } from '@sd/client';
import Card from '~/components/layout/Card'; import Card from '~/components/layout/Card';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { Divider } from '~/components/primitive/Divider'; import { Divider } from '~/components/primitive/Divider';
import { Input } from '~/components/primitive/Input'; import { Input } from '~/components/primitive/Input';
import { SettingsTitle } from '~/components/settings/SettingsContainer'; import { SettingsTitle } from '~/components/settings/SettingsContainer';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
const GeneralSettingsScreen = () => { const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'GeneralSettings'>) => {
const { data: node } = useBridgeQuery(['nodeState']); const { data: node } = useBridgeQuery(['nodeState']);
const debugState = useDebugState(); const debugState = useDebugState();
@ -15,12 +16,7 @@ const GeneralSettingsScreen = () => {
if (!node) return null; if (!node) return null;
return ( return (
<ScreenContainer <ScreenContainer style={tw`justify-start gap-0 px-6`} scrollview={false}>
header={{
title: 'General',
navBack: true,
}}
style={tw`justify-start gap-0 px-6`} scrollview={false}>
<Card> <Card>
{/* Card Header */} {/* Card Header */}
<View style={tw`flex flex-row justify-between`}> <View style={tw`flex flex-row justify-between`}>

View file

@ -1,9 +1,8 @@
import { LibraryConfigWrapped, useBridgeQuery, useCache, useNodes } from '@sd/client';
import { DotsThreeOutlineVertical, Pen, Trash } from 'phosphor-react-native'; import { DotsThreeOutlineVertical, Pen, Trash } from 'phosphor-react-native';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { Animated, Pressable, Text, View } from 'react-native'; import { Animated, FlatList, Pressable, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler'; import { Swipeable } from 'react-native-gesture-handler';
import { default as Reanimated } from 'react-native-reanimated'; import { LibraryConfigWrapped, useBridgeQuery, useCache, useNodes } from '@sd/client';
import Fade from '~/components/layout/Fade'; import Fade from '~/components/layout/Fade';
import { ModalRef } from '~/components/layout/Modal'; import { ModalRef } from '~/components/layout/Modal';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
@ -79,9 +78,7 @@ function LibraryItem({
); );
} }
const LibrarySettingsScreen = ({ const LibrarySettingsScreen = ({ navigation }: SettingsStackScreenProps<'LibrarySettings'>) => {
navigation,
}: SettingsStackScreenProps<'LibrarySettings'>) => {
const libraryList = useBridgeQuery(['library.list']); const libraryList = useBridgeQuery(['library.list']);
useNodes(libraryList.data?.nodes); useNodes(libraryList.data?.nodes);
const libraries = useCache(libraryList.data?.items); const libraries = useCache(libraryList.data?.items);
@ -104,10 +101,7 @@ const LibrarySettingsScreen = ({
const modalRef = useRef<ModalRef>(null); const modalRef = useRef<ModalRef>(null);
return ( return (
<ScreenContainer header={{ <ScreenContainer style={tw`justify-start gap-0 px-6 py-0`} scrollview={false}>
navBack: true,
title: 'Libraries',
}} scrollview={false} style={tw`justify-start gap-0 px-6 py-0`}>
<Fade <Fade
fadeSides="top-bottom" fadeSides="top-bottom"
orientation="vertical" orientation="vertical"
@ -115,7 +109,7 @@ const LibrarySettingsScreen = ({
width={30} width={30}
height="100%" height="100%"
> >
<Reanimated.FlatList <FlatList
data={libraries} data={libraries}
contentContainerStyle={tw`py-5`} contentContainerStyle={tw`py-5`}
keyExtractor={(item) => item.uuid} keyExtractor={(item) => item.uuid}

View file

@ -5,10 +5,7 @@ import { tw } from '~/lib/tailwind';
const PrivacySettingsScreen = () => { const PrivacySettingsScreen = () => {
return ( return (
<ScreenContainer header={{ <ScreenContainer scrollview={false} style={tw`px-6`}>
title: 'Privacy',
navBack: true,
}} scrollview={false} style={tw`px-6`}>
<Text style={tw`text-ink`}>TODO</Text> <Text style={tw`text-ink`}>TODO</Text>
</ScreenContainer> </ScreenContainer>
); );

View file

@ -1,8 +1,8 @@
import { useBridgeQuery } from '@sd/client';
import { Image } from 'expo-image'; import { Image } from 'expo-image';
import { Globe } from 'phosphor-react-native'; import { Globe } from 'phosphor-react-native';
import React from 'react'; import React from 'react';
import { Linking, Platform, Text, View } from 'react-native'; import { Linking, Platform, Text, View } from 'react-native';
import { useBridgeQuery } from '@sd/client';
import { DiscordIcon, GitHubIcon } from '~/components/icons/Brands'; import { DiscordIcon, GitHubIcon } from '~/components/icons/Brands';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
@ -11,16 +11,14 @@ import { tw } from '~/lib/tailwind';
const AboutScreen = () => { const AboutScreen = () => {
const buildInfo = useBridgeQuery(['buildInfo']); const buildInfo = useBridgeQuery(['buildInfo']);
return ( return (
<ScreenContainer header={{ <ScreenContainer style={tw`justify-start gap-0 px-6`}>
title: 'About',
navBack: true,
}} style={tw`justify-start gap-0 px-6`}>
<View style={tw`flex flex-row items-center`}> <View style={tw`flex flex-row items-center`}>
<Image <Image
source={require('../../../../assets/icon.png')} source={require('../../../../assets/icon.png')}
style={tw`mr-8 h-[88px] w-[88px] rounded-3xl`} style={tw`mr-8 h-[88px] w-[88px] rounded-3xl`}
contentFit="contain" resizeMode="contain"
/> />
<View style={tw`flex flex-col`}> <View style={tw`flex flex-col`}>
<Text style={tw`text-2xl font-bold text-white`}> <Text style={tw`text-2xl font-bold text-white`}>

View file

@ -1,8 +1,7 @@
import { useDebugState, useFeatureFlags } from '@sd/client';
import React from 'react'; import React from 'react';
import { Text } from 'react-native'; import { Text, View } from 'react-native';
import { toggleFeatureFlag, useDebugState, useFeatureFlags } from '@sd/client';
import Card from '~/components/layout/Card'; import Card from '~/components/layout/Card';
import ScreenContainer from '~/components/layout/ScreenContainer';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack'; import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
@ -12,16 +11,15 @@ const DebugScreen = ({ navigation }: SettingsStackScreenProps<'Debug'>) => {
const featureFlags = useFeatureFlags(); const featureFlags = useFeatureFlags();
return ( return (
<ScreenContainer style={tw`px-6`} header={{ title: 'Debug', navBack: true }}> <View style={tw`flex-1 p-4`}>
<Card style={tw`gap-y-4`}> <Card style={tw`gap-y-4 bg-app-box`}>
<Text style={tw`font-semibold text-ink`}>Debug</Text> <Text style={tw`font-semibold text-ink`}>Debug</Text>
<Button variant="darkgray" onPress={() => (debugState.rspcLogger = !debugState.rspcLogger)}> <Button onPress={() => (debugState.rspcLogger = !debugState.rspcLogger)}>
<Text style={tw`text-ink`}>Toggle rspc logger</Text> <Text style={tw`text-ink`}>Toggle rspc logger</Text>
</Button> </Button>
<Text style={tw`text-ink`}>{JSON.stringify(featureFlags)}</Text> <Text style={tw`text-ink`}>{JSON.stringify(featureFlags)}</Text>
<Text style={tw`text-ink`}>{JSON.stringify(debugState)}</Text> <Text style={tw`text-ink`}>{JSON.stringify(debugState)}</Text>
<Button <Button
variant="darkgray"
onPress={() => { onPress={() => {
navigation.popToTop(); navigation.popToTop();
navigation.replace('Settings'); navigation.replace('Settings');
@ -31,7 +29,7 @@ const DebugScreen = ({ navigation }: SettingsStackScreenProps<'Debug'>) => {
<Text style={tw`text-ink`}>Disable Debug Mode</Text> <Text style={tw`text-ink`}>Disable Debug Mode</Text>
</Button> </Button>
</Card> </Card>
</ScreenContainer> </View>
); );
}; };

View file

@ -1,18 +1,12 @@
import React from 'react'; import React from 'react';
import { Text } from 'react-native'; import { Text, View } from 'react-native';
import ScreenContainer from '~/components/layout/ScreenContainer';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
const SupportScreen = () => { const SupportScreen = () => {
return ( return (
<ScreenContainer <View>
style={tw`justify-start px-6 py-5`}
header={{
title: 'Support',
navBack: true,
}}>
<Text style={tw`text-ink`}>TODO</Text> <Text style={tw`text-ink`}>TODO</Text>
</ScreenContainer> </View>
); );
}; };

View file

@ -1,10 +1,10 @@
import { useLibraryMutation, useLibraryQuery, useNormalisedCache, useZodForm } from '@sd/client';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { Archive, ArrowsClockwise, Trash } from 'phosphor-react-native'; import { Archive, ArrowsClockwise, Trash } from 'phosphor-react-native';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import { Alert, Text, View } from 'react-native'; import { Alert, Text, View } from 'react-native';
import { z } from 'zod'; import { z } from 'zod';
import { useLibraryMutation, useLibraryQuery, useNormalisedCache, useZodForm } from '@sd/client';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { AnimatedButton } from '~/components/primitive/Button'; import { AnimatedButton } from '~/components/primitive/Button';
import { Divider } from '~/components/primitive/Divider'; import { Divider } from '~/components/primitive/Divider';
@ -28,9 +28,10 @@ const schema = z.object({
const EditLocationSettingsScreen = ({ const EditLocationSettingsScreen = ({
route, route,
navigation, navigation
}: SettingsStackScreenProps<'EditLocationSettings'>) => { }: SettingsStackScreenProps<'EditLocationSettings'>) => {
const { id } = route.params; const { id } = route.params;
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const cache = useNormalisedCache(); const cache = useNormalisedCache();
@ -110,10 +111,7 @@ const EditLocationSettingsScreen = ({
const fullRescan = useLibraryMutation('locations.fullRescan'); const fullRescan = useLibraryMutation('locations.fullRescan');
return ( return (
<ScreenContainer header={{ <ScreenContainer style={tw`px-6`}>
title: 'Edit Location',
navBack: true,
}} scrollview style={tw`px-6`}>
{/* Inputs */} {/* Inputs */}
<View> <View>
<SettingsTitle style={tw`mb-1`}>Display Name</SettingsTitle> <SettingsTitle style={tw`mb-1`}>Display Name</SettingsTitle>

View file

@ -1,8 +1,8 @@
import { useBridgeMutation, useLibraryContext, useZodForm } from '@sd/client';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler'; import { TouchableOpacity } from 'react-native-gesture-handler';
import { z } from 'zod'; import { z } from 'zod';
import { useBridgeMutation, useLibraryContext, useZodForm } from '@sd/client';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import DeleteLibraryModal from '~/components/modal/confirmModals/DeleteLibraryModal'; import DeleteLibraryModal from '~/components/modal/confirmModals/DeleteLibraryModal';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
@ -14,10 +14,11 @@ import { SettingsTitle } from '~/components/settings/SettingsContainer';
import SettingsToggle from '~/components/settings/SettingsToggle'; import SettingsToggle from '~/components/settings/SettingsToggle';
import { useAutoForm } from '~/hooks/useAutoForm'; import { useAutoForm } from '~/hooks/useAutoForm';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
const schema = z.object({ name: z.string(), description: z.string() }); const schema = z.object({ name: z.string(), description: z.string() });
const LibraryGeneralSettingsScreen = () => { const LibraryGeneralSettingsScreen = (_: SettingsStackScreenProps<'LibraryGeneralSettings'>) => {
const { library } = useLibraryContext(); const { library } = useLibraryContext();
const form = useZodForm({ const form = useZodForm({
@ -37,10 +38,7 @@ const LibraryGeneralSettingsScreen = () => {
}); });
return ( return (
<ScreenContainer header={{ <ScreenContainer scrollview={false} style={tw`justify-start px-6 py-0`}>
title: 'Library Settings',
navBack: true,
}} style={tw`justify-start px-6 py-0`}>
<View style={tw`pt-5`}> <View style={tw`pt-5`}>
<SettingsTitle style={tw`mb-1`}>Name</SettingsTitle> <SettingsTitle style={tw`mb-1`}>Name</SettingsTitle>
<Controller <Controller

View file

@ -1,6 +1,6 @@
import { useDiscoveredPeers } from '@sd/client';
import React from 'react'; import React from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { useDiscoveredPeers } from '@sd/client';
import ScreenContainer from '~/components/layout/ScreenContainer'; import ScreenContainer from '~/components/layout/ScreenContainer';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack'; import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
@ -9,10 +9,7 @@ const NodesSettingsScreen = ({ navigation }: SettingsStackScreenProps<'NodesSett
const onlineNodes = useDiscoveredPeers(); const onlineNodes = useDiscoveredPeers();
return ( return (
<ScreenContainer header={{ <ScreenContainer scrollview={false} style={tw`gap-0 px-6`}>
title: 'Nodes',
navBack: true,
}} scrollview={false} style={tw`gap-0 px-6`}>
<Text style={tw`text-ink`}>Pairing</Text> <Text style={tw`text-ink`}>Pairing</Text>
{[...onlineNodes.entries()].map(([id, node]) => ( {[...onlineNodes.entries()].map(([id, node]) => (