597 lines
15 KiB
JavaScript
597 lines
15 KiB
JavaScript
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,
|
|
} 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 linkLabel = payload?.linkLabel ?? url;
|
|
const sectionTitle = payload?.sectionTitle ?? "Partager sur...";
|
|
const qrTitle = payload?.qrTitle ?? "Scannez le QR-code";
|
|
const qrUrl = payload?.qrUrl ?? musiclandShareUrl;
|
|
|
|
const [copied, setCopied] = useState(false);
|
|
const copyTimeout = useRef(null);
|
|
const blurProps =
|
|
Platform.OS === "android"
|
|
? { experimentalBlurMethod: "dimezisBlurView" }
|
|
: {};
|
|
|
|
const closeSheet = useCallback(() => {
|
|
return SheetManager.hide("Share");
|
|
}, []);
|
|
|
|
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);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const handleCopy = useCallback(async () => {
|
|
try {
|
|
if (copyTimeout.current) {
|
|
clearTimeout(copyTimeout.current);
|
|
}
|
|
|
|
if (isWeb && typeof navigator !== "undefined" && navigator.clipboard) {
|
|
await navigator.clipboard.writeText(url);
|
|
} else {
|
|
const Clipboard = await import("expo-clipboard");
|
|
if (typeof Clipboard?.setStringAsync === "function") {
|
|
await Clipboard.setStringAsync(url);
|
|
} else if (typeof Clipboard?.default?.setStringAsync === "function") {
|
|
await Clipboard.default.setStringAsync(url);
|
|
}
|
|
}
|
|
|
|
setCopied(true);
|
|
copyTimeout.current = setTimeout(() => {
|
|
setCopied(false);
|
|
}, 2000);
|
|
} catch (error) {
|
|
console.error("share.copy.failed", error);
|
|
}
|
|
}, [url]);
|
|
|
|
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") {
|
|
await closeSheet();
|
|
setGlobal({
|
|
shareQrModal: {
|
|
visible: true,
|
|
url: qrUrl,
|
|
title: qrTitle,
|
|
},
|
|
});
|
|
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) => <FontAwesome name="facebook" {...props} />,
|
|
target: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
|
|
},
|
|
{
|
|
key: "instagram",
|
|
label: "Partager sur Instagram",
|
|
Icon: (props) => <FontAwesome name="instagram" {...props} />,
|
|
target: `https://www.instagram.com/?url=${encodedUrl}`,
|
|
},
|
|
{
|
|
key: "x",
|
|
label: "Partager sur X",
|
|
Icon: (props) => <FontAwesome name="twitter" {...props} />,
|
|
target: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedShareText}`,
|
|
},
|
|
{
|
|
key: "email",
|
|
label: "Partager par e-mail",
|
|
Icon: (props) => <Feather name="mail" {...props} />,
|
|
target: `mailto:?subject=${encodeURIComponent(
|
|
shareTitle
|
|
)}&body=${encodedShareText}`,
|
|
},
|
|
{
|
|
key: "qr",
|
|
type: "qr",
|
|
label: "Partager par QR-code",
|
|
Icon: (props) => (
|
|
<MaterialCommunityIcons name="qrcode-scan" {...props} />
|
|
),
|
|
},
|
|
];
|
|
}, [encodedShareText, encodedUrl, payload?.options, shareTitle]);
|
|
|
|
const mobileContent = (
|
|
<View style={styles.container}>
|
|
<BlurView
|
|
style={[styles.card, styles.blurCard]}
|
|
tint="dark"
|
|
intensity={40}
|
|
{...blurProps}
|
|
>
|
|
<Text style={styles.heading}>{heading}</Text>
|
|
<View style={styles.linkRow}>
|
|
<Text numberOfLines={1} style={styles.linkText}>
|
|
{linkLabel}
|
|
</Text>
|
|
<View style={styles.linkActions}>
|
|
<Pressable
|
|
hitSlop={8}
|
|
onPress={handleCopy}
|
|
style={styles.iconButton}
|
|
>
|
|
<Feather
|
|
name={copied ? "check" : "copy"}
|
|
size={16}
|
|
color={Palette.white}
|
|
/>
|
|
</Pressable>
|
|
<Pressable
|
|
hitSlop={8}
|
|
onPress={onSystemShare}
|
|
style={styles.iconButton}
|
|
>
|
|
<Feather name="share-2" size={16} color={Palette.white} />
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
{copied ? <Text style={styles.copiedText}>Lien copié</Text> : null}
|
|
</BlurView>
|
|
|
|
<BlurView intensity={40} tint="dark" style={styles.card} {...blurProps}>
|
|
<Text style={styles.sectionHeading}>{sectionTitle}</Text>
|
|
<View style={styles.optionsList}>
|
|
{shareOptions.map((option, index) => {
|
|
const Icon = option?.Icon ?? Feather;
|
|
return (
|
|
<BlurView
|
|
key={option?.key || index}
|
|
style={styles.optionBlur}
|
|
intensity={100}
|
|
{...blurProps}
|
|
>
|
|
<Pressable
|
|
onPress={() => handleShareOption(option)}
|
|
style={styles.optionRow}
|
|
>
|
|
<View style={styles.optionIconWrapper}>
|
|
<Icon size={18} color={Palette.white} />
|
|
</View>
|
|
<Text style={styles.optionLabel}>{option?.label}</Text>
|
|
</Pressable>
|
|
</BlurView>
|
|
);
|
|
})}
|
|
</View>
|
|
</BlurView>
|
|
</View>
|
|
);
|
|
|
|
const webContent = (
|
|
<View style={styles.webContainer}>
|
|
<View style={styles.webHeader}>
|
|
<Text style={styles.webHeading}>{heading}</Text>
|
|
<Pressable
|
|
onPress={closeSheet}
|
|
hitSlop={12}
|
|
style={styles.webCloseButton}
|
|
>
|
|
<Feather name="x" size={16} color={Palette.white} />
|
|
</Pressable>
|
|
</View>
|
|
|
|
<Text style={styles.webSubheading}>{shareMessage}</Text>
|
|
|
|
<View style={styles.webContent}>
|
|
<View style={styles.webLinkCard}>
|
|
<Text style={styles.webLinkLabel}>{linkLabel}</Text>
|
|
<View style={styles.webLinkActions}>
|
|
<Pressable
|
|
onPress={handleCopy}
|
|
style={[
|
|
styles.webActionButton,
|
|
styles.webPrimaryButton,
|
|
copied && styles.webActionButtonActive,
|
|
]}
|
|
>
|
|
<Feather
|
|
name={copied ? "check" : "copy"}
|
|
size={16}
|
|
color={Palette.white}
|
|
/>
|
|
<Text style={styles.webActionLabel}>
|
|
{copied ? "Lien copié" : "Copier le lien"}
|
|
</Text>
|
|
</Pressable>
|
|
|
|
<Pressable
|
|
onPress={onSystemShare}
|
|
style={[styles.webActionButton, styles.webSecondaryButton]}
|
|
>
|
|
<Feather name="share-2" size={16} color={Palette.white} />
|
|
<Text style={styles.webActionLabel}>Partager</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.webOptionsCard}>
|
|
<Text style={styles.webSectionHeading}>{sectionTitle}</Text>
|
|
<View style={styles.webOptionsGrid}>
|
|
{shareOptions.map((option, index) => {
|
|
const Icon = option?.Icon ?? Feather;
|
|
return (
|
|
<Pressable
|
|
key={option?.key || index}
|
|
onPress={() => handleShareOption(option)}
|
|
style={styles.webOptionRow}
|
|
>
|
|
<View style={styles.webOptionIcon}>
|
|
<Icon size={18} color={Palette.white} />
|
|
</View>
|
|
<Text style={styles.webOptionLabel}>{option?.label}</Text>
|
|
<Feather
|
|
name="chevron-right"
|
|
size={16}
|
|
color="rgba(255, 255, 255, 0.6)"
|
|
/>
|
|
</Pressable>
|
|
);
|
|
})}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<AppActionSheet id="Share" webModal={isWeb} onClose={closeSheet}>
|
|
{isWeb ? webContent : mobileContent}
|
|
</AppActionSheet>
|
|
);
|
|
};
|
|
|
|
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: 20,
|
|
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;
|