Skip to content
Snippets Groups Projects
ChatListViewModel.swift 4.62 KiB
Newer Older
Bruno Muniz's avatar
Bruno Muniz committed
import HUD
Ahmed Shehata's avatar
Ahmed Shehata committed
import UIKit
Bruno Muniz's avatar
Bruno Muniz committed
import Shared
import Models
import Combine
Bruno Muniz's avatar
Bruno Muniz committed
import Defaults
import Integration
import DependencyInjection

Ahmed Shehata's avatar
Ahmed Shehata committed
enum SearchSection {
    case chats
    case connections
}
Bruno Muniz's avatar
Bruno Muniz committed

Ahmed Shehata's avatar
Ahmed Shehata committed
enum SearchItem: Equatable, Hashable {
    case chat(Chat)
    case connection(Contact)
Bruno Muniz's avatar
Bruno Muniz committed
}

Ahmed Shehata's avatar
Ahmed Shehata committed
typealias RecentsSnapshot = NSDiffableDataSourceSnapshot<SectionId, Contact>
typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>

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

Ahmed Shehata's avatar
Ahmed Shehata committed
    var isOnline: AnyPublisher<Bool, Never> {
        session.isOnline
    }
Bruno Muniz's avatar
Bruno Muniz committed

Ahmed Shehata's avatar
Ahmed Shehata committed
    var chatsPublisher: AnyPublisher<[Chat], Never> {
        chatsSubject.eraseToAnyPublisher()
    }
Bruno Muniz's avatar
Bruno Muniz committed

Ahmed Shehata's avatar
Ahmed Shehata committed
    var hudPublisher: AnyPublisher<HUDStatus, Never> {
        hudSubject.eraseToAnyPublisher()
    }

    var recentsPublisher: AnyPublisher<RecentsSnapshot, Never> {
        session.contacts(.isRecent).map {
            let section = SectionId()
            var snapshot = RecentsSnapshot()
            snapshot.appendSections([section])
            snapshot.appendItems($0, toSection: section)
            return snapshot
        }.eraseToAnyPublisher()
    }

    var searchPublisher: AnyPublisher<SearchSnapshot, Never> {
        Publishers.CombineLatest3(
            session.contacts(.all),
            chatsPublisher,
            searchSubject
                .removeDuplicates()
                .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main)
                .eraseToAnyPublisher()
        )
            .map { (contacts, chats, query) in
                let connectionItems = contacts.filter {
                    let username = $0.username.lowercased().contains(query.lowercased())
                    let nickname = $0.nickname?.lowercased().contains(query.lowercased()) ?? false
                    return username || nickname
                }.map(SearchItem.connection)

                let chatItems = chats.filter {
                    switch $0 {
                    case .contact(let info):
                        let username = info.contact.username.lowercased().contains(query.lowercased())
                        let nickname = info.contact.nickname?.lowercased().contains(query.lowercased()) ?? false
                        let lastMessage = info.lastMessage?.payload.text.lowercased().contains(query.lowercased()) ?? false
                        return username || nickname || lastMessage

                    case .group(let info):
                        let name = info.group.name.lowercased().contains(query.lowercased())
                        let last = info.lastMessage?.payload.text.lowercased().contains(query.lowercased()) ?? false
                        return name || last
                    }
                }.map(SearchItem.chat)

                var snapshot = SearchSnapshot()

                if connectionItems.count > 0 {
                    snapshot.appendSections([.connections])
                    snapshot.appendItems(connectionItems, toSection: .connections)
                }

                if chatItems.count > 0 {
                    snapshot.appendSections([.chats])
                    snapshot.appendItems(chatItems, toSection: .chats)
                }

                return snapshot
            }.eraseToAnyPublisher()
    }
Bruno Muniz's avatar
Bruno Muniz committed

Ahmed Shehata's avatar
Ahmed Shehata committed
    var badgeCountPublisher: AnyPublisher<Int, Never> {
Bruno Muniz's avatar
Bruno Muniz committed
        Publishers.CombineLatest(
            session.contacts(.received),
            session.groups(.pending)
Ahmed Shehata's avatar
Ahmed Shehata committed
        )
        .map { $0.0.count + $0.1.count }
Bruno Muniz's avatar
Bruno Muniz committed
        .eraseToAnyPublisher()
    }

Ahmed Shehata's avatar
Ahmed Shehata committed
    private var cancellables = Set<AnyCancellable>()
    private let searchSubject = CurrentValueSubject<String, Never>("")
    private let chatsSubject = CurrentValueSubject<[Chat], Never>([])
    private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
Bruno Muniz's avatar
Bruno Muniz committed

    init() {
Ahmed Shehata's avatar
Ahmed Shehata committed
        Publishers.CombineLatest(
Bruno Muniz's avatar
Bruno Muniz committed
            session.singleChats(.all),
Ahmed Shehata's avatar
Ahmed Shehata committed
            session.groupChats(.accepted)
        ).map {
            let groups = $0.1.map(Chat.group)
            let chats = $0.0.map(Chat.contact)
            return (chats + groups).sorted { $0.orderingDate > $1.orderingDate }
        }
        .sink { [unowned self] in chatsSubject.send($0) }
Bruno Muniz's avatar
Bruno Muniz committed
        .store(in: &cancellables)
    }

Ahmed Shehata's avatar
Ahmed Shehata committed
    func updateSearch(query: String) {
        searchSubject.send(query)
Ahmed Shehata's avatar
Ahmed Shehata committed
    func leave(_ group: Group) {
        hudSubject.send(.on(nil))
Bruno Muniz's avatar
Bruno Muniz committed

        do {
            try session.leave(group: group)
Ahmed Shehata's avatar
Ahmed Shehata committed
            session.deleteAll(from: group)
            hudSubject.send(.none)
Bruno Muniz's avatar
Bruno Muniz committed
        } catch {
Ahmed Shehata's avatar
Ahmed Shehata committed
            hudSubject.send(.error(.init(with: error)))
Ahmed Shehata's avatar
Ahmed Shehata committed
    func clear(_ contact: Contact) {
        session.deleteAll(from: contact)
Bruno Muniz's avatar
Bruno Muniz committed
    }
}