import Foundation import Supabase /// Verwaltet die Echtzeit-Verbindung für "Gerade Jetzt". /// Neue Posts erscheinen sofort ohne Polling. @MainActor class RealtimeService: ObservableObject { @Published var newPostsCount = 0 private var channel: RealtimeChannelV2? private var onNewPost: ((Post) -> Void)? func startListening(onNewPost: @escaping (Post) -> Void) async { self.onNewPost = onNewPost guard channel == nil else { return } let ch = await supabase.channel("public:posts") // Neue Posts in Echtzeit empfangen let stream = await ch.postgresChange( InsertAction.self, schema: "public", table: "posts" ) await ch.subscribe() self.channel = ch // Stream im Hintergrund konsumieren Task { [weak self] in for await action in stream { await self?.handleInsert(action) } } } func stopListening() async { if let ch = channel { await supabase.removeChannel(ch) channel = nil } } private func handleInsert(_ action: InsertAction) { // Den neuen Post aus dem Record dekodieren guard let id = action.record["id"]?.stringValue, let content = action.record["content"]?.stringValue, let createdAt = action.record["created_at"]?.stringValue .flatMap({ ISO8601DateFormatter().date(from: $0) }), let userId = action.record["user_id"]?.stringValue, let isAnon = action.record["is_anonymous"]?.boolValue else { return } let moodString = action.record["mood"]?.stringValue let mood = moodString.flatMap(Mood.init(rawValue:)) let post = Post( id: id, author: User.anonymousPlaceholder, // Profil wird lazily nachgeladen content: content, mood: mood, createdAt: createdAt, resonanceCount: 0, hasResonated: false, commentCount: 0, isAnonymous: isAnon, nightOf: createdAt ) newPostsCount += 1 onNewPost?(post) } } // MARK: - User placeholder für Realtime (Profil wird nachgeladen) extension User { static let anonymousPlaceholder = User( id: "anonymous", username: "anonym", displayName: "anonym", bio: nil, avatarURL: nil, followerCount: 0, followingCount: 0, postCount: 0, isFollowing: false ) }