Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MOB-58] Settings routes new design & more #2103

Merged
merged 4 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 114 additions & 34 deletions apps/mobile/src/components/browse/BrowseTags.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,135 @@
import { useNavigation } from '@react-navigation/native';
import { DotsThreeOutlineVertical, Eye, Plus } from 'phosphor-react-native';
import React, { useRef } from 'react';
import { Pressable, Text, View } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import { Tag, useCache, useLibraryQuery, useNodes } from '@sd/client';
import { DotsThreeOutlineVertical, Eye, Pen, Plus, Trash } from 'phosphor-react-native';
import React, { useRef } from 'react';
import { Animated, Pressable, Text, View } from 'react-native';
import { FlatList, Swipeable } from 'react-native-gesture-handler';
import { ClassInput } from 'twrnc/dist/esm/types';
import { ModalRef } from '~/components/layout/Modal';
import { tw, twStyle } from '~/lib/tailwind';
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';

import { Icon } from '../icons/Icon';
import Fade from '../layout/Fade';
import DeleteTagModal from '../modal/confirmModals/DeleteTagModal';
import CreateTagModal from '../modal/tag/CreateTagModal';
import { TagModal } from '../modal/tag/TagModal';
import UpdateTagModal from '../modal/tag/UpdateTagModal';
import { AnimatedButton, FakeButton } from '../primitive/Button';

type BrowseTagItemProps = {
type TagItemProps = {
tag: Tag;
onPress: () => void;
tagStyle?: string;
tagStyle?: ClassInput;
viewStyle?: 'grid' | 'list';
rightActions?: () => void;
};

export const BrowseTagItem: React.FC<BrowseTagItemProps> = ({ tag, onPress, tagStyle }) => {
export const TagItem = ({
tag,
onPress,
rightActions,
tagStyle,
viewStyle = 'grid'
}: TagItemProps) => {
const modalRef = useRef<ModalRef>(null);

const renderTagView = () => (
<View
style={twStyle(
`h-auto flex-col justify-center gap-2.5 rounded-md border border-app-line/50 bg-app-box/50 p-2`,
viewStyle === 'grid' ? 'w-[90px]' : 'w-full',
tagStyle
)}
>
<View style={tw`flex-row items-center justify-between`}>
<View
style={twStyle('h-[28px] w-[28px] rounded-full', {
backgroundColor: tag.color!
})}
/>
<Pressable onPress={() => modalRef.current?.present()}>
<DotsThreeOutlineVertical
weight="fill"
size={20}
color={tw.color('ink-faint')}
/>
</Pressable>
</View>
<Text style={tw`w-full max-w-[75px] text-xs font-bold text-white`} numberOfLines={1}>
{tag.name}
</Text>
</View>
);

const renderRightActions = (
progress: Animated.AnimatedInterpolation<number>,
_dragX: Animated.AnimatedInterpolation<number>,
swipeable: Swipeable
) => {
const translate = progress.interpolate({
inputRange: [0, 1],
outputRange: [100, 0],
extrapolate: 'clamp'
});

return (
<Animated.View
style={[
tw`ml-5 flex flex-row items-center`,
{ transform: [{ translateX: translate }] }
]}
>
<UpdateTagModal tag={tag} ref={modalRef} onSubmit={() => swipeable.close()} />
<AnimatedButton onPress={() => modalRef.current?.present()}>
<Pen size={18} color="white" />
</AnimatedButton>
<DeleteTagModal
tagId={tag.id}
trigger={
<FakeButton style={tw`mx-2`}>
<Trash size={18} color="white" />
</FakeButton>
}
/>
</Animated.View>
);
};

return (
<Pressable onPress={onPress} testID="browse-tag">
<View
style={twStyle(
'h-auto w-[90px] flex-col justify-center gap-2.5 rounded-md border border-app-line/50 bg-app-box/50 p-2',
tagStyle
)}
>
<View style={tw`flex-row items-center justify-between`}>
<View
style={twStyle('h-[28px] w-[28px] rounded-full', {
backgroundColor: tag.color!
})}
/>
<Pressable onPress={() => modalRef.current?.present()}>
<DotsThreeOutlineVertical
weight="fill"
size={20}
color={tw.color('ink-faint')}
/>
</Pressable>
</View>
<Text
style={tw`w-full max-w-[75px] text-xs font-bold text-white`}
numberOfLines={1}
{viewStyle === 'grid' ? (
renderTagView()
) : (
<Swipeable
containerStyle={tw`rounded-md border border-app-line/50 bg-app-box/50 p-3`}
enableTrackpadTwoFingerGesture
renderRightActions={renderRightActions}
>
{tag.name}
</Text>
</View>
<View style={twStyle('h-auto flex-row items-center justify-between', tagStyle)}>
<View style={tw`flex-1 flex-row items-center gap-2`}>
<View
style={twStyle('h-[28px] w-[28px] rounded-full', {
backgroundColor: tag.color!
})}
/>
<Text
style={tw`w-full max-w-[75px] text-xs font-bold text-white`}
numberOfLines={1}
>
{tag.name}
</Text>
</View>
<Pressable onPress={() => modalRef.current?.present()}>
<DotsThreeOutlineVertical
weight="fill"
size={20}
color={tw.color('ink-faint')}
/>
</Pressable>
</View>
</Swipeable>
)}
<TagModal ref={modalRef} tag={tag} />
</Pressable>
);
Expand Down Expand Up @@ -102,7 +182,7 @@ const BrowseTags = () => {
</View>
)}
renderItem={({ item }) => (
<BrowseTagItem
<TagItem
tag={item}
onPress={() =>
navigation.navigate('Tag', { id: item.id, color: item.color! })
Expand Down
68 changes: 39 additions & 29 deletions apps/mobile/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,6 @@ export default function Header({
const explorerStore = useExplorerStore();
const routeParams = route?.route.params as any;

const SearchType = () => {
switch (searchType) {
case 'explorer':
return 'Explorer'; //TODO
case 'location':
return <Search placeholder="Location name..." />;
default:
return null;
}
};
const HeaderIconKind = () => {
switch (headerKind) {
case 'location':
return <Icon size={32} name="Folder" />;
case 'tag':
return (
<View
style={twStyle('h-6 w-6 rounded-full', {
backgroundColor: routeParams.color
})}
/>
);
default:
return null;
}
};

return (
<View
style={twStyle(
Expand All @@ -91,7 +64,7 @@ export default function Header({
</Pressable>
)}
<View style={tw`flex-row items-center gap-2`}>
<HeaderIconKind />
<HeaderIconKind headerKind={headerKind} routeParams={routeParams} />
<Text
numberOfLines={1}
style={tw`max-w-[190px] text-[22px] font-bold text-white`}
Expand Down Expand Up @@ -126,8 +99,45 @@ export default function Header({
</View>

{showLibrary && <BrowseLibraryManager style="mt-4" />}
{searchType && <SearchType />}
{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..." />;
default:
return null;
}
};

interface HeaderIconKindProps {
headerKind: HeaderProps['headerKind'];
routeParams?: any;
}

const HeaderIconKind = ({ headerKind, routeParams }: HeaderIconKindProps) => {
switch (headerKind) {
case 'location':
return <Icon size={32} name="Folder" />;
case 'tag':
return (
<View
style={twStyle('h-6 w-6 rounded-full', {
backgroundColor: routeParams.color
})}
/>
);
default:
return null;
}
};
6 changes: 3 additions & 3 deletions apps/mobile/src/components/overview/Categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface Props {
const Categories = ({ kinds }: Props) => {
return (
<View>
<Text style={tw`pb-5 text-lg font-bold text-white px-7`}>Categories</Text>
<Text style={tw`px-7 pb-5 text-lg font-bold text-white`}>Categories</Text>
<View>
<Fade color="mobile-screen" width={30} height="100%">
<VirtualizedListWrapper horizontal>
Expand All @@ -29,7 +29,7 @@ const Categories = ({ kinds }: Props) => {
key={kinds.data?.statistics ? 'kinds' : '_'} //needed to update numColumns when data is available
keyExtractor={(item) => item.name}
scrollEnabled={false}
ItemSeparatorComponent={() => <View style={tw`w-3 h-3`} />}
ItemSeparatorComponent={() => <View style={tw`h-3 w-3`} />}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => {
const { kind, name, count } = item;
Expand Down Expand Up @@ -79,7 +79,7 @@ const KindItem = ({ name, icon, items }: KindItemProps) => {
}}
>
<View style={twStyle('shrink-0 flex-row items-center', 'gap-2 rounded-lg text-sm')}>
<Icon name={icon} size={40} style={tw`w-12 h-12 mr-3`} />
<Icon name={icon} size={40} style={tw`mr-3 h-12 w-12`} />
<View>
<Text style={tw`text-sm font-medium text-ink`}>{name}</Text>
{items !== undefined && (
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/overview/OverviewStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const StatItem = ({ title, bytes, isLoading, style }: StatItemProps) => {
)}
>
<Text style={tw`text-sm font-bold text-gray-400`}>{title}</Text>
<View style={tw`flex-row items-baseline mt-1`}>
<View style={tw`mt-1 flex-row items-baseline`}>
<Text style={twStyle('text-xl font-bold tabular-nums text-white')}>{count}</Text>
<Text style={tw`ml-1 text-sm text-gray-400`}>{unit}</Text>
</View>
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/overview/StatCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const StatCard = ({ icon, name, connectionType, ...stats }: StatCardProps) => {
<View
style={tw`absolute flex-row items-end gap-0.5 text-lg font-semibold`}
>
<Text style={tw`mx-auto font-semibold text-md text-ink`}>
<Text style={tw`mx-auto text-md font-semibold text-ink`}>
{fill.toFixed(0)}
</Text>
<Text style={tw`text-xs font-bold text-ink-dull opacity-60`}>
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/src/components/primitive/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const button = cva(['items-center justify-center rounded-md border shadow-sm'],
});

type ButtonProps = VariantProps<typeof button> & PressableProps;
export type ButtonVariants = ButtonProps['variant'];

export const Button: FC<ButtonProps> = ({ variant, size, disabled, ...props }) => {
const { style, ...otherProps } = props;
Expand Down
46 changes: 46 additions & 0 deletions apps/mobile/src/components/settings/SettingsButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Text, View } from 'react-native';
import { ClassInput } from 'twrnc/dist/esm/types';
import { tw, twStyle } from '~/lib/tailwind';

import { Button, ButtonVariants } from '../primitive/Button';

interface Props {
buttonText: string;
title: string;
description?: string;
buttonPress?: () => void;
buttonVariant?: ButtonVariants;
buttonTextStyle?: string;
buttonIcon?: JSX.Element;
infoContainerStyle?: ClassInput;
}

const SettingsButton = ({
buttonText,
title,
description,
buttonVariant,
buttonTextStyle,
buttonIcon,
infoContainerStyle,
buttonPress
}: Props) => {
return (
<View style={tw`flex-row items-center justify-between`}>
<View style={twStyle('w-73%', infoContainerStyle)}>
<Text style={tw`text-sm font-medium text-ink`}>{title}</Text>
{description && <Text style={tw`mt-1 text-xs text-ink-dull`}>{description}</Text>}
</View>
<Button
style={tw`flex-row items-center gap-2`}
variant={buttonVariant}
onPress={buttonPress}
>
{buttonIcon}
<Text style={twStyle(buttonTextStyle)}>{buttonText}</Text>
</Button>
</View>
);
};

export default SettingsButton;
8 changes: 3 additions & 5 deletions apps/mobile/src/components/settings/SettingsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ type SettingsContainerProps = PropsWithChildren<{
export function SettingsContainer({ children, title, description }: SettingsContainerProps) {
return (
<View>
{title && (
<Text style={tw`pb-2 pl-3 text-sm font-semibold text-ink-dull`}>{title}</Text>
)}
{title && <Text style={tw`pb-2 text-sm font-semibold text-ink-dull`}>{title}</Text>}
{children}
{description && <Text style={tw`px-3 pt-2 text-sm text-ink-dull`}>{description}</Text>}
{description && <Text style={tw`pt-2 text-sm text-ink-dull`}>{description}</Text>}
</View>
);
}

export const SettingsTitle = styled(Text, 'text-ink mb-1.5 ml-1 text-sm font-medium');
export const SettingsTitle = styled(Text, 'text-ink text-sm font-medium');
export const SettingsInputInfo = styled(Text, 'mt-2 text-xs text-ink-faint');
Loading
Loading