2020-05-08 16:37:49 +00:00
|
|
|
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
|
2020-07-20 16:35:17 +00:00
|
|
|
index 478af12..d3cd45c 100644
|
2020-05-08 16:37:49 +00:00
|
|
|
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
|
|
|
|
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
|
2020-07-20 16:35:17 +00:00
|
|
|
@@ -700,6 +700,7 @@ export type Props = $ReadOnly<{|
|
|
|
|
|
|
|
|
type ImperativeMethods = $ReadOnly<{|
|
|
|
|
clear: () => void,
|
|
|
|
+ setTextAndSelection: () => void,
|
|
|
|
isFocused: () => boolean,
|
|
|
|
getNativeRef: () => ?React.ElementRef<HostComponent<mixed>>,
|
|
|
|
|}>;
|
|
|
|
@@ -947,6 +948,18 @@ function InternalTextInput(props: Props): React.Node {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ function setTextAndSelection(_text, _selection): void {
|
|
|
|
+ if (inputRef.current != null) {
|
|
|
|
+ viewCommands.setTextAndSelection(
|
|
|
|
+ inputRef.current,
|
|
|
|
+ mostRecentEventCount,
|
|
|
|
+ _text,
|
|
|
|
+ _selection?.start ?? -1,
|
|
|
|
+ _selection?.end ?? -1,
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
// TODO: Fix this returning true on null === null, when no input is focused
|
|
|
|
function isFocused(): boolean {
|
|
|
|
return TextInputState.currentlyFocusedInput() === inputRef.current;
|
|
|
|
@@ -985,6 +998,7 @@ function InternalTextInput(props: Props): React.Node {
|
|
|
|
*/
|
|
|
|
if (ref) {
|
2020-05-08 16:37:49 +00:00
|
|
|
ref.clear = clear;
|
2020-07-20 16:35:17 +00:00
|
|
|
+ ref.setTextAndSelection = setTextAndSelection;
|
2020-05-08 16:37:49 +00:00
|
|
|
ref.isFocused = isFocused;
|
|
|
|
ref.getNativeRef = getNativeRef;
|
|
|
|
}
|
2020-09-25 19:05:07 +00:00
|
|
|
diff --git a/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m b/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m
|
|
|
|
index af4becd..55bc2c8 100644
|
|
|
|
--- a/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m
|
|
|
|
+++ b/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m
|
|
|
|
@@ -275,6 +275,8 @@ - (void)displayLayer:(CALayer *)layer
|
|
|
|
if (_currentFrame) {
|
|
|
|
layer.contentsScale = self.animatedImageScale;
|
|
|
|
layer.contents = (__bridge id)_currentFrame.CGImage;
|
|
|
|
+ } else {
|
|
|
|
+ [super displayLayer:layer];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-02 16:19:05 +00:00
|
|
|
diff --git a/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm b/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm
|
2020-08-19 17:14:22 +00:00
|
|
|
index 274f381..fa74a27 100644
|
2019-09-02 16:19:05 +00:00
|
|
|
--- a/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm
|
|
|
|
+++ b/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm
|
2020-08-19 17:14:22 +00:00
|
|
|
@@ -8,11 +8,12 @@
|
|
|
|
#import <React/RCTHTTPRequestHandler.h>
|
2020-03-11 19:06:55 +00:00
|
|
|
|
2020-08-19 17:14:22 +00:00
|
|
|
#import <mutex>
|
|
|
|
-
|
|
|
|
+#import <MMKV/MMKV.h>
|
2020-03-11 19:06:55 +00:00
|
|
|
#import <React/RCTNetworking.h>
|
2020-05-08 16:37:49 +00:00
|
|
|
#import <ReactCommon/RCTTurboModule.h>
|
2020-03-11 19:06:55 +00:00
|
|
|
|
2020-05-08 16:37:49 +00:00
|
|
|
#import "RCTNetworkPlugins.h"
|
2020-08-19 17:14:22 +00:00
|
|
|
+#import "SecureStorage.h"
|
|
|
|
|
|
|
|
@interface RCTHTTPRequestHandler () <NSURLSessionDataDelegate, RCTTurboModule>
|
|
|
|
|
|
|
|
@@ -58,6 +59,101 @@ - (BOOL)canHandleRequest:(NSURLRequest *)request
|
2019-09-02 16:19:05 +00:00
|
|
|
return [schemes containsObject:request.URL.scheme.lowercaseString];
|
|
|
|
}
|
|
|
|
|
|
|
|
+-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password
|
|
|
|
+{
|
|
|
|
+ NSString *authMethod = [[challenge protectionSpace] authenticationMethod];
|
|
|
|
+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
|
|
|
+
|
|
|
|
+ if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || path == nil || password == nil) {
|
|
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
|
|
+ } else if (path && password) {
|
|
|
|
+ NSMutableArray *policies = [NSMutableArray array];
|
|
|
|
+ [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
|
|
|
|
+ SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
|
|
|
|
+
|
|
|
|
+ SecTrustResultType result;
|
|
|
|
+ SecTrustEvaluate(serverTrust, &result);
|
|
|
|
+
|
|
|
|
+ if (![[NSFileManager defaultManager] fileExistsAtPath:path])
|
|
|
|
+ {
|
|
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ NSData *p12data = [NSData dataWithContentsOfFile:path];
|
|
|
|
+ NSDictionary* options = @{ (id)kSecImportExportPassphrase:password };
|
|
|
|
+ CFArrayRef rawItems = NULL;
|
|
|
|
+ OSStatus status = SecPKCS12Import((__bridge CFDataRef)p12data,
|
|
|
|
+ (__bridge CFDictionaryRef)options,
|
|
|
|
+ &rawItems);
|
|
|
|
+
|
|
|
|
+ if (status != noErr) {
|
|
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ NSArray* items = (NSArray*)CFBridgingRelease(rawItems);
|
|
|
|
+ NSDictionary* firstItem = nil;
|
|
|
|
+ if ((status == errSecSuccess) && ([items count]>0)) {
|
|
|
|
+ firstItem = items[0];
|
|
|
|
+ }
|
2020-05-08 16:37:49 +00:00
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+ SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]);
|
|
|
|
+ SecCertificateRef certificate = NULL;
|
|
|
|
+ if (identity) {
|
|
|
|
+ SecIdentityCopyCertificate(identity, &certificate);
|
|
|
|
+ if (certificate) { CFRelease(certificate); }
|
|
|
|
+ }
|
2020-05-08 16:37:49 +00:00
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+ NSMutableArray *certificates = [[NSMutableArray alloc] init];
|
|
|
|
+ [certificates addObject:CFBridgingRelease(certificate)];
|
2020-05-08 16:37:49 +00:00
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+ return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone];
|
|
|
|
+ }
|
2020-05-08 16:37:49 +00:00
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
|
|
+}
|
|
|
|
+
|
2020-08-19 17:14:22 +00:00
|
|
|
+- (NSString *)stringToHex:(NSString *)string
|
|
|
|
+{
|
|
|
|
+ char *utf8 = (char *)[string UTF8String];
|
|
|
|
+ NSMutableString *hex = [NSMutableString string];
|
|
|
|
+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF];
|
|
|
|
+
|
|
|
|
+ return [[NSString stringWithFormat:@"%@", hex] lowercaseString];
|
|
|
|
+}
|
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
|
|
|
|
+
|
|
|
|
+ NSString *host = challenge.protectionSpace.host;
|
2020-08-19 17:14:22 +00:00
|
|
|
+
|
|
|
|
+ // Read the clientSSL info from MMKV
|
|
|
|
+ __block NSDictionary *clientSSL;
|
|
|
|
+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
|
|
|
|
+
|
|
|
|
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
|
|
|
|
+ [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"] callback:^(NSArray *response) {
|
|
|
|
+ // Error happened
|
|
|
|
+ if ([response objectAtIndex:0] != [NSNull null]) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ NSString *key = [response objectAtIndex:1];
|
|
|
|
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
|
|
|
|
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
|
|
|
|
+
|
|
|
|
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
|
|
|
|
+ }];
|
2019-09-02 16:19:05 +00:00
|
|
|
+
|
|
|
|
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
|
|
+
|
|
|
|
+ if (clientSSL != (id)[NSNull null]) {
|
|
|
|
+ NSString *path = [clientSSL objectForKey:@"path"];
|
|
|
|
+ NSString *password = [clientSSL objectForKey:@"password"];
|
|
|
|
+ credential = [self getUrlCredential:challenge path:path password:password];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
|
|
|
+}
|
2020-07-20 16:35:17 +00:00
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+
|
2020-05-08 16:37:49 +00:00
|
|
|
- (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request
|
|
|
|
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
|
|
|
{
|
2019-08-14 11:16:59 +00:00
|
|
|
diff --git a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m
|
2020-08-19 17:14:22 +00:00
|
|
|
index b967c14..5ffc258 100644
|
2019-08-14 11:16:59 +00:00
|
|
|
--- a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m
|
|
|
|
+++ b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m
|
2020-08-19 17:14:22 +00:00
|
|
|
@@ -24,6 +24,9 @@
|
2020-03-11 19:06:55 +00:00
|
|
|
#import <React/RCTAssert.h>
|
|
|
|
#import <React/RCTLog.h>
|
2020-07-20 16:35:17 +00:00
|
|
|
|
2020-08-19 17:14:22 +00:00
|
|
|
+#import <MMKV/MMKV.h>
|
|
|
|
+#import "SecureStorage.h"
|
|
|
|
+
|
2020-03-11 19:06:55 +00:00
|
|
|
typedef NS_ENUM(NSInteger, RCTSROpCode) {
|
|
|
|
RCTSROpCodeTextFrame = 0x1,
|
2020-08-19 17:14:22 +00:00
|
|
|
RCTSROpCodeBinaryFrame = 0x2,
|
|
|
|
@@ -478,6 +481,38 @@ - (void)didConnect
|
2019-09-02 16:19:05 +00:00
|
|
|
[self _readHTTPHeader];
|
|
|
|
}
|
|
|
|
|
|
|
|
+- (void)setClientSSL:(NSString *)path password:(NSString *)password options:(NSMutableDictionary *)options;
|
|
|
|
+{
|
|
|
|
+ if ([[NSFileManager defaultManager] fileExistsAtPath:path])
|
|
|
|
+ {
|
|
|
|
+ NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:path];
|
|
|
|
+ NSDictionary* certOptions = @{ (id)kSecImportExportPassphrase:password };
|
|
|
|
+ CFArrayRef keyref = NULL;
|
|
|
|
+ OSStatus sanityChesk = SecPKCS12Import((__bridge CFDataRef)pkcs12data,
|
|
|
|
+ (__bridge CFDictionaryRef)certOptions,
|
|
|
|
+ &keyref);
|
|
|
|
+ if (sanityChesk == noErr) {
|
|
|
|
+ CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
|
|
|
|
+ SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
|
|
|
|
+ SecCertificateRef cert = NULL;
|
|
|
|
+ OSStatus status = SecIdentityCopyCertificate(identityRef, &cert);
|
|
|
|
+ if (!status) {
|
|
|
|
+ NSArray *certificates = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, (__bridge id)cert, nil];
|
|
|
|
+ [options setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
2020-08-19 17:14:22 +00:00
|
|
|
+
|
|
|
|
+- (NSString *)stringToHex:(NSString *)string
|
|
|
|
+{
|
|
|
|
+ char *utf8 = (char *)[string UTF8String];
|
|
|
|
+ NSMutableString *hex = [NSMutableString string];
|
|
|
|
+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF];
|
|
|
|
+
|
|
|
|
+ return [[NSString stringWithFormat:@"%@", hex] lowercaseString];
|
|
|
|
+}
|
2019-09-02 16:19:05 +00:00
|
|
|
+
|
2020-05-08 16:37:49 +00:00
|
|
|
- (void)_initializeStreams
|
2019-09-02 16:19:05 +00:00
|
|
|
{
|
|
|
|
assert(_url.port.unsignedIntValue <= UINT32_MAX);
|
2020-08-19 17:14:22 +00:00
|
|
|
@@ -515,6 +550,31 @@ - (void)_initializeStreams
|
2019-09-02 16:19:05 +00:00
|
|
|
RCTLogInfo(@"SocketRocket: In debug mode. Allowing connection to any root cert");
|
|
|
|
#endif
|
|
|
|
|
2020-08-19 17:14:22 +00:00
|
|
|
+ // Read the clientSSL info from MMKV
|
|
|
|
+ __block NSDictionary *clientSSL;
|
|
|
|
+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
|
|
|
|
+
|
|
|
|
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
|
|
|
|
+ [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"] callback:^(NSArray *response) {
|
|
|
|
+ // Error happened
|
|
|
|
+ if ([response objectAtIndex:0] != [NSNull null]) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ NSString *key = [response objectAtIndex:1];
|
|
|
|
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
|
|
|
|
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
|
|
|
|
+
|
|
|
|
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
|
|
|
|
+ }];
|
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+ if (clientSSL != (id)[NSNull null]) {
|
|
|
|
+ NSString *path = [clientSSL objectForKey:@"path"];
|
|
|
|
+ NSString *password = [clientSSL objectForKey:@"password"];
|
|
|
|
+
|
|
|
|
+ [self setClientSSL:path password:password options:SSLOptions];
|
|
|
|
+ }
|
2020-07-20 16:35:17 +00:00
|
|
|
+
|
2019-09-02 16:19:05 +00:00
|
|
|
+
|
|
|
|
[_outputStream setProperty:SSLOptions
|
|
|
|
forKey:(__bridge id)kCFStreamPropertySSLSettings];
|
|
|
|
}
|
2020-08-19 17:14:22 +00:00
|
|
|
@@ -594,6 +654,7 @@ - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason
|
2019-08-14 11:16:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ [self.delegate webSocket:self didCloseWithCode:code reason:reason wasClean:YES];
|
|
|
|
[self _sendFrameWithOpcode:RCTSROpCodeConnectionClose data:payload];
|
|
|
|
});
|
|
|
|
}
|
2019-11-25 20:01:17 +00:00
|
|
|
diff --git a/node_modules/react-native/React/Base/RCTKeyCommands.h b/node_modules/react-native/React/Base/RCTKeyCommands.h
|
2020-07-20 16:35:17 +00:00
|
|
|
index 983348e..95742f4 100644
|
2019-11-25 20:01:17 +00:00
|
|
|
--- a/node_modules/react-native/React/Base/RCTKeyCommands.h
|
|
|
|
+++ b/node_modules/react-native/React/Base/RCTKeyCommands.h
|
2020-07-20 16:35:17 +00:00
|
|
|
@@ -18,6 +18,12 @@
|
2019-11-25 20:01:17 +00:00
|
|
|
modifierFlags:(UIKeyModifierFlags)flags
|
|
|
|
action:(void (^)(UIKeyCommand *command))block;
|
|
|
|
|
|
|
|
+- (void)registerKeyCommand:(NSString *)input
|
|
|
|
+ modifierFlags:(UIKeyModifierFlags)flags
|
|
|
|
+ discoverabilityTitle:(NSString *)discoverabilityTitle
|
|
|
|
+ action:(void (^)(UIKeyCommand *))block;
|
2020-07-20 16:35:17 +00:00
|
|
|
+
|
2019-11-25 20:01:17 +00:00
|
|
|
+
|
|
|
|
/**
|
|
|
|
* Unregister a single-press keyboard command.
|
|
|
|
*/
|
|
|
|
diff --git a/node_modules/react-native/React/Base/RCTKeyCommands.m b/node_modules/react-native/React/Base/RCTKeyCommands.m
|
2020-07-20 16:35:17 +00:00
|
|
|
index d48ba93..387d551 100644
|
2019-11-25 20:01:17 +00:00
|
|
|
--- a/node_modules/react-native/React/Base/RCTKeyCommands.m
|
|
|
|
+++ b/node_modules/react-native/React/Base/RCTKeyCommands.m
|
|
|
|
@@ -12,8 +12,6 @@
|
|
|
|
#import "RCTDefines.h"
|
|
|
|
#import "RCTUtils.h"
|
|
|
|
|
|
|
|
-#if RCT_DEV
|
|
|
|
-
|
|
|
|
@interface RCTKeyCommand : NSObject <NSCopying>
|
|
|
|
|
|
|
|
@property (nonatomic, strong) UIKeyCommand *keyCommand;
|
2020-07-20 16:35:17 +00:00
|
|
|
@@ -115,7 +113,9 @@ - (void)RCT_handleKeyCommand:(UIKeyCommand *)key
|
2019-11-25 20:01:17 +00:00
|
|
|
// NOTE: throttle the key handler because on iOS 9 the handleKeyCommand:
|
|
|
|
// method gets called repeatedly if the command key is held down.
|
|
|
|
static NSTimeInterval lastCommand = 0;
|
|
|
|
- if (CACurrentMediaTime() - lastCommand > 0.5) {
|
|
|
|
+ if (CACurrentMediaTime() - lastCommand > 0.5 ||
|
2020-05-08 16:37:49 +00:00
|
|
|
+ [key.input isEqualToString:@"UIKeyInputUpArrow"] || // repeat command if is scroll
|
|
|
|
+ [key.input isEqualToString:@"UIKeyInputDownArrow"]) {
|
2019-11-25 20:01:17 +00:00
|
|
|
for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) {
|
|
|
|
if ([command.keyCommand.input isEqualToString:key.input] &&
|
|
|
|
command.keyCommand.modifierFlags == key.modifierFlags) {
|
2020-07-20 16:35:17 +00:00
|
|
|
@@ -178,6 +178,8 @@ - (void)RCT_handleDoublePressKeyCommand:(UIKeyCommand *)key
|
2019-11-25 20:01:17 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
+#if RCT_DEV
|
|
|
|
+
|
|
|
|
@implementation RCTKeyCommands
|
|
|
|
|
|
|
|
+ (void)initialize
|
2020-07-20 16:35:17 +00:00
|
|
|
@@ -220,6 +222,23 @@ - (void)registerKeyCommandWithInput:(NSString *)input
|
2019-11-25 20:01:17 +00:00
|
|
|
[_commands addObject:keyCommand];
|
|
|
|
}
|
|
|
|
|
|
|
|
+- (void)registerKeyCommand:(NSString *)input
|
|
|
|
+ modifierFlags:(UIKeyModifierFlags)flags
|
|
|
|
+ discoverabilityTitle:(NSString *)discoverabilityTitle
|
|
|
|
+ action:(void (^)(UIKeyCommand *))block
|
|
|
|
+{
|
|
|
|
+ RCTAssertMainQueue();
|
|
|
|
+
|
|
|
|
+ UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input
|
|
|
|
+ modifierFlags:flags
|
|
|
|
+ action:@selector(RCT_handleKeyCommand:)
|
|
|
|
+ discoverabilityTitle:discoverabilityTitle];
|
|
|
|
+
|
|
|
|
+ RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] initWithKeyCommand:command block:block];
|
|
|
|
+ [_commands removeObject:keyCommand];
|
|
|
|
+ [_commands addObject:keyCommand];
|
|
|
|
+}
|
|
|
|
+
|
2020-07-20 16:35:17 +00:00
|
|
|
- (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags
|
2019-11-25 20:01:17 +00:00
|
|
|
{
|
2020-07-20 16:35:17 +00:00
|
|
|
RCTAssertMainQueue();
|
|
|
|
@@ -289,9 +308,48 @@ - (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input modifierFlag
|
2019-11-25 20:01:17 +00:00
|
|
|
|
|
|
|
@implementation RCTKeyCommands
|
|
|
|
|
|
|
|
++ (void)initialize
|
|
|
|
+{
|
|
|
|
+ // swizzle UIResponder
|
|
|
|
+ RCTSwapInstanceMethods([UIResponder class],
|
|
|
|
+ @selector(keyCommands),
|
|
|
|
+ @selector(RCT_keyCommands));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ (instancetype)sharedInstance
|
|
|
|
{
|
|
|
|
- return nil;
|
|
|
|
+ static RCTKeyCommands *sharedInstance;
|
|
|
|
+ static dispatch_once_t onceToken;
|
|
|
|
+ dispatch_once(&onceToken, ^{
|
|
|
|
+ sharedInstance = [self new];
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return sharedInstance;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (instancetype)init
|
|
|
|
+{
|
|
|
|
+ if ((self = [super init])) {
|
|
|
|
+ _commands = [NSMutableSet new];
|
|
|
|
+ }
|
|
|
|
+ return self;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)registerKeyCommand:(NSString *)input
|
|
|
|
+ modifierFlags:(UIKeyModifierFlags)flags
|
2020-07-20 16:35:17 +00:00
|
|
|
+ discoverabilityTitle:(NSString *)discoverabilityTitle
|
|
|
|
+ action:(void (^)(UIKeyCommand *))block
|
2019-11-25 20:01:17 +00:00
|
|
|
+{
|
|
|
|
+ RCTAssertMainQueue();
|
|
|
|
+
|
|
|
|
+ UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input
|
|
|
|
+ modifierFlags:flags
|
2020-07-20 16:35:17 +00:00
|
|
|
+ action:@selector(RCT_handleKeyCommand:)
|
|
|
|
+ discoverabilityTitle:discoverabilityTitle];
|
2019-11-25 20:01:17 +00:00
|
|
|
+
|
|
|
|
+ RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] initWithKeyCommand:command block:block];
|
|
|
|
+ [_commands removeObject:keyCommand];
|
|
|
|
+ [_commands addObject:keyCommand];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)registerKeyCommandWithInput:(NSString *)input
|
2020-07-20 16:35:17 +00:00
|
|
|
@@ -302,6 +360,13 @@ - (void)registerKeyCommandWithInput:(NSString *)input
|
2019-11-25 20:01:17 +00:00
|
|
|
|
2020-07-20 16:35:17 +00:00
|
|
|
- (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags
|
|
|
|
{
|
2019-11-25 20:01:17 +00:00
|
|
|
+ RCTAssertMainQueue();
|
|
|
|
+ for (RCTKeyCommand *command in _commands.allObjects) {
|
|
|
|
+ if ([command matchesInput:input flags:flags]) {
|
|
|
|
+ [_commands removeObject:command];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
2020-07-20 16:35:17 +00:00
|
|
|
}
|
2019-11-25 20:01:17 +00:00
|
|
|
|
2020-07-20 16:35:17 +00:00
|
|
|
- (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags
|