Spaces:
Running
Running
wuyiqunLu
commited on
feat: virtual scroll for sidebar list (#46)
Browse fileshttps://github.com/landing-ai/vision-agent-ui/assets/132986242/34e787a6-c708-4f87-958a-594696c2fb42
- app/all/layout.tsx +3 -1
- app/chat/layout.tsx +5 -1
- auth.ts +5 -1
- components/chat-sidebar/ChatCard.tsx +1 -1
- components/chat-sidebar/ChatListSidebar.tsx +48 -13
- package.json +3 -0
- pnpm-lock.yaml +46 -0
app/all/layout.tsx
CHANGED
|
@@ -10,10 +10,12 @@ interface ChatLayoutProps {
|
|
| 10 |
}
|
| 11 |
|
| 12 |
export default async function Layout({ children }: ChatLayoutProps) {
|
| 13 |
-
const { isAdmin } = await authEmail();
|
| 14 |
|
| 15 |
if (!isAdmin) {
|
| 16 |
redirect('/');
|
|
|
|
|
|
|
| 17 |
}
|
| 18 |
|
| 19 |
const chats = await adminGetAllKVChats();
|
|
|
|
| 10 |
}
|
| 11 |
|
| 12 |
export default async function Layout({ children }: ChatLayoutProps) {
|
| 13 |
+
const { isAdmin, user } = await authEmail();
|
| 14 |
|
| 15 |
if (!isAdmin) {
|
| 16 |
redirect('/');
|
| 17 |
+
} else if (!user) {
|
| 18 |
+
return null;
|
| 19 |
}
|
| 20 |
|
| 21 |
const chats = await adminGetAllKVChats();
|
app/chat/layout.tsx
CHANGED
|
@@ -9,8 +9,12 @@ interface ChatLayoutProps {
|
|
| 9 |
}
|
| 10 |
|
| 11 |
export default async function Layout({ children }: ChatLayoutProps) {
|
| 12 |
-
const { email, isAdmin } = await authEmail();
|
| 13 |
const chats = await getKVChats();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
return (
|
| 15 |
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
|
| 16 |
<div
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
export default async function Layout({ children }: ChatLayoutProps) {
|
| 12 |
+
const { email, isAdmin, user } = await authEmail();
|
| 13 |
const chats = await getKVChats();
|
| 14 |
+
if (!user) {
|
| 15 |
+
return null;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
return (
|
| 19 |
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
|
| 20 |
<div
|
auth.ts
CHANGED
|
@@ -62,5 +62,9 @@ export const {
|
|
| 62 |
export async function authEmail() {
|
| 63 |
const session = await auth();
|
| 64 |
const email = session?.user?.email;
|
| 65 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
}
|
|
|
|
| 62 |
export async function authEmail() {
|
| 63 |
const session = await auth();
|
| 64 |
const email = session?.user?.email;
|
| 65 |
+
return {
|
| 66 |
+
email,
|
| 67 |
+
isAdmin: !!email?.endsWith('landing.ai'),
|
| 68 |
+
user: session?.user,
|
| 69 |
+
};
|
| 70 |
}
|
components/chat-sidebar/ChatCard.tsx
CHANGED
|
@@ -23,7 +23,7 @@ export const ChatCardLayout: React.FC<
|
|
| 23 |
return (
|
| 24 |
<Link
|
| 25 |
className={cn(
|
| 26 |
-
'p-2
|
| 27 |
classNames,
|
| 28 |
)}
|
| 29 |
href={link}
|
|
|
|
| 23 |
return (
|
| 24 |
<Link
|
| 25 |
className={cn(
|
| 26 |
+
'p-2 bg-background max-h-[100px] rounded-xl shadow-md flex items-center border border-transparent hover:border-gray-500 transition-all cursor-pointer w-full',
|
| 27 |
classNames,
|
| 28 |
)}
|
| 29 |
href={link}
|
components/chat-sidebar/ChatListSidebar.tsx
CHANGED
|
@@ -1,34 +1,69 @@
|
|
|
|
|
|
|
|
| 1 |
import ChatCard, { ChatCardLayout } from './ChatCard';
|
| 2 |
import { IconPlus } from '../ui/Icons';
|
| 3 |
import { auth } from '@/auth';
|
| 4 |
import { ChatEntity } from '@/lib/types';
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
export interface ChatSidebarListProps {
|
| 7 |
chats: ChatEntity[];
|
| 8 |
isAdminView?: boolean;
|
| 9 |
}
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
export default async function ChatSidebarList({
|
| 12 |
chats,
|
| 13 |
isAdminView,
|
| 14 |
}: ChatSidebarListProps) {
|
| 15 |
-
const session = await auth();
|
| 16 |
-
if (!session || !session.user) {
|
| 17 |
-
return null;
|
| 18 |
-
}
|
| 19 |
return (
|
| 20 |
<>
|
| 21 |
{!isAdminView && (
|
| 22 |
-
<
|
| 23 |
-
<
|
| 24 |
-
<
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
| 28 |
)}
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
</>
|
| 33 |
);
|
| 34 |
}
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
import ChatCard, { ChatCardLayout } from './ChatCard';
|
| 4 |
import { IconPlus } from '../ui/Icons';
|
| 5 |
import { auth } from '@/auth';
|
| 6 |
import { ChatEntity } from '@/lib/types';
|
| 7 |
+
import { VariableSizeList as List } from 'react-window';
|
| 8 |
+
import { cleanInputMessage } from '@/lib/messageUtils';
|
| 9 |
+
import AutoSizer from 'react-virtualized-auto-sizer';
|
| 10 |
|
| 11 |
export interface ChatSidebarListProps {
|
| 12 |
chats: ChatEntity[];
|
| 13 |
isAdminView?: boolean;
|
| 14 |
}
|
| 15 |
|
| 16 |
+
const getItemSize = (message: string, isAdminView?: boolean) => {
|
| 17 |
+
if (message.length >= 45) return 116;
|
| 18 |
+
else if (message.length >= 20) return 104;
|
| 19 |
+
else return 88;
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
export default async function ChatSidebarList({
|
| 23 |
chats,
|
| 24 |
isAdminView,
|
| 25 |
}: ChatSidebarListProps) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
return (
|
| 27 |
<>
|
| 28 |
{!isAdminView && (
|
| 29 |
+
<div className="p-2">
|
| 30 |
+
<ChatCardLayout link="/chat">
|
| 31 |
+
<div className="overflow-hidden flex items-center size-full">
|
| 32 |
+
<IconPlus className="w-1/4 font-bold" />
|
| 33 |
+
<p className="text-sm w-3/4 ml-3 font-bold">New chat</p>
|
| 34 |
+
</div>
|
| 35 |
+
</ChatCardLayout>
|
| 36 |
+
</div>
|
| 37 |
)}
|
| 38 |
+
<AutoSizer>
|
| 39 |
+
{({ height, width }) => (
|
| 40 |
+
<List
|
| 41 |
+
itemData={chats}
|
| 42 |
+
height={height}
|
| 43 |
+
itemCount={chats.length}
|
| 44 |
+
itemSize={index =>
|
| 45 |
+
getItemSize(
|
| 46 |
+
cleanInputMessage(chats[index].messages?.[0]?.content ?? ''),
|
| 47 |
+
isAdminView,
|
| 48 |
+
)
|
| 49 |
+
}
|
| 50 |
+
width={width}
|
| 51 |
+
>
|
| 52 |
+
{({ style, index, data }) => (
|
| 53 |
+
<div
|
| 54 |
+
style={style}
|
| 55 |
+
className="px-2 flex items-center overflow-hidden"
|
| 56 |
+
>
|
| 57 |
+
<ChatCard
|
| 58 |
+
key={data[index].id}
|
| 59 |
+
chat={data[index]}
|
| 60 |
+
isAdminView={isAdminView}
|
| 61 |
+
/>
|
| 62 |
+
</div>
|
| 63 |
+
)}
|
| 64 |
+
</List>
|
| 65 |
+
)}
|
| 66 |
+
</AutoSizer>
|
| 67 |
</>
|
| 68 |
);
|
| 69 |
}
|
package.json
CHANGED
|
@@ -49,6 +49,8 @@
|
|
| 49 |
"react-markdown": "^8.0.7",
|
| 50 |
"react-syntax-highlighter": "^15.5.0",
|
| 51 |
"react-textarea-autosize": "^8.5.3",
|
|
|
|
|
|
|
| 52 |
"remark-gfm": "^3.0.1",
|
| 53 |
"remark-math": "^5.1.1",
|
| 54 |
"sharp": "^0.33.3",
|
|
@@ -60,6 +62,7 @@
|
|
| 60 |
"@types/react": "^18.2.48",
|
| 61 |
"@types/react-dom": "^18.2.18",
|
| 62 |
"@types/react-syntax-highlighter": "^15.5.11",
|
|
|
|
| 63 |
"@types/uuid": "^9.0.8",
|
| 64 |
"@typescript-eslint/parser": "^6.19.0",
|
| 65 |
"autoprefixer": "^10.4.17",
|
|
|
|
| 49 |
"react-markdown": "^8.0.7",
|
| 50 |
"react-syntax-highlighter": "^15.5.0",
|
| 51 |
"react-textarea-autosize": "^8.5.3",
|
| 52 |
+
"react-virtualized-auto-sizer": "^1.0.24",
|
| 53 |
+
"react-window": "^1.8.10",
|
| 54 |
"remark-gfm": "^3.0.1",
|
| 55 |
"remark-math": "^5.1.1",
|
| 56 |
"sharp": "^0.33.3",
|
|
|
|
| 62 |
"@types/react": "^18.2.48",
|
| 63 |
"@types/react-dom": "^18.2.18",
|
| 64 |
"@types/react-syntax-highlighter": "^15.5.11",
|
| 65 |
+
"@types/react-window": "^1.8.8",
|
| 66 |
"@types/uuid": "^9.0.8",
|
| 67 |
"@typescript-eslint/parser": "^6.19.0",
|
| 68 |
"autoprefixer": "^10.4.17",
|
pnpm-lock.yaml
CHANGED
|
@@ -116,6 +116,12 @@ importers:
|
|
| 116 |
react-textarea-autosize:
|
| 117 |
specifier: ^8.5.3
|
| 118 |
version: 8.5.3(@types/[email protected])([email protected])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
remark-gfm:
|
| 120 |
specifier: ^3.0.1
|
| 121 |
version: 3.0.1
|
|
@@ -144,6 +150,9 @@ importers:
|
|
| 144 |
'@types/react-syntax-highlighter':
|
| 145 |
specifier: ^15.5.11
|
| 146 |
version: 15.5.11
|
|
|
|
|
|
|
|
|
|
| 147 |
'@types/uuid':
|
| 148 |
specifier: ^9.0.8
|
| 149 |
version: 9.0.8
|
|
@@ -1298,6 +1307,9 @@ packages:
|
|
| 1298 |
'@types/[email protected]':
|
| 1299 |
resolution: {integrity: sha512-ZqIJl+Pg8kD+47kxUjvrlElrraSUrYa4h0dauY/U/FTUuprSCqvUj+9PNQNQzVc6AJgIWUUxn87/gqsMHNbRjw==}
|
| 1300 |
|
|
|
|
|
|
|
|
|
|
| 1301 |
'@types/[email protected]':
|
| 1302 |
resolution: {integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==}
|
| 1303 |
|
|
@@ -2571,6 +2583,9 @@ packages:
|
|
| 2571 | |
| 2572 |
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
|
| 2573 |
|
|
|
|
|
|
|
|
|
|
| 2574 | |
| 2575 |
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
| 2576 |
engines: {node: '>= 8'}
|
|
@@ -3122,6 +3137,19 @@ packages:
|
|
| 3122 |
peerDependencies:
|
| 3123 |
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
| 3124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3125 | |
| 3126 |
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
| 3127 |
engines: {node: '>=0.10.0'}
|
|
@@ -5294,6 +5322,10 @@ snapshots:
|
|
| 5294 |
dependencies:
|
| 5295 |
'@types/react': 18.2.79
|
| 5296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5297 |
'@types/[email protected]':
|
| 5298 |
dependencies:
|
| 5299 |
'@types/prop-types': 15.7.12
|
|
@@ -6804,6 +6836,8 @@ snapshots:
|
|
| 6804 |
|
| 6805 | |
| 6806 |
|
|
|
|
|
|
|
| 6807 | |
| 6808 |
|
| 6809 | |
|
@@ -7460,6 +7494,18 @@ snapshots:
|
|
| 7460 |
transitivePeerDependencies:
|
| 7461 |
- '@types/react'
|
| 7462 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7463 | |
| 7464 |
dependencies:
|
| 7465 |
loose-envify: 1.4.0
|
|
|
|
| 116 |
react-textarea-autosize:
|
| 117 |
specifier: ^8.5.3
|
| 118 |
version: 8.5.3(@types/[email protected])([email protected])
|
| 119 |
+
react-virtualized-auto-sizer:
|
| 120 |
+
specifier: ^1.0.24
|
| 121 |
+
version: 1.0.24([email protected]([email protected]))([email protected])
|
| 122 |
+
react-window:
|
| 123 |
+
specifier: ^1.8.10
|
| 124 |
+
version: 1.8.10([email protected]([email protected]))([email protected])
|
| 125 |
remark-gfm:
|
| 126 |
specifier: ^3.0.1
|
| 127 |
version: 3.0.1
|
|
|
|
| 150 |
'@types/react-syntax-highlighter':
|
| 151 |
specifier: ^15.5.11
|
| 152 |
version: 15.5.11
|
| 153 |
+
'@types/react-window':
|
| 154 |
+
specifier: ^1.8.8
|
| 155 |
+
version: 1.8.8
|
| 156 |
'@types/uuid':
|
| 157 |
specifier: ^9.0.8
|
| 158 |
version: 9.0.8
|
|
|
|
| 1307 |
'@types/[email protected]':
|
| 1308 |
resolution: {integrity: sha512-ZqIJl+Pg8kD+47kxUjvrlElrraSUrYa4h0dauY/U/FTUuprSCqvUj+9PNQNQzVc6AJgIWUUxn87/gqsMHNbRjw==}
|
| 1309 |
|
| 1310 |
+
'@types/[email protected]':
|
| 1311 |
+
resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==}
|
| 1312 |
+
|
| 1313 |
'@types/[email protected]':
|
| 1314 |
resolution: {integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==}
|
| 1315 |
|
|
|
|
| 2583 | |
| 2584 |
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
|
| 2585 |
|
| 2586 | |
| 2587 |
+
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
| 2588 |
+
|
| 2589 | |
| 2590 |
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
| 2591 |
engines: {node: '>= 8'}
|
|
|
|
| 3137 |
peerDependencies:
|
| 3138 |
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
| 3139 |
|
| 3140 | |
| 3141 |
+
resolution: {integrity: sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==}
|
| 3142 |
+
peerDependencies:
|
| 3143 |
+
react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0
|
| 3144 |
+
react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0
|
| 3145 |
+
|
| 3146 | |
| 3147 |
+
resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==}
|
| 3148 |
+
engines: {node: '>8.0.0'}
|
| 3149 |
+
peerDependencies:
|
| 3150 |
+
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
|
| 3151 |
+
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
|
| 3152 |
+
|
| 3153 | |
| 3154 |
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
| 3155 |
engines: {node: '>=0.10.0'}
|
|
|
|
| 5322 |
dependencies:
|
| 5323 |
'@types/react': 18.2.79
|
| 5324 |
|
| 5325 |
+
'@types/[email protected]':
|
| 5326 |
+
dependencies:
|
| 5327 |
+
'@types/react': 18.2.79
|
| 5328 |
+
|
| 5329 |
'@types/[email protected]':
|
| 5330 |
dependencies:
|
| 5331 |
'@types/prop-types': 15.7.12
|
|
|
|
| 6836 |
|
| 6837 | |
| 6838 |
|
| 6839 |
+
[email protected]: {}
|
| 6840 |
+
|
| 6841 | |
| 6842 |
|
| 6843 | |
|
|
|
| 7494 |
transitivePeerDependencies:
|
| 7495 |
- '@types/react'
|
| 7496 |
|
| 7497 | |
| 7498 |
+
dependencies:
|
| 7499 |
+
react: 18.2.0
|
| 7500 |
+
react-dom: 18.2.0([email protected])
|
| 7501 |
+
|
| 7502 | |
| 7503 |
+
dependencies:
|
| 7504 |
+
'@babel/runtime': 7.24.4
|
| 7505 |
+
memoize-one: 5.2.1
|
| 7506 |
+
react: 18.2.0
|
| 7507 |
+
react-dom: 18.2.0([email protected])
|
| 7508 |
+
|
| 7509 | |
| 7510 |
dependencies:
|
| 7511 |
loose-envify: 1.4.0
|