adding in navbar and other components
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
import Conversation from '@nirvana/core/models/conversation.model';
|
||||||
|
import React from 'react';
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
import User from '@nirvana/core/models/user.model';
|
||||||
|
import useAuth from '../providers/AuthProvider';
|
||||||
|
|
||||||
|
type IConversationLabel = (
|
||||||
|
| {
|
||||||
|
conversationName?: string;
|
||||||
|
users: User[];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
conversationName: string;
|
||||||
|
users?: User[];
|
||||||
|
}
|
||||||
|
) & { isSelected?: boolean };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if there is a name in the conversation, then return that
|
||||||
|
* if not, then return first three names
|
||||||
|
*/
|
||||||
|
export default function ConversationLabel({
|
||||||
|
conversationName,
|
||||||
|
users,
|
||||||
|
isSelected = false,
|
||||||
|
}: IConversationLabel) {
|
||||||
|
const { user } = useAuth();
|
||||||
|
|
||||||
|
if (conversationName) {
|
||||||
|
return (
|
||||||
|
<Typography
|
||||||
|
noWrap
|
||||||
|
variant={'overline'}
|
||||||
|
color={isSelected && 'primary'}
|
||||||
|
sx={{ fontWeight: isSelected && 'bold' }}
|
||||||
|
>
|
||||||
|
{conversationName}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredUsers = users.filter((convoUser) => convoUser._id !== user._id);
|
||||||
|
|
||||||
|
if (filteredUsers.length === 1) {
|
||||||
|
const firstName = filteredUsers[0].name;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography
|
||||||
|
noWrap
|
||||||
|
variant={'overline'}
|
||||||
|
color={isSelected && 'primary'}
|
||||||
|
sx={{ fontWeight: isSelected && 'bold' }}
|
||||||
|
>
|
||||||
|
{firstName}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteredUsers.length > 1) {
|
||||||
|
const firstNames = filteredUsers.map((filteredUser) => filteredUser.name);
|
||||||
|
|
||||||
|
const formattedFirstnames = firstNames.slice(0, -1).join(',') + ' and ' + firstNames.slice(-1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography
|
||||||
|
noWrap
|
||||||
|
variant={'overline'}
|
||||||
|
color={isSelected && 'primary'}
|
||||||
|
sx={{ fontWeight: isSelected && 'bold' }}
|
||||||
|
>
|
||||||
|
{formattedFirstnames}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Box, Paper, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
import { KeyboardShortcuts } from '../util/keyboard';
|
||||||
|
import React from 'react';
|
||||||
|
import { blueGrey } from '@mui/material/colors';
|
||||||
|
|
||||||
|
export default function KeyboardShortcutLabel({
|
||||||
|
label,
|
||||||
|
}: {
|
||||||
|
label: keyof typeof KeyboardShortcuts;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Paper
|
||||||
|
elevation={0}
|
||||||
|
variant={'outlined'}
|
||||||
|
sx={{
|
||||||
|
px: 1,
|
||||||
|
py: 0.25,
|
||||||
|
bgcolor: blueGrey[50],
|
||||||
|
color: blueGrey[300],
|
||||||
|
border: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="caption">{label}</Typography>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Avatar, Box, Stack, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import User from '@nirvana/core/models/user.model';
|
||||||
|
|
||||||
|
export default function UserDetailRow({
|
||||||
|
user,
|
||||||
|
rightContent,
|
||||||
|
}: {
|
||||||
|
user: User;
|
||||||
|
rightContent?: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
alignItems="center"
|
||||||
|
spacing={1}
|
||||||
|
sx={{
|
||||||
|
px: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar src={user.picture} alt={user.givenName} />
|
||||||
|
|
||||||
|
<Stack>
|
||||||
|
<Typography variant="subtitle2" color="info" gutterBottom={false}>
|
||||||
|
{user.givenName}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant={'overline'}>{user.email}</Typography>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
ml: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{rightContent}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { CircularProgress, IconButton, Input, Stack, Tooltip } from '@mui/material';
|
||||||
|
import { FiSearch, FiUsers } from 'react-icons/fi';
|
||||||
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
|
import { useKeyPressEvent, useRendersCount } from 'react-use';
|
||||||
|
|
||||||
|
import KeyboardShortcutLabel from '../subcomponents/KeyboardShortcutLabel';
|
||||||
|
import { KeyboardShortcuts } from '../util/keyboard';
|
||||||
|
import NirvanaLogo from '../subcomponents/NirvanaLogo';
|
||||||
|
import { blueGrey } from '@mui/material/colors';
|
||||||
|
import useTerminal from './Terminal';
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
|
|
||||||
|
const searchRef = useRef<HTMLInputElement>(null);
|
||||||
|
const onSearchFocus = useCallback(() => {
|
||||||
|
if (searchRef?.current) searchRef.current.focus();
|
||||||
|
}, [searchRef]);
|
||||||
|
|
||||||
|
useKeyPressEvent(KeyboardShortcuts.search.shortcutKey, onSearchFocus);
|
||||||
|
|
||||||
|
const handleSearchChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearchQuery(e.target.value);
|
||||||
|
},
|
||||||
|
[setSearchQuery],
|
||||||
|
);
|
||||||
|
|
||||||
|
const rendersCount = useRendersCount();
|
||||||
|
console.warn('RENDER COUNT | NAVBAR | ', rendersCount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
justifyContent={'flex-start'}
|
||||||
|
alignItems={'center'}
|
||||||
|
spacing={2}
|
||||||
|
sx={{
|
||||||
|
WebkitAppRegion: 'drag',
|
||||||
|
cursor: 'pointer',
|
||||||
|
p: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NirvanaLogo />
|
||||||
|
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
sx={{
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
bgcolor: blueGrey[100],
|
||||||
|
opacity: '50%',
|
||||||
|
borderRadius: 1,
|
||||||
|
px: 1,
|
||||||
|
py: 0.5,
|
||||||
|
flex: 1,
|
||||||
|
}}
|
||||||
|
alignItems={'center'}
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<FiSearch style={{ color: blueGrey[500] }} />
|
||||||
|
|
||||||
|
<Input
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
value={searchQuery}
|
||||||
|
placeholder={'Find or start a conversation'}
|
||||||
|
inputRef={searchRef}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* {isSearching && <CircularProgress size={20} />} */}
|
||||||
|
|
||||||
|
{searchQuery ? (
|
||||||
|
<KeyboardShortcutLabel label={KeyboardShortcuts.escape.label} />
|
||||||
|
) : (
|
||||||
|
<KeyboardShortcutLabel label={KeyboardShortcuts.search.label} />
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Tooltip title={'Group conversation'}>
|
||||||
|
<IconButton color="default" size={'small'}>
|
||||||
|
<FiUsers />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Container, Grid } from '@mui/material';
|
import { Container, Grid } from '@mui/material';
|
||||||
|
|
||||||
|
import Navbar from './Navbar';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { blueGrey } from '@mui/material/colors';
|
import { blueGrey } from '@mui/material/colors';
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ export default function Terminal() {
|
|||||||
>
|
>
|
||||||
<Grid container spacing={0}>
|
<Grid container spacing={0}>
|
||||||
<Grid item xs={4}>
|
<Grid item xs={4}>
|
||||||
This is the sidepanel
|
<Navbar />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={8}>
|
<Grid item xs={8}>
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
type Shortcut = { label: string; shortcutKey: string };
|
||||||
|
|
||||||
|
export const KeyboardShortcuts: Record<string, Shortcut> = {
|
||||||
|
search: {
|
||||||
|
label: '/',
|
||||||
|
shortcutKey: '/',
|
||||||
|
},
|
||||||
|
escape: {
|
||||||
|
shortcutKey: 'Escape',
|
||||||
|
label: 'esc',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// the rules of the game
|
||||||
|
|
||||||
|
export const NirvanaRules = {
|
||||||
|
maxMembersPerConversation: 8,
|
||||||
|
|
||||||
|
maxPriorityConvos: 5,
|
||||||
|
|
||||||
|
topXConversationsWithShortcuts: 3,
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* The string that should be used to search for the support account, regardless of the environment
|
||||||
|
*/
|
||||||
|
export const SUPPORT_DISPLAY_NAME = 'Nirvana | Team';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const toTitleCase = (str: string) => {
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.split(' ')
|
||||||
|
.map((s) => s.charAt(0).toUpperCase() + s.substring(1))
|
||||||
|
.join(' ');
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { ContentBlock } from '@nirvana/core/models/content.model';
|
||||||
|
import Conversation from '@nirvana/core/models/conversation.model';
|
||||||
|
import User from '@nirvana/core/models/user.model';
|
||||||
|
|
||||||
|
export type ConversationMap = {
|
||||||
|
[conversationId: string]: Conversation;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserMap = {
|
||||||
|
[userId: string]: User;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConversationContentMap = {
|
||||||
|
[conversationId: string]: ContentBlock[];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user