fix(app): iPad hardware Enter submits the composer

Fixes #810
This commit is contained in:
Bolun Zhang
2026-05-13 17:52:39 +08:00
committed by GitHub
parent 72293f7f4f
commit 81697f85b2
9 changed files with 356 additions and 0 deletions
@@ -0,0 +1,7 @@
{
"platforms": ["ios"],
"ios": {
"modules": ["PaseoHardwareKeyboardModule"],
"reactDelegateHandlers": ["PaseoHardwareKeyboardReactDelegateHandler"]
}
}
@@ -0,0 +1,24 @@
require 'json'
Pod::Spec.new do |s|
s.name = 'PaseoHardwareKeyboard'
s.version = '0.1.0'
s.summary = 'Hardware keyboard shortcuts for Paseo'
s.description = 'Hardware keyboard shortcuts for Paseo'
s.license = 'AGPL-3.0-or-later'
s.author = 'Paseo'
s.homepage = 'https://paseo.sh'
s.platforms = { :ios => '13.4' }
s.swift_version = '5.4'
s.source = { :path => '.' }
s.static_framework = true
s.dependency 'ExpoModulesCore'
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'SWIFT_COMPILATION_MODE' => 'wholemodule'
}
s.source_files = "**/*.{h,m,swift}"
end
@@ -0,0 +1,99 @@
import ExpoModulesCore
import UIKit
private let hardwareSubmitEventName = "onHardwareKeyboardSubmit"
private weak var activeModule: PaseoHardwareKeyboardModule?
private var isHardwareSubmitEnabled = false
@objc
public class PaseoHardwareKeyboardReactDelegateHandler: ExpoReactDelegateHandler {
public override func createRootViewController() -> UIViewController? {
return PaseoHardwareKeyboardRootViewController()
}
}
public class PaseoHardwareKeyboardModule: Module {
public func definition() -> ModuleDefinition {
Name("PaseoHardwareKeyboard")
Events(hardwareSubmitEventName)
OnCreate {
activeModule = self
}
Function("setHardwareKeyboardSubmitEnabled") { (enabled: Bool) in
DispatchQueue.main.async {
isHardwareSubmitEnabled = enabled
}
}
OnDestroy {
if activeModule === self {
activeModule = nil
}
isHardwareSubmitEnabled = false
}
}
fileprivate func emitHardwareKeyboardSubmit() {
sendEvent(hardwareSubmitEventName, [:])
}
}
private final class PaseoHardwareKeyboardRootViewController: UIViewController {
override var keyCommands: [UIKeyCommand]? {
guard isHardwareSubmitEnabled && UIDevice.current.userInterfaceIdiom == .pad else {
return super.keyCommands
}
let command = UIKeyCommand(
input: "\r",
modifierFlags: [],
action: #selector(handleHardwareKeyboardSubmit(_:))
)
if #available(iOS 15.0, *) {
command.wantsPriorityOverSystemBehavior = true
}
return (super.keyCommands ?? []) + [command]
}
@objc
private func handleHardwareKeyboardSubmit(_ sender: UIKeyCommand) {
guard canSubmitCurrentTextInput() else {
return
}
activeModule?.emitHardwareKeyboardSubmit()
}
private func canSubmitCurrentTextInput() -> Bool {
guard let responder = UIResponder.paseoCurrentFirstResponder else {
return false
}
guard let textInput = responder as? UITextInput else {
return false
}
return textInput.markedTextRange == nil
}
}
private extension UIResponder {
private static weak var currentFirstResponder: UIResponder?
static var paseoCurrentFirstResponder: UIResponder? {
currentFirstResponder = nil
UIApplication.shared.sendAction(
#selector(captureCurrentFirstResponder(_:)),
to: nil,
from: nil,
for: nil
)
return currentFirstResponder
}
@objc
private func captureCurrentFirstResponder(_ sender: Any?) {
UIResponder.currentFirstResponder = self
}
}
@@ -0,0 +1,5 @@
{
"name": "paseo-hardware-keyboard",
"version": "0.1.0",
"private": true
}