mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-04 01:48:59 +00:00
* fix(ios): resign KeyboardGestureArea input before its screen leaves the window react-native-keyboard-controller postpones resignFirstResponder by one frame for KeyboardGestureArea inputs. On iOS 26 UIKit asks the input to resign while it removes a popped screen; a frame later the input is off the window, the resign fails and the keyboard stays open. Backport of upstream kirillzyusko/react-native-keyboard-controller#1678. * fix(channel): re-read loading state after the Intro subscribes The Intro reads the post-list loading flag at render but subscribes to its change event only in an effect. A load that finishes in between is missed, and the Intro stays on its spinner instead of showing the channel intro. * fix(e2e): scroll the iOS sidebar's outer list to reach the last channel On iOS the categories list grows to its full height inside the sidebar's outer list, so only the outer list scrolls. Give it a testID and scroll it from the page object. * fix(e2e): clear the search box before each hashtag search
86 lines
4.3 KiB
Diff
86 lines
4.3 KiB
Diff
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt
|
|
index 8bffcae..cdb9a3a 100644
|
|
--- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt
|
|
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt
|
|
@@ -54,7 +54,7 @@ class KeyboardControllerModuleImpl(
|
|
view.clearFocus()
|
|
}
|
|
}
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !animated) {
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !animated && !controller.isInsetAnimationInProgress()) {
|
|
controller.startControlRequest(view) { insetsController ->
|
|
insetsController.finish(false)
|
|
|
|
diff --git a/node_modules/react-native-keyboard-controller/ios/swizzling/UIResponderSwizzle.swift b/node_modules/react-native-keyboard-controller/ios/swizzling/UIResponderSwizzle.swift
|
|
index 32ae6de..ace9817 100644
|
|
--- a/node_modules/react-native-keyboard-controller/ios/swizzling/UIResponderSwizzle.swift
|
|
+++ b/node_modules/react-native-keyboard-controller/ios/swizzling/UIResponderSwizzle.swift
|
|
@@ -9,6 +9,7 @@ import Foundation
|
|
import UIKit
|
|
|
|
private var pendingBecomeResponder: TextInput?
|
|
+private weak var pendingResignResponder: UIResponder?
|
|
private var originalResignFirstResponder: IMP?
|
|
private var originalBecomeFirstResponder: IMP?
|
|
|
|
@@ -69,9 +70,13 @@ extension UIResponder {
|
|
}
|
|
|
|
KeyboardAreaExtender.shared.hide()
|
|
+ pendingResignResponder = self
|
|
|
|
// Postpone execution of the original resignFirstResponder
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + UIUtils.nextFrame) {
|
|
+ if pendingResignResponder === self {
|
|
+ pendingResignResponder = nil
|
|
+ }
|
|
NotificationCenter.default.post(
|
|
name: .shouldIgnoreKeyboardEvents, object: nil, userInfo: ["ignore": false]
|
|
)
|
|
@@ -86,6 +91,45 @@ extension UIResponder {
|
|
|
|
let implementation = imp_implementationWithBlock(swizzledResignImplementation)
|
|
method_setImplementation(originalResignMethod, implementation)
|
|
+
|
|
+ swizzleWillMoveToWindow(originalResignSelector)
|
|
+ }
|
|
+
|
|
+ /// If an input with a postponed resign is about to leave its window (native-stack pop on iOS 26+,
|
|
+ /// unmount, etc.), then the postponed resign will fail, because input will not be attached to the
|
|
+ /// window anymore, and window will keep a stale first responder. So we resign it immediately while
|
|
+ /// input is still attached.
|
|
+ private static func swizzleWillMoveToWindow(_ resignSelector: Selector) {
|
|
+ let selector = #selector(UIView.willMove(toWindow:))
|
|
+
|
|
+ for cls in [UITextView.self, UITextField.self] as [AnyClass] {
|
|
+ guard let method = class_getInstanceMethod(cls, selector) else { continue }
|
|
+
|
|
+ // may be inherited from `UIView` - we add own method to the class in this case,
|
|
+ // so the change doesn't affect all views
|
|
+ let originalImplementation = method_getImplementation(method)
|
|
+ typealias Function = @convention(c) (AnyObject, Selector, UIWindow?) -> Void
|
|
+ let castOriginalImplementation = unsafeBitCast(originalImplementation, to: Function.self)
|
|
+
|
|
+ let swizzledImplementation: @convention(block) (UIView, UIWindow?) -> Void = { view, newWindow in
|
|
+ if newWindow !== view.window, pendingResignResponder === view {
|
|
+ pendingResignResponder = nil
|
|
+ NotificationCenter.default.post(
|
|
+ name: .shouldIgnoreKeyboardEvents, object: nil, userInfo: ["ignore": false]
|
|
+ )
|
|
+ (view as? TextInput)?.inputAccessoryView = nil
|
|
+ KeyboardAreaExtender.shared.remove()
|
|
+ _ = view.callOriginalResignFirstResponder(resignSelector)
|
|
+ }
|
|
+
|
|
+ castOriginalImplementation(view, selector, newWindow)
|
|
+ }
|
|
+
|
|
+ let implementation = imp_implementationWithBlock(swizzledImplementation)
|
|
+ if !class_addMethod(cls, selector, implementation, method_getTypeEncoding(method)) {
|
|
+ method_setImplementation(method, implementation)
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
private func callOriginalResignFirstResponder(_ selector: Selector) -> Bool {
|