wip
This commit is contained in:
parent
c47b6e9c21
commit
05c23f8fd9
|
@ -0,0 +1,77 @@
|
||||||
|
import Combine
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
final class ClientMartinMessagesManager {
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
|
init(_ xmppConnection: XMPPClient) {
|
||||||
|
cancellable = xmppConnection.module(MessageModule.self).messagesPublisher
|
||||||
|
.sink { [weak self] message in
|
||||||
|
self?.handleClientMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleClientMessage(_ martinMessage: MessageModule.MessageReceived) {
|
||||||
|
#if DEBUG
|
||||||
|
print("---")
|
||||||
|
print("Message received: \(martinMessage)")
|
||||||
|
print("---")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// extension Message {
|
||||||
|
// static func map(from martinMessage: Martin.Message) -> Message? {
|
||||||
|
// #if DEBUG
|
||||||
|
// print("---")
|
||||||
|
// print("Message received: \(martinMessage)")
|
||||||
|
// print("---")
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// // Check that the message type is supported
|
||||||
|
// let chatTypes: [StanzaType] = [.chat, .groupchat]
|
||||||
|
// guard let mType = martinMessage.type, chatTypes.contains(mType) else {
|
||||||
|
// #if DEBUG
|
||||||
|
// print("Unsupported message type: \(martinMessage.type?.rawValue ?? "nil")")
|
||||||
|
// #endif
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Type
|
||||||
|
// let type = MessageType(rawValue: martinMessage.type?.rawValue ?? "") ?? .chat
|
||||||
|
//
|
||||||
|
// // Content type
|
||||||
|
// var contentType: MessageContentType = .text
|
||||||
|
// if martinMessage.hints.contains(.noStore) {
|
||||||
|
// contentType = .typing
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // From/To
|
||||||
|
// let from = martinMessage.from?.bareJid.stringValue ?? ""
|
||||||
|
// let to = martinMessage.to?.bareJid.stringValue
|
||||||
|
//
|
||||||
|
// // Extract date or set current
|
||||||
|
// var date = Date()
|
||||||
|
// if let timestampStr = martinMessage.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) {
|
||||||
|
// date = Date(timeIntervalSince1970: timeInterval)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Msg
|
||||||
|
// let msg = Message(
|
||||||
|
// id: martinMessage.id ?? UUID().uuidString,
|
||||||
|
// type: type,
|
||||||
|
// date: date,
|
||||||
|
// contentType: contentType,
|
||||||
|
// status: .sent,
|
||||||
|
// from: from,
|
||||||
|
// to: to,
|
||||||
|
// body: martinMessage.body,
|
||||||
|
// subject: martinMessage.subject,
|
||||||
|
// thread: martinMessage.thread,
|
||||||
|
// oobUrl: martinMessage.oob
|
||||||
|
// )
|
||||||
|
// return msg
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -24,11 +24,13 @@ final class Client: ObservableObject {
|
||||||
|
|
||||||
private var rosterManager = ClientMartinRosterManager()
|
private var rosterManager = ClientMartinRosterManager()
|
||||||
private var chatsManager = ClientMartinChatsManager()
|
private var chatsManager = ClientMartinChatsManager()
|
||||||
|
private var messageManager: ClientMartinMessagesManager
|
||||||
|
|
||||||
init(credentials: Credentials) {
|
init(credentials: Credentials) {
|
||||||
self.credentials = credentials
|
self.credentials = credentials
|
||||||
state = credentials.isActive ? .enabled(.disconnected) : .disabled
|
state = credentials.isActive ? .enabled(.disconnected) : .disabled
|
||||||
connection = Self.prepareConnection(credentials, rosterManager, chatsManager)
|
connection = Self.prepareConnection(credentials, rosterManager, chatsManager)
|
||||||
|
messageManager = ClientMartinMessagesManager(connection)
|
||||||
connectionCancellable = connection.$state
|
connectionCancellable = connection.$state
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
|
@ -17,3 +17,18 @@ struct Chat: DBStorable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Chat: Equatable {}
|
extension Chat: Equatable {}
|
||||||
|
|
||||||
|
extension Chat {
|
||||||
|
func fetchRoster() async throws -> Roster {
|
||||||
|
try await Database.shared.dbQueue.read { db in
|
||||||
|
guard
|
||||||
|
let roster = try Roster
|
||||||
|
.filter(Column("bareJid") == account && Column("contactBareJid") == participant)
|
||||||
|
.fetchOne(db)
|
||||||
|
else {
|
||||||
|
throw ClientStoreError.rosterNotFound
|
||||||
|
}
|
||||||
|
return roster
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,57 +36,3 @@ struct Message: DBStorable, Equatable {
|
||||||
let thread: String?
|
let thread: String?
|
||||||
let oobUrl: String?
|
let oobUrl: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Message {
|
|
||||||
static func map(from martinMessage: Martin.Message) -> Message? {
|
|
||||||
#if DEBUG
|
|
||||||
print("---")
|
|
||||||
print("Message received: \(martinMessage)")
|
|
||||||
print("---")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check that the message type is supported
|
|
||||||
let chatTypes: [StanzaType] = [.chat, .groupchat]
|
|
||||||
guard let mType = martinMessage.type, chatTypes.contains(mType) else {
|
|
||||||
#if DEBUG
|
|
||||||
print("Unsupported message type: \(martinMessage.type?.rawValue ?? "nil")")
|
|
||||||
#endif
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type
|
|
||||||
let type = MessageType(rawValue: martinMessage.type?.rawValue ?? "") ?? .chat
|
|
||||||
|
|
||||||
// Content type
|
|
||||||
var contentType: MessageContentType = .text
|
|
||||||
if martinMessage.hints.contains(.noStore) {
|
|
||||||
contentType = .typing
|
|
||||||
}
|
|
||||||
|
|
||||||
// From/To
|
|
||||||
let from = martinMessage.from?.bareJid.stringValue ?? ""
|
|
||||||
let to = martinMessage.to?.bareJid.stringValue
|
|
||||||
|
|
||||||
// Extract date or set current
|
|
||||||
var date = Date()
|
|
||||||
if let timestampStr = martinMessage.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) {
|
|
||||||
date = Date(timeIntervalSince1970: timeInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Msg
|
|
||||||
let msg = Message(
|
|
||||||
id: martinMessage.id ?? UUID().uuidString,
|
|
||||||
type: type,
|
|
||||||
date: date,
|
|
||||||
contentType: contentType,
|
|
||||||
status: .sent,
|
|
||||||
from: from,
|
|
||||||
to: to,
|
|
||||||
body: martinMessage.body,
|
|
||||||
subject: martinMessage.subject,
|
|
||||||
thread: martinMessage.thread,
|
|
||||||
oobUrl: martinMessage.oob
|
|
||||||
)
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
enum ClientStoreError: Error {
|
enum ClientStoreError: Error {
|
||||||
case clientNotFound
|
case clientNotFound
|
||||||
|
case rosterNotFound
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,4 +152,17 @@ extension ClientsStore {
|
||||||
|
|
||||||
return ConversationStore(roster: roster, client: client)
|
return ConversationStore(roster: roster, client: client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func conversationStore(for chat: Chat) async throws -> ConversationStore {
|
||||||
|
while !ready {
|
||||||
|
await Task.yield()
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let client = clients.first(where: { $0.credentials.bareJid == chat.account }) else {
|
||||||
|
throw ClientStoreError.clientNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
let roster = try await chat.fetchRoster()
|
||||||
|
return ConversationStore(roster: roster, client: client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
|
|
||||||
// MARK: Chats list screen
|
// MARK: Chats list screen
|
||||||
"ChatsList.title" = "Chats";
|
"ChatsList.title" = "Chats";
|
||||||
|
"Chats.Create.Main.title" = "Create";
|
||||||
|
|
||||||
// MARK: Conversation
|
// MARK: Conversation
|
||||||
"Conversation.title" = "Conversation";
|
"Conversation.title" = "Conversation";
|
||||||
|
@ -52,9 +53,12 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//"Chat.textfieldPrompt" = "Type a message";
|
//"Chat.textfieldPrompt" = "Type a message";
|
||||||
|
|
||||||
//"Chats.Create.Main.title" = "Create";
|
|
||||||
//"Chats.Create.Main.createGroup" = "Create public group";
|
//"Chats.Create.Main.createGroup" = "Create public group";
|
||||||
//"Chats.Create.Main.createPrivateGroup" = "Create private group";
|
//"Chats.Create.Main.createPrivateGroup" = "Create private group";
|
||||||
//"Chats.Create.Main.findGroup" = "Find public group";
|
//"Chats.Create.Main.findGroup" = "Find public group";
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ChatsCreateScreenMain: View {
|
||||||
|
@Environment(\.router) var router
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
// Background color
|
||||||
|
Color.Material.Background.light
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
// Content
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
// Header
|
||||||
|
SharedNavigationBar(
|
||||||
|
leftButton: .init(
|
||||||
|
image: Image(systemName: "xmark"),
|
||||||
|
action: {
|
||||||
|
router.dismissScreen()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
centerText: .init(text: L10n.Chats.Create.Main.title)
|
||||||
|
)
|
||||||
|
|
||||||
|
// List
|
||||||
|
List {
|
||||||
|
Text("test")
|
||||||
|
// ChatsCreateRowButton(
|
||||||
|
// title: L10n.Chats.Create.Main.createGroup,
|
||||||
|
// image: "person.2.fill",
|
||||||
|
// action: {}
|
||||||
|
// )
|
||||||
|
// ChatsCreateRowButton(
|
||||||
|
// title: L10n.Chats.Create.Main.createPrivateGroup,
|
||||||
|
// image: "person.2.fill",
|
||||||
|
// action: {}
|
||||||
|
// )
|
||||||
|
// ChatsCreateRowButton(
|
||||||
|
// title: L10n.Chats.Create.Main.findGroup,
|
||||||
|
// image: "magnifyingglass",
|
||||||
|
// action: {}
|
||||||
|
// )
|
||||||
|
|
||||||
|
// for contacts list
|
||||||
|
// let rosters = store.state.rostersState.rosters.filter { !$0.locallyDeleted }
|
||||||
|
// if rosters.isEmpty {
|
||||||
|
// ChatsCreateRowSeparator()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
.listStyle(.plain)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private struct ChatsCreateRowButton: View {
|
||||||
|
// var title: String
|
||||||
|
// var image: String
|
||||||
|
// var action: () -> Void
|
||||||
|
//
|
||||||
|
// var body: some View {
|
||||||
|
// VStack(alignment: .center, spacing: 0) {
|
||||||
|
// Spacer()
|
||||||
|
// HStack(alignment: .center, spacing: 16) {
|
||||||
|
// Image(systemName: image)
|
||||||
|
// .font(.head2)
|
||||||
|
// .foregroundColor(.Material.Elements.active)
|
||||||
|
// .padding(.leading, 16)
|
||||||
|
// Text(title)
|
||||||
|
// .font(.body1)
|
||||||
|
// .foregroundColor(.Material.Text.main)
|
||||||
|
// Spacer()
|
||||||
|
// }
|
||||||
|
// Spacer()
|
||||||
|
// Rectangle()
|
||||||
|
// .frame(maxWidth: .infinity)
|
||||||
|
// .frame(height: 1)
|
||||||
|
// .foregroundColor(.Material.Background.dark)
|
||||||
|
// }
|
||||||
|
// .sharedListRow()
|
||||||
|
// .frame(height: 48)
|
||||||
|
// .onTapGesture {
|
||||||
|
// action()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
private struct ChatsCreateRowSeparator: View {
|
||||||
|
var body: some View {
|
||||||
|
Text("aa")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// import SwiftUI
|
||||||
|
//
|
||||||
|
// struct CreateConversationMainScreen: View {
|
||||||
|
// @EnvironmentObject var store: AppStore
|
||||||
|
//
|
||||||
|
// var body: some View {
|
||||||
|
// ZStack {
|
||||||
|
// // Background color
|
||||||
|
// Color.Material.Background.light
|
||||||
|
// .ignoresSafeArea()
|
||||||
|
//
|
||||||
|
// // Content
|
||||||
|
// VStack(spacing: 0) {
|
||||||
|
// // Header
|
||||||
|
// CreateConversationHeader()
|
||||||
|
//
|
||||||
|
// // Chats list
|
||||||
|
// // if !store.state.chatsState.chats.isEmpty {
|
||||||
|
// // List {
|
||||||
|
// // ForEach(store.state.chatsState.chats) { chat in
|
||||||
|
// // ChatsRow(chat: chat)
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // .listStyle(.plain)
|
||||||
|
// // .background(Color.Material.Background.light)
|
||||||
|
// // } else {
|
||||||
|
// // Spacer()
|
||||||
|
// // }
|
||||||
|
// //
|
||||||
|
// // // Tab bar
|
||||||
|
// // SharedTabBar()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private struct CreateConversationHeader: View {
|
||||||
|
// @EnvironmentObject var store: AppStore
|
||||||
|
//
|
||||||
|
// var body: some View {
|
||||||
|
// ZStack {
|
||||||
|
// // bg
|
||||||
|
// Color.Material.Background.dark
|
||||||
|
// .ignoresSafeArea()
|
||||||
|
//
|
||||||
|
// HStack(spacing: 0) {
|
||||||
|
// Image(systemName: "arrow.left")
|
||||||
|
// .foregroundColor(.Material.Elements.active)
|
||||||
|
// .padding(.leading, 16)
|
||||||
|
// .tappablePadding(.symmetric(12)) {
|
||||||
|
// store.dispatch(.changeFlow(store.state.previousFlow))
|
||||||
|
// }
|
||||||
|
// Spacer()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // title
|
||||||
|
// Text("New conversation")
|
||||||
|
// .font(.head2)
|
||||||
|
// .foregroundColor(Color.Material.Text.main)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
// Preview
|
||||||
|
// #if DEBUG
|
||||||
|
// struct ChatsCreateMainScreen_Previews: PreviewProvider {
|
||||||
|
// static var previews: some View {
|
||||||
|
// ChatsCreateMainScreen(isPresented: .constant(true))
|
||||||
|
// .environmentObject(pStore)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// static var pStore: AppStore {
|
||||||
|
// let state = pState
|
||||||
|
// return AppStore(initialState: state, reducer: AppState.reducer, middlewares: [])
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// static var pState: AppState {
|
||||||
|
// var state = AppState()
|
||||||
|
//
|
||||||
|
// state.rostersState.rosters = [
|
||||||
|
// .init(contactBareJid: "test@me.com", subscription: "both", ask: true, data: .init(groups: [], annotations: []))
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
// return state
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// #endif
|
|
@ -1,6 +1,7 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChatsListScreen: View {
|
struct ChatsListScreen: View {
|
||||||
|
@Environment(\.router) var router
|
||||||
@EnvironmentObject var clientsStore: ClientsStore
|
@EnvironmentObject var clientsStore: ClientsStore
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -17,7 +18,9 @@ struct ChatsListScreen: View {
|
||||||
rightButton: .init(
|
rightButton: .init(
|
||||||
image: Image(systemName: "square.and.pencil"),
|
image: Image(systemName: "square.and.pencil"),
|
||||||
action: {
|
action: {
|
||||||
// isCretePanelPresented = true
|
router.showScreen(.fullScreenCover) { _ in
|
||||||
|
ChatsCreateScreenMain()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -36,9 +39,6 @@ struct ChatsListScreen: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .fullScreenCover(isPresented: $isCretePanelPresented) {
|
|
||||||
// ChatsCreateMainScreen(isPresented: $isCretePanelPresented)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,29 +51,30 @@ private struct ChatsRow: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
SharedListRow(iconType: .charCircle(chat.participant), text: chat.participant)
|
SharedListRow(iconType: .charCircle(chat.participant), text: chat.participant)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
// Task {
|
Task {
|
||||||
// router.showModal {
|
router.showModal {
|
||||||
// LoadingScreen()
|
LoadingScreen()
|
||||||
// }
|
}
|
||||||
// defer {
|
defer {
|
||||||
// router.dismissModal()
|
router.dismissModal()
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// do {
|
do {
|
||||||
// let conversation = try await clientsStore.conversationStore(for: roster)
|
let conversation = try await clientsStore.conversationStore(for: chat)
|
||||||
// router.showScreen(.push) { _ in
|
router.showScreen(.push) { _ in
|
||||||
// ConversationScreen(conversation: conversation)
|
ConversationScreen(conversation: conversation)
|
||||||
// }
|
.navigationBarHidden(true)
|
||||||
// } catch {
|
}
|
||||||
// router.showAlert(
|
} catch {
|
||||||
// .alert,
|
router.showAlert(
|
||||||
// title: L10n.Global.Error.title,
|
.alert,
|
||||||
// subtitle: L10n.Conversation.startError
|
title: L10n.Global.Error.title,
|
||||||
// ) {
|
subtitle: L10n.Conversation.startError
|
||||||
// Button(L10n.Global.ok, role: .cancel) {}
|
) {
|
||||||
// }
|
Button(L10n.Global.ok, role: .cancel) {}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue