0011 0012 patch fixes
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

This commit is contained in:
wt committed 2026-08-11 22:49:52 +07:00
1 parent 65605ded95
commit 57eaf56023
3 files changed
+128 -16

No files matched your search

+23 -6
View File
@@ -96,6 +96,7 @@ final class AppModel: ObservableObject {
private var lastSuccessfulMAMSync: Date?
private var lastSuccessfulMAMCursor: String?
private var mamCheckpoints: [MAMArchiveKey: MAMArchiveCheckpoint] = [:]
private var hasMoreOlderHistoryByConversation: [String: Bool] = [:]
private var pendingRoomPasswords: [String: String] = [:]
private var joiningRoomJIDs: Set<String> = []
private var mediaSendActivity = MediaSendActivityTracker()
@@ -510,22 +511,31 @@ final class AppModel: ObservableObject {
// }
// schedulePersist()
// syncWatch()
hasMoreOlderHistory = true
// hasMoreOlderHistory = true
hasMoreOlderHistory = hasMoreOlderHistoryByConversation[normalized] ?? true
}
func loadOlderHistoryForSelectedConversation() {
guard !isLoadingOlderHistory,
hasMoreOlderHistory,
let conversation = selectedConversation else { return }
// let oldestServerID = selectedMessages
// .sorted { lhs, rhs in
// if lhs.timestamp != rhs.timestamp { return lhs.timestamp < rhs.timestamp }
// return lhs.id < rhs.id
// }
// .compactMap(\.stanzaID)
// .first
let oldestServerID = selectedMessages
.sorted { lhs, rhs in
.filter { $0.stanzaID != nil }
.min { lhs, rhs in
if lhs.timestamp != rhs.timestamp { return lhs.timestamp < rhs.timestamp }
return lhs.id < rhs.id
}
.compactMap(\.stanzaID)
.first
}?
.stanzaID
isLoadingOlderHistory = true
let conversationID = conversation.id.lowercased()
xmpp.loadOlderHistory(
conversationJID: conversation.jid,
isGroup: conversation.isGroup,
@@ -535,7 +545,11 @@ final class AppModel: ObservableObject {
self.isLoadingOlderHistory = false
switch result {
case .success(let hasMore):
self.hasMoreOlderHistory = hasMore
// self.hasMoreOlderHistory = hasMore
self.hasMoreOlderHistoryByConversation[conversationID] = hasMore
if self.selectedConversationID == conversationID {
self.hasMoreOlderHistory = hasMore
}
case .failure(let error):
self.errorMessage = error.localizedDescription
}
@@ -1926,6 +1940,9 @@ final class AppModel: ObservableObject {
}
private func consumeMessage(_ envelope: XMPPService.MessageEnvelope) {
if !envelope.isGroupMessage {
upsertConversation(jid: envelope.peerJID, name: nil)
}
if let stanzaID = envelope.stanzaID,
messageIndex(referenceID: stanzaID, conversationID: envelope.peerJID) != nil
{
+44 -4
View File
@@ -45,6 +45,7 @@ struct ChatView: View {
@State private var hasCompletedInitialScroll = false
@State private var isNearTimelineBottom = true
@State private var historyLoadAnchorID: String?
@State private var historyTopTriggerArmed = true
@State private var activeCaptureMode: ComposerCaptureMode?
@State private var preparingCaptureMode: ComposerCaptureMode?
@State private var captureGestureIsActive = false
@@ -239,6 +240,7 @@ struct ChatView: View {
hasCompletedInitialScroll = false
isNearTimelineBottom = true
historyLoadAnchorID = nil
historyTopTriggerArmed = true
}
.onDisappear {
model.endComposerActivity(in: liveConversation)
@@ -332,10 +334,30 @@ struct ChatView: View {
Color.clear
.frame(height: 1)
.accessibilityHidden(true)
.onAppear {
guard !model.isLoadingOlderHistory else { return }
historyLoadAnchorID = timelineEntries.first?.id
model.loadOlderHistoryForSelectedConversation()
// .onAppear {
//// guard !model.isLoadingOlderHistory else { return }
// guard historyTopTriggerArmed,
// !model.isLoadingOlderHistory
// else {
// return
// }
//
// historyTopTriggerArmed = false
// historyLoadAnchorID = timelineEntries.first?.id
// model.loadOlderHistoryForSelectedConversation()
// }
.background {
GeometryReader { geometry in
Color.clear.preference(
key: TimelineTopYPreferenceKey.self,
value: geometry.frame(
in: .named(Self.timelineCoordinateSpace)
).minY
)
}
}
.onDisappear {
historyTopTriggerArmed = true
}
if model.isLoadingOlderHistory {
ProgressView()
@@ -419,6 +441,16 @@ struct ChatView: View {
)
}
#endif
.onPreferenceChange(TimelineTopYPreferenceKey.self) { topY in
guard hasCompletedInitialScroll,
!timelineEntries.isEmpty,
model.hasMoreOlderHistory,
!model.isLoadingOlderHistory,
topY <= 24
else { return }
historyLoadAnchorID = timelineEntries.first?.id
model.loadOlderHistoryForSelectedConversation()
}
.task(id: timelineEntries.last?.id) {
await performInitialScrollAfterLayout(using: proxy)
}
@@ -1576,6 +1608,14 @@ private struct TimelineBottomYPreferenceKey: PreferenceKey {
}
}
private struct TimelineTopYPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = .greatestFiniteMagnitude
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
#if os(iOS)
/// Reads the real UIScrollView offset without replacing its delegate or
/// installing another pan recognizer. SwiftUI geometry preferences can stop
+61 -6
View File
@@ -296,6 +296,13 @@ final class XMPPService {
private var mucCatchupLastCursor: String?
private var mucCursorFallbackArchives: Set<MAMArchiveKey> = []
private var delayedLiveByArchive: [MAMArchiveKey: [BufferedLiveDelivery]] = [:]
private struct PendingOlderHistoryRequest {
let conversationJID: String
let isGroup: Bool
let before: String?
let completion: (Result<Bool, Error>) -> Void
}
private var pendingOlderHistoryRequest: PendingOlderHistoryRequest?
private let archiveStanzaInbox = ArchiveStanzaInbox(
maximumCount: ArchiveMessageBatchPolicy.maximumBufferedStanzas
)
@@ -825,12 +832,38 @@ final class XMPPService {
completion(.failure(LumaXMPPError.notConnected))
return
}
guard !archiveSyncStarted, activeMUCCatchup == nil else {
completion(.success(false))
// guard !archiveSyncStarted, activeMUCCatchup == nil else {
// completion(.success(false))
if archiveSyncStarted || activeMUCCatchup != nil {
pendingOlderHistoryRequest = PendingOlderHistoryRequest(
conversationJID: conversationJID,
isGroup: isGroup,
before: before,
completion: completion
)
return
}
performOlderHistoryRequest(
conversationJID: conversationJID,
isGroup: isGroup,
before: before,
completion: completion
)
}
private func performOlderHistoryRequest(
conversationJID: String,
isGroup: Bool,
before: String?,
completion: @escaping (Result<Bool, Error>) -> Void
) {
guard let client, client.state == .connected() else {
completion(.failure(LumaXMPPError.notConnected))
return
}
guard let version = client.module(.mam).availableVersions.first else {
completion(.success(false))
// completion(.success(false))
completion(.failure(LumaXMPPError.connection("MAM недоступен")))
return
}
@@ -893,7 +926,8 @@ final class XMPPService {
message: stanza.message,
timestamp: stanza.timestamp,
archiveID: stanza.archiveID,
isArchived: true
isArchived: true,
archivedPeerJID: isGroup ? nil : BareJID(conversationJID.lowercased())
)
}
}
@@ -911,6 +945,20 @@ final class XMPPService {
}
}
}
private func startPendingOlderHistoryIfPossible() {
guard !archiveSyncStarted,
activeMUCCatchup == nil,
let pending = pendingOlderHistoryRequest
else { return }
pendingOlderHistoryRequest = nil
performOlderHistoryRequest(
conversationJID: pending.conversationJID,
isGroup: pending.isGroup,
before: pending.before,
completion: pending.completion
)
}
func sendRetraction(
to recipient: String,
@@ -1567,7 +1615,8 @@ final class XMPPService {
message: Message,
timestamp: Date,
archiveID: String?,
isArchived: Bool = false
isArchived: Bool = false,
archivedPeerJID: BareJID? = nil
) {
guard message.type != .error,
message.type != .groupchat,
@@ -1578,8 +1627,12 @@ final class XMPPService {
let ownJID = client.userBareJid
let outgoing = sender == ownJID
// guard let peer = outgoing ? message.to?.bareJid : message.from?.bareJid else { return }
// let peer: BareJID?
// if outgoing {
let peer: BareJID?
if outgoing {
if isArchived, let archivedPeerJID {
peer = archivedPeerJID
} else if outgoing {
peer = message.to?.bareJid ?? message.from?.bareJid
} else {
peer = message.from?.bareJid
@@ -2707,6 +2760,7 @@ final class XMPPService {
mucCatchupLastCursor = nil
replayDelayedLive(for: archive)
startNextMUCCatchupIfPossible()
startPendingOlderHistoryIfPossible()
}
guard succeeded else { return }
let checkpoint = MAMArchiveCheckpoint(
@@ -3181,6 +3235,7 @@ final class XMPPService {
}
setArchiveSyncIndicator(false)
archiveSyncStarted = false
startPendingOlderHistoryIfPossible()
archiveIsBootstrapQuery = false
archiveHighWatermark = nil
archiveHasCompletedPage = false