Skip to content

Commit

Permalink
feat: add FullWindowOverlay component for Fabric (#1495)
Browse files Browse the repository at this point in the history
* bookmark: fabric-full-window-overlay INIT

* chore: adapt RNSFullWindowOverlay native interface to Fabric

* chore: initial adaptation of RNSFullWindowOverlay implementation

WIP

* chore: add FullWindowOverlay to Fabric JS

* chore: add componentDescriptorProvider method & RNSFullWindowOverlayCls

* chore: exclude RNSFullWindowOverlayManager#view method from Fabric

* chore: add missing headers

* fix: init props

* chore: add prepareForRecycle method

However more extensive logic may be required for Fabric as it seems to
me, that _container wont be initialized for the second time

* chore: move reactSetFrame method to paper specific section

* chore: mount child component view & update layout metrics

* fix: add maybeShow method.

When the component unmounts we remove it from window's children,
so when the component gets recycled we need to add it back.

Also see comments added in the code.
  • Loading branch information
kkafar authored Jun 29, 2022
1 parent 24393d3 commit 16289ce
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 9 deletions.
19 changes: 17 additions & 2 deletions ios/RNSFullWindowOverlay.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#import <React/RCTViewManager.h>

#ifdef RN_FABRIC_ENABLED
#import <React/RCTViewComponentView.h>
#else
#import <React/RCTInvalidating.h>
#import <React/RCTView.h>
#import <React/RCTViewManager.h>
#endif

@interface RNSFullWindowOverlayManager : RCTViewManager

Expand All @@ -10,6 +15,16 @@

@end

@interface RNSFullWindowOverlay : RCTView <RCTInvalidating>
@interface RNSFullWindowOverlay :
#ifdef RN_FABRIC_ENABLED
RCTViewComponentView
#else
RCTView <RCTInvalidating>
#endif // RN_FABRIC_ENABLED

#ifdef RN_FABRIC_ENABLED
@property (nonatomic) facebook::react::LayoutMetrics oldLayoutMetrics;
@property (nonatomic) facebook::react::LayoutMetrics newLayoutMetrics;
#endif // RN_FABRIC_ENABLED

@end
113 changes: 107 additions & 6 deletions ios/RNSFullWindowOverlay.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

#import "RNSFullWindowOverlay.h"

#ifdef RN_FABRIC_ENABLED
#import <React/RCTConversions.h>
#import <React/RCTSurfaceTouchHandler.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
#import <react/renderer/components/rnscreens/Props.h>
#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
#import "RCTFabricComponentsPlugins.h"
#else
#import <React/RCTTouchHandler.h>
#endif // RN_FABRIC_ENABLED

@implementation RNSFullWindowOverlayContainer

Expand All @@ -22,25 +31,40 @@ @implementation RNSFullWindowOverlay {
__weak RCTBridge *_bridge;
RNSFullWindowOverlayContainer *_container;
CGRect _reactFrame;
#ifdef RN_FABRIC_ENABLED
RCTSurfaceTouchHandler *_touchHandler;
#else
RCTTouchHandler *_touchHandler;
#endif // RN_FABRIC_ENABLED
}

#ifdef RN_FABRIC_ENABLED
- (instancetype)init
{
if (self = [super init]) {
static const auto defaultProps = std::make_shared<const facebook::react::RNSFullWindowOverlayProps>();
_props = defaultProps;
[self _initCommon];
}
return self;
}
#endif // RN_FABRIC_ENABLED

- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super init]) {
_bridge = bridge;
_reactFrame = CGRectNull;
_container = self.container;
[self show];
[self _initCommon];
}

return self;
}

- (void)reactSetFrame:(CGRect)frame
- (void)_initCommon
{
_reactFrame = frame;
[_container setFrame:frame];
_reactFrame = CGRectNull;
_container = self.container;
[self show];
}

- (void)addSubview:(UIView *)view
Expand Down Expand Up @@ -72,27 +96,104 @@ - (void)didMoveToWindow
}
} else {
if (_touchHandler == nil) {
#ifdef RN_FABRIC_ENABLED
_touchHandler = [RCTSurfaceTouchHandler new];
#else
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
#endif
}
[_touchHandler attachToView:_container];
}
}

#ifdef RN_FABRIC_ENABLED
#pragma mark - Fabric Specific

// When the component unmounts we remove it from window's children,
// so when the component gets recycled we need to add it back.
- (void)maybeShow
{
UIWindow *window = RCTSharedApplication().delegate.window;
if (![[window subviews] containsObject:self]) {
[window addSubview:_container];
}
}

+ (facebook::react::ComponentDescriptorProvider)componentDescriptorProvider
{
return facebook::react::concreteComponentDescriptorProvider<
facebook::react::RNSFullWindowOverlayComponentDescriptor>();
}

- (void)prepareForRecycle
{
[_container removeFromSuperview];
// Due to view recycling we don't really want to set _container = nil
// as it won't be instantiated when the component appears for the second time.
// We could consider nulling in here & using container (lazy getter) everywhere else.
// _container = nil;
[super prepareForRecycle];
}

- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
// When the component unmounts we remove it from window's children,
// so when the component gets recycled we need to add it back.
// As for now it is called here as we lack of method that is called
// just before component gets restored (from recycle pool).
[self maybeShow];
[self addSubview:childComponentView];
}

- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
[childComponentView removeFromSuperview];
}

- (void)updateLayoutMetrics:(facebook::react::LayoutMetrics const &)layoutMetrics
oldLayoutMetrics:(facebook::react::LayoutMetrics const &)oldLayoutMetrics
{
CGRect frame = RCTCGRectFromRect(layoutMetrics.frame);
_reactFrame = frame;
[_container setFrame:frame];
}

#else
#pragma mark - Paper specific

- (void)reactSetFrame:(CGRect)frame
{
_reactFrame = frame;
[_container setFrame:frame];
}

- (void)invalidate
{
[_container removeFromSuperview];
_container = nil;
}

#endif // RN_FABRIC_ENABLED

@end

#ifdef RN_FABRIC_ENABLED
Class<RCTComponentViewProtocol> RNSFullWindowOverlayCls(void)
{
return RNSFullWindowOverlay.class;
}
#endif // RN_FABRIC_ENABLED

@implementation RNSFullWindowOverlayManager

RCT_EXPORT_MODULE()

#ifdef RN_FABRIC_ENABLED
#else
- (UIView *)view
{
return [[RNSFullWindowOverlay alloc] initWithBridge:self.bridge];
}
#endif // RN_FABRIC_ENABLED

@end
13 changes: 13 additions & 0 deletions src/fabric/FullWindowOverlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import FullWindowOverlayNativeComponent from './FullWindowOverlayNativeComponent';

function FullWindowOverlay(props) {
return (
<FullWindowOverlayNativeComponent
{...props}
style={[{ flex: 1 }, props.style]}
/>
);
}

export default FullWindowOverlay;
19 changes: 19 additions & 0 deletions src/fabric/FullWindowOverlayNativeComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @flow strict-local
* @format
*/
/* eslint-disable */
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes';
import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';

type NativeProps = $ReadOnly<{|
...ViewProps,
|}>;

type ComponentType = HostComponent<NativeProps>;

export default (codegenNativeComponent<NativeProps>(
'RNSFullWindowOverlay',
{}
): ComponentType);
2 changes: 2 additions & 0 deletions src/fabric/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ScreenStack from './ScreenStack';
import SearchBar from './SearchBar';
import ScreenContainer from './ScreenContainer';
import ScreenNavigationContainer from './ScreenNavigationContainer';
import FullWindowOverlay from './FullWindowOverlay';

export {
Screen,
Expand All @@ -14,4 +15,5 @@ export {
SearchBar,
ScreenContainer,
ScreenNavigationContainer,
FullWindowOverlay,
};
4 changes: 3 additions & 1 deletion src/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ const ScreensNativeModules = {

get NativeFullWindowOverlay() {
NativeFullWindowOverlay =
NativeFullWindowOverlay || requireNativeComponent('RNSFullWindowOverlay');
NativeFullWindowOverlay ||
FabricComponents.FullWindowOverlay ||
requireNativeComponent('RNSFullWindowOverlay');
return NativeFullWindowOverlay;
},
};
Expand Down

0 comments on commit 16289ce

Please sign in to comment.