This commit is contained in:
fmodf 2024-08-15 17:15:49 +02:00
parent da9ba6ba0f
commit 9d8426612e
4 changed files with 80 additions and 203 deletions

View file

@ -86,6 +86,31 @@ extension Client {
} }
} }
extension Client {
func sendMessage(_ message: Message) async {
guard let to = message.to else {
return
}
guard let chat = connection.module(MessageModule.self).chatManager.chat(for: connection.context, with: BareJID(to)) else {
return
}
let msg = chat.createMessage(text: message.body ?? "??", id: message.id)
do {
try await chat.send(message: msg)
} catch {
print("Error sending message: \(error)")
}
}
// func sendMessage(_ message: String, to roster: Roster) async {
// guard let chat = chatsManager.chat(for: connection.context, with: BareJID(roster.contactBareJid)) else {
// return
// }
// let message = chat.createMessage(text: message, id: UUID().uuidString)
// try? await chat.send(message: message)
// }
}
extension Client { extension Client {
static func tryLogin(with credentials: Credentials) async throws -> Client { static func tryLogin(with credentials: Credentials) async throws -> Client {
let client = Client(credentials: credentials) let client = Client(credentials: credentials)

View file

@ -5,7 +5,8 @@ import GRDB
@MainActor @MainActor
final class ConversationStore: ObservableObject { final class ConversationStore: ObservableObject {
@Published private(set) var messages: Deque<Message> = [] @Published private(set) var messages: [Message] = []
@Published var replyText = ""
private(set) var roster: Roster private(set) var roster: Roster
private let client: Client private let client: Client
@ -17,91 +18,50 @@ final class ConversationStore: ObservableObject {
init(roster: Roster, client: Client) { init(roster: Roster, client: Client) {
self.client = client self.client = client
self.roster = roster self.roster = roster
subscribe()
} }
} }
extension ConversationStore { extension ConversationStore {
func loadMoreBackward() async { func sendMessage(_ message: String) async {
let earliestDate = messages.last?.date ?? Date() // prepare message
resubscribe(.before(earliestDate)) let message = Message(
// let fetchedMessages = await fetchBlock(earliestDate, nil) id: UUID().uuidString,
// messages.append(contentsOf: fetchedMessages) type: .chat,
// if messages.count > messagesMax { date: Date(),
// messages.removeFirst(messages.count - messagesMax) contentType: .text,
// } status: .pending,
from: roster.bareJid,
to: roster.contactBareJid,
body: message,
subject: nil,
thread: nil,
oobUrl: nil
)
// guard let lastMessage = messages.last else { return } // store as pending on db, and send
// let messages = await fetchBlock(lastMessage.date, nil) do {
// self.messages.append(contentsOf: messages) try await message.save()
} await client.sendMessage(message)
} catch {}
func loadMoreForward() async {
// guard let firstMessage = messages.first else { return }
// let messages = await fetchBlock(nil, firstMessage.date)
// self.messages.insert(contentsOf: messages, at: 0)
} }
} }
private extension ConversationStore { private extension ConversationStore {
enum FetchDirection { func subscribe() {
case before(Date)
case after(Date)
}
func resubscribe(_ side: FetchDirection) {
switch side {
case .before(let date):
messagesCancellable = ValueObservation.tracking(Message messagesCancellable = ValueObservation.tracking(Message
.filter( .filter(
(Column("to") == roster.bareJid && Column("from") == roster.contactBareJid) || (Column("to") == roster.bareJid && Column("from") == roster.contactBareJid) ||
(Column("from") == roster.bareJid && Column("to") == roster.contactBareJid) (Column("from") == roster.bareJid && Column("to") == roster.contactBareJid)
) )
.filter(Column("date") < date)
.limit(blockSize)
.order(Column("date").desc) .order(Column("date").desc)
.fetchAll .fetchAll
) )
.publisher(in: Database.shared.dbQueue, scheduling: .immediate) .publisher(in: Database.shared.dbQueue, scheduling: .immediate)
.receive(on: DispatchQueue.main)
.sink { _ in .sink { _ in
} receiveValue: { [weak self] messages in } receiveValue: { [weak self] messages in
self?.processFetched(messages, side) self?.messages = messages
}
case .after(let date):
messagesCancellable = ValueObservation.tracking(Message
.filter(
(Column("to") == roster.bareJid && Column("from") == roster.contactBareJid) ||
(Column("from") == roster.bareJid && Column("to") == roster.contactBareJid)
)
.filter(Column("date") > date)
.limit(blockSize)
.order(Column("date").desc)
.fetchAll
)
.publisher(in: Database.shared.dbQueue, scheduling: .immediate)
.sink { _ in
} receiveValue: { [weak self] messages in
self?.processFetched(messages, side)
} }
} }
} }
func processFetched(_ messages: [Message], _ side: FetchDirection) {
switch side {
case .before:
self.messages.append(contentsOf: messages)
case .after:
self.messages.insert(contentsOf: messages, at: 0)
}
Task {
await processAttachments(messages)
}
}
func processAttachments(_ messages: [Message]) async {
// load attachment here
print(messages.count)
}
}

View file

@ -5,7 +5,7 @@ import SwiftUI
struct ConversationScreen: View { struct ConversationScreen: View {
@Environment(\.router) var router @Environment(\.router) var router
@State var conversation: ConversationStore @StateObject var conversation: ConversationStore
@State private var autoScroll = true @State private var autoScroll = true
@State private var firstIsVisible = true @State private var firstIsVisible = true
@ -100,100 +100,7 @@ struct ConversationScreen: View {
.environmentObject(conversation) .environmentObject(conversation)
.safeAreaInset(edge: .bottom, spacing: 0) { .safeAreaInset(edge: .bottom, spacing: 0) {
ConversationTextInput(autoScroll: $autoScroll) ConversationTextInput(autoScroll: $autoScroll)
} .environmentObject(conversation)
.task {
await conversation.loadMoreBackward()
} }
} }
} }
// Preview
// #if DEBUG
// struct ConversationScreen_Previews: PreviewProvider {
// static var previews: some View {
// ConversationScreen()
// .environmentObject(pStore)
// }
//
// static var pStore: AppStore {
// let state = pState
// return AppStore(initialState: state, reducer: AppState.reducer, middlewares: [])
// }
//
// static var pState: AppState {
// var state = AppState()
//
// let acc = "user@test.com"
// let contact = "some@test.com"
//
// state.conversationsState.currentChat = Chat(id: "1", account: acc, participant: contact, type: .chat)
// state.conversationsState.currentMessages = [
// Message(
// id: "1",
// type: .chat,
// contentType: .text,
// from: contact,
// to: acc,
// body: "this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf ",
// subject: nil,
// thread: nil,
// oobUrl: nil,
// date: Date(),
// 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,
// 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,
// contentType: .text,
// from: acc,
// to: contact,
//
// body: "this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf ",
// subject: nil,
// thread: nil,
// oobUrl: nil,
// 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, 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)
// ]
//
// state.conversationsState.replyText = "> Some Text here! And if it a long and very long text sdfsadfsadfsafsadfsadfsadfsadfassadfsadfsafsafdsadfsafdsadfsadfas sdf sdf asdf sdfasdfsd sdfasdf sdfsdfdsasdfsdfa dsafsaf"
//
// return state
// }
// }
// #endif

View file

@ -2,6 +2,8 @@ import SwiftUI
import UIKit import UIKit
struct ConversationTextInput: View { struct ConversationTextInput: View {
@EnvironmentObject var conversation: ConversationStore
@State private var messageStr = "" @State private var messageStr = ""
@FocusState private var isFocused: Bool @FocusState private var isFocused: Bool
@Binding var autoScroll: Bool @Binding var autoScroll: Bool
@ -12,10 +14,10 @@ struct ConversationTextInput: View {
.foregroundColor(.Material.Shape.separator) .foregroundColor(.Material.Shape.separator)
.frame(height: 0.5) .frame(height: 0.5)
.padding(.bottom, 8) .padding(.bottom, 8)
if !replyText.isEmpty { if !conversation.replyText.isEmpty {
VStack(spacing: 0) { VStack(spacing: 0) {
HStack(alignment: .top) { HStack(alignment: .top) {
Text(replyText) Text(conversation.replyText)
.font(.body3) .font(.body3)
.foregroundColor(Color.Material.Text.main) .foregroundColor(Color.Material.Text.main)
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
@ -27,7 +29,7 @@ struct ConversationTextInput: View {
.foregroundColor(.Material.Elements.active) .foregroundColor(.Material.Elements.active)
.padding(.leading, 8) .padding(.leading, 8)
.tappablePadding(.symmetric(8)) { .tappablePadding(.symmetric(8)) {
// store.dispatch(.conversationAction(.setReplyText(""))) conversation.replyText = ""
} }
.padding(8) .padding(8)
} }
@ -66,45 +68,28 @@ struct ConversationTextInput: 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 } Task(priority: .userInitiated) {
// guard let contact = store.state.conversationsState.currentChat?.participant else { return } await conversation.sendMessage(composedMessage)
// store.dispatch(.conversationAction(.sendMessage( messageStr = ""
// from: acc, autoScroll = true
// to: contact, }
// body: composedMessage
// )))
// messageStr = ""
// // UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
// store.dispatch(.conversationAction(.setReplyText("")))
// autoScroll = true
} }
} }
} }
} }
.padding(.bottom, 8) .padding(.bottom, 8)
.background(Color.Material.Background.dark) .background(Color.Material.Background.dark)
// .onChange(of: store.state.conversationsState.replyText) { new in .onChange(of: conversation.replyText) { new in
// if !new.isEmpty { if !new.isEmpty {
// isFocused = true isFocused = true
// } }
// }
// .fullScreenCover(isPresented: Binding<Bool>(
// get: { store.state.sharingState.sharingShown },
// set: { _ in }
// )) {
// AttachmentPickerScreen()
// }
} }
private var replyText: String {
""
// store.state.conversationsState.replyText
} }
private var composedMessage: String { private var composedMessage: String {
var result = "" var result = ""
if !replyText.isEmpty { if !conversation.replyText.isEmpty {
result += replyText + "\n\n" result += conversation.replyText + "\n\n"
} }
result += messageStr result += messageStr
return result return result