This commit is contained in:
fmodf 2024-06-26 10:00:59 +02:00
parent e23a312538
commit 20c89c65e9
5 changed files with 72 additions and 12 deletions

View file

@ -1,3 +1,7 @@
enum ConversationAction: Codable { enum ConversationAction: Codable {
case makeConversationActive(chat: Chat, roster: Roster?) case makeConversationActive(chat: Chat, roster: Roster?)
case sendMessage(from: String, to: String, body: String)
case messagesUpdated(messages: [Message])
} }

View file

@ -6,6 +6,7 @@ final class DatabaseMiddleware {
static let shared = DatabaseMiddleware() static let shared = DatabaseMiddleware()
private let database = Database.shared private let database = Database.shared
private var cancellables: Set<AnyCancellable> = [] private var cancellables: Set<AnyCancellable> = []
private var conversationCancellables: Set<AnyCancellable> = []
private init() { private init() {
// Database changes // Database changes
@ -35,6 +36,7 @@ final class DatabaseMiddleware {
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> { func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
switch action { switch action {
// MARK: Accounts
case .startAction(.loadStoredAccounts): case .startAction(.loadStoredAccounts):
return Future<AppAction, Never> { promise in return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in Task(priority: .background) { [weak self] in
@ -81,6 +83,7 @@ final class DatabaseMiddleware {
} }
.eraseToAnyPublisher() .eraseToAnyPublisher()
// MARK: Rosters
case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)): case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)):
return Future<AppAction, Never> { promise in return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in Task(priority: .background) { [weak self] in
@ -125,6 +128,7 @@ final class DatabaseMiddleware {
} }
.eraseToAnyPublisher() .eraseToAnyPublisher()
// MARK: Chats
case .chatsAction(.createNewChat(let accountJid, let participantJid)): case .chatsAction(.createNewChat(let accountJid, let participantJid)):
return Future<AppAction, Never> { promise in return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in Task(priority: .background) { [weak self] in
@ -150,11 +154,60 @@ final class DatabaseMiddleware {
} }
.eraseToAnyPublisher() .eraseToAnyPublisher()
case .xmppAction(.xmppMessageReceived(let message)): // MARK: Conversation and messages
if message.type != .chat { case .conversationAction(.makeConversationActive(let chat, _)):
return Empty().eraseToAnyPublisher() return Future<AppAction, Never> { [weak self] promise in
guard let wSelf = self else {
promise(.success(.empty))
return
} }
// TODO: Store msg here! // Observe changes for current conversation
wSelf.conversationCancellables = []
ValueObservation
.tracking(
Message
.filter(
Column("from") == chat.account ||
Column("from") == chat.participant ||
Column("to") == chat.participant ||
Column("to") == chat.participant
)
.order(Column("timestamp").asc)
.fetchAll
)
.publisher(in: wSelf.database._db, scheduling: .immediate)
.sink { _ in
// Handle completion
} receiveValue: { messages in
DispatchQueue.main.async {
store.dispatch(.conversationAction(.messagesUpdated(messages: messages)))
}
}
.store(in: &wSelf.conversationCancellables)
promise(.success(.empty))
}
.eraseToAnyPublisher()
case .xmppAction(.xmppMessageReceived(let message)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.empty))
return
}
do {
try database._db.write { db in
try message.insert(db)
promise(.success(.empty))
}
} catch {
promise(.success(.databaseAction(.updateAccountFailed)))
}
}
}
.eraseToAnyPublisher()
case .conversationAction(.sendMessage(let from, let to, let body)):
return Empty().eraseToAnyPublisher() return Empty().eraseToAnyPublisher()
default: default:

View file

@ -14,7 +14,7 @@ enum MessageContentType: String, Codable, DatabaseValueConvertible {
case invite case invite
} }
struct Message: Stateable, Identifiable, DatabaseValueConvertible { struct Message: DBStorable, Equatable {
let id: String let id: String
let type: MessageType let type: MessageType
let contentType: MessageContentType let contentType: MessageContentType

View file

@ -7,6 +7,9 @@ extension ConversationState {
state.currentChat = chat state.currentChat = chat
state.currentRoster = roster state.currentRoster = roster
case .messagesUpdated(let messages):
state.currentMessages = messages
default: default:
break break
} }

View file

@ -70,13 +70,13 @@ struct ConversationScreenTextInput: View {
.padding(.trailing, 8) .padding(.trailing, 8)
.tappablePadding(.symmetric(8)) { .tappablePadding(.symmetric(8)) {
if !messageStr.isEmpty { if !messageStr.isEmpty {
// guard let acc = store.state.conversationsState.currentChat?.account else { return } guard let acc = store.state.conversationsState.currentChat?.account else { return }
// guard let contact = store.state.conversationsState.currentChat?.participant else { return } guard let contact = store.state.conversationsState.currentChat?.participant else { return }
// store.dispatch(.conversationAction(.sendMessage( store.dispatch(.conversationAction(.sendMessage(
// from: acc, from: acc,
// to: contact, to: contact,
// body: messageStr body: messageStr
// ))) )))
messageStr = "" messageStr = ""
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
} }