diff --git a/packages/desktop/src/subcomponents/ConversationLabel.tsx b/packages/desktop/src/subcomponents/ConversationLabel.tsx
new file mode 100644
index 0000000..26f760b
--- /dev/null
+++ b/packages/desktop/src/subcomponents/ConversationLabel.tsx
@@ -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 (
+
+ {conversationName}
+
+ );
+ }
+
+ const filteredUsers = users.filter((convoUser) => convoUser._id !== user._id);
+
+ if (filteredUsers.length === 1) {
+ const firstName = filteredUsers[0].name;
+
+ return (
+
+ {firstName}
+
+ );
+ }
+
+ if (filteredUsers.length > 1) {
+ const firstNames = filteredUsers.map((filteredUser) => filteredUser.name);
+
+ const formattedFirstnames = firstNames.slice(0, -1).join(',') + ' and ' + firstNames.slice(-1);
+
+ return (
+
+ {formattedFirstnames}
+
+ );
+ }
+
+ return;
+}
diff --git a/packages/desktop/src/subcomponents/KeyboardShortcutLabel.tsx b/packages/desktop/src/subcomponents/KeyboardShortcutLabel.tsx
new file mode 100644
index 0000000..643aefa
--- /dev/null
+++ b/packages/desktop/src/subcomponents/KeyboardShortcutLabel.tsx
@@ -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 (
+
+ {label}
+
+ );
+}
diff --git a/packages/desktop/src/subcomponents/UserDetailRow.tsx b/packages/desktop/src/subcomponents/UserDetailRow.tsx
new file mode 100644
index 0000000..21d86c0
--- /dev/null
+++ b/packages/desktop/src/subcomponents/UserDetailRow.tsx
@@ -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 (
+
+
+
+
+
+ {user.givenName}
+
+ {user.email}
+
+
+
+ {rightContent}
+
+
+ );
+}
diff --git a/packages/desktop/src/tree/Navbar.tsx b/packages/desktop/src/tree/Navbar.tsx
new file mode 100644
index 0000000..150e603
--- /dev/null
+++ b/packages/desktop/src/tree/Navbar.tsx
@@ -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('');
+
+ const searchRef = useRef(null);
+ const onSearchFocus = useCallback(() => {
+ if (searchRef?.current) searchRef.current.focus();
+ }, [searchRef]);
+
+ useKeyPressEvent(KeyboardShortcuts.search.shortcutKey, onSearchFocus);
+
+ const handleSearchChange = useCallback(
+ (e: React.ChangeEvent) => {
+ setSearchQuery(e.target.value);
+ },
+ [setSearchQuery],
+ );
+
+ const rendersCount = useRendersCount();
+ console.warn('RENDER COUNT | NAVBAR | ', rendersCount);
+
+ return (
+
+
+
+
+
+
+
+
+ {/* {isSearching && } */}
+
+ {searchQuery ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Navbar;
diff --git a/packages/desktop/src/tree/Terminal.tsx b/packages/desktop/src/tree/Terminal.tsx
index 8112eb2..a37e238 100644
--- a/packages/desktop/src/tree/Terminal.tsx
+++ b/packages/desktop/src/tree/Terminal.tsx
@@ -1,5 +1,6 @@
import { Container, Grid } from '@mui/material';
+import Navbar from './Navbar';
import React from 'react';
import { blueGrey } from '@mui/material/colors';
@@ -16,7 +17,7 @@ export default function Terminal() {
>
- This is the sidepanel
+
diff --git a/packages/desktop/src/util/keyboard.ts b/packages/desktop/src/util/keyboard.ts
new file mode 100644
index 0000000..e453595
--- /dev/null
+++ b/packages/desktop/src/util/keyboard.ts
@@ -0,0 +1,12 @@
+type Shortcut = { label: string; shortcutKey: string };
+
+export const KeyboardShortcuts: Record = {
+ search: {
+ label: '/',
+ shortcutKey: '/',
+ },
+ escape: {
+ shortcutKey: 'Escape',
+ label: 'esc',
+ },
+};
diff --git a/packages/desktop/src/util/rules.ts b/packages/desktop/src/util/rules.ts
new file mode 100644
index 0000000..08f7172
--- /dev/null
+++ b/packages/desktop/src/util/rules.ts
@@ -0,0 +1,9 @@
+// the rules of the game
+
+export const NirvanaRules = {
+ maxMembersPerConversation: 8,
+
+ maxPriorityConvos: 5,
+
+ topXConversationsWithShortcuts: 3,
+};
diff --git a/packages/desktop/src/util/support.ts b/packages/desktop/src/util/support.ts
new file mode 100644
index 0000000..ee6c8f5
--- /dev/null
+++ b/packages/desktop/src/util/support.ts
@@ -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';
diff --git a/packages/desktop/src/util/text.ts b/packages/desktop/src/util/text.ts
new file mode 100644
index 0000000..101408a
--- /dev/null
+++ b/packages/desktop/src/util/text.ts
@@ -0,0 +1,7 @@
+export const toTitleCase = (str: string) => {
+ return str
+ .toLowerCase()
+ .split(' ')
+ .map((s) => s.charAt(0).toUpperCase() + s.substring(1))
+ .join(' ');
+};
diff --git a/packages/desktop/src/util/types.ts b/packages/desktop/src/util/types.ts
new file mode 100644
index 0000000..331d414
--- /dev/null
+++ b/packages/desktop/src/util/types.ts
@@ -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[];
+};