Spiegel von @mana/shared-schemas/pageta@0.1.21 (Web = SOT): - ArticleMediaArchivedPayload + Reducer-Case (non-destruktiv, if-let wie warning); ArticleEnriched clobbert imageUrl nicht mehr - ArticleState.htmlContent; stateToArticle mappt es (war nil) - ArticleExtractPreview dekodiert imageUrl (og:image-Hotlink) - archiveMedia(): POST /articles/archive-media, fire-and-forget nach Save/Enrich; NUR die archivierte media.mana.how-URL wird emittiert, nie der Hotlink; Delete-Race-Guard wie im Web-Client - MediaURL.variant (thumb/medium/large, Spiegel von media-url.ts) - Lese-Liste: 56pt-Thumbnail; Reader-Header: Lead-Image (medium, 200pt) - 5 neue Reducer-/MediaURL-Tests (54/54 grün, iPhone-17-Simulator) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
172 lines
5.5 KiB
Swift
172 lines
5.5 KiB
Swift
import Foundation
|
|
import ManaEventSync
|
|
|
|
// Typed payloads per event type — Swift-Spiegel der `emit`-Aufrufe in
|
|
// `apps/web/src/lib/event-sync-api.ts`. Pageta hat noch kein
|
|
// `shared-schemas/pageta/events/*.ts`; der Web-Client ist die SOT.
|
|
//
|
|
// **Wire-Roundtrip:** Beim Emit: payload-Struct → `JSONValue.from(_:)`
|
|
// → `EventEnvelope.payload`. Beim Reduce: `EventEnvelope.payload`
|
|
// → `JSONValue.decode(_:)` → payload-Struct.
|
|
|
|
// MARK: - Event Type Names
|
|
|
|
/// Stabile Event-Type-Strings. Müssen exakt mit den `eventType`-Werten
|
|
/// auf der Web-Seite übereinstimmen — der Server reicht heute alles
|
|
/// durch, aber Cross-Client-Replay braucht Konsistenz.
|
|
enum EventType {
|
|
// article
|
|
static let articleSaved = "ArticleSaved"
|
|
/// Local-first deferred extraction: `ArticleSaved` darf mit leerem
|
|
/// `content` emittiert werden (z.B. ein ausgeloggt aus Safari geteilter
|
|
/// Link — `/extract/preview` braucht eine gültige Session). `ArticleEnriched`
|
|
/// füllt Titel/Inhalt/Metadaten nach, sobald die Extraktion möglich war,
|
|
/// **ohne** User-Felder (Status/Progress/Favorit/Tags/Notiz) zu berühren.
|
|
/// **Cross-Client:** Web-Reducer kennt das Event noch nicht (Follow-up) —
|
|
/// auf Web bleiben so geteilte Artikel bis dahin inhaltsleer.
|
|
static let articleEnriched = "ArticleEnriched"
|
|
static let articleStatusChanged = "ArticleStatusChanged"
|
|
static let articleProgressUpdated = "ArticleProgressUpdated"
|
|
static let articleFavoriteToggled = "ArticleFavoriteToggled"
|
|
static let articleNoteUpdated = "ArticleNoteUpdated"
|
|
static let articleTagsSet = "ArticleTagsSet"
|
|
static let articleWarningFlagged = "ArticleWarningFlagged"
|
|
static let articleWarningCleared = "ArticleWarningCleared"
|
|
/// Archivierungs-Ergebnis (`evt_article_media_archived_v1`): Bilder
|
|
/// liegen in mana-media, `imageUrl` zeigt auf die archivierte
|
|
/// media.mana.how-URL — nie Hotlinks in Events (Design-Regel, siehe
|
|
/// shared-schemas `ArticleMediaArchived`).
|
|
static let articleMediaArchived = "ArticleMediaArchived"
|
|
static let articleDeleted = "ArticleDeleted"
|
|
|
|
// highlight
|
|
static let highlightCreated = "HighlightCreated"
|
|
static let highlightNoteUpdated = "HighlightNoteUpdated"
|
|
static let highlightColorChanged = "HighlightColorChanged"
|
|
static let highlightDeleted = "HighlightDeleted"
|
|
}
|
|
|
|
// MARK: - Article payloads
|
|
|
|
/// Snapshot des Extract-Preview + URL-Metadaten. Spiegelt das `emit`-
|
|
/// Payload in `event-sync-api.ts#saveArticleViaEventSync`.
|
|
struct ArticleSavedPayload: Codable {
|
|
var articleId: String
|
|
var originalUrl: String
|
|
var urlHash: String
|
|
var title: String
|
|
var excerpt: String?
|
|
var content: String
|
|
var author: String?
|
|
var siteName: String?
|
|
var imageUrl: String?
|
|
var wordCount: Int?
|
|
var readingTimeMinutes: Int?
|
|
var publishedAt: String?
|
|
var userNote: String?
|
|
// `warning` lebt seit shared-schemas 0.1.11 in einem eigenen
|
|
// ArticleWarningFlagged-Event, nicht mehr in der ArticleSaved-
|
|
// Payload. ArticleSaved bleibt frozen (`evt_article_saved_v1`).
|
|
}
|
|
|
|
/// Reichert einen zuvor minimal gespeicherten Artikel mit dem
|
|
/// Extract-Preview an. Nur Inhalts-/Metadaten-Felder — User-Mutationen
|
|
/// bleiben unberührt (das macht den Reducer-Apply non-destruktiv).
|
|
struct ArticleEnrichedPayload: Codable {
|
|
var articleId: String
|
|
var title: String
|
|
var excerpt: String?
|
|
var content: String
|
|
var author: String?
|
|
var siteName: String?
|
|
var imageUrl: String?
|
|
var wordCount: Int?
|
|
var readingTimeMinutes: Int?
|
|
var publishedAt: String?
|
|
/// Consent-Wall-Hinweis vom Extract, falls erkannt.
|
|
var warning: String?
|
|
}
|
|
|
|
struct ArticleStatusChangedPayload: Codable {
|
|
var articleId: String
|
|
var newStatus: ArticleStatus
|
|
}
|
|
|
|
struct ArticleProgressUpdatedPayload: Codable {
|
|
var articleId: String
|
|
var newProgress: Double
|
|
}
|
|
|
|
struct ArticleFavoriteToggledPayload: Codable {
|
|
var articleId: String
|
|
var isFavorite: Bool
|
|
}
|
|
|
|
struct ArticleNoteUpdatedPayload: Codable {
|
|
var articleId: String
|
|
var newNote: String?
|
|
}
|
|
|
|
struct ArticleTagsSetPayload: Codable {
|
|
var articleId: String
|
|
var tags: [String]
|
|
}
|
|
|
|
/// Setzt eine Soft-Warnung am Artikel. Heute nur
|
|
/// `"probable_consent_wall"`. Spiegelt
|
|
/// `shared-schemas/pageta/events/article.ts#ArticleWarningFlagged`.
|
|
struct ArticleWarningFlaggedPayload: Codable {
|
|
var articleId: String
|
|
var warning: String
|
|
}
|
|
|
|
/// Entfernt eine vorher gesetzte Warnung („Hinweis ausblenden" im UI).
|
|
struct ArticleWarningClearedPayload: Codable {
|
|
var articleId: String
|
|
}
|
|
|
|
/// Archivierungs-Ergebnis für einen Artikel — Swift-Spiegel von
|
|
/// `shared-schemas/pageta/events/article.ts#ArticleMediaArchived`.
|
|
/// Non-destruktiv (`?? state`-Semantik im Reducer), damit ein
|
|
/// imageUrl-only-Archiv (Native-Pfad ohne HTML) ein früheres
|
|
/// Voll-Archiv nicht leert — und umgekehrt. Native emittiert
|
|
/// `htmlContent: nil` (der Reader ist Plain-Text); `mediaIds`
|
|
/// referenziert die mana-media-Objekte für Cleanup/DSGVO-Zuordnung.
|
|
struct ArticleMediaArchivedPayload: Codable {
|
|
var articleId: String
|
|
var htmlContent: String?
|
|
var imageUrl: String?
|
|
var mediaIds: [String]
|
|
}
|
|
|
|
struct ArticleDeletedPayload: Codable {
|
|
var articleId: String
|
|
}
|
|
|
|
// MARK: - Highlight payloads
|
|
|
|
struct HighlightCreatedPayload: Codable {
|
|
var highlightId: String
|
|
var articleId: String
|
|
var text: String
|
|
var note: String?
|
|
var color: HighlightColor
|
|
var startOffset: Int
|
|
var endOffset: Int
|
|
var contextBefore: String?
|
|
var contextAfter: String?
|
|
}
|
|
|
|
struct HighlightNoteUpdatedPayload: Codable {
|
|
var highlightId: String
|
|
var newNote: String?
|
|
}
|
|
|
|
struct HighlightColorChangedPayload: Codable {
|
|
var highlightId: String
|
|
var newColor: HighlightColor
|
|
}
|
|
|
|
struct HighlightDeletedPayload: Codable {
|
|
var highlightId: String
|
|
}
|