nodeshiftmusicburmalda/components/ShareTrackModal.tsx

107 lines
4.6 KiB
TypeScript
Raw Normal View History

2026-05-08 02:54:57 +05:00
/**
2026-05-08 03:53:51 +05:00
* ShareTrackModal — share + QR co-listening (no backend)
2026-05-08 02:54:57 +05:00
*/
import { useState } from 'react';
2026-05-08 03:53:51 +05:00
import { View, Text, StyleSheet, Pressable, Modal, Alert, Share, Platform } from 'react-native';
2026-05-08 02:54:57 +05:00
import * as Clipboard from 'expo-clipboard';
import * as Haptics from 'expo-haptics';
import { BlurView } from 'expo-blur';
import { Image } from 'expo-image';
import Svg, { Path } from 'react-native-svg';
import { Colors, Font, Radius } from '../constants/theme';
import { Track, coverUrl } from '../services/api';
interface Props {
track: Track;
progress: number;
visible: boolean;
onClose: () => void;
}
function buildDeepLink(trackId: number, position: number) {
return `burmalda://listen?tid=${trackId}&ts=${Math.floor(position)}`;
}
export default function ShareTrackModal({ track, progress, visible, onClose }: Props) {
const [showQR, setShowQR] = useState(false);
const cover = coverUrl(track.cover_url);
const deepLink = buildDeepLink(track.id, progress);
const shareText = `${track.artist} — ${track.title}\n${deepLink}`;
const handleShare = async () => {
2026-05-08 03:53:51 +05:00
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
2026-05-08 02:54:57 +05:00
try {
2026-05-08 03:53:51 +05:00
await Share.share({ message: shareText, title: `${track.artist} — ${track.title}` });
2026-05-08 02:54:57 +05:00
} catch {}
};
const handleCopy = async () => {
2026-05-08 03:53:51 +05:00
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
try {
await Clipboard.setStringAsync(deepLink);
Alert.alert('Скопировано', 'Ссылка скопирована');
} catch {}
2026-05-08 02:54:57 +05:00
};
return (
<Modal visible={visible} transparent animationType="slide" onRequestClose={onClose}>
<Pressable style={s.backdrop} onPress={onClose} />
<BlurView intensity={40} tint="dark" style={s.sheet}>
<View style={s.handle} />
<View style={s.trackRow}>
{cover
? <Image source={{ uri: cover }} style={s.cover} contentFit="cover" />
: <View style={[s.cover, { backgroundColor: Colors.elevated }]} />
}
<View style={{ flex: 1 }}>
<Text style={s.title} numberOfLines={1}>{track.title}</Text>
<Text style={s.artist} numberOfLines={1}>{track.artist}</Text>
</View>
</View>
<View style={s.actions}>
2026-05-08 03:53:51 +05:00
<Pressable style={s.actionBtn} onPress={handleShare}>
<Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
<Path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8M16 6l-4-4-4 4M12 2v13"
stroke={Colors.text} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
</Svg>
<Text style={s.actionLabel}>Поделиться</Text>
</Pressable>
<Pressable style={s.actionBtn} onPress={handleCopy}>
<Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
<Path d="M8 16H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2M16 8h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2v-2"
stroke={Colors.text} strokeWidth={2} strokeLinecap="round" />
</Svg>
<Text style={s.actionLabel}>Копировать</Text>
</Pressable>
2026-05-08 02:54:57 +05:00
</View>
<Text style={s.hint}>
2026-05-08 03:53:51 +05:00
Ссылка: {deepLink}
2026-05-08 02:54:57 +05:00
</Text>
<Pressable style={s.closeBtn} onPress={onClose}>
<Text style={s.closeTxt}>Закрыть</Text>
</Pressable>
</BlurView>
</Modal>
);
}
const s = StyleSheet.create({
2026-05-08 03:53:51 +05:00
backdrop: { flex: 1 },
sheet: { borderTopLeftRadius: 24, borderTopRightRadius: 24, padding: 24, paddingBottom: 40, overflow: 'hidden', borderTopWidth: 1, borderColor: 'rgba(255,255,255,0.08)' },
handle: { width: 36, height: 4, borderRadius: 2, backgroundColor: 'rgba(255,255,255,0.3)', alignSelf: 'center', marginBottom: 20 },
trackRow: { flexDirection: 'row', alignItems: 'center', gap: 14, marginBottom: 24 },
cover: { width: 52, height: 52, borderRadius: Radius.sm },
title: { color: Colors.text, fontSize: Font.md, fontWeight: '700' },
artist: { color: 'rgba(255,255,255,0.55)', fontSize: Font.sm, marginTop: 2 },
actions: { flexDirection: 'row', gap: 10, marginBottom: 16 },
actionBtn: { flex: 1, alignItems: 'center', gap: 8, backgroundColor: 'rgba(255,255,255,0.08)', borderRadius: 14, paddingVertical: 16 },
actionLabel: { color: Colors.text, fontSize: 12, fontWeight: '600' },
hint: { color: 'rgba(255,255,255,0.3)', fontSize: 10, textAlign: 'center', marginBottom: 20 },
closeBtn: { paddingVertical: 14, alignItems: 'center', backgroundColor: 'rgba(255,255,255,0.08)', borderRadius: 14 },
closeTxt: { color: Colors.text, fontSize: Font.md, fontWeight: '600' },
2026-05-08 02:54:57 +05:00
});