[MOB-89] Separate headers (#2408)

* separate headers

improvements to headers

cleanup

missed cleanup

documentation

* Update SearchStack.tsx
This commit is contained in:
ameer2468 2024-04-27 18:08:07 +03:00 committed by GitHub
parent a61a7bee65
commit 9126332df1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 229 additions and 152 deletions

View file

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

View file

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

View file

@ -0,0 +1,110 @@
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
import { RouteProp, useNavigation } from '@react-navigation/native';
import { NativeStackHeaderProps } from '@react-navigation/native-stack';
import { ArrowLeft, DotsThreeOutline, MagnifyingGlass } from 'phosphor-react-native';
import { Platform, Pressable, Text, View } from 'react-native';
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';
type Props = {
headerRoute?: NativeStackHeaderProps; //supporting title from the options object of navigation
optionsRoute?: RouteProp<any, any>; //supporting params passed
kind: 'tag' | 'location'; //the kind of icon to display
explorerMenu?: boolean; //whether to show the explorer menu
};
export default function DynamicHeader({
headerRoute,
optionsRoute,
kind,
explorerMenu = true
}: Props) {
const navigation = useNavigation<DrawerNavigationHelpers>();
const headerHeight = useSafeAreaInsets().top;
const isAndroid = Platform.OS === 'android';
const explorerStore = useExplorerStore();
return (
<View
style={twStyle('relative h-auto w-full border-b border-app-cardborder bg-app-header', {
paddingTop: headerHeight + (isAndroid ? 15 : 0)
})}
>
<View style={tw`mx-auto h-auto w-full justify-center px-5 pb-3`}>
<View style={tw`w-full flex-row items-center justify-between`}>
<View style={tw`flex-row items-center gap-3`}>
<Pressable
hitSlop={24}
onPress={() => navigation.goBack()}
>
<ArrowLeft size={23} color={tw.color('ink')} />
</Pressable>
<View style={tw`flex-row items-center gap-1.5`}>
<HeaderIconKind routeParams={optionsRoute?.params} kind={kind} />
<Text
numberOfLines={1}
style={tw`max-w-[200px] text-xl font-bold text-white`}
>
{headerRoute?.options.title}
</Text>
</View>
</View>
<View style={tw`flex-row gap-3`}>
{explorerMenu && <Pressable
hitSlop={12}
onPress={() => {
getExplorerStore().toggleMenu = !explorerStore.toggleMenu;
}}
>
<DotsThreeOutline
size={24}
color={tw.color(
explorerStore.toggleMenu ? 'text-accent' : 'text-zinc-300'
)}
/>
</Pressable>}
<Pressable
hitSlop={12}
onPress={() => {
navigation.navigate('SearchStack', {
screen: 'Search'
});
}}
>
<MagnifyingGlass
size={24}
weight="bold"
color={tw.color('text-zinc-300')}
/>
</Pressable>
</View>
</View>
</View>
</View>
);
}
interface HeaderIconKindProps {
routeParams?: any;
kind: Props['kind'];
}
const HeaderIconKind = ({routeParams, kind }: HeaderIconKindProps) => {
switch (kind) {
case 'location':
return <Icon size={30} name="Folder" />;
case 'tag':
return (
<View
style={twStyle('h-[24px] w-[24px] rounded-full', {
backgroundColor: routeParams.color
})}
/>
);
default:
return null;
}
};

View file

@ -1,48 +1,25 @@
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
import { useNavigation } from '@react-navigation/native';
import { NativeStackHeaderProps } from '@react-navigation/native-stack';
import { ArrowLeft, DotsThreeOutline, List, MagnifyingGlass } from 'phosphor-react-native';
import { RouteProp, useNavigation } from '@react-navigation/native';
import { ArrowLeft, List, MagnifyingGlass } from 'phosphor-react-native';
import { Platform, Pressable, Text, View } from 'react-native';
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 Search from '../search/Search';
type HeaderProps = {
title?: string; //title of the page
showSearch?: boolean; //show the search button
showDrawer?: boolean; //show the drawer button
searchType?: 'explorer' | 'location' | 'categories' | 'tags'; //Temporary
navBack?: boolean; //navigate back to the previous screen
headerKind?: 'default' | 'location' | 'tag'; //kind of header
route?: never;
routeTitle?: never;
type Props = {
route?: RouteProp<any, any>; // supporting title from the options object of navigation
navBack?: boolean; // whether to show the back icon
search?: boolean; // whether to show the search icon
title?: string; // in some cases - we want to override the route title
};
//you can pass in a routeTitle only if route is passed in
type Props =
| HeaderProps
| ({
route: NativeStackHeaderProps;
routeTitle?: boolean;
} & Omit<HeaderProps, 'route' | 'routeTitle'>);
// Default header with search bar and button to open drawer
export default function Header({
title,
searchType,
navBack,
route,
routeTitle,
headerKind = 'default',
showDrawer = false,
showSearch = false,
navBack,
title,
search = false
}: Props) {
const navigation = useNavigation<DrawerNavigationHelpers>();
const explorerStore = useExplorerStore();
const routeParams = route?.route.params as any;
const headerHeight = useSafeAreaInsets().top;
const isAndroid = Platform.OS === 'android';
@ -55,35 +32,22 @@ export default function Header({
<View style={tw`mx-auto h-auto w-full justify-center px-5 pb-3`}>
<View style={tw`w-full flex-row items-center justify-between`}>
<View style={tw`flex-row items-center gap-3`}>
{navBack && (
{navBack ? (
<Pressable
hitSlop={24}
onPress={() => {
navigation.goBack();
}}
>
<ArrowLeft size={23} color={tw.color('ink')} />
</Pressable>
)}
<View style={tw`flex-row items-center gap-2`}>
<HeaderIconKind headerKind={headerKind} routeParams={routeParams} />
{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`}
>
{title || (routeTitle && route?.options.title)}
</Text>
</View>
hitSlop={24}
onPress={() => navigation.goBack()}
>
<ArrowLeft size={24} color={tw.color('ink')} />
</Pressable>
) : (
<Pressable onPress={() => navigation.openDrawer()}>
<List size={24} color={tw.color('ink')} />
</Pressable>
)}
<Text style={tw`text-xl font-bold text-ink`}>{title || route?.name}</Text>
</View>
<View style={tw`relative flex-row items-center gap-3`}>
{showSearch && (
<View style={tw`flex-row items-center gap-2`}>
<Pressable
{search && <Pressable
hitSlop={24}
onPress={() => {
navigation.navigate('SearchStack', {
@ -96,69 +60,9 @@ export default function Header({
weight="bold"
color={tw.color('text-zinc-300')}
/>
</Pressable>
</View>
)}
{(headerKind === 'location' || headerKind === 'tag') && (
<Pressable
hitSlop={24}
onPress={() => {
getExplorerStore().toggleMenu = !explorerStore.toggleMenu;
}}
>
<DotsThreeOutline
size={24}
color={tw.color(
explorerStore.toggleMenu ? 'text-accent' : 'text-zinc-300'
)}
/>
</Pressable>
)}
</View>
</Pressable>}
</View>
{searchType && <HeaderSearchType searchType={searchType} />}
</View>
</View>
);
}
interface HeaderSearchTypeProps {
searchType: HeaderProps['searchType'];
}
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:
return null;
}
};
interface HeaderIconKindProps {
headerKind: HeaderProps['headerKind'];
routeParams?: any;
}
const HeaderIconKind = ({ headerKind, routeParams }: HeaderIconKindProps) => {
switch (headerKind) {
case 'location':
return <Icon size={30} name="Folder" />;
case 'tag':
return (
<View
style={twStyle('h-[24px] w-[24px] rounded-full', {
backgroundColor: routeParams.color
})}
/>
);
default:
return null;
}
};

View file

@ -0,0 +1,53 @@
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types';
import { RouteProp, useNavigation } from '@react-navigation/native';
import { ArrowLeft } from 'phosphor-react-native';
import { Platform, Pressable, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { tw, twStyle } from '~/lib/tailwind';
import Search from '../search/Search';
const searchPlaceholder = {
locations: 'Search location name...',
tags: 'Search tag name...',
categories: 'Search category name...',
}
type Props = {
route?: RouteProp<any, any>; // supporting title from the options object of navigation
kind: keyof typeof searchPlaceholder; // the kind of search we are doing
title?: string; // in some cases - we want to override the route title
};
export default function SearchHeader({
route,
kind,
title
}: Props) {
const navigation = useNavigation<DrawerNavigationHelpers>();
const headerHeight = useSafeAreaInsets().top;
const isAndroid = Platform.OS === 'android';
return (
<View
style={twStyle('relative h-auto w-full border-b border-app-cardborder bg-app-header', {
paddingTop: headerHeight + (isAndroid ? 15 : 0)
})}
>
<View style={tw`mx-auto h-auto w-full justify-center px-5 pb-3`}>
<View style={tw`w-full flex-row items-center justify-between`}>
<View style={tw`flex-row items-center gap-3`}>
<Pressable
hitSlop={24}
onPress={() => navigation.goBack()}
>
<ArrowLeft size={24} color={tw.color('ink')} />
</Pressable>
<Text style={tw`text-xl font-bold text-ink`}>{title || route?.name}</Text>
</View>
</View>
<Search placeholder={searchPlaceholder[kind]} />
</View>
</View>
);
}

View file

@ -21,7 +21,7 @@ export default function SearchStack() {
component={FiltersScreen}
options={{
header: () => {
return <Header navBack showSearch={false} title="Search filters" />;
return <Header navBack title="Search filters" />;
}
}}
/>

View file

@ -8,6 +8,8 @@ import LocationsScreen from '~/screens/browse/Locations';
import TagScreen from '~/screens/browse/Tag';
import TagsScreen from '~/screens/browse/Tags';
import DynamicHeader from '~/components/header/DynamicHeader';
import SearchHeader from '~/components/header/SearchHeader';
import { TabScreenProps } from '../TabNavigator';
const Stack = createNativeStackNavigator<BrowseStackParamList>();
@ -18,44 +20,44 @@ export default function BrowseStack() {
<Stack.Screen
name="Browse"
component={BrowseScreen}
options={{ header: () => <Header showSearch showDrawer title="Browse" /> }}
options={({route}) => ({
header: () => <Header search route={route} />
})}
/>
<Stack.Screen
name="Location"
component={LocationScreen}
options={{
header: (route) => (
<Header route={route} showSearch headerKind="location" routeTitle navBack />
)
}}
options={({route: optionsRoute}) => ({
header: (route) => <DynamicHeader optionsRoute={optionsRoute} headerRoute={route} kind="location" />
})}
/>
<Stack.Screen
name="Tags"
component={TagsScreen}
options={{
header: () => <Header searchType='tags' navBack title="Tags" />
}}
options={({route}) => ({
header: () => <SearchHeader kind="tags" route={route} />
})}
/>
<Stack.Screen
name="Locations"
component={LocationsScreen}
options={{
header: () => <Header navBack searchType="location" title="Locations" />
}}
options={({route}) => ({
header: () => <SearchHeader kind="locations" route={route} />
})}
/>
<Stack.Screen
name="Tag"
component={TagScreen}
options={{
header: (route) => <Header showSearch navBack routeTitle route={route} headerKind="tag" />
}}
options={({route: optionsRoute}) => ({
header: (route) => <DynamicHeader optionsRoute={optionsRoute} headerRoute={route} kind="tag" />
})}
/>
<Stack.Screen
name="Library"
component={LibraryScreen}
options={{
header: () => <Header navBack title="Library" />
}}
options={({route}) => ({
header: () => <Header navBack route={route} />
})}
/>
</Stack.Navigator>
);

View file

@ -13,7 +13,9 @@ export default function NetworkStack() {
<Stack.Screen
name="Network"
component={NetworkScreen}
options={{ header: () => <Header showSearch showDrawer title="Network" /> }}
options={({route}) => ({
header: () => <Header search route={route} />
})}
/>
</Stack.Navigator>
);

View file

@ -1,9 +1,10 @@
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 Header from '~/components/header/Header';
import SearchHeader from '~/components/header/SearchHeader';
import { TabScreenProps } from '../TabNavigator';
const Stack = createNativeStackNavigator<OverviewStackParamList>();
@ -14,14 +15,16 @@ export default function OverviewStack() {
<Stack.Screen
name="Overview"
component={OverviewScreen}
options={{ header: () => <Header showSearch showDrawer title="Overview" /> }}
options={({route}) => ({
header: () => <Header search route={route} />
})}
/>
<Stack.Screen
name="Categories"
component={CategoriesScreen}
options={{
header: () => <Header searchType="categories" navBack title="Categories" />
}}
options={({route}) => ({
header: () => <SearchHeader kind="categories" route={route} />
})}
/>
</Stack.Navigator>
);

View file

@ -18,6 +18,7 @@ import NodesSettingsScreen from '~/screens/settings/library/NodesSettings';
import TagsSettingsScreen from '~/screens/settings/library/TagsSettings';
import SettingsScreen from '~/screens/settings/Settings';
import SearchHeader from '~/components/header/SearchHeader';
import { TabScreenProps } from '../TabNavigator';
const Stack = createNativeStackNavigator<SettingsStackParamList>();
@ -28,7 +29,9 @@ export default function SettingsStack() {
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{ header: () => <Header showSearch showDrawer title="Settings" /> }}
options={({route}) => ({
header: () => <Header search route={route} />
})}
/>
{/* Client */}
<Stack.Screen
@ -65,9 +68,9 @@ export default function SettingsStack() {
<Stack.Screen
name="LocationSettings"
component={LocationSettingsScreen}
options={{
header: () => <Header searchType="location" navBack title="Locations" />
}}
options={() => ({
header: () => <SearchHeader title="Locations" kind="locations" />
})}
/>
<Stack.Screen
name="EditLocationSettings"