HuggingChat-Mac/Models/MenuViewModel.swift (87 lines of code) (raw):

// // MenuViewModel.swift // HuggingChat-Mac // // Created by Cyril Zakka on 12/17/24. // import SwiftUI import Combine enum GroupedConversationType { case section, conversation(Conversation) } struct GroupedConversation: Identifiable { var id: String { switch type { case .section: return "section-\(title)" case .conversation(let conversation): return "conv-\(conversation.id)" } } let title: String let type: GroupedConversationType } @Observable final class MenuViewModel { var conversations: [String: [Conversation]] = [:] private var cancellables = [AnyCancellable]() var currentConversationId: String = "" func refreshState() { HuggingChatSession.shared.refreshLoginState() if (HuggingChatSession.shared.currentUser != nil) { self.getConversations() } else { self.conversations = [:] } if let conversation = HuggingChatSession.shared.currentConversation { self.currentConversationId = conversation } } func didSelectConversation(at indexPath: IndexPath) { // let conv = conversations[indexPath.row] // guard case let .conversation(conversation) = conv.type else { return } // currentConversationId = conversation.id // internalDelegate?.reloadData() // // DispatchQueue.main.asyncAfter(deadline: .now()) { // self.delegate?.didSelect(conversation: conversation) // } } func deleteConversation(at indexPath: IndexPath) { // guard case let .conversation(conversation) = conversations[indexPath.row].type else { return } // conversations.remove(at: indexPath.row) // internalDelegate?.reloadData() // // NetworkService.deleteConversation(id: conversation.id) // .receive(on: DispatchQueue.main) // .sink { [weak self] completion in // switch completion { // case .finished: break // case .failure(let error): // self?.delegate?.showError(error: error) // self?.getConversations() // } // } receiveValue: { _ in // // }.store(in: &cancellables) } func editConversationTitle(at indexPath: IndexPath, title: String) { // guard case let .conversation(conversation) = conversations[indexPath.row].type, !title.isEmpty else { return } // conversation.title = title // internalDelegate?.reloadData() // // NetworkService.editConversationTitle(conversation: conversation) // .receive(on: DispatchQueue.main) // .sink { [weak self] completion in // switch completion { // case .finished: break // case .failure(let error): // self?.delegate?.showError(error: error) // self?.getConversations() // } // } receiveValue: { _ in // // }.store(in: &cancellables) } func getConversations() { NetworkService.getConversations() .receive(on: DispatchQueue.main) .sink { completion in switch completion { case .finished: break case .failure(let error): print(error.localizedDescription) } } receiveValue: { [weak self] conversations in let conversations = MenuViewModel.groupConversationsByDates( conversations: conversations) if !conversations.isEmpty { self?.conversations = conversations } }.store(in: &cancellables) } func getConversation(withServerId id: String) -> Conversation? { for (_, conversations) in conversations { if let conversation = conversations.first(where: { $0.serverId == id }) { return conversation } } return nil } static func groupConversationsByDates(conversations: [Conversation]) -> [String: [Conversation]] { let date = Date() let calendar = Calendar.current // Get start of today let startOfToday = calendar.startOfDay(for: date) // Initialize the grouped dictionary var grouped: [String: [Conversation]] = [ "Today": [], "This Week": [], "This Month": [], "Older": [] ] for conversation in conversations { if calendar.isDate(conversation.updatedAt, inSameDayAs: date) { grouped["Today"]?.append(conversation) } else if conversation.updatedAt >= calendar.date(byAdding: .day, value: -7, to: startOfToday)! { grouped["This Week"]?.append(conversation) } else if conversation.updatedAt >= calendar.date(byAdding: .day, value: -30, to: startOfToday)! { grouped["This Month"]?.append(conversation) } else { grouped["Older"]?.append(conversation) } } // Remove empty sections return grouped.filter { !$0.value.isEmpty } } }