mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-19 04:06:43 +02:00
improve home page
This commit is contained in:
@@ -35,10 +35,6 @@ const PILL_HEIGHT = 56;
|
||||
const ART = 42;
|
||||
const CURVE_POINTS = 64;
|
||||
|
||||
interface MiniPlayerProps {
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
function MiniProgress({
|
||||
currentTime,
|
||||
duration,
|
||||
@@ -70,7 +66,7 @@ function PhoneMiniProgress({ isPlaying }: { isPlaying: boolean }) {
|
||||
* bar with the live filled-line spectrum drifting behind the metadata. Tapping
|
||||
* opens the full now-playing screen.
|
||||
*/
|
||||
export function MiniPlayer({ visible = true }: MiniPlayerProps) {
|
||||
export function MiniPlayer() {
|
||||
const styles = useStyles();
|
||||
const colors = useColors();
|
||||
const ripple = useRipple();
|
||||
@@ -110,7 +106,7 @@ export function MiniPlayer({ visible = true }: MiniPlayerProps) {
|
||||
const isLoading = presentation.playbackState === 'loading';
|
||||
// The pill sits underneath the now-playing overlay; don't burn a second
|
||||
// live-scope frame loop while it's fully occluded.
|
||||
const liveScopeActive = visible && scopeActive && !isDesktop && !playerOpen;
|
||||
const liveScopeActive = scopeActive && !isDesktop && !playerOpen;
|
||||
|
||||
const onLayout = (e: LayoutChangeEvent) => setPillWidth(e.nativeEvent.layout.width);
|
||||
const onTogglePlay = () => {
|
||||
@@ -135,9 +131,8 @@ export function MiniPlayer({ visible = true }: MiniPlayerProps) {
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
pointerEvents={visible ? 'auto' : 'none'}
|
||||
android_ripple={ripple.bounded}
|
||||
style={[styles.pill, !visible && styles.hidden]}
|
||||
style={styles.pill}
|
||||
onPress={() => usePlayerUiStore.getState().openPlayer()}
|
||||
onLayout={onLayout}
|
||||
>
|
||||
@@ -238,9 +233,6 @@ const useStyles = createThemedStyles((colors) => ({
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
},
|
||||
spectrum: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Pressable,
|
||||
@@ -22,14 +22,9 @@ import {
|
||||
import { createThemedStyles, useColors } from '@/theme/themed';
|
||||
import { useRipple } from '@/theme/ripple';
|
||||
import { motion } from '@/theme/motion';
|
||||
import { TAB_TRANSITION_SETTLE_MS } from '@/navigation/tabTransition';
|
||||
import { useDesktopRemoteStore } from '@/stores/desktopRemoteStore';
|
||||
import { usePlaybackTargetStore } from '@/stores/playbackTargetStore';
|
||||
import { usePlayerStore } from '@/stores/playerStore';
|
||||
import { playHaptic } from '@/lib/haptics';
|
||||
|
||||
type IconName = keyof typeof Ionicons.glyphMap;
|
||||
type MiniPlayerPhase = 'hidden' | 'reserved' | 'visible';
|
||||
|
||||
export const TAB_META: Record<string, { label: string; icon: IconName }> = {
|
||||
index: { label: 'Home', icon: 'home' },
|
||||
@@ -58,12 +53,6 @@ export function TabBar({ items, onPress }: TabBarProps) {
|
||||
const styles = useStyles();
|
||||
const insets = useSafeAreaInsets();
|
||||
const tabs = items.filter((item) => TAB_META[item.name]);
|
||||
const homeFocused = items.some((item) => item.name === 'index' && item.focused);
|
||||
const selectedTarget = usePlaybackTargetStore((s) => s.target);
|
||||
const phoneTrack = usePlayerStore((s) => s.currentTrack);
|
||||
const desktopConnection = useDesktopRemoteStore((s) => s.connection);
|
||||
const desktopTrack = useDesktopRemoteStore((s) => s.snapshot?.currentTrack);
|
||||
const [settledHomeFocused, setSettledHomeFocused] = useState(homeFocused);
|
||||
const count = tabs.length;
|
||||
const activeIndex = Math.max(
|
||||
0,
|
||||
@@ -79,32 +68,6 @@ export function TabBar({ items, onPress }: TabBarProps) {
|
||||
position.value = withTiming(activeIndex, motion.snap);
|
||||
}, [activeIndex, position]);
|
||||
|
||||
useEffect(() => {
|
||||
if (settledHomeFocused === homeFocused) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(
|
||||
() => setSettledHomeFocused(homeFocused),
|
||||
TAB_TRANSITION_SETTLE_MS
|
||||
);
|
||||
return () => clearTimeout(timer);
|
||||
}, [homeFocused, settledHomeFocused]);
|
||||
|
||||
const remoteMiniVisibleOnHome =
|
||||
(selectedTarget === 'desktop' && Boolean(desktopConnection || desktopTrack)) ||
|
||||
(!phoneTrack && Boolean(desktopTrack));
|
||||
const suppressMiniForHome = homeFocused && !remoteMiniVisibleOnHome;
|
||||
const suppressMiniForSettledHome = settledHomeFocused && !remoteMiniVisibleOnHome;
|
||||
|
||||
const miniPlayerPhase: MiniPlayerPhase = suppressMiniForHome
|
||||
? suppressMiniForSettledHome
|
||||
? 'hidden'
|
||||
: 'reserved'
|
||||
: suppressMiniForSettledHome
|
||||
? 'hidden'
|
||||
: 'visible';
|
||||
|
||||
const indicatorStyle = useAnimatedStyle(() => {
|
||||
const segment = count > 0 ? barWidth.value / count : 0;
|
||||
return {
|
||||
@@ -119,8 +82,7 @@ export function TabBar({ items, onPress }: TabBarProps) {
|
||||
|
||||
return (
|
||||
<View style={styles.wrap}>
|
||||
{miniPlayerPhase === 'visible' ? <MiniPlayer /> : null}
|
||||
{miniPlayerPhase === 'reserved' ? <MiniPlayer visible={false} /> : null}
|
||||
<MiniPlayer />
|
||||
<View
|
||||
style={[
|
||||
styles.bar,
|
||||
|
||||
@@ -28,6 +28,7 @@ import { useThemeStore } from '@/stores/themeStore';
|
||||
import type { LastFmStatus } from '@/types/lastFm';
|
||||
import { Text } from '@/components/Text';
|
||||
import { playHaptic } from '@/lib/haptics';
|
||||
import type { HomeGreetingTextMode } from '@/home/homeGreeting';
|
||||
|
||||
export function lastFmScrobbleSubtitle(status: LastFmStatus | null): string {
|
||||
const connected = status?.profiles.filter((p) => p.connected).length ?? 0;
|
||||
@@ -59,6 +60,12 @@ const DARK_STYLE_SEGMENTS = [
|
||||
{ key: 'amoled', label: 'AMOLED' },
|
||||
];
|
||||
|
||||
const HOME_GREETING_SEGMENTS = [
|
||||
{ key: 'messages', label: 'Messages' },
|
||||
{ key: 'clock', label: 'Clock' },
|
||||
{ key: 'off', label: 'Off' },
|
||||
];
|
||||
|
||||
const ARTIST_GROUPING_OPTIONS: { mode: ArtistGroupingMode; title: string; description: string }[] = [
|
||||
{
|
||||
mode: 'astra',
|
||||
@@ -101,6 +108,8 @@ export function AppearanceSettingsPanel() {
|
||||
const accentApplies = !resolvedId.startsWith('materialYou');
|
||||
const nowPlayingScopeStyle = useSettingsStore((s) => s.nowPlayingScopeStyle);
|
||||
const setNowPlayingScopeStyle = useSettingsStore((s) => s.setNowPlayingScopeStyle);
|
||||
const homeGreetingTextMode = useSettingsStore((s) => s.homeGreetingTextMode);
|
||||
const setHomeGreetingTextMode = useSettingsStore((s) => s.setHomeGreetingTextMode);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -156,6 +165,21 @@ export function AppearanceSettingsPanel() {
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<SettingsSectionLabel spaced>HOME</SettingsSectionLabel>
|
||||
<SettingsCard>
|
||||
<Text variant="body" style={styles.settingTitle}>
|
||||
Greeting
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.textSecondary} style={styles.settingNote}>
|
||||
Show rotating Astra messages, a clock, or only the search action on Home.
|
||||
</Text>
|
||||
<SegmentedControl
|
||||
segments={HOME_GREETING_SEGMENTS}
|
||||
value={homeGreetingTextMode}
|
||||
onChange={(key) => void setHomeGreetingTextMode(key as HomeGreetingTextMode)}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
<SettingsSectionLabel spaced>NOW PLAYING SCOPES</SettingsSectionLabel>
|
||||
<ScopeStyleCards
|
||||
value={nowPlayingScopeStyle}
|
||||
|
||||
Reference in New Issue
Block a user