2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
#import "RCTEventDispatcher.h"
|
|
|
|
|
|
|
|
#import "RCTAssert.h"
|
|
|
|
#import "RCTBridge.h"
|
2015-08-11 13:37:12 +00:00
|
|
|
#import "RCTUtils.h"
|
2015-05-28 03:15:33 +00:00
|
|
|
|
2015-07-21 19:37:24 +00:00
|
|
|
const NSInteger RCTTextUpdateLagWarningThreshold = 3;
|
|
|
|
|
2015-08-11 13:37:12 +00:00
|
|
|
NSString *RCTNormalizeInputEventName(NSString *eventName)
|
|
|
|
{
|
|
|
|
if ([eventName hasPrefix:@"on"]) {
|
|
|
|
eventName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 2} withString:@"top"];
|
|
|
|
} else if (![eventName hasPrefix:@"top"]) {
|
|
|
|
eventName = [[@"top" stringByAppendingString:[eventName substringToIndex:1].uppercaseString]
|
|
|
|
stringByAppendingString:[eventName substringFromIndex:1]];
|
|
|
|
}
|
|
|
|
return eventName;
|
|
|
|
}
|
|
|
|
|
2015-06-02 09:58:49 +00:00
|
|
|
static NSNumber *RCTGetEventID(id<RCTEvent> event)
|
2015-05-28 03:15:33 +00:00
|
|
|
{
|
2015-06-02 09:58:49 +00:00
|
|
|
return @(
|
2015-08-24 10:14:33 +00:00
|
|
|
event.viewTag.intValue |
|
2016-02-03 13:22:12 +00:00
|
|
|
(((uint64_t)event.eventName.hash & 0xFFFF) << 32));
|
2015-05-28 03:15:33 +00:00
|
|
|
}
|
|
|
|
|
2015-08-25 11:27:09 +00:00
|
|
|
@interface RCTEventDispatcher() <RCTFrameUpdateObserver>
|
2015-05-28 03:15:33 +00:00
|
|
|
|
|
|
|
@end
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
@implementation RCTEventDispatcher
|
|
|
|
{
|
2015-06-02 09:58:49 +00:00
|
|
|
NSMutableDictionary *_eventQueue;
|
2015-05-28 03:15:33 +00:00
|
|
|
NSLock *_eventQueueLock;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-05-28 03:15:33 +00:00
|
|
|
@synthesize bridge = _bridge;
|
2015-06-07 15:42:31 +00:00
|
|
|
@synthesize paused = _paused;
|
2015-09-29 12:26:01 +00:00
|
|
|
@synthesize pauseCallback = _pauseCallback;
|
2015-05-28 03:15:33 +00:00
|
|
|
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2015-11-25 11:09:00 +00:00
|
|
|
- (void)setBridge:(RCTBridge *)bridge
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-11-25 11:09:00 +00:00
|
|
|
_bridge = bridge;
|
|
|
|
_paused = YES;
|
|
|
|
_eventQueue = [NSMutableDictionary new];
|
|
|
|
_eventQueueLock = [NSLock new];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-09-29 12:26:01 +00:00
|
|
|
- (void)setPaused:(BOOL)paused
|
|
|
|
{
|
|
|
|
if (_paused != paused) {
|
|
|
|
_paused = paused;
|
|
|
|
if (_pauseCallback) {
|
|
|
|
_pauseCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-24 17:06:57 +00:00
|
|
|
- (void)sendAppEventWithName:(NSString *)name body:(id)body
|
|
|
|
{
|
|
|
|
[_bridge enqueueJSCall:@"RCTNativeAppEventEmitter.emit"
|
2015-02-20 04:10:52 +00:00
|
|
|
args:body ? @[name, body] : @[name]];
|
|
|
|
}
|
|
|
|
|
2015-02-24 17:06:57 +00:00
|
|
|
- (void)sendDeviceEventWithName:(NSString *)name body:(id)body
|
|
|
|
{
|
|
|
|
[_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit"
|
|
|
|
args:body ? @[name, body] : @[name]];
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body
|
|
|
|
{
|
2015-08-11 13:37:12 +00:00
|
|
|
if (RCT_DEBUG) {
|
|
|
|
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
|
|
|
|
@"Event body dictionary must include a 'target' property containing a React tag");
|
|
|
|
}
|
2015-03-23 20:28:42 +00:00
|
|
|
|
2015-08-11 13:37:12 +00:00
|
|
|
name = RCTNormalizeInputEventName(name);
|
2015-02-20 04:10:52 +00:00
|
|
|
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
|
2015-02-24 17:06:57 +00:00
|
|
|
args:body ? @[body[@"target"], name, body] : @[body[@"target"], name]];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)sendTextEventWithType:(RCTTextEventType)type
|
|
|
|
reactTag:(NSNumber *)reactTag
|
|
|
|
text:(NSString *)text
|
2015-11-02 17:13:41 +00:00
|
|
|
key:(NSString *)key
|
2015-07-21 19:37:24 +00:00
|
|
|
eventCount:(NSInteger)eventCount
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
|
|
|
static NSString *events[] = {
|
2015-08-11 13:37:12 +00:00
|
|
|
@"focus",
|
|
|
|
@"blur",
|
|
|
|
@"change",
|
|
|
|
@"submitEditing",
|
|
|
|
@"endEditing",
|
2015-11-02 17:13:41 +00:00
|
|
|
@"keyPress"
|
2015-02-20 04:10:52 +00:00
|
|
|
};
|
2015-03-23 20:28:42 +00:00
|
|
|
|
2015-11-02 17:13:41 +00:00
|
|
|
NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{
|
2015-07-21 19:37:24 +00:00
|
|
|
@"eventCount": @(eventCount),
|
2015-02-24 17:06:57 +00:00
|
|
|
@"target": reactTag
|
2015-02-20 04:10:52 +00:00
|
|
|
}];
|
2015-11-02 17:13:41 +00:00
|
|
|
|
|
|
|
if (text) {
|
|
|
|
body[@"text"] = text;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key) {
|
|
|
|
if (key.length == 0) {
|
|
|
|
key = @"Backspace"; // backspace
|
|
|
|
} else {
|
|
|
|
switch ([key characterAtIndex:0]) {
|
|
|
|
case '\t':
|
|
|
|
key = @"Tab";
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
key = @"Enter";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
body[@"key"] = key;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self sendInputEventWithName:events[type] body:body];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-05-28 03:15:33 +00:00
|
|
|
- (void)sendEvent:(id<RCTEvent>)event
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-05-28 03:15:33 +00:00
|
|
|
if (!event.canCoalesce) {
|
2016-02-03 13:22:15 +00:00
|
|
|
[self flushEventsQueue];
|
2015-05-28 03:15:33 +00:00
|
|
|
[self dispatchEvent:event];
|
|
|
|
return;
|
|
|
|
}
|
2015-03-23 20:28:42 +00:00
|
|
|
|
2015-05-28 03:15:33 +00:00
|
|
|
[_eventQueueLock lock];
|
|
|
|
|
2015-06-02 09:58:49 +00:00
|
|
|
NSNumber *eventID = RCTGetEventID(event);
|
2015-05-28 03:15:33 +00:00
|
|
|
id<RCTEvent> previousEvent = _eventQueue[eventID];
|
|
|
|
|
|
|
|
if (previousEvent) {
|
|
|
|
event = [previousEvent coalesceWithEvent:event];
|
|
|
|
}
|
|
|
|
|
|
|
|
_eventQueue[eventID] = event;
|
2015-09-29 12:26:01 +00:00
|
|
|
self.paused = NO;
|
2015-05-28 03:15:33 +00:00
|
|
|
|
|
|
|
[_eventQueueLock unlock];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dispatchEvent:(id<RCTEvent>)event
|
|
|
|
{
|
2016-02-03 13:22:14 +00:00
|
|
|
[_bridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]];
|
2015-05-28 03:15:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (dispatch_queue_t)methodQueue
|
|
|
|
{
|
|
|
|
return RCTJSThread;
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
|
2016-02-03 13:22:15 +00:00
|
|
|
{
|
|
|
|
[self flushEventsQueue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)flushEventsQueue
|
2015-05-28 03:15:33 +00:00
|
|
|
{
|
|
|
|
[_eventQueueLock lock];
|
2016-02-03 13:22:15 +00:00
|
|
|
NSDictionary *eventQueue = _eventQueue;
|
2015-08-17 14:35:34 +00:00
|
|
|
_eventQueue = [NSMutableDictionary new];
|
2015-09-29 12:26:01 +00:00
|
|
|
self.paused = YES;
|
2015-05-28 03:15:33 +00:00
|
|
|
[_eventQueueLock unlock];
|
|
|
|
|
2015-06-02 09:58:49 +00:00
|
|
|
for (id<RCTEvent> event in eventQueue.allValues) {
|
2015-05-28 03:15:33 +00:00
|
|
|
[self dispatchEvent:event];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|