This commit is contained in:
fmodf 2024-06-26 12:26:04 +02:00
parent 995d627fde
commit 1e27b8643c
7 changed files with 149 additions and 47 deletions

View file

@ -1,4 +1,8 @@
enum XMPPAction: Codable {
case clientConnectionChanged(jid: String, state: ConnectionStatus)
case xmppMessageReceived(Message)
case xmppMessageSent(Message)
case xmppMessageSendFailed(msgId: String)
case xmppMessageSendSuccess(msgId: String)
}

View file

@ -57,6 +57,7 @@ extension Database {
table.column("oobUrl", .text)
table.column("date", .datetime).notNull()
table.column("pending", .boolean).notNull()
table.column("sentError", .boolean).notNull()
}
}

View file

@ -184,7 +184,83 @@ final class DatabaseMiddleware {
.eraseToAnyPublisher()
case .conversationAction(.sendMessage(let from, let to, let body)):
return Empty().eraseToAnyPublisher()
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
return
}
do {
let message = Message(
id: UUID().uuidString,
type: .chat,
contentType: .text,
from: from,
to: to,
body: body,
subject: nil,
thread: nil,
oobUrl: nil,
date: Date(),
pending: true,
sentError: false
)
try database._db.write { db in
try message.insert(db)
}
promise(.success(.xmppAction(.xmppMessageSent(message))))
} catch {
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
}
}
}
.eraseToAnyPublisher()
case .xmppAction(.xmppMessageSendSuccess(let msgId)):
// mark message as pending false and sentError false
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
return
}
do {
_ = try database._db.write { db in
try Message
.filter(Column("id") == msgId)
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: false))
}
promise(.success(.empty))
} catch {
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
)
}
}
}
.eraseToAnyPublisher()
case .xmppAction(.xmppMessageSendFailed(let msgId)):
// mark message as pending false and sentError true
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
return
}
do {
_ = try database._db.write { db in
try Message
.filter(Column("id") == msgId)
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true))
}
promise(.success(.empty))
} catch {
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
)
}
}
}
.eraseToAnyPublisher()
default:
return Empty().eraseToAnyPublisher()
@ -206,9 +282,7 @@ private extension DatabaseMiddleware {
.fetchAll
)
.publisher(in: database._db, scheduling: .immediate)
.sink { res in
print("!!!---Messages received: \(res)")
// Handle completion
.sink { _ in
} receiveValue: { messages in
DispatchQueue.main.async {
store.dispatch(.conversationAction(.messagesUpdated(messages: messages)))

View file

@ -86,13 +86,19 @@ final class XMPPMiddleware {
}
.eraseToAnyPublisher()
// case .conversationAction(.sendMessage(let from, let to, let body)):
// return Future<AppAction, Never> { [weak self] promise in
// // TODO: handle errors
// self?.service.sendMessage(from: from, to: to, body: body)
// promise(.success(.empty))
// }
// .eraseToAnyPublisher()
case .xmppAction(.xmppMessageSent(let message)):
return Future<AppAction, Never> { [weak self] promise in
DispatchQueue.global().async {
self?.service.sendMessage(message: message) { done in
if done {
promise(.success(.xmppAction(.xmppMessageSendSuccess(msgId: message.id))))
} else {
promise(.success(.xmppAction(.xmppMessageSendFailed(msgId: message.id))))
}
}
}
}
.eraseToAnyPublisher()
default:
return Empty().eraseToAnyPublisher()

View file

@ -31,6 +31,7 @@ struct Message: DBStorable, Equatable {
let date: Date
let pending: Bool
let sentError: Bool
}
extension Message {
@ -76,7 +77,8 @@ extension Message {
thread: martinMessage.thread,
oobUrl: martinMessage.oob,
date: Date(),
pending: false
pending: false,
sentError: false
)
return msg
}

View file

@ -104,16 +104,25 @@ final class XMPPService: ObservableObject {
clients.first { $0.connectionConfiguration.userJid.stringValue == jid }
}
// TODO: add handler
func sendMessage(from: String, to: String, body: String) {
guard let client = getClient(for: from) else { return }
let message = Martin.Message()
// message.from = client.connectionConfiguration.userJid
message.to = JID(to)
message.body = body
client.writer.write(message) { res in
print(res)
func sendMessage(message: Message, completion: @escaping (Bool) -> Void) {
guard let client = getClient(for: message.from), let to = message.to else {
completion(false)
return
}
let message = Martin.Message()
message.to = JID(to)
message.body = message.body
client.module(MessageModule.self)
.chatManager
.chat(for: client.context, with: JID(to).bareJid)?
.send(message: message) { res in
switch res {
case .success:
completion(true)
case .failure:
completion(false)
}
}
}
}

View file

@ -129,21 +129,15 @@ private struct ConversationMessageRow: View {
HStack(spacing: 0) {
if isOutgoing() {
Spacer()
VStack(spacing: 0) {
MessageTime(message: message)
Spacer()
}
.padding(.trailing, 4)
MessageAttr(message: message)
.padding(.trailing, 4)
}
MessageContainer(message: message, isOutgoing: isOutgoing())
.background(isOutgoing() ? Color.Material.greenDark100 : Color.Main.white)
.clipShape(MessageBubble(isOutgoing: isOutgoing()))
if !isOutgoing() {
VStack(spacing: 0) {
MessageTime(message: message)
Spacer()
}
.padding(.leading, 4)
MessageAttr(message: message)
.padding(.leading, 4)
Spacer()
}
}
@ -184,13 +178,25 @@ struct MessageBubble: Shape {
}
}
struct MessageTime: View {
struct MessageAttr: View {
let message: Message
var body: some View {
Text(message.date, style: .time)
.font(.sub2)
.foregroundColor(Color.Main.gray)
VStack(alignment: .leading, spacing: 0) {
Text(message.date, style: .time)
.font(.sub2)
.foregroundColor(Color.Main.gray)
Spacer()
if message.sentError {
Image(systemName: "exclamationmark.circle")
.font(.body3)
.foregroundColor(Color.Tango.redLight)
} else if message.pending {
Image(systemName: "clock")
.font(.body3)
.foregroundColor(Color.Main.gray)
}
}
}
}
@ -226,13 +232,13 @@ struct MessageTime: View {
thread: nil,
oobUrl: nil,
date: Date(),
pending: false
pending: true, sentError: false
),
Message(id: "2", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for testsdfsdf sdfsdf sdfs sdf sdffsdf sdf sdf sdf sdf sdf sdff sdfffwwe ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "3", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "4", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for test sdfkjwek jwkjfh jwerf jdfhskjdhf jsdhfjhwefh sjdhfh fsdjhfh sd ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "5", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test sdfjkkeke kekkddjw;; w;edkdjfj l kjwekrjfk wef", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "6", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for testsdf dsdkkekkddn wejkjfj ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "2", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for testsdfsdf sdfsdf sdfs sdf sdffsdf sdf sdf sdf sdf sdf sdff sdfffwwe ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(id: "3", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: true),
Message(id: "4", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for test sdfkjwek jwkjfh jwerf jdfhskjdhf jsdhfjhwefh sjdhfh fsdjhfh sd ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(id: "5", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test sdfjkkeke kekkddjw;; w;edkdjfj l kjwekrjfk wef", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(id: "6", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for testsdf dsdkkekkddn wejkjfj ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(
id: "7",
type: .chat,
@ -244,12 +250,12 @@ struct MessageTime: View {
subject: nil,
thread: nil,
oobUrl: nil,
date: Date(), pending: false
date: Date(), pending: false, sentError: false
),
Message(id: "8", type: .chat, contentType: .text, from: acc, to: contact, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "9", type: .chat, contentType: .text, from: contact, to: acc, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "10", type: .chat, contentType: .text, from: acc, to: contact, body: "so test so test so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false),
Message(id: "11", type: .chat, contentType: .text, from: contact, to: acc, body: "xD", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false)
Message(id: "8", type: .chat, contentType: .text, from: acc, to: contact, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(id: "9", type: .chat, contentType: .text, from: contact, to: acc, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(id: "10", type: .chat, contentType: .text, from: acc, to: contact, body: "so test so test so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false),
Message(id: "11", type: .chat, contentType: .text, from: contact, to: acc, body: "xD", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false)
]
return state
}