wip
This commit is contained in:
parent
995d627fde
commit
1e27b8643c
|
@ -1,4 +1,8 @@
|
||||||
enum XMPPAction: Codable {
|
enum XMPPAction: Codable {
|
||||||
case clientConnectionChanged(jid: String, state: ConnectionStatus)
|
case clientConnectionChanged(jid: String, state: ConnectionStatus)
|
||||||
case xmppMessageReceived(Message)
|
case xmppMessageReceived(Message)
|
||||||
|
|
||||||
|
case xmppMessageSent(Message)
|
||||||
|
case xmppMessageSendFailed(msgId: String)
|
||||||
|
case xmppMessageSendSuccess(msgId: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ extension Database {
|
||||||
table.column("oobUrl", .text)
|
table.column("oobUrl", .text)
|
||||||
table.column("date", .datetime).notNull()
|
table.column("date", .datetime).notNull()
|
||||||
table.column("pending", .boolean).notNull()
|
table.column("pending", .boolean).notNull()
|
||||||
|
table.column("sentError", .boolean).notNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,83 @@ final class DatabaseMiddleware {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .conversationAction(.sendMessage(let from, let to, let body)):
|
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:
|
default:
|
||||||
return Empty().eraseToAnyPublisher()
|
return Empty().eraseToAnyPublisher()
|
||||||
|
@ -206,9 +282,7 @@ private extension DatabaseMiddleware {
|
||||||
.fetchAll
|
.fetchAll
|
||||||
)
|
)
|
||||||
.publisher(in: database._db, scheduling: .immediate)
|
.publisher(in: database._db, scheduling: .immediate)
|
||||||
.sink { res in
|
.sink { _ in
|
||||||
print("!!!---Messages received: \(res)")
|
|
||||||
// Handle completion
|
|
||||||
} receiveValue: { messages in
|
} receiveValue: { messages in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
store.dispatch(.conversationAction(.messagesUpdated(messages: messages)))
|
store.dispatch(.conversationAction(.messagesUpdated(messages: messages)))
|
||||||
|
|
|
@ -86,13 +86,19 @@ final class XMPPMiddleware {
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
// case .conversationAction(.sendMessage(let from, let to, let body)):
|
case .xmppAction(.xmppMessageSent(let message)):
|
||||||
// return Future<AppAction, Never> { [weak self] promise in
|
return Future<AppAction, Never> { [weak self] promise in
|
||||||
// // TODO: handle errors
|
DispatchQueue.global().async {
|
||||||
// self?.service.sendMessage(from: from, to: to, body: body)
|
self?.service.sendMessage(message: message) { done in
|
||||||
// promise(.success(.empty))
|
if done {
|
||||||
// }
|
promise(.success(.xmppAction(.xmppMessageSendSuccess(msgId: message.id))))
|
||||||
// .eraseToAnyPublisher()
|
} else {
|
||||||
|
promise(.success(.xmppAction(.xmppMessageSendFailed(msgId: message.id))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Empty().eraseToAnyPublisher()
|
return Empty().eraseToAnyPublisher()
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct Message: DBStorable, Equatable {
|
||||||
|
|
||||||
let date: Date
|
let date: Date
|
||||||
let pending: Bool
|
let pending: Bool
|
||||||
|
let sentError: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Message {
|
extension Message {
|
||||||
|
@ -76,7 +77,8 @@ extension Message {
|
||||||
thread: martinMessage.thread,
|
thread: martinMessage.thread,
|
||||||
oobUrl: martinMessage.oob,
|
oobUrl: martinMessage.oob,
|
||||||
date: Date(),
|
date: Date(),
|
||||||
pending: false
|
pending: false,
|
||||||
|
sentError: false
|
||||||
)
|
)
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,16 +104,25 @@ final class XMPPService: ObservableObject {
|
||||||
clients.first { $0.connectionConfiguration.userJid.stringValue == jid }
|
clients.first { $0.connectionConfiguration.userJid.stringValue == jid }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add handler
|
func sendMessage(message: Message, completion: @escaping (Bool) -> Void) {
|
||||||
func sendMessage(from: String, to: String, body: String) {
|
guard let client = getClient(for: message.from), let to = message.to else {
|
||||||
guard let client = getClient(for: from) else { return }
|
completion(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
let message = Martin.Message()
|
let message = Martin.Message()
|
||||||
// message.from = client.connectionConfiguration.userJid
|
|
||||||
message.to = JID(to)
|
message.to = JID(to)
|
||||||
message.body = body
|
message.body = message.body
|
||||||
|
|
||||||
client.writer.write(message) { res in
|
client.module(MessageModule.self)
|
||||||
print(res)
|
.chatManager
|
||||||
|
.chat(for: client.context, with: JID(to).bareJid)?
|
||||||
|
.send(message: message) { res in
|
||||||
|
switch res {
|
||||||
|
case .success:
|
||||||
|
completion(true)
|
||||||
|
case .failure:
|
||||||
|
completion(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,20 +129,14 @@ private struct ConversationMessageRow: View {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
if isOutgoing() {
|
if isOutgoing() {
|
||||||
Spacer()
|
Spacer()
|
||||||
VStack(spacing: 0) {
|
MessageAttr(message: message)
|
||||||
MessageTime(message: message)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.trailing, 4)
|
.padding(.trailing, 4)
|
||||||
}
|
}
|
||||||
MessageContainer(message: message, isOutgoing: isOutgoing())
|
MessageContainer(message: message, isOutgoing: isOutgoing())
|
||||||
.background(isOutgoing() ? Color.Material.greenDark100 : Color.Main.white)
|
.background(isOutgoing() ? Color.Material.greenDark100 : Color.Main.white)
|
||||||
.clipShape(MessageBubble(isOutgoing: isOutgoing()))
|
.clipShape(MessageBubble(isOutgoing: isOutgoing()))
|
||||||
if !isOutgoing() {
|
if !isOutgoing() {
|
||||||
VStack(spacing: 0) {
|
MessageAttr(message: message)
|
||||||
MessageTime(message: message)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.leading, 4)
|
.padding(.leading, 4)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
@ -184,13 +178,25 @@ struct MessageBubble: Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MessageTime: View {
|
struct MessageAttr: View {
|
||||||
let message: Message
|
let message: Message
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
Text(message.date, style: .time)
|
Text(message.date, style: .time)
|
||||||
.font(.sub2)
|
.font(.sub2)
|
||||||
.foregroundColor(Color.Main.gray)
|
.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,
|
thread: nil,
|
||||||
oobUrl: nil,
|
oobUrl: nil,
|
||||||
date: Date(),
|
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: "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),
|
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),
|
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),
|
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),
|
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(
|
Message(
|
||||||
id: "7",
|
id: "7",
|
||||||
type: .chat,
|
type: .chat,
|
||||||
|
@ -244,12 +250,12 @@ struct MessageTime: View {
|
||||||
subject: nil,
|
subject: nil,
|
||||||
thread: nil,
|
thread: nil,
|
||||||
oobUrl: 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: "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),
|
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),
|
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)
|
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
|
return state
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue