clean stuff with client side search and working filtering conversations list

This commit is contained in:
talksik
2022-05-30 11:34:01 -05:00
parent 0c6aff6ada
commit cac33b3898
5 changed files with 174 additions and 60 deletions
@@ -94,7 +94,7 @@ export function ConversationList({ lookingForSomeone = false }: { lookingForSome
);
}
function ConversationRow({ conversation }: { conversation: Conversation }) {
export function ConversationRow({ conversation }: { conversation: Conversation }) {
const { user } = useAuth();
const { getUser, selectedConversation, selectConversation } = useTerminal();
+106 -59
View File
@@ -24,6 +24,8 @@ import {
Typography,
} from '@mui/material';
import { ContentBlock, ContentType } from '@nirvana/core/src/models/content.model';
import { ConversationContentMap, ConversationMap, UserMap } from '../util/types';
import { ConversationList, ConversationRow } from './ConversationList';
import { FiHeadphones, FiLogOut, FiMonitor, FiSun, FiZap } from 'react-icons/fi';
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { Unsubscribe, onSnapshot } from 'firebase/firestore';
@@ -39,7 +41,6 @@ import { useDebounce, useKeyPressEvent, useRendersCount, useUnmount } from 'reac
import Conversation from '@nirvana/core/src/models/conversation.model';
import ConversationLabel from '../subcomponents/ConversationLabel';
import { ConversationList } from './ConversationList';
import FooterControls from './FooterControls';
import MainPanel from './MainPanel';
import Navbar from './Navbar';
@@ -50,20 +51,9 @@ import { createGroupConversation } from '../firebase/firestore';
import { uploadAudioClip } from '../firebase/firebaseStorage';
import useAuth from '../providers/AuthProvider';
import { useImmer } from 'use-immer';
import { useSearchConversations } from '../util/clientSearch';
import { useSnackbar } from 'notistack';
type ConversationMap = {
[conversationId: string]: Conversation;
};
type UserMap = {
[userId: string]: User;
};
type ConversationContentMap = {
[conversationId: string]: ContentBlock[];
};
interface ITerminalContext {
conversationMap: ConversationMap;
conversationContentMap: ConversationContentMap;
@@ -117,6 +107,8 @@ export function TerminalProvider({ children }: { children?: React.ReactNode }) {
const [userMap, updateUserMap] = useImmer<UserMap>({});
const [conversationContentMap, updateContentMap] = useImmer<ConversationContentMap>({});
const { searchRelevantConversations } = useSearchConversations(conversationMap);
const [contentListeners, setContentListeners] = useImmer<{
[conversationId: string]: Unsubscribe;
}>({});
@@ -413,8 +405,10 @@ export function TerminalProvider({ children }: { children?: React.ReactNode }) {
const [searchVal, setSearchVal] = useState<string>('');
const [searchUsersResults, setSearchUsersResults] = useState<User[]>([]);
const [searchConversationsResults, setSearchConversationsResults] = useState<Conversation[]>([]);
const [searching, setSearching] = useState<boolean>(false);
// debounce user search
const [, cancel] = useDebounce(
async () => {
if (searchVal) {
@@ -432,15 +426,22 @@ export function TerminalProvider({ children }: { children?: React.ReactNode }) {
);
const handleOmniSearch = useCallback(
(searchQuery: string) => {
async (searchQuery: string) => {
searchQuery = searchQuery.replace('/', '');
setSearchVal(searchQuery);
if (!searchQuery) {
return;
}
// user search
setSearching(true);
setSearchVal(searchQuery.replace('/', ''));
// client side conversation search
const relevantConversations = await searchRelevantConversations(searchQuery);
setSearchConversationsResults(relevantConversations);
},
[setSearching, setSearchVal],
[setSearching, setSearchVal, setSearchConversationsResults, searchRelevantConversations],
);
const handleChangeSearchInput = useCallback(
@@ -546,7 +547,10 @@ export function TerminalProvider({ children }: { children?: React.ReactNode }) {
<Box sx={{ p: 2 }}>
{searchVal ? (
<ListPeople people={searchUsersResults} />
<SearchResults
people={searchUsersResults}
conversations={searchConversationsResults}
/>
) : (
<ConversationList
lookingForSomeone={selectedConversationId && !selectedConversation}
@@ -577,58 +581,101 @@ export default function useTerminal() {
return useContext(TerminalContext);
}
function ListPeople({ people }: { people: User[] }) {
const { handleQuickDial } = useTerminal();
function SearchResults({
people,
conversations,
}: {
people: User[];
conversations: Conversation[];
}) {
const { handleQuickDial, selectConversation } = useTerminal();
return (
<List
sx={{
pt: 2,
}}
subheader={
<ListSubheader
<>
<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',
}}
>
<Typography align={'center'} variant="subtitle2">
Search Results
</Typography>
</ListSubheader>
}
>
<ListItem
{people.length === 0 && (
<Typography align="center" variant="caption">
{`Can't find someone? Invite them!`}
</Typography>
)}
</ListItem>
{people.map((person) => (
<Tooltip key={`${person.uid}-searchUsers`} title={'quick dial'}>
<ListItem>
<ListItemButton onClick={() => handleQuickDial(person)}>
<ListItemAvatar>
<Avatar alt={person.displayName} src={person.photoUrl} />
</ListItemAvatar>
<ListItemText primary={person.displayName} secondary={person.email} />
<ListItemSecondaryAction sx={{ color: 'GrayText' }}>
<FiZap />
</ListItemSecondaryAction>
</ListItemButton>
</ListItem>
</Tooltip>
))}
</List>
<List
sx={{
display: 'flex',
justifyContent: 'center',
pt: 2,
}}
subheader={
<ListSubheader
sx={{
display: 'flex',
justifyContent: 'center',
}}
>
<Typography align={'center'} variant="subtitle2">
Conversations
</Typography>
</ListSubheader>
}
>
{people.length === 0 && (
<Typography align="center" variant="caption">
{`Can't find someone? Invite them!`}
</Typography>
)}
</ListItem>
<ListItem
sx={{
display: 'flex',
justifyContent: 'center',
}}
>
{conversations.length === 0 && (
<Typography align="center" variant="caption">
{`Can't find a conversation? Just create one!`}
</Typography>
)}
</ListItem>
{people.map((person) => (
<Tooltip key={`${person.uid}-searchUsers`} title={'quick dial'}>
<ListItem>
<ListItemButton onClick={() => handleQuickDial(person)}>
<ListItemAvatar>
<Avatar alt={person.displayName} src={person.photoUrl} />
</ListItemAvatar>
<ListItemText primary={person.displayName} secondary={person.email} />
<ListItemSecondaryAction sx={{ color: 'GrayText' }}>
<FiZap />
</ListItemSecondaryAction>
</ListItemButton>
</ListItem>
</Tooltip>
))}
</List>
{conversations.map((conversation) => (
<Tooltip key={`${conversation.id}-searchConversations`} title={'continue conversation'}>
<ConversationRow conversation={conversation} />
</Tooltip>
))}
</List>
</>
);
}
const maxPriorityConvos = 5;
+50
View File
@@ -0,0 +1,50 @@
import React, { useCallback } from 'react';
import Conversation from '@nirvana/core/src/models/conversation.model';
import { ConversationMap } from './types';
/**
*
* @param searchQuery
* given a search query, finds conversations
*/
export function useSearchConversations(conversationMap: ConversationMap) {
const searchRelevantConversations = useCallback(
async (searchQuery: string): Promise<Conversation[]> => {
const relevantConversations: Conversation[] = [];
Object.values(conversationMap).forEach((conversation) => {
for (const cachedConversationUser of conversation.userCache) {
if (
cachedConversationUser.email
.toLocaleLowerCase()
.includes(searchQuery.toLocaleLowerCase())
) {
relevantConversations.push(conversation);
return;
}
if (
cachedConversationUser.displayName
.toLocaleLowerCase()
.includes(searchQuery.toLocaleLowerCase())
) {
relevantConversations.push(conversation);
return;
}
}
if (conversation.name?.toLocaleLowerCase().includes(searchQuery.toLocaleLowerCase())) {
relevantConversations.push(conversation);
return;
}
});
return relevantConversations;
},
[conversationMap],
);
return { searchRelevantConversations };
}
+2
View File
@@ -2,4 +2,6 @@
export const NirvanaRules = {
maxMembersPerConversation: 8,
maxPriorityConvos: 5,
};
+15
View File
@@ -0,0 +1,15 @@
import { ContentBlock } from '@nirvana/core/src/models/content.model';
import Conversation from '@nirvana/core/src/models/conversation.model';
import { User } from '@nirvana/core/src/models/user.model';
export type ConversationMap = {
[conversationId: string]: Conversation;
};
export type UserMap = {
[userId: string]: User;
};
export type ConversationContentMap = {
[conversationId: string]: ContentBlock[];
};