mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-19 12:14:47 +02:00
smoothing
This commit is contained in:
@@ -5,17 +5,21 @@ import { Easing } from 'react-native';
|
||||
import { TabBar, type TabItem } from '@/components/TabBar';
|
||||
import { colors } from '@/theme';
|
||||
|
||||
const TAB_TRANSITION_MS = 160;
|
||||
|
||||
export default function TabsLayout() {
|
||||
return (
|
||||
<Tabs
|
||||
detachInactiveScreens
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
freezeOnBlur: true,
|
||||
sceneStyle: { backgroundColor: colors.bgPrimary },
|
||||
// Directional slide + cross-fade between tabs, following tab order.
|
||||
animation: 'shift',
|
||||
transitionSpec: {
|
||||
animation: 'timing',
|
||||
config: { duration: 200, easing: Easing.out(Easing.cubic) },
|
||||
config: { duration: TAB_TRANSITION_MS, easing: Easing.out(Easing.cubic) },
|
||||
},
|
||||
}}
|
||||
tabBar={({ state, navigation }) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Pressable, StyleSheet, View, useWindowDimensions } from 'react-native';
|
||||
import { InteractionManager, Pressable, StyleSheet, View, useWindowDimensions } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useFocusEffect } from 'expo-router';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
@@ -56,9 +56,12 @@ export default function EQScreen() {
|
||||
// Gate the post-EQ tap to while this screen is visible.
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setFocused(true);
|
||||
setActivePostEqNative(true);
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
setFocused(true);
|
||||
setActivePostEqNative(true);
|
||||
});
|
||||
return () => {
|
||||
task.cancel();
|
||||
setFocused(false);
|
||||
setActivePostEqNative(false);
|
||||
};
|
||||
|
||||
+15
-12
@@ -358,16 +358,7 @@ export default function HomeScreen() {
|
||||
const [randomAlbumKey, setRandomAlbumKey] = useState<string | null>(null);
|
||||
const [randomSeed] = useState(() => Math.random());
|
||||
const scrollTop = useScrollTopGate();
|
||||
|
||||
const tracksByAlbum = useMemo(() => {
|
||||
const map = new Map<string, DbTrack[]>();
|
||||
for (const track of tracks) {
|
||||
const list = map.get(track.album_identity_key) ?? [];
|
||||
list.push(track);
|
||||
map.set(track.album_identity_key, list);
|
||||
}
|
||||
return map;
|
||||
}, [tracks]);
|
||||
const hasLibrary = tracks.length > 0;
|
||||
|
||||
const recentlyAddedAlbums = useMemo(
|
||||
() => [...albums].sort((a, b) => b.latest_added_at - a.latest_added_at).slice(0, RECENT_ALBUM_LIMIT),
|
||||
@@ -394,10 +385,21 @@ export default function HomeScreen() {
|
||||
if (selected) return selected;
|
||||
return albums[Math.floor(randomSeed * albums.length) % albums.length];
|
||||
}, [albums, randomAlbumKey, randomSeed]);
|
||||
const randomTracks = randomAlbum ? (tracksByAlbum.get(randomAlbum.identity_key) ?? []) : [];
|
||||
const randomAlbumNeedsTracks = hasLibrary && !currentTrack && randomAlbum != null;
|
||||
const tracksByAlbum = useMemo(() => {
|
||||
if (!randomAlbumNeedsTracks) return null;
|
||||
const map = new Map<string, DbTrack[]>();
|
||||
for (const track of tracks) {
|
||||
const list = map.get(track.album_identity_key) ?? [];
|
||||
list.push(track);
|
||||
map.set(track.album_identity_key, list);
|
||||
}
|
||||
return map;
|
||||
}, [randomAlbumNeedsTracks, tracks]);
|
||||
const randomTracks =
|
||||
randomAlbum && tracksByAlbum ? (tracksByAlbum.get(randomAlbum.identity_key) ?? []) : [];
|
||||
const recentTracks = recentlyPlayedTracks.slice(0, RECENT_TRACK_LIMIT);
|
||||
const canExpandRecentTracks = recentlyPlayedTracks.length > RECENT_TRACK_LIMIT;
|
||||
const hasLibrary = tracks.length > 0;
|
||||
|
||||
const openAlbum = (album: Album) => {
|
||||
router.push({
|
||||
@@ -412,6 +414,7 @@ export default function HomeScreen() {
|
||||
};
|
||||
|
||||
const playAlbum = (album: Album, shuffled = false) => {
|
||||
if (!tracksByAlbum) return;
|
||||
const albumTracks = tracksByAlbum.get(album.identity_key) ?? [];
|
||||
if (albumTracks.length === 0) return;
|
||||
if (shuffled) {
|
||||
|
||||
@@ -52,7 +52,10 @@ export default function LibraryScreen() {
|
||||
|
||||
const isEmpty = tracks.length === 0 && folders.length === 0 && !isScanning;
|
||||
|
||||
const sortedTracks = useMemo(() => sortTracks(tracks, trackSort), [tracks, trackSort]);
|
||||
const sortedTracks = useMemo(
|
||||
() => (viewMode === 'tracks' ? sortTracks(tracks, trackSort) : []),
|
||||
[trackSort, tracks, viewMode]
|
||||
);
|
||||
|
||||
// Tap index is within sortedTracks so the tapped row is the track that plays.
|
||||
const playAllFrom = (index: number) => {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Alert, View, Pressable, ScrollView, StyleSheet, Switch } from 'react-native';
|
||||
import {
|
||||
Alert,
|
||||
InteractionManager,
|
||||
View,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Switch,
|
||||
} from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { Screen } from '@/components/Screen';
|
||||
@@ -237,7 +245,10 @@ export default function SettingsScreen() {
|
||||
const setReplayGainMode = useAudioSettingsStore((s) => s.setReplayGainMode);
|
||||
|
||||
useEffect(() => {
|
||||
void initDesktopRemote();
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
void initDesktopRemote();
|
||||
});
|
||||
return () => task.cancel();
|
||||
}, [initDesktopRemote]);
|
||||
|
||||
const desktopRemoteSubtitle = desktopRemoteConnection
|
||||
|
||||
Reference in New Issue
Block a user