Skip to content

feat(conversation-list): 添加置顶和取消置顶会话功能#434

Open
1024570189 wants to merge 1 commit intolexmin0412:mainfrom
1024570189:feature-topPost
Open

feat(conversation-list): 添加置顶和取消置顶会话功能#434
1024570189 wants to merge 1 commit intolexmin0412:mainfrom
1024570189:feature-topPost

Conversation

@1024570189
Copy link
Copy Markdown

@1024570189 1024570189 commented Dec 3, 2025

Description

Linked Issues

Additional context

Summary by CodeRabbit

  • New Features

    • Conversation pinning: Pin your most frequently-used conversations to a dedicated top section, keeping them easily accessible and visible above your regular conversation list.
    • Conveniently toggle pin status through the conversation context menu for easy management.
  • Bug Fixes

    • Enhanced conversation rename validation with improved error handling and user feedback during the renaming process.

✏️ Tip: You can customize this high-level summary in your review settings.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Dec 3, 2025
@vercel
Copy link
Copy Markdown

vercel Bot commented Dec 3, 2025

Someone is attempting to deploy a commit to the lexmin0412's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 3, 2025

Walkthrough

Enhanced the conversation list component with pinning functionality. Added local state for managing top/pinned conversations, updated the label type definition to support ReactNode, implemented top/untop conversation handlers, and refactored rendering to display pinned conversations separately from regular ones.

Changes

Cohort / File(s) Summary
Conversation List Enhancement
packages/react-app/src/components/conversation-list/index.tsx
Added pinning functionality with topItems state management; enhanced IConversationItem.label type to accept ReactNode; implemented topConversation and untopConversation handlers with validation; refactored menu configuration to support pinned/unpinned toggle; updated rendering to display two separate conversation blocks (pinned and regular) with appropriate filtering and menu options

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Type system changes: Verify IConversationItem.label type expansion to ReactNode doesn't break existing usage
  • State management: Review topItems state logic and ensure it properly tracks pinned conversations across re-renders
  • Handler validation: Confirm topConversation and untopConversation handlers correctly validate conversation.label and prevent invalid states
  • Rendering logic: Verify the two-block conversation rendering (pinned and filtered regular) correctly handles edge cases and re-render performance
  • Menu configuration: Test that the menu wiring in handleMenuClick correctly maps to all handler functions

Poem

📌 Conversations now rest at the top,
Pinned with precision, a treasured crop,
Labels dancing as nodes so fine,
State manages what's yours and mine,
Organization hops with delight! 🐰✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature added: pinning and unpinning conversations, which is the primary change in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@1024570189
Copy link
Copy Markdown
Author

我想增加一个左侧聊天列表的一个置顶功能,希望可以作为参考增加功能,或者看一眼我的代码

@dosubot dosubot Bot added enhancement New feature or request javascript Pull requests that update javascript code labels Dec 3, 2025
Copy link
Copy Markdown

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react-app/src/components/conversation-list/index.tsx (1)

78-79: Remove duplicate validateFields call.

Line 78 calls validateFields() but doesn't use the result. Line 79 calls it again and uses the result. This is redundant.

Apply this diff:

 	onOk: async () => {
-		await renameForm.validateFields()
 		const values = await renameForm.validateFields()
 		await renameConversationPromise(conversation.key, values.name)
 		message.success('对话重命名成功')
 	},
🧹 Nitpick comments (4)
packages/react-app/src/components/conversation-list/index.tsx (4)

1-1: Consider more semantically appropriate icons for pin/unpin actions.

DownloadOutlined and UploadOutlined may not clearly convey "pin" and "unpin" operations to users. Consider using PushpinOutlined/PushpinFilled or similar icons that better represent pinning functionality.


114-129: Consider defining menu action keys as constants.

Menu keys like 'delete', 'rename', 'top', 'untop' are used as magic strings across switch statement and menu items. Extracting them as constants improves maintainability and prevents typos.

const MENU_ACTIONS = {
	DELETE: 'delete',
	RENAME: 'rename',
	TOP: 'top',
	UNTOP: 'untop',
} as const

Then use MENU_ACTIONS.DELETE etc. in the switch statement and menu items.

Also applies to: 136-175


200-200: Optimize filtering logic for better performance.

The nested filter with .some() has O(n*m) time complexity. For large conversation lists, this could cause performance issues on every render.

Use a Set for O(n) lookup:

+const topItemKeys = new Set(topItems.map(item => item.key))
+const normalItems = items.filter(item => !topItemKeys.has(item.key))
+
 {/* 普通会话列表 */}
 <Conversations
 	className="!p-0"
-	items={items.filter(item => !topItems.some(topItem => topItem.key === item.key))}
+	items={normalItems}
 	activeKey={activeKey}
 	onActiveChange={onActiveChange}
 	menu={generateMenuConfig(false)}
 />

180-195: Simplify conditional rendering.

Using both display: 'none' and checking topItems.length > 0 for visibility is redundant. Use conditional rendering instead for cleaner code.

-{/* 置顶列表 */}
-<div
-	style={{
-		marginBottom: '10px',
-		borderBottom: topItems.length > 0 ? '1px solid #f0f0f0' : 'none',
-		paddingBottom: '10px',
-		display: topItems.length > 0 ? 'block' : 'none',
-	}}
->
-	<Conversations
-		className="!p-0"
-		items={topItems}
-		activeKey={activeKey}
-		onActiveChange={onActiveChange}
-		menu={generateMenuConfig(true)}
-	/>
-</div>
+{/* 置顶列表 */}
+{topItems.length > 0 && (
+	<div
+		style={{
+			marginBottom: '10px',
+			borderBottom: '1px solid #f0f0f0',
+			paddingBottom: '10px',
+		}}
+	>
+		<Conversations
+			className="!p-0"
+			items={topItems}
+			activeKey={activeKey}
+			onActiveChange={onActiveChange}
+			menu={generateMenuConfig(true)}
+		/>
+	</div>
+)}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7608120 and 072937b.

📒 Files selected for processing (1)
  • packages/react-app/src/components/conversation-list/index.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/{api,core,docs,helpers,platform,react-app,theme}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not import the deprecated packages/components library from any other subpackage

Files:

  • packages/react-app/src/components/conversation-list/index.tsx
🔇 Additional comments (2)
packages/react-app/src/components/conversation-list/index.tsx (2)

8-8: Type change from string to ReactNode requires verification of rename function compatibility.

Changing label to ReactNode may conflict with downstream usage if it's cast to a string elsewhere. Verify that the rename function at line 63 and other usages properly handle ReactNode values, or consider using a separate field for rendering if label needs to remain a string identifier.


58-61: Guard condition may indicate incomplete data validation upstream.

If conversation.label can be empty, determine whether this represents legitimate data that should be prevented earlier (e.g., at creation time) or if it's defensive programming for unexpected edge cases. If labels must always be populated when conversations are created, move this validation to the data source. If defensive programming is intentional, consider clarifying the guard's purpose in a comment.

props

const [renameForm] = Form.useForm()
const [topItems, setTopItems] = useState<IConversationItem[]>([])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Pinned state is not persisted and will be lost.

The topItems state is local and ephemeral - it will reset on component unmount or page refresh. Users will lose their pinning preferences, which severely impacts UX for a pinning feature.

Consider:

  1. Lift state to parent component and accept pinnedItems as a prop with an onPinnedChange callback, or
  2. Persist to localStorage/backend, or
  3. Add a prop like topConversationPromise: (conversationId: string) => Promise<unknown> to persist pin state

Apply this diff to lift state to parent:

 interface IConversationListProps {
 	deleteConversationPromise: (conversationId: string) => Promise<unknown>
 	renameConversationPromise: (conversationId: string, name: string) => Promise<unknown>
+	topConversationPromise?: (conversationId: string, isPinned: boolean) => Promise<unknown>
 	items: IConversationItem[]
+	pinnedConversationIds?: string[]
 	activeKey?: string
 	onActiveChange?: (key: string) => void
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/react-app/src/components/conversation-list/index.tsx around line 42,
the local topItems state is ephemeral and causes pinned items to be lost on
unmount/refresh; replace the internal useState with a controlled prop (e.g.,
pinnedItems: IConversationItem[]) and an onPinnedChange callback so the parent
owns persistence and updates; update the component props/type definitions to
accept pinnedItems and onPinnedChange (default pinnedItems to []), remove
setTopItems usage and call onPinnedChange whenever a pin/unpin occurs;
alternatively support a persistence callback prop like
topConversationPromise(conversationId) to persist pin state to
localStorage/backend if lifting is not desirable.

Comment thread packages/react-app/src/components/conversation-list/index.tsx
Comment on lines +109 to +112
const validConversation: IConversationItem = {
key: conversation.key,
label: conversation.label || '未命名会话',
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated validConversation construction.

The same object construction pattern appears twice (lines 109-112 and 168-171). Extract this into a helper function to reduce duplication.

Add this helper before handleMenuClick:

+const ensureValidConversation = (conversation: IConversationItem): IConversationItem => ({
+	key: conversation.key,
+	label: conversation.label || '未命名会话',
+})
+
 const handleMenuClick = (conversation: IConversationItem, menuKey: string) => {
-	const validConversation: IConversationItem = {
-		key: conversation.key,
-		label: conversation.label || '未命名会话',
-	}
+	const validConversation = ensureValidConversation(conversation)

And use it in generateMenuConfig:

 	onClick: async (menuInfo: { domEvent: React.MouseEvent; key: string }) => {
 		menuInfo.domEvent.stopPropagation()
-		const validConversation: IConversationItem = {
-			key: conversation.key,
-			label: conversation.label || '未命名会话',
-		}
+		const validConversation = ensureValidConversation(conversation)
 		handleMenuClick(validConversation, menuInfo.key)
 	},

Also applies to: 168-171

🤖 Prompt for AI Agents
In packages/react-app/src/components/conversation-list/index.tsx around lines
109-112 and 168-171, the construction of validConversation is duplicated; create
a small helper (e.g., createValidConversationItem(conversation: IConversation):
IConversationItem) placed before handleMenuClick that returns { key:
conversation.key, label: conversation.label || '未命名会话' }, then replace the
inline object constructions in both locations (lines ~109-112 and ~168-171) to
call this helper so the logic is centralized and duplication removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request javascript Pull requests that update javascript code size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant