Skip to content

Conversation

@graycreate
Copy link
Member

Summary

Add real-time online user count display in the Feed page pull-to-refresh indicator.

Changes

  • OnlineStatsInfo model: Parses online user count and max record from V2EX homepage HTML
  • New API endpoint: /onlineStats endpoint with desktop UA to fetch V2EX homepage
  • Redux data flow: FetchOnlineStats action and reducer to manage online stats state
  • UI integration: Display online count in pull-to-refresh indicator (e.g., "2613 人在线")
  • Parallel fetching: Online stats fetched alongside feed data for better UX

Screenshots

When users pull down to refresh the feed, they can now see the current number of online users in the community, providing real-time activity information.

Technical Details

  • Uses desktop UA to access V2EX homepage (mobile version doesn't include online count)
  • Parses HTML using regex pattern (\d+)\s*人在线
  • Fetches data in parallel with feed refresh to avoid blocking
  • Only displays when data is valid (online count > 0)

Test Plan

  • Build succeeds on iOS 15.0+
  • Pull-to-refresh displays online user count
  • Data updates correctly on each refresh
  • No impact on existing pull-to-refresh functionality
  • Tested on physical device (iPhone)

🤖 Generated with Claude Code

Add real-time online user count display in the Feed page pull-to-refresh indicator, fetched from V2EX homepage.

Changes:
- Add OnlineStatsInfo model to parse online user count from V2EX HTML
- Add /onlineStats API endpoint with desktop UA to fetch homepage data
- Create FetchOnlineStats action and reducer to handle data flow
- Update HeadIndicatorView to display online count (e.g., "2613 人在线")
- Modify UpdatableView to pass onlineStats through to HeadIndicatorView
- Fetch online stats in parallel with feed data on pull-to-refresh

The online user count appears in the pull-to-refresh indicator when users refresh the feed, providing real-time community activity information.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings October 18, 2025 05:06
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 real-time online user count display to the Feed page's pull-to-refresh indicator. When users pull down to refresh, they can now see the current number of online users (e.g., "2613 人在线") providing real-time activity information about the V2EX community.

  • Creates OnlineStatsInfo model to parse online user count from V2EX homepage HTML using regex patterns
  • Adds new /onlineStats API endpoint with desktop user agent to fetch V2EX homepage
  • Integrates online stats into Redux data flow with actions and reducers
  • Updates pull-to-refresh UI components to display online count when available

Reviewed Changes

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

Show a summary per file
File Description
OnlineStatsInfo.swift New model for parsing online user count and max record from V2EX homepage HTML
Endpoint.swift Adds /onlineStats endpoint with desktop user agent for fetching homepage
FeedState.swift Adds onlineStats property to store online statistics
FeedReducer.swift Implements FetchOnlineStats actions and reducer logic
FeedPage.swift Initiates parallel fetch of online stats during refresh
UpdatableView.swift Passes online stats through the updatable view hierarchy
HeadIndicatorView.swift Displays online user count in pull-to-refresh indicator
project.pbxproj Adds OnlineStatsInfo.swift to Xcode project

Comment on lines 67 to 106
private func extractNumber(before keyword: String, from text: String) -> Int? {
let components = text.components(separatedBy: keyword)
guard let firstPart = components.first else { return nil }

// Extract the last number from the text before keyword
let pattern = "(\\d+)\\s*$"
guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }

let nsText = firstPart as NSString
let matches = regex.matches(in: firstPart, range: NSRange(location: 0, length: nsText.length))

if let match = matches.last, match.numberOfRanges > 1 {
let numberStr = nsText.substring(with: match.range(at: 1))
return Int(numberStr.replacingOccurrences(of: ",", with: ""))
}

return nil
}

private func extractNumber(after keyword: String, from text: String) -> Int? {
let components = text.components(separatedBy: keyword)
guard components.count > 1 else { return nil }

let afterPart = components[1]

// Extract the first number from the text after keyword
let pattern = "(\\d+)"
guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }

let nsText = afterPart as NSString
let matches = regex.matches(in: afterPart, range: NSRange(location: 0, length: nsText.length))

if let match = matches.first, match.numberOfRanges > 1 {
let numberStr = nsText.substring(with: match.range(at: 1))
return Int(numberStr.replacingOccurrences(of: ",", with: ""))
}

return nil
}

Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

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

The extractNumber(before:) and extractNumber(after:) methods are defined but not used in the current implementation. These methods create unnecessary code complexity and should be removed since the parsing logic uses direct regex patterns instead.

Suggested change
private func extractNumber(before keyword: String, from text: String) -> Int? {
let components = text.components(separatedBy: keyword)
guard let firstPart = components.first else { return nil }
// Extract the last number from the text before keyword
let pattern = "(\\d+)\\s*$"
guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }
let nsText = firstPart as NSString
let matches = regex.matches(in: firstPart, range: NSRange(location: 0, length: nsText.length))
if let match = matches.last, match.numberOfRanges > 1 {
let numberStr = nsText.substring(with: match.range(at: 1))
return Int(numberStr.replacingOccurrences(of: ",", with: ""))
}
return nil
}
private func extractNumber(after keyword: String, from text: String) -> Int? {
let components = text.components(separatedBy: keyword)
guard components.count > 1 else { return nil }
let afterPart = components[1]
// Extract the first number from the text after keyword
let pattern = "(\\d+)"
guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }
let nsText = afterPart as NSString
let matches = regex.matches(in: afterPart, range: NSRange(location: 0, length: nsText.length))
if let match = matches.first, match.numberOfRanges > 1 {
let numberStr = nsText.substring(with: match.range(at: 1))
return Int(numberStr.replacingOccurrences(of: ",", with: ""))
}
return nil
}

Copilot uses AI. Check for mistakes.
.updatable(autoRefresh: state.showProgressView, hasMoreData: state.hasMoreData, max(state.scrollToTop, scrollTop(tab: .feed)), onlineStats: state.onlineStats) {
if AccountState.hasSignIn() {
// Fetch online stats in parallel with feed data
async let _ = run(action: FeedActions.FetchOnlineStats.Start())
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

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

Using async let _ = discards the result and creates a fire-and-forget task. Consider using Task { await run(...) } to be more explicit about the parallel execution intent and avoid potential compiler warnings.

Suggested change
async let _ = run(action: FeedActions.FetchOnlineStats.Start())
Task { await run(action: FeedActions.FetchOnlineStats.Start()) }

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

Code Coverage Report ❌

Current coverage: 0%

- Remove unused extractNumber helper methods in OnlineStatsInfo
- Use explicit Task syntax for parallel execution instead of async let

Co-Authored-By: GitHub Copilot <noreply@github.com>
@github-actions github-actions bot added size/M and removed size/M labels Oct 18, 2025
@github-actions
Copy link

Code Coverage Report ❌

Current coverage: 0%

@graycreate graycreate merged commit 06d1675 into main Oct 18, 2025
5 of 6 checks passed
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