diff --git a/frontend/nextjs/src/components/ui/infinite-scroller.tsx b/frontend/nextjs/src/components/ui/infinite-scroller.tsx
index 25c3377..129a696 100644
--- a/frontend/nextjs/src/components/ui/infinite-scroller.tsx
+++ b/frontend/nextjs/src/components/ui/infinite-scroller.tsx
@@ -71,7 +71,7 @@ export default function InfiniteScroll({
getNextPageParam ||
((lastPage, allPages) => {
if (max && allPages.length >= max) return undefined;
- return lastPage[lastPage.length - 1]?.timestamp;
+ return lastPage[lastPage.length - 1]
}),
select: (data) => {
return {
diff --git a/frontend/nextjs/src/components/ui/use-toast.ts b/frontend/nextjs/src/components/ui/use-toast.ts
index d251748..17ee01c 100644
--- a/frontend/nextjs/src/components/ui/use-toast.ts
+++ b/frontend/nextjs/src/components/ui/use-toast.ts
@@ -23,7 +23,6 @@ type ToasterToast = ToastProps & {
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
- loadProgress?: number
}
const actionTypes = {
diff --git a/frontend/nextjs/src/lib/hooks/useBackend.tsx b/frontend/nextjs/src/lib/hooks/useBackend.tsx
index 16c38a5..c1dc0b9 100644
--- a/frontend/nextjs/src/lib/hooks/useBackend.tsx
+++ b/frontend/nextjs/src/lib/hooks/useBackend.tsx
@@ -162,10 +162,12 @@ export default function useBackend() {
const { data: exptLevels } = useQuery({
queryKey: ["exptlevels"],
queryFn: async () => {
- const levelsPromises = exptLevelKeys.map((key) =>
- emtMarketplace.exptLevels(key)
- );
+ const levelsPromises = exptLevelKeys.map(async (key) => {
+ const l = await emtMarketplace.exptLevels(key);
+ return { requiredMent: Number(l[0]), receivableExpt: Number(l[1]) };
+ });
const levels = await Promise.all(levelsPromises);
+ console.log("exptLevels", levels);
return levels;
},
throwOnError: (error) => {
@@ -196,9 +198,12 @@ export default function useBackend() {
const contract = stableCoin.attach(
token.address!
) as typeof stableCoin;
- const balance = await contract.balanceOf(currentUserId);
+ const balance = Number(await contract.balanceOf(currentUserId));
return {
- [token.name]: parseFloat(ethers.formatUnits(balance, 6)).toFixed(2),
+ [token.name]:
+ token.name === "USDT"
+ ? parseFloat(ethers.formatUnits(balance, 6)).toFixed(2)
+ : balance,
};
},
enabled: !wrongChain && !!user?.uid,
@@ -277,19 +282,28 @@ export default function useBackend() {
}
}
async function claimMent() {
- function getMentClaimed(receipt: ContractTransactionReceipt) {
- const filter = emtMarketplace.filters.MentClaimed().fragment;
- let mentClaimed = 0;
+ function getTokenIdsClaimed(receipt: ContractTransactionReceipt) {
+ const fragment = expertToken.filters.Transfer().fragment;
// console.log(receipt?.logs)
- receipt?.logs.some((log) => {
- const d = emtMarketplace.interface.decodeEventLog(filter, log.data);
- console.log("l", d);
- mentClaimed = Number(d[1]);
- return false;
+ const tokenIds: number[] = [];
+ receipt?.logs.forEach((log, i) => {
+ try {
+ const d = emtMarketplace.interface.decodeEventLog(
+ fragment,
+ log.data,
+ log.topics
+ );
+ // console.log("log" + i, d);
+ // mentClaimed = Number(d[2]);
+ tokenIds.push(Number(d[2]));
+ } catch (e) {
+ console.log("log" + i, log, e);
+ }
});
- console.log("mentClaimed", mentClaimed);
- return mentClaimed;
+ console.log("tokenIds", tokenIds);
+ return tokenIds;
}
+
if (!user?.uid) {
throw new Error("User not logged in");
}
@@ -297,17 +311,22 @@ export default function useBackend() {
const tx = await emtMarketplace.claimMent();
const receipt = await tx!.wait();
console.log("claimed ment");
- // @ts-ignore
- const mentClaimed = getMentClaimed(receipt);
+ const tokenIds = getTokenIdsClaimed(receipt!);
const historyItem: Omit = {
type: "ment",
- amount: mentClaimed,
+ amount: tokenIds.length,
+ tokenIds,
uid: user.uid,
};
const claimHistoryItem =
await saveClaimHistoryItemToFirestore(historyItem);
const newMent = await updateUserMentInFirestore();
- return { mentClaimed, newMent, claimHistoryItem };
+ return {
+ mentClaimed: tokenIds.length,
+ tokenIds,
+ newMent,
+ claimHistoryItem,
+ };
} catch (err: any) {
console.log(err);
throw new Error("Error claiming ment. Message: " + err.message);
@@ -316,62 +335,86 @@ export default function useBackend() {
async function fetchMentAndLevel(uid = user?.uid): Promise<[number, number]> {
console.log("fetchMentAndLevel", uid);
- let ment = 0;
+ let ment = balances.MENT;
if (uid !== user?.uid) {
ment = await fetchMent(uid);
}
console.log("ment2222: ", ment);
- const level = exptLevels
- ? Object.entries(exptLevels).find(
- ([key, level]) => (ment || 0) > level.requiredMent
- )?.[0] || 0
+ let userlevel = 0;
+ exptLevels
+ ? exptLevels.some((level, index) => {
+ if ((ment || 0) >= level.requiredMent) {
+ userlevel = index + 1;
+ return false;
+ } else return true;
+ })
: 0;
- console.log("level: ", level);
- return [Number(ment), Number(level)];
+ console.log("level: ", userlevel);
+ return [Number(ment), Number(userlevel)];
}
async function claimExpt() {
- function getExptClaimed(receipt: ContractTransactionReceipt) {
+ function extractExptClaimedFromTxReceipt(
+ receipt: ContractTransactionReceipt
+ ) {
const filter = emtMarketplace.filters.ExptClaimed().fragment;
let exptClaimed = 0;
// console.log(receipt?.logs)
receipt?.logs.some((log) => {
- const d = emtMarketplace.interface.decodeEventLog(filter, log.data);
- console.log("l", d);
- exptClaimed = Number(d[1]);
- return false;
+ try {
+ const d = emtMarketplace.interface.decodeEventLog(filter, log.data);
+ console.log("l", d);
+ exptClaimed = Number(d[1]);
+ return true;
+ } catch (err) {
+ return false;
+ }
});
console.log("exptClaimed", exptClaimed);
return exptClaimed;
}
if (!user?.uid) {
+ toast({
+ title: "Login",
+ description: "Please login to claim expt",
+ });
throw new Error("User not logged in");
}
- try {
- const [_, level] = await fetchMentAndLevel();
-
- if (!level) {
- throw new Error("Not qualified for expt");
+ for (const i of exptLevelKeys) {
+ const level = i + 1;
+ const t = loadingToast("Claiming expt", 1);
+ try {
+ const tx = await emtMarketplace.claimExpt(level);
+ t("mining transaction", 50);
+ const receipt = await tx!.wait();
+ const exptClaimed = extractExptClaimedFromTxReceipt(receipt!);
+ const historyItem: Omit = {
+ type: "expt",
+ amount: exptClaimed,
+ level: Number(level),
+ uid: user.uid,
+ };
+ t("almost there", 80);
+ const val = await expertToken.balanceOf(user.uid);
+ const newExptBalance = Number(val);
+ const claimHistoryItem =
+ await saveClaimHistoryItemToFirestore(historyItem);
+ console.log("claimed expt");
+ t(exptClaimed + " Expts claimed for level " + level, 100);
+ return { newExptBalance, claimHistoryItem };
+ } catch (err: any) {
+ if (
+ err.message.includes("Not qualified for level") ||
+ err.message.includes("Level has already been claimed") ||
+ err.message.includes("Expt Level does not exists")
+ ) {
+ continue;
+ } else {
+ console.log(err);
+ t("Error claiming expt at level " + level, 0, true);
+ throw new Error("Error claiming expt. Message: " + err.message);
+ }
}
- const tx = await emtMarketplace.claimExpt(level);
- const receipt = await tx!.wait();
- const val = await expertToken.balanceOf(user.uid);
- const newExptBalance = Number(val);
- const exptClaimed = getExptClaimed(receipt!);
-
- const historyItem: Omit = {
- type: "expt",
- amount: exptClaimed,
- level: level,
- uid: user.uid,
- };
- const claimHistoryItem =
- await saveClaimHistoryItemToFirestore(historyItem);
- console.log("claimed expt. New expt balance: ", newExptBalance);
- return { newExptBalance, claimHistoryItem };
- } catch (err: any) {
- console.log(err);
- throw new Error("Error claiming ment. Message: " + err.message);
}
}
@@ -494,7 +537,7 @@ export default function useBackend() {
const userDocRef = doc(USERS_COLLECTION, owner);
const userDoc = await getDoc(userDocRef);
const author = userDoc.data() as Content["author"];
- console.log('usebackend author: ', userDoc.data())
+ console.log("usebackend author: ", userDoc.data());
const votes = await fetchPostVotes(id);
return {
@@ -637,9 +680,17 @@ export default function useBackend() {
}
try {
console.log("fetching unclaimed expt");
- const [_, level] = await fetchMentAndLevel();
- const val = await emtMarketplace.unclaimedExpt(user.uid, level || 1);
- const unclaimedExpt = Number(val);
+ let unclaimedExpt = 0;
+ for (let i = 0; i < exptLevelKeys.length; i++) {
+ try {
+ const val = await emtMarketplace.unclaimedExpt(user.uid, i + 1);
+ unclaimedExpt += Number(val);
+ } catch (err) {
+ console.log(
+ "Error fetching unclaimed expt for level " + (i + 1) + err
+ );
+ }
+ }
console.log("unclaimed expt:", unclaimedExpt);
return unclaimedExpt;
} catch (err: any) {
@@ -710,7 +761,7 @@ export default function useBackend() {
* @param uid - The user ID. If not provided, the current user's ID will be used.
* @returns A promise that resolves to an array of followings IDs.
*/
- async function getUserFollowingsIds(uid = user?.uid, size=10) {
+ async function getUserFollowingsIds(uid = user?.uid, size = 10) {
try {
const q = query(
collectionGroup(firestore, "followers"),
@@ -719,7 +770,7 @@ export default function useBackend() {
);
console.log("isfollowing");
const snap = await getDocs(q);
- console.log(snap)
+ console.log(snap);
const uids = snap.docs.map((d) => d.ref.path.split("/")[1]);
return uids;
} catch (e) {
@@ -756,8 +807,8 @@ export default function useBackend() {
}
if (filters?.isFollowing) {
const uidsOfFollowings = await getUserFollowingsIds();
- console.log('pppp, fetching posts following', uidsOfFollowings)
- q = query(q, where('owner', "in", uidsOfFollowings));
+ console.log("pppp, fetching posts following", uidsOfFollowings);
+ q = query(q, where("owner", "in", uidsOfFollowings));
}
const querySnapshot = await getDocs(q);
@@ -1034,7 +1085,10 @@ export default function useBackend() {
//TODO: @Jovells enforce this at rules level and remove this check to avoid extra roundrtip to db
if (await checkFollowing(id)) return false;
- await setDoc(userFollowersRef, { timestamp: serverTimestamp(), uid: user.uid });
+ await setDoc(userFollowersRef, {
+ timestamp: serverTimestamp(),
+ uid: user.uid,
+ });
createNotification({ type: "follow", recipients: [id] });
@@ -1119,22 +1173,34 @@ export default function useBackend() {
}
}
if (!user?.uid) {
+ toast({
+ title: "Login",
+ description: "Please login to list expts",
+ });
throw new Error("User not logged in");
}
+ const t = loadingToast("Listing Expts", 1);
try {
- await expertToken.setApprovalForAll(emtMarketplace.target, true);
- console.log("listing expts in contract");
+ t("seeking approval to transfer expts", 10);
+ const approvalTxn = await expertToken.setApprovalForAll(emtMarketplace.target, true);
+ t("mining transaction", 20);
+ await approvalTxn!.wait();
+ t("listing expts in contract", 40);
const tx = await emtMarketplace.offerExpts(
listing.tokenIds,
stableCoin.target,
listing.price
);
+ t("mining transaction", 50);
await tx!.wait();
console.log("listed expts in contract");
+ t("Finalising Listing", 70);
const id = await saveExptListingToFirestore(listing);
+ t("Expts listed successfully", 100);
return id;
} catch (err: any) {
console.log(err);
+ t("Error listing expts", 0, true);
throw new Error("Error listing expts. Message: " + err.message);
}
}
@@ -1147,58 +1213,119 @@ export default function useBackend() {
* @returns A promise that resolves to an array of expt listings with author profile.
*/
async function fetchExptListings(
- lastDocTimeStamp?: Timestamp,
+ lastDoc?: ExptListingWithAuthorProfile,
size = 1,
filters?: ExptFilters
): Promise {
- // Split the tokenIds array into chunks of 30 because of firebase array-contains limit
- if (filters?.mentee) {
- const ownedExpts = await fetchOwnedExptIds(filters.mentee);
- filters.tokenIds = ownedExpts;
- }
- const tokenIdsChunks = filters?.tokenIds
- ? chunkArray(filters.tokenIds, 30)
- : [[]];
-
- const listingPromises = tokenIdsChunks.map(async (tokenIds) => {
- let q = query(
- EXPT_LISTINGS_COLLECTION,
- orderBy("timestamp", "desc"),
- limit(size)
- );
+ //fetch expts that the user bought from another
+ try {
- if (lastDocTimeStamp) {
- q = query(q, startAfter(lastDocTimeStamp));
- }
- if (filters?.tags) {
- q = query(q, where("tags", "array-contains-any", filters.tags));
- }
- if (filters?.author) {
- q = query(q, where("owner", "==", filters.author));
+ if (filters?.mentee) {
+ const ownedExpts = await fetchOwnedExptIds(filters.mentee);
+ filters.tokenIds = ownedExpts;
}
- if (tokenIds.length > 0) {
- q = query(q, where("tokenIds", "array-contains-any", tokenIds));
+ // fetch expts that the user listed
+ if (filters?.mentor) {
+ const ownedExpts = await fetchOwnedExptIds(filters.mentor);
+ filters.tokenIds = ownedExpts;
}
-
- const querySnapshot = await getDocs(q);
- if (querySnapshot.empty) return [];
-
+ // Split the tokenIds array into chunks of 30 because of firebase array-contains limit
+ const tokenIdsChunks = filters?.tokenIds
+ ? chunkArray(filters.tokenIds, 30)
+ : [[]];
+
+ const listingPromises = tokenIdsChunks.map(async (tokenIds) => {
+ // when fetching the listings for the mentee we use an inequality operator in the author field
+ // which requires us to order by the author field and start after the last doc's author
+ // this is because firebase does not allow inequality operators on multiple fields
+ // https://firebase.google.com/docs/firestore/query-data/order-limit-data#limitations
+ // so we check if the mentee filter is NOT present and if so we order by and starty after the timestamp
+ let q = query(
+ EXPT_LISTINGS_COLLECTION,
+ limit(size)
+ );
+ if (!(filters?.mentee) ) {
+ q = query(q, orderBy("timestamp", "desc"), startAfter(lastDoc?.timestamp || ''));
+ }
+ if (filters?.mentee) {
+ q = query(q, orderBy("author"), where("author", "!=", filters.mentee), startAfter(lastDoc?.author || ''));
+ }
+ // fetch expts that the user listed
+ if (filters?.mentor) {
+ q = query(q, where("author", "==", filters.mentor));
+ }
+ if (filters?.tags) {
+ q = query(q, where("tags", "array-contains-any", filters.tags));
+ }
+ if (filters?.author) {
+ q = query(q, where("owner", "==", filters.author));
+ }
+ if (tokenIds.length > 0) {
+ q = query(q, where("tokenIds", "array-contains-any", tokenIds));
+ }
+ console.log('query', )
+
+ const querySnapshot = await getDocs(q);
+ if (querySnapshot.empty) return [];
+
const withAuthorPromises = querySnapshot.docs.map(async (doc) => {
const listing = doc.data() as ExptListingWithAuthorProfile;
listing.id = doc.id;
listing.authorProfile = await fetchProfile(listing.author);
+ listing.tokensOfCurrentUser = listing.tokenIds.filter(
+ (tokenId) => filters?.tokenIds?.includes(tokenId)
+ );
return listing;
});
return await Promise.all(withAuthorPromises);
});
+
+ const listingsArrays = await Promise.all(listingPromises);
+
+ // Flatten the array of arrays into a single array
+ const listings = listingsArrays.flat();
+ return listings;
+ } catch (err: any) {
+ console.log("Error fetching expt listings. Details: " + err);
+ throw new Error(err);
+ }
+
+ }
- const listingsArrays = await Promise.all(listingPromises);
+ async function fetchExpts(
+ lastDoc?: { id: string; listing: ExptListingWithAuthorProfile },
+ size = 1,
+ filters?: ExptFilters
+ ) {
+ try {
+ const listings = await fetchExptListings(lastDoc?.listing, size, filters);
+ const exptsPromises = listings.map((listing) => listing.tokensOfCurrentUser!.map( async (tokenId) => {
+ const remainingSessions = await getRemainingSessions(listing, tokenId);
+ return {
+ id: tokenId,
+ listing,
+ remainingSessions,
+ }
+ }
+ )
+ ).flat()
+ return await Promise.all(exptsPromises);
+ } catch (err: any) {
+ console.log("Error fetching expts. Details: " + err);
+ throw new Error(err);
+ }
- // Flatten the array of arrays into a single array
- const listings = listingsArrays.flat();
+ async function getRemainingSessions(listing: ExptListingWithAuthorProfile, tokenId: number) {
+ const q = query(BOOKINGS_COLLECTION, where('exptTokenId', '==', tokenId))
+ const bookings = (await getDocs(q)).docs.map(d=> d.data()) as Booking[];
+ const bookedSessions = bookings.reduce((acc, booking) => {
+ return acc + booking.sessionCount;
+ }, 0)
- return listings;
+ return listing.sessionCount - bookedSessions;
}
+}
+
/**
* Fetches the list of bookings based on the provided filters.
@@ -1208,7 +1335,7 @@ export default function useBackend() {
* @returns A promise that resolves to an array of bookings.
*/
async function fetchBookings(
- lastDocTimeStamp?: Timestamp,
+ lastDoc? : Booking,
size = 1,
filters?: BookingFilters
): Promise {
@@ -1218,8 +1345,8 @@ export default function useBackend() {
limit(size)
);
- if (lastDocTimeStamp) {
- q = query(q, startAfter(lastDocTimeStamp));
+ if (lastDoc) {
+ q = query(q, startAfter(lastDoc.timestamp));
}
if (filters?.tags) {
q = query(q, where("tags", "array-contains-any", filters.tags));
@@ -1249,6 +1376,40 @@ export default function useBackend() {
return bookings;
}
+ async function bookExpert(
+ data: Omit<
+ Booking,
+ "id" | "timestamp" | "mentee" | "isCompleted" | "exptListing"
+ >
+ ) {
+ if (!user?.uid) {
+ toast({
+ title: "Login",
+ description: "Please login to book an expert",
+ });
+ throw new Error("User not logged in");
+ }
+ const t = loadingToast("Booking Expert", 1);
+ try {
+ const docRef = doc(BOOKINGS_COLLECTION);
+ const booking: Omit = {
+ ...data,
+ isCompleted: false,
+ mentee: user?.uid!,
+ timestamp: serverTimestamp(),
+ };
+
+ await setDoc(docRef, booking);
+ console.log("saved booking to firestore");
+ t("Expert Booked successfully", 100);
+ return { ...booking, id: docRef.id } satisfies Booking;
+ } catch (err: any) {
+ console.log(err);
+ t("Error booking expert", 0, true);
+ throw new Error("Error booking expert. Message: " + err.message);
+ }
+ }
+
// Helper function to split an array into chunks
function chunkArray(array: T[], chunkSize: number): T[][] {
return Array(Math.ceil(array.length / chunkSize))
@@ -1329,7 +1490,7 @@ export default function useBackend() {
user?.uid!
);
const userFollowersSnap = await getDoc(userFollowersRef);
- return !!userFollowersSnap.data()?.uid
+ return !!userFollowersSnap.data()?.uid;
} catch (err: any) {
console.log("error checking following", err.message, id, user);
}
@@ -1371,6 +1532,7 @@ export default function useBackend() {
if (!user?.uid) {
throw new Error("User not logged in");
}
+ const t = loadingToast("Buying Expts", 1);
async function updateListingInFireStore(boughtTokenId: number) {
try {
updateDoc(doc(EXPT_LISTINGS_COLLECTION, listing.id), {
@@ -1382,34 +1544,45 @@ export default function useBackend() {
}
}
try {
+ t("seeking approval to transfer stablecoin", 10);
console.log("approving stableCoin transfer in contract");
const tx = await stableCoin.approve(
emtMarketplace.target,
listing.price * 10 ** 6
);
+ t("mining transaction", 20);
const receipt = await tx.wait();
console.log(receipt);
console.log("buying expts in contract");
+ t("buying expts in contract", 40);
let exptToBuyIndex = listing.remainingTokenIds.length - 1;
//this loop is here because the chosen expt to buy
// might have been bought already before this user completes the purchase
while (exptToBuyIndex >= 0) {
const tokenToBuyId = listing.remainingTokenIds[exptToBuyIndex];
+ let loadingPercent = 40 + ((listing.remainingTokenIds.length - exptToBuyIndex) / listing.remainingTokenIds.length) * 30;
try {
console.log("tokenToBuyId", tokenToBuyId, listing);
const tx = await emtMarketplace.buyExpt(tokenToBuyId);
await tx!.wait();
- console.log("bought expts in contract");
+ console.log("bought expt in contract");
+ t("Finalising Purchase", 80);
await updateListingInFireStore(tokenToBuyId);
+ t("Expt bought successfully", 100);
return true;
} catch (err: any) {
if (err.message.includes("No deposit yet for token id")) {
console.log("this expt has probably been bought. Trying the next");
exptToBuyIndex = exptToBuyIndex - 1;
- } else throw new Error(err);
+ t("buying expts in contract", loadingPercent);
+ } else {
+ t("Error buying expts", 0, true);
+ throw new Error(err);}
}
+ t("this listing has been sold out", undefined, true);
}
+
} catch (err: any) {
console.log("Error buying expts. Message: " + err.message);
return false;
@@ -1435,6 +1608,56 @@ export default function useBackend() {
}
}
+ async function fetchOwnedExptIdsOfMentee(uid = user?.uid) {
+ if (!uid) return [];
+ try {
+ console.log("fetching tokens of user");
+ const val = await expertToken.tokensOfOwner(uid);
+ const tokenIds = val.map((id) => Number(id));
+ console.log("tokens of owner", tokenIds);
+ return tokenIds;
+ } catch (err: any) {
+ console.log("error fetching owned expts ids ", err);
+ return [];
+ }
+ }
+
+ async function fetchMentorExptIds(uid = user?.uid) {
+ if (!uid) return [];
+ console.log("fetching mentor expts ids");
+ try {
+ const q = query(
+ CLAIM_HISTORY_COLLECTION,
+ where("uid", "==", uid),
+ where("type", "==", "expt")
+ );
+ const querySnapshot = await getDocs(q);
+ const tokenIds = querySnapshot.docs.reduce((acc, doc) => {
+ const data = doc.data();
+ if (data.tokenId) acc.push(...data.tokenIds);
+ return acc;
+ }, [] as number[]);
+ return tokenIds;
+ } catch (err: any) {
+ console.log("error fetching mentor expts ids ", err);
+ return [];
+ }
+ }
+
+ async function fetchMenteeExptIds(uid = user?.uid) {
+ if (!uid) return [];
+ console.log("fetching mentee expts ids");
+ try {
+ const q = query(
+ EXPT_LISTINGS_COLLECTION,
+ where(`buyers.${uid}`, ">", "")
+ );
+ } catch (err: any) {
+ console.log("error fetching mentee expts ids ", err);
+ return [];
+ }
+ }
+
/**
* Fetches the profiles based on the provided filters.
* @param lastdocParam - The last document parameter.
@@ -1451,7 +1674,11 @@ export default function useBackend() {
if (filters?.ment) {
console.log("filters.ment", filters);
- q = query(q, orderBy("ment", filters.ment), startAfter(lastdoc?.ment || ""));
+ q = query(
+ q,
+ orderBy("ment", filters.ment),
+ startAfter(lastdoc?.ment || "")
+ );
}
if (filters?.level) {
q = query(
@@ -1464,12 +1691,16 @@ export default function useBackend() {
}
if (filters?.numFollowers) {
//TODO @Jovells update follow function to store count in firestore
- q = query(q, orderBy("numFollowers", filters.numFollowers), startAfter(lastdoc?.numFollowers || ""));
+ q = query(
+ q,
+ orderBy("numFollowers", filters.numFollowers),
+ startAfter(lastdoc?.numFollowers || "")
+ );
}
if (filters?.usernames) {
q = query(
q,
- orderBy('username'),
+ orderBy("username"),
startAfter(lastdoc?.username || ""),
where(
"usernameLowercase",
@@ -1479,44 +1710,47 @@ export default function useBackend() {
);
}
if (filters?.isFollowing) {
- console.log('fetching profiles following')
+ console.log("fetching profiles following");
const uidsOfFollowings = await getUserFollowingsIds();
filters.uids = filters.uids
? filters.uids.filter((value) => uidsOfFollowings.includes(value))
: uidsOfFollowings;
}
if (filters?.uids) {
- q = query(q, where(documentId(), "in", filters.uids), orderBy(documentId()), startAfter(lastdoc?.uid || ""));
+ q = query(
+ q,
+ where(documentId(), "in", filters.uids),
+ orderBy(documentId()),
+ startAfter(lastdoc?.uid || "")
+ );
}
if (filters?.isNotFollowing) {
- q= query(q, orderBy(documentId()), startAfter(lastdoc?.uid || " "))
- console.trace('1. fetching profiles not following', size)
+ q = query(q, orderBy(documentId()), startAfter(lastdoc?.uid || " "));
+ console.trace("1. fetching profiles not following", size);
const profiles = await doFetch();
const profilesNotFollowing: UserProfile[] = [];
- if(profiles.length === 0) return []
+ if (profiles.length === 0) return [];
for (let profile of profiles) {
const isFollowing = await checkFollowing(profile.uid);
- if (profile.uid !== user?.uid && !isFollowing ) {
- profilesNotFollowing.push(profile)
+ if (profile.uid !== user?.uid && !isFollowing) {
+ profilesNotFollowing.push(profile);
}
}
if (profilesNotFollowing.length < size) {
const moreProfiles = await fetchProfiles(
- profiles[
- profiles.length - 1
- ],
+ profiles[profiles.length - 1],
size - profilesNotFollowing.length,
filters
);
profilesNotFollowing.push(...moreProfiles);
- }
+ }
return profilesNotFollowing;
}
-
+
const profiles = await doFetch();
- return profiles
-
+ return profiles;
+
async function doFetch() {
const querySnapshot = await getDocs(q);
const profiles = querySnapshot.docs.map((doc) => {
@@ -1614,5 +1848,7 @@ export default function useBackend() {
fetchSinglePost,
fetchProfiles,
fetchPrivacyPolicy,
+ bookExpert,
+ fetchExpts,
};
}
diff --git a/frontend/nextjs/src/lib/types.ts b/frontend/nextjs/src/lib/types.ts
index 40544fb..9eda398 100644
--- a/frontend/nextjs/src/lib/types.ts
+++ b/frontend/nextjs/src/lib/types.ts
@@ -109,6 +109,7 @@ export type ExptFilters = {
tags?: string[],
author?: string,
mentee?: string,
+ mentor?: string,
tokenIds?: number[],
}
export type Coin = {
@@ -136,6 +137,7 @@ export type ClaimHistoryItem ={
timestamp: Timestamp,
amount: number,
level?: number,
+ tokenIds?: number[],
uid: string,
}
@@ -166,7 +168,7 @@ export type Booking ={
message?: string;
mentor: string;
mentee: string;
- sessionTimestamp: string;
+ sessionTimestamp: Timestamp;
sessionCount: number;
exptListing?: ExptListingWithAuthorProfile;
exptListingId: string;