Skip to content

Commit

Permalink
fix(website): cleanup and simplify chat panel
Browse files Browse the repository at this point in the history
- remove split panel and just use flex directly
- resolve flex and stretch issues
- simplify infinte-ish scrolling to work except it fetches all, but correctly
 - need to update later to be onscroll in view
- change Dev Settings name to Chat Settings
- fix text input resizing
- fix general layout sizing and issues
  • Loading branch information
JeremyJonas committed Oct 7, 2023
1 parent 82672ff commit c6b7545
Show file tree
Hide file tree
Showing 15 changed files with 488 additions and 636 deletions.
9 changes: 7 additions & 2 deletions demo/api/model/src/main/smithy/chat/chat-messages.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,16 @@ list ChatMessages {
//
@input
structure ListChatMessagesInput for ChatMessageResource with [PaginatedInputMixin] {
// Id of the chat to list messages for.
@required
@httpLabel
chatId: String
// Indicates if resulting messages should be returned in reverse order
// which is generally how the UI expects them
// Indicates if messages are queried in ascending order. Useful for infinite scrolling.
@httpQuery("ascending")
asc: Boolean
// Indicates if resulting page of messages should be returned in reverse order.
// This does not change the query result order, only the order of items in the current
// page. Useful for infinite scroll in combination with `ascending=true`
@httpQuery("reverse")
reverse: Boolean
}
Expand Down
399 changes: 220 additions & 179 deletions demo/corpus/embeddings/poetry.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,8 @@ export const handler = listChatMessagesHandler(

const chatId = input.requestParameters.chatId;

let pageSize = 20;
try {
if (input.requestParameters?.pageSize) {
pageSize = parseInt(String(input.requestParameters.pageSize));
}
} catch {}
const pageSize = input.requestParameters?.pageSize ?? 20;
const ascending = input.requestParameters?.ascending ?? false;

const nextToken = input.requestParameters.nextToken
? parseNextToken(input.requestParameters.nextToken)
Expand All @@ -52,7 +48,7 @@ export const handler = listChatMessagesHandler(
indexName,
userId,
chatId,
false,
ascending,
pageSize,
nextToken
);
Expand Down
6 changes: 3 additions & 3 deletions demo/website/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const App: React.FC = () => {
{
text: "API Explorer",
type: "link",
href: "/apiExplorer"
href: "/apiExplorer",
},
// TODO: enable settings once we implement it
// {
Expand All @@ -95,11 +95,11 @@ const App: React.FC = () => {
// href: "/settings",
// },
],
},)
});
}

return _navItems;
}, [isAdmin])
}, [isAdmin]);

return (
<>
Expand Down
179 changes: 89 additions & 90 deletions demo/website/src/components/chat/ChatPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
PDX-License-Identifier: Apache-2.0 */
import { Badge, FormField, Modal } from "@cloudscape-design/components";
import { Badge, Modal } from "@cloudscape-design/components";
import Box from "@cloudscape-design/components/box";
import Button from "@cloudscape-design/components/button";
import Header from "@cloudscape-design/components/header";
import SpaceBetween from "@cloudscape-design/components/space-between";
import Textarea from "@cloudscape-design/components/textarea";
import { Chat } from "api-typescript-react-query-hooks";
import { isEmpty } from "lodash";
import { useCallback, useRef, useState } from "react";
import Conversation, { ConversationViewInfinite } from "./ConversationView";
import { ConversationView } from "./ConversationView";
import { useIsAdmin } from "../../Auth";
import {
useCreateChatMessageMutation,
useDeleteChatMutation,
useUpdateChatMutation,
} from "../../hooks/chats";
import { useChatEngineConfig } from "../../providers/ChatEngineConfig";
import { useFeatureFlag } from "../../providers/FlagsProvider";
import InlineEditor from "../InlineEditor";

type SessionChatProps = {
chat: Chat;
};

export default function ChatPanel(props: SessionChatProps) {
const conversationRef = useRef<HTMLDivElement>(null);
const isAdmin = useIsAdmin();

const [config] = useChatEngineConfig();

const [currentMessage, setCurrentMessage] = useState("");

const createChatMessage = useCreateChatMessageMutation(props.chat.chatId);

const updateChat = useUpdateChatMutation();

const [deleteChatModalVisible, setDeleteChatModalVisiblity] = useState(false);

const deleteChat = useDeleteChatMutation(() => {
setDeleteChatModalVisiblity(false);
});

const shouldUseInfiniteQuery = useFeatureFlag("infiniteQuery");
const onMessageCreated = useCallback(() => {
// scroll to new message when created
if (conversationRef.current) {
conversationRef.current.scrollTop = conversationRef.current.scrollHeight;
}
}, [conversationRef]);
const createChatMessage = useCreateChatMessageMutation(
props.chat.chatId,
onMessageCreated
);

async function deleteChatHanlder() {
await deleteChat.mutateAsync({ chatId: props.chat.chatId });
Expand All @@ -68,61 +68,19 @@ export default function ChatPanel(props: SessionChatProps) {
});
}

// scroll to bottom when messages are updated
const messageContainerRef = useRef<HTMLDivElement>(null);
const onMessages = useCallback(() => {
if (messageContainerRef.current) {
messageContainerRef.current.scrollTop =
messageContainerRef.current.scrollHeight;
}
}, [messageContainerRef]);

return (
<div
style={{
display: "flex",
flexDirection: "column",
height: "100%",
gap: "16px",
gap: 16,
flex: 1,
}}
>
<Modal
onDismiss={() => setDeleteChatModalVisiblity(false)}
visible={deleteChatModalVisible}
footer={
<Box float="right">
<SpaceBetween direction="horizontal" size="xs">
<Button
variant="link"
onClick={() => setDeleteChatModalVisiblity(false)}
>
Cancel
</Button>
<Button
variant="primary"
loading={deleteChat.isLoading}
disabled={deleteChat.isError}
onClick={() => deleteChatHanlder()}
>
Ok
</Button>
</SpaceBetween>
</Box>
}
header="Confirm chat delete"
>
<p>
Are you sure you want to delete the chat?
<br />
This operation will delete all of the chat history and can not be
reversed.
</p>
</Modal>

{/* Title */}
<div
style={{
height: "40px",
width: "100%",
flex: 0,
display: "flex",
justifyContent: "space-between",
}}
Expand All @@ -141,26 +99,26 @@ export default function ChatPanel(props: SessionChatProps) {
onClick={() => setDeleteChatModalVisiblity(true)}
/>
</div>

{/* Dialog */}
<div
style={{
display: "flex",
flex: 1,
justifySelf: "stretch",
alignSelf: "stretch",
overflow: "hidden",
flexGrow: 1,
boxShadow: "inset 0px 0px 9px 0px rgba(0,0,0,0.1)",
height: "0px",
}}
>
<div
ref={messageContainerRef}
style={{ height: "100%", overflowY: "auto" }}
>
{shouldUseInfiniteQuery ? (
<ConversationViewInfinite chatId={props.chat.chatId} />
) : (
<Conversation chatId={props.chat.chatId} onMessages={onMessages} />
)}
</div>
<ConversationView ref={conversationRef} chatId={props.chat.chatId} />
</div>
<Box>

{/* Input */}
<div
style={{
flex: 0,
}}
>
<SpaceBetween direction="vertical" size="m">
<div
style={{
Expand All @@ -169,25 +127,32 @@ export default function ChatPanel(props: SessionChatProps) {
width: "100%",
gap: "14px",
alignItems: "center",
maxHeight: 300,
}}
>
<div style={{ flex: 1 }}>
<FormField stretch>
<Textarea
value={currentMessage}
onChange={({ detail }) => setCurrentMessage(detail.value)}
disabled={createChatMessage.isLoading}
onKeyUp={
currentMessage.length
? async ({ detail }) => {
if (detail.ctrlKey && detail.key === "Enter") {
await sendMessage();
}
<textarea
value={currentMessage}
onChange={({ target }) => setCurrentMessage(target.value)}
disabled={createChatMessage.isLoading}
onKeyUp={
currentMessage.length > 3
? ({ ctrlKey, key }) => {
if (ctrlKey && key === "Enter") {
sendMessage().catch(console.error);
}
: undefined
}
/>
</FormField>
}
: undefined
}
style={{
minHeight: 80,
maxHeight: 300,
width: "90%",
resize: "vertical",
borderRadius: 10,
padding: 6,
}}
/>
</div>
<div
style={{
Expand All @@ -203,7 +168,7 @@ export default function ChatPanel(props: SessionChatProps) {
variant="primary"
onClick={sendMessage}
loading={createChatMessage.isLoading}
disabled={currentMessage.length === 0}
disabled={currentMessage.length <= 3}
>
Send
</Button>
Expand All @@ -216,7 +181,41 @@ export default function ChatPanel(props: SessionChatProps) {
</div>
</div>
</SpaceBetween>
</Box>
</div>

{/* Model */}
<Modal
onDismiss={() => setDeleteChatModalVisiblity(false)}
visible={deleteChatModalVisible}
footer={
<Box float="right">
<SpaceBetween direction="horizontal" size="xs">
<Button
variant="link"
onClick={() => setDeleteChatModalVisiblity(false)}
>
Cancel
</Button>
<Button
variant="primary"
loading={deleteChat.isLoading}
disabled={deleteChat.isError}
onClick={() => deleteChatHanlder()}
>
Ok
</Button>
</SpaceBetween>
</Box>
}
header="Confirm chat delete"
>
<p>
Are you sure you want to delete the chat?
<br />
This operation will delete all of the chat history and can not be
reversed.
</p>
</Modal>
</div>
);
}
Loading

0 comments on commit c6b7545

Please sign in to comment.