mv-experiment #1
|
@ -5,19 +5,70 @@ import Martin
|
||||||
|
|
||||||
final class ClientMartinMAM {
|
final class ClientMartinMAM {
|
||||||
private var cancellables: Set<AnyCancellable> = []
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
private let messageProcessor: ArchivedMessageProcessor
|
||||||
|
|
||||||
init(_ xmppConnection: XMPPClient) {
|
init(_ xmppConnection: XMPPClient) {
|
||||||
|
messageProcessor = ArchivedMessageProcessor()
|
||||||
|
|
||||||
// subscribe to archived messages
|
// subscribe to archived messages
|
||||||
xmppConnection.module(.mam).archivedMessagesPublisher
|
xmppConnection.module(.mam).archivedMessagesPublisher
|
||||||
.sink(receiveValue: { [weak self] archived in
|
.sink(receiveValue: { [weak self] archived in
|
||||||
let message = archived.message
|
guard let self = self else { return }
|
||||||
message.attribute("archived_date", newValue: "\(archived.timestamp.timeIntervalSince1970)")
|
Task {
|
||||||
self?.handleMessage(archived)
|
await self.messageProcessor.addMessage(archived)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleMessage(_ received: Martin.MessageArchiveManagementModule.ArchivedMessageReceived) {
|
// func requestArchivedMessages(for roster: Roster, before: String? = nil, after: String? = nil, in module: MessageArchiveManagementModule) async {
|
||||||
|
// print(roster, before, after, module)
|
||||||
|
//
|
||||||
|
// // let endDate = Date()
|
||||||
|
// // let startDate = Calendar.current.date(byAdding: .day, value: -Const.mamRequestDaysLength, to: endDate) ?? Date()
|
||||||
|
// // let response = try? await module.queryItems(
|
||||||
|
// // componentJid: JID(credentials.bareJid),
|
||||||
|
// // with: JID(roster.bareJid),
|
||||||
|
// // start: startDate,
|
||||||
|
// // end: endDate,
|
||||||
|
// // queryId: UUID().uuidString
|
||||||
|
// // )
|
||||||
|
// // let query: RSM.Query = .init(before: nil, after: nil, max: nil)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func requestArchivedMessages(for roster: Roster, before: String? = nil, after: String? = nil) async {
|
||||||
|
// assert(before != nil || after != nil, "Either before or after must be provided")
|
||||||
|
// if !discoManager.features.map({ $0.xep }).contains("XEP-0313") {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// let module = connection.module(MessageArchiveManagementModule.self)
|
||||||
|
// await mamManager.requestArchivedMessages(for: roster, before: before, after: after, in: module)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
private actor ArchivedMessageProcessor {
|
||||||
|
private var messageBuffer: [Martin.MessageArchiveManagementModule.ArchivedMessageReceived] = []
|
||||||
|
private let batchSize = 20
|
||||||
|
|
||||||
|
func addMessage(_ message: Martin.MessageArchiveManagementModule.ArchivedMessageReceived) async {
|
||||||
|
messageBuffer.append(message)
|
||||||
|
if messageBuffer.count >= batchSize {
|
||||||
|
await processBatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processBatch() async {
|
||||||
|
guard !messageBuffer.isEmpty else { return }
|
||||||
|
|
||||||
|
let batch = messageBuffer.prefix(batchSize)
|
||||||
|
messageBuffer.removeFirst(min(batchSize, messageBuffer.count))
|
||||||
|
|
||||||
|
for archived in batch {
|
||||||
|
await handleMessage(archived)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleMessage(_ received: Martin.MessageArchiveManagementModule.ArchivedMessageReceived) async {
|
||||||
let message = received.message
|
let message = received.message
|
||||||
let date = received.timestamp
|
let date = received.timestamp
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -27,8 +78,16 @@ final class ClientMartinMAM {
|
||||||
print("---")
|
print("---")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Skip archived message if such message already exists in the database
|
||||||
|
if let archiveId = message.id {
|
||||||
|
try? await Database.shared.dbQueue.read { db in
|
||||||
|
if try Message.fetchOne(db, key: archiveId) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let msg = Message.map(message) {
|
if let msg = Message.map(message) {
|
||||||
Task {
|
|
||||||
do {
|
do {
|
||||||
var msg = msg
|
var msg = msg
|
||||||
msg.date = received.timestamp
|
msg.date = received.timestamp
|
||||||
|
@ -39,4 +98,41 @@ final class ClientMartinMAM {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// final class ClientMartinMAM {
|
||||||
|
// private var cancellables: Set<AnyCancellable> = []
|
||||||
|
//
|
||||||
|
// init(_ xmppConnection: XMPPClient) {
|
||||||
|
// // subscribe to archived messages
|
||||||
|
// xmppConnection.module(.mam).archivedMessagesPublisher
|
||||||
|
// .sink(receiveValue: { [weak self] archived in
|
||||||
|
// let message = archived.message
|
||||||
|
// message.attribute("archived_date", newValue: "\(archived.timestamp.timeIntervalSince1970)")
|
||||||
|
// self?.handleMessage(archived)
|
||||||
|
// })
|
||||||
|
// .store(in: &cancellables)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private func handleMessage(_ received: Martin.MessageArchiveManagementModule.ArchivedMessageReceived) {
|
||||||
|
// let message = received.message
|
||||||
|
// let date = received.timestamp
|
||||||
|
// #if DEBUG
|
||||||
|
// print("---")
|
||||||
|
// print("Archive message received: \(message)")
|
||||||
|
// print("Date: \(date)")
|
||||||
|
// print("---")
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// if let msg = Message.map(message) {
|
||||||
|
// Task {
|
||||||
|
// do {
|
||||||
|
// var msg = msg
|
||||||
|
// msg.date = received.timestamp
|
||||||
|
// try await msg.save()
|
||||||
|
// } catch {
|
||||||
|
// logIt(.error, "Error saving message: \(error)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
@ -140,18 +140,6 @@ extension Client {
|
||||||
throw URLError(.badServerResponse)
|
throw URLError(.badServerResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestArchivedMessages(for roster: Roster) async {
|
|
||||||
print(roster)
|
|
||||||
|
|
||||||
// if !discoManager.features.map({ $0.xep }).contains("XEP-0313") {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// let module = connection.module(MessageArchiveManagementModule.self)
|
|
||||||
// let endDate = Date()
|
|
||||||
// let startDate = Calendar.current.date(byAdding: .day, value: -Const.mamRequestDaysLength, to: endDate) ?? Date()
|
|
||||||
// let response = try? await module.queryItems(componentJid: JID(credentials.bareJid), with: JID(roster.bareJid), start: startDate, end: endDate, queryId: UUID().uuidString)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Client {
|
extension Client {
|
||||||
|
|
15
ConversationsClassic/UIToolkit/View+Flip.swift
Normal file
15
ConversationsClassic/UIToolkit/View+Flip.swift
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct FlipView: ViewModifier {
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.rotationEffect(.radians(Double.pi))
|
||||||
|
.scaleEffect(x: -1, y: 1, anchor: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func flip() -> some View {
|
||||||
|
modifier(FlipView())
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,10 +34,12 @@ struct ConversationScreen: View {
|
||||||
let messages = messages.messages
|
let messages = messages.messages
|
||||||
if !messages.isEmpty {
|
if !messages.isEmpty {
|
||||||
ScrollViewReader { proxy in
|
ScrollViewReader { proxy in
|
||||||
List {
|
ScrollView {
|
||||||
|
LazyVStack(spacing: 0) {
|
||||||
ForEach(messages) { message in
|
ForEach(messages) { message in
|
||||||
ConversationMessageRow(message: message)
|
ConversationMessageRow(message: message)
|
||||||
.id(message.id)
|
.id(message.id)
|
||||||
|
.flip()
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if message.id == messages.first?.id {
|
if message.id == messages.first?.id {
|
||||||
firstIsVisible = true
|
firstIsVisible = true
|
||||||
|
@ -51,13 +53,10 @@ struct ConversationScreen: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.rotationEffect(.degrees(180))
|
|
||||||
}
|
}
|
||||||
.rotationEffect(.degrees(180))
|
}
|
||||||
.listStyle(.plain)
|
.flip()
|
||||||
.background(Color.Material.Background.light)
|
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
.scrollIndicators(.hidden)
|
|
||||||
.onChange(of: autoScroll) { new in
|
.onChange(of: autoScroll) { new in
|
||||||
if new, !firstIsVisible {
|
if new, !firstIsVisible {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
|
|
Loading…
Reference in a new issue