This commit is contained in:
Philip Cesar Garay
2025-07-31 21:25:33 +08:00
parent 5cfbc81e3a
commit 84fc719a10
307 changed files with 46470 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
import useDebounce from './useDebounce';
export {useDebounce};
+51
View File
@@ -0,0 +1,51 @@
import { useEffect, useState } from "reactn";
import algoliasearch from "algoliasearch/lite";
export default ({
query = "",
options = {},
algoliaObject: { index = null, projectID, publicKey },
documentID = "id",
}) => {
const [result, setResult] = useState([]);
const [indexAlgolia, setIndexAlgolia] = useState(null);
useEffect(() => {
if (!indexAlgolia) {
setIndexAlgolia(algoliasearch(projectID, publicKey).initIndex(index));
}
return () => {
setIndexAlgolia(null);
};
}, [index]);
useEffect(() => {
if (indexAlgolia) {
getSearchResult();
}
}, [query, indexAlgolia]);
const getSearchResult = async () => {
try {
if (query.length > 1) {
const { hits } = await indexAlgolia.search(query, options);
setResult(
hits.map((itemHit) => ({
...itemHit,
[documentID]: itemHit.objectID,
}))
);
} else {
setResult([]);
}
} catch (e) {
setResult([]);
console.log("ALGOLIA" + e);
}
};
return result;
};
+32
View File
@@ -0,0 +1,32 @@
import { useEffect, useState } from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
const useAsyncStorage = (key, initialValue) => {
const [data, setData] = useState(initialValue);
const [retrievedFromStorage, setRetrievedFromStorage] = useState(false);
useEffect(() => {
(async () => {
try {
const value = await AsyncStorage.getItem(key);
console.log(`Retrieved data for ${key}: ${value}`);
setData(JSON.parse(value) || initialValue);
setRetrievedFromStorage(true);
} catch (error) {
console.error("useAsyncStorage getItem error:", error);
}
})();
}, [key, initialValue]);
const setNewData = async (value) => {
try {
await AsyncStorage.setItem(key, JSON.stringify(value));
setData(value);
} catch (error) {
console.error("useAsyncStorage setItem error:", error);
}
};
return [data, setNewData, retrievedFromStorage];
};
export default useAsyncStorage;
+167
View File
@@ -0,0 +1,167 @@
import { useState, setGlobal } from "reactn";
import { useEffect } from "react";
import _ from "lodash";
export default function useDataFromRef({
ref,
initialState = [],
refreshArray = [],
initialDocumentRef = null,
documentID = "id",
listener = false,
condition = true,
simpleRef = false,
usePagination = false,
updateGlobalState = null,
batchSize = 4,
format = null,
onUpdate = () => null,
}) {
const [loading, setLoading] = useState(true);
const [endReached, setEndReached] = useState(false);
const [data, setData] = useState(initialState);
const [lastVisible, setLastVisible] = useState(null);
useEffect(() => {
if (condition) {
if (listener && !usePagination) {
const subData = getListenerData();
return () => subData?.();
} else {
resetState();
getData({ paginate: usePagination });
}
} else {
resetState();
}
}, [...refreshArray, condition, usePagination]);
const loadMore = () => {
if (!loading && !endReached) {
getData({ paginate: true });
console.log("Loading more");
}
};
const resetState = () => {
if (!_.isEqual(data, initialState)) {
onUpdate(initialState);
}
console.log("Reset state", initialState);
setEndReached(false);
setLastVisible(null);
setData(initialState);
};
const getData = async ({ paginate = false } = {}) => {
try {
setLoading(true);
let newData = simpleRef ? null : [];
let initialDoc = null;
if (initialDocumentRef && !lastVisible && !simpleRef) {
initialDoc = await initialDocumentRef.get();
}
const dynamicRef =
paginate && lastVisible
? ref.startAfter(lastVisible).limit(batchSize)
: initialDoc
? ref.startAt(initialDoc).limit(batchSize)
: paginate
? ref.limit(batchSize)
: ref;
const dataSnap = await dynamicRef.get();
if (simpleRef && dataSnap.data()) {
newData = { ...dataSnap.data(), [documentID]: dataSnap.id };
} else if (!simpleRef && dataSnap.docs.length > 0) {
newData = dataSnap.docs.map((item) => {
return { ...item.data(), [documentID]: item.id };
});
if (paginate) {
if (batchSize !== dataSnap.docs.length) {
setEndReached(true);
}
setLastVisible(dataSnap.docs[dataSnap.docs.length - 1]);
}
}
if (newData) {
if (paginate) {
console.log("new pagination");
await updateData([...data, ...newData]);
} else {
await updateData(newData);
}
} else {
console.log("NO DATA");
}
} catch (e) {
if (e.code === "firestore/permission-denied") {
console.warn(
"Permission denied for ref: ",
ref?._collectionPath?.relativeName
);
} else {
console.log(e);
}
await updateData([]);
} finally {
setLoading(false);
}
};
const getListenerData = () => {
return ref?.onSnapshot(
async (dataSnap) => {
let newData;
if (simpleRef) {
newData = { ...dataSnap.data(), [documentID]: dataSnap.id };
} else {
newData = dataSnap.docs.map((item) => {
return { ...item.data(), [documentID]: item.id };
});
}
await updateData(newData);
setLoading(false);
},
async (e) => {
if (e.code === "firestore/permission-denied") {
console.warn(
"Permission denied for ref: ",
ref?._collectionPath?.relativeName
);
} else {
console.log(e);
}
await updateData([]);
setLoading(false);
}
);
};
const updateData = async (newData) => {
setData(format ? await format(newData) : newData);
onUpdate(newData);
if (updateGlobalState) {
setGlobal({ [updateGlobalState]: data });
}
};
return { data, setData, loading, loadMore };
}
+73
View File
@@ -0,0 +1,73 @@
import { useState, useEffect } from "react";
export const useFirestorePagination = ({
ref = null,
refreshArray = [],
condition = false,
documentID = "id",
batchSize = 10,
maxLoads = 15, // Maximum number of times loadMore can be called
}) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [lastVisible, setLastVisible] = useState(null);
const [error, setError] = useState(null);
const [loadMoreCount, setLoadMoreCount] = useState(0); // Counter for loadMore invocations
useEffect(() => {
let unsubscribe = () => {};
if (condition && ref) {
setLoading(true);
unsubscribe = ref.limit(batchSize).onSnapshot(
(snapshot) => {
const fetchedDocuments = snapshot.docs.map((doc) => ({
[documentID]: doc.id,
...doc.data(),
}));
const lastVisibleDocument = snapshot.docs[snapshot.docs.length - 1];
setData(fetchedDocuments);
setLastVisible(lastVisibleDocument);
setLoading(false);
},
(err) => {
setError(err);
setLoading(false);
}
);
}
return () => unsubscribe();
}, [...refreshArray, condition, ref, batchSize]);
const loadMore = () => {
if (lastVisible && !loading && loadMoreCount < maxLoads) {
setLoading(true);
setLoadMoreCount((count) => count + 1); // Increment loadMore counter
ref
.startAfter(lastVisible)
.limit(batchSize)
.onSnapshot(
(snapshot) => {
const newDocuments = snapshot.docs.map((doc) => ({
[documentID]: doc.id,
...doc.data(),
}));
setData((prev) => [...prev, ...newDocuments]);
setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
setLoading(false);
},
(err) => {
setError(err);
setLoading(false);
}
);
}
};
// Function to check if more data can be loaded
const canLoadMore = loadMoreCount < maxLoads;
return { data, loading, error, loadMore, canLoadMore };
};
+102
View File
@@ -0,0 +1,102 @@
import { useState, useEffect } from "react";
import { Platform } from "react-native";
import { responsiveWidth } from "react-native-responsive-dimensions";
const getLayoutType = () => {
const windowWidth = responsiveWidth(100);
const isWeb = Platform.OS === "web";
const isNative = !isWeb;
const isIOS = Platform.OS === "ios";
const isTauri = typeof window.__TAURI__ !== "undefined";
const superSmallDesktopBreakpoint = 800;
const smallDesktopBreakpoint = 1100;
const mediumDesktopBreakpoint = smallDesktopBreakpoint * 1.2;
const largeDesktopBreakpoint = smallDesktopBreakpoint * 1.5;
const isDesktop = windowWidth > superSmallDesktopBreakpoint;
const isDesktopWeb = isDesktop && isWeb;
const isMobile = !isDesktop;
const isMobileWeb = isMobile && isWeb;
const isMobileNative = isMobile && !isWeb;
const isDesktopNative = isDesktop && !isWeb;
const isSuperSmallDesktop =
windowWidth > superSmallDesktopBreakpoint &&
windowWidth < smallDesktopBreakpoint;
const isSmallDesktop =
windowWidth > smallDesktopBreakpoint &&
windowWidth < mediumDesktopBreakpoint;
const isMediumDesktop = windowWidth > mediumDesktopBreakpoint;
const isLargeDesktop = windowWidth > largeDesktopBreakpoint;
const sidebarWidth = windowWidth > 1350 ? 400 : 320;
return {
isWeb,
isNative,
isIOS,
isTauri,
superSmallDesktopBreakpoint,
smallDesktopBreakpoint,
mediumDesktopBreakpoint,
largeDesktopBreakpoint,
isDesktop,
isSuperSmallDesktop,
isSmallDesktop,
isMediumDesktop,
isLargeDesktop,
isDesktopWeb,
isMobile,
isMobileWeb,
isMobileNative,
isDesktopNative,
sidebarWidth,
windowWidth,
};
};
export const {
isWeb,
isNative,
isIOS,
isTauri,
superSmallDesktopBreakpoint,
smallDesktopBreakpoint,
mediumDesktopBreakpoint,
largeDesktopBreakpoint,
isDesktop,
isSuperSmallDesktop,
isSmallDesktop,
isMediumDesktop,
isLargeDesktop,
isDesktopWeb,
isMobile,
isMobileWeb,
isMobileNative,
sidebarWidth,
windowWidth,
} = getLayoutType();
export default () => {
const [layoutType, setLayoutType] = useState(getLayoutType());
useEffect(() => {
if (isWeb) {
const handleResize = () => {
setLayoutType(getLayoutType());
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}
}, []);
return layoutType;
};
+72
View File
@@ -0,0 +1,72 @@
import { useGlobal, useContext } from "reactn";
import firebase from "../config/firebase";
import { checkBillingDetails } from "../helpers";
import { StripeEmbeddedContext } from "../providers/StripeEmbeddedProvider";
import { useWebView } from "../providers/WebViewProvider";
import useLayoutType from "./useLayoutType";
const usePaymentSession = () => {
const [, setIsLoading] = useGlobal("_isLoading");
const [, setTooltip] = useGlobal("_tooltip");
const { isNative } = useLayoutType();
const { setWebViewUrl } = useWebView();
const [currentProjectID] = useGlobal("currentProjectID");
const { setClientSecret } = useContext(StripeEmbeddedContext);
const onCreatePaymentSession = async ({
numberOfCoin = 0,
taskList = [],
} = {}) => {
checkBillingDetails({
onValidBillingDetails: () => {
triggerPayment({ numberOfCoin, taskList });
},
});
};
const triggerPayment = async ({ numberOfCoin = 0, taskList = [] }) => {
try {
setIsLoading(true);
const { data } = await firebase
.functions()
.httpsCallable("coins-createPaymentSession")({
numberOfCoin,
taskList,
projectID: currentProjectID,
type: "CHECKOUT",
isEmbedded: !isNative,
});
if (data) {
if (isNative) {
setWebViewUrl(data);
} else {
setClientSecret(data);
}
} else {
throw new Error("Erreur lors de la création du paiement");
}
} catch (error) {
console.log(error);
setTooltip({
type: "error",
text: error.message,
});
} finally {
setIsLoading(false);
}
};
return {
onCreatePaymentSession,
};
};
export default usePaymentSession;