From 5203211e550b06b06bd36a3291eb3b77d11b041c Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Wed, 2 Feb 2022 21:51:09 -0800 Subject: [PATCH] astarting data stuff --- documentation/ConversationsFlow.md | 13 ++- packages/common/models/conversation.ts | 16 --- .../common/services/conversationService.ts | 1 + .../CreateConversation.tsx.tsx | 10 +- .../web/components/MainTabsOrPages/Header.tsx | 1 - packages/web/pages/s/index.tsx | 5 + packages/web/recoil/MainRecoilDataHandler.tsx | 100 ++++++++++++++++++ packages/web/recoil/main.tsx | 65 +++++++++++- 8 files changed, 181 insertions(+), 30 deletions(-) create mode 100644 packages/web/recoil/MainRecoilDataHandler.tsx diff --git a/documentation/ConversationsFlow.md b/documentation/ConversationsFlow.md index 9e90008..b49a8a5 100644 --- a/documentation/ConversationsFlow.md +++ b/documentation/ConversationsFlow.md @@ -53,8 +53,11 @@ all conversations in the conversation map and place in right category /\*\* ### Data Needed - live conversations going on right now and which people are in them -- all inbox-like conversations such as today, last 7 days, etc - a. being able to play the last message in the conversation with a play button - b. see if there is a new update in the conversation which I have not viewed -- priority conversations - a. need to be able to play any messages from there +- all inbox-like conversations such as today, last 7 days, etc a. being able to + play the last message in the conversation with a play button b. see if there + is a new update in the conversation which I have not viewed c. how many new + audio clips or links are in the conversation +- priority conversations a. need to be able to play last convo chunk from here + and reply from here + +- drawer a. need all links in all conversations where I am active b. diff --git a/packages/common/models/conversation.ts b/packages/common/models/conversation.ts index 9ca861f..563cf35 100644 --- a/packages/common/models/conversation.ts +++ b/packages/common/models/conversation.ts @@ -1,22 +1,6 @@ import { v4 as uuid } from "uuid"; import { Timestamp } from "firebase/firestore"; -export class CompleteConversation { - id: string; // conversation id - - conversation: Conversation; - - constructor(convo: Conversation) { - this.conversation = convo; - this.id = this.conversation.id; - } - - userMember?: ConversationMember; - - members: ConversationMember[] = [] as ConversationMember[]; - audioClips: AudioClip[] = [] as AudioClip[]; - links: Link[] = [] as Link[]; -} export default class Conversation { id: string = uuid(); diff --git a/packages/common/services/conversationService.ts b/packages/common/services/conversationService.ts index d27c608..5be04ad 100644 --- a/packages/common/services/conversationService.ts +++ b/packages/common/services/conversationService.ts @@ -41,6 +41,7 @@ export default class ConversationService { { ...conversation, createdDate: serverTimestamp(), + lastActivityDate: serverTimestamp(), }, { merge: true } ); diff --git a/packages/web/components/FullPageExperiences/CreateConversation.tsx.tsx b/packages/web/components/FullPageExperiences/CreateConversation.tsx.tsx index bc54f04..cb3aa8c 100644 --- a/packages/web/components/FullPageExperiences/CreateConversation.tsx.tsx +++ b/packages/web/components/FullPageExperiences/CreateConversation.tsx.tsx @@ -1,5 +1,5 @@ import { User } from "@nirvana/common/models/user"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { RefObject, useEffect, useMemo, useRef, useState } from "react"; import { FaPlus, FaRegTimesCircle, FaSearch } from "react-icons/fa"; import { usersIndex } from "../../services/algoliaSearchService"; import SimpleLoadingDot from "../Loading/SimpleLoadingDot"; @@ -164,10 +164,10 @@ export default function CreateConversation() { toast.success("Created Conversation"); - // router.push({ - // pathname: Routes.home, - // query: { page: QueryRoutes.convos }, - // }); + router.push({ + pathname: Routes.home, + query: { page: QueryRoutes.convos }, + }); } catch (error) { toast.error("Problem in creating conversation"); } diff --git a/packages/web/components/MainTabsOrPages/Header.tsx b/packages/web/components/MainTabsOrPages/Header.tsx index 187f49b..5c6a74a 100644 --- a/packages/web/components/MainTabsOrPages/Header.tsx +++ b/packages/web/components/MainTabsOrPages/Header.tsx @@ -17,7 +17,6 @@ import UserStatusBubble from "../UserDetails/UserStatusBubble"; export default function Header() { const { currUser } = useAuth(); - console.log(currUser); const router = useRouter(); diff --git a/packages/web/pages/s/index.tsx b/packages/web/pages/s/index.tsx index 1f88a1f..664ba8b 100644 --- a/packages/web/pages/s/index.tsx +++ b/packages/web/pages/s/index.tsx @@ -13,8 +13,11 @@ import KeyboardShortcutHandler from "../../components/MainTabsOrPages/KeyboardSh import SearchResults from "../../components/FullPageExperiences/SearchResults"; import { QueryRoutes, Routes } from "@nirvana/common/helpers/routes"; import ViewConvo from "../../components/MainTabsOrPages/ViewConvo"; +import MainRecoilDataHandler from "../../recoil/MainRecoilDataHandler"; export default function Me() { + // TODO: if not authenticated, take user away + // figure out what content to render from here const router = useRouter(); const currPage = router.query.page; @@ -113,6 +116,8 @@ export default function Me() { Me.getLayout = function (content: ReactElement) { return (
+ +
diff --git a/packages/web/recoil/MainRecoilDataHandler.tsx b/packages/web/recoil/MainRecoilDataHandler.tsx new file mode 100644 index 0000000..351a271 --- /dev/null +++ b/packages/web/recoil/MainRecoilDataHandler.tsx @@ -0,0 +1,100 @@ +import Conversation, { + ConversationMember, +} from "@nirvana/common/models/conversation"; +import Collections from "@nirvana/common/services/collections"; +import { + collection, + collectionGroup, + doc, + getFirestore, + onSnapshot, + query, + Unsubscribe, + where, +} from "firebase/firestore"; +import { useEffect } from "react"; +import toast from "react-hot-toast"; +import { useRecoilState } from "recoil"; +import { useAuth } from "../contexts/authContext"; +import { allRelevantConversations, allUsersConversations } from "./main"; + +const db = getFirestore(); + +export default function MainRecoilDataHandler() { + const { currUser } = useAuth(); + + const [userConvos, setUserConvos] = useRecoilState(allUsersConversations); + const [relevantConvos, setRelevantConvos] = useRecoilState( + allRelevantConversations + ); + + useEffect(() => { + const unsubs: Unsubscribe[] = [] as Unsubscribe[]; + + const convoSubs: Unsubscribe[] = [] as Unsubscribe; + + (async function () { + try { + // get all conversations that I am a part of + const q = query( + collectionGroup(db, Collections.conversationMembers), + where("id", "==", currUser.uid) + ); + const unsubscribeUserConvoMember = onSnapshot(q, (snapshot) => { + snapshot.docChanges().forEach((change) => { + if (change.type === "added" || change.type === "modified") { + const newConvoMember: ConversationMember = + change.doc.data() as ConversationMember; + newConvoMember.id = change.doc.id; + console.log("modified convo member found: ", change.doc.data()); + + // update all user convo associations + setUserConvos((prevMap) => { + return new Map(prevMap.set(newConvoMember.id, newConvoMember)); + }); + + // if we are adding a new convo relevant to me, then start a convo listener for this convo + const convoId = change.doc.ref.parent.parent?.id; + + console.log(convoId); + if (change.type === "added" && convoId) { + const unsubConvo = onSnapshot( + doc(db, Collections.conversations, convoId), + (convoDoc) => { + const updatedConvo = convoDoc.data() as Conversation; + console.log("got convo data and subscribed"); + console.log(updatedConvo); + + setRelevantConvos((prevMap) => { + return new Map(prevMap.set(convoId, updatedConvo)); + }); + } + ); + + // todo : remove specific listeners if they are no longer priority conversations + + unsubs.push(unsubConvo); + } + } + if (change.type === "removed") { + console.log("Removed convo member: ", change.doc.data()); + } + }); + }); + + unsubs.push(unsubscribeUserConvoMember); + } catch (error) { + console.log(error); + toast.error("Problem in retrieving data"); + } + })(); + + return () => { + unsubs.forEach((unsub) => { + unsub(); + }); + }; + }, []); + + return <>; +} diff --git a/packages/web/recoil/main.tsx b/packages/web/recoil/main.tsx index 244ae4d..806ed28 100644 --- a/packages/web/recoil/main.tsx +++ b/packages/web/recoil/main.tsx @@ -1,10 +1,69 @@ +import Conversation, { + ConversationMember, + AudioClip, + Link, +} from "@nirvana/common/models/conversation"; + import { atom } from "recoil"; -// export enum RecoilActions { -// CURR_PAGE_PATH = "CURR_PAGE_PATH", -// } +export enum RecoilActions { + CURR_PAGE_PATH = "CURR_PAGE_PATH", + ALL_COMPLETE_CONVERSATIONS = "ALL_COMPLETE_CONVERSATIONS", + ALL_USERS_CONVERSATION_RELATIONSHIPS = "ALL_USERS_CONVERSATION_RELATIONSHIPS", + ALL_RELEVANT_CONVERSATIONS = "ALL_RELEVANT_CONVERSATIONS", +} // export const currPagePath = atom({ // key: RecoilActions.CURR_PAGE_PATH, // unique ID (with respect to other atoms/selectors) // default: "/s", // default value (aka initial value) // }); + +export class CompleteConversation { + id: string; // conversation id + + get isLive(): boolean { + return this.members?.length > 0; + } + + // method/property for knowing if this there is a new activity for me in this conversation + + // method/property for getting the latest link if valid + + // method/property to get the latest convo chunk/last person talking + + conversation: Conversation; + + constructor(convo: Conversation) { + this.conversation = convo; + this.id = this.conversation.id; + } + + userMember?: ConversationMember; + + members: ConversationMember[] = [] as ConversationMember[]; + audioClips: AudioClip[] = [] as AudioClip[]; + links: Link[] = [] as Link[]; +} + +// map cache of all complete conversation objects +export const allCompleteConversations = atom({ + key: RecoilActions.ALL_COMPLETE_CONVERSATIONS, + default: new Map(), +}); + +// map cache of all relevant users + +// selector for the convos that have members in the live room + +// selectors for convos: inbox/default, live, later, done, priority + +// approach: fill in all "bottom" level atoms, and then build the tree later with selectors +export const allUsersConversations = atom({ + key: RecoilActions.ALL_USERS_CONVERSATION_RELATIONSHIPS, + default: new Map(), +}); + +export const allRelevantConversations = atom({ + key: RecoilActions.ALL_RELEVANT_CONVERSATIONS, + default: new Map(), +});