2020-10-14 12:37:51 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import "RCTEventDispatcher.h"
|
|
|
|
|
|
|
|
#import <React/RCTAssert.h>
|
|
|
|
#import <React/RCTBridge+Private.h>
|
|
|
|
#import <React/RCTBridge.h>
|
|
|
|
#import <React/RCTComponentEvent.h>
|
|
|
|
#import <React/RCTProfile.h>
|
|
|
|
#import <React/RCTUtils.h>
|
|
|
|
#import <ReactCommon/RCTTurboModule.h>
|
|
|
|
|
|
|
|
#import "CoreModulesPlugins.h"
|
|
|
|
|
|
|
|
static NSNumber *RCTGetEventID(NSNumber *viewTag, NSString *eventName, uint16_t coalescingKey)
|
|
|
|
{
|
|
|
|
return @(viewTag.intValue | (((uint64_t)eventName.hash & 0xFFFF) << 32) | (((uint64_t)coalescingKey) << 48));
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t RCTUniqueCoalescingKeyGenerator = 0;
|
|
|
|
|
|
|
|
@interface RCTEventDispatcher () <RCTTurboModule>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RCTEventDispatcher {
|
|
|
|
// We need this lock to protect access to _events, _eventQueue and _eventsDispatchScheduled. It's filled in on main
|
|
|
|
// thread and consumed on js thread.
|
|
|
|
NSLock *_eventQueueLock;
|
|
|
|
// We have this id -> event mapping so we coalesce effectively.
|
|
|
|
NSMutableDictionary<NSNumber *, id<RCTEvent>> *_events;
|
|
|
|
// This array contains ids of events in order they come in, so we can emit them to JS in the exact same order.
|
|
|
|
NSMutableArray<NSNumber *> *_eventQueue;
|
|
|
|
BOOL _eventsDispatchScheduled;
|
|
|
|
NSHashTable<id<RCTEventDispatcherObserver>> *_observers;
|
|
|
|
NSLock *_observersLock;
|
|
|
|
}
|
|
|
|
|
|
|
|
@synthesize bridge = _bridge;
|
|
|
|
@synthesize dispatchToJSThread = _dispatchToJSThread;
|
2021-05-14 19:20:09 +03:00
|
|
|
@synthesize callableJSModules = _callableJSModules;
|
2020-10-14 12:37:51 +03:00
|
|
|
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2021-05-22 00:48:26 +03:00
|
|
|
- (void)initialize
|
2020-10-14 12:37:51 +03:00
|
|
|
{
|
|
|
|
_events = [NSMutableDictionary new];
|
|
|
|
_eventQueue = [NSMutableArray new];
|
|
|
|
_eventQueueLock = [NSLock new];
|
|
|
|
_eventsDispatchScheduled = NO;
|
|
|
|
_observers = [NSHashTable weakObjectsHashTable];
|
|
|
|
_observersLock = [NSLock new];
|
|
|
|
}
|
|
|
|
|
2021-09-03 02:48:53 +03:00
|
|
|
- (void)sendViewEventWithName:(NSString *)name reactTag:(NSNumber *)reactTag
|
|
|
|
{
|
|
|
|
[_callableJSModules invokeModule:@"RCTViewEventEmitter" method:@"emit" withArgs:@[ name, RCTNullIfNil(reactTag) ]];
|
|
|
|
}
|
|
|
|
|
2020-10-14 12:37:51 +03:00
|
|
|
- (void)sendAppEventWithName:(NSString *)name body:(id)body
|
|
|
|
{
|
2021-05-14 19:20:09 +03:00
|
|
|
[_callableJSModules invokeModule:@"RCTNativeAppEventEmitter"
|
|
|
|
method:@"emit"
|
|
|
|
withArgs:body ? @[ name, body ] : @[ name ]];
|
2020-10-14 12:37:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)sendDeviceEventWithName:(NSString *)name body:(id)body
|
|
|
|
{
|
2021-05-14 19:20:09 +03:00
|
|
|
[_callableJSModules invokeModule:@"RCTDeviceEventEmitter" method:@"emit" withArgs:body ? @[ name, body ] : @[ name ]];
|
2020-10-14 12:37:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)sendTextEventWithType:(RCTTextEventType)type
|
|
|
|
reactTag:(NSNumber *)reactTag
|
|
|
|
text:(NSString *)text
|
|
|
|
key:(NSString *)key
|
|
|
|
eventCount:(NSInteger)eventCount
|
|
|
|
{
|
|
|
|
static NSString *events[] = {@"focus", @"blur", @"change", @"submitEditing", @"endEditing", @"keyPress"};
|
|
|
|
|
|
|
|
NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{
|
|
|
|
@"eventCount" : @(eventCount),
|
|
|
|
}];
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCTComponentEvent *event = [[RCTComponentEvent alloc] initWithName:events[type] viewTag:reactTag body:body];
|
|
|
|
[self sendEvent:event];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)sendEvent:(id<RCTEvent>)event
|
|
|
|
{
|
|
|
|
[_observersLock lock];
|
|
|
|
|
|
|
|
for (id<RCTEventDispatcherObserver> observer in _observers) {
|
|
|
|
[observer eventDispatcherWillDispatchEvent:event];
|
|
|
|
}
|
|
|
|
|
|
|
|
[_observersLock unlock];
|
|
|
|
|
|
|
|
[_eventQueueLock lock];
|
|
|
|
|
|
|
|
NSNumber *eventID;
|
|
|
|
if (event.canCoalesce) {
|
|
|
|
eventID = RCTGetEventID(event.viewTag, event.eventName, event.coalescingKey);
|
|
|
|
id<RCTEvent> previousEvent = _events[eventID];
|
|
|
|
if (previousEvent) {
|
|
|
|
event = [previousEvent coalesceWithEvent:event];
|
|
|
|
} else {
|
|
|
|
[_eventQueue addObject:eventID];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
id<RCTEvent> previousEvent = _events[eventID];
|
|
|
|
eventID = RCTGetEventID(event.viewTag, event.eventName, RCTUniqueCoalescingKeyGenerator++);
|
|
|
|
RCTAssert(
|
|
|
|
previousEvent == nil,
|
|
|
|
@"Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@",
|
|
|
|
event,
|
|
|
|
eventID,
|
|
|
|
previousEvent);
|
|
|
|
[_eventQueue addObject:eventID];
|
|
|
|
}
|
|
|
|
|
|
|
|
_events[eventID] = event;
|
|
|
|
|
|
|
|
BOOL scheduleEventsDispatch = NO;
|
|
|
|
if (!_eventsDispatchScheduled) {
|
|
|
|
_eventsDispatchScheduled = YES;
|
|
|
|
scheduleEventsDispatch = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have to release the lock before dispatching block with events,
|
|
|
|
// since dispatchBlock: can be executed synchronously on the same queue.
|
|
|
|
// (This is happening when chrome debugging is turned on.)
|
|
|
|
[_eventQueueLock unlock];
|
|
|
|
|
|
|
|
if (scheduleEventsDispatch) {
|
|
|
|
if (_bridge) {
|
|
|
|
[_bridge
|
|
|
|
dispatchBlock:^{
|
|
|
|
[self flushEventsQueue];
|
|
|
|
}
|
|
|
|
queue:RCTJSThread];
|
|
|
|
} else if (_dispatchToJSThread) {
|
|
|
|
_dispatchToJSThread(^{
|
|
|
|
[self flushEventsQueue];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)addDispatchObserver:(id<RCTEventDispatcherObserver>)observer
|
|
|
|
{
|
|
|
|
[_observersLock lock];
|
|
|
|
[_observers addObject:observer];
|
|
|
|
[_observersLock unlock];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)removeDispatchObserver:(id<RCTEventDispatcherObserver>)observer
|
|
|
|
{
|
|
|
|
[_observersLock lock];
|
|
|
|
[_observers removeObject:observer];
|
|
|
|
[_observersLock unlock];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dispatchEvent:(id<RCTEvent>)event
|
|
|
|
{
|
2021-05-14 19:20:09 +03:00
|
|
|
NSString *moduleDotMethod = [[event class] moduleDotMethod];
|
|
|
|
NSArray<NSString *> *const components = [moduleDotMethod componentsSeparatedByString:@"."];
|
|
|
|
NSString *const moduleName = components[0];
|
|
|
|
NSString *const methodName = components[1];
|
|
|
|
|
|
|
|
[_callableJSModules invokeModule:moduleName method:methodName withArgs:[event arguments]];
|
2020-10-14 12:37:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (dispatch_queue_t)methodQueue
|
|
|
|
{
|
|
|
|
return RCTJSThread;
|
|
|
|
}
|
|
|
|
|
|
|
|
// js thread only (which surprisingly can be the main thread, depends on used JS executor)
|
|
|
|
- (void)flushEventsQueue
|
|
|
|
{
|
|
|
|
[_eventQueueLock lock];
|
|
|
|
NSDictionary *events = _events;
|
|
|
|
_events = [NSMutableDictionary new];
|
|
|
|
NSMutableArray *eventQueue = _eventQueue;
|
|
|
|
_eventQueue = [NSMutableArray new];
|
|
|
|
_eventsDispatchScheduled = NO;
|
|
|
|
[_eventQueueLock unlock];
|
|
|
|
|
|
|
|
for (NSNumber *eventId in eventQueue) {
|
|
|
|
[self dispatchEvent:events[eventId]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:01 +03:00
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
|
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-10-14 12:37:51 +03:00
|
|
|
@end
|
|
|
|
|
|
|
|
Class RCTEventDispatcherCls(void)
|
|
|
|
{
|
|
|
|
return RCTEventDispatcher.class;
|
|
|
|
}
|