fix tickets add reort mail and design fixes
This commit is contained in:
@@ -1,26 +1,38 @@
|
||||
import { Portal } from "@gorhom/portal";
|
||||
import { BlurView } from "expo-blur";
|
||||
import React from "react";
|
||||
import React, { useCallback } from "react";
|
||||
import { Platform, Pressable, StyleSheet, View } from "react-native";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
import ActionSheet, { SheetManager } from "react-native-actions-sheet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { isWeb } from "../hooks/useLayoutType";
|
||||
import { gutters, Palette } from "../styles";
|
||||
|
||||
const AppActionSheet = ({
|
||||
id,
|
||||
children,
|
||||
webModal = false,
|
||||
onClose = () => {},
|
||||
...sheetProps
|
||||
}) => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const handleRequestClose = useCallback(() => {
|
||||
if (id) {
|
||||
Promise.resolve(SheetManager.hide(id))
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
onClose?.();
|
||||
});
|
||||
return;
|
||||
}
|
||||
onClose?.();
|
||||
}, [id, onClose]);
|
||||
|
||||
if (isWeb && webModal) {
|
||||
return (
|
||||
<Portal>
|
||||
<View style={styles.webOverlay}>
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
onPress={handleRequestClose}
|
||||
accessibilityRole="button"
|
||||
style={styles.webBackdrop}
|
||||
/>
|
||||
@@ -36,6 +48,7 @@ const AppActionSheet = ({
|
||||
|
||||
return (
|
||||
<ActionSheet
|
||||
id={id}
|
||||
containerStyle={{ backgroundColor: Palette.tran }}
|
||||
gestureEnabled
|
||||
indicatorStyle={{
|
||||
|
||||
@@ -6,6 +6,18 @@ import { FONT_FAMILY } from "../styles/Fonts";
|
||||
import { size } from "../styles/Style";
|
||||
import BorderGradient from "./BorderGradient/BorderGradient";
|
||||
|
||||
const HEIGHT_BY_SIZE = {
|
||||
small: 40,
|
||||
medium: 50,
|
||||
large: 58,
|
||||
};
|
||||
|
||||
const FONT_SIZE_BY_SIZE = {
|
||||
small: 13,
|
||||
medium: 15,
|
||||
large: 17,
|
||||
};
|
||||
|
||||
const BorderGradientButton = ({
|
||||
title = "J’ai déjà mes paroles",
|
||||
onPress,
|
||||
@@ -15,7 +27,13 @@ const BorderGradientButton = ({
|
||||
tint = "dark",
|
||||
disabled = false,
|
||||
maxWidth = null,
|
||||
size = "medium",
|
||||
}) => {
|
||||
const resolvedSize = HEIGHT_BY_SIZE[size] ? size : "medium";
|
||||
const buttonHeight = HEIGHT_BY_SIZE[resolvedSize];
|
||||
const fontSize = FONT_SIZE_BY_SIZE[resolvedSize];
|
||||
const iconSize = resolvedSize === "small" ? 14 : 16;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
@@ -34,7 +52,7 @@ const BorderGradientButton = ({
|
||||
locations: [0, 1],
|
||||
}}
|
||||
style={{
|
||||
height: 50,
|
||||
height: buttonHeight,
|
||||
borderRadius: 14,
|
||||
borderWidth: 1,
|
||||
zIndex: 1,
|
||||
@@ -61,10 +79,10 @@ const BorderGradientButton = ({
|
||||
gap: 11,
|
||||
}}
|
||||
>
|
||||
{icon && <Image source={icon} style={size({ size: 16 })} />}
|
||||
{icon && <Image source={icon} style={size({ size: iconSize })} />}
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontSize,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
...titleStyle,
|
||||
|
||||
@@ -5,6 +5,18 @@ import { FONT_FAMILY } from "../styles/Fonts";
|
||||
import { size } from "../styles/Style";
|
||||
import { LinearGradient } from "./LinearGradient/LinearGradient";
|
||||
|
||||
const HEIGHT_BY_SIZE = {
|
||||
small: 44,
|
||||
medium: 50,
|
||||
large: 58,
|
||||
};
|
||||
|
||||
const FONT_SIZE_BY_SIZE = {
|
||||
small: 14,
|
||||
medium: 15,
|
||||
large: 17,
|
||||
};
|
||||
|
||||
const GradientButton = ({
|
||||
title = "",
|
||||
colors = ["#F94697", "#7023F7"],
|
||||
@@ -14,7 +26,14 @@ const GradientButton = ({
|
||||
icon,
|
||||
disabled = false,
|
||||
maxWidth = null,
|
||||
size = "medium",
|
||||
textStyle = {},
|
||||
gradientStyle = {},
|
||||
}) => {
|
||||
const resolvedSize = HEIGHT_BY_SIZE[size] ? size : "medium";
|
||||
const buttonHeight = HEIGHT_BY_SIZE[resolvedSize];
|
||||
const fontSize = FONT_SIZE_BY_SIZE[resolvedSize];
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
@@ -30,9 +49,10 @@ const GradientButton = ({
|
||||
style={{
|
||||
...Style.containerCenter,
|
||||
...Style.containerRow,
|
||||
height: 50,
|
||||
height: buttonHeight,
|
||||
borderRadius: 14,
|
||||
gap: 10,
|
||||
...gradientStyle,
|
||||
}}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
@@ -41,10 +61,11 @@ const GradientButton = ({
|
||||
{icon && <Image source={icon} style={size({ size: 16 })} />}
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontSize,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
paddingHorizontal: 10,
|
||||
...textStyle,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
|
||||
@@ -166,6 +166,7 @@ const ListSelection = ({
|
||||
const itemKey = buildItemKey(String(val ?? ""), cat);
|
||||
const isHovered = isWeb && !disableHover && hoveredKey === itemKey;
|
||||
const showHoverOutline = isHovered && !sel;
|
||||
const showSelectionOutline = !isWeb && sel;
|
||||
|
||||
const baseContent = renderSimpleContent(item);
|
||||
const headerContainerStyle = {
|
||||
@@ -205,7 +206,7 @@ const ListSelection = ({
|
||||
{withWebBlueGradientIfSelected(baseContent, sel)}
|
||||
</CreateLyricsHeader>
|
||||
|
||||
{showHoverOutline && (
|
||||
{(showHoverOutline || showSelectionOutline) && (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={{
|
||||
|
||||
@@ -6,7 +6,11 @@ import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { Entypo } from "@expo/vector-icons";
|
||||
import { openShareSheet } from "../../utils/shareSheet";
|
||||
|
||||
export default function ShareBtn({ style, onPress = null }) {
|
||||
export default function ShareBtn({
|
||||
style,
|
||||
onPress = null,
|
||||
label = "Partager l’expérience",
|
||||
}) {
|
||||
const handlePress = useCallback(() => {
|
||||
if (typeof onPress === "function") {
|
||||
onPress();
|
||||
@@ -41,7 +45,7 @@ export default function ShareBtn({ style, onPress = null }) {
|
||||
marginRight: 5,
|
||||
}}
|
||||
>
|
||||
{"Partager l’expérience"}
|
||||
{label}
|
||||
</Text>
|
||||
<Entypo name="share-alternative" size={16} color="white" />
|
||||
</BlurView>
|
||||
|
||||
@@ -30,7 +30,7 @@ const DeleteAccountModal = () => {
|
||||
onPress: () => {
|
||||
alert(
|
||||
"Confirmation",
|
||||
"Dernière vérification: supprimer définitivement votre profil ?",
|
||||
"Dernière vérification : souhaitez-vous supprimer définitivement votre profil ?",
|
||||
[
|
||||
{ text: "Non", style: "cancel", onPress: () => {} },
|
||||
{
|
||||
@@ -85,8 +85,8 @@ const DeleteAccountModal = () => {
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Es-tu sur de vouloir supprimer ton compte?{"\n"}Attention, cette
|
||||
action est irréversible!
|
||||
Es-tu sûr de vouloir supprimer ton compte ?{"\n"}Attention, cette
|
||||
action est irréversible !
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ width: "80%", alignSelf: "center", gap: 12 }}>
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { SheetManager } from "react-native-actions-sheet";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
Platform,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { useGlobal } from "reactn";
|
||||
import BorderGradientButton from "../BorderGradientButton";
|
||||
import GradientButton from "../GradientButton";
|
||||
import { REPORT_OPTIONS } from "../../constants/reportOptions";
|
||||
import { reportsRef, serverTimestamp } from "../../config/firebase";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
|
||||
const SHEET_ID = "Report";
|
||||
|
||||
const ReportModal = ({ payload }) => {
|
||||
const [, setTooltip] = useGlobal("_tooltip");
|
||||
const [selectedReason, setSelectedReason] = useState(REPORT_OPTIONS[0].key);
|
||||
const [details, setDetails] = useState("");
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
const resetForm = useCallback(() => {
|
||||
setSelectedReason(REPORT_OPTIONS[0].key);
|
||||
setDetails("");
|
||||
setIsSubmitting(false);
|
||||
}, []);
|
||||
|
||||
const targetContext = useMemo(() => {
|
||||
return {
|
||||
type: payload?.targetType || "music",
|
||||
projectId: payload?.projectId || null,
|
||||
playbackId: payload?.playbackId || null,
|
||||
title: payload?.title || "",
|
||||
ownerId: payload?.ownerId || null,
|
||||
};
|
||||
}, [payload]);
|
||||
|
||||
const closeSheet = useCallback(() => {
|
||||
setIsVisible(false);
|
||||
Promise.resolve(SheetManager.hide(SHEET_ID))
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
resetForm();
|
||||
});
|
||||
}, [resetForm]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(true);
|
||||
}, [payload]);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (isSubmitting) return;
|
||||
if (selectedReason === "other" && !details.trim()) return;
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const selectedOption = REPORT_OPTIONS.find(
|
||||
(option) => option.key === selectedReason,
|
||||
);
|
||||
await reportsRef.add({
|
||||
...targetContext,
|
||||
reason: selectedReason,
|
||||
reasonLabel: selectedOption?.label || selectedReason,
|
||||
details: details.trim() || null,
|
||||
platform: Platform.OS,
|
||||
createdAt: serverTimestamp(),
|
||||
});
|
||||
setTooltip({
|
||||
type: "success",
|
||||
text: "Merci pour ton signalement, notre équipe va l'examiner.",
|
||||
});
|
||||
closeSheet();
|
||||
} catch (error) {
|
||||
setTooltip({
|
||||
type: "error",
|
||||
text:
|
||||
error?.message ||
|
||||
"Impossible d'envoyer le signalement pour le moment.",
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [
|
||||
details,
|
||||
closeSheet,
|
||||
isSubmitting,
|
||||
selectedReason,
|
||||
setTooltip,
|
||||
targetContext,
|
||||
]);
|
||||
|
||||
const showDetailsField = selectedReason === "other";
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={isVisible}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={closeSheet}
|
||||
onDismiss={resetForm}
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
<Pressable style={styles.backdrop} onPress={closeSheet} />
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : undefined}
|
||||
style={styles.modalWrapper}
|
||||
>
|
||||
<View style={styles.card}>
|
||||
<View style={{ gap: 8 }}>
|
||||
<Text style={styles.heading}>Signaler ce contenu</Text>
|
||||
{targetContext?.title ? (
|
||||
<Text style={styles.subheading}>{targetContext.title}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<View style={{ gap: 12 }}>
|
||||
{REPORT_OPTIONS.map((option) => {
|
||||
const isSelected = option.key === selectedReason;
|
||||
return (
|
||||
<Pressable
|
||||
key={option.key}
|
||||
style={[
|
||||
styles.optionRow,
|
||||
isSelected ? styles.optionRowSelected : null,
|
||||
]}
|
||||
onPress={() => setSelectedReason(option.key)}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.radio,
|
||||
isSelected ? styles.radioSelected : null,
|
||||
]}
|
||||
/>
|
||||
<Text style={styles.optionLabel}>{option.label}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
{showDetailsField ? (
|
||||
<View style={{ gap: 6 }}>
|
||||
<Text style={styles.fieldLabel}>Détails (optionnel)</Text>
|
||||
<TextInput
|
||||
style={styles.textArea}
|
||||
placeholder="Explique-nous rapidement le problème…"
|
||||
placeholderTextColor="rgba(255, 255, 255, 0.55)"
|
||||
multiline
|
||||
onChangeText={setDetails}
|
||||
value={details}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<View style={{ gap: 12 }}>
|
||||
<GradientButton
|
||||
title={isSubmitting ? "Envoi en cours…" : "Envoyer"}
|
||||
onPress={handleSubmit}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
(selectedReason === "other" && details.trim().length === 0)
|
||||
}
|
||||
gradientStyle={{ minWidth: 160 }}
|
||||
/>
|
||||
<BorderGradientButton
|
||||
title="Annuler"
|
||||
onPress={closeSheet}
|
||||
containerStyle={{ width: "100%" }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 24,
|
||||
position: "relative",
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
},
|
||||
modalWrapper: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
card: {
|
||||
gap: 20,
|
||||
borderRadius: 24,
|
||||
padding: 24,
|
||||
backgroundColor: "rgba(18, 16, 24, 0.94)",
|
||||
width: "100%",
|
||||
maxWidth: 420,
|
||||
alignSelf: "center",
|
||||
},
|
||||
heading: {
|
||||
fontSize: 20,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.HelveticaNeueMedium,
|
||||
textAlign: "center",
|
||||
},
|
||||
subheading: {
|
||||
fontSize: 16,
|
||||
color: Palette.transparentWhite,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
textAlign: "center",
|
||||
},
|
||||
optionRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 12,
|
||||
borderRadius: 12,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.05)",
|
||||
gap: 12,
|
||||
},
|
||||
optionRowSelected: {
|
||||
backgroundColor: "rgba(251, 104, 168, 0.18)",
|
||||
},
|
||||
radio: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderRadius: 8,
|
||||
borderWidth: 2,
|
||||
borderColor: Palette.transparentWhite,
|
||||
},
|
||||
radioSelected: {
|
||||
borderColor: Palette.primary,
|
||||
backgroundColor: Palette.primary,
|
||||
},
|
||||
optionLabel: {
|
||||
flex: 1,
|
||||
fontSize: 15,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
},
|
||||
fieldLabel: {
|
||||
fontSize: 14,
|
||||
color: Palette.transparentWhite,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
},
|
||||
textArea: {
|
||||
minHeight: 90,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.12)",
|
||||
padding: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 14,
|
||||
textAlignVertical: "top",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.25)",
|
||||
},
|
||||
});
|
||||
|
||||
export default ReportModal;
|
||||
Reference in New Issue
Block a user