diff --git a/packages/desktop/src/components/ConversationDetails.tsx b/packages/desktop/src/components/ConversationDetails.tsx new file mode 100644 index 0000000..d49e33d --- /dev/null +++ b/packages/desktop/src/components/ConversationDetails.tsx @@ -0,0 +1,232 @@ +/* eslint-disable jsx-a11y/media-has-caption */ +import { blueGrey } from '@mui/material/colors'; +import React, { useState, useEffect, useMemo } from 'react'; +import { joinConversation, leaveConversation } from '../firebase/firestore'; +import useAuth from '../providers/AuthProvider'; +import { useImmer } from 'use-immer'; +import useTerminal from './Terminal'; +import { User } from '@nirvana/core/src/models/user.model'; +import { + Stack, + IconButton, + Typography, + AvatarGroup, + Avatar, + Box, + Container, + Fab, + Paper, +} from '@mui/material'; +import { FiSun, FiMoreVertical, FiPlay, FiCloudRain } from 'react-icons/fi'; + +export default function ConversationDetails() { + const { user } = useAuth(); + const { getUser, selectedConversation, selectConversation, isCloudDoingMagic } = useTerminal(); + const [conversationUsers, setConversationUsers] = useImmer([]); + + // TODO: at the terminal level, make sure we are listening for audio clips + // and here it's just an access of that map of content for each conversation's blocks + + // the reference last active date based on current session of viewing this conversation + const [lastActiveDate, setLastActiveDate] = useState(null); + + // join the room + useEffect(() => { + if (selectedConversation.membersInRoom?.length > 1) { + // join the audio room with hms + } + }, [selectedConversation.membersInRoom]); + + // put me in the membersInRoom and leave every time I change conversations + useEffect(() => { + joinConversation(selectedConversation.id, user.uid); + + const handleCloseWindow = (event: BeforeUnloadEvent) => { + leaveConversation(selectedConversation.id, user.uid); + }; + + window.addEventListener('beforeunload', handleCloseWindow); + + return () => { + leaveConversation(selectedConversation.id, user.uid); + window.removeEventListener('beforeunload', handleCloseWindow); + }; + }, [selectedConversation.id, user.uid]); + + // update my last active date in this conversation for this viewing session's ui + useEffect(() => { + setLastActiveDate((prevLastActiveForCurrentSession) => { + if (prevLastActiveForCurrentSession) return prevLastActiveForCurrentSession; + + return selectedConversation.members[user.uid].lastActiveDate?.toDate(); + }); + }, [selectedConversation.members, user.uid, setLastActiveDate]); + + // TODO: uncheck priority or not + // soft max of 10...not going to enforce by counting all in database, but relying on client side list of convos + + useEffect(() => { + (async () => { + const userPromises = selectedConversation.memberIdsList + // .filter((memberId) => memberId !== user.uid) + .map(async (memberId) => await getUser(memberId)); + + const userSettled = await Promise.all(userPromises); + + setConversationUsers(userSettled); + })(); + + return; + }, [selectedConversation.memberIdsList, getUser, user, setConversationUsers]); + + return ( + <> + + + + + + + {selectedConversation.name ?? + conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')} + + + + {conversationUsers.map((conversationUser, index) => ( + + ))} + + + + + + + + + + + + + + + + + + + + ); +} + +function ConversationHistory() { + const { isCloudDoingMagic, selectedConversation, conversationContentMap } = useTerminal(); + + const contentBlocks = useMemo(() => { + return conversationContentMap[selectedConversation.id] ?? []; + }, [conversationContentMap, selectedConversation.id]); + + return ( + + + yesterday + + + + + + + + {'Viet Phan'} + + + + + + + + + + + + today + {contentBlocks.map((contentBlock) => ( + + + {/* + + + + {'Viet Phan'} + + */} + + + + ))} + + {isCloudDoingMagic && ( + + + + )} + + + ); +} diff --git a/packages/desktop/src/components/ConversationList.tsx b/packages/desktop/src/components/ConversationList.tsx new file mode 100644 index 0000000..cdabbff --- /dev/null +++ b/packages/desktop/src/components/ConversationList.tsx @@ -0,0 +1,149 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; + +import { + Avatar, + Badge, + Divider, + List, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemText, + ListSubheader, + Stack, + Typography, + Input, + CircularProgress, + IconButton, + Box, + Tooltip, + ListItemSecondaryAction, + AvatarGroup, +} from '@mui/material'; +import { blueGrey } from '@mui/material/colors'; +import { FiActivity, FiInbox, FiSearch, FiUsers, FiCoffee, FiCircle } from 'react-icons/fi'; +import NirvanaAvatar from './NirvanaAvatar'; +import { useDebounce, useKey } from 'react-use'; +import { useSnackbar } from 'notistack'; +import KeyboardShortcutLabel from './KeyboardShortcutLabel'; +import { searchUsers } from '../firebase/firestore'; +import { User } from '@nirvana/core/src/models/user.model'; +import useTerminal from './Terminal'; +import Conversation from '@nirvana/core/src/models/conversation.model'; +import useAuth from '../providers/AuthProvider'; +import { useImmer } from 'use-immer'; + +/** + * + * @param lookingForSomeone: if a conversation id has been selected and we are looking for it + * @returns + */ +export function ConversationList({ lookingForSomeone = false }: { lookingForSomeone: boolean }) { + const { conversationMap } = useTerminal(); + + if (Object.keys(conversationMap).length === 0) { + return ( + + Look for someone by name or email to start a conversation! + + ); + } + + return ( + <> + + + Priority + + } + > + {lookingForSomeone && } + + {Object.values(conversationMap).map((currentConversation) => ( + + ))} + + + + + {/* + + Inbox + + } + > */} + + ); +} + +function ConversationRow({ conversation }: { conversation: Conversation }) { + const { user } = useAuth(); + const { getUser, selectedConversation, selectConversation } = useTerminal(); + + const [conversationUsers, setConversationUsers] = useImmer([]); + + useEffect(() => { + (async () => { + const userPromises = conversation.memberIdsList + // .filter((memberId) => memberId !== user.uid) + .map(async (memberId) => await getUser(memberId)); + + const userSettled = await Promise.all(userPromises); + + setConversationUsers(userSettled); + })(); + + return; + }, [conversation.memberIdsList, getUser, user, setConversationUsers]); + + return ( + + selectConversation(conversation.id)} + > + + + + + + {conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')} + + + + + {conversationUsers.map((conversationUser, index) => ( + + ))} + + + + {/* 20 sec */} + + + ); +} diff --git a/packages/desktop/src/components/Conversations.tsx b/packages/desktop/src/components/Conversations.tsx deleted file mode 100644 index 2b12234..0000000 --- a/packages/desktop/src/components/Conversations.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useCallback, useRef, useState } from 'react'; - -import { - Avatar, - Badge, - Divider, - List, - ListItem, - ListItemAvatar, - ListItemButton, - ListItemText, - ListSubheader, - Stack, - Typography, - Input, - CircularProgress, - IconButton, - Box, - Tooltip, - ListItemSecondaryAction, -} from '@mui/material'; -import { blueGrey } from '@mui/material/colors'; -import { FiActivity, FiInbox, FiSearch, FiUsers, FiCoffee } from 'react-icons/fi'; -import NirvanaAvatar from './NirvanaAvatar'; -import { useDebounce, useKey } from 'react-use'; -import { useSnackbar } from 'notistack'; -import KeyboardShortcutLabel from './KeyboardShortcutLabel'; -import { searchUsers } from '../firebase/firestore'; -import { User } from '@nirvana/core/src/models/user.model'; -import useTerminal from './Terminal'; - -// const Conversations = () => { - -// return ( - -// ); -// }; - -// export default Conversations; diff --git a/packages/desktop/src/components/MainPanel.tsx b/packages/desktop/src/components/MainPanel.tsx new file mode 100644 index 0000000..a8e5ae9 --- /dev/null +++ b/packages/desktop/src/components/MainPanel.tsx @@ -0,0 +1,43 @@ +import { Grid, Container, Typography } from '@mui/material'; +import { blueGrey } from '@mui/material/colors'; +import React from 'react'; +import useAuth from '../providers/AuthProvider'; +import ConversationDetails from './ConversationDetails'; +import useTerminal from './Terminal'; + +export default function MainPanel() { + const { user } = useAuth(); + const { selectedConversation } = useTerminal(); + + // if selected conversation, show details + // do all fetching necessary to paint things here + // join the call so to speak + + // show who is + + return ( + + {selectedConversation ? ( + + ) : ( + + {`Hi ${ + user.displayName?.split(' ')[0] ?? 'there' + }!`} + Take a deep breath in. + + )} + + ); +} diff --git a/packages/desktop/src/components/Terminal.tsx b/packages/desktop/src/components/Terminal.tsx index 03de1a7..185d625 100644 --- a/packages/desktop/src/components/Terminal.tsx +++ b/packages/desktop/src/components/Terminal.tsx @@ -79,6 +79,8 @@ import Channels from '../electron/constants'; import { uploadAudioClip } from '../firebase/firebaseStorage'; import { ContentBlock, ContentType } from '@nirvana/core/src/models/content.model'; import Navbar from './Navbar'; +import MainPanel from './MainPanel'; +import { ConversationList } from './ConversationList'; type ConversationMap = { [conversationId: string]: Conversation; }; @@ -562,7 +564,7 @@ export function TerminalProvider({ children }: { children?: React.ReactNode }) { {searchVal ? ( ) : ( - )} @@ -748,363 +750,4 @@ function ListPeople({ people }: { people: User[] }) { ); } -function ListConversations({ lookingForSomeone = false }: { lookingForSomeone: boolean }) { - const { conversationMap } = useTerminal(); - - if (Object.keys(conversationMap).length === 0) { - return ( - - Look for someone by name or email to start a conversation! - - ); - } - - return ( - <> - - - Priority - - } - > - {lookingForSomeone && } - - {Object.values(conversationMap).map((currentConversation) => ( - - ))} - - - - - {/* - - Inbox - - } - > */} - - ); -} - -function ConversationRow({ conversation }: { conversation: Conversation }) { - const { user } = useAuth(); - const { getUser, selectedConversation, selectConversation } = useTerminal(); - - const [conversationUsers, setConversationUsers] = useImmer([]); - - useEffect(() => { - (async () => { - const userPromises = conversation.memberIdsList - // .filter((memberId) => memberId !== user.uid) - .map(async (memberId) => await getUser(memberId)); - - const userSettled = await Promise.all(userPromises); - - setConversationUsers(userSettled); - })(); - - return; - }, [conversation.memberIdsList, getUser, user, setConversationUsers]); - - return ( - - selectConversation(conversation.id)} - > - - - - - - {conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')} - - - - - {conversationUsers.map((conversationUser, index) => ( - - ))} - - - - {/* 20 sec */} - - - ); -} - -function MainPanel() { - const { user } = useAuth(); - const { selectedConversation } = useTerminal(); - - // if selected conversation, show details - // do all fetching necessary to paint things here - // join the call so to speak - - // show who is - - return ( - - {selectedConversation ? ( - - ) : ( - - {`Hi ${ - user.displayName?.split(' ')[0] ?? 'there' - }!`} - Take a deep breath in. - - )} - - ); -} - const maxPriorityConvos = 5; - -function ConversationDetails() { - const { user } = useAuth(); - const { getUser, selectedConversation, selectConversation, isCloudDoingMagic } = useTerminal(); - const [conversationUsers, setConversationUsers] = useImmer([]); - - // TODO: at the terminal level, make sure we are listening for audio clips - // and here it's just an access of that map of content for each conversation's blocks - - // the reference last active date based on current session of viewing this conversation - const [lastActiveDate, setLastActiveDate] = useState(null); - - // join the room - useEffect(() => { - if (selectedConversation.membersInRoom?.length > 1) { - // join the audio room with hms - } - }, [selectedConversation.membersInRoom]); - - // put me in the membersInRoom and leave every time I change conversations - useEffect(() => { - joinConversation(selectedConversation.id, user.uid); - - const handleCloseWindow = (event: BeforeUnloadEvent) => { - leaveConversation(selectedConversation.id, user.uid); - }; - - window.addEventListener('beforeunload', handleCloseWindow); - - return () => { - leaveConversation(selectedConversation.id, user.uid); - window.removeEventListener('beforeunload', handleCloseWindow); - }; - }, [selectedConversation.id, user.uid]); - - // update my last active date in this conversation for this viewing session's ui - useEffect(() => { - setLastActiveDate((prevLastActiveForCurrentSession) => { - if (prevLastActiveForCurrentSession) return prevLastActiveForCurrentSession; - - return selectedConversation.members[user.uid].lastActiveDate?.toDate(); - }); - }, [selectedConversation.members, user.uid, setLastActiveDate]); - - // TODO: uncheck priority or not - // soft max of 10...not going to enforce by counting all in database, but relying on client side list of convos - - useEffect(() => { - (async () => { - const userPromises = selectedConversation.memberIdsList - // .filter((memberId) => memberId !== user.uid) - .map(async (memberId) => await getUser(memberId)); - - const userSettled = await Promise.all(userPromises); - - setConversationUsers(userSettled); - })(); - - return; - }, [selectedConversation.memberIdsList, getUser, user, setConversationUsers]); - - return ( - <> - - - - - - - {selectedConversation.name ?? - conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')} - - - - {conversationUsers.map((conversationUser, index) => ( - - ))} - - - - - - - - - - - - - - - - - - - - ); -} - -function ConversationHistory() { - const { isCloudDoingMagic, selectedConversation, conversationContentMap } = useTerminal(); - - const contentBlocks = useMemo(() => { - return conversationContentMap[selectedConversation.id] ?? []; - }, [conversationContentMap, selectedConversation.id]); - - return ( - - - yesterday - - - - - - - - {'Viet Phan'} - - - - - - - - - - - - today - {contentBlocks.map((contentBlock) => ( - - - {/* - - - - {'Viet Phan'} - - */} - - - - ))} - - {isCloudDoingMagic && ( - - - - )} - - - ); -}