update
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import React, { useState, useRef, createContext } from "react";
|
||||
import { View, TouchableOpacity } from "react-native";
|
||||
import { Motion } from "@legendapp/motion";
|
||||
|
||||
import { mainBorderRadius } from "../styles/Style";
|
||||
import { Palette } from "../styles";
|
||||
|
||||
export const BottomSheetContext = createContext();
|
||||
|
||||
export default ({ children }) => {
|
||||
const [showSheet, setShowSheet] = useState(false);
|
||||
const [sheetContent, setSheetContent] = useState(null);
|
||||
|
||||
const bottomSheetRef = useRef();
|
||||
|
||||
return (
|
||||
<BottomSheetContext.Provider
|
||||
value={{ showSheet, setShowSheet, setSheetContent }}
|
||||
>
|
||||
{children}
|
||||
|
||||
{showSheet && (
|
||||
<Motion.View
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.9, opacity: 0 }}
|
||||
style={{
|
||||
position: "fixed",
|
||||
right: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
zIndex: 1000000,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={() => setShowSheet(false)}
|
||||
ref={bottomSheetRef}
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(0,0,0,0.4)",
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
width: "70%",
|
||||
height: "auto",
|
||||
maxWidth: 500,
|
||||
maxHeight: "60vh",
|
||||
minHeight: 400,
|
||||
alignSelf: "center",
|
||||
backgroundColor: Palette.lightPurple,
|
||||
position: "absolute",
|
||||
overflow: "scroll",
|
||||
borderRadius: mainBorderRadius,
|
||||
}}
|
||||
>
|
||||
{sheetContent}
|
||||
</View>
|
||||
</Motion.View>
|
||||
)}
|
||||
</BottomSheetContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import React, { useGlobal } from "reactn";
|
||||
import { ActivityIndicator, View } from "react-native";
|
||||
|
||||
import { Palette } from "../styles";
|
||||
import useLayoutType from "../hooks/useLayoutType";
|
||||
|
||||
export default ({ children }) => {
|
||||
const [isGlobalLoading] = useGlobal("_isLoading");
|
||||
|
||||
const { isDesktopWeb = false, isMobileWeb = false } = useLayoutType();
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
|
||||
{isGlobalLoading && (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
zIndex: 9999,
|
||||
backgroundColor: Palette.transparentBlack,
|
||||
},
|
||||
isDesktopWeb
|
||||
? { position: "fixed" }
|
||||
: isMobileWeb
|
||||
? { position: "fixed" }
|
||||
: { position: "absolute" },
|
||||
]}
|
||||
>
|
||||
<LoaderIndicator />
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const LoaderIndicator = () => {
|
||||
return <ActivityIndicator size={"large"} color={Palette.primary} />;
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useGlobal,
|
||||
setGlobal,
|
||||
} from "reactn";
|
||||
import messaging from "@react-native-firebase/messaging";
|
||||
import notifee, { EventType } from "@notifee/react-native";
|
||||
import { CommonActions } from "@react-navigation/core";
|
||||
import { useAppState } from "@react-native-community/hooks";
|
||||
import { Linking } from "react-native";
|
||||
|
||||
import { dispatch } from "../navigation/NavigationService";
|
||||
import { Routes } from "../navigation";
|
||||
import firebase, { usersRef } from "../config/firebase";
|
||||
import alert from "../components/Alert";
|
||||
|
||||
export const NotificationContext = createContext();
|
||||
|
||||
export async function displayNotification(data) {
|
||||
const { title, message, picture = null, video = null } = data || {};
|
||||
|
||||
let ios = {
|
||||
categoryId: "default",
|
||||
};
|
||||
|
||||
if (picture) {
|
||||
ios.attachments = [{ url: picture }];
|
||||
}
|
||||
|
||||
if (video) {
|
||||
ios.attachments = [
|
||||
{
|
||||
url: video,
|
||||
thumbnailTime: data?.thumbnailTime
|
||||
? parseInt(data?.thumbnailTime, 10)
|
||||
: 0,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
await notifee.displayNotification({
|
||||
title,
|
||||
body: message,
|
||||
ios,
|
||||
android: {
|
||||
channelId: "default",
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
await notifee.incrementBadgeCount();
|
||||
}
|
||||
|
||||
export async function triggerEvent({ type, detail }) {
|
||||
const { notification } = detail;
|
||||
|
||||
if (type === EventType.PRESS) {
|
||||
const { data = {} } = notification || {};
|
||||
const { projectID = null, taskID = null, messageID = null } = data || {};
|
||||
|
||||
if (projectID) {
|
||||
setGlobal({ currentProjectID: projectID });
|
||||
|
||||
if (taskID) {
|
||||
dispatch(
|
||||
CommonActions.navigate({
|
||||
name: Routes.TaskDetails,
|
||||
params: { taskID, messageID },
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ({ children }) => {
|
||||
const [currentUID] = useGlobal("currentUID");
|
||||
|
||||
const currentAppState = useAppState();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUID) {
|
||||
updateFCMToken({ uid: currentUID });
|
||||
}
|
||||
}, [currentUID]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = messaging().onMessage(async (remoteMessage) => {
|
||||
console.log("Message handled in the foreground!", remoteMessage);
|
||||
const { data } = remoteMessage;
|
||||
|
||||
await displayNotification(data);
|
||||
});
|
||||
const foregroundSubscription = notifee.onForegroundEvent(triggerEvent);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
foregroundSubscription();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentAppState === "active") {
|
||||
notifee.setBadgeCount(0);
|
||||
}
|
||||
}, [currentAppState]);
|
||||
|
||||
const updateFCMToken = async ({ uid }) => {
|
||||
try {
|
||||
const authResponse = await messaging().requestPermission();
|
||||
const enabled = authResponse === messaging.AuthorizationStatus.AUTHORIZED;
|
||||
if (enabled) {
|
||||
const fcmToken = await messaging().getToken();
|
||||
if (fcmToken) {
|
||||
await usersRef.doc(uid).update({
|
||||
fcmTokens: firebase.firestore.FieldValue.arrayUnion(fcmToken),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
alert(
|
||||
"Activez les notifications",
|
||||
"Afin de rester informé de l'avancée de votre projet, il est important d'activer les notifications.",
|
||||
[
|
||||
{
|
||||
text: "Annuler",
|
||||
onPress: () => {},
|
||||
style: "cancel",
|
||||
},
|
||||
{
|
||||
text: "Paramètres",
|
||||
onPress: async () => {
|
||||
Linking.openSettings();
|
||||
},
|
||||
style: "confirm",
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<NotificationContext.Provider value={{}}>
|
||||
{children}
|
||||
</NotificationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useNotification = () => {
|
||||
return useContext(NotificationContext);
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import React, { useContext, createContext, useEffect, useState } from "react";
|
||||
|
||||
import firebase from "../config/firebase";
|
||||
import { useUserData } from "./UserDataProvider";
|
||||
|
||||
export const PremiumContext = createContext();
|
||||
|
||||
export default ({ children }) => {
|
||||
const { currentUID } = useUserData();
|
||||
|
||||
const [subscriptions, setSubscriptions] = useState(null);
|
||||
|
||||
const subscriptionType = subscriptions?.length > 0 ? "PREMIUM" : "FREE";
|
||||
const isPaying = subscriptionType !== "FREE";
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUID) {
|
||||
getPremiumStatus();
|
||||
}
|
||||
}, [currentUID]);
|
||||
|
||||
const getPremiumStatus = async () => {
|
||||
try {
|
||||
const { data } = await firebase
|
||||
.functions()
|
||||
.httpsCallable("payment-getPremiumStatus")({
|
||||
userID: currentUID,
|
||||
});
|
||||
|
||||
if (data?.subscriptions?.length) {
|
||||
setSubscriptions(
|
||||
(data.subscriptions || [])?.filter((sub) => sub.status === "active")
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PremiumContext.Provider
|
||||
value={{
|
||||
getPremiumStatus,
|
||||
|
||||
subscriptionType,
|
||||
subscriptions,
|
||||
|
||||
isPaying,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</PremiumContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePremium = () => useContext(PremiumContext);
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from "react";
|
||||
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
|
||||
import UserDataProvider from "./UserDataProvider";
|
||||
import WebViewProvider from "./WebViewProvider";
|
||||
import SplashAnimationProvider from "./SplashAnimationProvider";
|
||||
import UniversalLinkProvider from "./UniversalLinkProvider";
|
||||
import PremiumProvider from "./PremiumProvider";
|
||||
import StripeEmbeddedProvider from "./StripeEmbeddedProvider";
|
||||
|
||||
const SharedProviders = ({ children }) => {
|
||||
const providers = [
|
||||
[SafeAreaProvider, {}],
|
||||
[ActionSheetProvider, {}],
|
||||
[SplashAnimationProvider, {}],
|
||||
[WebViewProvider, {}],
|
||||
[UserDataProvider, {}],
|
||||
[StripeEmbeddedProvider, {}],
|
||||
[PremiumProvider, {}],
|
||||
[UniversalLinkProvider, {}],
|
||||
];
|
||||
|
||||
// Dynamically nest the providers using reduce
|
||||
const Combined = providers.reduceRight(
|
||||
(AccumulatedChildren, [Provider, props]) => {
|
||||
return ({ children }) => (
|
||||
<Provider {...props}>
|
||||
<AccumulatedChildren>{children}</AccumulatedChildren>
|
||||
</Provider>
|
||||
);
|
||||
},
|
||||
({ children }) => <>{children}</>
|
||||
);
|
||||
|
||||
return <Combined>{children}</Combined>;
|
||||
};
|
||||
|
||||
export default SharedProviders;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useState, useEffect, createContext, useRef } from "react";
|
||||
import { StyleSheet, Text, Animated } from "react-native";
|
||||
import { Fonts, Palette, Style } from "../styles";
|
||||
import { isWeb } from "../hooks/useLayoutType";
|
||||
|
||||
export const SplashAnimationContext = createContext();
|
||||
|
||||
const SplashAnimationProvider = ({ children }) => {
|
||||
const [isFullyLoaded, setIsFullyLoaded] = useState(false);
|
||||
const [showOverlay, setShowOverlay] = useState(true);
|
||||
|
||||
const fadeAnim = useRef(new Animated.Value(1)).current;
|
||||
|
||||
useEffect(() => {
|
||||
if (isFullyLoaded) {
|
||||
Animated.timing(fadeAnim, {
|
||||
toValue: 0,
|
||||
duration: 500,
|
||||
useNativeDriver: true,
|
||||
}).start(() => {
|
||||
setShowOverlay(false);
|
||||
});
|
||||
}
|
||||
}, [isFullyLoaded]);
|
||||
|
||||
return (
|
||||
<SplashAnimationContext.Provider
|
||||
value={{ isFullyLoaded, setIsFullyLoaded }}
|
||||
>
|
||||
{children}
|
||||
|
||||
{showOverlay && (
|
||||
<Animated.View
|
||||
style={{
|
||||
...StyleSheet.absoluteFillObject,
|
||||
flex: 1,
|
||||
...Style.containerCenter,
|
||||
backgroundColor: Palette.darkPurple,
|
||||
opacity: fadeAnim,
|
||||
}}
|
||||
>
|
||||
{isWeb && <Text style={Fonts({ type: "megaTitle" })}>m</Text>}
|
||||
</Animated.View>
|
||||
)}
|
||||
</SplashAnimationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SplashAnimationProvider;
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
EmbeddedCheckoutProvider,
|
||||
EmbeddedCheckout,
|
||||
} from "@stripe/react-stripe-js";
|
||||
import { useState, createContext } from "react";
|
||||
import { View, StyleSheet } from "react-native";
|
||||
|
||||
import { loadStripe } from "@stripe/stripe-js";
|
||||
|
||||
import {
|
||||
STRIPE_PUBLISHABLE_KEY_LIVE,
|
||||
STRIPE_PUBLISHABLE_KEY_TEST,
|
||||
} from "../data/keys";
|
||||
import { mainBorderRadius } from "../styles/Style";
|
||||
import Overlay from "../components/Overlay";
|
||||
import Button from "../components/Button";
|
||||
import useLayoutType from "../hooks/useLayoutType";
|
||||
|
||||
const isStripeTesting = false;
|
||||
const stripePromise = loadStripe(
|
||||
isStripeTesting ? STRIPE_PUBLISHABLE_KEY_TEST : STRIPE_PUBLISHABLE_KEY_LIVE
|
||||
);
|
||||
|
||||
export const StripeEmbeddedContext = createContext();
|
||||
|
||||
export default ({ children }) => {
|
||||
const { isMobile } = useLayoutType();
|
||||
|
||||
const [clientSecret, setClientSecret] = useState(null);
|
||||
|
||||
return (
|
||||
<StripeEmbeddedContext.Provider value={{ setClientSecret }}>
|
||||
{children}
|
||||
<Overlay
|
||||
isVisible={clientSecret !== null}
|
||||
setIsVisible={() => setClientSecret(null)}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
...StyleSheet.absoluteFillObject,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
width: isMobile ? "90%" : "80%",
|
||||
height: "70vh",
|
||||
borderRadius: mainBorderRadius,
|
||||
overflow: "scroll",
|
||||
}}
|
||||
>
|
||||
<EmbeddedCheckoutProvider
|
||||
stripe={stripePromise}
|
||||
options={{ clientSecret }}
|
||||
>
|
||||
<EmbeddedCheckout />
|
||||
</EmbeddedCheckoutProvider>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
type="secondary"
|
||||
isAbsoluteBottom
|
||||
text="Fermer"
|
||||
onPress={() => setClientSecret(null)}
|
||||
/>
|
||||
</View>
|
||||
</Overlay>
|
||||
</StripeEmbeddedContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import React, { useEffect, useGlobal } from "reactn";
|
||||
import { Text } from "react-native";
|
||||
import { Motion } from "@legendapp/motion";
|
||||
|
||||
import { Fonts, Palette } from "../styles";
|
||||
import { responsiveHeight } from "../actions/responsiveSizes";
|
||||
|
||||
function TooltipProvider({ children }) {
|
||||
const [tooltip, setTooltip] = useGlobal("_tooltip");
|
||||
|
||||
useEffect(() => {
|
||||
const resetTimeout = setTimeout(async () => {
|
||||
setTooltip(null);
|
||||
}, 2500);
|
||||
|
||||
return () => clearTimeout(resetTimeout);
|
||||
}, [tooltip]);
|
||||
|
||||
function getBackgroundColor() {
|
||||
if (tooltip?.type === "error") {
|
||||
return Palette.red;
|
||||
}
|
||||
return Palette.primary;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
|
||||
<Motion.View
|
||||
animate={{ top: tooltip ? responsiveHeight(5) : -50 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
damping: 20,
|
||||
stiffness: 400,
|
||||
}}
|
||||
style={{
|
||||
position: "absolute",
|
||||
alignSelf: "center",
|
||||
backgroundColor: getBackgroundColor(),
|
||||
padding: 10,
|
||||
paddingHorizontal: 20,
|
||||
borderRadius: 30,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={{
|
||||
textAlign: "center",
|
||||
...Fonts({
|
||||
type: "default",
|
||||
color: Palette.white,
|
||||
style: { textAlign: "center" },
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{tooltip?.text}
|
||||
</Text>
|
||||
</Motion.View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default TooltipProvider;
|
||||
@@ -0,0 +1,111 @@
|
||||
import { useContext, useState, useEffect } from "reactn";
|
||||
import * as Linking from "expo-linking";
|
||||
|
||||
import { UserDataContext } from "./UserDataProvider";
|
||||
import { isWeb } from "../hooks/useLayoutType";
|
||||
import { navigateToTask } from "../navigation/NavigationService";
|
||||
import { SplashAnimationContext } from "./SplashAnimationProvider";
|
||||
|
||||
const appJson = require("../../app.json");
|
||||
|
||||
export const storeURL = {
|
||||
apple: "apps.apple.com/app/id1661696886",
|
||||
google: "play.google.com/store/apps",
|
||||
};
|
||||
|
||||
const UniversalLinkProvider = ({ children }) => {
|
||||
const { currentUID } = useContext(UserDataContext);
|
||||
const { isFullyLoaded } = useContext(SplashAnimationContext);
|
||||
|
||||
const [tempTaskData, setTempTaskData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleDeepLink = async (event) => {
|
||||
console.log("event", event);
|
||||
|
||||
const { path = "", queryParams } = Linking.parse(event.url);
|
||||
|
||||
console.log("path", path);
|
||||
console.log("queryParams", queryParams);
|
||||
|
||||
const appSchemeURL = `${
|
||||
appJson.expo.scheme
|
||||
}://app/${path}?${new URLSearchParams(queryParams).toString()}`;
|
||||
|
||||
const appStoreURL = `itms-apps://${storeURL.apple}`;
|
||||
|
||||
if (isWeb) {
|
||||
const userAgent =
|
||||
navigator.userAgent || navigator.vendor || window.opera;
|
||||
const isIOSBrowser =
|
||||
/iPad|iPhone|iPod/.test(userAgent) ||
|
||||
(/Macintosh/.test(userAgent) &&
|
||||
navigator.maxTouchPoints &&
|
||||
navigator.maxTouchPoints > 2);
|
||||
|
||||
if (isIOSBrowser) {
|
||||
try {
|
||||
const canOpen = await Linking.canOpenURL(appSchemeURL);
|
||||
if (canOpen) {
|
||||
window.location.href = appSchemeURL;
|
||||
} else {
|
||||
window.location.href = appStoreURL;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Redirection error:", error);
|
||||
handleParams(path);
|
||||
}
|
||||
} else {
|
||||
handleParams(path);
|
||||
}
|
||||
} else {
|
||||
handleParams(path);
|
||||
}
|
||||
};
|
||||
|
||||
const initDeepLinkHandling = async () => {
|
||||
const initialUrl = await Linking.getInitialURL();
|
||||
if (initialUrl) {
|
||||
console.log("Initial URL:", initialUrl);
|
||||
handleDeepLink({ url: initialUrl });
|
||||
}
|
||||
|
||||
Linking.addEventListener("url", handleDeepLink);
|
||||
|
||||
return () => {
|
||||
Linking.removeEventListener("url", handleDeepLink);
|
||||
};
|
||||
};
|
||||
|
||||
initDeepLinkHandling();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (tempTaskData && currentUID && isFullyLoaded) {
|
||||
console.log("try to navigate to task");
|
||||
navigateToTask(tempTaskData);
|
||||
setTempTaskData(null);
|
||||
}
|
||||
}, [tempTaskData, currentUID, isFullyLoaded]);
|
||||
|
||||
const handleParams = (path) => {
|
||||
console.log("path", path);
|
||||
|
||||
cleanURL();
|
||||
};
|
||||
|
||||
const cleanURL = () => {
|
||||
if (isWeb) {
|
||||
const url = new URL(window.location);
|
||||
|
||||
console.log(url);
|
||||
|
||||
url.search = "";
|
||||
window.history.replaceState({}, document.title, url.origin.toString());
|
||||
}
|
||||
};
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
export default UniversalLinkProvider;
|
||||
@@ -0,0 +1,165 @@
|
||||
import { useContext, createContext, useGlobal } from "reactn";
|
||||
import { useDataFromRef } from "react-native-minuit/src/hooks";
|
||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit";
|
||||
|
||||
import firebase, { usersRef } from "../config/firebase";
|
||||
import { useState } from "react";
|
||||
import { checkIfEmailIsValid } from "../actions/signupActions";
|
||||
|
||||
export const UserDataContext = createContext();
|
||||
|
||||
export default ({ children }) => {
|
||||
const { setIsLoading, setTooltip } = useMinuit();
|
||||
|
||||
const [pendingUserData, setPendingUserData] = useState({
|
||||
creationTimestamp: new Date(),
|
||||
});
|
||||
|
||||
const [currentUID] = useGlobal("currentUID");
|
||||
const [currentUserData, setCurrentUserData] = useGlobal("currentUserData");
|
||||
const [currentUserRoles] = useGlobal("currentUserRoles");
|
||||
|
||||
useDataFromRef({
|
||||
ref: currentUID ? usersRef.doc(currentUID) : null,
|
||||
simpleRef: true,
|
||||
listener: true,
|
||||
condition: currentUID,
|
||||
refreshArray: [currentUID],
|
||||
onUpdate: (data) => {
|
||||
if (data) {
|
||||
setCurrentUserData(data);
|
||||
} else {
|
||||
setCurrentUserData(null);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const updatePendingUserData = (newData) => {
|
||||
setPendingUserData((prevData) => ({
|
||||
...prevData,
|
||||
...newData,
|
||||
}));
|
||||
};
|
||||
|
||||
const updateUserData = async ({ data, shouldSetTooltip = true }) => {
|
||||
try {
|
||||
await setIsLoading(true);
|
||||
|
||||
let dynamicUID = currentUID || null;
|
||||
|
||||
let cleanData = {
|
||||
...currentUserData,
|
||||
...data,
|
||||
lastUpdateTimestamp: new Date(),
|
||||
};
|
||||
|
||||
// if (cleanData?.firstName?.length > 2 && cleanData?.lastName?.length > 2) {
|
||||
// cleanData = {
|
||||
// ...cleanData,
|
||||
// firstName: capitalize(cleanData?.firstName?.toLowerCase()),
|
||||
// lastName: capitalize(cleanData?.lastName?.toLowerCase()),
|
||||
// };
|
||||
// } else {
|
||||
// throw new Error("Veuillez renseigner un prénom et un nom");
|
||||
// }
|
||||
|
||||
if (!checkIfEmailIsValid({ email: cleanData?.email })) {
|
||||
throw new Error("Veuillez renseigner un email valide");
|
||||
}
|
||||
|
||||
// let cleanPhoneNumber = formatPhoneNumber({
|
||||
// phoneNumber: cleanData.phoneNumber,
|
||||
// });
|
||||
|
||||
// if (cleanPhoneNumber) {
|
||||
// cleanData.phoneNumber = cleanPhoneNumber;
|
||||
// } else {
|
||||
// throw new Error("Veuillez renseigner un numéro de téléphone valide");
|
||||
// }
|
||||
|
||||
if (!dynamicUID) {
|
||||
const { email, password } = cleanData;
|
||||
|
||||
const { user = null } = await firebase
|
||||
.auth()
|
||||
.createUserWithEmailAndPassword(email, password);
|
||||
|
||||
dynamicUID = user.uid;
|
||||
}
|
||||
|
||||
delete cleanData.password;
|
||||
|
||||
cleanData = {
|
||||
...cleanData,
|
||||
userID: dynamicUID,
|
||||
};
|
||||
|
||||
if (!cleanData?.creationTimestamp) {
|
||||
cleanData.creationTimestamp = new Date();
|
||||
}
|
||||
|
||||
console.log("cleanData", cleanData);
|
||||
|
||||
await usersRef.doc(dynamicUID).set(cleanData, { merge: true });
|
||||
|
||||
if (shouldSetTooltip) {
|
||||
setTooltip({
|
||||
type: "success",
|
||||
text: "Données enregistrées avec succès",
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
setTooltip({
|
||||
type: "error",
|
||||
text: "Erreur lors de la mise à jour des données utilisateur",
|
||||
});
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
await setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSignOut = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
await firebase.auth().signOut();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const isSuperAdmin = currentUserRoles.some((role) => role === "SUPERADMIN");
|
||||
|
||||
return (
|
||||
<UserDataContext.Provider
|
||||
value={{
|
||||
isSuperAdmin,
|
||||
|
||||
currentUserRoles,
|
||||
currentUID,
|
||||
currentUserData,
|
||||
|
||||
setCurrentUserData,
|
||||
|
||||
onSignOut,
|
||||
|
||||
pendingUserData,
|
||||
updatePendingUserData,
|
||||
|
||||
updateUserData,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</UserDataContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useUserData = () => {
|
||||
return useContext(UserDataContext);
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import React, { useRef, useEffect, useMemo, createContext } from "reactn";
|
||||
|
||||
import BottomSheetContainer from "../components/BottomSheetContainer";
|
||||
import { WebView } from "../components/WebView";
|
||||
|
||||
import { Palette } from "../styles";
|
||||
import { useContext } from "react";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export const WebViewContext = createContext();
|
||||
|
||||
export default ({ children }) => {
|
||||
const [webViewUrl, setWebViewUrl] = React.useState(Platform.OS == "ios" ? null : undefined);
|
||||
|
||||
const snapPoints = useMemo(() => ["70%", "90%"], []);
|
||||
const bottomSheetRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (webViewUrl) {
|
||||
bottomSheetRef.current?.expand();
|
||||
} else {
|
||||
bottomSheetRef.current?.close();
|
||||
}
|
||||
}, [webViewUrl]);
|
||||
|
||||
const handleCloseWebView = (url) => {
|
||||
if (url?.includes("minuit.starter")) {
|
||||
setWebViewUrl(Platform.OS == "ios" ? null : undefined); // Réinitialiser l'URL
|
||||
bottomSheetRef.current?.close(); // Fermer la BottomSheet
|
||||
}
|
||||
};
|
||||
|
||||
const docExtensionList = [".docx", ".doc", ".xlsx", ".xls", ".pptx", ".ppt"];
|
||||
const isOfficeDoc = docExtensionList.some((ext) =>
|
||||
webViewUrl?.toLowerCase()?.includes(ext)
|
||||
);
|
||||
|
||||
const dynamicWebViewUrl = isOfficeDoc
|
||||
? `https://docs.google.com/gview?embedded=true&url=${encodeURIComponent(
|
||||
webViewUrl
|
||||
)}`
|
||||
: webViewUrl;
|
||||
|
||||
return (
|
||||
<WebViewContext.Provider value={{ webViewUrl, setWebViewUrl }}>
|
||||
{children}
|
||||
|
||||
<BottomSheetContainer
|
||||
bottomSheetRef={bottomSheetRef}
|
||||
snapPoints={snapPoints}
|
||||
onChange={(index) => {
|
||||
if (index === -1) {
|
||||
setWebViewUrl(Platform.OS == "ios" ? null : undefined);
|
||||
}
|
||||
}}
|
||||
index={-1}
|
||||
enablePanDownToClose
|
||||
>
|
||||
<WebView
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: Palette.darkPurple,
|
||||
borderWidth: 0,
|
||||
}}
|
||||
source={{ uri: dynamicWebViewUrl }}
|
||||
onNavigationStateChange={(event) => handleCloseWebView(event.url)}
|
||||
/>
|
||||
</BottomSheetContainer>
|
||||
</WebViewContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useWebView = () => useContext(WebViewContext);
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { MinuitProvider } from "react-native-minuit";
|
||||
|
||||
import SharedProviders from "./SharedProviders";
|
||||
import NotificationProvider from "./NotificationProvider";
|
||||
|
||||
import { Palette } from "../styles";
|
||||
|
||||
export default ({ children }) => {
|
||||
return (
|
||||
<MinuitProvider
|
||||
projectID={"minuitapp"}
|
||||
themeColors={{
|
||||
primary: Palette.primary,
|
||||
secondary: Palette.darkPurple,
|
||||
destructive: "red",
|
||||
}}
|
||||
>
|
||||
<SharedProviders>
|
||||
<NotificationProvider>{children}</NotificationProvider>
|
||||
</SharedProviders>
|
||||
</MinuitProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
|
||||
import LoadingProvider from "./LoadingProvider";
|
||||
import TooltipProvider from "./TooltipProvider";
|
||||
import StripeEmbeddedProvider from "./StripeEmbeddedProvider";
|
||||
|
||||
import SharedProviders from "./SharedProviders";
|
||||
|
||||
export default ({ children }) => {
|
||||
return (
|
||||
<LoadingProvider>
|
||||
<TooltipProvider>
|
||||
<StripeEmbeddedProvider>
|
||||
<SharedProviders>{children}</SharedProviders>
|
||||
</StripeEmbeddedProvider>
|
||||
</TooltipProvider>
|
||||
</LoadingProvider>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user