Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support more user-friendly scrolling #5821

Merged
merged 1 commit into from
Nov 16, 2024

Conversation

Sherlocksuper
Copy link
Contributor

@Sherlocksuper Sherlocksuper commented Nov 13, 2024

💻 变更类型 | Change Type

  • [x ] feat
  • fix
  • refactor
  • perf
  • style
  • test
  • docs
  • ci
  • chore
  • build

🔀 变更说明 | Description of Change

支持更人性化滚动设置

  1. 用户输入的时候,自动滚动到最下面
  2. 当最后一条消息为ai回复的时候,占满一定空间则自动为用户取消滚动,方便浏览
    附件为演示视频

📝 补充信息 | Additional Information

相关issue:
#5798

可能优化的点:或许我们可以使用ref来标记最后一条消息,而不是findElementById,这样性能上会有优化(可能会很小),不过我担心这样在代码结构上影响较大并不美观 ?

1731500423219.zip

Summary by CodeRabbit

  • New Features

    • Enhanced message handling with improved auto-scrolling behavior based on message positioning.
    • Added functionality for copying message content to the clipboard on right-click.
    • Introduced actions for stopping responses, retrying, deleting, and pinning messages.
    • Integrated modals for editing messages and displaying shortcut keys.
  • Improvements

    • Refined user input handling, including better management of attached images and chat commands.
    • Improved interactivity and responsiveness of the chat interface.

Copy link

vercel bot commented Nov 13, 2024

@Sherlocksuper is attempting to deploy a commit to the NextChat Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Nov 13, 2024

Walkthrough

The pull request introduces enhancements to the Chat component in app/components/chat.tsx. Key updates include the addition of the isAttachWithTop function for improved auto-scrolling behavior, modifications to the useScrollToBottom hook, and refinements to the doSubmit function for better user input management. The onRightClick function is updated to allow message content copying, while new actions in the ChatAction component and integration of modal components enhance the overall chat interface functionality.

Changes

File Change Summary
app/components/chat.tsx - Added isAttachWithTop function for message positioning.
- Updated useScrollToBottom to incorporate new scrolling logic.
- Modified doSubmit for enhanced user input handling and image management.
- Updated onRightClick to copy message content to clipboard.
- Refined ChatAction with new actions: stop responses, retry messages, delete, and pin messages.
- Integrated modal components for editing messages and displaying shortcuts into chat flow.

Possibly related PRs

  • feat: allow send image only #5599: The changes in this PR also modify the doSubmit function in app/components/chat.tsx, enhancing user input handling and image attachment functionality, which is directly related to the updates made in the main PR.
  • feat: update real 'currentSession' #5765: This PR modifies the Chat component in app/components/chat.tsx, including changes to user input handling and session management, which may intersect with the enhancements made in the main PR regarding message processing and user interaction.

Suggested reviewers

  • lloydzhou
  • kalvarahul

🐰 In the chat where messages flow,
New features added, watch them glow!
With clicks to copy and scrolls that sway,
Our chat's now brighter, hip-hip-hooray!
Messages pinned, and edits so neat,
A rabbit's delight, oh what a treat! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (3)
app/components/chat.tsx (3)

971-975: Simplify the calculation of topDistance

To improve code readability, consider simplifying the calculation of topDistance by using optional chaining and moving calculations into a single return statement.

Apply this diff:

-return topDistance < 100;
+return (
+  lastMessage.getBoundingClientRect().top -
+  scrollRef.current.getBoundingClientRect().top <
+  100
+);

963-976: Optimize isAttachWithTop function for performance

The isAttachWithTop function recalculates values on every render due to dependencies on session.messages and scrollRef. Since session.messages can change frequently, it's important to ensure this function remains performant.

Consider memoizing computations or minimizing dependencies to enhance performance.

🧰 Tools
🪛 eslint

[error] 963-963: React Hook "useCallback" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


963-976: Provide fallback or default behavior for scrolling when messages are absent

In the isAttachWithTop function, if session.messages is empty, lastMessageId could be undefined, potentially causing issues. It's important to handle this edge case.

Consider adding a default return value or handling when there are no messages.

🧰 Tools
🪛 eslint

[error] 963-963: React Hook "useCallback" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b08ce56 and 6dfee8e.

📒 Files selected for processing (1)
  • app/components/chat.tsx (2 hunks)
🧰 Additional context used
🪛 eslint
app/components/chat.tsx

[error] 963-963: React Hook "useCallback" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


[error] 982-982: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

🔇 Additional comments (3)
app/components/chat.tsx (3)

980-984: Adjust the condition for auto-scrolling behavior

The condition passed to useScrollToBottom may not correctly reflect the desired scrolling behavior, especially considering the isTyping state and isAttachWithTop() result.

Please verify that the condition (isScrolledToBottom || isAttachWithTop()) && !isTyping accurately represents when auto-scrolling should occur. If not, consider revising the logic to match the intended behavior described in the PR objectives.

🧰 Tools
🪛 eslint

[error] 982-982: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


978-979: Ensure isTyping state accurately reflects user input

The check const isTyping = userInput !== ""; determines if the user is currently typing. Verify that userInput accurately captures the user's input state at all relevant times, especially when images are being attached or uploaded.

Ensure that the isTyping state is correctly managed throughout the component lifecycle to prevent unexpected auto-scrolling behavior.


982-984: Verify the dependencies passed to useScrollToBottom

The dependencies for useScrollToBottom function include scrollRef and a complex condition. Ensure that all necessary dependencies are included and that the function behaves as expected when these dependencies change.

Review the dependencies array and update it if necessary to prevent potential bugs related to stale closures.

🧰 Tools
🪛 eslint

[error] 982-982: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

app/components/chat.tsx Show resolved Hide resolved
app/components/chat.tsx Outdated Show resolved Hide resolved
app/components/chat.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
app/components/chat.tsx (2)

963-977: Consider extracting magic number and adding comments

The implementation looks good, but could benefit from some minor improvements:

  1. Extract the magic number 100 into a named constant for better maintainability
  2. Add a comment explaining the distance threshold's purpose
+// Threshold in pixels to determine if the last message is close enough to the viewport top
+const TOP_DISTANCE_THRESHOLD = 100;
+
 const isAttachWithTop = useCallback(() => {
   if (!scrollRef.current) return false;
   const lastMessageId = session.messages[session.messages.length - 1]?.id;
   if (!lastMessageId) return false;
   const lastMessage = document.getElementById(lastMessageId);
   if (!lastMessage) return false;

   const topDistance =
     lastMessage.getBoundingClientRect().top -
     scrollRef.current.getBoundingClientRect().top;
-  return topDistance < 100;
+  return topDistance < TOP_DISTANCE_THRESHOLD;
 }, [session.messages, scrollRef]);
🧰 Tools
🪛 eslint

[error] 963-963: React Hook "useCallback" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


1740-1740: Add a comment explaining the ID's purpose

Consider adding a comment to explain that this ID is used for scroll behavior management.

+                      {/* Message ID used by isAttachWithTop for scroll behavior */}
                       id={message.id}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6dfee8e and e6b9c07.

📒 Files selected for processing (1)
  • app/components/chat.tsx (2 hunks)
🧰 Additional context used
📓 Learnings (1)
app/components/chat.tsx (1)
Learnt from: Sherlocksuper
PR: ChatGPTNextWeb/ChatGPT-Next-Web#5821
File: app/components/chat.tsx:963-963
Timestamp: 2024-11-14T02:52:38.538Z
Learning: 在此项目中,拉取请求(PR)应只包含一个功能或修改,其他改动应在单独的 PR 中提交。
🪛 eslint
app/components/chat.tsx

[error] 963-963: React Hook "useCallback" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


[error] 983-983: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

🔇 Additional comments (1)
app/components/chat.tsx (1)

982-985: LGTM! Smart scroll behavior implementation

The scroll behavior implementation effectively addresses the PR objectives by:

  • Auto-scrolling when the user is typing
  • Disabling auto-scroll for long AI responses when the message is not near the viewport top
🧰 Tools
🪛 eslint

[error] 983-983: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

app/components/chat.tsx Outdated Show resolved Hide resolved
app/components/chat.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
app/components/chat.tsx (2)

963-972: Consider performance optimizations for DOM measurements

The isAttachWithTop function performs DOM measurements within a useMemo hook. While the implementation is functionally correct, there are a few considerations:

  1. DOM measurements can trigger layout reflows
  2. The calculations are performed on every render where the dependencies change

Consider using ResizeObserver or debouncing the measurements to reduce performance impact. Here's a potential optimization:

-const isAttachWithTop = useMemo(() => {
+const isAttachWithTop = useCallback(() => {
   const lastMessage = scrollRef.current?.lastElementChild as HTMLElement;
   if (!scrollRef?.current || !lastMessage) return false;
   
   const topDistance =
     lastMessage!.getBoundingClientRect().top -
     scrollRef.current.getBoundingClientRect().top;
   return topDistance < 100;
-}, [scrollRef?.current?.scrollHeight]);
+}, []);
+
+const debouncedIsAttachWithTop = useMemo(
+  () => debounce(isAttachWithTop, 100),
+  [isAttachWithTop]
+);
🧰 Tools
🪛 eslint

[error] 963-963: React Hook "useMemo" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


1735-1735: Consider using a more semantic id format

While adding an id attribute is good for DOM operations, using a more semantic format could improve maintainability.

Consider using a more descriptive format:

-id={message.id}
+id={`chat-message-${message.id}`}

This makes it clearer that the element is a chat message when inspecting the DOM or using selectors.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e6b9c07 and 9cacaa2.

📒 Files selected for processing (1)
  • app/components/chat.tsx (2 hunks)
🧰 Additional context used
🪛 eslint
app/components/chat.tsx

[error] 963-963: React Hook "useMemo" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


[error] 978-978: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

🔇 Additional comments (1)
app/components/chat.tsx (1)

976-980: LGTM! Improved scroll behavior implementation

The modifications to useScrollToBottom effectively implement the desired scrolling behavior:

  • Auto-scrolls when user is typing
  • Maintains scroll position when viewing previous messages
  • Considers message position relative to viewport
🧰 Tools
🪛 eslint

[error] 978-978: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
app/components/chat.tsx (1)

963-972: Consider improving function name and extracting magic number

The memoization is well-implemented, but a few improvements could make the code more maintainable:

  1. Rename isAttachWithTop to something more descriptive like isLastMessageNearTop or shouldAutoScrollDisabled
  2. Extract the magic number 100 into a named constant like SCROLL_THRESHOLD_PX
+const SCROLL_THRESHOLD_PX = 100;
+
 const isAttachWithTop = useMemo(() => {
   const lastMessage = scrollRef.current?.lastElementChild as HTMLElement;
   // if scrolllRef is not ready or no message, return false
   if (!scrollRef?.current || !lastMessage) return false;
   const topDistance =
     lastMessage!.getBoundingClientRect().top -
     scrollRef.current.getBoundingClientRect().top;
   // leave some space for user question
-  return topDistance < 100;
+  return topDistance < SCROLL_THRESHOLD_PX;
 }, [scrollRef?.current?.scrollHeight]);
🧰 Tools
🪛 eslint

[error] 963-963: React Hook "useMemo" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9cacaa2 and 19facc7.

📒 Files selected for processing (1)
  • app/components/chat.tsx (1 hunks)
🧰 Additional context used
🪛 eslint
app/components/chat.tsx

[error] 963-963: React Hook "useMemo" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)


[error] 978-978: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

🔇 Additional comments (1)
app/components/chat.tsx (1)

974-980: LGTM! Scroll behavior enhancement implemented correctly

The updated scroll behavior successfully implements the PR objective by:

  1. Auto-scrolling when user is typing
  2. Auto-scrolling when already at bottom
  3. Disabling auto-scroll when the last message is near the top

This improves the user experience by preventing unwanted scrolling when reading previous messages.

🧰 Tools
🪛 eslint

[error] 978-978: React Hook "useScrollToBottom" is called in function "_Chat" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

(react-hooks/rules-of-hooks)

@Dogtiti Dogtiti merged commit a2adfbb into ChatGPTNextWeb:main Nov 16, 2024
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants