Skip to content

Conversation

@graycreate
Copy link
Member

Summary

  • Add ability to sort replies by popularity (like count) in topic detail page
  • Implement segmented control UI to toggle between time-based and popularity-based sorting
  • Support dark/light mode color adaptation for the segment control

Changes

  • FeedDetailState.swift: Add ReplySortType enum and replySortType state
  • FeedDetailInfo.swift: Add loveCount computed property to parse like counts
  • FeedDetailPage.swift: Add sorting logic and segmented control UI

Test plan

  • Open any topic with replies
  • Verify "回复" section header shows segmented control with "时间" and "热门" options
  • Tap "热门" to sort by likes (highest first)
  • Tap "时间" to restore original floor order
  • Test in both light and dark mode for proper color adaptation

🤖 Generated with Claude Code

- Add ReplySortType enum with byTime and byPopularity options
- Add loveCount computed property to parse like counts from reply items
- Add segmented control UI to toggle between time and popularity sorting
- Sort replies by like count (descending), with floor number as tiebreaker
- Support dark/light mode color adaptation for the segment control

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copilot AI review requested due to automatic review settings December 26, 2025 23:02
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds the ability to sort topic replies by popularity (like count) in addition to the default chronological ordering. Users can toggle between "时间" (time/floor order) and "热门" (popularity/likes) sorting using a new segmented control in the reply section header.

Key Changes:

  • Added ReplySortType enum to define sorting modes with display names and icons
  • Implemented loveCount computed property to parse numeric like counts from reply data
  • Added segmented control UI in reply section header with proper dark/light mode color adaptation

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
V2er/State/DataFlow/State/FeedDetailState.swift Defines ReplySortType enum and adds replySortType state property to track current sort mode
V2er/State/DataFlow/Model/FeedDetailInfo.swift Adds loveCount computed property to extract numeric value from the love string field for sorting
V2er/View/FeedDetail/FeedDetailPage.swift Implements sortedReplies computed property for sorting logic and replySectionHeader UI with segmented control

ForEach(ReplySortType.allCases, id: \.self) { sortType in
Button {
withAnimation(.easeInOut(duration: 0.2)) {
store.appState.feedDetailStates[instanceId]?.replySortType = sortType
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

Direct state mutation is used here for UI-only state. While the project follows a Redux-like architecture where state changes typically flow through actions and reducers (see CLAUDE.md), there are a few existing instances of direct mutation for simple UI state (e.g., line 310 in this file for replyContent, line 28 in ExplorePage.swift for scrollTopTab).

However, for consistency and maintainability, consider whether this UI state should be a local @State variable instead (similar to how MyFavoritePage.swift uses @State private var selectedTab for its segmented control), or follow the Redux pattern with an action and reducer case. This would make the state management pattern more consistent and easier to understand.

Copilot uses AI. Check for mistakes.
Comment on lines 225 to 228
// 提取数字部分
let numbers = trimmed.components(separatedBy: CharacterSet.decimalDigits.inverted)
.joined()
return Int(numbers) ?? 0
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

The parsing logic for loveCount may not correctly handle all possible formats of the love field. The current implementation uses components(separatedBy: CharacterSet.decimalDigits.inverted).joined() which will concatenate all digit sequences together.

For example, if love is "♥ 1 2 3", this would return "123" instead of extracting just the first number. While the V2EX format typically shows "♥ 3" or just "3", it's safer to extract only the first number sequence to avoid unexpected behavior.

Consider using a more robust approach like trimmed.components(separatedBy: CharacterSet.decimalDigits.inverted).first(where: { !$0.isEmpty }) or a regex pattern to extract the first numeric value.

Suggested change
// 提取数字部分
let numbers = trimmed.components(separatedBy: CharacterSet.decimalDigits.inverted)
.joined()
return Int(numbers) ?? 0
// 提取第一个连续的数字片段,避免将多个数字段拼接在一起
let numberString = trimmed
.components(separatedBy: CharacterSet.decimalDigits.inverted)
.first(where: { !$0.isEmpty }) ?? ""
return Int(numberString) ?? 0

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

Code Coverage Report ❌

Current coverage: 32.36%

Address Copilot review feedback: use first(where:) instead of joined()
to extract only the first digit sequence, avoiding concatenation of
multiple number segments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@github-actions github-actions bot added size/M and removed size/M labels Dec 26, 2025
@github-actions
Copy link

Code Coverage Report ❌

Current coverage: 30.75%

- Remove animation when switching sort type for instant feedback
- Use floor as stable id in ForEach to prevent list jumping on sort change

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copilot AI review requested due to automatic review settings December 27, 2025 02:02
@github-actions github-actions bot added size/M and removed size/M labels Dec 27, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

? Color.tintColor
: Color.clear
)
}
Copy link

Copilot AI Dec 27, 2025

Choose a reason for hiding this comment

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

The segmented control buttons should use .buttonStyle(.plain) to prevent default button highlighting behavior in iOS. Without this, tapping a button may show unwanted highlighting effects that conflict with the custom selected state styling. Add .buttonStyle(.plain) after the Button's label closure (line 353).

Suggested change
}
}
.buttonStyle(.plain)

Copilot uses AI. Check for mistakes.

// Reply Section
ForEach(state.model.replyInfo.items) { item in
ForEach(sortedReplies, id: \.floor) { item in
Copy link

Copilot AI Dec 27, 2025

Choose a reason for hiding this comment

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

Using floor as the ForEach identifier instead of the default id (UUID) property will cause SwiftUI view identity issues when sorting changes. When the sort order toggles between time and popularity, SwiftUI will incorrectly reuse views because the floor numbers don't change, leading to:

  1. Animation glitches or no animations
  2. Potential view state persistence issues (e.g., expanded states, gestures)
  3. Views not properly recreating when they should

The Item struct is already Identifiable with a UUID id property. Keep using the default identifier by changing this line back to ForEach(sortedReplies) without the id: parameter.

Suggested change
ForEach(sortedReplies, id: \.floor) { item in
ForEach(sortedReplies) { item in

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

Code Coverage Report ❌

Current coverage: 28.78%

@graycreate graycreate merged commit 54a62cd into main Dec 27, 2025
12 checks passed
@graycreate graycreate deleted the feature/reply-sort-by-popularity branch December 27, 2025 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants