Skip to content
Draft
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
14 changes: 9 additions & 5 deletions apps/src/shared/gamma/containers/stack/StackContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import {
ScreenStackHost,
StackScreen,
StackScreenLifecycleState,
StackScreenHeaderConfig,
} from 'react-native-screens';
import type { StackScreenNativeProps } from 'react-native-screens/components/gamma/StackScreen';
import type { StackScreenHeaderConfigNativeProps } from 'react-native-screens/components/gamma/StackScreenHeaderConfig';

let id = 0;

Expand All @@ -17,7 +19,7 @@ interface StackProps {
}

interface ScreenProps {
// TBA
navigationBar: StackScreenHeaderConfigNativeProps;
}

type Path = {
Expand Down Expand Up @@ -156,11 +158,13 @@ export function StackContainer({ pathConfigs }: StackContainerProps) {
onPop={handlePop}>
<StackNavigationContext.Provider
value={{
...screenProps,
pop,
push,
}}>
...screenProps,
pop,
push,
}}
>
<Component />
<StackScreenHeaderConfig {...(screenProps?.options?.navigationBar ?? {})}/>
</StackNavigationContext.Provider>
</StackScreen>
))}
Expand Down
16 changes: 9 additions & 7 deletions apps/src/tests/TestScreenStack/helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@ import React from 'react';
import { Button } from 'react-native';
import { ScreenLayout } from './ScreenLayout';
import { useStackNavigation } from '../../shared/gamma/containers/stack/StackContainer';
import { ScreenProps } from 'react-native-screens';

export function generateStackWithNames(screenNames: string[]) {
export function generateStackWithNames(screens: {name: string; options?: ScreenProps;}[]) {
const TestComponent = () => {
const navigation = useStackNavigation();

return (
<ScreenLayout>
{screenNames.map(screenName => (
{screens.map(screen => (
<Button
onPress={() => navigation.push(screenName)}
title={`Push ${screenName}`}
key={screenName}
onPress={() => navigation.push(screen.name)}
title={`Push ${screen.name}`}
key={screen.name}
/>
))}
</ScreenLayout>
);
};

return screenNames.map(screenName => ({
name: screenName,
return screens.map(screen => ({
name: screen.name,
component: TestComponent,
options: screen.options,
}));
}
42 changes: 39 additions & 3 deletions apps/src/tests/TestScreenStack/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
import React from 'react';

import { generateStackWithNames } from './helper';
import { StackContainer } from '../../shared/gamma/containers/stack/StackContainer';
import { generateStackWithNames } from './helper';


const config = generateStackWithNames([
{
name: 'A',
options: {
navigationBar: {
title: 'Screen A',
},
},
},
{
name: 'B',
options: {
navigationBar: {
title: 'Screen B',
},
},
},
{
name: 'C',
options: {
navigationBar: {
title: 'Screen C',
},
},
},
]);

export default function App() {
return (
<StackContainer pathConfigs={generateStackWithNames(['A', 'B', 'C'])} />
<StackContainer pathConfigs={config} />
);
}

// import { generateStackWithNames } from './helper';
// import { StackContainer } from './StackContainer';

// export default function App() {
// return (
// <StackContainer config={generateStackWithNames(['A', 'B', 'C'])}/>
// );
// }
2 changes: 1 addition & 1 deletion ios/bottom-tabs/RNSTabBarController.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readwrite) bool needsUpdateOfSelectedTab;

/**
* Tell the controller that some configuration regarding the tab bar apperance has changed & the appearance requires
* Tell the controller that some configuration regarding the tab bar appearance has changed & the appearance requires
* update.
*/
@property (nonatomic, readwrite) bool needsUpdateOfTabBarAppearance;
Expand Down
3 changes: 2 additions & 1 deletion ios/gamma/stack/RNSScreenStackHostComponentView.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

NS_ASSUME_NONNULL_BEGIN

@class RNSStackController;

@interface RNSScreenStackHostComponentView : RNSReactBaseView

@property (nonatomic, nonnull, strong, readonly) RNSStackController *stackController;

- (nonnull NSMutableArray<RNSStackScreenComponentView *> *)reactSubviews;

@end
Expand Down
53 changes: 40 additions & 13 deletions ios/gamma/stack/RNSStackController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import UIKit
@objc
public class RNSStackController: UINavigationController, ReactMountingTransactionObserving {
private var needsChildViewControllersUpdate = false
private var needsNavigationBarAppearanceUpdate = false
private let screenStackHostComponentView: RNSScreenStackHostComponentView

private let navigationAppearanceCoordinator: RNSStackNavigationAppearanceCoordinator

@objc public required init(stackHostComponentView: RNSScreenStackHostComponentView) {
self.screenStackHostComponentView = stackHostComponentView
self.screenStackHostComponentView = stackHostComponentView;
self.navigationAppearanceCoordinator = RNSStackNavigationAppearanceCoordinator()
super.init(nibName: nil, bundle: nil)
}

Expand All @@ -21,6 +24,11 @@ public class RNSStackController: UINavigationController, ReactMountingTransactio
public func setNeedsUpdateOfChildViewControllers() {
needsChildViewControllersUpdate = true
}

@objc
public func setNeedsNavigationBarAppearanceUpdate() {
needsNavigationBarAppearanceUpdate = true
}

// MARK: Updating

Expand All @@ -34,23 +42,41 @@ public class RNSStackController: UINavigationController, ReactMountingTransactio
@objc
public func updateChildViewControllers() {
precondition(
needsChildViewControllersUpdate,
"[RNScreens] Child view controller must be invalidated when update is forced!")
needsChildViewControllersUpdate,
"[RNScreens] Child view controller must be invalidated when update is forced!")

let activeControllers = sourceAllViewControllers()
.filter { screenCtrl in screenCtrl.screenStackComponentView.maxLifecycleState == .attached }
let activeControllers = sourceAllViewControllers()
.filter { screenCtrl in screenCtrl.screenStackComponentView.maxLifecycleState == .attached }

setViewControllers(activeControllers, animated: true)
setViewControllers(activeControllers, animated: true)

needsChildViewControllersUpdate = false
needsChildViewControllersUpdate = false
}

private func sourceAllViewControllers() -> [RNSStackScreenController] {
let screenStackComponents =
screenStackHostComponentView.reactSubviews() as! [RNSStackScreenComponentView]
return screenStackComponents.lazy.map(\.controller)
let screenStackComponents =
screenStackHostComponentView.reactSubviews() as! [RNSStackScreenComponentView]
return screenStackComponents.lazy.map(\.controller)
}

@objc
public func updateNavigationBarAppearanceIfNeeded() {
if needsNavigationBarAppearanceUpdate {
updateNavigationBarAppearance()
}
}


@objc
public func updateNavigationBarAppearance() {
precondition(needsNavigationBarAppearanceUpdate, "[RNScreens] Header appearance must be invalidated when update is forced!")

let currentSubviews = screenStackHostComponentView.reactSubviews() as! [RNSStackScreenComponentView]
let viewControllers = currentSubviews.map { $0.controller }

navigationAppearanceCoordinator.updateNavigationBarAppearance(navigationBar: self.navigationBar, viewControllers: viewControllers);
needsNavigationBarAppearanceUpdate = false
}

// MARK: ReactMountingTransactionObserving

@objc
Expand All @@ -61,5 +87,6 @@ public class RNSStackController: UINavigationController, ReactMountingTransactio
@objc
public func reactMountingTransactionDidMount() {
updateChildViewControllersIfNeeded()
updateNavigationBarAppearanceIfNeeded()
}
}
7 changes: 7 additions & 0 deletions ios/gamma/stack/RNSStackNavigationAppearance.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

@objc
public class RNSStackNavigationAppearance: NSObject {
@objc
public var title: String?
}
21 changes: 21 additions & 0 deletions ios/gamma/stack/RNSStackNavigationAppearanceCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation
import UIKit

@objc
public class RNSStackNavigationAppearanceCoordinator: NSObject {
@objc
func updateNavigationBarAppearance(navigationBar: UINavigationBar, viewControllers: [RNSStackScreenController]) {
for viewController in viewControllers {
if (!viewController.needsNavigationBarAppearanceUpdate) {
continue
}

// TODO: Improve once more props is available
viewController.navigationItem.title = viewController.navigationAppearance?.title

viewController.navigationBarAppearanceDidUpdate()
}
}


}
7 changes: 7 additions & 0 deletions ios/gamma/stack/RNSStackScreenComponentView.mm
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#import "RNSStackScreenComponentView.h"
#import "RNSStackScreenHeaderConfigComponentView.h"

#import <React/RCTConversions.h>
#import <React/RCTMountingTransactionObserving.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
Expand Down Expand Up @@ -58,6 +60,11 @@ - (void)setupController
_controller.view = self;
}

- (void)didMoveToWindow
{
[_controller didMoveToWindow];
}

#pragma mark - Events

- (nonnull RNSStackScreenComponentEventEmitter *)reactEventEmitter
Expand Down
50 changes: 38 additions & 12 deletions ios/gamma/stack/RNSStackScreenController.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Foundation
import UIKit

@objc
public class RNSStackScreenController: UIViewController {
let screenStackComponentView: RNSStackScreenComponentView
public var navigationAppearance: RNSStackNavigationAppearance?
public var needsNavigationBarAppearanceUpdate: Bool = false

private var reactEventEmitter: RNSStackScreenComponentEventEmitter {
return screenStackComponentView.reactEventEmitter()
}

@objc public required init(componentView: RNSStackScreenComponentView) {
self.screenStackComponentView = componentView
super.init(nibName: nil, bundle: nil)
Expand All @@ -19,23 +19,49 @@ public class RNSStackScreenController: UIViewController {

func findStackController() -> RNSStackController? {
if let navCtrl = self.navigationController {
return navCtrl as? RNSStackController
}
return navCtrl as? RNSStackController
}

if let stackHost = self.screenStackComponentView.stackHost {
return stackHost.stackController
}
if let stackHost = self.screenStackComponentView.stackHost {
return stackHost.stackController
}

return nil
return nil
}


@objc func requestNavigationBarAppearanceUpdate() {
let stackController = findStackController()

if (stackController != nil && needsNavigationBarAppearanceUpdate) {
// We're not clearing the flag, as it will be cleared after appearance update by host
stackController?.setNeedsNavigationBarAppearanceUpdate()
}
}

// MARK: Signals

@objc
public func didMoveToWindow() {
requestNavigationBarAppearanceUpdate()
}

@objc
public func setNeedsLifecycleStateUpdate() {
findStackController()?.setNeedsUpdateOfChildViewControllers()
}


@objc
public func setNeedsNavigationBarAppearanceUpdate(_ navigationAppearance: RNSStackNavigationAppearance) {
self.navigationAppearance = navigationAppearance
needsNavigationBarAppearanceUpdate = true
requestNavigationBarAppearanceUpdate()
}

@objc
public func navigationBarAppearanceDidUpdate() {
needsNavigationBarAppearanceUpdate = false
}

// MARK: Events

public override func viewWillAppear(_ animated: Bool) {
Expand Down
15 changes: 15 additions & 0 deletions ios/gamma/stack/RNSStackScreenHeaderConfigComponentView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import "RNSReactBaseView.h"

NS_ASSUME_NONNULL_BEGIN

@interface RNSStackScreenHeaderConfigComponentView : RNSReactBaseView

@property (nonatomic, strong, readonly, nullable) NSString *title;

@end

@interface RNSStackScreenHeaderConfigComponentView()

@end

NS_ASSUME_NONNULL_END
Loading
Loading