massive UI/UX overhaul

This commit is contained in:
Boof2015
2026-07-03 23:56:34 -04:00
parent 9ce825b673
commit 61e6147d3e
71 changed files with 2806 additions and 953 deletions
+16 -148
View File
@@ -1,13 +1,16 @@
import { useState } from 'react';
import { Pressable, StyleSheet, View } from 'react-native';
import { BottomSheetTextInput } from '@gorhom/bottom-sheet';
import { useRouter } from 'expo-router';
import { Text } from '@/components/Text';
import { AppSheet, AppSheetItem, AppSheetSection, type AppSheetItemProps } from '@/components/sheets/AppSheet';
import {
AppSheet,
AppSheetItem,
AppSheetSection,
AppSheetTitle,
type AppSheetItemProps,
} from '@/components/sheets/AppSheet';
import { PlaylistPickerSheet } from '@/components/sheets/PlaylistPickerSheet';
import { enqueueEnd, enqueueTop } from '@/audio/playbackController';
import { dbTrackToTrack } from '@/library/trackAdapter';
import { resolveCanonicalBrowseArtist, resolveStrictBrowseArtist } from '@/library/artistGrouping';
import { colors, fonts, radius, spacing } from '@/theme';
import { usePlaylistStore } from '@/stores/playlistStore';
import { useSettingsStore } from '@/stores/settingsStore';
import type { DbTrack } from '@/types/library';
@@ -40,33 +43,19 @@ function TrackActionsSheetInner({
extraItems = [],
}: TrackActionsSheetProps & { track: DbTrack }) {
const router = useRouter();
const [step, setStep] = useState<'menu' | 'pickPlaylist' | 'newPlaylist'>(initialStep);
const [playlistName, setPlaylistName] = useState('');
const [step, setStep] = useState<'menu' | 'pickPlaylist'>(initialStep);
const groupingMode = useSettingsStore((s) => s.artistGroupingMode);
const playlists = usePlaylistStore((s) => s.playlists);
const isFavorite = usePlaylistStore((s) => s.favoritePaths.has(track.path));
const toggleFavorite = usePlaylistStore((s) => s.toggleFavorite);
const addTracksToPlaylist = usePlaylistStore((s) => s.addTracksToPlaylist);
const createPlaylist = usePlaylistStore((s) => s.createPlaylist);
const artistName =
groupingMode === 'fileTags' ? resolveStrictBrowseArtist(track) : resolveCanonicalBrowseArtist(track);
const trimmedPlaylistName = playlistName.trim();
const closeAndRun = (run: () => void) => {
onClose();
run();
};
const addToNewPlaylist = () => {
if (!trimmedPlaylistName) return;
void (async () => {
const playlist = await createPlaylist(trimmedPlaylistName);
await addTracksToPlaylist(playlist.id, [track]);
})();
onClose();
};
const menuItems: TrackActionSheetItem[] = [
{
key: 'play-next',
@@ -119,78 +108,20 @@ function TrackActionsSheetInner({
},
];
const pickItems: TrackActionSheetItem[] = [
...playlists.map((playlist) => ({
key: `playlist-${playlist.id}`,
label: playlist.name,
icon: 'musical-notes-outline' as const,
onPress: () => closeAndRun(() => void addTracksToPlaylist(playlist.id, [track])),
})),
{
key: 'new-playlist',
label: 'New playlist...',
icon: 'add',
onPress: () => setStep('newPlaylist'),
},
];
if (step === 'pickPlaylist') {
return (
<AppSheet onClose={onClose}>
<SheetTitle title="Add to playlist" subtitle={track.title} />
{initialStep === 'menu' ? (
<AppSheetItem label="Track actions" icon="arrow-back" onPress={() => setStep('menu')} />
) : null}
{playlists.length === 0 ? (
<Text variant="caption" color={colors.textTertiary} style={styles.empty}>
No playlists yet.
</Text>
) : null}
{pickItems.map(({ key, ...item }) => (
<AppSheetItem key={key} {...item} />
))}
</AppSheet>
);
}
if (step === 'newPlaylist') {
return (
<AppSheet onClose={onClose}>
<SheetTitle title="New playlist" subtitle={track.title} />
<BottomSheetTextInput
value={playlistName}
onChangeText={setPlaylistName}
placeholder="Playlist name"
placeholderTextColor={colors.textTertiary}
style={styles.input}
autoFocus
returnKeyType="done"
onSubmitEditing={addToNewPlaylist}
selectionColor={colors.accent}
/>
<View style={styles.actions}>
<Pressable style={[styles.btn, styles.cancel]} onPress={() => setStep('pickPlaylist')}>
<Text variant="label" color={colors.textSecondary}>
Back
</Text>
</Pressable>
<Pressable
style={[styles.btn, styles.create, !trimmedPlaylistName && styles.createDisabled]}
disabled={!trimmedPlaylistName}
onPress={addToNewPlaylist}
>
<Text variant="label" color={colors.accentTextStrong}>
Create
</Text>
</Pressable>
</View>
</AppSheet>
<PlaylistPickerSheet
tracks={[track]}
subtitle={track.title}
onClose={onClose}
onBackToMenu={initialStep === 'menu' ? () => setStep('menu') : undefined}
/>
);
}
return (
<AppSheet onClose={onClose}>
<SheetTitle title={track.title} subtitle={track.artist} />
<AppSheetTitle title={track.title} subtitle={track.artist} />
{menuItems.map(({ key, ...item }) => (
<AppSheetItem key={key} {...item} />
))}
@@ -205,66 +136,3 @@ function TrackActionsSheetInner({
</AppSheet>
);
}
function SheetTitle({ title, subtitle }: { title: string; subtitle?: string }) {
return (
<View style={styles.titleBlock}>
<Text variant="heading" numberOfLines={1} style={styles.title}>
{title}
</Text>
{subtitle ? (
<Text variant="label" numberOfLines={1} color={colors.textSecondary}>
{subtitle}
</Text>
) : null}
</View>
);
}
const styles = StyleSheet.create({
titleBlock: {
marginTop: spacing.xs,
marginBottom: spacing.sm,
gap: 2,
},
title: {
paddingRight: spacing.lg,
},
empty: {
paddingVertical: spacing.sm,
},
input: {
color: colors.textPrimary,
fontFamily: fonts.sans.regular,
fontSize: 16,
paddingHorizontal: spacing.md,
paddingVertical: spacing.md,
borderRadius: radius.md,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.glassBorder,
backgroundColor: colors.glassBg,
},
actions: {
flexDirection: 'row',
justifyContent: 'flex-end',
gap: spacing.sm,
marginTop: spacing.lg,
},
btn: {
paddingHorizontal: spacing.xl,
paddingVertical: spacing.md,
borderRadius: radius.pill,
},
cancel: {
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.glassBorder,
},
create: {
backgroundColor: colors.accentGlow,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.accent,
},
createDisabled: {
opacity: 0.4,
},
});