nice search and quick dial right there
This commit is contained in:
@@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user