Skip to content

Commit d2a0cba

Browse files
committed
feat(ios): add Android/WebApp parity sections and UX improvements
1. ScanResultView Parity (Android/WebApp): - Added ScanStatusBadge showing scan completion status - Added UrlDisplayRow with link icon - Added AnalysisMetaRow ('Analyzed offline • No data leaves device') - Added EngineStatsCard (analysis time, signals, engine version) - Added TopAnalysisFactorsSection with PASS/FAIL/WARN/CRITICAL cards 2. History Navigation Fix: - Changed from popup sheet to full NavigationLink navigation - Clicking history item now opens full ScanResultView - Added toRiskAssessment() conversion on HistoryItemMock 3. Beat the Bot Reset Button: - Added reset button (⟲) to toolbar - resetGame() resets all state and starts fresh Files: 8 files changed Version: 1.20.26
1 parent f39dc31 commit d2a0cba

8 files changed

Lines changed: 518 additions & 33 deletions

File tree

.agent/agent.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This file tracks significant changes made during development sessions.
88

99
## ⚠️ CRITICAL: Version Management
1010

11-
**Current App Version: `1.20.4`** (as of December 30, 2025)
11+
**Current App Version: `1.20.26`** (as of December 31, 2025)
1212

1313
### 🔴 After Making ANY Improvements, YOU MUST Update Version Numbers:
1414

@@ -185,6 +185,53 @@ Any important notes for future agents.
185185

186186
---
187187

188+
# 📱 December 31, 2025 (Session 10k+66) - iOS Parity & UX Improvements
189+
190+
### Summary
191+
Added Android/WebApp parity sections to iOS ScanResultView, fixed History navigation to show full result screen, and added Reset button to Beat the Bot game.
192+
193+
## ✅ Changes Made
194+
195+
### 1. ScanResultView Parity (Android/WebApp)
196+
- Added `ScanStatusBadge` - shows "Scan Complete" / "Threat Detected" based on verdict
197+
- Added `UrlDisplayRow` - URL with link icon
198+
- Added `AnalysisMetaRow` - "Analyzed offline • No data leaves device"
199+
- Added `EngineStatsCard` - analysis time (ms), signals count, engine version (KMP/Swift)
200+
- Added `TopAnalysisFactorsSection` - grid of factor cards with PASS/FAIL/WARN/CRITICAL tags
201+
- HTTPS check
202+
- Domain analysis
203+
- Database check
204+
- Heuristics check
205+
206+
### 2. History Navigation Fix
207+
- **Before:** Clicking history item showed small popup sheet (HistoryDetailSheet)
208+
- **After:** Clicking history item navigates to full ScanResultView
209+
- Added `toRiskAssessment()` conversion method on HistoryItemMock
210+
- Removed unused `selectedItem` state and sheet modifier
211+
212+
### 3. Beat the Bot Reset Button
213+
- Added reset button (⟲) to toolbar leading position
214+
- `resetGame()` function resets: timer, points, streak, accuracy, challenge, signals
215+
- Includes haptic feedback on reset
216+
217+
## 📁 Files Modified
218+
219+
| File | Change |
220+
|------|--------|
221+
| `ScanResultView.swift` | +5 new sections (ScanStatusBadge, UrlDisplayRow, AnalysisMetaRow, EngineStatsCard, TopAnalysisFactorsSection) |
222+
| `HistoryView.swift` | Changed to NavigationLink, removed sheet |
223+
| `MockTypes.swift` | Added `toRiskAssessment()` conversion |
224+
| `BeatTheBotView.swift` | Added reset button and `resetGame()` function |
225+
| `en.lproj/Localizable.strings` | +30 new strings |
226+
| `project.pbxproj` | Version 1.20.26 |
227+
| `CHANGELOG.md` | Version 1.20.26 entry |
228+
| `agent.md` | This session entry |
229+
230+
## Build Verification
231+
- `xcodebuild -scheme QRShield build`
232+
233+
---
234+
188235
# 🔄 December 31, 2025 (Session 10k+65) - iOS Full Parity Audit - Dynamic Analysis
189236

190237
### Summary

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,45 @@
22

33
## Unreleased
44

5+
## [1.20.26] - 2025-12-31
6+
7+
### Raouf: iOS Parity & UX Improvements (2025-12-31 10:45 AEDT)
8+
9+
**Scope:** iOS ScanResultView parity with Android/Desktop, History navigation fix, Beat the Bot reset button
10+
11+
**Changes:**
12+
13+
1. **ScanResultView Parity (Android/WebApp)**
14+
- Added `ScanStatusBadge` showing scan completion status
15+
- Added `UrlDisplayRow` with link icon
16+
- Added `AnalysisMetaRow` ("Analyzed offline • No data leaves device")
17+
- Added `EngineStatsCard` showing analysis time, signals count, engine version
18+
- Added `TopAnalysisFactorsSection` with factor cards (PASS/FAIL/WARN/CRITICAL tags)
19+
- Factor cards include: HTTPS check, Domain analysis, Database check, Heuristics check
20+
21+
2. **History Navigation Fix**
22+
- Changed from popup sheet to full NavigationLink navigation
23+
- Clicking a history item now opens the full ScanResultView
24+
- Added `toRiskAssessment()` conversion method on HistoryItemMock
25+
26+
3. **Beat the Bot Reset Button**
27+
- Added reset button (⟲) to toolbar leading position
28+
- `resetGame()` function resets all game state and starts fresh
29+
- Includes haptic feedback on reset
30+
31+
**Files Modified:**
32+
- `ScanResultView.swift` - Added 5 new sections and ~200 lines
33+
- `HistoryView.swift` - Changed to NavigationLink, removed sheet
34+
- `MockTypes.swift` - Added `toRiskAssessment()` conversion
35+
- `BeatTheBotView.swift` - Added reset button and function
36+
- `en.lproj/Localizable.strings` - Added 30+ new strings
37+
- `project.pbxproj` - Version 1.20.26
38+
39+
**Build Verification:**
40+
- `xcodebuild -scheme QRShield build`
41+
42+
---
43+
544
## [1.20.25] - 2025-12-31
645

746
### Raouf: iOS Full Parity Audit - Dynamic Analysis Breakdowns (2025-12-31 10:20 AEDT)

iosApp/QRShield.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@
413413
"$(inherited)",
414414
"@executable_path/Frameworks",
415415
);
416-
MARKETING_VERSION = 1.20.25;
416+
MARKETING_VERSION = 1.20.26;
417417
PRODUCT_BUNDLE_IDENTIFIER = com.qrshield.ios;
418418
PRODUCT_NAME = "$(TARGET_NAME)";
419419
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -448,7 +448,7 @@
448448
"$(inherited)",
449449
"@executable_path/Frameworks",
450450
);
451-
MARKETING_VERSION = 1.20.25;
451+
MARKETING_VERSION = 1.20.26;
452452
PRODUCT_BUNDLE_IDENTIFIER = com.qrshield.ios;
453453
PRODUCT_NAME = "$(TARGET_NAME)";
454454
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

iosApp/QRShield/Models/MockTypes.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,20 @@ struct HistoryItemMock: Identifiable, Sendable, Hashable, Codable {
154154
static func == (lhs: HistoryItemMock, rhs: HistoryItemMock) -> Bool {
155155
lhs.id == rhs.id
156156
}
157+
158+
/// Converts HistoryItemMock to RiskAssessmentMock for display in ScanResultView
159+
/// The flags will be re-analyzed by the engine when ScanResultView loads
160+
func toRiskAssessment() -> RiskAssessmentMock {
161+
RiskAssessmentMock(
162+
id: UUID(uuidString: id) ?? UUID(),
163+
score: score,
164+
verdict: verdict,
165+
flags: [], // Will be populated by re-analysis in ScanResultView
166+
confidence: Double(score) / 100.0,
167+
url: url,
168+
scannedAt: scannedAt
169+
)
170+
}
157171
}
158172

159173
#endif

iosApp/QRShield/UI/History/HistoryView.swift

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ struct HistoryView: View {
3030
@State private var viewModel = HistoryViewModel()
3131
@State private var searchText = ""
3232
@State private var selectedFilter: VerdictFilter = .all
33-
@State private var selectedItem: HistoryItemMock?
3433
@State private var showExportedToast = false
3534
@State private var showClearConfirmation = false
3635
@State private var showStatsPopover = false
@@ -158,11 +157,6 @@ struct HistoryView: View {
158157
.onChange(of: searchText) { _, newValue in
159158
viewModel.search(query: newValue)
160159
}
161-
.sheet(item: $selectedItem) { item in
162-
HistoryDetailSheet(item: item)
163-
.presentationDetents([.medium])
164-
.presentationBackground(.ultraThinMaterial)
165-
}
166160
.onAppear {
167161
viewModel.refreshHistory()
168162
}
@@ -233,33 +227,33 @@ struct HistoryView: View {
233227
ScrollView {
234228
LazyVStack(spacing: 8) {
235229
ForEach(viewModel.filteredHistory) { item in
236-
HistoryRow(item: item)
237-
.onTapGesture {
238-
selectedItem = item
230+
NavigationLink(destination: ScanResultView(assessment: item.toRiskAssessment())) {
231+
HistoryRow(item: item)
232+
}
233+
.buttonStyle(.plain)
234+
.contextMenu {
235+
Button {
236+
UIPasteboard.general.string = item.url
237+
SettingsManager.shared.triggerHaptic(.success)
238+
} label: {
239+
Label(NSLocalizedString("result.copy_url", comment: ""), systemImage: "doc.on.doc")
239240
}
240-
.contextMenu {
241-
Button {
242-
UIPasteboard.general.string = item.url
243-
SettingsManager.shared.triggerHaptic(.success)
244-
} label: {
245-
Label(NSLocalizedString("result.copy_url", comment: ""), systemImage: "doc.on.doc")
246-
}
247-
248-
ShareLink(item: item.url) {
249-
Label(NSLocalizedString("result.share", comment: ""), systemImage: "square.and.arrow.up")
250-
}
251-
252-
Divider()
253-
254-
Button(role: .destructive) {
255-
withAnimation(.spring(response: 0.3)) {
256-
viewModel.delete(item)
257-
}
258-
} label: {
259-
Label(NSLocalizedString("history.delete", comment: ""), systemImage: "trash")
241+
242+
ShareLink(item: item.url) {
243+
Label(NSLocalizedString("result.share", comment: ""), systemImage: "square.and.arrow.up")
244+
}
245+
246+
Divider()
247+
248+
Button(role: .destructive) {
249+
withAnimation(.spring(response: 0.3)) {
250+
viewModel.delete(item)
260251
}
252+
} label: {
253+
Label(NSLocalizedString("history.delete", comment: ""), systemImage: "trash")
261254
}
262-
.transition(.move(edge: .trailing).combined(with: .opacity))
255+
}
256+
.transition(.move(edge: .trailing).combined(with: .opacity))
263257
}
264258
}
265259
.padding(.horizontal)

0 commit comments

Comments
 (0)