share modal
This commit is contained in:
@@ -37,6 +37,7 @@ import Providers from "./src/providers";
|
|||||||
import { LogBox } from "react-native";
|
import { LogBox } from "react-native";
|
||||||
import CommentsBottomSheet from "./src/components/bottomsheets/CommentsBottomSheet";
|
import CommentsBottomSheet from "./src/components/bottomsheets/CommentsBottomSheet";
|
||||||
import "./src/utils/Sheet";
|
import "./src/utils/Sheet";
|
||||||
|
import ShareQrModalContainer from "./src/components/modal/ShareQrModalContainer";
|
||||||
|
|
||||||
console.disableYellowBox = true;
|
console.disableYellowBox = true;
|
||||||
console.reportErrorsAsExceptions = false;
|
console.reportErrorsAsExceptions = false;
|
||||||
@@ -173,6 +174,7 @@ const App = () => {
|
|||||||
<MainStack />
|
<MainStack />
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
<CommentsBottomSheet />
|
<CommentsBottomSheet />
|
||||||
|
<ShareQrModalContainer />
|
||||||
{/* </AppLayout> */}
|
{/* </AppLayout> */}
|
||||||
</MenuProvider>
|
</MenuProvider>
|
||||||
</Providers>
|
</Providers>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import React from "react";
|
|||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
import ActionSheet from "react-native-actions-sheet";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
import { isWeb } from "../hooks/useLayoutType";
|
||||||
import { gutters, Palette } from "../styles";
|
import { gutters, Palette } from "../styles";
|
||||||
|
|
||||||
const AppActionSheet = ({ children, props }) => {
|
const AppActionSheet = ({ children, props }) => {
|
||||||
@@ -19,7 +20,7 @@ const AppActionSheet = ({ children, props }) => {
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<BlurView
|
<BlurView
|
||||||
intensity={Platform.OS === "ios" ? 20 : 10}
|
intensity={Platform.OS === "ios" || isWeb ? 20 : 10}
|
||||||
style={{
|
style={{
|
||||||
paddingTop: 36,
|
paddingTop: 36,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { View, Text, Pressable, Image, Platform } from "react-native";
|
|
||||||
import React from "react";
|
|
||||||
import BorderGradient from "./BorderGradient/BorderGradient";
|
|
||||||
import { Palette, Style } from "../styles";
|
|
||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
|
import React from "react";
|
||||||
|
import { Image, Pressable, Text, View } from "react-native";
|
||||||
|
import { Palette, Style } from "../styles";
|
||||||
import { FONT_FAMILY } from "../styles/Fonts";
|
import { FONT_FAMILY } from "../styles/Fonts";
|
||||||
import { size } from "../styles/Style";
|
import { size } from "../styles/Style";
|
||||||
|
import BorderGradient from "./BorderGradient/BorderGradient";
|
||||||
|
|
||||||
const BorderGradientButton = ({
|
const BorderGradientButton = ({
|
||||||
title = "J’ai déjà mes paroles",
|
title = "J’ai déjà mes paroles",
|
||||||
@@ -45,7 +45,7 @@ const BorderGradientButton = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BlurView
|
<BlurView
|
||||||
intensity={Platform.OS === "ios" ? 40 : 10}
|
intensity={40}
|
||||||
tint={tint}
|
tint={tint}
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import Svg, { Rect } from "react-native-svg";
|
||||||
|
import { generateQrMatrix } from "../utils/generateQrMatrix";
|
||||||
|
|
||||||
|
const DEFAULT_SIZE = 220;
|
||||||
|
|
||||||
|
const QrCode = ({
|
||||||
|
value,
|
||||||
|
size = DEFAULT_SIZE,
|
||||||
|
color = "#FFFFFF",
|
||||||
|
backgroundColor = "transparent",
|
||||||
|
errorCorrectionLevel = "M",
|
||||||
|
}) => {
|
||||||
|
const { matrix } = useMemo(() => {
|
||||||
|
return generateQrMatrix({ value, errorCorrectionLevel });
|
||||||
|
}, [value, errorCorrectionLevel]);
|
||||||
|
|
||||||
|
const cellSize = useMemo(() => {
|
||||||
|
if (!matrix || !matrix.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return size / matrix.length;
|
||||||
|
}, [matrix, size]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
backgroundColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
||||||
|
{matrix.map((row, rowIndex) => {
|
||||||
|
return row.map((isDark, colIndex) => {
|
||||||
|
if (!isDark) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = `${rowIndex}-${colIndex}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Rect
|
||||||
|
key={key}
|
||||||
|
x={colIndex * cellSize}
|
||||||
|
y={rowIndex * cellSize}
|
||||||
|
width={cellSize}
|
||||||
|
height={cellSize}
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
</Svg>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QrCode;
|
||||||
|
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
import {
|
||||||
|
Feather,
|
||||||
|
FontAwesome,
|
||||||
|
MaterialCommunityIcons,
|
||||||
|
} from "@expo/vector-icons";
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
@@ -15,21 +20,18 @@ import {
|
|||||||
View,
|
View,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { SheetManager } from "react-native-actions-sheet";
|
import { SheetManager } from "react-native-actions-sheet";
|
||||||
import {
|
|
||||||
Feather,
|
|
||||||
FontAwesome,
|
|
||||||
MaterialCommunityIcons,
|
|
||||||
} from "@expo/vector-icons";
|
|
||||||
|
|
||||||
import AppActionSheet from "../AppActionSheet";
|
import { BlurView } from "expo-blur";
|
||||||
import { Palette } from "../../styles";
|
import { setGlobal } from "reactn";
|
||||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
|
||||||
import { isWeb } from "../../hooks/useLayoutType";
|
|
||||||
import {
|
import {
|
||||||
musiclandShareHeading,
|
musiclandShareHeading,
|
||||||
musiclandShareMessage,
|
musiclandShareMessage,
|
||||||
musiclandShareUrl,
|
musiclandShareUrl,
|
||||||
} from "../../data";
|
} 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 ShareModal = ({ payload }) => {
|
||||||
const heading = payload?.heading ?? musiclandShareHeading;
|
const heading = payload?.heading ?? musiclandShareHeading;
|
||||||
@@ -38,9 +40,12 @@ const ShareModal = ({ payload }) => {
|
|||||||
const shareMessage = payload?.shareMessage ?? musiclandShareMessage;
|
const shareMessage = payload?.shareMessage ?? musiclandShareMessage;
|
||||||
const linkLabel = payload?.linkLabel ?? url;
|
const linkLabel = payload?.linkLabel ?? url;
|
||||||
const sectionTitle = payload?.sectionTitle ?? "Partager sur...";
|
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 [copied, setCopied] = useState(false);
|
||||||
const copyTimeout = useRef(null);
|
const copyTimeout = useRef(null);
|
||||||
|
const qrOpenTimeout = useRef(null);
|
||||||
|
|
||||||
const closeSheet = useCallback(() => {
|
const closeSheet = useCallback(() => {
|
||||||
SheetManager.hide("Share");
|
SheetManager.hide("Share");
|
||||||
@@ -61,6 +66,9 @@ const ShareModal = ({ payload }) => {
|
|||||||
if (copyTimeout.current) {
|
if (copyTimeout.current) {
|
||||||
clearTimeout(copyTimeout.current);
|
clearTimeout(copyTimeout.current);
|
||||||
}
|
}
|
||||||
|
if (qrOpenTimeout.current) {
|
||||||
|
clearTimeout(qrOpenTimeout.current);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -123,6 +131,22 @@ const ShareModal = ({ payload }) => {
|
|||||||
|
|
||||||
const handleShareOption = useCallback(
|
const handleShareOption = useCallback(
|
||||||
async (option) => {
|
async (option) => {
|
||||||
|
if (option?.type === "qr") {
|
||||||
|
const qrTitleValue = qrTitle;
|
||||||
|
|
||||||
|
closeSheet();
|
||||||
|
qrOpenTimeout.current = setTimeout(() => {
|
||||||
|
setGlobal({
|
||||||
|
shareQrModal: {
|
||||||
|
visible: true,
|
||||||
|
url: qrUrl,
|
||||||
|
title: qrTitleValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const target = option?.target;
|
const target = option?.target;
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
@@ -151,7 +175,7 @@ const ShareModal = ({ payload }) => {
|
|||||||
await handleSystemShare();
|
await handleSystemShare();
|
||||||
closeSheet();
|
closeSheet();
|
||||||
},
|
},
|
||||||
[closeSheet, handleSystemShare]
|
[closeSheet, handleSystemShare, qrTitle, qrUrl]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSystemShare = useCallback(async () => {
|
const onSystemShare = useCallback(async () => {
|
||||||
@@ -193,11 +217,11 @@ const ShareModal = ({ payload }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "qr",
|
key: "qr",
|
||||||
|
type: "qr",
|
||||||
label: "Partager par QR-code",
|
label: "Partager par QR-code",
|
||||||
Icon: (props) => (
|
Icon: (props) => (
|
||||||
<MaterialCommunityIcons name="qrcode-scan" {...props} />
|
<MaterialCommunityIcons name="qrcode-scan" {...props} />
|
||||||
),
|
),
|
||||||
target: `https://api.qrserver.com/v1/create-qr-code/?size=512x512&data=${encodedUrl}`,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [encodedShareText, encodedUrl, payload?.options, shareTitle]);
|
}, [encodedShareText, encodedUrl, payload?.options, shareTitle]);
|
||||||
@@ -205,7 +229,12 @@ const ShareModal = ({ payload }) => {
|
|||||||
return (
|
return (
|
||||||
<AppActionSheet id="Share">
|
<AppActionSheet id="Share">
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<View style={styles.card}>
|
<BlurView
|
||||||
|
style={[styles.card, styles.blurCard]}
|
||||||
|
tint="dark"
|
||||||
|
intensity={40}
|
||||||
|
experimentalBlurMethod="dimezisBlurView"
|
||||||
|
>
|
||||||
<Text style={styles.heading}>{heading}</Text>
|
<Text style={styles.heading}>{heading}</Text>
|
||||||
<View style={styles.linkRow}>
|
<View style={styles.linkRow}>
|
||||||
<Text numberOfLines={1} style={styles.linkText}>
|
<Text numberOfLines={1} style={styles.linkText}>
|
||||||
@@ -232,19 +261,26 @@ const ShareModal = ({ payload }) => {
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{copied ? (
|
{copied ? <Text style={styles.copiedText}>Lien copié</Text> : null}
|
||||||
<Text style={styles.copiedText}>Lien copie</Text>
|
</BlurView>
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.card}>
|
<BlurView
|
||||||
|
intensity={70}
|
||||||
|
experimentalBlurMethod="dimezisBlurView"
|
||||||
|
style={styles.card}
|
||||||
|
>
|
||||||
<Text style={styles.sectionHeading}>{sectionTitle}</Text>
|
<Text style={styles.sectionHeading}>{sectionTitle}</Text>
|
||||||
<View style={styles.optionsList}>
|
<View style={styles.optionsList}>
|
||||||
{shareOptions.map((option) => {
|
{shareOptions.map((option, index) => {
|
||||||
const Icon = option?.Icon ?? Feather;
|
const Icon = option?.Icon ?? Feather;
|
||||||
return (
|
return (
|
||||||
|
<BlurView
|
||||||
|
key={option?.key || index}
|
||||||
|
style={styles.optionBlur}
|
||||||
|
intensity={40}
|
||||||
|
experimentalBlurMethod="dimezisBlurView"
|
||||||
|
>
|
||||||
<Pressable
|
<Pressable
|
||||||
key={option?.key}
|
|
||||||
onPress={() => handleShareOption(option)}
|
onPress={() => handleShareOption(option)}
|
||||||
style={styles.optionRow}
|
style={styles.optionRow}
|
||||||
>
|
>
|
||||||
@@ -253,10 +289,11 @@ const ShareModal = ({ payload }) => {
|
|||||||
</View>
|
</View>
|
||||||
<Text style={styles.optionLabel}>{option?.label}</Text>
|
<Text style={styles.optionLabel}>{option?.label}</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
</BlurView>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</BlurView>
|
||||||
</View>
|
</View>
|
||||||
</AppActionSheet>
|
</AppActionSheet>
|
||||||
);
|
);
|
||||||
@@ -267,7 +304,8 @@ const styles = StyleSheet.create({
|
|||||||
gap: 24,
|
gap: 24,
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
backgroundColor: Palette.glass,
|
// backgroundColor: Palette.glass,
|
||||||
|
overflow: "hidden",
|
||||||
borderRadius: 24,
|
borderRadius: 24,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "rgba(255, 255, 255, 0.08)",
|
borderColor: "rgba(255, 255, 255, 0.08)",
|
||||||
@@ -275,6 +313,9 @@ const styles = StyleSheet.create({
|
|||||||
paddingVertical: 18,
|
paddingVertical: 18,
|
||||||
gap: 16,
|
gap: 16,
|
||||||
},
|
},
|
||||||
|
blurCard: {
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
heading: {
|
heading: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Palette.white,
|
color: Palette.white,
|
||||||
@@ -325,11 +366,15 @@ const styles = StyleSheet.create({
|
|||||||
optionsList: {
|
optionsList: {
|
||||||
gap: 12,
|
gap: 12,
|
||||||
},
|
},
|
||||||
|
optionBlur: {
|
||||||
|
borderRadius: 18,
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
optionRow: {
|
optionRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
backgroundColor: "rgba(255, 255, 255, 0.04)",
|
backgroundColor: "rgba(255, 255, 255, 0.04)",
|
||||||
borderRadius: 18,
|
// borderRadius: 18,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
paddingVertical: 14,
|
paddingVertical: 14,
|
||||||
gap: 14,
|
gap: 14,
|
||||||
@@ -351,4 +396,3 @@ const styles = StyleSheet.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default ShareModal;
|
export default ShareModal;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { BlurView } from "expo-blur";
|
||||||
|
import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
|
||||||
|
import { Feather } from "@expo/vector-icons";
|
||||||
|
import { Portal } from "@gorhom/portal";
|
||||||
|
|
||||||
|
import QrCode from "../QrCode";
|
||||||
|
import { Palette } from "../../styles";
|
||||||
|
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||||
|
|
||||||
|
const ShareQrModal = ({ visible, onClose, url, title = "Scannez le QR-code" }) => {
|
||||||
|
const shareUrl = useMemo(() => url, [url]);
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blurIntensity = Platform.select({ ios: 40, default: 20 });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<View style={StyleSheet.absoluteFill} pointerEvents="box-none">
|
||||||
|
<Pressable
|
||||||
|
onPress={onClose}
|
||||||
|
style={[StyleSheet.absoluteFill, styles.backdrop]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<View style={styles.centerWrapper} pointerEvents="box-none">
|
||||||
|
<BlurView intensity={blurIntensity} tint="dark" style={styles.modalCard}>
|
||||||
|
<Pressable onPress={onClose} style={styles.closeButton} hitSlop={10}>
|
||||||
|
<Feather name="x" size={20} color={Palette.white} />
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
<View style={styles.content}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<Text style={styles.subtitle}>
|
||||||
|
Partage ce code pour guider tes contacts directement vers MusicLand.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles.qrWrapper}>
|
||||||
|
<QrCode value={shareUrl} size={220} backgroundColor="transparent" />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text numberOfLines={1} ellipsizeMode="middle" style={styles.linkText}>
|
||||||
|
{shareUrl}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</BlurView>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
backdrop: {
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.65)",
|
||||||
|
},
|
||||||
|
centerWrapper: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: 24,
|
||||||
|
},
|
||||||
|
modalCard: {
|
||||||
|
width: 360,
|
||||||
|
maxWidth: "90%",
|
||||||
|
borderRadius: 28,
|
||||||
|
overflow: "hidden",
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingVertical: 32,
|
||||||
|
backgroundColor: Palette.glass,
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 18,
|
||||||
|
right: 18,
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
borderRadius: 18,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.12)",
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 18,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: Palette.white,
|
||||||
|
fontFamily: FONT_FAMILY.HelveticaNeueMedium,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: Palette.white,
|
||||||
|
opacity: 0.8,
|
||||||
|
textAlign: "center",
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
},
|
||||||
|
qrWrapper: {
|
||||||
|
padding: 16,
|
||||||
|
borderRadius: 24,
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
||||||
|
},
|
||||||
|
linkText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: Palette.white,
|
||||||
|
opacity: 0.9,
|
||||||
|
textAlign: "center",
|
||||||
|
fontFamily: FONT_FAMILY.InterMedium,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ShareQrModal;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useGlobal } from "reactn";
|
||||||
|
|
||||||
|
import { musiclandShareUrl } from "../../data";
|
||||||
|
import ShareQrModal from "./ShareQrModal";
|
||||||
|
|
||||||
|
const ShareQrModalContainer = () => {
|
||||||
|
const [shareQrModal, setShareQrModal] = useGlobal("shareQrModal");
|
||||||
|
|
||||||
|
const { visible, url, title } = shareQrModal || {};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setShareQrModal({
|
||||||
|
visible: false,
|
||||||
|
url: null,
|
||||||
|
title: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ShareQrModal
|
||||||
|
visible={!!visible}
|
||||||
|
url={url || musiclandShareUrl}
|
||||||
|
title={title || "Scannez le QR-code"}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ShareQrModalContainer;
|
||||||
@@ -8,6 +8,12 @@ export default {
|
|||||||
webBackgroundImg: null,
|
webBackgroundImg: null,
|
||||||
webLayoutMode: "default",
|
webLayoutMode: "default",
|
||||||
|
|
||||||
|
shareQrModal: {
|
||||||
|
visible: false,
|
||||||
|
url: null,
|
||||||
|
title: "",
|
||||||
|
},
|
||||||
|
|
||||||
_config: {
|
_config: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: Palette.primary,
|
primary: Palette.primary,
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ const HitParade = () => {
|
|||||||
backgroundImg={isWeb ? background.libraryBgWeb : background.hitParadeBG}
|
backgroundImg={isWeb ? background.libraryBgWeb : background.hitParadeBG}
|
||||||
headerType="NONE"
|
headerType="NONE"
|
||||||
width="100%"
|
width="100%"
|
||||||
maxWidth={null}
|
maxWidth={800}
|
||||||
blurIntensity={isWeb ? 2 : 0}
|
blurIntensity={isWeb ? 2 : 0}
|
||||||
>
|
>
|
||||||
<View style={{ gap: 12, flex: 1 }}>
|
<View style={{ gap: 12, flex: 1 }}>
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
import { SheetManager } from "react-native-actions-sheet";
|
||||||
|
import { useGlobal } from "reactn";
|
||||||
|
import MoreMenu from "../../../components/MoreMenu";
|
||||||
|
import { projectsRef } from "../../../config/firebase";
|
||||||
import { Routes } from "../../../navigation";
|
import { Routes } from "../../../navigation";
|
||||||
import { navigate } from "../../../navigation/NavigationService";
|
import { navigate } from "../../../navigation/NavigationService";
|
||||||
import { useUser } from "../../../providers/UserDataProvider";
|
import { useUser } from "../../../providers/UserDataProvider";
|
||||||
import CardContainer from "./CardContainer";
|
import CardContainer from "./CardContainer";
|
||||||
import MusicCard from "./MusicCard";
|
import MusicCard from "./MusicCard";
|
||||||
import MoreMenu from "../../../components/MoreMenu";
|
|
||||||
|
|
||||||
const MyMusic = () => {
|
const MyMusic = () => {
|
||||||
const { userProjects = [] } = useUser();
|
const { userProjects = [] } = useUser();
|
||||||
const [menuPosition, setMenuPosition] = useState(null);
|
const [menuPosition, setMenuPosition] = useState(null);
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const [selectedProjectId, setSelectedProjectId] = useState(null);
|
const [selectedProjectId, setSelectedProjectId] = useState(null);
|
||||||
const projects = Array.isArray(userProjects) ? userProjects.slice(0, 3) : [];
|
const projects = Array.isArray(userProjects) ? userProjects.slice(0, 3) : [];
|
||||||
|
|
||||||
|
const [, setTooltip] = useGlobal("_tooltip");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContainer
|
<CardContainer
|
||||||
label="Mes musiques"
|
label="Mes musiques"
|
||||||
@@ -39,8 +44,7 @@ const MyMusic = () => {
|
|||||||
setSelectedProjectId(p.id);
|
setSelectedProjectId(p.id);
|
||||||
setMenuPosition(posTop);
|
setMenuPosition(posTop);
|
||||||
setShowMenu(
|
setShowMenu(
|
||||||
(prev) =>
|
(prev) => !prev || posTop?.top !== (menuPosition?.top ?? null)
|
||||||
!prev || posTop?.top !== (menuPosition?.top ?? null),
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -52,6 +56,34 @@ const MyMusic = () => {
|
|||||||
onClose={() => setShowMenu(false)}
|
onClose={() => setShowMenu(false)}
|
||||||
inPlaylist={false}
|
inPlaylist={false}
|
||||||
projectId={selectedProjectId}
|
projectId={selectedProjectId}
|
||||||
|
extraItems={[
|
||||||
|
{
|
||||||
|
label: "Supprimer",
|
||||||
|
onPress: () =>
|
||||||
|
SheetManager.show("Delete", {
|
||||||
|
payload: {
|
||||||
|
title: "Supprimer le projet",
|
||||||
|
message:
|
||||||
|
"Cette action supprimera définitivement ce projet.",
|
||||||
|
onConfirm: async () => {
|
||||||
|
try {
|
||||||
|
if (!selectedProjectId) return;
|
||||||
|
await projectsRef.doc(selectedProjectId).delete();
|
||||||
|
setTooltip({
|
||||||
|
type: "success",
|
||||||
|
text: "Projet supprimé",
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setTooltip({
|
||||||
|
type: "error",
|
||||||
|
text: e?.message || "Suppression impossible",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</CardContainer>
|
</CardContainer>
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
import {
|
import { BlurView } from "expo-blur";
|
||||||
View,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
Image,
|
|
||||||
Pressable,
|
|
||||||
Platform,
|
|
||||||
} from "react-native";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { Platform, Pressable, Text, TextInput, View } from "react-native";
|
||||||
|
import EyeSlashSVG from "../../../assets/UI/EyeSlashSVG";
|
||||||
|
import EyeSVG from "../../../assets/UI/EyeSVG";
|
||||||
import { Palette, Style } from "../../../styles";
|
import { Palette, Style } from "../../../styles";
|
||||||
import { FONT_FAMILY } from "../../../styles/Fonts";
|
import { FONT_FAMILY } from "../../../styles/Fonts";
|
||||||
import { BlurView } from "expo-blur";
|
|
||||||
import { icons } from "../../../assets";
|
|
||||||
|
|
||||||
const EditInput = ({
|
const EditInput = ({
|
||||||
label = "",
|
label = "",
|
||||||
@@ -79,7 +73,7 @@ const EditInput = ({
|
|||||||
/>
|
/>
|
||||||
{type === "password" && (
|
{type === "password" && (
|
||||||
<Pressable onPress={() => setShowPassword(!showPassword)}>
|
<Pressable onPress={() => setShowPassword(!showPassword)}>
|
||||||
<Image source={icons.eye} />
|
{showPassword ? <EyeSVG /> : <EyeSlashSVG />}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)}
|
)}
|
||||||
</BlurView>
|
</BlurView>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import QRCode from "qrcode-terminal/vendor/QRCode";
|
||||||
|
import QRErrorCorrectLevel from "qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel";
|
||||||
|
|
||||||
|
const getErrorLevel = (level) => {
|
||||||
|
if (typeof level === "number") {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = String(level || "M").toUpperCase();
|
||||||
|
return QRErrorCorrectLevel?.[normalized] ?? QRErrorCorrectLevel.M;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateQrMatrix = ({
|
||||||
|
value,
|
||||||
|
errorCorrectionLevel = "M",
|
||||||
|
}) => {
|
||||||
|
if (!value) {
|
||||||
|
throw new Error("generateQrMatrix requires a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
const qr = new QRCode(-1, getErrorLevel(errorCorrectionLevel));
|
||||||
|
qr.addData(value);
|
||||||
|
qr.make();
|
||||||
|
|
||||||
|
const size = qr.getModuleCount();
|
||||||
|
const matrix = new Array(size);
|
||||||
|
|
||||||
|
for (let row = 0; row < size; row += 1) {
|
||||||
|
matrix[row] = new Array(size);
|
||||||
|
for (let col = 0; col < size; col += 1) {
|
||||||
|
matrix[row][col] = qr.isDark(row, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { matrix, size };
|
||||||
|
};
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ const defaultPayload = {
|
|||||||
url: musiclandShareUrl,
|
url: musiclandShareUrl,
|
||||||
shareTitle: "MusicLand",
|
shareTitle: "MusicLand",
|
||||||
shareMessage: musiclandShareMessage,
|
shareMessage: musiclandShareMessage,
|
||||||
|
qrUrl: musiclandShareUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openShareSheet = (payload = {}) => {
|
export const openShareSheet = (payload = {}) => {
|
||||||
@@ -21,4 +22,3 @@ export const openShareSheet = (payload = {}) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user