nice search and quick dial right there

This commit is contained in:
talksik
2022-06-11 13:50:36 -05:00
parent a1ffe5249c
commit 643b547bd0
4 changed files with 175 additions and 35 deletions
@@ -1,9 +1,10 @@
import { ConversationMap, MasterConversation } from '../util/types';
import React, { useCallback, useContext, useState } from 'react'; import React, { useCallback, useContext, useState } from 'react';
import Conversation from '@nirvana/core/models/conversation.model'; import Conversation from '@nirvana/core/models/conversation.model';
import { ConversationMap } from '../util/types';
import User from '@nirvana/core/models/user.model'; import User from '@nirvana/core/models/user.model';
import useAuth from './AuthProvider'; import useAuth from './AuthProvider';
import useConversations from './ConversationProvider';
import { useDebounce } from 'react-use'; import { useDebounce } from 'react-use';
import { userSearch } from '../api/NirvanaApi'; import { userSearch } from '../api/NirvanaApi';
@@ -12,7 +13,7 @@ interface ISearchContext {
omniSearch?: (searchQuery: string) => void; omniSearch?: (searchQuery: string) => void;
searchConversations?: (searchQuery: string) => void; searchConversations?: (searchQuery: string) => void;
conversationResults: Conversation[]; conversationResults: MasterConversation[];
userResults: User[]; userResults: User[];
isSearching: boolean; isSearching: boolean;
@@ -31,12 +32,12 @@ const SearchContext = React.createContext<ISearchContext>({
export function SearchProvider({ children }: { children: React.ReactNode }) { export function SearchProvider({ children }: { children: React.ReactNode }) {
const { user } = useAuth(); const { user } = useAuth();
// const { conversationMap } = useConversations(); const { conversationMap } = useConversations();
const [searchQuery, setSearchQuery] = useState<string>(''); const [searchQuery, setSearchQuery] = useState<string>('');
const [userResults, setUserResultsResults] = useState<User[]>([]); const [userResults, setUserResultsResults] = useState<User[]>([]);
const [conversationResults, setConversationResultsResults] = useState<Conversation[]>([]); const [conversationResults, setConversationResultsResults] = useState<MasterConversation[]>([]);
const [isSearching, setIsSearching] = useState<boolean>(false); const [isSearching, setIsSearching] = useState<boolean>(false);
// debounce user search and any other async ones // debounce user search and any other async ones
@@ -71,39 +72,39 @@ export function SearchProvider({ children }: { children: React.ReactNode }) {
*/ */
const searchRelevantConversations = useCallback( const searchRelevantConversations = useCallback(
async (searchQuery: string): Promise<Conversation[]> => { async (searchQuery: string): Promise<MasterConversation[]> => {
const relevantConversations: Conversation[] = []; const relevantConversations: MasterConversation[] = [];
// Object.values(conversationMap).forEach((conversation) => { Object.values(conversationMap).forEach((conversation) => {
// for (const cachedConversationUser of conversation.userCache) { for (const cachedConversationUser of conversation.members) {
// if ( if (
// cachedConversationUser.email cachedConversationUser.email
// .toLocaleLowerCase() .toLocaleLowerCase()
// .includes(searchQuery.toLocaleLowerCase()) .includes(searchQuery.toLocaleLowerCase())
// ) { ) {
// relevantConversations.push(conversation); relevantConversations.push(conversation);
// return; return;
// } }
// if ( if (
// cachedConversationUser.displayName cachedConversationUser.name
// .toLocaleLowerCase() .toLocaleLowerCase()
// .includes(searchQuery.toLocaleLowerCase()) .includes(searchQuery.toLocaleLowerCase())
// ) { ) {
// relevantConversations.push(conversation); relevantConversations.push(conversation);
// return; return;
// } }
// } }
// if (conversation.name?.toLocaleLowerCase().includes(searchQuery.toLocaleLowerCase())) { if (conversation.name?.toLocaleLowerCase().includes(searchQuery.toLocaleLowerCase())) {
// relevantConversations.push(conversation); relevantConversations.push(conversation);
// return; return;
// } }
// }); });
return relevantConversations; return relevantConversations;
}, },
[], [conversationMap],
); );
const omniSearch = useCallback( const omniSearch = useCallback(
@@ -106,9 +106,11 @@ export function ConversationList() {
export function ConversationRow({ export function ConversationRow({
conversation, conversation,
index, index,
onClick,
}: { }: {
conversation: MasterConversation; conversation: MasterConversation;
index?: number; index?: number;
onClick?: () => void;
}) { }) {
const { user } = useAuth(); const { user } = useAuth();
@@ -126,8 +128,12 @@ export function ConversationRow({
}, [index]); }, [index]);
const handleSelectConversation = useCallback(() => { const handleSelectConversation = useCallback(() => {
if (onClick) {
onClick();
}
selectConversation(conversation._id.toString(), false); selectConversation(conversation._id.toString(), false);
}, [conversation, selectConversation]); }, [conversation, selectConversation, onClick]);
useKeyPressEvent(`${keyboardShortcut?.toString()}`, handleSelectConversation); useKeyPressEvent(`${keyboardShortcut?.toString()}`, handleSelectConversation);
@@ -0,0 +1,131 @@
import {
Avatar,
List,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemSecondaryAction,
ListItemText,
ListSubheader,
Tooltip,
Typography,
} from '@mui/material';
import React, { useCallback } from 'react';
import Conversation from '@nirvana/core/models/conversation.model';
import { ConversationRow } from './ConversationList';
import { FiZap } from 'react-icons/fi';
import User from '@nirvana/core/models/user.model';
import useConversations from '../providers/ConversationProvider';
import useSearch from '../providers/SearchProvider';
import useTerminal from './Terminal';
export default function OmniSearchResults() {
const { handleStartConversation } = useConversations();
const { userResults: people, conversationResults: conversations, clearSearch } = useSearch();
const handleQuickDial = useCallback(
async (otherUser: User) => {
const succeededDialed = await handleStartConversation([otherUser]);
if (succeededDialed) {
clearSearch();
}
},
[handleStartConversation, clearSearch],
);
return (
<>
<List
sx={{
pt: 2,
}}
subheader={
<ListSubheader
sx={{
display: 'flex',
justifyContent: 'center',
}}
>
<Typography align={'center'} variant="subtitle2">
People
</Typography>
</ListSubheader>
}
>
<ListItem
sx={{
display: 'flex',
justifyContent: 'center',
}}
>
{people.length === 0 && (
<Typography align="center" variant="caption">
{`Can't find someone? Invite them!`}
</Typography>
)}
</ListItem>
{people.map((person) => (
<Tooltip key={`${person._id.toString()}-searchUsers`} title={'quick dial'}>
<ListItem>
<ListItemButton onClick={() => handleQuickDial(person)}>
<ListItemAvatar>
<Avatar alt={person.givenName} src={person.picture} />
</ListItemAvatar>
<ListItemText primary={person.name} secondary={person.email} />
<ListItemSecondaryAction sx={{ color: 'GrayText' }}>
<FiZap />
</ListItemSecondaryAction>
</ListItemButton>
</ListItem>
</Tooltip>
))}
</List>
<List
sx={{
pt: 2,
}}
subheader={
<ListSubheader
sx={{
display: 'flex',
justifyContent: 'center',
}}
>
<Typography align={'center'} variant="subtitle2">
Conversations
</Typography>
</ListSubheader>
}
>
<ListItem
sx={{
display: 'flex',
justifyContent: 'center',
}}
>
{conversations.length === 0 && (
<Typography align="center" variant="caption">
{`Can't find a conversation? Just create one!`}
</Typography>
)}
</ListItem>
{conversations.map((conversation) => (
<Tooltip
key={`tooltip-${conversation._id.toString()}-searchConversations`}
title={'continue conversation'}
>
<ConversationRow conversation={conversation} onClick={clearSearch} />
</Tooltip>
))}
</List>
</>
);
}
+5 -3
View File
@@ -4,13 +4,17 @@ import { ConversationList } from './ConversationList';
import MainPanel from './MainPanel'; import MainPanel from './MainPanel';
import Navbar from './Navbar'; import Navbar from './Navbar';
import NewConversationDialog from './NewConversationDialog'; import NewConversationDialog from './NewConversationDialog';
import OmniSearchResults from './OmniSearchResults';
import React from 'react'; import React from 'react';
import { blueGrey } from '@mui/material/colors'; import { blueGrey } from '@mui/material/colors';
import useRouter from '../providers/RouterProvider'; import useRouter from '../providers/RouterProvider';
import useSearch from '../providers/SearchProvider';
export default function Terminal() { export default function Terminal() {
const { page } = useRouter(); const { page } = useRouter();
const { searchQuery } = useSearch();
return ( return (
<Container <Container
maxWidth={false} maxWidth={false}
@@ -36,9 +40,7 @@ export default function Terminal() {
> >
<Navbar /> <Navbar />
<Box sx={{ p: 2 }}> <Box sx={{ p: 2 }}>{searchQuery ? <OmniSearchResults /> : <ConversationList />}</Box>
<ConversationList />
</Box>
</Grid> </Grid>
<Grid <Grid