fix MAM history loading, multi-device resource, and OMEMO self-session
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

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:
wt committed 2026-08-18 02:41:52 +07:00
1 parent 0b4a552044
commit 9c8cff7168
9 files changed
+235 -141

No files matched your search

+17 -8
View File
@@ -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