Skip to content
Snippets Groups Projects
GroupChatViewModel.swift 3.92 KiB
Newer Older
Bruno Muniz's avatar
Bruno Muniz committed
import UIKit
import Models
import Combine
import XXModels
Bruno Muniz's avatar
Bruno Muniz committed
import Foundation
import Integration
import DifferenceKit
import DependencyInjection

Ahmed Shehata's avatar
Ahmed Shehata committed
enum GroupChatNavigationRoutes: Equatable {
    case waitingRound
    case webview(String)
}

Bruno Muniz's avatar
Bruno Muniz committed
final class GroupChatViewModel {
    @Dependency private var session: SessionType

    var replyPublisher: AnyPublisher<(String, String), Never> {
Ahmed Shehata's avatar
Ahmed Shehata committed
        replySubject.eraseToAnyPublisher()
    }

    var routesPublisher: AnyPublisher<GroupChatNavigationRoutes, Never> {
        routesSubject.eraseToAnyPublisher()
    }

    let info: GroupInfo
Bruno Muniz's avatar
Bruno Muniz committed
    private var stagedReply: Reply?
    private var cancellables = Set<AnyCancellable>()
    private let replySubject = PassthroughSubject<(String, String), Never>()
Ahmed Shehata's avatar
Ahmed Shehata committed
    private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>()
Bruno Muniz's avatar
Bruno Muniz committed

    var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> {
Bruno Muniz's avatar
Bruno Muniz committed
        session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id), isSenderBanned: false))
            .assertNoFailure()
            .map { messages -> [ArraySection<ChatSection, Message>] in
                let groupedByDate = Dictionary(grouping: messages) { domainModel -> Date in
Bruno Muniz's avatar
Bruno Muniz committed
                    let components = Calendar.current.dateComponents([.day, .month, .year], from: domainModel.date)
                    return Calendar.current.date(from: components)!
                }

                return groupedByDate
                    .map { .init(model: ChatSection(date: $0.key), elements: $0.value) }
                    .sorted(by: { $0.model.date < $1.model.date })
            }
            .map { sections -> [ArraySection<ChatSection, Message>] in
            var snapshot = [ArraySection<ChatSection, Message>]()
Bruno Muniz's avatar
Bruno Muniz committed
            sections.forEach { snapshot.append(.init(model: $0.model, elements: $0.elements)) }
            return snapshot
        }.eraseToAnyPublisher()
    }

    init(_ info: GroupInfo) {
Bruno Muniz's avatar
Bruno Muniz committed
        self.info = info
    }

    func readAll() {
        let assignment = Message.Assignments(isUnread: false)
        let query = Message.Query(chat: .group(info.group.id))
        _ = try? session.dbManager.bulkUpdateMessages(query, assignment)
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func didRequestDelete(_ messages: [Message]) {
        _ = try? session.dbManager.deleteMessages(.init(id: Set(messages.map(\.id))))
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func send(_ text: String) {
        session.send(.init(
            text: text.trimmingCharacters(in: .whitespacesAndNewlines),
            reply: stagedReply
Bruno Muniz's avatar
Bruno Muniz committed
        ), toGroup: info.group)
        stagedReply = nil
    }

    func retry(_ message: Message) {
        guard let id = message.id else { return }
        session.retryMessage(id)
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func showRoundFrom(_ roundURL: String?) {
Ahmed Shehata's avatar
Ahmed Shehata committed
        if let urlString = roundURL, !urlString.isEmpty {
            routesSubject.send(.webview(urlString))
        } else {
            routesSubject.send(.waitingRound)
        }
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func abortReply() {
        stagedReply = nil
    }

    func getReplyContent(for messageId: Data) -> (String, String) {
        guard let message = try? session.dbManager.fetchMessages(.init(networkId: messageId)).first else {
            return ("[DELETED]", "[DELETED]")
Bruno Muniz's avatar
Bruno Muniz committed
        return (getName(from: message.senderId), message.text)
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func getName(from senderId: Data) -> String {
Bruno Muniz's avatar
Bruno Muniz committed
        guard senderId != session.myId else { return "You" }

        guard let contact = try? session.dbManager.fetchContacts(.init(id: [senderId])).first else {
Bruno Muniz's avatar
Bruno Muniz committed
            return "[DELETED]"
        var name = (contact.nickname ?? contact.username) ?? "Fetching username..."

        if contact.isBlocked {
            name = "\(name) (Blocked)"
        }

        return name
    func didRequestReply(_ message: Message) {
        guard let networkId = message.networkId else { return }
        stagedReply = Reply(messageId: networkId, senderId: message.senderId)
Bruno Muniz's avatar
Bruno Muniz committed
        replySubject.send(getReplyContent(for: networkId))
Bruno Muniz's avatar
Bruno Muniz committed
}