loading messages
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import "./rotationTestStyle.css";
|
||||||
|
|
||||||
|
// Web-only rotating gradient border container
|
||||||
|
// Props:
|
||||||
|
// - active: show rotating gradient when true (default)
|
||||||
|
// - style, className: optional DOM styles/classes
|
||||||
|
const RotationBorder = ({ children, style, className, active = true }) => {
|
||||||
|
const classes = ["box", active ? "a" : null, className]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
return (
|
||||||
|
<div className={classes} style={style}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RotationBorder;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
.box {
|
||||||
|
--border-angle: 0deg;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 100%;
|
||||||
|
height: 260px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0px 2px 4px hsl(0 0% 0% / 25%);
|
||||||
|
animation: border-angle-rotate 5s infinite linear;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.a {
|
||||||
|
background: linear-gradient(#11012f, #11012f) padding-box,
|
||||||
|
conic-gradient(
|
||||||
|
from var(--border-angle),
|
||||||
|
oklch(0.6659 0.2211 304.21),
|
||||||
|
oklch(0.6659 0.2211 304.21 / 0%),
|
||||||
|
oklch(0.6659 0.2211 304.21),
|
||||||
|
oklch(0.6659 0.2211 304.21 / 0%),
|
||||||
|
oklch(0.6659 0.2211 304.21)
|
||||||
|
|
||||||
|
)
|
||||||
|
border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes border-angle-rotate {
|
||||||
|
from {
|
||||||
|
--border-angle: 0deg;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
--border-angle: 360deg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --border-angle {
|
||||||
|
syntax: "<angle>";
|
||||||
|
initial-value: 0deg;
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
@@ -14,6 +14,9 @@ export default {
|
|||||||
title: "",
|
title: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_isLoading: false,
|
||||||
|
_loadingMessage: null,
|
||||||
|
|
||||||
_config: {
|
_config: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: Palette.primary,
|
primary: Palette.primary,
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
const loaderMessages = {
|
||||||
|
globalDefault: "",
|
||||||
|
profilePhotoUploadWeb: "",
|
||||||
|
photoCoverGenerationWeb: "",
|
||||||
|
pouchReadyGenerationWeb: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default loaderMessages;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { useGlobal } from "reactn";
|
||||||
|
|
||||||
|
const useGlobalLoading = () => {
|
||||||
|
const [, setIsLoading] = useGlobal("_isLoading");
|
||||||
|
const [, setLoadingMessage] = useGlobal("_loadingMessage");
|
||||||
|
|
||||||
|
const setLoading = useCallback(
|
||||||
|
async (loading, { message } = {}) => {
|
||||||
|
if (loading) {
|
||||||
|
await setLoadingMessage(message ?? null);
|
||||||
|
await setIsLoading(true);
|
||||||
|
} else {
|
||||||
|
await setIsLoading(false);
|
||||||
|
await setLoadingMessage(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setIsLoading, setLoadingMessage]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
setLoading,
|
||||||
|
setLoadingMessage,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useGlobalLoading;
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
|
import { ActivityIndicator, Text, View } from "react-native";
|
||||||
import React, { useGlobal } from "reactn";
|
import React, { useGlobal } from "reactn";
|
||||||
import { ActivityIndicator, View } from "react-native";
|
|
||||||
|
|
||||||
import { Palette } from "../styles";
|
|
||||||
import useLayoutType from "../hooks/useLayoutType";
|
import useLayoutType from "../hooks/useLayoutType";
|
||||||
|
import { Palette } from "../styles";
|
||||||
|
import { FONT_FAMILY } from "../styles/Fonts";
|
||||||
export default ({ children }) => {
|
export default ({ children }) => {
|
||||||
const [isGlobalLoading] = useGlobal("_isLoading");
|
const [isGlobalLoading] = useGlobal("_isLoading");
|
||||||
|
const [loadingMessage] = useGlobal("_loadingMessage");
|
||||||
|
|
||||||
const { isDesktopWeb = false, isMobileWeb = false } = useLayoutType();
|
const { isDesktopWeb = false, isMobileWeb = false } = useLayoutType();
|
||||||
|
const isWeb = isDesktopWeb || isMobileWeb;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -30,17 +32,35 @@ export default ({ children }) => {
|
|||||||
isDesktopWeb
|
isDesktopWeb
|
||||||
? { position: "fixed" }
|
? { position: "fixed" }
|
||||||
: isMobileWeb
|
: isMobileWeb
|
||||||
? { position: "fixed" }
|
? { position: "fixed" }
|
||||||
: { position: "absolute" },
|
: { position: "absolute" },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<LoaderIndicator />
|
<LoaderIndicator
|
||||||
|
isWeb={isWeb}
|
||||||
|
message={isWeb ? loadingMessage : null}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LoaderIndicator = () => {
|
export const LoaderIndicator = ({ isWeb = false, message = null }) => {
|
||||||
return <ActivityIndicator size={"large"} color={Palette.primary} />;
|
return (
|
||||||
|
<View style={{ alignItems: "center", gap: 10 }}>
|
||||||
|
<ActivityIndicator size={"large"} color={Palette.primary} />
|
||||||
|
{isWeb && message ? (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: Palette.white,
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+150
-26
@@ -1,8 +1,10 @@
|
|||||||
import { useRoute } from "@react-navigation/native";
|
import { useRoute } from "@react-navigation/native";
|
||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
import { Image as ExpoImage } from "expo-image";
|
import { Image as ExpoImage } from "expo-image";
|
||||||
|
import * as ImagePicker from "expo-image-picker";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
FlatList,
|
FlatList,
|
||||||
Image,
|
Image,
|
||||||
Pressable,
|
Pressable,
|
||||||
@@ -18,13 +20,19 @@ import { background, icons, img } from "../../assets";
|
|||||||
import BorderGradient from "../../components/BorderGradient/BorderGradient";
|
import BorderGradient from "../../components/BorderGradient/BorderGradient";
|
||||||
import MoreMenu from "../../components/MoreMenu";
|
import MoreMenu from "../../components/MoreMenu";
|
||||||
import PressableScale from "../../components/PressableScale";
|
import PressableScale from "../../components/PressableScale";
|
||||||
import { projectsRef, usersRef } from "../../config/firebase";
|
import firebase, {
|
||||||
|
projectsRef,
|
||||||
|
serverTimestamp,
|
||||||
|
usersRef,
|
||||||
|
} from "../../config/firebase";
|
||||||
|
import loaderMessages from "../../config/loaderMessages";
|
||||||
|
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
|
||||||
import useDataFromRef from "../../hooks/useDataFromRef";
|
import useDataFromRef from "../../hooks/useDataFromRef";
|
||||||
import useLayoutType from "../../hooks/useLayoutType";
|
import useLayoutType from "../../hooks/useLayoutType";
|
||||||
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
|
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
|
||||||
import Page from "../../layouts/Page";
|
import Page from "../../layouts/Page";
|
||||||
import { Routes } from "../../navigation";
|
import { Routes } from "../../navigation";
|
||||||
import { navigate, push } from "../../navigation/NavigationService";
|
import { push } from "../../navigation/NavigationService";
|
||||||
import { useUser } from "../../providers/UserDataProvider";
|
import { useUser } from "../../providers/UserDataProvider";
|
||||||
import { Palette } from "../../styles";
|
import { Palette } from "../../styles";
|
||||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||||
@@ -54,6 +62,8 @@ const Profile = () => {
|
|||||||
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 [updatingPhoto, setUpdatingPhoto] = useState(false);
|
||||||
|
const [webUploadMessage, setWebUploadMessage] = useState("");
|
||||||
const navigateToMusicDetails = useNavigateToMusicDetails();
|
const navigateToMusicDetails = useNavigateToMusicDetails();
|
||||||
const onPressMenu = (item) => {
|
const onPressMenu = (item) => {
|
||||||
setSelected(item);
|
setSelected(item);
|
||||||
@@ -114,6 +124,59 @@ const Profile = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChangeProfilePicture = async (message = "") => {
|
||||||
|
if (!isSelf || updatingPhoto) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setUpdatingPhoto(true);
|
||||||
|
setWebUploadMessage(isWeb && message ? message : "");
|
||||||
|
const uid = currentUID;
|
||||||
|
if (!uid) return;
|
||||||
|
|
||||||
|
const result = await ImagePicker.launchImageLibraryAsync({
|
||||||
|
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
||||||
|
allowsEditing: true,
|
||||||
|
aspect: [4, 4],
|
||||||
|
quality: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickedUri = result?.assets?.[0]?.uri || null;
|
||||||
|
if (!pickedUri) return;
|
||||||
|
|
||||||
|
const { resultURI = null } = await uploadFileToFirebase({
|
||||||
|
uri: pickedUri,
|
||||||
|
path: `users/${uid}/profilePicture.png`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resultURI) {
|
||||||
|
throw new Error("Téléversement de l'image impossible");
|
||||||
|
}
|
||||||
|
|
||||||
|
await usersRef.doc(uid).set(
|
||||||
|
{
|
||||||
|
profilePictureURL: resultURI,
|
||||||
|
updatedAt: serverTimestamp(),
|
||||||
|
},
|
||||||
|
{ merge: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const authUser = firebase.auth().currentUser;
|
||||||
|
if (authUser) {
|
||||||
|
await authUser.updateProfile({ photoURL: resultURI });
|
||||||
|
}
|
||||||
|
|
||||||
|
setTooltip({ text: "Photo de profil mise à jour", type: "success" });
|
||||||
|
} catch (e) {
|
||||||
|
setTooltip({
|
||||||
|
text: e?.message || "Erreur changement photo de profil",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setUpdatingPhoto(false);
|
||||||
|
setWebUploadMessage("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Composant factorisé pour afficher la liste de musiques
|
// Composant factorisé pour afficher la liste de musiques
|
||||||
const MusicListSection = ({ data, emptyText }) => (
|
const MusicListSection = ({ data, emptyText }) => (
|
||||||
<ScrollView style={{ flex: 1, marginBottom: 70 }}>
|
<ScrollView style={{ flex: 1, marginBottom: 70 }}>
|
||||||
@@ -286,30 +349,59 @@ const Profile = () => {
|
|||||||
</PressableScale>
|
</PressableScale>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View style={{ alignItems: "center" }}>
|
<View style={{ alignItems: "center" }}>
|
||||||
<ExpoImage
|
<View style={styles.avatarWrapper}>
|
||||||
source={
|
<ExpoImage
|
||||||
(
|
source={
|
||||||
isSelf
|
(
|
||||||
? currentUserData?.profilePictureURL
|
isSelf
|
||||||
: userData?.profilePictureURL
|
? currentUserData?.profilePictureURL
|
||||||
)
|
: userData?.profilePictureURL
|
||||||
? {
|
)
|
||||||
uri: isSelf
|
? {
|
||||||
? currentUserData?.profilePictureURL
|
uri: isSelf
|
||||||
: userData?.profilePictureURL,
|
? currentUserData?.profilePictureURL
|
||||||
}
|
: userData?.profilePictureURL,
|
||||||
: img.profile
|
}
|
||||||
}
|
: img.profile
|
||||||
cachePolicy="memory-disk"
|
}
|
||||||
priority="high"
|
cachePolicy="memory-disk"
|
||||||
contentFit="cover"
|
priority="high"
|
||||||
transition={100}
|
contentFit="cover"
|
||||||
style={{
|
transition={100}
|
||||||
...size({ size: 108 }),
|
style={{
|
||||||
borderRadius: 100,
|
...size({ size: 108 }),
|
||||||
}}
|
borderRadius: 100,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
{isSelf && (
|
||||||
|
<Pressable
|
||||||
|
style={styles.editAvatarButton}
|
||||||
|
onPress={() =>
|
||||||
|
onChangeProfilePicture(
|
||||||
|
loaderMessages.profilePhotoUploadWeb
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={updatingPhoto}
|
||||||
|
hitSlop={10}
|
||||||
|
>
|
||||||
|
<View style={styles.editAvatarBackground}>
|
||||||
|
{updatingPhoto ? (
|
||||||
|
<ActivityIndicator size="small" color={Palette.white} />
|
||||||
|
) : (
|
||||||
|
<Image
|
||||||
|
source={icons.edit}
|
||||||
|
style={styles.editAvatarIcon}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Pressable>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
{isWeb && updatingPhoto && webUploadMessage ? (
|
||||||
|
<Text style={styles.webLoaderText}>{webUploadMessage}</Text>
|
||||||
|
) : null}
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
@@ -498,4 +590,36 @@ const styles = StyleSheet.create({
|
|||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
...Style.containerCenter,
|
...Style.containerCenter,
|
||||||
},
|
},
|
||||||
|
avatarWrapper: {
|
||||||
|
position: "relative",
|
||||||
|
...size({ size: 108 }),
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
editAvatarButton: {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 4,
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
editAvatarBackground: {
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
borderRadius: 18,
|
||||||
|
backgroundColor: Palette.transparentBlack,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: Palette.white,
|
||||||
|
...Style.containerCenter,
|
||||||
|
},
|
||||||
|
editAvatarIcon: {
|
||||||
|
...size({ size: 16 }),
|
||||||
|
tintColor: Palette.white,
|
||||||
|
},
|
||||||
|
webLoaderText: {
|
||||||
|
marginTop: 8,
|
||||||
|
color: Palette.white,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.85,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import BorderGradientButton from "../../components/BorderGradientButton";
|
|||||||
import GradientButton from "../../components/GradientButton";
|
import GradientButton from "../../components/GradientButton";
|
||||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||||
import firebase, { tasksRef } from "../../config/firebase";
|
import firebase, { tasksRef } from "../../config/firebase";
|
||||||
|
import loaderMessages from "../../config/loaderMessages";
|
||||||
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
|
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
|
||||||
import { isWeb } from "../../hooks/useLayoutType";
|
import { isWeb } from "../../hooks/useLayoutType";
|
||||||
import Page from "../../layouts/Page";
|
import Page from "../../layouts/Page";
|
||||||
@@ -22,6 +23,9 @@ const PhotoCover = () => {
|
|||||||
useUserData();
|
useUserData();
|
||||||
const { setIsLoading } = useMinuit();
|
const { setIsLoading } = useMinuit();
|
||||||
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
||||||
|
const coverGenerationMessage = isWeb
|
||||||
|
? loaderMessages.photoCoverGenerationWeb
|
||||||
|
: "";
|
||||||
|
|
||||||
const onChooseForegroundImage = async () => {
|
const onChooseForegroundImage = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -102,9 +106,31 @@ const PhotoCover = () => {
|
|||||||
{isGenerating && (
|
{isGenerating && (
|
||||||
<View style={{ alignItems: "center", gap: 8 }}>
|
<View style={{ alignItems: "center", gap: 8 }}>
|
||||||
<ActivityIndicator color={Palette.white} />
|
<ActivityIndicator color={Palette.white} />
|
||||||
<Text style={{ color: Palette.white, marginTop: 6 }}>
|
{isWeb ? (
|
||||||
Génération en cours.
|
coverGenerationMessage ? (
|
||||||
</Text>
|
<Text
|
||||||
|
style={{
|
||||||
|
color: Palette.white,
|
||||||
|
marginTop: 6,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{coverGenerationMessage}
|
||||||
|
</Text>
|
||||||
|
) : null
|
||||||
|
) : (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: Palette.white,
|
||||||
|
marginTop: 6,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Génération en cours.
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import BorderGradientButton from "../../components/BorderGradientButton";
|
|||||||
import GradientButton from "../../components/GradientButton";
|
import GradientButton from "../../components/GradientButton";
|
||||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||||
import firebase, { tasksRef } from "../../config/firebase";
|
import firebase, { tasksRef } from "../../config/firebase";
|
||||||
|
import loaderMessages from "../../config/loaderMessages";
|
||||||
import { isWeb } from "../../hooks/useLayoutType";
|
import { isWeb } from "../../hooks/useLayoutType";
|
||||||
import Page from "../../layouts/Page";
|
import Page from "../../layouts/Page";
|
||||||
import { Routes } from "../../navigation";
|
import { Routes } from "../../navigation";
|
||||||
@@ -21,6 +22,9 @@ const PouchReady = () => {
|
|||||||
const { setIsLoading } = useMinuit();
|
const { setIsLoading } = useMinuit();
|
||||||
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
||||||
const isFocused = useIsFocused();
|
const isFocused = useIsFocused();
|
||||||
|
const coverBackgroundMessage = isWeb
|
||||||
|
? loaderMessages.pouchReadyGenerationWeb
|
||||||
|
: "";
|
||||||
|
|
||||||
const generateCover = useCallback(async () => {
|
const generateCover = useCallback(async () => {
|
||||||
if (!selectedProjectId) return;
|
if (!selectedProjectId) return;
|
||||||
@@ -126,9 +130,31 @@ const PouchReady = () => {
|
|||||||
{isGenerating && (
|
{isGenerating && (
|
||||||
<View style={{ alignItems: "center", gap: 8 }}>
|
<View style={{ alignItems: "center", gap: 8 }}>
|
||||||
<ActivityIndicator color={Palette.white} />
|
<ActivityIndicator color={Palette.white} />
|
||||||
<Text style={{ color: Palette.white, marginTop: 6 }}>
|
{isWeb ? (
|
||||||
Génération en cours.
|
coverBackgroundMessage ? (
|
||||||
</Text>
|
<Text
|
||||||
|
style={{
|
||||||
|
color: Palette.white,
|
||||||
|
marginTop: 6,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{coverBackgroundMessage}
|
||||||
|
</Text>
|
||||||
|
) : null
|
||||||
|
) : (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: Palette.white,
|
||||||
|
marginTop: 6,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Génération en cours.
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user