feat: automatic foreground sync — 5 min timer + resume refresh
Some checks failed
CI / Build · macos-14 · Debug (push) Has been cancelled
CI / Build · macos-15 · Debug (push) Has been cancelled
CI / Build · macos-14 · Release (push) Has been cancelled
CI / Build · macos-15 · Release (push) Has been cancelled

StoryStore gains startForegroundSync() / stopForegroundSync():
- Polls loadStories(refresh:true) every 5 minutes while app is active
- WebSocket handles real-time storyCreated events between polls
- Timer cancels when app goes to background to save battery

JarvisApp wires scenePhase:
- .active  → start 5-min timer; immediate refresh if last sync > 2 min ago
- .background → cancel timer
- On launch → timer starts after setup completes

Net result: feed replenishes automatically; user never hits an empty list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-06-21 02:57:54 +03:00
parent 7a97118d12
commit a1d7979048
2 changed files with 50 additions and 2 deletions

View File

@@ -16,10 +16,32 @@ final class StoryStore: ObservableObject {
@Published var lastSyncedAt: Date?
@Published var hasMore = false
@Published var selectedTopic: String? = nil
@Published var selectedTags: Set<String> = []
private var nextCursor: String?
private var refreshTask: Task<Void, Never>?
private var foregroundSyncTask: Task<Void, Never>?
private var cancellables = Set<AnyCancellable>()
// MARK: - Foreground sync
/// Call when app becomes active. Polls every 5 min so the feed never runs dry.
/// The WebSocket handles real-time events between polls.
func startForegroundSync() {
foregroundSyncTask?.cancel()
foregroundSyncTask = Task { [weak self] in
while !Task.isCancelled {
try? await Task.sleep(nanoseconds: 5 * 60 * 1_000_000_000)
guard !Task.isCancelled else { return }
await self?.loadStories(refresh: true)
}
}
}
func stopForegroundSync() {
foregroundSyncTask?.cancel()
foregroundSyncTask = nil
}
private let ws = WebSocketManager.shared
private let api = APIClient.shared
@@ -42,7 +64,8 @@ final class StoryStore: ObservableObject {
do {
let result = try await api.fetchStories(
cursor: cursor,
topic: selectedTopic
topic: selectedTopic,
tags: selectedTags
)
stories = refresh ? result.data : stories + result.data
nextCursor = result.nextCursor
@@ -61,7 +84,7 @@ final class StoryStore: ObservableObject {
guard hasMore, !isLoadingMore, let cursor = nextCursor else { return }
isLoadingMore = true
do {
let result = try await api.fetchStories(cursor: cursor, topic: selectedTopic)
let result = try await api.fetchStories(cursor: cursor, topic: selectedTopic, tags: selectedTags)
stories += result.data
nextCursor = result.nextCursor
hasMore = result.hasMore
@@ -82,6 +105,14 @@ final class StoryStore: ObservableObject {
func setTopic(_ topic: String?) async {
selectedTopic = topic
selectedTags = []
await loadStories(refresh: true)
}
func setPill(_ pill: StoryPill) async {
selectedTopic = nil
selectedTags = pill.slugs
nextCursor = nil
await loadStories(refresh: true)
}