mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-19 05:59:16 -04:00
[MOB-53] All tags screen - visual improvements - and more (#2074)
all tags screen, visual improvements, and bug fix
This commit is contained in:
@@ -16,14 +16,18 @@ import { TagModal } from '../modal/tag/TagModal';
|
||||
type BrowseTagItemProps = {
|
||||
tag: Tag;
|
||||
onPress: () => void;
|
||||
tagStyle?: string;
|
||||
};
|
||||
|
||||
const BrowseTagItem: React.FC<BrowseTagItemProps> = ({ tag, onPress }) => {
|
||||
export const BrowseTagItem: React.FC<BrowseTagItemProps> = ({ tag, onPress, tagStyle }) => {
|
||||
const modalRef = useRef<ModalRef>(null);
|
||||
return (
|
||||
<Pressable onPress={onPress} testID="browse-tag">
|
||||
<View
|
||||
style={tw`h-auto w-[90px] flex-col justify-center gap-2.5 rounded-md border border-sidebar-line/50 bg-sidebar-box p-2`}
|
||||
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
|
||||
@@ -63,10 +67,10 @@ const BrowseTags = () => {
|
||||
|
||||
return (
|
||||
<View style={tw`gap-5`}>
|
||||
<View style={tw`w-full flex-row items-center justify-between px-7`}>
|
||||
<View style={tw`flex-row items-center justify-between w-full px-7`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>Tags</Text>
|
||||
<View style={tw`flex-row gap-3`}>
|
||||
<Pressable>
|
||||
<Pressable onPress={() => navigation.navigate('Tags')}>
|
||||
<View
|
||||
style={tw`h-8 w-8 items-center justify-center rounded-md bg-accent ${
|
||||
tags.data?.nodes.length === 0 ? 'opacity-40' : 'opacity-100'
|
||||
@@ -77,7 +81,7 @@ const BrowseTags = () => {
|
||||
</Pressable>
|
||||
<Pressable testID="add-tag" onPress={() => modalRef.current?.present()}>
|
||||
<View
|
||||
style={tw`h-8 w-8 items-center justify-center rounded-md border border-dashed border-ink-faint bg-transparent`}
|
||||
style={tw`items-center justify-center w-8 h-8 bg-transparent border border-dashed rounded-md border-ink-faint`}
|
||||
>
|
||||
<Plus weight="bold" size={18} style={tw`text-ink-faint`} />
|
||||
</View>
|
||||
@@ -92,7 +96,7 @@ const BrowseTags = () => {
|
||||
style={tw`relative h-auto w-[85.5vw] flex-col items-center justify-center overflow-hidden rounded-md border border-dashed border-sidebar-line p-4`}
|
||||
>
|
||||
<Icon name="Tags" size={38} />
|
||||
<Text style={tw`mt-2 text-center font-medium text-ink-dull`}>
|
||||
<Text style={tw`mt-2 font-medium text-center text-ink-dull`}>
|
||||
You have no tags
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -51,7 +51,7 @@ interface CategoryProps {
|
||||
const Category = ({ name, icon }: CategoryProps) => {
|
||||
return (
|
||||
<View
|
||||
style={tw`h-[70px] w-[70px] flex-col items-center justify-center rounded-md border border-sidebar-line/50 bg-sidebar-box`}
|
||||
style={tw`h-[70px] w-[70px] flex-col items-center justify-center rounded-md border border-app-line/50 bg-app-box/50`}
|
||||
>
|
||||
{icon}
|
||||
<Text style={tw`mt-2 text-xs text-white`}>{name}</Text>
|
||||
|
||||
@@ -10,7 +10,7 @@ import Fade from '../layout/Fade';
|
||||
const Jobs = () => {
|
||||
return (
|
||||
<View style={tw`gap-5`}>
|
||||
<View style={tw`w-full flex-row items-center justify-between px-7`}>
|
||||
<View style={tw`flex-row items-center justify-between w-full px-7`}>
|
||||
<Text style={tw`text-lg font-bold text-white`}>Jobs</Text>
|
||||
</View>
|
||||
<Fade color="mobile-screen" height="100%" width={30}>
|
||||
@@ -45,18 +45,18 @@ const Job = ({ progress, message, error }: JobProps) => {
|
||||
: tw.color('accent');
|
||||
return (
|
||||
<View
|
||||
style={tw`h-[170px] w-[310px] flex-col rounded-md border border-sidebar-line/50 bg-sidebar-box`}
|
||||
style={tw`h-[170px] w-[310px] flex-col rounded-md border border-app-line/50 bg-app-box/50`}
|
||||
>
|
||||
<View
|
||||
style={tw`w-full flex-row items-center justify-between rounded-t-md border-b border-sidebar-line/80 bg-mobile-header/50 px-5 py-2`}
|
||||
style={tw`flex-row items-center justify-between w-full px-5 py-2 border-b rounded-t-md border-sidebar-line/80 bg-mobile-header/50`}
|
||||
>
|
||||
<View style={tw`flex-row items-center gap-2`}>
|
||||
<FolderIcon size={36} />
|
||||
<Text style={tw`text-md font-bold text-white`}>Added Memories</Text>
|
||||
<Text style={tw`font-bold text-white text-md`}>Added Memories</Text>
|
||||
</View>
|
||||
<DotsThreeOutlineVertical weight="fill" size={20} color={tw.color('ink-faint')} />
|
||||
</View>
|
||||
<View style={tw`mx-auto flex-1 flex-row items-center justify-between gap-5 px-5 py-3`}>
|
||||
<View style={tw`flex-row items-center justify-between flex-1 gap-5 px-5 py-3 mx-auto`}>
|
||||
<AnimatedCircularProgress
|
||||
size={80}
|
||||
width={7}
|
||||
|
||||
@@ -43,6 +43,7 @@ export default function Header({
|
||||
const navigation = useNavigation();
|
||||
const explorerStore = useExplorerStore();
|
||||
const routeParams = route?.route.params as any;
|
||||
|
||||
const SearchType = () => {
|
||||
switch (searchType) {
|
||||
case 'explorer':
|
||||
@@ -93,7 +94,7 @@ export default function Header({
|
||||
<HeaderIconKind />
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={tw`max-w-[190px] text-lg font-bold text-white`}
|
||||
style={tw`max-w-[190px] text-[22px] font-bold text-white`}
|
||||
>
|
||||
{title || (routeTitle && route?.options.title)}
|
||||
</Text>
|
||||
|
||||
@@ -23,11 +23,11 @@ export function SettingsItem(props: SettingsItemProps) {
|
||||
: 'border-app-input border-l border-r';
|
||||
return (
|
||||
<Pressable onPress={props.onPress}>
|
||||
<View style={twStyle(' border-app-input bg-sidebar-box', borderRounded, border)}>
|
||||
<View style={twStyle(' border-app-line/50 bg-app-box/50', borderRounded, border)}>
|
||||
<View style={tw`flex-row items-center h-auto`}>
|
||||
{props.leftIcon && (
|
||||
<View
|
||||
style={tw`items-center justify-center w-8 h-8 ml-4 mr-5 rounded-full bg-app-input`}
|
||||
style={tw`items-center justify-center w-8 h-8 ml-4 mr-5 border rounded-full bg-app-input border-app-line`}
|
||||
>
|
||||
{props.leftIcon({ size: 20, color: tw.color('ink-dull') })}
|
||||
</View>
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function TabNavigator() {
|
||||
tabBarTestID: screen.testID
|
||||
})}
|
||||
listeners={() => ({
|
||||
tabPress: () => {
|
||||
focus: () => {
|
||||
setActiveIndex(index);
|
||||
}
|
||||
})}
|
||||
|
||||
@@ -6,6 +6,7 @@ import BrowseScreen from '~/screens/browse';
|
||||
import LocationScreen from '~/screens/Location';
|
||||
import { Locations } from '~/screens/Locations';
|
||||
import TagScreen from '~/screens/Tag';
|
||||
import Tags from '~/screens/Tags';
|
||||
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
@@ -36,6 +37,13 @@ export default function BrowseStack() {
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Tags"
|
||||
component={Tags}
|
||||
options={{
|
||||
header: () => <Header navBack title="Tags" />
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Locations"
|
||||
component={Locations}
|
||||
@@ -59,6 +67,7 @@ export type BrowseStackParamList = {
|
||||
Location: { id: number; path?: string };
|
||||
Locations: undefined;
|
||||
Tag: { id: number; color: string };
|
||||
Tags: undefined;
|
||||
};
|
||||
|
||||
export type BrowseStackScreenProps<Screen extends keyof BrowseStackParamList> =
|
||||
|
||||
@@ -48,7 +48,7 @@ export const Locations = () => {
|
||||
fadeSides="top-bottom"
|
||||
orientation="vertical"
|
||||
color="mobile-screen"
|
||||
width={50}
|
||||
width={30}
|
||||
height="100%"
|
||||
>
|
||||
<FlatList
|
||||
@@ -92,7 +92,7 @@ const LocationItem: React.FC<LocationItemProps> = ({
|
||||
return (
|
||||
<Pressable onPress={onPress}>
|
||||
<View
|
||||
style={tw`h-auto w-full flex-row justify-between gap-3 rounded-md border border-sidebar-line/50 bg-sidebar-box p-2`}
|
||||
style={tw`flex-row justify-between w-full h-auto gap-3 p-2 border rounded-md border-sidebar-line/50 bg-sidebar-box`}
|
||||
>
|
||||
<View style={tw`flex-row items-center gap-2`}>
|
||||
<View style={tw`relative`}>
|
||||
@@ -114,7 +114,7 @@ const LocationItem: React.FC<LocationItemProps> = ({
|
||||
<View style={tw`flex-row items-center gap-3`}>
|
||||
<View style={tw`rounded-md bg-app-input p-1.5`}>
|
||||
<Text
|
||||
style={tw`text-left text-xs font-bold text-ink-dull`}
|
||||
style={tw`text-xs font-bold text-left text-ink-dull`}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{`${byteSize(location.size_in_bytes)}`}
|
||||
|
||||
49
apps/mobile/src/screens/Tags.tsx
Normal file
49
apps/mobile/src/screens/Tags.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { View } from 'react-native';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
|
||||
import { BrowseTagItem } from '~/components/browse/BrowseTags';
|
||||
import Fade from '~/components/layout/Fade';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
||||
export default function Tags() {
|
||||
const height = useBottomTabBarHeight();
|
||||
const tags = useLibraryQuery(['tags.list']);
|
||||
const navigation = useNavigation<BrowseStackScreenProps<'Browse'>['navigation']>();
|
||||
|
||||
useNodes(tags.data?.nodes);
|
||||
const tagData = useCache(tags.data?.items);
|
||||
return (
|
||||
<View style={twStyle('relative flex-1 bg-mobile-screen px-7', { marginBottom: height })}>
|
||||
<Fade
|
||||
fadeSides="top-bottom"
|
||||
orientation="vertical"
|
||||
color="mobile-screen"
|
||||
width={30}
|
||||
height="100%"
|
||||
>
|
||||
<FlatList
|
||||
data={tagData}
|
||||
renderItem={({ item }) => (
|
||||
<BrowseTagItem
|
||||
tagStyle="w-[105px]"
|
||||
tag={item}
|
||||
onPress={() =>
|
||||
navigation.navigate('Tag', { id: item.id, color: item.color! })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
numColumns={3}
|
||||
columnWrapperStyle={tw`gap-2.5`}
|
||||
horizontal={false}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
ItemSeparatorComponent={() => <View style={tw`h-2.5`} />}
|
||||
contentContainerStyle={tw`py-5`}
|
||||
/>
|
||||
</Fade>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -22,8 +22,8 @@ const StatCard = ({ icon, name, connectionType, ...stats }: StatCardProps) => {
|
||||
const isDark = useIsDark();
|
||||
|
||||
const { totalSpace, freeSpace, usedSpaceSpace } = useMemo(() => {
|
||||
const totalSpace = byteSize(stats.totalSpace)
|
||||
const freeSpace = stats.freeSpace == null ? totalSpace : byteSize(stats.freeSpace)
|
||||
const totalSpace = byteSize(stats.totalSpace);
|
||||
const freeSpace = stats.freeSpace == null ? totalSpace : byteSize(stats.freeSpace);
|
||||
return {
|
||||
totalSpace,
|
||||
freeSpace,
|
||||
@@ -42,7 +42,7 @@ const StatCard = ({ icon, name, connectionType, ...stats }: StatCardProps) => {
|
||||
|
||||
return (
|
||||
<Card className="flex w-[280px] shrink-0 flex-col bg-app-box/50 !p-0 ">
|
||||
<div className="flex flex-row items-center gap-5 p-4 px-6 ">
|
||||
<div className="flex flex-row items-center gap-5 p-4 px-6">
|
||||
{stats.freeSpace && (
|
||||
<CircularProgress
|
||||
radius={40}
|
||||
@@ -78,7 +78,7 @@ const StatCard = ({ icon, name, connectionType, ...stats }: StatCardProps) => {
|
||||
<Pill className="uppercase">{connectionType || 'Local'}</Pill>
|
||||
<div className="grow" />
|
||||
{/* <Button size="icon" variant="outline">
|
||||
<Ellipsis className="h-3 w-3 opacity-50" />
|
||||
<Ellipsis className="w-3 h-3 opacity-50" />
|
||||
</Button> */}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user