diff --git a/src/app/now-playing.tsx b/src/app/now-playing.tsx index c4300cf..a5f15d2 100644 --- a/src/app/now-playing.tsx +++ b/src/app/now-playing.tsx @@ -32,6 +32,8 @@ const SUB_CONTROLS: { icon: IconName; label: string }[] = [ const DISMISS_DISTANCE = 140; const DISMISS_VELOCITY = 1000; +const COMPACT_HEIGHT = 780; +const TIGHT_HEIGHT = 700; export default function NowPlayingScreen() { const router = useRouter(); @@ -44,8 +46,26 @@ export default function NowPlayingScreen() { const isPlaying = playbackState === 'playing'; const isLoading = playbackState === 'loading'; - const contentWidth = windowWidth - spacing.xl * 2; - const artSize = Math.min(296, contentWidth); + const availableHeight = windowHeight - insets.top - insets.bottom; + const isCompact = availableHeight < COMPACT_HEIGHT; + const isTight = availableHeight < TIGHT_HEIGHT; + const contentPadding = isTight ? spacing.lg : spacing.xl; + const contentWidth = windowWidth - contentPadding * 2; + const artSize = Math.round( + Math.min( + contentWidth, + isTight ? 128 : isCompact ? 200 : 296, + Math.max(isTight ? 96 : 144, availableHeight * (isTight ? 0.18 : isCompact ? 0.24 : 0.32)) + ) + ); + const visualizerHeight = isTight ? 58 : isCompact ? 76 : 96; + const waveformHeight = isTight ? 42 : isCompact ? 50 : 58; + const waveformTouchPadding = isTight ? spacing.xs : spacing.md; + const playButtonSize = isTight ? 58 : isCompact ? 62 : 68; + const skipIconSize = isTight ? 28 : 32; + const playIconSize = isTight ? 30 : 34; + const subButtonSize = isTight ? 36 : 40; + const subIconSize = isTight ? 18 : 20; const source = track?.album?.trim() ? track.album : 'Library'; // Swipe down to minimize. The stack transition is disabled for this route, so @@ -107,7 +127,11 @@ export default function NowPlayingScreen() { style={[ styles.content, contentStyle, - { paddingTop: insets.top + spacing.sm, paddingBottom: insets.bottom + spacing.lg }, + { + paddingHorizontal: contentPadding, + paddingTop: insets.top + (isTight ? spacing.xs : spacing.sm), + paddingBottom: insets.bottom + (isTight ? spacing.sm : spacing.lg), + }, ]} > @@ -129,7 +153,15 @@ export default function NowPlayingScreen() { {track ? ( <> - + {track.artworkData ? ( - + - + {track.title} {track.artist} - + - + void seekTo(seconds)} /> - + - + - + - + - + - + {SUB_CONTROLS.map((c) => ( - + ))} @@ -220,7 +270,6 @@ const styles = StyleSheet.create({ content: { flex: 1, backgroundColor: colors.bgPrimary, - paddingHorizontal: spacing.xl, }, header: { flexDirection: 'row', @@ -249,8 +298,6 @@ const styles = StyleSheet.create({ artWrap: { alignItems: 'center', justifyContent: 'center', - marginTop: spacing.lg, - marginBottom: spacing.lg, }, art: { borderRadius: radius.lg, @@ -264,7 +311,6 @@ const styles = StyleSheet.create({ height: '100%', }, trackInfo: { - marginTop: spacing.md, alignItems: 'center', }, centered: { @@ -275,24 +321,18 @@ const styles = StyleSheet.create({ marginTop: spacing.xs, }, badges: { - marginTop: spacing.md, }, progressBlock: { - marginTop: spacing.lg, }, spacer: { flex: 1, - minHeight: spacing.md, }, transport: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', - gap: spacing.xxl, }, playButton: { - width: 68, - height: 68, borderRadius: radius.pill, backgroundColor: colors.accent, alignItems: 'center', @@ -302,12 +342,8 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - marginTop: spacing.lg, - paddingHorizontal: spacing.sm, }, subBtn: { - width: 40, - height: 40, alignItems: 'center', justifyContent: 'center', }, diff --git a/src/components/Visualizer.tsx b/src/components/Visualizer.tsx index dc832b6..a22b8d1 100644 --- a/src/components/Visualizer.tsx +++ b/src/components/Visualizer.tsx @@ -17,7 +17,7 @@ type Mode = 'spectrum' | 'scope'; * in the layout. Tap anywhere on it to switch between the live filled-line * Spectrum and the Scope (oscilloscope, placeholder until its native path lands). */ -export function Visualizer({ width }: { width: number }) { +export function Visualizer({ width, height = CANVAS_HEIGHT }: { width: number; height?: number }) { const [mode, setMode] = useState('spectrum'); const scopeActive = useScopeActive(); const spectrumActive = scopeActive && mode === 'spectrum'; @@ -39,9 +39,9 @@ export function Visualizer({ width }: { width: number }) { - + {mode === 'spectrum' ? ( - + ) : ( diff --git a/src/components/WaveformSeekBar.tsx b/src/components/WaveformSeekBar.tsx index ab3c323..108f51e 100644 --- a/src/components/WaveformSeekBar.tsx +++ b/src/components/WaveformSeekBar.tsx @@ -18,6 +18,8 @@ interface WaveformSeekBarProps { currentTime: number; duration: number; onSeek: (seconds: number) => void; + height?: number; + touchPadding?: number; /** Identity of the playing track; a pending seek only applies to its own track. */ trackKey?: string | number; /** Track file URI used to load/cache the offline waveform peaks. */ @@ -36,6 +38,8 @@ export function WaveformSeekBar({ currentTime, duration, onSeek, + height = CANVAS_HEIGHT, + touchPadding = spacing.md, trackKey, trackPath, }: WaveformSeekBarProps) { @@ -125,20 +129,20 @@ export function WaveformSeekBar({ const r = BAR_WIDTH / 2; for (let i = 0; i < barCount; i++) { const amp = Math.max(MIN_BAR, display[i] ?? MIN_BAR); - const h = amp * CANVAS_HEIGHT; + const h = amp * height; const x = i * (BAR_WIDTH + BAR_GAP); - const y = (CANVAS_HEIGHT - h) / 2; + const y = (height - h) / 2; path.addRRect(Skia.RRectXY(Skia.XYWHRect(x, y, BAR_WIDTH, h), r, r)); } return path; - }, [source, barCount, barWidth]); + }, [source, barCount, barWidth, height]); const splitX = fraction * barWidth; return ( duration > 0} onMoveShouldSetResponder={() => duration > 0} @@ -151,11 +155,11 @@ export function WaveformSeekBar({ accessibilityLabel="Seek" accessibilityValue={{ min: 0, max: Math.round(duration), now: Math.round(shownTime) }} > - - + + - + @@ -175,7 +179,6 @@ export function WaveformSeekBar({ const styles = StyleSheet.create({ touchArea: { justifyContent: 'center', - height: CANVAS_HEIGHT + spacing.md * 2, // generous touch target around the canvas }, times: { flexDirection: 'row',