import { Feather, FontAwesome, MaterialCommunityIcons } from '@expo/vector-icons' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Linking, Platform, Pressable, Share, StyleSheet, Text, View, findNodeHandle, } from 'react-native' import { SheetManager } from 'react-native-actions-sheet' import { BlurView } from 'expo-blur' import { setGlobal } from 'reactn' import { musiclandShareHeading, musiclandShareMessage, musiclandShareUrl } from '../../data' import { isWeb } from '../../hooks/useLayoutType' import { Palette } from '../../styles' import { FONT_FAMILY } from '../../styles/Fonts' import AppActionSheet from '../AppActionSheet' const ShareModal = ({ payload }) => { const heading = payload?.heading ?? musiclandShareHeading const url = payload?.url ?? musiclandShareUrl const shareTitle = payload?.shareTitle ?? heading const shareMessage = payload?.shareMessage ?? musiclandShareMessage const copyUrl = payload?.copyUrl ?? payload?.qrUrl ?? payload?.url ?? musiclandShareUrl const linkLabel = payload?.linkLabel ?? copyUrl const sectionTitle = payload?.sectionTitle ?? 'Partager sur...' const qrTitle = payload?.qrTitle ?? musiclandShareHeading const qrUrl = payload?.qrUrl ?? musiclandShareUrl const [copied, setCopied] = useState(false) const copyTimeout = useRef(null) const shareQrTimeoutRef = useRef(null) const modalRef = useRef(null) const [webVisible, setWebVisible] = useState(true) const blurProps = Platform.OS === 'android' ? { experimentalBlurMethod: 'dimezisBlurView' } : {} const closeSheet = useCallback(() => { if (isWeb) { setWebVisible(false) } try { const maybePromise = SheetManager.hide('Share') return Promise.resolve(maybePromise) } catch (error) { console.error('share.sheet.close', error) return Promise.resolve() } }, []) useEffect(() => { if (isWeb) { setWebVisible(true) } }, [payload]) const shareText = useMemo(() => { return [shareMessage, url].filter(Boolean).join(' ') }, [shareMessage, url]) const encodedUrl = useMemo(() => encodeURIComponent(url), [url]) const encodedShareText = useMemo(() => encodeURIComponent(shareText), [shareText]) useEffect(() => { return () => { if (copyTimeout.current) { clearTimeout(copyTimeout.current) } } }, []) useEffect(() => { if (!isWeb || typeof document === 'undefined' || !webVisible) { return } const handlePointerDown = (event) => { const node = modalRef.current if (!node) { return } const domNode = findNodeHandle(node) if (domNode && typeof domNode.contains === 'function') { if (domNode.contains(event.target)) { return } } if (domNode === event.target) { return } closeSheet() } const handleKeyDown = (event) => { if (event.key === 'Escape') { closeSheet() } } document.addEventListener('pointerdown', handlePointerDown) document.addEventListener('keydown', handleKeyDown) return () => { document.removeEventListener('pointerdown', handlePointerDown) document.removeEventListener('keydown', handleKeyDown) } }, [closeSheet, webVisible]) const handleCopy = useCallback(async () => { try { if (copyTimeout.current) { clearTimeout(copyTimeout.current) } if (isWeb && typeof navigator !== 'undefined' && navigator.clipboard) { await navigator.clipboard.writeText(copyUrl) } else { const Clipboard = await import('expo-clipboard') if (typeof Clipboard?.setStringAsync === 'function') { await Clipboard.setStringAsync(copyUrl) } else if (typeof Clipboard?.default?.setStringAsync === 'function') { await Clipboard.default.setStringAsync(copyUrl) } } setCopied(true) copyTimeout.current = setTimeout(() => { setCopied(false) }, 2000) } catch (error) { console.error('share.copy.failed', error) } }, [copyUrl]) const handleSystemShare = useCallback(async () => { if (Platform.OS === 'web' && typeof navigator !== 'undefined') { try { if (typeof navigator.share === 'function') { await navigator.share({ url, text: shareText, title: shareTitle, }) return } } catch (error) { console.error('share.system.web', error) } } try { await Share.share( Platform.select({ ios: url ? { url, message: shareText, title: shareTitle } : { message: shareText, title: shareTitle }, android: { message: shareText, title: shareTitle }, default: { message: shareText, title: shareTitle }, }) ) } catch (error) { console.error('share.system.native', error) } }, [shareText, shareTitle, url]) const handleShareOption = useCallback( async (option) => { if (option?.type === 'qr') { console.log('share.modal.handleShareOption', { key: option?.key, platform: Platform.OS, }) if (shareQrTimeoutRef.current) { clearTimeout(shareQrTimeoutRef.current) } const openQrModal = () => { console.log('share.modal.openQrModal', { platform: Platform.OS, url: qrUrl, title: qrTitle, }) shareQrTimeoutRef.current = null setGlobal({ shareQrModal: { visible: true, url: qrUrl, title: qrTitle, }, }) } if (Platform.OS === 'web') { console.log('share.modal.scheduleQr', { platform: Platform.OS }) const maybePromise = closeSheet() shareQrTimeoutRef.current = setTimeout(openQrModal, 80) if (maybePromise && typeof maybePromise.catch === 'function') { maybePromise.catch((error) => { console.error('share.modal.closeSheet.error', error) }) } } else { await closeSheet() openQrModal() } return } const target = option?.target if (!target) { await handleSystemShare() await closeSheet() return } try { if (Platform.OS === 'web') { window.open(target, '_blank', 'noopener,noreferrer') await closeSheet() return } const supported = await Linking.canOpenURL(target) if (supported) { await Linking.openURL(target) await closeSheet() return } } catch (error) { console.error(`share.option.error.${option?.key || 'unknown'}`, error) } await handleSystemShare() await closeSheet() }, [closeSheet, handleSystemShare, qrTitle, qrUrl] ) const onSystemShare = useCallback(async () => { await handleSystemShare() await closeSheet() }, [closeSheet, handleSystemShare]) const shareOptions = useMemo(() => { if (Array.isArray(payload?.options) && payload.options.length) { return payload.options } return [ { key: 'facebook', label: 'Partager sur Facebook', Icon: (props) => , target: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`, }, { key: 'instagram', label: 'Partager sur Instagram', Icon: (props) => , target: `https://www.instagram.com/?url=${encodedUrl}`, }, { key: 'x', label: 'Partager sur X', Icon: (props) => , target: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedShareText}`, }, { key: 'email', label: 'Partager par e-mail', Icon: (props) => , target: `mailto:?subject=${encodeURIComponent(shareTitle)}&body=${encodedShareText}`, }, { key: 'qr', type: 'qr', label: 'Partager par QR-code', Icon: (props) => , }, ] }, [encodedShareText, encodedUrl, payload?.options, shareTitle]) const mobileContent = ( {heading} {linkLabel} {copied ? Lien copiƩ : null} {sectionTitle} {shareOptions.map((option, index) => { const Icon = option?.Icon ?? Feather return ( handleShareOption(option)} style={styles.optionRow}> {option?.label} ) })} ) if (isWeb && !webVisible) { return null } return ( {isWeb ? ( {heading} {shareMessage} {linkLabel} {copied ? 'Lien copiƩ' : 'Copier le lien'} {/* Partager */} {sectionTitle} {shareOptions.map((option, index) => { const Icon = option?.Icon ?? Feather return ( handleShareOption(option)} style={styles.webOptionRow} > {option?.label} ) })} ) : ( mobileContent )} ) } const styles = StyleSheet.create({ container: { gap: 24, }, card: { backgroundColor: Palette.glass, overflow: 'hidden', borderRadius: 24, borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.08)', paddingHorizontal: 20, paddingVertical: 18, gap: 16, }, blurCard: { overflow: 'hidden', position: 'relative', }, closeButton: { position: 'absolute', top: 16, right: 16, width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(255, 255, 255, 0.12)', zIndex: 2, }, heading: { fontSize: 16, color: Palette.white, textAlign: 'center', fontFamily: FONT_FAMILY.HelveticaNeueMedium, }, linkRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(255, 255, 255, 0.06)', borderRadius: 16, paddingHorizontal: 14, paddingVertical: 10, gap: 12, }, linkText: { flex: 1, color: Palette.white, fontSize: 15, fontFamily: FONT_FAMILY.InterMedium, }, linkActions: { flexDirection: 'row', alignItems: 'center', gap: 8, }, iconButton: { width: 32, height: 32, borderRadius: 16, backgroundColor: 'rgba(255, 255, 255, 0.08)', alignItems: 'center', justifyContent: 'center', }, copiedText: { fontSize: 12, textAlign: 'center', color: Palette.white, opacity: 0.8, fontFamily: FONT_FAMILY.InterMedium, }, sectionHeading: { fontSize: 18, color: Palette.white, fontFamily: FONT_FAMILY.HelveticaNeueMedium, textAlign: 'center', }, optionsList: { gap: 12, }, optionBlur: { borderRadius: 18, overflow: 'hidden', }, optionRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(255, 255, 255, 0.04)', // borderRadius: 18, paddingHorizontal: 14, paddingVertical: 14, gap: 14, }, optionIconWrapper: { width: 36, height: 36, borderRadius: 18, backgroundColor: 'rgba(255, 255, 255, 0.08)', alignItems: 'center', justifyContent: 'center', }, optionLabel: { flex: 1, fontSize: 15, color: Palette.white, fontFamily: FONT_FAMILY.InterMedium, }, webContainer: { gap: 24, }, webHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 12, }, webHeading: { flex: 1, fontSize: 24, color: Palette.white, fontFamily: FONT_FAMILY.HelveticaNeueMedium, }, webCloseButton: { width: 32, height: 32, borderRadius: 16, backgroundColor: 'rgba(255, 255, 255, 0.08)', alignItems: 'center', justifyContent: 'center', }, webSubheading: { fontSize: 14, color: 'rgba(255, 255, 255, 0.65)', fontFamily: FONT_FAMILY.InterMedium, }, webContent: { gap: 20, }, webLinkCard: { backgroundColor: 'rgba(255, 255, 255, 0.06)', borderRadius: 20, paddingHorizontal: 20, paddingVertical: 18, gap: 16, }, webLinkLabel: { color: Palette.white, fontSize: 15, fontFamily: FONT_FAMILY.InterMedium, overflow: 'hidden', }, webLinkActions: { flexDirection: 'row', alignItems: 'center', gap: 12, flexWrap: 'wrap', }, webActionButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10, paddingHorizontal: 18, paddingVertical: 12, borderRadius: 16, }, webPrimaryButton: { backgroundColor: Palette.primary, }, webSecondaryButton: { backgroundColor: 'rgba(255, 255, 255, 0.08)', }, webActionButtonActive: { backgroundColor: Palette.darkRadioactivGreen, }, webActionLabel: { fontSize: 14, color: Palette.white, fontFamily: FONT_FAMILY.InterMedium, }, webOptionsCard: { backgroundColor: 'rgba(255, 255, 255, 0.06)', borderRadius: 20, paddingHorizontal: 20, paddingVertical: 22, gap: 16, }, webSectionHeading: { fontSize: 18, color: Palette.white, fontFamily: FONT_FAMILY.HelveticaNeueMedium, }, webOptionsGrid: { gap: 12, }, webOptionRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 12, backgroundColor: 'rgba(255, 255, 255, 0.04)', borderRadius: 16, paddingHorizontal: 18, paddingVertical: 14, }, webOptionIcon: { width: 36, height: 36, borderRadius: 18, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(255, 255, 255, 0.08)', marginRight: 4, }, webOptionLabel: { flex: 1, fontSize: 15, color: Palette.white, fontFamily: FONT_FAMILY.InterMedium, }, }) export default ShareModal