Home-Screen-Widgets für Pageta — drei Größen, jede liest aus dem
App-Group-Snapshot den die App nach jedem Login/onAppear refreshed.
Widget-Process macht keine Netzwerk-Calls (kein JWT-Refresh in einer
Extension mit 30s-Limit). Pattern aus herbatrium-native + seepuls-
native: App fetched + persistiert, Widget rendert read-only.
- Sources/Core/WidgetSnapshot.swift: WidgetSnapshot + WidgetFeedItem +
SavedCounts. Mirror von server-side /api/v1/snapshot/widget.
WidgetSnapshotStore mit UserDefaults(suiteName: group.ev.mana.pageta).
- Sources/Core/Sync/SnapshotSync.swift: ruft fetchWidgetSnapshot,
persistiert, triggert WidgetCenter.reloadAllTimelines(). Soft-Fail
bei Network — letzter Snapshot bleibt sichtbar.
- PagetaAPI.fetchWidgetSnapshot() neu.
- PagetaNativeApp.onAppear: zusätzlich zu flushPendingShares jetzt
syncWidgetSnapshot.
- Widgets/PagetaWidget/:
- PagetaWidgetBundle: @main, FeedWidget mit kind "ev.mana.pageta.feed",
3 system-Families, paper-Cream Container-Background.
- FeedProvider: getTimeline alle 1h Fallback, App überschreibt via
WidgetCenter. Preview-Snapshot mit 3 realistischen Items.
- SmallFeedView: Top-1 Item mit Site-Caption + Serif-Title, widgetURL
pageta://article/{id}.
- MediumFeedView: Top-3 mit Header + ungelesen-Count + Color-Bar.
- LargeFeedView: Top-3 als Link (pageta://) + CountsRow (unread/lese
als Pills oben rechts).
- WidgetTheme.swift: paper-Brand-Tokens dupliziert (Widget-Bundle
kann App-Modul nicht importieren).
- EmptyState: Fallback wenn kein Snapshot da.
- project.yml: PagetaWidgetExtension-Target (Bundle
ev.mana.pageta.widget, App-Group, embed in main App). Shared
WidgetSnapshot.swift in beiden Targets — DTO-Drift wird so
unmöglich.
2 neue Tests (WidgetSnapshot-Wire-Format-Decode + Store-Round-Trip),
25/25 grün, iOS+macOS Build green.
Apple-Dev-Portal-Pre für Real-Run:
- Bundle ev.mana.pageta.widget registrieren
- App-Group group.ev.mana.pageta für alle 3 Targets aktivieren
Phase-2 jetzt 4/4 (π-5..π-7+π-6) — nur noch π-8 Spotlight + π-9
macOS-Polish + π-10 ASC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
38 lines
1.2 KiB
Swift
38 lines
1.2 KiB
Swift
import Foundation
|
|
import ManaCore
|
|
#if canImport(WidgetKit)
|
|
import WidgetKit
|
|
#endif
|
|
|
|
/// Treibt den Server→App-Group-UserDefaults-Pfad für das Widget.
|
|
///
|
|
/// Aufrufer: PagetaNativeApp beim Launch (nach Login-Status-Check)
|
|
/// und FeedTabView nach erfolgreichem Feed-Refresh. Snapshot bleibt
|
|
/// auch bei Network-Fail erhalten — letzter erfolgreicher Stand
|
|
/// wird vom Widget angezeigt bis zur nächsten Sync.
|
|
struct SnapshotSync {
|
|
let api: PagetaAPI
|
|
let store: WidgetSnapshotStore
|
|
|
|
init(auth: AuthClient, store: WidgetSnapshotStore = WidgetSnapshotStore()) {
|
|
self.api = PagetaAPI(auth: auth)
|
|
self.store = store
|
|
}
|
|
|
|
/// Pullt das Server-Snapshot und schreibt es in den App-Group.
|
|
/// Triggert `WidgetCenter.reloadAllTimelines()` damit das Widget
|
|
/// auf iOS bei nächster Chance neu rendert.
|
|
func sync() async {
|
|
do {
|
|
let snapshot = try await api.fetchWidgetSnapshot()
|
|
try store.save(snapshot)
|
|
#if canImport(WidgetKit)
|
|
WidgetCenter.shared.reloadAllTimelines()
|
|
#endif
|
|
} catch {
|
|
// Soft-Fail — Widget zeigt den letzten Snapshot weiter.
|
|
// Logging kommt vom Caller, weil wir hier keinen Log-Bezug
|
|
// haben (Log lebt im App-Target).
|
|
}
|
|
}
|
|
}
|