separate out components
This commit is contained in:
@@ -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<User[]>([]);
|
||||||
|
|
||||||
|
// 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<Date>(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 (
|
||||||
|
<>
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
sx={{
|
||||||
|
py: 2,
|
||||||
|
px: 2,
|
||||||
|
borderBottom: '1px solid',
|
||||||
|
borderBottomColor: blueGrey[100],
|
||||||
|
WebkitAppRegion: 'drag',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
alignItems={'center'}
|
||||||
|
justifyContent={'flex-start'}
|
||||||
|
>
|
||||||
|
<IconButton color="primary" size="small">
|
||||||
|
<FiSun />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<Typography sx={{ color: 'GrayText' }} variant={'overline'}>
|
||||||
|
{selectedConversation.name ??
|
||||||
|
conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<AvatarGroup variant={'rounded'} sx={{ ml: 'auto' }}>
|
||||||
|
{conversationUsers.map((conversationUser, index) => (
|
||||||
|
<Avatar
|
||||||
|
key={`${selectedConversation.id}-${conversationUser.uid}-convoIcon`}
|
||||||
|
alt={conversationUser?.displayName}
|
||||||
|
src={conversationUser?.photoUrl}
|
||||||
|
sx={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
opacity: selectedConversation.membersInRoom?.includes(conversationUser.uid)
|
||||||
|
? '100%'
|
||||||
|
: '20%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</AvatarGroup>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<IconButton size="small">
|
||||||
|
<FiMoreVertical />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Container maxWidth={false} sx={{ position: 'relative', flex: 1 }}>
|
||||||
|
<ConversationHistory />
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 10,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
padding: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Fab color="primary" aria-label="add" size="medium">
|
||||||
|
<FiSun />
|
||||||
|
</Fab>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConversationHistory() {
|
||||||
|
const { isCloudDoingMagic, selectedConversation, conversationContentMap } = useTerminal();
|
||||||
|
|
||||||
|
const contentBlocks = useMemo(() => {
|
||||||
|
return conversationContentMap[selectedConversation.id] ?? [];
|
||||||
|
}, [conversationContentMap, selectedConversation.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container maxWidth="xs">
|
||||||
|
<Stack
|
||||||
|
justifyContent={'flex-start'}
|
||||||
|
alignItems={'center'}
|
||||||
|
sx={{
|
||||||
|
pt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="caption">yesterday</Typography>
|
||||||
|
|
||||||
|
<Paper elevation={1} sx={{ p: 1, width: '100%' }}>
|
||||||
|
<Stack direction={'row'} alignItems="center">
|
||||||
|
<Stack spacing={2} direction={'row'} alignItems={'center'}>
|
||||||
|
<Avatar alt={'Arjun Patel'} src="https://mui.com/static/images/avatar/2.jpg" />
|
||||||
|
|
||||||
|
<Typography color={'GrayText'} variant="overline">
|
||||||
|
{'Viet Phan'}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
ml: 'auto',
|
||||||
|
color: 'GrayText',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FiPlay />
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack
|
||||||
|
justifyContent={'flex-start'}
|
||||||
|
alignItems={'center'}
|
||||||
|
sx={{
|
||||||
|
pt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="caption">today</Typography>
|
||||||
|
{contentBlocks.map((contentBlock) => (
|
||||||
|
<Paper key={contentBlock.id} elevation={8} sx={{ p: 1, width: '100%' }}>
|
||||||
|
<Stack direction={'row'} alignItems="center">
|
||||||
|
{/* <Stack spacing={2} direction={'row'} alignItems={'center'}>
|
||||||
|
<Avatar alt={'Arjun Patel'} src="https://mui.com/static/images/avatar/2.jpg" />
|
||||||
|
|
||||||
|
<Typography color={'GrayText'} variant="overline">
|
||||||
|
{'Viet Phan'}
|
||||||
|
</Typography>
|
||||||
|
</Stack> */}
|
||||||
|
|
||||||
|
<audio controls src={contentBlock.contentUrl} />
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
ml: 'auto',
|
||||||
|
color: 'GrayText',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FiPlay />
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{isCloudDoingMagic && (
|
||||||
|
<IconButton size="small" color="secondary">
|
||||||
|
<FiCloudRain />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<Typography align="center" variant="caption">
|
||||||
|
Look for someone by name or email to start a conversation!
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<List
|
||||||
|
sx={{
|
||||||
|
pt: 2,
|
||||||
|
}}
|
||||||
|
subheader={
|
||||||
|
<ListSubheader>
|
||||||
|
<FiActivity />
|
||||||
|
<Typography variant="subtitle2"> Priority</Typography>
|
||||||
|
</ListSubheader>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{lookingForSomeone && <CircularProgress />}
|
||||||
|
|
||||||
|
{Object.values(conversationMap).map((currentConversation) => (
|
||||||
|
<ConversationRow
|
||||||
|
key={`${currentConversation.id}-priorityConvoList`}
|
||||||
|
conversation={currentConversation}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* <List
|
||||||
|
sx={{
|
||||||
|
pt: 2,
|
||||||
|
}}
|
||||||
|
subheader={
|
||||||
|
<ListSubheader>
|
||||||
|
<FiInbox />
|
||||||
|
<Typography variant="subtitle2"> Inbox</Typography>
|
||||||
|
</ListSubheader>
|
||||||
|
}
|
||||||
|
></List> */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConversationRow({ conversation }: { conversation: Conversation }) {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { getUser, selectedConversation, selectConversation } = useTerminal();
|
||||||
|
|
||||||
|
const [conversationUsers, setConversationUsers] = useImmer<User[]>([]);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<ListItem key={`${conversation.id}-priorityConvoList`}>
|
||||||
|
<ListItemButton
|
||||||
|
selected={selectedConversation?.id === conversation.id}
|
||||||
|
onClick={() => selectConversation(conversation.id)}
|
||||||
|
>
|
||||||
|
<Box sx={{ color: 'GrayText', fontSize: 10, mr: 2 }}>
|
||||||
|
<FiCircle />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Typography sx={{ mr: 'auto', color: 'GrayText' }} variant={'overline'}>
|
||||||
|
{conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<ListItemAvatar>
|
||||||
|
<AvatarGroup variant={'rounded'}>
|
||||||
|
{conversationUsers.map((conversationUser, index) => (
|
||||||
|
<Avatar
|
||||||
|
key={`${conversation.id}-${conversationUser.uid}-convoIcon`}
|
||||||
|
alt={conversationUser?.displayName}
|
||||||
|
src={conversationUser?.photoUrl}
|
||||||
|
sx={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
opacity: conversation.membersInRoom?.includes(conversationUser.uid)
|
||||||
|
? '100%'
|
||||||
|
: '20%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</AvatarGroup>
|
||||||
|
</ListItemAvatar>
|
||||||
|
|
||||||
|
{/* <Typography variant={'caption'}>20 sec</Typography> */}
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
@@ -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 (
|
||||||
|
<Grid item xs={8} sx={{ backgroundColor: 'white', display: 'flex', flexDirection: 'column' }}>
|
||||||
|
{selectedConversation ? (
|
||||||
|
<ConversationDetails />
|
||||||
|
) : (
|
||||||
|
<Container
|
||||||
|
maxWidth={false}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2,
|
||||||
|
flex: 1,
|
||||||
|
background: blueGrey[50],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6">{`Hi ${
|
||||||
|
user.displayName?.split(' ')[0] ?? 'there'
|
||||||
|
}!`}</Typography>
|
||||||
|
<Typography variant="caption">Take a deep breath in.</Typography>
|
||||||
|
</Container>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -79,6 +79,8 @@ import Channels from '../electron/constants';
|
|||||||
import { uploadAudioClip } from '../firebase/firebaseStorage';
|
import { uploadAudioClip } from '../firebase/firebaseStorage';
|
||||||
import { ContentBlock, ContentType } from '@nirvana/core/src/models/content.model';
|
import { ContentBlock, ContentType } from '@nirvana/core/src/models/content.model';
|
||||||
import Navbar from './Navbar';
|
import Navbar from './Navbar';
|
||||||
|
import MainPanel from './MainPanel';
|
||||||
|
import { ConversationList } from './ConversationList';
|
||||||
type ConversationMap = {
|
type ConversationMap = {
|
||||||
[conversationId: string]: Conversation;
|
[conversationId: string]: Conversation;
|
||||||
};
|
};
|
||||||
@@ -562,7 +564,7 @@ export function TerminalProvider({ children }: { children?: React.ReactNode }) {
|
|||||||
{searchVal ? (
|
{searchVal ? (
|
||||||
<ListPeople people={searchUsersResults} />
|
<ListPeople people={searchUsersResults} />
|
||||||
) : (
|
) : (
|
||||||
<ListConversations
|
<ConversationList
|
||||||
lookingForSomeone={selectedConversationId && !selectedConversation}
|
lookingForSomeone={selectedConversationId && !selectedConversation}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -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 (
|
|
||||||
<Typography align="center" variant="caption">
|
|
||||||
Look for someone by name or email to start a conversation!
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<List
|
|
||||||
sx={{
|
|
||||||
pt: 2,
|
|
||||||
}}
|
|
||||||
subheader={
|
|
||||||
<ListSubheader>
|
|
||||||
<FiActivity />
|
|
||||||
<Typography variant="subtitle2"> Priority</Typography>
|
|
||||||
</ListSubheader>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{lookingForSomeone && <CircularProgress />}
|
|
||||||
|
|
||||||
{Object.values(conversationMap).map((currentConversation) => (
|
|
||||||
<ConversationRow
|
|
||||||
key={`${currentConversation.id}-priorityConvoList`}
|
|
||||||
conversation={currentConversation}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{/* <List
|
|
||||||
sx={{
|
|
||||||
pt: 2,
|
|
||||||
}}
|
|
||||||
subheader={
|
|
||||||
<ListSubheader>
|
|
||||||
<FiInbox />
|
|
||||||
<Typography variant="subtitle2"> Inbox</Typography>
|
|
||||||
</ListSubheader>
|
|
||||||
}
|
|
||||||
></List> */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConversationRow({ conversation }: { conversation: Conversation }) {
|
|
||||||
const { user } = useAuth();
|
|
||||||
const { getUser, selectedConversation, selectConversation } = useTerminal();
|
|
||||||
|
|
||||||
const [conversationUsers, setConversationUsers] = useImmer<User[]>([]);
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<ListItem key={`${conversation.id}-priorityConvoList`}>
|
|
||||||
<ListItemButton
|
|
||||||
selected={selectedConversation?.id === conversation.id}
|
|
||||||
onClick={() => selectConversation(conversation.id)}
|
|
||||||
>
|
|
||||||
<Box sx={{ color: 'GrayText', fontSize: 10, mr: 2 }}>
|
|
||||||
<FiCircle />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Typography sx={{ mr: 'auto', color: 'GrayText' }} variant={'overline'}>
|
|
||||||
{conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<ListItemAvatar>
|
|
||||||
<AvatarGroup variant={'rounded'}>
|
|
||||||
{conversationUsers.map((conversationUser, index) => (
|
|
||||||
<Avatar
|
|
||||||
key={`${conversation.id}-${conversationUser.uid}-convoIcon`}
|
|
||||||
alt={conversationUser?.displayName}
|
|
||||||
src={conversationUser?.photoUrl}
|
|
||||||
sx={{
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
opacity: conversation.membersInRoom?.includes(conversationUser.uid)
|
|
||||||
? '100%'
|
|
||||||
: '20%',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</AvatarGroup>
|
|
||||||
</ListItemAvatar>
|
|
||||||
|
|
||||||
{/* <Typography variant={'caption'}>20 sec</Typography> */}
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<Grid item xs={8} sx={{ backgroundColor: 'white', display: 'flex', flexDirection: 'column' }}>
|
|
||||||
{selectedConversation ? (
|
|
||||||
<ConversationDetails />
|
|
||||||
) : (
|
|
||||||
<Container
|
|
||||||
maxWidth={false}
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 2,
|
|
||||||
flex: 1,
|
|
||||||
background: blueGrey[50],
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="h6">{`Hi ${
|
|
||||||
user.displayName?.split(' ')[0] ?? 'there'
|
|
||||||
}!`}</Typography>
|
|
||||||
<Typography variant="caption">Take a deep breath in.</Typography>
|
|
||||||
</Container>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxPriorityConvos = 5;
|
const maxPriorityConvos = 5;
|
||||||
|
|
||||||
function ConversationDetails() {
|
|
||||||
const { user } = useAuth();
|
|
||||||
const { getUser, selectedConversation, selectConversation, isCloudDoingMagic } = useTerminal();
|
|
||||||
const [conversationUsers, setConversationUsers] = useImmer<User[]>([]);
|
|
||||||
|
|
||||||
// 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<Date>(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 (
|
|
||||||
<>
|
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
sx={{
|
|
||||||
py: 2,
|
|
||||||
px: 2,
|
|
||||||
borderBottom: '1px solid',
|
|
||||||
borderBottomColor: blueGrey[100],
|
|
||||||
WebkitAppRegion: 'drag',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent={'flex-start'}
|
|
||||||
>
|
|
||||||
<IconButton color="primary" size="small">
|
|
||||||
<FiSun />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<Typography sx={{ color: 'GrayText' }} variant={'overline'}>
|
|
||||||
{selectedConversation.name ??
|
|
||||||
conversationUsers.map((conversationUser) => conversationUser.displayName).join(', ')}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<AvatarGroup variant={'rounded'} sx={{ ml: 'auto' }}>
|
|
||||||
{conversationUsers.map((conversationUser, index) => (
|
|
||||||
<Avatar
|
|
||||||
key={`${selectedConversation.id}-${conversationUser.uid}-convoIcon`}
|
|
||||||
alt={conversationUser?.displayName}
|
|
||||||
src={conversationUser?.photoUrl}
|
|
||||||
sx={{
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
opacity: selectedConversation.membersInRoom?.includes(conversationUser.uid)
|
|
||||||
? '100%'
|
|
||||||
: '20%',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</AvatarGroup>
|
|
||||||
|
|
||||||
<Box>
|
|
||||||
<IconButton size="small">
|
|
||||||
<FiMoreVertical />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Container maxWidth={false} sx={{ position: 'relative', flex: 1 }}>
|
|
||||||
<ConversationHistory />
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
zIndex: 10,
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
padding: 3,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Fab color="primary" aria-label="add" size="medium">
|
|
||||||
<FiSun />
|
|
||||||
</Fab>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConversationHistory() {
|
|
||||||
const { isCloudDoingMagic, selectedConversation, conversationContentMap } = useTerminal();
|
|
||||||
|
|
||||||
const contentBlocks = useMemo(() => {
|
|
||||||
return conversationContentMap[selectedConversation.id] ?? [];
|
|
||||||
}, [conversationContentMap, selectedConversation.id]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container maxWidth="xs">
|
|
||||||
<Stack
|
|
||||||
justifyContent={'flex-start'}
|
|
||||||
alignItems={'center'}
|
|
||||||
sx={{
|
|
||||||
pt: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="caption">yesterday</Typography>
|
|
||||||
|
|
||||||
<Paper elevation={1} sx={{ p: 1, width: '100%' }}>
|
|
||||||
<Stack direction={'row'} alignItems="center">
|
|
||||||
<Stack spacing={2} direction={'row'} alignItems={'center'}>
|
|
||||||
<Avatar alt={'Arjun Patel'} src="https://mui.com/static/images/avatar/2.jpg" />
|
|
||||||
|
|
||||||
<Typography color={'GrayText'} variant="overline">
|
|
||||||
{'Viet Phan'}
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
ml: 'auto',
|
|
||||||
color: 'GrayText',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FiPlay />
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Stack
|
|
||||||
justifyContent={'flex-start'}
|
|
||||||
alignItems={'center'}
|
|
||||||
sx={{
|
|
||||||
pt: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="caption">today</Typography>
|
|
||||||
{contentBlocks.map((contentBlock) => (
|
|
||||||
<Paper key={contentBlock.id} elevation={8} sx={{ p: 1, width: '100%' }}>
|
|
||||||
<Stack direction={'row'} alignItems="center">
|
|
||||||
{/* <Stack spacing={2} direction={'row'} alignItems={'center'}>
|
|
||||||
<Avatar alt={'Arjun Patel'} src="https://mui.com/static/images/avatar/2.jpg" />
|
|
||||||
|
|
||||||
<Typography color={'GrayText'} variant="overline">
|
|
||||||
{'Viet Phan'}
|
|
||||||
</Typography>
|
|
||||||
</Stack> */}
|
|
||||||
|
|
||||||
<audio controls src={contentBlock.contentUrl} />
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
ml: 'auto',
|
|
||||||
color: 'GrayText',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FiPlay />
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{isCloudDoingMagic && (
|
|
||||||
<IconButton size="small" color="secondary">
|
|
||||||
<FiCloudRain />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user