field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit 43653030823fa17024ee4ba11bc711763306049b
parent 0cbebebf022a883db2043b55ad5ced506885a19b
Author: triesap <tyson@radroots.org>
Date:   Fri, 28 Aug 2026 19:15:56 +0000

ios: expose deterministic Today refresh

- keep an ordinary refresh action available after Today has cards
- retain the empty-state refresh identifier as a distinct UI element
- require persona qualification to invoke the visible refresh action
- bind relay rematerialization to an exact user-observable operation

Diffstat:
MRadroots/Views/RadrootsTodayView.swift | 10+++++++++-
MRadrootsUITests/RadrootsRemoteQualificationUITests.swift | 6++++--
2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/Radroots/Views/RadrootsTodayView.swift b/Radroots/Views/RadrootsTodayView.swift @@ -90,7 +90,7 @@ struct RadrootsTodayView: View { .tint(.primary) .frame(minWidth: 44, minHeight: 44) .contentShape(Rectangle()) - .accessibilityIdentifier("radroots.today.refresh") + .accessibilityIdentifier("radroots.today.refresh.empty") } .frame(maxWidth: .infinity) .padding() @@ -160,6 +160,14 @@ struct RadrootsTodayView: View { } ToolbarItem(placement: .topBarTrailing) { Button { + Task { await store.reload() } + } label: { + Label("Refresh", systemImage: "arrow.clockwise") + } + .accessibilityIdentifier("radroots.today.refresh") + } + ToolbarItem(placement: .topBarTrailing) { + Button { showsSearch = true } label: { Label("Search", systemImage: "magnifyingglass") diff --git a/RadrootsUITests/RadrootsRemoteQualificationUITests.swift b/RadrootsUITests/RadrootsRemoteQualificationUITests.swift @@ -1034,9 +1034,11 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { private func assertTodayContains(_ app: XCUIApplication, markers: [String]) throws { app.tabBars.buttons["Today"].tap() let refresh = app.buttons["radroots.today.refresh"] - if refresh.waitForExistence(timeout: 5) { - refresh.tap() + guard refresh.waitForExistence(timeout: 10), refresh.isHittable else { + XCTFail("The ordinary Today refresh action was unavailable") + throw QualificationError.missingProductSurface } + refresh.tap() let feed = app.descendants(matching: .any)["radroots.today.feed"].firstMatch guard feed.waitForExistence(timeout: 30) else { XCTFail("The local relay refresh did not materialize the Today feed")