caching relevant users for my disposal and starting to render content

This commit is contained in:
Arjun Patel
2022-02-03 22:48:22 -08:00
parent 44871cd45f
commit 35bfd5312a
4 changed files with 141 additions and 43 deletions
@@ -1,24 +1,15 @@
import Conversation from "@nirvana/common/models/conversation"; import Conversation from "@nirvana/common/models/conversation";
import { Avatar } from "antd"; import { Avatar } from "antd";
import { FaWalking } from "react-icons/fa"; import { FaWalking } from "react-icons/fa";
import { MasterAvatarGroupWithUserFetch } from "../UserDetails/MasterAvatarGroup";
export default function LiveRoom(props: { conversation: Conversation }) { export default function LiveRoom(props: { conversation: Conversation }) {
return ( return (
<span className="group relative flex flex-row items-center bg-slate-50 rounded-lg border p-5 animate-pulse"> <span className="group relative flex flex-row items-center bg-slate-50 rounded-lg border p-5 animate-pulse">
<Avatar.Group <MasterAvatarGroupWithUserFetch
maxCount={3} listOfUserIds={props.conversation.membersInLiveRoom}
size={{ xs: 1000 }} showCurrUser={true}
maxStyle={{ color: "#f56a00", backgroundColor: "#fde3cf" }} />
>
<Avatar
src="https://joeschmoe.io/api/v1/random"
style={{ backgroundColor: "cyan" }}
/>
<Avatar src="https://joeschmoe.io/api/v1/100" />
<Avatar src="https://joeschmoe.io/api/v1/2" />
<Avatar src="https://joeschmoe.io/api/v1/10" />
<Avatar src="https://joeschmoe.io/api/v1/8" />
</Avatar.Group>
<span className="text-md font-semibold ml-2 mr-10">Engineering</span> <span className="text-md font-semibold ml-2 mr-10">Engineering</span>
@@ -3,9 +3,15 @@ import { useAuth } from "../../contexts/authContext";
import { useRecoilValue, useRecoilState } from "recoil"; import { useRecoilValue, useRecoilState } from "recoil";
import { allRelevantContactsAtom } from "../../recoil/main"; import { allRelevantContactsAtom } from "../../recoil/main";
import { userService } from "@nirvana/common/services"; import { userService } from "@nirvana/common/services";
import { Avatar } from "antd";
export default function MasterAvatarGroup(props: { listOfUsers: User[] }) { const AVATAR_SHAPE = "square";
const contactsMap = useRecoilValue(allRelevantContactsAtom);
export default function MasterAvatarGroup(props: {
listOfUsers: User[];
showCurrUser?: boolean;
}) {
const { currUser } = useAuth();
/** /**
* Requirements: * Requirements:
@@ -15,46 +21,100 @@ export default function MasterAvatarGroup(props: { listOfUsers: User[] }) {
* if 6 more 'other' people, then show 3 and then one circle saying how many more there are * if 6 more 'other' people, then show 3 and then one circle saying how many more there are
*/ */
const { currUser } = useAuth(); let finalistUsers: User[] = props.listOfUsers;
const listOfOtherUsers = props.listOfUsers.filter( if (!props.showCurrUser) {
(lUser) => lUser.id != currUser!.uid finalistUsers = props.listOfUsers.filter(
); (lUser) => lUser.id != currUser!.uid
);
}
// these are the finalists, so now show all of them depending on the number
if (finalistUsers?.length == 1) {
return (
<Avatar
key={finalistUsers[0].id}
src={finalistUsers[0].avatarUrl}
shape={AVATAR_SHAPE}
size={"large"}
/>
);
}
if (finalistUsers?.length == 2) {
return (
<span className="flex flex-row items-center w-[4rem] relative">
<span className="absolute left-0">
<Avatar
key={finalistUsers[0].id}
src={finalistUsers[0].avatarUrl}
shape={AVATAR_SHAPE}
size={"default"}
/>
</span>
<span className="absolute right-0">
<Avatar
key={finalistUsers[1].id}
src={finalistUsers[1].avatarUrl}
shape={AVATAR_SHAPE}
size={"default"}
/>
</span>
</span>
);
}
return ( return (
<span> <Avatar.Group
{listOfOtherUsers.map((oUser) => ( maxCount={2}
<img key={oUser.id} src={oUser.avatarUrl} /> size="default"
maxStyle={{
color: "rgb(203 213 225)",
fontSize: "10px",
backgroundColor: "rgb(248 250 252)",
borderRadius: 0,
}}
className="bg-slate-50 text-slate-300"
>
{finalistUsers.map((oUser) => (
<Avatar
key={oUser.id}
src={oUser.avatarUrl}
shape={AVATAR_SHAPE}
size={"default"}
/>
))} ))}
</span> </Avatar.Group>
); );
} }
export function MasterAvatarGroupWithUserFetch(props: { export function MasterAvatarGroupWithUserFetch(props: {
listOfUserIds: string[]; listOfUserIds: string[];
showCurrUser?: boolean;
}) { }) {
const [contactsMap, setContactsMap] = useRecoilState(allRelevantContactsAtom); const relContactsMap = useRecoilValue(allRelevantContactsAtom);
const { currUser } = useAuth(); const { currUser } = useAuth();
const listOfOtherUsers = props.listOfUserIds.filter( let listOfOtherUsers: string[] = [] as string[];
(lUser) => lUser != currUser!.uid if (props.showCurrUser) {
); listOfOtherUsers = props.listOfUserIds;
} else {
listOfOtherUsers = props.listOfUserIds.filter(
(lUser) => lUser != currUser!.uid
);
}
const resultUsers: User[] = [] as User[]; const resultUsers: User[] = [] as User[];
listOfOtherUsers.forEach(async (oUser) => { console.log(listOfOtherUsers);
if (contactsMap.has(oUser) && contactsMap.get(oUser) instanceof User) { listOfOtherUsers.forEach((oUser) => {
resultUsers.push(contactsMap.get(oUser)!); if (relContactsMap.has(oUser)) {
} else { resultUsers.push(relContactsMap.get(oUser)!);
// otherwise, fetch document from firestore and then set the cache
const retrievedUser = await userService.getUser(oUser);
if (retrievedUser) {
setContactsMap(new Map(contactsMap.set(oUser, retrievedUser)));
}
} }
}); });
return <MasterAvatarGroup listOfUsers={resultUsers} />; console.log("fetched users for frontend: ", resultUsers);
return <MasterAvatarGroup listOfUsers={resultUsers} {...props} />;
} }
+40 -1
View File
@@ -15,15 +15,18 @@ import {
} from "firebase/firestore"; } from "firebase/firestore";
import { useEffect } from "react"; import { useEffect } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useRecoilState } from "recoil"; import { useRecoilState, useSetRecoilState } from "recoil";
import { useAuth } from "../contexts/authContext"; import { useAuth } from "../contexts/authContext";
import { import {
allRelevantContactsAtom,
allRelevantConversationsAtom, allRelevantConversationsAtom,
allUsersConversationsAtom, allUsersConversationsAtom,
cachedRelevantContactsSelector,
nirvanaUserDataAtom, nirvanaUserDataAtom,
} from "./main"; } from "./main";
import { firestoreDb as db } from "../services/firebaseService"; import { firestoreDb as db } from "../services/firebaseService";
import { userService } from "@nirvana/common/services";
export default function MainRecoilDataHandler() { export default function MainRecoilDataHandler() {
const { currUser } = useAuth(); const { currUser } = useAuth();
@@ -34,6 +37,8 @@ export default function MainRecoilDataHandler() {
allRelevantConversationsAtom allRelevantConversationsAtom
); );
const [relContacts, setRelContacts] = useRecoilState(allRelevantContactsAtom);
useEffect(() => { useEffect(() => {
const unsubs: Unsubscribe[] = [] as Unsubscribe[]; const unsubs: Unsubscribe[] = [] as Unsubscribe[];
@@ -120,6 +125,40 @@ export default function MainRecoilDataHandler() {
} }
}); });
// build user's cache
let allUsersToCache: string[] = [] as string[];
arrayConvos.forEach((currconvo) => {
allUsersToCache = [...allUsersToCache, ...currconvo.activeMembers];
});
console.log(
"going to try caching a bunch of users...maybe some duplicates",
allUsersToCache
);
// if this userId is in the contacts map, then cool
// otherwise fetch with userService
allUsersToCache.map(async (oUser) => {
if (
relContacts.has(oUser) &&
relContacts.get(oUser) instanceof User
) {
// do nothing
console.log("user already cached, no need to re-cache");
} else {
// otherwise, fetch document from firestore and then set the cache
const retrievedUser = await userService.getUser(oUser);
console.log("fetching user to add to cache");
if (retrievedUser) {
setRelContacts(new Map(relContacts.set(oUser, retrievedUser)));
}
}
});
// add to the main convos atom, by modifying the current map // add to the main convos atom, by modifying the current map
setRelevantConvos((prevConvosMap) => { setRelevantConvos((prevConvosMap) => {
const newMap = new Map(prevConvosMap); const newMap = new Map(prevConvosMap);
+11 -3
View File
@@ -5,8 +5,9 @@ import Conversation, {
Link, Link,
} from "@nirvana/common/models/conversation"; } from "@nirvana/common/models/conversation";
import { User as NirvanaUser } from "@nirvana/common/models/user"; import { User as NirvanaUser } from "@nirvana/common/models/user";
import { userService } from "@nirvana/common/services";
import { atom, selector, selectorFamily } from "recoil"; import { atom, DefaultValue, selector, selectorFamily } from "recoil";
export enum RecoilActions { export enum RecoilActions {
TEST = "TEST", TEST = "TEST",
@@ -110,11 +111,18 @@ export const allRelevantContactsAtom = atom<Map<string, NirvanaUser>>({
// todo: useful selector where a component can pass in a list of users // todo: useful selector where a component can pass in a list of users
// and return their full information // and return their full information
export const cachedRelevantContactsSelector = selector<NirvanaUser[]>({ export const cachedRelevantContactsSelector = selector<string[]>({
key: RecoilActions.RELEVANT_CONTACTS_SELECTOR_CACHE, key: RecoilActions.RELEVANT_CONTACTS_SELECTOR_CACHE,
get: async ({ get }) => { get: async ({ get }) => {
const currContacts: Map<string, NirvanaUser> = get(allRelevantContactsAtom);
return []; return [];
}, },
// selector set (pass in a user and build cache if somethings not in the cache already) // selector set (pass in a user and build cache if somethings not in the cache already)
set: async ({ set, get }, listUserIdsToCache: string[]) => {
if (listUserIdsToCache instanceof DefaultValue) {
// set(newUserToCache)
return;
}
const contactsMap = get(allRelevantContactsAtom);
},
}); });