mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 13:23:28 +00:00
parent
b4037d6537
commit
6a556a457d
|
@ -1,8 +1,8 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { DotsThreeOutline, Plus } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
@ -64,7 +64,7 @@ const BrowseLocations = () => {
|
|||
initial: false
|
||||
})
|
||||
}
|
||||
onPress={() => navigation.navigate('Location', { id: location.id })}
|
||||
onPress={() => navigation.navigate('Location', { id: location.id, title: location.name })}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { DotsThreeOutline, Plus } from 'phosphor-react-native';
|
||||
import React, { useRef } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
@ -57,7 +57,7 @@ const BrowseTags = () => {
|
|||
key={tag.id}
|
||||
tag={tag}
|
||||
onPress={() =>
|
||||
navigation.navigate('Tag', { id: tag.id, color: tag.color! })
|
||||
navigation.navigate('Tag', { id: tag.id, color: tag.color!, title: tag.name })
|
||||
}
|
||||
/>
|
||||
))
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
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 { UseInfiniteQueryResult } from '@tanstack/react-query';
|
||||
import { ActivityIndicator, Pressable } from 'react-native';
|
||||
import { isPath, SearchData, type ExplorerItem } from '@sd/client';
|
||||
import { SharedValue } from 'react-native-reanimated';
|
||||
import Layout from '~/constants/Layout';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
import { useExplorerStore } from '~/stores/explorerStore';
|
||||
import { useActionsModalStore } from '~/stores/modalStore';
|
||||
|
||||
import { HeaderProps } from '../header/Header';
|
||||
import ScreenContainer from '../layout/ScreenContainer';
|
||||
import FileItem from './FileItem';
|
||||
import FileRow from './FileRow';
|
||||
import Menu from './menu/Menu';
|
||||
|
||||
|
||||
type ExplorerProps = {
|
||||
tabHeight?: boolean;
|
||||
items: ExplorerItem[] | null;
|
||||
|
@ -21,13 +24,15 @@ type ExplorerProps = {
|
|||
loadMore: () => void;
|
||||
query: UseInfiniteQueryResult<SearchData<ExplorerItem>>;
|
||||
count?: number;
|
||||
scrollY?: SharedValue<number>;
|
||||
route?: NativeStackHeaderProps['route'];
|
||||
headerKind?: HeaderProps['headerKind'];
|
||||
hideHeader?: boolean;
|
||||
};
|
||||
|
||||
const Explorer = (props: ExplorerProps) => {
|
||||
const navigation = useNavigation<BrowseStackScreenProps<'Location'>['navigation']>();
|
||||
|
||||
const store = useExplorerStore();
|
||||
|
||||
const { modalRef, setData } = useActionsModalStore();
|
||||
|
||||
function handlePress(data: ExplorerItem) {
|
||||
|
@ -43,8 +48,20 @@ const Explorer = (props: ExplorerProps) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<ScreenContainer tabHeight={props.tabHeight} scrollview={false} style={'gap-0 py-0'}>
|
||||
<Menu />
|
||||
<ScreenContainer
|
||||
hideHeader={props.hideHeader}
|
||||
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 */}
|
||||
<FlashList
|
||||
key={store.layoutMode}
|
||||
|
@ -66,6 +83,11 @@ const Explorer = (props: ExplorerProps) => {
|
|||
)}
|
||||
</Pressable>
|
||||
)}
|
||||
scrollEventThrottle={1}
|
||||
onScroll={(e) => {
|
||||
if (!props.scrollY) return;
|
||||
props.scrollY.value = e.nativeEvent.contentOffset.y;
|
||||
}}
|
||||
contentContainerStyle={tw`px-2 py-5`}
|
||||
extraData={store.layoutMode}
|
||||
estimatedItemSize={
|
||||
|
|
|
@ -9,7 +9,6 @@ import SortByMenu from './SortByMenu';
|
|||
|
||||
const Menu = () => {
|
||||
const store = useExplorerStore();
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{store.toggleMenu && (
|
||||
|
|
|
@ -2,32 +2,42 @@ import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackHeaderProps } from '@react-navigation/native-stack';
|
||||
import { ArrowLeft, DotsThreeOutline, List, MagnifyingGlass } from 'phosphor-react-native';
|
||||
import { Platform, Pressable, Text, View } from 'react-native';
|
||||
import React from 'react';
|
||||
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 { tw, twStyle } from '~/lib/tailwind';
|
||||
import { getExplorerStore, useExplorerStore } from '~/stores/explorerStore';
|
||||
|
||||
import { Icon } from '../icons/Icon';
|
||||
import { AnimatedPressable } from '../reanimated/components';
|
||||
import Search from '../search/Search';
|
||||
|
||||
type HeaderProps = {
|
||||
|
||||
type Props = {
|
||||
title?: string; //title of the page
|
||||
showSearch?: boolean; //show the search button
|
||||
showDrawer?: boolean; //show the drawer button
|
||||
searchType?: 'explorer' | 'location' | 'categories'; //Temporary
|
||||
searchType?: 'location' | 'categories' | 'tags'; //Temporary
|
||||
navBack?: boolean; //navigate back to the previous screen
|
||||
headerKind?: 'default' | 'location' | 'tag'; //kind of header
|
||||
route?: never;
|
||||
routeTitle?: never;
|
||||
scrollY?: SharedValue<number>; //scrollY of screen
|
||||
};
|
||||
|
||||
//you can pass in a routeTitle only if route is passed in
|
||||
type Props =
|
||||
| HeaderProps
|
||||
export type HeaderProps =
|
||||
| Props
|
||||
| ({
|
||||
route: NativeStackHeaderProps;
|
||||
route: NativeStackHeaderProps['route'];
|
||||
routeTitle?: boolean;
|
||||
} & Omit<HeaderProps, 'route' | 'routeTitle'>);
|
||||
} & Omit<Props, 'route' | 'routeTitle'>);
|
||||
|
||||
|
||||
// Default header with search bar and button to open drawer
|
||||
export default function Header({
|
||||
|
@ -35,55 +45,94 @@ export default function Header({
|
|||
searchType,
|
||||
navBack,
|
||||
route,
|
||||
routeTitle,
|
||||
headerKind = 'default',
|
||||
showDrawer = false,
|
||||
showSearch = true
|
||||
}: Props) {
|
||||
showSearch = false,
|
||||
scrollY
|
||||
}: HeaderProps) {
|
||||
const navigation = useNavigation<DrawerNavigationHelpers>();
|
||||
const explorerStore = useExplorerStore();
|
||||
const routeParams = route?.route.params as any;
|
||||
const headerHeight = useSafeAreaInsets().top;
|
||||
const routeParams = route?.params as any;
|
||||
const headerSafeArea = useSafeAreaInsets();
|
||||
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 (
|
||||
<View
|
||||
style={twStyle('relative h-auto w-full border-b border-app-cardborder bg-app-header', {
|
||||
paddingTop: headerHeight + (isAndroid ? 15 : 0)
|
||||
})}
|
||||
<Animated.View
|
||||
style={[
|
||||
twStyle('mt-0 w-full border-b border-app-cardborder bg-app-header', {
|
||||
paddingTop: headerSafeArea.top + (isAndroid ? 15 : 5),
|
||||
}),
|
||||
scrollYHeader
|
||||
]}
|
||||
>
|
||||
<View style={tw`mx-auto h-auto w-full justify-center px-5 pb-4`}>
|
||||
<View style={tw`mx-auto h-auto w-full justify-center px-5 pb-6`}>
|
||||
<View style={tw`w-full flex-row items-center justify-between`}>
|
||||
<View style={tw`flex-row items-center gap-3`}>
|
||||
<View style={tw`flex-row items-center`}>
|
||||
{navBack && (
|
||||
<Pressable
|
||||
<AnimatedPressable
|
||||
style={scrollYIcon}
|
||||
hitSlop={24}
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
}}
|
||||
onPress={() => navigation.goBack()}
|
||||
>
|
||||
<ArrowLeft size={23} color={tw.color('ink')} />
|
||||
</Pressable>
|
||||
</AnimatedPressable>
|
||||
)}
|
||||
<View style={tw`flex-row items-center gap-2`}>
|
||||
<View style={tw`flex-row items-center gap-1.5`}>
|
||||
<Animated.View style={scrollYIcon}>
|
||||
<HeaderIconKind headerKind={headerKind} routeParams={routeParams} />
|
||||
</Animated.View>
|
||||
{showDrawer && (
|
||||
<Pressable onPress={() => navigation.openDrawer()}>
|
||||
<List size={24} color={tw.color('text-zinc-300')} />
|
||||
</Pressable>
|
||||
)}
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={tw`max-w-[200px] text-xl font-bold text-white`}
|
||||
<AnimatedPressable
|
||||
style={scrollYIcon}
|
||||
onPress={() => navigation.openDrawer()}
|
||||
>
|
||||
{title || (routeTitle && route?.options.title)}
|
||||
</Text>
|
||||
<List style={twStyle({
|
||||
top: isAndroid ? 2 : 0 //fixes the icon alignment on android
|
||||
})} size={24} color={tw.color('text-zinc-300')} />
|
||||
</AnimatedPressable>
|
||||
)}
|
||||
<Animated.Text
|
||||
numberOfLines={1}
|
||||
style={[twStyle('max-w-[200px] text-md font-bold text-ink'), scrollYTitle]}
|
||||
>
|
||||
{title || routeParams?.title}
|
||||
</Animated.Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={tw`relative flex-row items-center gap-3`}>
|
||||
{showSearch && (
|
||||
<View style={tw`flex-row items-center gap-2`}>
|
||||
<Pressable
|
||||
<AnimatedPressable
|
||||
style={scrollYIcon}
|
||||
hitSlop={24}
|
||||
onPress={() => {
|
||||
navigation.navigate('SearchStack', {
|
||||
|
@ -92,11 +141,10 @@ export default function Header({
|
|||
}}
|
||||
>
|
||||
<MagnifyingGlass
|
||||
size={24}
|
||||
weight="bold"
|
||||
color={tw.color('text-zinc-300')}
|
||||
/>
|
||||
</Pressable>
|
||||
</AnimatedPressable>
|
||||
</View>
|
||||
)}
|
||||
{(headerKind === 'location' || headerKind === 'tag') && (
|
||||
|
@ -118,7 +166,7 @@ export default function Header({
|
|||
</View>
|
||||
{searchType && <HeaderSearchType searchType={searchType} />}
|
||||
</View>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -128,10 +176,10 @@ interface HeaderSearchTypeProps {
|
|||
|
||||
const HeaderSearchType = ({ searchType }: HeaderSearchTypeProps) => {
|
||||
switch (searchType) {
|
||||
case 'explorer':
|
||||
return 'Explorer'; //TODO
|
||||
case 'location':
|
||||
return <Search placeholder="Location name..." />;
|
||||
case 'tags':
|
||||
return <Search placeholder="Tag name..." />;
|
||||
case 'categories':
|
||||
return <Search placeholder="Category name..." />;
|
||||
default:
|
||||
|
@ -147,11 +195,11 @@ interface HeaderIconKindProps {
|
|||
const HeaderIconKind = ({ headerKind, routeParams }: HeaderIconKindProps) => {
|
||||
switch (headerKind) {
|
||||
case 'location':
|
||||
return <Icon size={30} name="Folder" />;
|
||||
return <Icon style={tw`ml-3`} size={30} name="Folder" />;
|
||||
case 'tag':
|
||||
return (
|
||||
<View
|
||||
style={twStyle('h-[30px] w-[30px] rounded-full', {
|
||||
style={twStyle('ml-3 h-[24px] w-[24px] rounded-full', {
|
||||
backgroundColor: routeParams.color
|
||||
})}
|
||||
/>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { useRoute } from '@react-navigation/native';
|
||||
import { DimensionValue, Platform } from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { ClassInput } from 'twrnc';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { useExplorerStore } from '~/stores/explorerStore';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode; // children of fade
|
||||
|
@ -13,7 +11,6 @@ interface Props {
|
|||
orientation?: 'horizontal' | 'vertical'; // orientation of fade
|
||||
fadeSides?: 'left-right' | 'top-bottom'; // which sides to fade
|
||||
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
|
||||
topFadeStyle?: ClassInput; // tailwind style for top fade
|
||||
}
|
||||
|
@ -25,20 +22,15 @@ const Fade = ({
|
|||
height,
|
||||
bottomFadeStyle,
|
||||
topFadeStyle,
|
||||
noConditions = false,
|
||||
screenFade = false,
|
||||
fadeSides = 'left-right',
|
||||
orientation = 'horizontal'
|
||||
}: Props) => {
|
||||
const route = useRoute();
|
||||
const { toggleMenu } = useExplorerStore();
|
||||
const bottomTabBarHeight = Platform.OS === 'ios' ? 80 : 60;
|
||||
const gradientStartEndMap = {
|
||||
'left-right': { start: { x: 0, y: 0 }, end: { x: 1, 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 (
|
||||
<>
|
||||
<LinearGradient
|
||||
|
@ -46,10 +38,7 @@ const Fade = ({
|
|||
width: orientation === 'vertical' ? height : width,
|
||||
height: orientation === 'vertical' ? width : height,
|
||||
position: 'absolute',
|
||||
top:
|
||||
!noConditions && toggleMenu && routesWithMenu.includes(route.name)
|
||||
? menuHeight
|
||||
: 0,
|
||||
top: 0,
|
||||
alignSelf: 'center',
|
||||
left: fadeSides === 'left-right' ? 0 : undefined,
|
||||
transform: fadeSides === 'left-right' ? undefined : [{ rotate: '180deg' }],
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { ReactNode, useRef } from 'react';
|
||||
import { Platform, ScrollView, View } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { ReactNode, useEffect, useRef } from 'react';
|
||||
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 { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import Fade from './Fade';
|
||||
import Header, { HeaderProps } from '../header/Header';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
|
@ -16,37 +19,60 @@ interface Props {
|
|||
/** Styling of both side fades */
|
||||
topFadeStyle?: string;
|
||||
bottomFadeStyle?: string;
|
||||
scrollY?: SharedValue<number>;
|
||||
/* Header properties */
|
||||
header?: HeaderProps;
|
||||
hideHeader?: boolean; // Hide the header
|
||||
}
|
||||
|
||||
const ScreenContainer = ({
|
||||
children,
|
||||
style,
|
||||
topFadeStyle,
|
||||
bottomFadeStyle,
|
||||
header,
|
||||
scrollY,
|
||||
hideHeader = false,
|
||||
scrollview = true,
|
||||
tabHeight = true,
|
||||
scrollToBottomOnChange = false
|
||||
scrollToBottomOnChange = false,
|
||||
}: Props) => {
|
||||
const ref = useRef<ScrollView>(null);
|
||||
const ref = useRef<AnimatedScrollView>(null);
|
||||
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 ? (
|
||||
<View style={tw`relative flex-1`}>
|
||||
<Fade
|
||||
topFadeStyle={topFadeStyle}
|
||||
bottomFadeStyle={bottomFadeStyle}
|
||||
screenFade
|
||||
fadeSides="top-bottom"
|
||||
orientation="vertical"
|
||||
color="black"
|
||||
width={30}
|
||||
height="100%"
|
||||
>
|
||||
<ScrollView
|
||||
{!hideHeader && <Header {...header} scrollY={scrollY} />}
|
||||
<Animated.ScrollView
|
||||
ref={ref}
|
||||
onContentSizeChange={() => {
|
||||
if (!scrollToBottomOnChange) return;
|
||||
ref.current?.scrollToEnd({ animated: true });
|
||||
}}
|
||||
scrollEventThrottle={1}
|
||||
onScroll={scrollHandler}
|
||||
contentContainerStyle={twStyle('justify-between gap-10 py-6', style)}
|
||||
style={twStyle(
|
||||
'flex-1 bg-black',
|
||||
|
@ -54,21 +80,11 @@ const ScreenContainer = ({
|
|||
)}
|
||||
>
|
||||
{children}
|
||||
</ScrollView>
|
||||
</Fade>
|
||||
</Animated.ScrollView>
|
||||
</View>
|
||||
) : (
|
||||
<View style={tw`relative flex-1`}>
|
||||
<Fade
|
||||
topFadeStyle={topFadeStyle}
|
||||
bottomFadeStyle={bottomFadeStyle}
|
||||
screenFade
|
||||
fadeSides="top-bottom"
|
||||
orientation="vertical"
|
||||
color="black"
|
||||
width={30}
|
||||
height="100%"
|
||||
>
|
||||
{!hideHeader && <Header {...header} />}
|
||||
<View
|
||||
style={twStyle(
|
||||
'flex-1 justify-between gap-10 bg-black py-6',
|
||||
|
@ -78,7 +94,6 @@ const ScreenContainer = ({
|
|||
>
|
||||
{children}
|
||||
</View>
|
||||
</Fade>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Location } from '@sd/client';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable } from 'react-native';
|
||||
import { Location } from '@sd/client';
|
||||
import { twStyle } from '~/lib/tailwind';
|
||||
|
||||
import { ModalRef } from '../layout/Modal';
|
||||
|
@ -23,7 +23,6 @@ export const LocationItem = ({
|
|||
}: LocationItemProps) => {
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
style={twStyle(viewStyle === 'grid' ? `w-[31.5%]` : `flex-1`)}
|
||||
onPress={onPress}
|
||||
|
@ -44,6 +43,5 @@ export const LocationItem = ({
|
|||
<ListLocation location={location} />
|
||||
)}
|
||||
</Pressable>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import React, { useRef } from 'react';
|
||||
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 { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
|
||||
|
||||
|
@ -63,7 +63,7 @@ const Locations = () => {
|
|||
navigation.jumpTo('BrowseStack', {
|
||||
initial: false,
|
||||
screen: 'Location',
|
||||
params: { id: item.id }
|
||||
params: { id: item.id, title: item.name }
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -14,7 +14,7 @@ const OverviewSection = ({ title, count, children }: Props) => {
|
|||
<View style={tw`flex-row items-center gap-3 px-6 pb-3`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>{title}</Text>
|
||||
<View
|
||||
style={tw`flex h-[24px] w-[24px] items-center justify-center rounded-full border border-app-button/40 px-1`}
|
||||
style={tw`flex h-[24px] w-[24px] items-center justify-center rounded-full border border-app-button px-1`}
|
||||
>
|
||||
<Text style={tw`text-xs text-ink`}>{count}</Text>
|
||||
</View>
|
||||
|
|
4
apps/mobile/src/components/reanimated/components.tsx
Normal file
4
apps/mobile/src/components/reanimated/components.tsx
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { Pressable } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
export const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
@ -18,12 +18,12 @@ export default function Search({ placeholder }: Props) {
|
|||
}, [searchStore]);
|
||||
return (
|
||||
<View
|
||||
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`}
|
||||
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`}
|
||||
>
|
||||
<TextInput
|
||||
onChangeText={(text) => searchStore.setSearch(text)}
|
||||
placeholderTextColor={tw.color('text-ink-dull')}
|
||||
style={tw`w-[90%] text-white`}
|
||||
style={tw`leading-0 w-[90%] text-sm text-white`}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
<MagnifyingGlass size={20} weight="bold" color={tw.color('text-ink-dull')} />
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
import { useEffect, useRef } from 'react';
|
||||
import { FlatList, Pressable, Text, View } from 'react-native';
|
||||
import { Icon } from '~/components/icons/Icon';
|
||||
import Fade from '~/components/layout/Fade';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { SearchStackScreenProps } from '~/navigation/SearchStack';
|
||||
|
@ -47,7 +46,6 @@ const FiltersBar = () => {
|
|||
<Plus weight="bold" size={20} color={tw.color('text-ink-dull')} />
|
||||
</Button>
|
||||
<View style={tw`relative flex-1`}>
|
||||
<Fade noConditions height={'100%'} width={30} color="app-header">
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
|
@ -60,7 +58,6 @@ const FiltersBar = () => {
|
|||
)}
|
||||
contentContainerStyle={tw`flex-row gap-2 pl-4 pr-4`}
|
||||
/>
|
||||
</Fade>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Tag } from '@sd/client';
|
||||
import { DotsThreeOutlineVertical } from 'phosphor-react-native';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { Tag } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import Card from '../layout/Card';
|
||||
|
@ -20,7 +20,7 @@ const GridTag = ({ tag, modalRef }: GridTagProps) => {
|
|||
backgroundColor: tag.color!
|
||||
})}
|
||||
/>
|
||||
<Pressable hitSlop={24} onPress={() => modalRef.current?.present()}>
|
||||
<Pressable hitSlop={12} onPress={() => modalRef.current?.present()}>
|
||||
<DotsThreeOutlineVertical
|
||||
weight="fill"
|
||||
size={20}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
import Header from '~/components/header/Header';
|
||||
import FiltersScreen from '~/screens/search/Filters';
|
||||
import SearchScreen from '~/screens/search/Search';
|
||||
|
||||
|
@ -8,22 +7,18 @@ const Stack = createNativeStackNavigator<SearchStackParamList>();
|
|||
|
||||
export default function SearchStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Search">
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false
|
||||
}}
|
||||
initialRouteName="Search">
|
||||
<Stack.Screen
|
||||
name="Search"
|
||||
component={SearchScreen}
|
||||
options={{
|
||||
headerShown: false
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Filters"
|
||||
component={FiltersScreen}
|
||||
options={{
|
||||
header: () => {
|
||||
return <Header navBack showSearch={false} title="Search filters" />;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
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/Browse';
|
||||
import LibraryScreen from '~/screens/browse/Library';
|
||||
import LocationScreen from '~/screens/browse/Location';
|
||||
|
@ -14,48 +13,35 @@ const Stack = createNativeStackNavigator<BrowseStackParamList>();
|
|||
|
||||
export default function BrowseStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Browse">
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false
|
||||
}}
|
||||
initialRouteName="Browse">
|
||||
<Stack.Screen
|
||||
name="Browse"
|
||||
component={BrowseScreen}
|
||||
options={{ header: () => <Header showDrawer title="Browse" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Location"
|
||||
component={LocationScreen}
|
||||
options={{
|
||||
header: (route) => (
|
||||
<Header route={route} headerKind="location" routeTitle navBack />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{(props) => <LocationScreen {...props}/>}
|
||||
</Stack.Screen>
|
||||
<Stack.Screen
|
||||
name="Tags"
|
||||
component={TagsScreen}
|
||||
options={{
|
||||
header: () => <Header navBack title="Tags" />
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Locations"
|
||||
component={LocationsScreen}
|
||||
options={{
|
||||
header: () => <Header navBack searchType="location" title="Locations" />
|
||||
}}
|
||||
/>
|
||||
component={LocationsScreen}/>
|
||||
<Stack.Screen
|
||||
name="Tag"
|
||||
component={TagScreen}
|
||||
options={{
|
||||
header: (route) => <Header navBack routeTitle route={route} headerKind="tag" />
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{(props) => <TagScreen {...props} />}
|
||||
</Stack.Screen>
|
||||
<Stack.Screen
|
||||
name="Library"
|
||||
component={LibraryScreen}
|
||||
options={{
|
||||
header: () => <Header navBack title="Library" />
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
@ -63,9 +49,9 @@ export default function BrowseStack() {
|
|||
|
||||
export type BrowseStackParamList = {
|
||||
Browse: undefined;
|
||||
Location: { id: number; path?: string };
|
||||
Location: { id: number; path?: string, title?: string | null };
|
||||
Locations: undefined;
|
||||
Tag: { id: number; color: string };
|
||||
Tag: { id: number; color: string, title?: string | null };
|
||||
Tags: undefined;
|
||||
Library: undefined;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
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/Network';
|
||||
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
@ -9,11 +8,12 @@ const Stack = createNativeStackNavigator<NetworkStackParamList>();
|
|||
|
||||
export default function NetworkStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Network">
|
||||
<Stack.Navigator screenOptions={{
|
||||
headerShown: false
|
||||
}} initialRouteName="Network">
|
||||
<Stack.Screen
|
||||
name="Network"
|
||||
component={NetworkScreen}
|
||||
options={{ header: () => <Header showDrawer title="Network" /> }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
|
|
@ -1,27 +1,24 @@
|
|||
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/Overview';
|
||||
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
const Stack = createNativeStackNavigator<OverviewStackParamList>();
|
||||
|
||||
export default function OverviewStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Overview">
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false
|
||||
}}>
|
||||
<Stack.Screen
|
||||
name="Overview"
|
||||
component={OverviewScreen}
|
||||
options={{ header: () => <Header showDrawer title="Overview" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Categories"
|
||||
component={CategoriesScreen}
|
||||
options={{
|
||||
header: () => <Header searchType="categories" navBack title="Categories" />
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CompositeScreenProps } from '@react-navigation/native';
|
|||
// import KeysSettingsScreen from '~/screens/settings/library/KeysSettings';
|
||||
|
||||
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import LocationsScreen from '~/screens/browse/Locations';
|
||||
import AppearanceSettingsScreen from '~/screens/settings/client/AppearanceSettings';
|
||||
import ExtensionsSettingsScreen from '~/screens/settings/client/ExtensionsSettings';
|
||||
import GeneralSettingsScreen from '~/screens/settings/client/GeneralSettings';
|
||||
|
@ -13,7 +13,6 @@ import DebugScreen from '~/screens/settings/info/Debug';
|
|||
import SupportScreen from '~/screens/settings/info/Support';
|
||||
import EditLocationSettingsScreen from '~/screens/settings/library/EditLocationSettings';
|
||||
import LibraryGeneralSettingsScreen from '~/screens/settings/library/LibraryGeneralSettings';
|
||||
import LocationSettingsScreen from '~/screens/settings/library/LocationSettings';
|
||||
import NodesSettingsScreen from '~/screens/settings/library/NodesSettings';
|
||||
import TagsSettingsScreen from '~/screens/settings/library/TagsSettings';
|
||||
import SettingsScreen from '~/screens/settings/Settings';
|
||||
|
@ -24,65 +23,59 @@ const Stack = createNativeStackNavigator<SettingsStackParamList>();
|
|||
|
||||
export default function SettingsStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Settings">
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false
|
||||
}}
|
||||
initialRouteName="Settings">
|
||||
<Stack.Screen
|
||||
name="Settings"
|
||||
component={SettingsScreen}
|
||||
options={{ header: () => <Header showDrawer title="Settings" /> }}
|
||||
/>
|
||||
>
|
||||
{(props) => <SettingsScreen {...props}/>}
|
||||
</Stack.Screen>
|
||||
{/* Client */}
|
||||
<Stack.Screen
|
||||
name="GeneralSettings"
|
||||
component={GeneralSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="General" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="LibrarySettings"
|
||||
component={LibrarySettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Libraries" /> }}
|
||||
/>
|
||||
>
|
||||
{(props) => <LibrarySettingsScreen {...props} />}
|
||||
</Stack.Screen>
|
||||
<Stack.Screen
|
||||
name="AppearanceSettings"
|
||||
component={AppearanceSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Appearance" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PrivacySettings"
|
||||
component={PrivacySettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Privacy" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ExtensionsSettings"
|
||||
component={ExtensionsSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Extensions" /> }}
|
||||
/>
|
||||
{/* Library */}
|
||||
<Stack.Screen
|
||||
name="LibraryGeneralSettings"
|
||||
component={LibraryGeneralSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Library Settings" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="LocationSettings"
|
||||
component={LocationSettingsScreen}
|
||||
options={{
|
||||
header: () => <Header searchType="location" navBack title="Locations" />
|
||||
}}
|
||||
component={LocationsScreen}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="EditLocationSettings"
|
||||
component={EditLocationSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Edit Location" /> }}
|
||||
/>
|
||||
>
|
||||
{(props) => <EditLocationSettingsScreen {...props} />}
|
||||
</Stack.Screen>
|
||||
<Stack.Screen
|
||||
name="NodesSettings"
|
||||
component={NodesSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Nodes" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="TagsSettings"
|
||||
component={TagsSettingsScreen}
|
||||
options={{ header: () => <Header navBack title="Tags" /> }}
|
||||
/>
|
||||
{/* <Stack.Screen
|
||||
name="KeysSettings"
|
||||
|
@ -93,17 +86,14 @@ export default function SettingsStack() {
|
|||
<Stack.Screen
|
||||
name="About"
|
||||
component={AboutScreen}
|
||||
options={{ header: () => <Header navBack title="About" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Support"
|
||||
component={SupportScreen}
|
||||
options={{ header: () => <Header navBack title="Support" /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Debug"
|
||||
component={DebugScreen}
|
||||
options={{ header: () => <Header navBack title="Debug" /> }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import { useSharedValue } from 'react-native-reanimated';
|
||||
import BrowseCategories from '~/components/browse/BrowseCategories';
|
||||
import BrowseLocations from '~/components/browse/BrowseLocations';
|
||||
import BrowseTags from '~/components/browse/BrowseTags';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
|
||||
export default function BrowseScreen() {
|
||||
const scrollY = useSharedValue(0);
|
||||
return (
|
||||
<ScreenContainer>
|
||||
<ScreenContainer header={{
|
||||
scrollY: scrollY,
|
||||
showSearch: true,
|
||||
showDrawer: true,
|
||||
title: 'Browse',
|
||||
}}>
|
||||
<BrowseCategories />
|
||||
<BrowseLocations />
|
||||
<BrowseTags />
|
||||
|
|
|
@ -6,7 +6,13 @@ import { tw } from '~/lib/tailwind';
|
|||
|
||||
export default function LibraryScreen() {
|
||||
return (
|
||||
<ScreenContainer style={tw`px-6 py-0`} scrollview={false}>
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'Library',
|
||||
navBack: true,
|
||||
showSearch: true,
|
||||
}}
|
||||
style={tw`px-6 py-0`} scrollview={false}>
|
||||
<FlatList
|
||||
data={CATEGORIES_LIST}
|
||||
contentContainerStyle={tw`py-6`}
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import { useEffect } from 'react';
|
||||
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 { useSharedValue } from 'react-native-reanimated';
|
||||
import Explorer from '~/components/explorer/Explorer';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
import { BrowseStackParamList } from '~/navigation/tabs/BrowseStack';
|
||||
import { getExplorerStore } from '~/stores/explorerStore';
|
||||
|
||||
export default function LocationScreen({ navigation, route }: BrowseStackScreenProps<'Location'>) {
|
||||
const { id, path } = route.params;
|
||||
interface Props {
|
||||
route: RouteProp<BrowseStackParamList, 'Location'>;
|
||||
navigation: NativeStackNavigationProp<BrowseStackParamList, 'Location'>;
|
||||
}
|
||||
|
||||
export default function LocationScreen({ navigation, route }: Props) {
|
||||
const { id, path } = route.params;
|
||||
const scrollY = useSharedValue(0);
|
||||
const location = useLibraryQuery(['locations.get', route.params.id]);
|
||||
useNodes(location.data?.nodes);
|
||||
const locationData = useCache(location.data?.item);
|
||||
|
@ -59,5 +67,7 @@ export default function LocationScreen({ navigation, route }: BrowseStackScreenP
|
|||
getExplorerStore().path = path ?? '';
|
||||
}, [id, path]);
|
||||
|
||||
return <Explorer {...paths} />;
|
||||
return (
|
||||
<Explorer headerKind='location' route={route} scrollY={scrollY} {...paths} />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { Plus } from 'phosphor-react-native';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { FlatList, Pressable, View } from 'react-native';
|
||||
import { Pressable, View } from 'react-native';
|
||||
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import Empty from '~/components/layout/Empty';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
|
@ -19,6 +20,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export default function LocationsScreen({ viewStyle }: Props) {
|
||||
const scrollY = useSharedValue(0);
|
||||
const locationsQuery = useLibraryQuery(['locations.list']);
|
||||
useNodes(locationsQuery.data?.nodes);
|
||||
const locations = useCache(locationsQuery.data?.items);
|
||||
|
@ -37,8 +39,17 @@ export default function LocationsScreen({ viewStyle }: Props) {
|
|||
BrowseStackScreenProps<'Browse'>['navigation'] &
|
||||
SettingsStackScreenProps<'Settings'>['navigation']
|
||||
>();
|
||||
const scrollHandler = useAnimatedScrollHandler((e) => {
|
||||
scrollY.value = e.contentOffset.y;
|
||||
});
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'Locations',
|
||||
navBack: true,
|
||||
searchType: 'location',
|
||||
}}
|
||||
scrollview={false} style={tw`relative px-6 py-0`}>
|
||||
<Pressable
|
||||
style={tw`absolute bottom-7 right-7 z-10 h-12 w-12 items-center justify-center rounded-full bg-accent`}
|
||||
onPress={() => {
|
||||
|
@ -47,12 +58,13 @@ export default function LocationsScreen({ viewStyle }: Props) {
|
|||
>
|
||||
<Plus size={20} weight="bold" style={tw`text-ink`} />
|
||||
</Pressable>
|
||||
<FlatList
|
||||
<Animated.FlatList
|
||||
data={filteredLocations}
|
||||
contentContainerStyle={twStyle(
|
||||
`py-6`,
|
||||
filteredLocations.length === 0 && 'h-full items-center justify-center'
|
||||
)}
|
||||
onScroll={scrollHandler}
|
||||
keyExtractor={(location) => location.id.toString()}
|
||||
ItemSeparatorComponent={() => <View style={tw`h-2`} />}
|
||||
showsVerticalScrollIndicator={false}
|
||||
|
@ -71,7 +83,7 @@ export default function LocationsScreen({ viewStyle }: Props) {
|
|||
onPress={() =>
|
||||
navigation.navigate('BrowseStack', {
|
||||
screen: 'Location',
|
||||
params: { id: item.id }
|
||||
params: { id: item.id, title: item.name }
|
||||
})
|
||||
}
|
||||
editLocation={() =>
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useCache, useLibraryQuery, useNodes, useObjectsExplorerQuery } from '@sd/client';
|
||||
import { useEffect } from 'react';
|
||||
import { useSharedValue } from 'react-native-reanimated';
|
||||
import Explorer from '~/components/explorer/Explorer';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
||||
export default function TagScreen({ navigation, route }: BrowseStackScreenProps<'Tag'>) {
|
||||
const { id } = route.params;
|
||||
|
||||
interface Props {
|
||||
route: BrowseStackScreenProps<'Tag'>['route'];
|
||||
navigation: BrowseStackScreenProps<'Tag'>['navigation'];
|
||||
}
|
||||
|
||||
export default function TagScreen({
|
||||
navigation,
|
||||
route,
|
||||
}: Props) {
|
||||
const { id } = route.params;
|
||||
const scrollY = useSharedValue(0);
|
||||
const tag = useLibraryQuery(['tags.get', id]);
|
||||
useNodes(tag.data?.nodes);
|
||||
const tagData = useCache(tag.data?.item);
|
||||
|
@ -22,5 +32,7 @@ export default function TagScreen({ navigation, route }: BrowseStackScreenProps<
|
|||
});
|
||||
}, [tagData?.name, navigation]);
|
||||
|
||||
return <Explorer {...objects} />;
|
||||
return (
|
||||
<Explorer headerKind='tag' route={route} scrollY={scrollY} {...objects} />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { Plus } from 'phosphor-react-native';
|
||||
import { useRef } from 'react';
|
||||
import { Pressable, View } from 'react-native';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { Plus } from 'phosphor-react-native';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Pressable, View } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import Empty from '~/components/layout/Empty';
|
||||
import Fade from '~/components/layout/Fade';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import CreateTagModal from '~/components/modal/tag/CreateTagModal';
|
||||
import { TagItem } from '~/components/tags/TagItem';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
import { useSearchStore } from '~/stores/searchStore';
|
||||
|
||||
interface Props {
|
||||
viewStyle?: 'grid' | 'list';
|
||||
|
@ -20,13 +21,32 @@ interface Props {
|
|||
export default function TagsScreen({ viewStyle = 'list' }: Props) {
|
||||
const navigation = useNavigation<BrowseStackScreenProps<'Browse'>['navigation']>();
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
const { search } = useSearchStore();
|
||||
|
||||
|
||||
const tags = useLibraryQuery(['tags.list']);
|
||||
useNodes(tags.data?.nodes);
|
||||
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 (
|
||||
<ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'Tags',
|
||||
showSearch: false,
|
||||
navBack: true,
|
||||
searchType: 'tags',
|
||||
}}
|
||||
scrollview={false}
|
||||
style={tw`relative px-6 py-0`}>
|
||||
<Pressable
|
||||
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"
|
||||
|
@ -36,15 +56,9 @@ export default function TagsScreen({ viewStyle = 'list' }: Props) {
|
|||
>
|
||||
<Plus size={20} weight="bold" style={tw`text-ink`} />
|
||||
</Pressable>
|
||||
<Fade
|
||||
fadeSides="top-bottom"
|
||||
orientation="vertical"
|
||||
color="black"
|
||||
width={30}
|
||||
height="100%"
|
||||
>
|
||||
<FlatList
|
||||
data={tagData}
|
||||
<Animated.FlatList
|
||||
data={filteredTags}
|
||||
scrollEventThrottle={1}
|
||||
renderItem={({ item }) => (
|
||||
<TagItem
|
||||
viewStyle={viewStyle}
|
||||
|
@ -76,7 +90,6 @@ export default function TagsScreen({ viewStyle = 'list' }: Props) {
|
|||
tagData.length === 0 && 'h-full items-center justify-center'
|
||||
)}
|
||||
/>
|
||||
</Fade>
|
||||
<CreateTagModal ref={modalRef} />
|
||||
</ScreenContainer>
|
||||
);
|
||||
|
|
|
@ -6,7 +6,11 @@ import { NetworkStackScreenProps } from '~/navigation/tabs/NetworkStack';
|
|||
|
||||
export default function NetworkScreen({ navigation }: NetworkStackScreenProps<'Network'>) {
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`items-center justify-center gap-0`}>
|
||||
<ScreenContainer header={{
|
||||
showDrawer: true,
|
||||
title: 'Network',
|
||||
showSearch: true,
|
||||
}} scrollview={false} style={tw`items-center justify-center gap-0`}>
|
||||
<Icon name="Globe" size={128} />
|
||||
<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`}>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useMemo } from 'react';
|
||||
import { FlatList, View } from 'react-native';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { useMemo } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import { IconName } from '~/components/icons/Icon';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import CategoryItem from '~/components/overview/CategoryItem';
|
||||
|
@ -9,6 +10,7 @@ import { tw } from '~/lib/tailwind';
|
|||
import { useSearchStore } from '~/stores/searchStore';
|
||||
|
||||
const CategoriesScreen = () => {
|
||||
const scrollY = useSharedValue(0);
|
||||
const kinds = useLibraryQuery(['library.kindStatistics']);
|
||||
const { search } = useSearchStore();
|
||||
const [debouncedSearch] = useDebounce(search, 200);
|
||||
|
@ -19,13 +21,26 @@ const CategoriesScreen = () => {
|
|||
) ?? [],
|
||||
[debouncedSearch, kinds]
|
||||
);
|
||||
const scrollHandler = useAnimatedScrollHandler((e) => {
|
||||
scrollY.value = e.contentOffset.y;
|
||||
});
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
|
||||
<FlatList
|
||||
<ScreenContainer
|
||||
header={{
|
||||
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)}
|
||||
numColumns={3}
|
||||
onScroll={scrollHandler}
|
||||
contentContainerStyle={tw`py-6`}
|
||||
keyExtractor={(item) => item.name}
|
||||
scrollEventThrottle={1}
|
||||
ItemSeparatorComponent={() => <View style={tw`h-2`} />}
|
||||
showsVerticalScrollIndicator={false}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useBridgeQuery, useLibraryQuery } from '@sd/client';
|
||||
import { useSharedValue } from 'react-native-reanimated';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import Categories from '~/components/overview/Categories';
|
||||
import Cloud from '~/components/overview/Cloud';
|
||||
|
@ -20,13 +21,19 @@ const EMPTY_STATISTICS = {
|
|||
|
||||
export default function OverviewScreen() {
|
||||
const { data: node } = useBridgeQuery(['nodeState']);
|
||||
|
||||
const scrollY = useSharedValue(0);
|
||||
const stats = useLibraryQuery(['library.statistics'], {
|
||||
initialData: { ...EMPTY_STATISTICS }
|
||||
});
|
||||
|
||||
return (
|
||||
<ScreenContainer>
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'Overview',
|
||||
showSearch: true,
|
||||
showDrawer: true,
|
||||
}}
|
||||
scrollY={scrollY}>
|
||||
<OverviewStats stats={stats} />
|
||||
<Categories />
|
||||
<Devices stats={stats} node={node} />
|
||||
|
|
|
@ -5,7 +5,13 @@ import SaveAdd from '~/components/search/filters/SaveAdd';
|
|||
const FiltersScreen = () => {
|
||||
return (
|
||||
<>
|
||||
<ScreenContainer bottomFadeStyle="bottom-0" tabHeight={false}>
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'Filters',
|
||||
showSearch: false,
|
||||
navBack: true,
|
||||
}}
|
||||
tabHeight={false}>
|
||||
<FiltersList />
|
||||
</ScreenContainer>
|
||||
<SaveAdd />
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { SearchFilterArgs, useObjectsExplorerQuery } from '@sd/client';
|
||||
import { ArrowLeft, DotsThreeOutline, FunnelSimple, MagnifyingGlass } from 'phosphor-react-native';
|
||||
import { Suspense, useDeferredValue, useMemo, useState } from 'react';
|
||||
import { ActivityIndicator, Platform, Pressable, TextInput, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { SearchFilterArgs, useObjectsExplorerQuery } from '@sd/client';
|
||||
import Explorer from '~/components/explorer/Explorer';
|
||||
import FiltersBar from '~/components/search/filters/FiltersBar';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
@ -124,7 +124,7 @@ const SearchScreen = ({ navigation }: SearchStackScreenProps<'Search'>) => {
|
|||
{/* Content */}
|
||||
<View style={tw`flex-1`}>
|
||||
<Suspense fallback={<ActivityIndicator />}>
|
||||
<Explorer {...objects} tabHeight={false} />
|
||||
<Explorer hideHeader {...objects} tabHeight={false} />
|
||||
</Suspense>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { DebugState, useDebugState, useDebugStateEnabler } from '@sd/client';
|
||||
import {
|
||||
Books,
|
||||
FlyingSaucer,
|
||||
|
@ -12,9 +13,8 @@ import {
|
|||
ShieldCheck,
|
||||
TagSimple
|
||||
} from 'phosphor-react-native';
|
||||
import React from 'react';
|
||||
import { Platform, SectionList, Text, TouchableWithoutFeedback, View } from 'react-native';
|
||||
import { DebugState, useDebugState, useDebugStateEnabler } from '@sd/client';
|
||||
import { Platform, Text, TouchableWithoutFeedback, View } from 'react-native';
|
||||
import { useSharedValue } from 'react-native-reanimated';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { SettingsItem } from '~/components/settings/SettingsItem';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
@ -129,7 +129,7 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
|
|||
<Text
|
||||
style={twStyle(
|
||||
'mb-3 text-lg font-bold text-ink',
|
||||
section.title === 'Client' ? 'mt-2' : 'mt-5'
|
||||
section.title === 'Client' ? 'mt-0' : 'mt-5'
|
||||
)}
|
||||
>
|
||||
{section.title}
|
||||
|
@ -137,28 +137,34 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default function SettingsScreen({ navigation }: SettingsStackScreenProps<'Settings'>) {
|
||||
export default function SettingsScreen({
|
||||
navigation,
|
||||
}: SettingsStackScreenProps<'Settings'>) {
|
||||
const debugState = useDebugState();
|
||||
|
||||
const scrollY = useSharedValue(0);
|
||||
return (
|
||||
<ScreenContainer tabHeight={false} scrollview={false} style={tw`gap-0 px-6 py-0`}>
|
||||
<SectionList
|
||||
sections={sections(debugState)}
|
||||
contentContainerStyle={tw`h-auto pb-5 pt-3`}
|
||||
renderItem={({ item }) => (
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'Settings',
|
||||
showSearch: true,
|
||||
showDrawer: true,
|
||||
}}
|
||||
scrollY={scrollY} tabHeight={false} style={tw`gap-0 px-6`}>
|
||||
{sections(debugState).map((section, i) => (
|
||||
<View key={i}>
|
||||
{renderSectionHeader({ section })}
|
||||
{section.data.map((item, i) => (
|
||||
<SettingsItem
|
||||
key={i}
|
||||
title={item.title}
|
||||
leftIcon={item.icon}
|
||||
onPress={() => navigation.navigate(item.navigateTo as any)}
|
||||
rounded={item.rounded}
|
||||
/>
|
||||
)}
|
||||
renderSectionHeader={renderSectionHeader}
|
||||
ListFooterComponent={<FooterComponent />}
|
||||
showsVerticalScrollIndicator={false}
|
||||
stickySectionHeadersEnabled={false}
|
||||
initialNumToRender={50}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
<FooterComponent />
|
||||
</ScreenContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Themes, useThemeStore } from '@sd/client';
|
||||
import { CheckCircle } from 'phosphor-react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { ColorValue, Pressable, ScrollView, Text, View, ViewStyle } from 'react-native';
|
||||
import { Themes, useThemeStore } from '@sd/client';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { SettingsTitle } from '~/components/settings/SettingsContainer';
|
||||
import Colors from '~/constants/style/Colors';
|
||||
|
@ -126,7 +126,10 @@ const AppearanceSettingsScreen = ({
|
|||
// TODO: Hook this up to the theme store once light theme is fixed.
|
||||
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`gap-2 px-6`}>
|
||||
<ScreenContainer header={{
|
||||
navBack: true,
|
||||
title: 'Appearance'
|
||||
}} scrollview={false} style={tw`gap-2 px-6`}>
|
||||
<SettingsTitle>Theme</SettingsTitle>
|
||||
<ScrollView
|
||||
horizontal
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text } from 'react-native';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
||||
|
@ -8,7 +8,11 @@ const ExtensionsSettingsScreen = ({
|
|||
navigation
|
||||
}: SettingsStackScreenProps<'ExtensionsSettings'>) => {
|
||||
return (
|
||||
<ScreenContainer style={tw`px-6`}>
|
||||
<ScreenContainer header={{
|
||||
title: 'Extensions',
|
||||
navBack: true,
|
||||
}}
|
||||
style={tw`px-6`}>
|
||||
<Text style={tw`text-ink`}>TODO</Text>
|
||||
</ScreenContainer>
|
||||
);
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { Text, View } from 'react-native';
|
||||
import { useBridgeQuery, useDebugState } from '@sd/client';
|
||||
import { Text, View } from 'react-native';
|
||||
import Card from '~/components/layout/Card';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { Divider } from '~/components/primitive/Divider';
|
||||
import { Input } from '~/components/primitive/Input';
|
||||
import { SettingsTitle } from '~/components/settings/SettingsContainer';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
||||
|
||||
const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'GeneralSettings'>) => {
|
||||
const GeneralSettingsScreen = () => {
|
||||
const { data: node } = useBridgeQuery(['nodeState']);
|
||||
|
||||
const debugState = useDebugState();
|
||||
|
@ -16,7 +15,12 @@ const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'General
|
|||
if (!node) return null;
|
||||
|
||||
return (
|
||||
<ScreenContainer style={tw`justify-start gap-0 px-6`} scrollview={false}>
|
||||
<ScreenContainer
|
||||
header={{
|
||||
title: 'General',
|
||||
navBack: true,
|
||||
}}
|
||||
style={tw`justify-start gap-0 px-6`} scrollview={false}>
|
||||
<Card>
|
||||
{/* Card Header */}
|
||||
<View style={tw`flex flex-row justify-between`}>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { LibraryConfigWrapped, useBridgeQuery, useCache, useNodes } from '@sd/client';
|
||||
import { DotsThreeOutlineVertical, Pen, Trash } from 'phosphor-react-native';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Animated, FlatList, Pressable, Text, View } from 'react-native';
|
||||
import { Animated, Pressable, Text, View } from 'react-native';
|
||||
import { Swipeable } from 'react-native-gesture-handler';
|
||||
import { LibraryConfigWrapped, useBridgeQuery, useCache, useNodes } from '@sd/client';
|
||||
import { default as Reanimated } from 'react-native-reanimated';
|
||||
import Fade from '~/components/layout/Fade';
|
||||
import { ModalRef } from '~/components/layout/Modal';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
|
@ -78,7 +79,9 @@ function LibraryItem({
|
|||
);
|
||||
}
|
||||
|
||||
const LibrarySettingsScreen = ({ navigation }: SettingsStackScreenProps<'LibrarySettings'>) => {
|
||||
const LibrarySettingsScreen = ({
|
||||
navigation,
|
||||
}: SettingsStackScreenProps<'LibrarySettings'>) => {
|
||||
const libraryList = useBridgeQuery(['library.list']);
|
||||
useNodes(libraryList.data?.nodes);
|
||||
const libraries = useCache(libraryList.data?.items);
|
||||
|
@ -101,7 +104,10 @@ const LibrarySettingsScreen = ({ navigation }: SettingsStackScreenProps<'Library
|
|||
const modalRef = useRef<ModalRef>(null);
|
||||
|
||||
return (
|
||||
<ScreenContainer style={tw`justify-start gap-0 px-6 py-0`} scrollview={false}>
|
||||
<ScreenContainer header={{
|
||||
navBack: true,
|
||||
title: 'Libraries',
|
||||
}} scrollview={false} style={tw`justify-start gap-0 px-6 py-0`}>
|
||||
<Fade
|
||||
fadeSides="top-bottom"
|
||||
orientation="vertical"
|
||||
|
@ -109,7 +115,7 @@ const LibrarySettingsScreen = ({ navigation }: SettingsStackScreenProps<'Library
|
|||
width={30}
|
||||
height="100%"
|
||||
>
|
||||
<FlatList
|
||||
<Reanimated.FlatList
|
||||
data={libraries}
|
||||
contentContainerStyle={tw`py-5`}
|
||||
keyExtractor={(item) => item.uuid}
|
||||
|
|
|
@ -5,7 +5,10 @@ import { tw } from '~/lib/tailwind';
|
|||
|
||||
const PrivacySettingsScreen = () => {
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`px-6`}>
|
||||
<ScreenContainer header={{
|
||||
title: 'Privacy',
|
||||
navBack: true,
|
||||
}} scrollview={false} style={tw`px-6`}>
|
||||
<Text style={tw`text-ink`}>TODO</Text>
|
||||
</ScreenContainer>
|
||||
);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useBridgeQuery } from '@sd/client';
|
||||
import { Image } from 'expo-image';
|
||||
import { Globe } from 'phosphor-react-native';
|
||||
import React from 'react';
|
||||
import { Linking, Platform, Text, View } from 'react-native';
|
||||
import { useBridgeQuery } from '@sd/client';
|
||||
import { DiscordIcon, GitHubIcon } from '~/components/icons/Brands';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
|
@ -11,14 +11,16 @@ import { tw } from '~/lib/tailwind';
|
|||
|
||||
const AboutScreen = () => {
|
||||
const buildInfo = useBridgeQuery(['buildInfo']);
|
||||
|
||||
return (
|
||||
<ScreenContainer style={tw`justify-start gap-0 px-6`}>
|
||||
<ScreenContainer header={{
|
||||
title: 'About',
|
||||
navBack: true,
|
||||
}} style={tw`justify-start gap-0 px-6`}>
|
||||
<View style={tw`flex flex-row items-center`}>
|
||||
<Image
|
||||
source={require('../../../../assets/icon.png')}
|
||||
style={tw`mr-8 h-[88px] w-[88px] rounded-3xl`}
|
||||
resizeMode="contain"
|
||||
contentFit="contain"
|
||||
/>
|
||||
<View style={tw`flex flex-col`}>
|
||||
<Text style={tw`text-2xl font-bold text-white`}>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useDebugState, useFeatureFlags } from '@sd/client';
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { toggleFeatureFlag, useDebugState, useFeatureFlags } from '@sd/client';
|
||||
import { Text } from 'react-native';
|
||||
import Card from '~/components/layout/Card';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
||||
|
@ -11,15 +12,16 @@ const DebugScreen = ({ navigation }: SettingsStackScreenProps<'Debug'>) => {
|
|||
const featureFlags = useFeatureFlags();
|
||||
|
||||
return (
|
||||
<View style={tw`flex-1 p-4`}>
|
||||
<Card style={tw`gap-y-4 bg-app-box`}>
|
||||
<ScreenContainer style={tw`px-6`} header={{ title: 'Debug', navBack: true }}>
|
||||
<Card style={tw`gap-y-4`}>
|
||||
<Text style={tw`font-semibold text-ink`}>Debug</Text>
|
||||
<Button onPress={() => (debugState.rspcLogger = !debugState.rspcLogger)}>
|
||||
<Button variant="darkgray" onPress={() => (debugState.rspcLogger = !debugState.rspcLogger)}>
|
||||
<Text style={tw`text-ink`}>Toggle rspc logger</Text>
|
||||
</Button>
|
||||
<Text style={tw`text-ink`}>{JSON.stringify(featureFlags)}</Text>
|
||||
<Text style={tw`text-ink`}>{JSON.stringify(debugState)}</Text>
|
||||
<Button
|
||||
variant="darkgray"
|
||||
onPress={() => {
|
||||
navigation.popToTop();
|
||||
navigation.replace('Settings');
|
||||
|
@ -29,7 +31,7 @@ const DebugScreen = ({ navigation }: SettingsStackScreenProps<'Debug'>) => {
|
|||
<Text style={tw`text-ink`}>Disable Debug Mode</Text>
|
||||
</Button>
|
||||
</Card>
|
||||
</View>
|
||||
</ScreenContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text } from 'react-native';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
|
||||
const SupportScreen = () => {
|
||||
return (
|
||||
<View>
|
||||
<ScreenContainer
|
||||
style={tw`justify-start px-6 py-5`}
|
||||
header={{
|
||||
title: 'Support',
|
||||
navBack: true,
|
||||
}}>
|
||||
<Text style={tw`text-ink`}>TODO</Text>
|
||||
</View>
|
||||
</ScreenContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useLibraryMutation, useLibraryQuery, useNormalisedCache, useZodForm } from '@sd/client';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { Archive, ArrowsClockwise, Trash } from 'phosphor-react-native';
|
||||
import { useEffect } from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Alert, Text, View } from 'react-native';
|
||||
import { z } from 'zod';
|
||||
import { useLibraryMutation, useLibraryQuery, useNormalisedCache, useZodForm } from '@sd/client';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { AnimatedButton } from '~/components/primitive/Button';
|
||||
import { Divider } from '~/components/primitive/Divider';
|
||||
|
@ -28,10 +28,9 @@ const schema = z.object({
|
|||
|
||||
const EditLocationSettingsScreen = ({
|
||||
route,
|
||||
navigation
|
||||
navigation,
|
||||
}: SettingsStackScreenProps<'EditLocationSettings'>) => {
|
||||
const { id } = route.params;
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const cache = useNormalisedCache();
|
||||
|
||||
|
@ -111,7 +110,10 @@ const EditLocationSettingsScreen = ({
|
|||
const fullRescan = useLibraryMutation('locations.fullRescan');
|
||||
|
||||
return (
|
||||
<ScreenContainer style={tw`px-6`}>
|
||||
<ScreenContainer header={{
|
||||
title: 'Edit Location',
|
||||
navBack: true,
|
||||
}} scrollview style={tw`px-6`}>
|
||||
{/* Inputs */}
|
||||
<View>
|
||||
<SettingsTitle style={tw`mb-1`}>Display Name</SettingsTitle>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useBridgeMutation, useLibraryContext, useZodForm } from '@sd/client';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Text, View } from 'react-native';
|
||||
import { TouchableOpacity } from 'react-native-gesture-handler';
|
||||
import { z } from 'zod';
|
||||
import { useBridgeMutation, useLibraryContext, useZodForm } from '@sd/client';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import DeleteLibraryModal from '~/components/modal/confirmModals/DeleteLibraryModal';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
|
@ -14,11 +14,10 @@ import { SettingsTitle } from '~/components/settings/SettingsContainer';
|
|||
import SettingsToggle from '~/components/settings/SettingsToggle';
|
||||
import { useAutoForm } from '~/hooks/useAutoForm';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
||||
|
||||
const schema = z.object({ name: z.string(), description: z.string() });
|
||||
|
||||
const LibraryGeneralSettingsScreen = (_: SettingsStackScreenProps<'LibraryGeneralSettings'>) => {
|
||||
const LibraryGeneralSettingsScreen = () => {
|
||||
const { library } = useLibraryContext();
|
||||
|
||||
const form = useZodForm({
|
||||
|
@ -38,7 +37,10 @@ const LibraryGeneralSettingsScreen = (_: SettingsStackScreenProps<'LibraryGenera
|
|||
});
|
||||
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`justify-start px-6 py-0`}>
|
||||
<ScreenContainer header={{
|
||||
title: 'Library Settings',
|
||||
navBack: true,
|
||||
}} style={tw`justify-start px-6 py-0`}>
|
||||
<View style={tw`pt-5`}>
|
||||
<SettingsTitle style={tw`mb-1`}>Name</SettingsTitle>
|
||||
<Controller
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useDiscoveredPeers } from '@sd/client';
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { useDiscoveredPeers } from '@sd/client';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { SettingsStackScreenProps } from '~/navigation/tabs/SettingsStack';
|
||||
|
@ -9,7 +9,10 @@ const NodesSettingsScreen = ({ navigation }: SettingsStackScreenProps<'NodesSett
|
|||
const onlineNodes = useDiscoveredPeers();
|
||||
|
||||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`gap-0 px-6`}>
|
||||
<ScreenContainer header={{
|
||||
title: 'Nodes',
|
||||
navBack: true,
|
||||
}} scrollview={false} style={tw`gap-0 px-6`}>
|
||||
<Text style={tw`text-ink`}>Pairing</Text>
|
||||
|
||||
{[...onlineNodes.entries()].map(([id, node]) => (
|
||||
|
|
Loading…
Reference in a new issue