Skip to content

Commit

Permalink
⭐️ Impl: ModalSheetView.presentModal
Browse files Browse the repository at this point in the history
  • Loading branch information
dominicstop committed Sep 26, 2024
1 parent cd488bf commit 1f05e96
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 51 deletions.
14 changes: 6 additions & 8 deletions ios/RNIModalSheetView/RNIModalSheetView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@
#import "RNIModalSheetView.h"

#import "react-native-ios-modal/Swift.h"
#import <react-native-ios-utilities/RNIBaseView.h>
#import "react-native-ios-utilities/RNIBaseView.h"
#import "react-native-ios-utilities/RNIContentViewParentDelegate.h"

#import <react-native-ios-utilities/RNIContentViewParentDelegate.h>


#import <react-native-ios-utilities/UIApplication+RNIHelpers.h>
#import <react-native-ios-utilities/RNIObjcUtils.h>
#import "react-native-ios-utilities/UIApplication+RNIHelpers.h"
#import "react-native-ios-utilities/RNIObjcUtils.h"

#if RCT_NEW_ARCH_ENABLED
#include "RNIModalSheetViewComponentDescriptor.h"

#include <react-native-ios-utilities/RNIBaseViewState.h>
#include <react-native-ios-utilities/RNIBaseViewProps.h>
#include "react-native-ios-utilities/RNIBaseViewState.h"
#include "react-native-ios-utilities/RNIBaseViewProps.h"

#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
Expand Down
117 changes: 117 additions & 0 deletions ios/RNIModalSheetView/RNIModalSheetViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// RNIModalSheetViewController.swift
// react-native-ios-modal
//
// Created by Dominic Go on 9/26/24.
//

import UIKit
import react_native_ios_utilities

#if !RCT_NEW_ARCH_ENABLED
import React
#endif


open class RNIModalSheetViewController: UIViewController {

public var shouldTriggerDefaultCleanup = true;

public weak var mainSheetContentParent: RNIContentViewParentDelegate?;
private(set) public weak var mainSheetContent: RNIWrapperViewContent?;

public var positionConfigForMainSheetContent: AlignmentPositionConfig = .default;

// MARK: - View Controller Lifecycle
// ---------------------------------

public override func viewDidLoad() {

guard let mainSheetContentParent = self.mainSheetContentParent,
let mainSheetContent = mainSheetContentParent.contentDelegate as? RNIWrapperViewContent
else {
return;
};

self.mainSheetContent = mainSheetContent;
mainSheetContentParent.reactViewLifecycleDelegates.add(self);

// MARK: Setup Constraints
#if !RCT_NEW_ARCH_ENABLED
rootReactView.removeAllAncestorConstraints();
#endif

self.view.addSubview(mainSheetContent);
mainSheetContent.translatesAutoresizingMaskIntoConstraints = false;

let constraints = self.positionConfigForMainSheetContent.createConstraints(
forView: mainSheetContent,
attachingTo: self.view,
enclosingView: self.view
);

NSLayoutConstraint.activate(constraints);

// MARK: Set Initial Size
let hasValidSize = !self.view.bounds.size.isZero;
if hasValidSize {
self.positionConfigForMainSheetContent.setIntrinsicContentSizeOverrideIfNeeded(
forRootReactView: mainSheetContentParent,
withSize: self.view.bounds.size
);
};

let shouldSetSize =
hasValidSize
&& self.positionConfigForMainSheetContent.isStretchingOnBothAxis;

if shouldSetSize {
self.positionConfigForMainSheetContent.applySize(
toRootReactView: mainSheetContentParent,
attachingTo: self.view
);
};
};

public override func viewDidLayoutSubviews() {
guard let mainSheetContentParent = self.mainSheetContentParent else {
return;
};

self.positionConfigForMainSheetContent.setIntrinsicContentSizeOverrideIfNeeded(
forRootReactView: mainSheetContentParent,
withSize: self.view.bounds.size
);

self.positionConfigForMainSheetContent.applySize(
toRootReactView: mainSheetContentParent,
attachingTo: self.view
);
};

// MARK: Methods
// --------------
};

extension RNIModalSheetViewController: RNIViewLifecycle {

public func notifyOnRequestForCleanup(sender: RNIContentViewParentDelegate) {
guard self.shouldTriggerDefaultCleanup,
self.view.window != nil
else {
return;
};

if self.presentingViewController != nil {
self.dismiss(animated: true);

} else if self.parent != nil {
self.willMove(toParent: nil);
self.view.removeFromSuperview();
self.removeFromParent();

} else {
self.view.removeFromSuperview();
};
};
};
91 changes: 61 additions & 30 deletions ios/RNIModalSheetView/RNIModalSheetViewDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
//

import UIKit
import react_native_ios_utilities
import DGSwiftUtilities
import react_native_ios_utilities

@objc(RNIModalSheetViewDelegate)
public final class RNIModalSheetViewDelegate: UIView, RNIContentView {
Expand All @@ -30,6 +30,8 @@ public final class RNIModalSheetViewDelegate: UIView, RNIContentView {

public weak var parentReactView: RNIContentViewParentDelegate?;

public var detachedModalContentParentViews: [RNIContentViewParentDelegate] = [];

// MARK: Properties - Props
// ------------------------

Expand Down Expand Up @@ -60,25 +62,6 @@ public final class RNIModalSheetViewDelegate: UIView, RNIContentView {
parentReactView.setSize(.init(width: 300, height: 300));
};
};

func _setupContent(){
self.backgroundColor = .systemPink;

let label = UILabel();
label.text = "Fabric View (sort of) in Swift";

label.translatesAutoresizingMaskIntoConstraints = false;
self.addSubview(label);

NSLayoutConstraint.activate([
label.centerXAnchor.constraint(
equalTo: self.centerXAnchor
),
label.centerYAnchor.constraint(
equalTo: self.centerYAnchor
),
]);
};
};

extension RNIModalSheetViewDelegate: RNIContentViewDelegate {
Expand All @@ -87,23 +70,30 @@ extension RNIModalSheetViewDelegate: RNIContentViewDelegate {

// MARK: Paper + Fabric
// --------------------

public func notifyOnInit(sender: RNIContentViewParentDelegate) {
self._setupContent();
};

public func notifyOnMountChildComponentView(
sender: RNIContentViewParentDelegate,
childComponentView: UIView,
index: NSInteger,
superBlock: () -> Void
) {
#if !RCT_NEW_ARCH_ENABLED
superBlock();
#endif

guard let parentReactView = parentReactView else {
return;
};

defer {
parentReactView.requestToRemoveReactSubview(childComponentView);
childComponentView.removeFromSuperview();
};

guard let reactView = childComponentView as? RNIContentViewParentDelegate,
reactView.contentDelegate is RNIWrapperViewContent
else {
return;
};

// Note: Window might not be available yet
self.addSubview(childComponentView);
self.detachedModalContentParentViews.append(reactView);
};

public func notifyOnUnmountChildComponentView(
Expand Down Expand Up @@ -137,7 +127,48 @@ extension RNIModalSheetViewDelegate: RNIContentViewDelegate {
resolve resolveBlock: (NSDictionary) -> Void,
reject rejectBlock: (String) -> Void
) {
// no-op

do {
guard let commandArguments = commandArguments as? Dictionary<String, Any> else {
throw RNIUtilitiesError(errorCode: .guardCheckFailed);
};

switch commandName {
case "presentModal":
let closestVC =
self.recursivelyFindNextResponder(withType: UIViewController.self);

guard let closestVC = closestVC else {
throw RNIUtilitiesError(errorCode: .unexpectedNilValue);
};

let mainSheetContentParent =
self.detachedModalContentParentViews.first;

guard let mainSheetContentParent = mainSheetContentParent else {
throw RNIUtilitiesError(errorCode: .unexpectedNilValue);
};

let isAnimated: Bool = commandArguments.getValueFromDictionary(
forKey: "isAnimated",
fallbackValue: true
);

let modalVC = RNIModalSheetViewController();
modalVC.mainSheetContentParent = mainSheetContentParent;
modalVC.view.backgroundColor = .systemBackground;

closestVC.present(modalVC, animated: isAnimated);

resolveBlock([:]);

default:
throw RNIUtilitiesError(errorCode: .invalidValue);
};

} catch {
rejectBlock(error.localizedDescription);
};
};

// MARK: Fabric Only
Expand Down
2 changes: 0 additions & 2 deletions ios/RNIModalSheetView/RNIModalSheetViewManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#import "RNIModalSheetView.h"
#import <objc/runtime.h>

#import "react-native-ios-utilities/RNIBaseViewUtils.h"

#import "RCTBridge.h"
#import <React/RCTViewManager.h>
#import <React/RCTUIManager.h>
Expand Down
6 changes: 3 additions & 3 deletions ios/RNIModalSheetView/RNIModalSheetViewShadowNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
#if __cplusplus
#pragma once

#include <react-native-ios-utilities/RNIBaseViewShadowNode.h>
#include <react-native-ios-utilities/RNIBaseViewProps.h>
#include <react-native-ios-utilities/RNIBaseViewEventEmitter.h>
#include "react-native-ios-utilities/RNIBaseViewShadowNode.h"
#include "react-native-ios-utilities/RNIBaseViewProps.h"
#include "react-native-ios-utilities/RNIBaseViewEventEmitter.h"

#include <react/renderer/components/RNIModalSpec/EventEmitters.h>
#include <react/renderer/components/RNIModalSpec/Props.h>
Expand Down
10 changes: 9 additions & 1 deletion src/components/ModalSheetView/ModalSheetView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ export const ModalSheetView = React.forwardRef<
] = React.useState<ModalSheetContentMap>({});

React.useImperativeHandle(ref, () => ({
presentModal: async () => {
presentModal: async (commandArgs) => {
if(nativeRef.current == null) {
throw Error("Unable to get ref to native sheet");
};

await nativeRef.current.presentModal({
isAnimated: true,
...commandArgs,
});
},
}));

Expand Down
5 changes: 5 additions & 0 deletions src/components/ModalSheetView/ModalSheetViewNativeIDKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


export const MODAL_SHEET_VIEW_CONTENT_NATIVE_ID_KEYS = Object.freeze({
mainSheetContent: 'mainSheetContent',
});
13 changes: 9 additions & 4 deletions src/components/ModalSheetView/ModalSheetViewTypes.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { PropsWithChildren } from "react";
import type { ViewProps } from "react-native";
import type { RemapObject } from "react-native-ios-utilities";

import type { RNIModalSheetViewProps } from "../../native_components/RNIModalSheetVIew";
import type { RNIModalSheetViewProps, RNIModalSheetViewRef } from "../../native_components/RNIModalSheetVIew";


export type ModalSheetViewRef = {
presentModal: () => Promise<void>;
};
export type ModalSheetViewRef = RemapObject<Pick<RNIModalSheetViewRef,
| 'presentModal'
>, {
presentModal: (commandArgs?: {
isAnimated?: boolean;
}) => Promise<void>;
}>;

export type ModalSheetViewInheritedProps = Pick<RNIModalSheetViewProps,
| 'onDidSetViewID'
Expand Down
4 changes: 2 additions & 2 deletions src/native_components/RNIModalSheetVIew/RNIModalSheetView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ export const RNIModalSheetView = React.forwardRef<
getViewID: () => {
return viewID;
},
presentModal: async () => {
presentModal: async (commandArgs) => {
if(viewID == null) return;
const module = Helpers.getRNIUtilitiesModule();

await module.viewCommandRequest(
/* viewID : */ viewID,
/* commandName: */ 'presentModal',
/* commandArgs: */ {}
/* commandArgs: */ commandArgs,
);
},
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import type { RNIModalSheetNativeViewProps } from "./RNIModalSheetNativeView";
export type RNIModalSheetViewRef = {
getViewID: () => StateViewID;
getReactTag: () => StateReactTag;
presentModal: () => Promise<void>;

presentModal: (commandArgs: {
isAnimated: boolean;
}) => Promise<void>;
};

export type RNIModalSheetViewInheritedOptionalProps = Partial<Pick<RNIModalSheetNativeViewProps,
Expand Down

0 comments on commit 1f05e96

Please sign in to comment.