diff --git a/ConversationsClassic/AppData/Store/ClientsStore.swift b/ConversationsClassic/AppData/Store/ClientsStore.swift index 5710dcd..1820e29 100644 --- a/ConversationsClassic/AppData/Store/ClientsStore.swift +++ b/ConversationsClassic/AppData/Store/ClientsStore.swift @@ -9,9 +9,11 @@ final class ClientsStore: ObservableObject { @Published private(set) var ready = false @Published private(set) var clients: [Client] = [] @Published private(set) var actualRosters: [Roster] = [] + @Published private(set) var actualChats: [Chat] = [] private var credentialsCancellable: AnyCancellable? private var rostersCancellable: AnyCancellable? + private var chatsCancellable: AnyCancellable? init() { credentialsCancellable = ValueObservation @@ -44,6 +46,8 @@ final class ClientsStore: ObservableObject { } resubscribeRosters() + resubscribeChats() + reconnectAll() } private func client(for credentials: Credentials) -> Client? { @@ -62,7 +66,7 @@ extension ClientsStore { try? await client.credentials.save() } - func reconnectAll() { + private func reconnectAll() { Task { await withTaskGroup(of: Void.self) { taskGroup in for client in clients { @@ -116,3 +120,22 @@ extension ClientsStore { try await client.deleteRoster(roster) } } + +extension ClientsStore { + private func resubscribeChats() { + let clientsJids = clients + .filter { $0.state != .disabled } + .map { $0.credentials.bareJid } + + chatsCancellable = ValueObservation.tracking { db in + try Chat + .filter(clientsJids.contains(Column("account"))) + .fetchAll(db) + } + .publisher(in: Database.shared.dbQueue) + .catch { _ in Just([]) } + .sink { [weak self] chats in + self?.actualChats = chats + } + } +} diff --git a/ConversationsClassic/Resources/Strings/Localizable.strings b/ConversationsClassic/Resources/Strings/Localizable.strings index a8dbb8d..65097e2 100644 --- a/ConversationsClassic/Resources/Strings/Localizable.strings +++ b/ConversationsClassic/Resources/Strings/Localizable.strings @@ -41,6 +41,8 @@ "Contacts.Delete.message" = "You can delete contact from this device (contact will be available on other devices), or delete it completely"; "Contacts.Delete.error" = "Contact not deleted. Server returns error."; +// MARK: Chats list screen +"ChatsList.title" = "Chats"; @@ -48,10 +50,6 @@ -// MARK: Chats screen -//"Chats.title" = "Chats"; - -//"Chat.title" = "Chat"; //"Chat.textfieldPrompt" = "Type a message"; //"Chats.Create.Main.title" = "Create"; diff --git a/ConversationsClassic/View/Main/ChatList/ChatsListScreen.swift b/ConversationsClassic/View/Main/ChatList/ChatsListScreen.swift new file mode 100644 index 0000000..ee3391a --- /dev/null +++ b/ConversationsClassic/View/Main/ChatList/ChatsListScreen.swift @@ -0,0 +1,56 @@ +import SwiftUI + +struct ChatsListScreen: View { + @EnvironmentObject var clientsStore: ClientsStore + + var body: some View { + ZStack { + // Background color + Color.Material.Background.light + .ignoresSafeArea() + + // Content + VStack(spacing: 0) { + // Header + SharedNavigationBar( + centerText: .init(text: L10n.ChatsList.title), + rightButton: .init( + image: Image(systemName: "square.and.pencil"), + action: { + // isCretePanelPresented = true + } + ) + ) + + // Chats list + if !clientsStore.actualChats.isEmpty { + List { + ForEach(clientsStore.actualChats) { chat in + ChatsRow(chat: chat) + } + } + .listStyle(.plain) + .background(Color.Material.Background.light) + } else { + Spacer() + } + } + } + // .fullScreenCover(isPresented: $isCretePanelPresented) { + // ChatsCreateMainScreen(isPresented: $isCretePanelPresented) + // } + } +} + +private struct ChatsRow: View { + // @EnvironmentObject var store: AppStore + + var chat: Chat + + var body: some View { + SharedListRow(iconType: .charCircle(chat.participant), text: chat.participant) + .onTapGesture { + // store.dispatch(.chatsAction(.startChat(accountJid: chat.account, participantJid: chat.participant))) + } + } +} diff --git a/ConversationsClassic/View/Main/MainTabScreen.swift b/ConversationsClassic/View/Main/MainTabScreen.swift index bbeae39..fc09424 100644 --- a/ConversationsClassic/View/Main/MainTabScreen.swift +++ b/ConversationsClassic/View/Main/MainTabScreen.swift @@ -3,7 +3,7 @@ import SwiftfulRouting import SwiftUI private enum Tab { - case conversations + case chats case contacts case settings } @@ -11,7 +11,7 @@ private enum Tab { struct MainTabScreen: View { @EnvironmentObject var clientsStore: ClientsStore - @State private var selectedTab: Tab = .conversations + @State private var selectedTab: Tab = .chats var body: some View { ZStack { @@ -22,9 +22,8 @@ struct MainTabScreen: View { // Content VStack(spacing: 0) { switch selectedTab { - case .conversations: - Color.red - // ConversationsScreen() + case .chats: + ChatsListScreen() case .contacts: ContactsScreen() @@ -38,9 +37,6 @@ struct MainTabScreen: View { TabBar(selectedTab: $selectedTab) } } - .onLoad { - clientsStore.reconnectAll() - } } } @@ -55,7 +51,7 @@ private struct TabBar: View { .foregroundColor(.Material.Shape.separator) HStack(spacing: 0) { TabBarButton(buttonType: .contacts, selectedTab: $selectedTab) - TabBarButton(buttonType: .conversations, selectedTab: $selectedTab) + TabBarButton(buttonType: .chats, selectedTab: $selectedTab) TabBarButton(buttonType: .settings, selectedTab: $selectedTab) } .background(Color.Material.Background.dark) @@ -93,7 +89,7 @@ private struct TabBarButton: View { case .contacts: return Image(systemName: "person.2.fill") - case .conversations: + case .chats: return Image(systemName: "bubble.left.fill") case .settings: @@ -106,7 +102,7 @@ private struct TabBarButton: View { case .contacts: return L10n.Tabs.Name.contacts - case .conversations: + case .chats: return L10n.Tabs.Name.conversations case .settings: