stop the disconnected-history alert from blocking scrolling
- Skip older-history loads silently while the connection is down: the trigger previously failed with "not connected", popped the global alert over the timeline and froze scrolling until dismissed. - Accept fast reply-swipe flicks whose end state is clearly leftward even without an intermediate horizontal lock, while scroll-owned touches still never activate the swipe. - Add a seeded-chat UI test mode (launch argument), accessibility identifiers and a LumaUITests target with timeline scroll and reply swipe tests; guard the fix in verify.sh.
This commit is contained in:
1 parent
2868a13abd
commit
5a7fc36722
10 files changed
+304
-12
No files matched your search
@@ -301,6 +301,9 @@ final class AppModel: ObservableObject {
|
||||
}
|
||||
|
||||
func bootstrap() async {
|
||||
#if DEBUG
|
||||
if applyUITestChatIfRequested() { return }
|
||||
#endif
|
||||
guard !RuntimeEnvironment.isRunningTests else { return }
|
||||
guard let saved = preferences.load() else { return }
|
||||
prepareStore(for: saved.normalizedJID)
|
||||
@@ -459,6 +462,55 @@ final class AppModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// UI-test mode: seeds an in-memory conversation with numbered messages
|
||||
/// so XCUITests can exercise timeline scrolling and the reply swipe
|
||||
/// without an XMPP server. Activated with the `-luma-ui-test-chat`
|
||||
/// launch argument.
|
||||
private func applyUITestChatIfRequested() -> Bool {
|
||||
guard ProcessInfo.processInfo.arguments.contains("-luma-ui-test-chat") else {
|
||||
return false
|
||||
}
|
||||
let account = AccountConfiguration(
|
||||
jid: "uitest@example.org",
|
||||
displayName: "UI Test",
|
||||
resource: "uitest"
|
||||
)
|
||||
self.account = account
|
||||
let conversation = Conversation(
|
||||
jid: "peer@example.org",
|
||||
displayName: "uitest-peer",
|
||||
lastMessage: "Тестовое сообщение номер 60",
|
||||
lastActivity: Date(),
|
||||
kind: .direct
|
||||
)
|
||||
modelContext.insert(conversation)
|
||||
conversations = [conversation]
|
||||
let base = Date(timeIntervalSince1970: 1_700_000_000)
|
||||
var seeded: [ChatMessage] = []
|
||||
for index in 1...60 {
|
||||
let outgoing = index.isMultiple(of: 2)
|
||||
let message = ChatMessage(
|
||||
id: "uitest-msg-\(index)",
|
||||
conversationID: conversation.jid,
|
||||
senderJID: outgoing ? account.normalizedJID : conversation.jid,
|
||||
body: "Тестовое сообщение номер \(index)",
|
||||
timestamp: base.addingTimeInterval(TimeInterval(index * 60)),
|
||||
direction: outgoing ? .outgoing : .incoming,
|
||||
delivery: .sent,
|
||||
security: .plaintext,
|
||||
kind: .text
|
||||
)
|
||||
modelContext.insert(message)
|
||||
seeded.append(message)
|
||||
}
|
||||
messages = seeded
|
||||
rebuildMessageIndex()
|
||||
selectedConversationID = conversation.jid
|
||||
return true
|
||||
}
|
||||
#endif
|
||||
|
||||
func setApplicationActive(_ active: Bool) {
|
||||
appIsActive = active
|
||||
guard !RuntimeEnvironment.isRunningTests else { return }
|
||||
@@ -654,8 +706,13 @@ final class AppModel: ObservableObject {
|
||||
}
|
||||
|
||||
func loadOlderHistoryForSelectedConversation() {
|
||||
// While disconnected the request would only fail with
|
||||
// "not connected", which used to pop the global alert right over
|
||||
// the timeline and blocked scrolling. Skip silently instead: the
|
||||
// trigger re-fires once the connection is back.
|
||||
guard !isLoadingOlderHistory,
|
||||
hasMoreOlderHistory,
|
||||
connectionStatus == .connected,
|
||||
let conversation = selectedConversation else { return }
|
||||
// let oldestServerID = selectedMessages
|
||||
// .sorted { lhs, rhs in
|
||||
|
||||
@@ -14,10 +14,13 @@ struct MessageReplySwipePolicy {
|
||||
static let followDominance: CGFloat = 1.05
|
||||
|
||||
/// Whether this translation can lock the gesture as a horizontal swipe.
|
||||
/// Only right-to-left (swipe left, negative width) is accepted; the
|
||||
/// opposite direction is deliberately left to the scroll view.
|
||||
/// Only right-to-left (swipe left, negative width) is accepted, and the
|
||||
/// lock is only available early: once the finger has travelled mostly
|
||||
/// vertically the scroll view owns the touch and the swipe must never
|
||||
/// activate for the rest of that gesture.
|
||||
static func canLock(_ translation: CGSize) -> Bool {
|
||||
translation.width < 0
|
||||
&& abs(translation.height) <= 24
|
||||
&& isHorizontal(translation, dominance: lockDominance)
|
||||
&& abs(translation.width) >= activationDistance * 0.5
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user