Skip to content

Commit d33394c

Browse files
graycreateclaude
andcommitted
fix: Correct balance parsing and optimize UI display
Fix balance parsing issue where numbers were being incorrectly combined (e.g., "2,025,101,344" instead of separate gold/silver/bronze values). Changes: - Fix HTML selector from "div#money a.balance_area" to "div.balance_area" to match actual V2EX /balance endpoint structure - Improve parseFromBalanceDiv to correctly split numbers by <img> tags using regex - Handle comma-separated numbers properly (e.g., "2,025" parses as 2025) - Optimize BalanceView UI: remove capsule backgrounds, reduce font size to 10pt - Tighten spacing: 2pt between badges, 2pt horizontal padding - Only display non-zero balance values The balance now correctly displays as three separate values (e.g., 47 gold, 28 silver, 26 bronze) with a clean, compact design. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bf3cce4 commit d33394c

File tree

4 files changed

+58
-21
lines changed

4 files changed

+58
-21
lines changed

V2er.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
28B24CA92EA3460D00F82B2A /* BalanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B24CA82EA3460D00F82B2A /* BalanceView.swift */; };
1011
28CC76CC2E963D6700C939B5 /* FilterMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490A3E111D941C4B30F0BACA6B5E984 /* FilterMenuView.swift */; };
1112
4E55BE8A29D45FC00044389C /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4E55BE8929D45FC00044389C /* Kingfisher */; };
1213
4EC32AF029D81863003A3BD4 /* WebView in Frameworks */ = {isa = PBXBuildFile; productRef = 4EC32AEF29D81863003A3BD4 /* WebView */; };
@@ -168,6 +169,7 @@
168169
/* End PBXContainerItemProxy section */
169170

170171
/* Begin PBXFileReference section */
172+
28B24CA82EA3460D00F82B2A /* BalanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceView.swift; sourceTree = "<group>"; };
171173
4EC32AF129D818FC003A3BD4 /* WebBrowserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebBrowserView.swift; sourceTree = "<group>"; };
172174
5D02BD5E26909146007B6A1B /* LoadmoreIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadmoreIndicatorView.swift; sourceTree = "<group>"; };
173175
5D04BF9626C9FB6E0005F7E3 /* FeedInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedInfo.swift; sourceTree = "<group>"; };
@@ -349,6 +351,7 @@
349351
5D73FBD827284ACA004558E9 /* RichText */,
350352
5DE5B4CB268466AF00569684 /* Updatable */,
351353
5D1F4264249713640043F604 /* VEBlur.swift */,
354+
28B24CA82EA3460D00F82B2A /* BalanceView.swift */,
352355
5DE5B4C92684601A00569684 /* TopBar.swift */,
353356
5D4E43FB2699E20400650714 /* AvatarView.swift */,
354357
5DED91CD269FBA2D00BD942B /* NavbarHostView.swift */,
@@ -900,6 +903,7 @@
900903
5D0A513D26E36AB9006F3D9B /* FeedDetailState.swift in Sources */,
901904
5D1D8BFE26EBA857009DF65A /* UserDetailReducer.swift in Sources */,
902905
5DF417742712DA7500E6D135 /* MyRecentState.swift in Sources */,
906+
28B24CA92EA3460D00F82B2A /* BalanceView.swift in Sources */,
903907
5DF92A5D26859DDD00E6086E /* HeadIndicatorView.swift in Sources */,
904908
5D6AAAAC2691851100F42A13 /* Utils.swift in Sources */,
905909
5D612FA126C7C34E0009B8F9 /* NetworkException.swift in Sources */,

V2er/State/DataFlow/Model/AccountInfo.swift

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,72 @@ struct BalanceInfo: BaseModel, Codable {
2424
init(from html: Element?) {
2525
guard let root = html else { return }
2626

27-
// Strategy 1: Parse from div#money (main balance page)
27+
// Strategy 1: Parse from div.balance_area (main balance page from /balance endpoint)
28+
// HTML: <div class="balance_area bigger">47 <img src="/static/img/gold@2x.png"> 28 <img src="/static/img/silver@2x.png"> 26 <img src="/static/img/bronze@2x.png"></div>
29+
if let balanceDiv = root.pickOne("div.balance_area") {
30+
parseFromBalanceDiv(balanceDiv)
31+
}
32+
33+
// Strategy 2: Parse from div#money (alternative format, might be on homepage)
2834
// HTML: <div id="money"><a href="/balance" class="balance_area">47 <img...> 28 <img...> 26 <img...></a></div>
29-
if let moneyDiv = root.pickOne("div#money a.balance_area") {
30-
parseFromMoneyDiv(moneyDiv)
35+
if gold == 0 && silver == 0 && bronze == 0 {
36+
if let moneyDiv = root.pickOne("div#money a.balance_area") {
37+
parseFromBalanceDiv(moneyDiv)
38+
}
3139
}
3240

33-
// Strategy 2: Parse from table cells (alternative format)
41+
// Strategy 3: Parse from table cells (alternative format)
3442
// Structure: table.data > tbody > tr > td
3543
if gold == 0 && silver == 0 && bronze == 0 {
3644
parseFromTableCells(root: root)
3745
}
3846

39-
// Strategy 3: Parse from table rows with separate cells
47+
// Strategy 4: Parse from table rows with separate cells
4048
// Some V2EX pages show balance as: <td align="right">47</td><td>金币</td>
4149
if gold == 0 && silver == 0 && bronze == 0 {
4250
parseFromTableRows(root: root)
4351
}
4452
}
4553

46-
private mutating func parseFromMoneyDiv(_ element: Element) {
54+
private mutating func parseFromBalanceDiv(_ element: Element) {
4755
// Parse from: "47 <img src="/static/img/gold@2x.png"> 28 <img src="/static/img/silver@2x.png"> 26 <img src="/static/img/bronze@2x.png">"
48-
let text = element.value(.text)
56+
// Numbers can have commas: "2,025 <img> 101 <img> 344 <img>"
57+
58+
// Get the HTML to parse text nodes between img tags
59+
guard let html = try? element.html() else { return }
60+
61+
// Replace img tags with a delimiter, then split
62+
// The pattern is: NUMBER <img...> NUMBER <img...> NUMBER <img...>
63+
let imgPattern = "<img[^>]*>"
64+
guard let regex = try? NSRegularExpression(pattern: imgPattern, options: []) else { return }
4965

50-
// Split by whitespace and filter out empty strings
51-
let numbers = text.components(separatedBy: .whitespaces)
66+
let nsHtml = html as NSString
67+
let cleanedHtml = regex.stringByReplacingMatches(
68+
in: html,
69+
range: NSRange(location: 0, length: nsHtml.length),
70+
withTemplate: "|" // Use | as delimiter
71+
)
72+
73+
// Split by delimiter and extract numbers
74+
let parts = cleanedHtml.components(separatedBy: "|")
5275
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
5376
.filter { !$0.isEmpty }
54-
.compactMap { parseNumber($0) }
77+
78+
// Extract first number from each part (handling commas)
79+
var numbers: [Int] = []
80+
let numberPattern = "[0-9,]+"
81+
guard let numberRegex = try? NSRegularExpression(pattern: numberPattern) else { return }
82+
83+
for part in parts {
84+
let nsPart = part as NSString
85+
let matches = numberRegex.matches(in: part, range: NSRange(location: 0, length: nsPart.length))
86+
if let firstMatch = matches.first {
87+
let numStr = nsPart.substring(with: firstMatch.range)
88+
if let num = parseNumber(numStr) {
89+
numbers.append(num)
90+
}
91+
}
92+
}
5593

5694
// Assign in order: gold, silver, bronze
5795
if numbers.count >= 1 { gold = numbers[0] }

V2er/View/Me/MePage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ struct MePage: BaseHomePageView {
6565
Text(AccountState.userName)
6666
.font(.headline)
6767
if let balance = AccountState.balance, balance.isValid() {
68-
BalanceView(balance: balance, size: 14)
68+
BalanceView(balance: balance, size: 12)
6969
}
7070
}
7171
Spacer()

V2er/View/Widget/BalanceView.swift

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import SwiftUI
1010

1111
struct BalanceView: View {
1212
var balance: BalanceInfo
13-
var size: CGFloat = 14
13+
var size: CGFloat = 10
1414

1515
var body: some View {
16-
HStack(spacing: 8) {
16+
HStack(spacing: 2) {
1717
if balance.gold > 0 {
1818
BalanceBadge(count: balance.gold, icon: "🟡", color: .yellow, size: size)
1919
}
@@ -34,19 +34,14 @@ struct BalanceBadge: View {
3434
var size: CGFloat
3535

3636
var body: some View {
37-
HStack(spacing: 4) {
37+
HStack(spacing: 0) {
3838
Text(icon)
39-
.font(.system(size: size - 2))
39+
.font(.system(size: size - 1))
4040
Text("\(count)")
4141
.font(.system(size: size, weight: .medium))
4242
.foregroundColor(.primaryText)
4343
}
44-
.padding(.horizontal, 8)
45-
.padding(.vertical, 4)
46-
.background(
47-
RoundedRectangle(cornerRadius: 12)
48-
.fill(Color.lightGray.opacity(0.3))
49-
)
44+
.padding(.horizontal, 2)
5045
}
5146
}
5247

0 commit comments

Comments
 (0)