fix MAM history loading, multi-device resource, and OMEMO self-session
Multi-device: - Add DeviceResource with a stable per-install UUID-based resource (Luma-<hex>) persisted in UserDefaults and migrated from legacy "Luma". - AccountConfiguration.effectiveResource falls back to DeviceResource.default for a blank or legacy resource; LoginView defaults resource to "" with an auto placeholder. - Prevents two devices from sharing the same full JID resource and kicking each other off the stream (the "xmpp stream error" conflict). MAM / history: - loadOlderHistory now always issues the RSM page request instead of deferring, so scrolling up reliably loads older messages. - Track interactive history mutations separately so live messages are not dropped while an older-history page is decoding. - ChatView: replace the unreliable GeometryReader/preference scroll trigger with onAppear/onDisappear on a top sentinel; auto-load the first page when the conversation is empty. - AppModel: reset hasMoreOlderHistory for empty conversations and clear isLoadingOlderHistory on disconnect. - Decode own/duplicate/notEncrypted messages to decryption-failed/plaintext placeholders instead of dropping them, so every MAM stanza is collected. OMEMO: - Remove the encrypt-to-self setup that built a Signal session with the local device (cryptographically invalid), which caused an endless "Bad MAC" loop and a stream of undecryptable messages in the self-chat. - Add LumaOMEMOStore.removeSessionWithOwnDevice() and call it on connect to purge any self-session persisted by the previous build. Tests: - Update AccountConfigurationTests for the unique resource and add coverage for legacy-resource migration and explicit-resource preservation.
This commit is contained in:
1 parent
0b4a552044
commit
9c8cff7168
9 files changed
+235
-141
No files matched your search
@@ -513,6 +513,14 @@ final class AppModel: ObservableObject {
|
||||
// schedulePersist()
|
||||
// syncWatch()
|
||||
// hasMoreOlderHistory = true
|
||||
if hasMoreOlderHistoryByConversation[normalized] == false,
|
||||
selectedMessages.isEmpty
|
||||
{
|
||||
// An empty conversation must be allowed to retry a load after a
|
||||
// previous attempt returned zero results. Otherwise it stays empty
|
||||
// with `hasMore = false` and no way to trigger loading again.
|
||||
hasMoreOlderHistoryByConversation[normalized] = true
|
||||
}
|
||||
hasMoreOlderHistory = hasMoreOlderHistoryByConversation[normalized] ?? true
|
||||
}
|
||||
|
||||
@@ -520,13 +528,13 @@ final class AppModel: ObservableObject {
|
||||
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
|
||||
// if lhs.timestamp != rhs.timestamp { return lhs.timestamp < rhs.timestamp }
|
||||
// return lhs.id < rhs.id
|
||||
// }
|
||||
// .compactMap(\.stanzaID)
|
||||
// .first
|
||||
let oldestServerID = selectedMessages
|
||||
.filter { $0.stanzaID != nil }
|
||||
.min { lhs, rhs in
|
||||
@@ -534,7 +542,7 @@ final class AppModel: ObservableObject {
|
||||
return lhs.id < rhs.id
|
||||
}?
|
||||
.stanzaID
|
||||
|
||||
|
||||
isLoadingOlderHistory = true
|
||||
let conversationID = conversation.id.lowercased()
|
||||
xmpp.loadOlderHistory(
|
||||
@@ -1763,6 +1771,7 @@ final class AppModel: ObservableObject {
|
||||
// UI from displaying a stale spinner even if the archive
|
||||
// completion event was overtaken by a socket state change.
|
||||
isArchiveSyncing = false
|
||||
isLoadingOlderHistory = false
|
||||
for index in conversations.indices where conversations[index].isGroup {
|
||||
conversations[index].isGroupJoined = false
|
||||
conversations[index].occupantCount = 0
|
||||
|
||||
Reference in new issue
Block a user