Skip to content

feat: coalesced event #592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ios/RNCOnInsetsChangeEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
#import <React/RCTEventDispatcherProtocol.h>

@interface RNCOnInsetsChangeEvent : NSObject <RCTEvent>

- (instancetype)initWithEventName:(NSString *)eventName
reactTag:(NSNumber *)reactTag
insets:(UIEdgeInsets)insets
frame:(CGRect)frame
coalescingKey:(uint16_t)coalescingKey NS_DESIGNATED_INITIALIZER;

@end
79 changes: 79 additions & 0 deletions ios/RNCOnInsetsChangeEvent.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#import "RNCOnInsetsChangeEvent.h"
#import <React/RCTAssert.h>

@implementation RNCOnInsetsChangeEvent {
UIEdgeInsets _insets;
CGRect _frame;
uint16_t _coalescingKey;
}

@synthesize eventName = _eventName;
@synthesize viewTag = _viewTag;

- (instancetype)initWithEventName:(NSString *)eventName
reactTag:(NSNumber *)reactTag
insets:(UIEdgeInsets)insets
frame:(CGRect)frame
coalescingKey:(uint16_t)coalescingKey
{
RCTAssertParam(reactTag);

if ((self = [super init])) {
_eventName = [eventName copy];
_viewTag = reactTag;
_frame = frame;
_insets = insets;
_coalescingKey = coalescingKey;
}

return self;
}

RCT_NOT_IMPLEMENTED(-(instancetype)init)

- (uint16_t)coalescingKey
{
return _coalescingKey;
}

- (NSDictionary *)body
{
NSDictionary *body = @{
@"insets" : @{
@"top" : @(_insets.top),
@"right" : @(_insets.right),
@"bottom" : @(_insets.bottom),
@"left" : @(_insets.left),
},
@"frame" : @{
@"x" : @(_frame.origin.x),
@"y" : @(_frame.origin.y),
@"width" : @(_frame.size.width),
@"height" : @(_frame.size.height),
},
};

return body;
}

- (BOOL)canCoalesce
{
return YES;
}

- (RNCOnInsetsChangeEvent *)coalesceWithEvent:(RNCOnInsetsChangeEvent *)newEvent
{
return newEvent;
}

+ (NSString *)moduleDotMethod
{
return @"RCTEventEmitter.receiveEvent";
}

- (NSArray *)arguments
{
return @[ self.viewTag, RCTNormalizeInputEventName(self.eventName), [self body] ];
}

@end
6 changes: 6 additions & 0 deletions ios/RNCSafeAreaProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@
#import <AppKit/AppKit.h>
#endif

#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTView.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNCSafeAreaProvider : RCTView

- (instancetype)initWithEventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher NS_DESIGNATED_INITIALIZER;

// NOTE: currently these event props are only declared so we can export the
// event names to JS - we don't call the blocks directly because events
// need to be coalesced before sending, for performance reasons.
@property (nonatomic, copy) RCTBubblingEventBlock onInsetsChange;

@end
Expand Down
48 changes: 31 additions & 17 deletions ios/RNCSafeAreaProvider.m
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
#import "RNCSafeAreaProvider.h"

#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTUIManager.h>
#import "RCTUIManagerObserverCoordinator.h"
#import "RNCOnInsetsChangeEvent.h"
#import "RNCSafeAreaUtils.h"

@interface RNCSafeAreaProvider () <RCTUIManagerObserver>

@end

@implementation RNCSafeAreaProvider {
id<RCTEventDispatcherProtocol> _eventDispatcher;
UIEdgeInsets _currentSafeAreaInsets;
CGRect _currentFrame;
BOOL _initialInsetsSent;
}

- (instancetype)init
- (instancetype)initWithEventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher
{
if ((self = [super init])) {
RCTAssertParam(eventDispatcher);

if ((self = [super initWithFrame:CGRectZero])) {
#if !TARGET_OS_TV && !TARGET_OS_OSX

_eventDispatcher = eventDispatcher;

[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(invalidateSafeAreaInsets)
name:UIKeyboardDidShowNotification
Expand Down Expand Up @@ -58,6 +71,7 @@ - (void)invalidateSafeAreaInsets
safeAreaInsets = NSEdgeInsetsZero;
}
#endif

CGRect frame = [self convertRect:self.bounds toView:RNCParentViewController(self).view];

if (_initialInsetsSent &&
Expand All @@ -69,27 +83,19 @@ - (void)invalidateSafeAreaInsets
CGRectEqualToRect(frame, _currentFrame)) {
return;
}

_initialInsetsSent = YES;
_currentSafeAreaInsets = safeAreaInsets;
_currentFrame = frame;

[NSNotificationCenter.defaultCenter postNotificationName:RNCSafeAreaDidChange object:self userInfo:nil];

self.onInsetsChange(@{
@"insets" : @{
@"top" : @(safeAreaInsets.top),
@"right" : @(safeAreaInsets.right),
@"bottom" : @(safeAreaInsets.bottom),
@"left" : @(safeAreaInsets.left),
},
@"frame" : @{
@"x" : @(frame.origin.x),
@"y" : @(frame.origin.y),
@"width" : @(frame.size.width),
@"height" : @(frame.size.height),
},
});
RNCOnInsetsChangeEvent *onInsetsChangeEvent = [[RNCOnInsetsChangeEvent alloc] initWithEventName:@"onInsetsChange"
reactTag:self.reactTag
insets:safeAreaInsets
frame:frame
coalescingKey:0];

[_eventDispatcher sendEvent:onInsetsChangeEvent];
}

- (void)layoutSubviews
Expand All @@ -99,4 +105,12 @@ - (void)layoutSubviews
[self invalidateSafeAreaInsets];
}

RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)

- (void)dealloc
{
[_eventDispatcher.bridge.uiManager.observerCoordinator removeObserver:self];
}

@end
2 changes: 1 addition & 1 deletion ios/RNCSafeAreaProviderManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ - (UIView *)view
- (NSView *)view
#endif
{
return [RNCSafeAreaProvider new];
return [[RNCSafeAreaProvider alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
}

@end
6 changes: 6 additions & 0 deletions ios/RNSafeAreaContext.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
0C7844F027C02D03001807FB /* RNCSafeAreaProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C7844ED27C02D03001807FB /* RNCSafeAreaProvider.m */; };
AA53A9EE2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m in Sources */ = {isa = PBXBuildFile; fileRef = AA53A9ED2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m */; };
C923EDBC220C2C1A00D3100F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C923EDBB220C2C1A00D3100F /* SystemConfiguration.framework */; };
D697AA982D6F1D0A009C6433 /* RNCChangeEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D697AA972D6F1D08009C6433 /* RNCChangeEvent.m */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -59,6 +60,8 @@
AA53A9EC2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCSafeAreaViewEdgeModes.h; sourceTree = "<group>"; };
AA53A9ED2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCSafeAreaViewEdgeModes.m; sourceTree = "<group>"; };
C923EDBB220C2C1A00D3100F /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
D697AA962D6F1CE5009C6433 /* RNCChangeEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCChangeEvent.h; sourceTree = "<group>"; };
D697AA972D6F1D08009C6433 /* RNCChangeEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCChangeEvent.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -84,6 +87,8 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
D697AA972D6F1D08009C6433 /* RNCChangeEvent.m */,
D697AA962D6F1CE5009C6433 /* RNCChangeEvent.h */,
AA53A9EC2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.h */,
AA53A9ED2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m */,
0C7844EE27C02D03001807FB /* Fabric */,
Expand Down Expand Up @@ -183,6 +188,7 @@
0C7844E827C02CEE001807FB /* RNCSafeAreaView.m in Sources */,
0C7844E627C02CEE001807FB /* RNCSafeAreaViewEdges.m in Sources */,
0C7844E527C02CEE001807FB /* RNCSafeAreaViewManager.m in Sources */,
D697AA982D6F1D0A009C6433 /* RNCChangeEvent.m in Sources */,
0C7844EF27C02D03001807FB /* RNCSafeAreaContext.mm in Sources */,
0C7844E127C02CEE001807FB /* RNCSafeAreaViewLocalData.m in Sources */,
0C7844E227C02CEE001807FB /* RNCSafeAreaViewMode.m in Sources */,
Expand Down