New look: slate-indigo panels, pink accent, Poppins, and a gradient card for your song
Inspired by a dashboard design the user shared. - App: new palette and fonts, borderless rounded panels with soft shadows, a pink-to-blue gradient card for your own song (text and controls switch to white on it), flat cards for friends, a 'Hi Chris, welcome back' header, an outline bell, and a pink pill on the active tab. - Website: the same look, plus a wide-screen layout with a sidebar, the page in a rounded panel, and a right column with your profile, recent activity and recent plays. Phones keep the top bar and bottom tabs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Y2iUsKEWDuRGjK5FPmcUP
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { Image } from 'expo-image';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { router } from 'expo-router';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
@@ -13,7 +12,7 @@ import Animated, {
|
||||
} from 'react-native-reanimated';
|
||||
import { SERVICE_LABELS, timeAgo, type NowPlayingDTO, type StreamingService } from '@musicshare/shared';
|
||||
import { successHaptic, tapHaptic } from '@/lib/haptics';
|
||||
import { colors, radius, space } from '@/lib/theme';
|
||||
import { OnHero, colors, fonts, heroGradient, panelShadow, radius, space } from '@/lib/theme';
|
||||
import { Avatar } from './Avatar';
|
||||
import { Equalizer } from './Equalizer';
|
||||
import { Lyrics } from './Lyrics';
|
||||
@@ -54,15 +53,12 @@ export function NowPlayingCard({
|
||||
onTogglePin?.();
|
||||
};
|
||||
|
||||
// Your own song gets the gradient "hero" card; friends' songs sit on plain panels.
|
||||
const hero = !showUser;
|
||||
return (
|
||||
<View style={[styles.card, pinned && styles.pinned]}>
|
||||
{/* The card glows with its album art; crossfades when the song changes. */}
|
||||
{np.track.artworkUrl && (
|
||||
<Animated.View key={np.track.artworkUrl} style={StyleSheet.absoluteFill} entering={FadeIn.duration(700)} exiting={FadeOut.duration(500)}>
|
||||
<Image source={np.track.artworkUrl} style={styles.backdrop} blurRadius={40} contentFit="cover" />
|
||||
</Animated.View>
|
||||
)}
|
||||
<LinearGradient colors={['rgba(7,7,11,0.05)', 'rgba(7,7,11,0.6)']} style={StyleSheet.absoluteFill} />
|
||||
<OnHero.Provider value={hero}>
|
||||
<View style={[styles.card, hero && styles.heroCard, pinned && styles.pinned]}>
|
||||
{hero && <LinearGradient colors={heroGradient} start={{ x: 0, y: 0 }} end={{ x: 1, y: 1 }} style={StyleSheet.absoluteFill} />}
|
||||
|
||||
<View style={styles.header}>
|
||||
{showUser && (
|
||||
@@ -74,11 +70,11 @@ export function NowPlayingCard({
|
||||
</Pressable>
|
||||
)}
|
||||
<View style={styles.statusRow}>
|
||||
{np.isPlaying && <Equalizer playing />}
|
||||
<Text style={[styles.status, np.isPlaying && styles.live]} numberOfLines={1}>
|
||||
{np.isPlaying && <Equalizer playing color={hero ? '#ffffff' : colors.live} />}
|
||||
<Text style={[styles.status, hero && styles.heroText, np.isPlaying && (hero ? styles.heroLive : styles.live)]} numberOfLines={1}>
|
||||
{np.isPlaying ? 'Listening now' : `Played ${timeAgo(np.updatedAt)}`}
|
||||
<Text
|
||||
style={[styles.via, np.sourceUrl && styles.viaLink]}
|
||||
style={[styles.via, hero && styles.heroText, np.sourceUrl && styles.viaLink]}
|
||||
onPress={np.sourceUrl ? () => Linking.openURL(np.sourceUrl!) : undefined}>
|
||||
{' · '}
|
||||
{np.sourceApp ?? SERVICE_LABELS[np.source]}
|
||||
@@ -109,30 +105,33 @@ export function NowPlayingCard({
|
||||
</Animated.View>
|
||||
)}
|
||||
</View>
|
||||
</OnHero.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
borderRadius: radius.lg,
|
||||
padding: space.md + 2,
|
||||
padding: space.md + 4,
|
||||
gap: space.sm + 2,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: colors.surface,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
borderColor: 'transparent',
|
||||
...panelShadow,
|
||||
},
|
||||
heroCard: { overflow: 'hidden', shadowColor: colors.accent, shadowOpacity: 0.4, shadowRadius: 22 },
|
||||
heroText: { color: 'rgba(255,255,255,0.85)' },
|
||||
heroLive: { color: '#ffffff', fontFamily: fonts.bodyMedium },
|
||||
details: { gap: space.md },
|
||||
pinned: { borderColor: 'rgba(251,191,36,0.55)' },
|
||||
backdrop: { position: 'absolute', top: '-40%', left: '-40%', width: '180%', height: '180%', opacity: 0.55 },
|
||||
pinned: { borderColor: colors.accentLine },
|
||||
header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: space.sm },
|
||||
user: { flexDirection: 'row', alignItems: 'center', gap: space.sm, flexShrink: 1 },
|
||||
name: { color: colors.text, fontWeight: '700', fontSize: 15, flexShrink: 1 },
|
||||
name: { color: colors.text, fontFamily: fonts.bodyMedium, fontSize: 14.5, flexShrink: 1 },
|
||||
statusRow: { flexDirection: 'row', alignItems: 'center', gap: space.xs + 2, flexShrink: 1 },
|
||||
status: { color: colors.muted, fontSize: 13, flexShrink: 1 },
|
||||
live: { color: colors.live, fontWeight: '600' },
|
||||
via: { color: colors.muted, fontWeight: '400' },
|
||||
status: { color: colors.muted, fontSize: 12.5, fontFamily: fonts.body, flexShrink: 1 },
|
||||
live: { color: colors.live, fontFamily: fonts.bodyMedium },
|
||||
via: { color: colors.muted, fontFamily: fonts.body },
|
||||
viaLink: { textDecorationLine: 'underline' },
|
||||
pin: { color: colors.muted, fontSize: 21, marginLeft: space.xs },
|
||||
pinOn: { color: colors.pin, textShadowColor: 'rgba(251,191,36,0.6)', textShadowRadius: 10 },
|
||||
pinOn: { color: colors.accent, textShadowColor: 'rgba(255,79,139,0.6)', textShadowRadius: 10 },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user