race condition with search and firebase initializing

This commit is contained in:
talksik
2022-05-28 13:04:44 -05:00
parent 6b0d993e1e
commit e1584a6c0f
4 changed files with 105 additions and 11 deletions
-1
View File
@@ -9,7 +9,6 @@ import { NirvanaTheme } from './mui/NirvanaTheme';
import { ElectronProvider } from './providers/ElectronProvider'; import { ElectronProvider } from './providers/ElectronProvider';
import { AuthProvider } from './providers/AuthProvider'; import { AuthProvider } from './providers/AuthProvider';
import './firebase/connect';
import ErrorParent from './providers/ErrorBoundary'; import ErrorParent from './providers/ErrorBoundary';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
@@ -1,4 +1,4 @@
import React, { useCallback, useRef } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import { import {
Avatar, Avatar,
@@ -17,21 +17,37 @@ import {
import { blueGrey } from '@mui/material/colors'; import { blueGrey } from '@mui/material/colors';
import { FiActivity, FiInbox, FiSearch } from 'react-icons/fi'; import { FiActivity, FiInbox, FiSearch } from 'react-icons/fi';
import NirvanaAvatar from './NirvanaAvatar'; import NirvanaAvatar from './NirvanaAvatar';
import { useKey } from 'react-use'; import { useDebounce, useKey } from 'react-use';
import { useSnackbar } from 'notistack'; import { useSnackbar } from 'notistack';
import KeyboardShortcutLabel from './KeyboardShortcutLabel'; import KeyboardShortcutLabel from './KeyboardShortcutLabel';
import { searchUsers, User } from '../firebase/firestore';
const Conversations = () => { const Conversations = () => {
const { enqueueSnackbar } = useSnackbar(); const { enqueueSnackbar } = useSnackbar();
const searchRef = useRef<HTMLInputElement>(null); const searchRef = useRef<HTMLInputElement>(null);
const [searchVal, setSearchVal] = useState<string>('');
const onSearch = useCallback(() => { const onSearchFocus = useCallback(() => {
enqueueSnackbar('search focused'); enqueueSnackbar('search focused');
if (searchRef?.current) searchRef.current.focus(); if (searchRef?.current) searchRef.current.focus();
}, []); }, [enqueueSnackbar, searchRef]);
useKey('Shift', onSearch); const [searchUsersResults, setSearchUsersResults] = useState<User[]>([]);
useKey('Shift', onSearchFocus);
const [, cancel] = useDebounce(
async () => {
if (searchVal) {
enqueueSnackbar('searching...', { variant: 'info' });
setSearchUsersResults(await searchUsers(searchVal));
}
},
500,
[searchVal, enqueueSnackbar, setSearchUsersResults],
);
return ( return (
<> <>
@@ -51,11 +67,18 @@ const Conversations = () => {
> >
<FiSearch style={{ color: blueGrey[500] }} /> <FiSearch style={{ color: blueGrey[500] }} />
<Input placeholder={'Find or start a conversation'} inputRef={searchRef} /> <Input
onChange={(e) => setSearchVal(e.target.value)}
value={searchVal}
placeholder={'Find or start a conversation'}
inputRef={searchRef}
/>
<KeyboardShortcutLabel label="Shift" /> <KeyboardShortcutLabel label="Shift" />
</Stack> </Stack>
{searchVal && <ListPeople people={searchUsersResults} />}
<List <List
sx={{ sx={{
pt: 2, pt: 2,
@@ -149,4 +172,39 @@ const Conversations = () => {
); );
}; };
function ListPeople({ people }: { people: User[] }) {
return (
<List
sx={{
pt: 2,
}}
subheader={
<ListSubheader>
<FiActivity />
<Typography variant="subtitle2"> Priority</Typography>
</ListSubheader>
}
>
{people.length === 0 && (
<Typography variant="caption">
Sorry please try someone else or invite them to nirvana.
</Typography>
)}
{people.map((person) => (
<ListItem key={`${person.uid}-searchUsers`}>
<ListItemButton selected={true}>
<ListItemAvatar>
<Avatar alt={person.displayName} src={person.photoUrl} />
</ListItemAvatar>
<ListItemText primary={person.displayName} />
<Typography variant={'caption'}>20 sec</Typography>
</ListItemButton>
</ListItem>
))}
</List>
);
}
export default Conversations; export default Conversations;
+1 -1
View File
@@ -12,4 +12,4 @@ export const firebaseApp = initializeApp(devConfig);
export const firebaseAuth = getAuth(firebaseApp); export const firebaseAuth = getAuth(firebaseApp);
// Initialize Cloud Firestore and get a reference to the service // Initialize Cloud Firestore and get a reference to the service
export const firestore = getFirestore(firebaseApp); export const firestoreDb = getFirestore(firebaseApp);
+40 -3
View File
@@ -8,9 +8,14 @@ import {
QueryDocumentSnapshot, QueryDocumentSnapshot,
Timestamp, Timestamp,
FieldValue, FieldValue,
getDoc,
query,
where,
getDocs,
} from 'firebase/firestore'; } from 'firebase/firestore';
import { User as FirebaseUser } from 'firebase/auth'; import { User as FirebaseUser } from 'firebase/auth';
import { firestoreDb } from './connect';
/** /**
* UTILS * UTILS
@@ -20,11 +25,15 @@ const converter = <T>() => ({
fromFirestore: (snap: QueryDocumentSnapshot) => snap.data() as T, fromFirestore: (snap: QueryDocumentSnapshot) => snap.data() as T,
}); });
const dataPoint = <T>(collectionPath: string) => const docPoint = <T>(collectionPath: string) =>
doc(getFirestore(), collectionPath).withConverter(converter<T>()); firestoreDb && doc(getFirestore(), collectionPath).withConverter(converter<T>());
const collectionPoint = <T>(collectionPath: string) =>
firestoreDb && collection(getFirestore(), collectionPath).withConverter(converter<T>());
const db = { const db = {
user: (userId: string) => dataPoint<User>(`users/${userId}`), users: collectionPoint<User>(`users`),
user: (userId: string) => docPoint<User>(`users/${userId}`),
}; };
export class User { export class User {
@@ -65,3 +74,31 @@ export const createUser = async (user: FirebaseUser) => {
throw new Error('Error creating user'); throw new Error('Error creating user');
} }
}; };
export const searchUsers = async (searchQuery: string): Promise<User[] | undefined> => {
try {
const emaildocSearchQuery = query(
db.users,
where('email', '>=', searchQuery.toUpperCase()),
where('email', '<=', searchQuery.toUpperCase() + '\uf8ff'),
);
const nameSearchQuery = query(
db.users,
where('displayName', '>=', searchQuery.toUpperCase()),
where('displayName', '<=', searchQuery.toUpperCase() + '\uf8ff'),
);
const emailquerySnap = await getDocs(emaildocSearchQuery);
const namequerySnap = await getDocs(nameSearchQuery);
return [
...emailquerySnap.docs.map((doc) => doc.data()),
...namequerySnap.docs.map((doc) => doc.data()),
];
} catch (e) {
console.error('Error: ', e);
throw new Error('Error');
}
};