From 8c5ac1065ed499a139be9dd3bc1a23592d21dfff Mon Sep 17 00:00:00 2001 From: GwonHyeok Date: Wed, 19 Feb 2025 16:25:34 +0900 Subject: [PATCH] hide sidebar when mobile --- src/app/chat/[id]/page.tsx | 196 ++------------------------ src/app/chat/[id]/screen.tsx | 194 +++++++++++++++++++++++++ src/components/chat/chat-side-bar.tsx | 15 +- 3 files changed, 207 insertions(+), 198 deletions(-) create mode 100644 src/app/chat/[id]/screen.tsx diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index b2cc6ab..878953d 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -1,189 +1,9 @@ -'use client' - -import React, {useEffect, useMemo, useRef, useState} from 'react'; -import {Menu, Send, Settings} from 'lucide-react'; -import {Button} from "@/components/ui/button"; -import {Input} from "@/components/ui/input"; -import LogoutButton from "@/components/auth/logout-button"; - -import {Dialog, DialogContent, DialogHeader, DialogTitle} from "@/components/ui/dialog"; -import ChatSideBar from "@/components/chat/chat-side-bar"; -import ChatMessage from "@/components/chat/chat-message"; -import useSWR from "swr"; -import {useParams} from "next/navigation"; -import {ChatMessageListResponse} from "@/app/api/chat-rooms/[id]/messages/route"; -import {ChatRole} from "@prisma/client"; -import ChatSettingSideBar from "@/components/chat/chat-setting-side-bar"; - -export default function Page() { - const {id} = useParams<{ - id: string - }>(); - const messagesEndRef = useRef(null); - - const [inputText, setInputText] = useState(''); - const [sources] = useState([]); - const [isJsonViewerOpen, setIsJsonViewerOpen] = useState(false); - const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true); - const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(true); - - const {data, mutate} = useSWR(`/api/chat-rooms/${id}/messages`, async (url: string) => { - const response = await fetch(url); - return response.json(); - }); - const messages = useMemo(() => data?.chatMessages || [], [data]); - - useEffect(() => { - if (messagesEndRef.current) { - messagesEndRef.current.scrollIntoView({behavior: 'smooth'}); - } - }, [messages]); - - const handleSendMessage = async () => { - if (!inputText.trim()) return; - - // Clear input - setInputText(''); - - const oldMessages = [...messages, { - id: new Date().toISOString(), - content: inputText, - role: 'USER' as ChatRole, - createdAt: new Date(), - }]; - await mutate({chatMessages: oldMessages}, {revalidate: false}); - - const response = await fetch(`/api/chat-rooms/${id}/messages`, { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - content: inputText, - role: 'USER', - }) - }); - - // Read as a stream - const reader = response.body?.getReader(); - const decoder = new TextDecoder(); - const createdAt = new Date() - if (reader) { - let done = false; - while (!done) { - const {value, done: isDone} = await reader.read(); - done = isDone; - const content = decoder.decode(value, {stream: !done}); - for (const data of content.split('\n').filter(Boolean)) { - const {content, error}: { content?: string, error?: string } = JSON.parse(data) - if (error) { - console.error('Error from LLM:', error); - continue; - } - if (content) { - await mutate({ - chatMessages: [ - ...oldMessages, - {id: new Date().toISOString(), content: content, role: 'ASSISTANT', createdAt} - ] - }, {revalidate: false}); - } - } - } - await mutate(); - } - }; - - return ( -
-
-
- -

OpenHealth

-
-
-
- -
- - -
-
-
- -
- - -
-
- {messages.map((message, index) => ( - - ))} -
-
-
-
- setInputText(e.target.value)} - onKeyPress={(e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSendMessage(); - } - }} - /> - -
-
-
- - -
- - - - Source Data -
-
-              {JSON.stringify(sources, null, 2)}
-            
-
-
-
-
- ); +import React from 'react'; +import Screen from "@/app/chat/[id]/screen"; +import {userAgent} from "next/server"; +import {headers} from "next/headers"; + +export default async function Page() { + const {device} = userAgent({headers: await headers()}); + return } diff --git a/src/app/chat/[id]/screen.tsx b/src/app/chat/[id]/screen.tsx new file mode 100644 index 0000000..7ed9bf2 --- /dev/null +++ b/src/app/chat/[id]/screen.tsx @@ -0,0 +1,194 @@ +'use client' + +import React, {useEffect, useMemo, useRef, useState} from 'react'; +import {Menu, Send, Settings} from 'lucide-react'; +import {Button} from "@/components/ui/button"; +import {Input} from "@/components/ui/input"; +import LogoutButton from "@/components/auth/logout-button"; + +import {Dialog, DialogContent, DialogHeader, DialogTitle} from "@/components/ui/dialog"; +import ChatSideBar from "@/components/chat/chat-side-bar"; +import ChatMessage from "@/components/chat/chat-message"; +import useSWR from "swr"; +import {useParams} from "next/navigation"; +import {ChatMessageListResponse} from "@/app/api/chat-rooms/[id]/messages/route"; +import {ChatRole} from "@prisma/client"; +import ChatSettingSideBar from "@/components/chat/chat-setting-side-bar"; + +interface ScreenProps { + isMobile: boolean; +} + +export default function Screen( + {isMobile}: ScreenProps +) { + const {id} = useParams<{ id: string }>(); + + const messagesEndRef = useRef(null); + + const [inputText, setInputText] = useState(''); + const [sources] = useState([]); + const [isJsonViewerOpen, setIsJsonViewerOpen] = useState(false); + const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(!isMobile); + const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(!isMobile); + + const {data, mutate} = useSWR(`/api/chat-rooms/${id}/messages`, async (url: string) => { + const response = await fetch(url); + return response.json(); + }); + const messages = useMemo(() => data?.chatMessages || [], [data]); + + useEffect(() => { + if (messagesEndRef.current) { + messagesEndRef.current.scrollIntoView({behavior: 'smooth'}); + } + }, [messages]); + + const handleSendMessage = async () => { + if (!inputText.trim()) return; + + // Clear input + setInputText(''); + + const oldMessages = [...messages, { + id: new Date().toISOString(), + content: inputText, + role: 'USER' as ChatRole, + createdAt: new Date(), + }]; + await mutate({chatMessages: oldMessages}, {revalidate: false}); + + const response = await fetch(`/api/chat-rooms/${id}/messages`, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + content: inputText, + role: 'USER', + }) + }); + + // Read as a stream + const reader = response.body?.getReader(); + const decoder = new TextDecoder(); + const createdAt = new Date() + if (reader) { + let done = false; + while (!done) { + const {value, done: isDone} = await reader.read(); + done = isDone; + const content = decoder.decode(value, {stream: !done}); + for (const data of content.split('\n').filter(Boolean)) { + const {content, error}: { content?: string, error?: string } = JSON.parse(data) + if (error) { + console.error('Error from LLM:', error); + continue; + } + if (content) { + await mutate({ + chatMessages: [ + ...oldMessages, + {id: new Date().toISOString(), content: content, role: 'ASSISTANT', createdAt} + ] + }, {revalidate: false}); + } + } + } + await mutate(); + } + }; + + return ( +
+
+
+ +

OpenHealth

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+ {messages.map((message, index) => ( + + ))} +
+
+
+
+ setInputText(e.target.value)} + onKeyPress={(e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + }} + /> + +
+
+
+ + +
+ + + + Source Data +
+
+              {JSON.stringify(sources, null, 2)}
+            
+
+
+
+
+ ); +} diff --git a/src/components/chat/chat-side-bar.tsx b/src/components/chat/chat-side-bar.tsx index bb7e580..74e74c8 100644 --- a/src/components/chat/chat-side-bar.tsx +++ b/src/components/chat/chat-side-bar.tsx @@ -5,19 +5,14 @@ import React, {useMemo, useState} from "react"; import useSWR from "swr"; import {ChatRoom, ChatRoomListResponse} from "@/app/api/chat-rooms/route"; import {Button} from "@/components/ui/button"; -import {Files, FileText, MessageCircle, Download, Copy, Trash2} from "lucide-react"; +import {Copy, Download, Files, FileText, MessageCircle, Trash2} from "lucide-react"; import Link from "next/link"; import {useRouter} from "next/navigation"; import {HealthDataListResponse} from "@/app/api/health-data/route"; import dayjs from "@/lib/dayjs"; -import {Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter} from "@/components/ui/dialog"; +import {Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle} from "@/components/ui/dialog"; import JSONEditor from "@/components/form/json-editor"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip" +import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,} from "@/components/ui/tooltip" interface ChatSideBarProps { isLeftSidebarOpen: boolean; @@ -141,8 +136,8 @@ export default function ChatSideBar({ } return <> -
+