mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-12 05:10:52 +02:00
123 lines
3.0 KiB
TypeScript
123 lines
3.0 KiB
TypeScript
import { useState } from 'react';
|
|
import { Pressable, StyleSheet, View } from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { Text } from './Text';
|
|
import { SpectrumCurve } from './SpectrumCurve';
|
|
import { OscilloscopeWave } from './OscilloscopeWave';
|
|
import { colors, spacing } from '@/theme';
|
|
import { useScopeActive } from '@/scope/scopeStore';
|
|
|
|
const CANVAS_HEIGHT = 96;
|
|
const STAGE_FRAME_MS = 0; // display-sync
|
|
|
|
type Mode = 'spectrum' | 'scope';
|
|
|
|
interface VisualizerProps {
|
|
width: number;
|
|
height?: number;
|
|
interactive?: boolean;
|
|
showChrome?: boolean;
|
|
mode?: Mode;
|
|
edgeFade?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Visualizer for the now-playing screen. By default it remains an inline
|
|
* interactive component; inside the media stage it can render chrome-free so
|
|
* the parent owns artwork/scope switching without moving the surrounding UI.
|
|
*/
|
|
export function Visualizer({
|
|
width,
|
|
height = CANVAS_HEIGHT,
|
|
interactive = true,
|
|
showChrome = true,
|
|
mode: controlledMode,
|
|
edgeFade = false,
|
|
}: VisualizerProps) {
|
|
const [uncontrolledMode, setUncontrolledMode] = useState<Mode>('spectrum');
|
|
const mode = controlledMode ?? uncontrolledMode;
|
|
const scopeActive = useScopeActive();
|
|
const spectrumActive = scopeActive && mode === 'spectrum';
|
|
const scopeWaveActive = scopeActive && mode === 'scope';
|
|
|
|
const toggle = () => {
|
|
if (controlledMode) return;
|
|
setUncontrolledMode((m) => (m === 'spectrum' ? 'scope' : 'spectrum'));
|
|
};
|
|
|
|
const content = (
|
|
<>
|
|
{showChrome && (
|
|
<View style={styles.caption}>
|
|
<Text variant="caption" style={styles.captionText}>
|
|
{mode === 'spectrum' ? 'SPECTRUM' : 'SCOPE'}
|
|
</Text>
|
|
<Ionicons name="swap-horizontal" size={14} color={colors.textTertiary} />
|
|
</View>
|
|
)}
|
|
|
|
<View style={{ width, height }}>
|
|
{mode === 'spectrum' ? (
|
|
<SpectrumCurve
|
|
active={spectrumActive}
|
|
frameMs={STAGE_FRAME_MS}
|
|
width={width}
|
|
height={height}
|
|
glow
|
|
edgeFade={edgeFade}
|
|
/>
|
|
) : (
|
|
<OscilloscopeWave
|
|
active={scopeWaveActive}
|
|
width={width}
|
|
height={height}
|
|
glow
|
|
edgeFade={edgeFade}
|
|
/>
|
|
)}
|
|
</View>
|
|
</>
|
|
);
|
|
|
|
if (!interactive) {
|
|
return (
|
|
<View pointerEvents="none" style={[styles.stageWrap, { width, height }]}>
|
|
{content}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Pressable
|
|
onPress={toggle}
|
|
style={[styles.wrap, { width }]}
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`Visualizer showing ${mode}. Tap to switch.`}
|
|
>
|
|
{content}
|
|
</Pressable>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
wrap: {
|
|
paddingVertical: spacing.xs,
|
|
},
|
|
stageWrap: {
|
|
overflow: 'hidden',
|
|
},
|
|
caption: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
marginBottom: spacing.xs,
|
|
},
|
|
captionText: {
|
|
color: colors.textTertiary,
|
|
letterSpacing: 1.5,
|
|
fontSize: 10,
|
|
},
|
|
});
|
|
|
|
export default Visualizer;
|