From 329c3145aaeaa44f41aebd89c5e70b284cd02e3d Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Tue, 13 Sep 2022 14:19:16 -0400 Subject: [PATCH 1/7] Initial changes --- CONTRIBUTING.md | 27 +++ RNDateTimePicker.podspec | 26 ++- example/App.js | 2 +- example/SegmentedControl.js | 7 + example/ios/Podfile.lock | 2 +- .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + ios/fabric/RNDateTimePickerComponentView.h | 9 + ios/fabric/RNDateTimePickerComponentView.mm | 206 ++++++++++++++++++ package.json | 5 + src/datetimepicker.ios.js | 36 +-- src/picker.ios.js | 8 +- src/specs/DateTimePickerNativeComponent.js | 40 ++++ src/types.js | 10 +- src/utils.js | 9 + 15 files changed, 365 insertions(+), 40 deletions(-) create mode 100644 example/SegmentedControl.js create mode 100644 example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata create mode 100644 example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/fabric/RNDateTimePickerComponentView.h create mode 100644 ios/fabric/RNDateTimePickerComponentView.mm create mode 100644 src/specs/DateTimePickerNativeComponent.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c880b170..bf8c37c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,3 +68,30 @@ An existing Android emulator is required to match the name defined in `detox.con yarn detox:android:build:release yarn detox:android:test:release ``` + +### Fabric + +Fabric is the new React Native rendering system ([read more about it here](https://reactnative.dev/architecture/fabric-renderer)). + +#### iOS + +``` +yarn start +cd "example/ios" && RCT_NEW_ARCH_ENABLED=1 npx pod-install && cd - +yarn start:ios +``` + +If you want to go back to the old renderer (Paper), +remove `ios/build`, run `pod-install` without the `RCT_NEW_ARCH_ENABLED=1` and build again + +``` +rm -r "example/ios/build" +cd "example/ios" && npx pod-install && cd - +yarn start:ios +``` + + +#### Android + +The date time picker does not have a native UI component for Android but a native module. +([read more about native modules here](https://reactnative.dev/docs/native-modules-intro)). \ No newline at end of file diff --git a/RNDateTimePicker.podspec b/RNDateTimePicker.podspec index ae7cb4db..87fe7173 100644 --- a/RNDateTimePicker.podspec +++ b/RNDateTimePicker.podspec @@ -1,5 +1,7 @@ require 'json' +fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1' + package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) Pod::Spec.new do |s| @@ -12,8 +14,28 @@ Pod::Spec.new do |s| s.homepage = package['homepage'] s.platform = :ios, "11.0" s.source = { :git => "https://github.com/react-native-community/datetimepicker", :tag => "v#{s.version}" } - s.source_files = "ios/*.{h,m}" + s.source_files = "ios/**/*.{h,m,mm}" s.requires_arc = true - s.dependency "React-Core" + if fabric_enabled + folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly"', + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + + s.dependency "React" + s.dependency "React-RCTFabric" + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + else + s.exclude_files = "ios/fabric" + + s.dependency "React-Core" + end end diff --git a/example/App.js b/example/App.js index 1b370850..334d2f8f 100644 --- a/example/App.js +++ b/example/App.js @@ -12,7 +12,7 @@ import { Switch, } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; -import SegmentedControl from '@react-native-segmented-control/segmented-control'; +import SegmentedControl from './SegmentedControl'; import {Colors} from 'react-native/Libraries/NewAppScreen'; import React, {useRef, useState} from 'react'; import {Picker} from 'react-native-windows'; diff --git a/example/SegmentedControl.js b/example/SegmentedControl.js new file mode 100644 index 00000000..9186d263 --- /dev/null +++ b/example/SegmentedControl.js @@ -0,0 +1,7 @@ +import {isFabricEnabled} from '../src/utils'; + +import SegmentedControl from '@react-native-segmented-control/segmented-control'; +import JSSegmentedControl from '@react-native-segmented-control/segmented-control/js/SegmentedControl.js'; + +// Forcing the JS implementation for Fabric as the native module is not compatible with Fabric yet. +export default isFabricEnabled ? JSSegmentedControl : SegmentedControl; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 856293bb..e6a644b4 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -570,7 +570,7 @@ SPEC CHECKSUMS: ReactCommon: de55f940495d7bf87b5d7bf55b5b15cdd50d7d7b ReactTestApp-DevSupport: 8a8cff38c37cd8145a12ac7d7d0503dd08f97d65 ReactTestApp-Resources: ff5f151e465e890010b417ce65ca6c5de6aeccbb - RNDateTimePicker: 4f1fc917f5af9d9ae4c5fc0c63a4474d61338693 + RNDateTimePicker: 9d66f002d6095cc89fcb66d0dc54bc6191c9ab0d RNLocalize: cbcb55d0e19c78086ea4eea20e03fe8000bbbced SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 Yoga: 82c9e8f652789f67d98bed5aef9d6653f71b04a9 diff --git a/example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata b/example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..7b3c0ff9 --- /dev/null +++ b/example/ios/date-time-picker-example.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/fabric/RNDateTimePickerComponentView.h b/ios/fabric/RNDateTimePickerComponentView.h new file mode 100644 index 00000000..9b54089e --- /dev/null +++ b/ios/fabric/RNDateTimePickerComponentView.h @@ -0,0 +1,9 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RNDateTimePickerComponentView : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm new file mode 100644 index 00000000..e3230855 --- /dev/null +++ b/ios/fabric/RNDateTimePickerComponentView.mm @@ -0,0 +1,206 @@ +#import "RNDateTimePickerComponentView.h" + +#import + +#import +#import +#import +#import + +#import "RCTFabricComponentsPlugins.h" +#import "RNDateTimePicker.h" + +using namespace facebook::react; + +@interface RNDateTimePickerComponentView () +@end + +@implementation RNDateTimePickerComponentView { + UIDatePicker *_datePickerView; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + + _datePickerView = [[RNDateTimePicker alloc] initWithFrame:self.bounds]; + + [_datePickerView addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged]; + + // Default Picker mode + _datePickerView.datePickerMode = UIDatePickerModeDate; + + self.contentView = _datePickerView; + } + + return self; +} + +-(void)onChange:(RNDateTimePicker *)sender +{ + if (!_eventEmitter) { + return; + } + + NSTimeInterval timestamp = [sender.date timeIntervalSince1970]; + RNDateTimePickerEventEmitter::OnChange event = { + // Sending time in milliseconds + .timestamp = timestamp * 1000 + }; + + std::dynamic_pointer_cast(_eventEmitter) + ->onChange(event); +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +// JS Standard for time is milliseconds +NSDate* convertJSTimeToDate (double jsTime) { + double time = jsTime/1000.0; + return [NSDate dateWithTimeIntervalSince1970: time]; +} + +-(void)updateTextColor:(UIColor *)color +{ + if (@available(iOS 14.0, *)) { + if (_datePickerView.datePickerStyle != UIDatePickerStyleWheels) { + // prevents #247 + return; + } + } + + if (color == nil) { + // Default Text color + if (@available(iOS 13.0, *)) { + color = [UIColor labelColor]; + } else { + color = [UIColor blackColor]; + } + } + + [_datePickerView setValue:color forKey:@"textColor"]; + [_datePickerView setValue:@(NO) forKey:@"highlightsToday"]; +} + +- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps +{ + const auto &oldPickerProps = *std::static_pointer_cast(_props); + const auto &newPickerProps = *std::static_pointer_cast(props); + + if (oldPickerProps.date != newPickerProps.date) { + _datePickerView.date = convertJSTimeToDate(newPickerProps.date); + } + + if (oldPickerProps.minimumDate != newPickerProps.minimumDate) { + _datePickerView.minimumDate = convertJSTimeToDate(newPickerProps.minimumDate); + } + + if (oldPickerProps.maximumDate != newPickerProps.maximumDate) { + _datePickerView.maximumDate = convertJSTimeToDate(newPickerProps.maximumDate); + } + + if (oldPickerProps.locale != newPickerProps.locale) { + NSString *convertedLocale = RCTNSStringFromString(newPickerProps.locale); + NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:convertedLocale]; + + _datePickerView.locale = locale; + } + + if (oldPickerProps.mode != newPickerProps.mode) { + switch(newPickerProps.mode) { + case RNDateTimePickerMode::Time: + _datePickerView.datePickerMode = UIDatePickerModeTime; + break; + case RNDateTimePickerMode::Datetime: + _datePickerView.datePickerMode = UIDatePickerModeDateAndTime; + break; + case RNDateTimePickerMode::Countdown: + _datePickerView.datePickerMode = UIDatePickerModeCountDownTimer; + break; + default: + _datePickerView.datePickerMode = UIDatePickerModeDate; + } + } + + if (@available(iOS 14.0, *)) { + if (oldPickerProps.displayIOS != newPickerProps.displayIOS) { + switch(newPickerProps.displayIOS) { + case RNDateTimePickerDisplayIOS::Compact: + _datePickerView.preferredDatePickerStyle = UIDatePickerStyleCompact; + break; + case RNDateTimePickerDisplayIOS::Inline: + _datePickerView.preferredDatePickerStyle = UIDatePickerStyleInline; + break; + case RNDateTimePickerDisplayIOS::Spinner: + _datePickerView.preferredDatePickerStyle = UIDatePickerStyleWheels; + break; + default: + _datePickerView.preferredDatePickerStyle = UIDatePickerStyleAutomatic; + } + } + } + + if (oldPickerProps.minuteInterval != newPickerProps.minuteInterval) { + _datePickerView.minuteInterval = newPickerProps.minuteInterval; + } + + if (oldPickerProps.timeZoneOffsetInMinutes != newPickerProps.timeZoneOffsetInMinutes) { + // JS standard for time zones is minutes. + _datePickerView.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:newPickerProps.timeZoneOffsetInMinutes * 60.0]; + } + + if (oldPickerProps.accentColor != newPickerProps.accentColor) { + UIColor *color = RCTUIColorFromSharedColor(newPickerProps.accentColor); + + if (color != nil) { + [_datePickerView setTintColor:color]; + } else { + if (@available(iOS 15.0, *)) { + [_datePickerView setTintColor:[UIColor tintColor]]; + } else { + [_datePickerView setTintColor:[UIColor systemBlueColor]]; + } + } + } + + if (oldPickerProps.textColor != newPickerProps.textColor) { + [self updateTextColor:RCTUIColorFromSharedColor(newPickerProps.textColor)]; + } + + if (@available(iOS 13.0, *)) { + if (oldPickerProps.themeVariant != newPickerProps.themeVariant) { + switch (newPickerProps.themeVariant) { + case RNDateTimePickerThemeVariant::Light: + _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight; + break; + case RNDateTimePickerThemeVariant::Dark: + _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark; + break; + default: + _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified; + } + } + } + + if (oldPickerProps.enabled != newPickerProps.enabled) { + _datePickerView.enabled = newPickerProps.enabled; + } + + + [super updateProps:props oldProps:oldProps]; +} + +@end + +Class RNDateTimePickerCls(void) +{ + return RNDateTimePickerComponentView.class; +} + diff --git a/package.json b/package.json index 13cae3c9..122cf661 100644 --- a/package.json +++ b/package.json @@ -144,5 +144,10 @@ } } } + }, + "codegenConfig": { + "name": "RNDateTimePicker", + "type": "components", + "jsSrcsDir": "src/specs" } } diff --git a/src/datetimepicker.ios.js b/src/datetimepicker.ios.js index 5eaa7683..b4b7dc10 100644 --- a/src/datetimepicker.ios.js +++ b/src/datetimepicker.ios.js @@ -10,20 +10,22 @@ * @flow strict-local */ import RNDateTimePicker from './picker'; -import {sharedPropsValidation, toMilliseconds} from './utils'; +import { + dateToMilliseconds, + isFabricEnabled, + sharedPropsValidation, +} from './utils'; import {IOS_DISPLAY, ANDROID_MODE, EVENT_TYPE_SET} from './constants'; import invariant from 'invariant'; import * as React from 'react'; import {Platform} from 'react-native'; import type { + DateTimePickerEvent, NativeEventIOS, - NativeRef, IOSNativeProps, - DatePickerOptions, IOSDisplay, } from './types'; -import type {DateTimePickerEvent} from './types'; const getDisplaySafe = (display: IOSDisplay): IOSDisplay => { const majorVersionIOS = parseInt(Platform.Version, 10); @@ -60,24 +62,9 @@ export default function Picker({ }: IOSNativeProps): React.Node { sharedPropsValidation({value}); - const _picker: NativeRef = React.useRef(null); + const _picker = React.useRef(null); const display = getDisplaySafe(providedDisplay); - React.useEffect( - function ensureNativeIsInSyncWithJS() { - const {current} = _picker; - - if (value && onChange && current) { - const timestamp = value.getTime(); - // $FlowFixMe Cannot call `current.setNativeProps` because property `setNativeProps` is missing in `AbstractComponent` [1]. - current.setNativeProps({ - date: timestamp, - }); - } - }, - [onChange, value], - ); - const _onChange = (event: NativeEventIOS) => { const timestamp = event.nativeEvent.timestamp; // $FlowFixMe Cannot assign object literal to `unifiedEvent` because number [1] is incompatible with undefined [2] in property `nativeEvent.timestamp`. @@ -90,19 +77,16 @@ export default function Picker({ invariant(value, 'A date or time should be specified as `value`.'); - const dates: DatePickerOptions = {value, maximumDate, minimumDate}; - toMilliseconds(dates, 'value', 'minimumDate', 'maximumDate'); - return ( // $FlowFixMe - dozen of flow errors ; + +type NativeProps = $ReadOnly<{| + ...ViewProps, + onChange?: ?BubblingEventHandler, + maximumDate?: ?Double, + minimumDate?: ?Double, + date?: ?Double, + locale?: ?string, + minuteInterval?: ?Int32, + mode?: WithDefault<'date' | 'time' | 'datetime' | 'countdown', 'date'>, + timeZoneOffsetInMinutes?: ?Double, + textColor?: ?ColorValue, + accentColor?: ?ColorValue, + themeVariant?: WithDefault<'dark' | 'light' | 'unspecified', 'unspecified'>, + displayIOS?: WithDefault< + 'default' | 'spinner' | 'compact' | 'inline', + 'default', + >, + enabled?: WithDefault, +|}>; + +export default (codegenNativeComponent( + 'RNDateTimePicker', +): HostComponent); diff --git a/src/types.js b/src/types.js index 642e13d2..d0513627 100644 --- a/src/types.js +++ b/src/types.js @@ -26,9 +26,11 @@ type Display = $Keys; type AndroidEvtTypes = $Keys; type MinuteInterval = ?(1 | 2 | 3 | 4 | 5 | 6 | 10 | 12 | 15 | 20 | 30); -export type NativeEventIOS = SyntheticEvent<{| - timestamp: number, -|}>; +export type NativeEventIOS = SyntheticEvent< + $ReadOnly<{| + timestamp: number, + |}>, +>; export type DateTimePickerEvent = { type: AndroidEvtTypes, @@ -135,7 +137,7 @@ export type IOSNativeProps = $ReadOnly<{| /** * Sets the preferredDatePickerStyle for picker */ - display?: IOSDisplay, + displayIOS?: IOSDisplay, /** * Is this picker enabled? diff --git a/src/utils.js b/src/utils.js index cc7e87b3..e9522be7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -23,6 +23,13 @@ export function toMilliseconds( }); } +export function dateToMilliseconds(date: ?Date): ?number { + if (!date) { + return; + } + return date.getTime(); +} + export function sharedPropsValidation({value}: {value: ?Date}) { invariant(value, 'A date or time must be specified as `value` prop'); invariant( @@ -30,3 +37,5 @@ export function sharedPropsValidation({value}: {value: ?Date}) { '`value` prop must be an instance of Date object', ); } + +export const isFabricEnabled = global.nativeFabricUIManager !== null; From 081cf8fc3542cbceb496e81410ebafe1e47d6616 Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Thu, 15 Sep 2022 22:31:09 -0400 Subject: [PATCH 2/7] test fabric in CI --- .circleci/config.yml | 38 ++++++++++++++++++++++++++++++++++++++ src/types.js | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23b3274e..668a9b56 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,6 +64,42 @@ jobs: - store_artifacts: path: ./artifacts + e2e_release_ios_fabric: + executor: + name: rn/macos + xcode_version: '13.1.0' + environment: + RCT_NEW_ARCH_ENABLED: 1 + steps: + - checkout + - run: + name: install applesimutils + command: | + HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null + HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils >/dev/null + - rn/ios_simulator_start: + device: 'iPhone 11' + # - rn/yarn_install + - run: + command: yarn install --frozen-lockfile + name: yarn install + - run: + command: yarn bundle:ios + name: bundle js + - rn/pod_install: + pod_install_directory: 'example/ios' + - run: + command: curl https://raw.githubusercontent.com/facebook/react-native/6334ac35ac3cbc2c84b2d46d46ec118bf9bf714d/scripts/find-node.sh > node_modules/react-native/scripts/find-node.sh + name: fix issue with nvm # will be fixed in RN 67 (https://github.com/react-native-community/upgrade-support/issues/138) + - run: + command: yarn detox:ios:build:release + name: build app for e2e tests + - run: + command: yarn detox:ios:test:release + name: run e2e tests + - store_artifacts: + path: ./artifacts + e2e_release_android: executor: name: android/android-machine @@ -125,11 +161,13 @@ workflows: jobs: - analyse_js - e2e_release_ios + - e2e_release_ios_fabric - e2e_release_android - publish: requires: - e2e_release_android - e2e_release_ios + - e2e_release_ios_fabric filters: branches: only: master diff --git a/src/types.js b/src/types.js index d0513627..a531b7bf 100644 --- a/src/types.js +++ b/src/types.js @@ -137,7 +137,7 @@ export type IOSNativeProps = $ReadOnly<{| /** * Sets the preferredDatePickerStyle for picker */ - displayIOS?: IOSDisplay, + display?: IOSDisplay, /** * Is this picker enabled? From ea40cc1adc397eabfbd4d1443fa08362e3742b7c Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Tue, 20 Sep 2022 18:30:31 -0400 Subject: [PATCH 3/7] Sending picker size to shadow node --- RNDateTimePicker.podspec | 9 +- .../RNDateTimePicker/ComponentDescriptors.h | 44 ++++++ .../RNDateTimePickerState.cpp | 7 + .../RNDateTimePicker/RNDateTimePickerState.h | 18 +++ .../RNDateTimePicker/ShadowNodes.cpp | 19 +++ .../components/RNDateTimePicker/ShadowNodes.h | 41 ++++++ ios/fabric/RNDateTimePickerComponentView.mm | 127 ++++++++++++------ 7 files changed, 224 insertions(+), 41 deletions(-) create mode 100644 cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h create mode 100644 cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp create mode 100644 cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h create mode 100644 cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp create mode 100644 cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h diff --git a/RNDateTimePicker.podspec b/RNDateTimePicker.podspec index 87fe7173..d60e0695 100644 --- a/RNDateTimePicker.podspec +++ b/RNDateTimePicker.podspec @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.homepage = package['homepage'] s.platform = :ios, "11.0" s.source = { :git => "https://github.com/react-native-community/datetimepicker", :tag => "v#{s.version}" } - s.source_files = "ios/**/*.{h,m,mm}" + s.source_files = "ios/**/*.{h,m,mm,cpp}" s.requires_arc = true if fabric_enabled @@ -33,6 +33,13 @@ Pod::Spec.new do |s| s.dependency "RCTRequired" s.dependency "RCTTypeSafety" s.dependency "ReactCommon/turbomodule/core" + + s.subspec "cpp" do |ss| + ss.source_files = "cpp/**/*.{cpp,h}" + ss.header_dir = "rndatetimepicker" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/cpp\"" } + end + else s.exclude_files = "ios/fabric" diff --git a/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h b/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h new file mode 100644 index 00000000..80e8c6e5 --- /dev/null +++ b/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h @@ -0,0 +1,44 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateComponentDescriptorH.js + */ + +#pragma once + +#include "ShadowNodes.h" +#include + +namespace facebook { +namespace react { + +class RNDateTimePickerComponentDescriptor final : public ConcreteComponentDescriptor { + public: + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; + + void adopt(ShadowNode::Unshared const &shadowNode) const override { + react_native_assert(std::dynamic_pointer_cast(shadowNode)); + auto pickerShadowNode = std::static_pointer_cast(shadowNode); + + react_native_assert( + std::dynamic_pointer_cast(pickerShadowNode)); + auto layoutableShadowNode = + std::static_pointer_cast(pickerShadowNode); + + auto state = std::static_pointer_cast(shadowNode->getState()); + auto stateData = state->getData(); + + if(stateData.frameSize.width != 0 && stateData.frameSize.height != 0) { + layoutableShadowNode->setSize(Size{stateData.frameSize.width, stateData.frameSize.height}); + } + + ConcreteComponentDescriptor::adopt(shadowNode); + } +}; + +} // namespace react +} // namespace facebook diff --git a/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp new file mode 100644 index 00000000..e1cd3444 --- /dev/null +++ b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp @@ -0,0 +1,7 @@ +#include "RNDateTimePickerState.h" + +namespace facebook { +namespace react { + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h new file mode 100644 index 00000000..661da78c --- /dev/null +++ b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace facebook { +namespace react { + +class RNDateTimePickerState final { + public: + using Shared = std::shared_ptr; + RNDateTimePickerState(){}; + RNDateTimePickerState(Size frameSize_) : frameSize(frameSize_){}; + + Size frameSize{}; +}; + +} // namespace react +} // namespace facebook diff --git a/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp new file mode 100644 index 00000000..da16aad1 --- /dev/null +++ b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp @@ -0,0 +1,19 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateShadowNodeCpp.js + */ + +#include "ShadowNodes.h" + +namespace facebook { +namespace react { + +extern const char RNDateTimePickerComponentName[] = "RNDateTimePicker"; + +} // namespace react +} // namespace facebook diff --git a/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h new file mode 100644 index 00000000..4b176437 --- /dev/null +++ b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h @@ -0,0 +1,41 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateShadowNodeH.js + */ + +#pragma once + +#include "RNDateTimePickerState.h" +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +JSI_EXPORT extern const char RNDateTimePickerComponentName[]; + +/* + * `ShadowNode` for component. + */ +class JSI_EXPORT RNDateTimePickerShadowNode final : public ConcreteViewShadowNode { + +public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; + + static ShadowNodeTraits BaseTraits() { + auto traits = ConcreteViewShadowNode::BaseTraits(); + traits.set(ShadowNodeTraits::Trait::LeafYogaNode); + return traits; + } +}; + +} // namespace react +} // namespace facebook diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm index e3230855..91ed8072 100644 --- a/ios/fabric/RNDateTimePickerComponentView.mm +++ b/ios/fabric/RNDateTimePickerComponentView.mm @@ -1,8 +1,7 @@ #import "RNDateTimePickerComponentView.h" - #import -#import +#import #import #import #import @@ -12,11 +11,20 @@ using namespace facebook::react; +// JS Standard for time is milliseconds +NSDate* convertJSTimeToDate (double jsTime) { + double time = jsTime/1000.0; + return [NSDate dateWithTimeIntervalSince1970: time]; +} + @interface RNDateTimePickerComponentView () @end @implementation RNDateTimePickerComponentView { - UIDatePicker *_datePickerView; + UIDatePicker *_picker; + // Dummy picker to apply prop changes and calculate/update the size before the actual picker gets updated + UIDatePicker *_dummyPicker; + RNDateTimePickerShadowNode::ConcreteState::Shared _state; } - (instancetype)initWithFrame:(CGRect)frame @@ -25,14 +33,16 @@ - (instancetype)initWithFrame:(CGRect)frame static const auto defaultProps = std::make_shared(); _props = defaultProps; - _datePickerView = [[RNDateTimePicker alloc] initWithFrame:self.bounds]; + _picker = [RNDateTimePicker new]; + _dummyPicker = [RNDateTimePicker new]; - [_datePickerView addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged]; + [_picker addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged]; // Default Picker mode - _datePickerView.datePickerMode = UIDatePickerModeDate; + _picker.datePickerMode = UIDatePickerModeDate; + _dummyPicker.datePickerMode = UIDatePickerModeDate; - self.contentView = _datePickerView; + self.contentView = _picker; } return self; @@ -54,6 +64,16 @@ -(void)onChange:(RNDateTimePicker *)sender ->onChange(event); } +- (void) updateMeasurements { + if (_state == nullptr) { + return; + } + + CGSize size = [_dummyPicker sizeThatFits:UILayoutFittingCompressedSize]; + auto newState = RNDateTimePickerState{RCTSizeFromCGSize(size)}; + _state->updateState(std::move(newState)); +} + #pragma mark - RCTComponentViewProtocol + (ComponentDescriptorProvider)componentDescriptorProvider @@ -61,16 +81,16 @@ + (ComponentDescriptorProvider)componentDescriptorProvider return concreteComponentDescriptorProvider(); } -// JS Standard for time is milliseconds -NSDate* convertJSTimeToDate (double jsTime) { - double time = jsTime/1000.0; - return [NSDate dateWithTimeIntervalSince1970: time]; +- (void)prepareForRecycle +{ + [super prepareForRecycle]; + _state.reset(); } --(void)updateTextColor:(UIColor *)color +-(void)updateTextColorForPicker:(UIDatePicker *)picker color:(UIColor *)color { if (@available(iOS 14.0, *)) { - if (_datePickerView.datePickerStyle != UIDatePickerStyleWheels) { + if (picker.datePickerStyle != UIDatePickerStyleWheels) { // prevents #247 return; } @@ -85,114 +105,141 @@ -(void)updateTextColor:(UIColor *)color } } - [_datePickerView setValue:color forKey:@"textColor"]; - [_datePickerView setValue:@(NO) forKey:@"highlightsToday"]; + [picker setValue:color forKey:@"textColor"]; + [picker setValue:@(NO) forKey:@"highlightsToday"]; } -- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps -{ +- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState { + _state = std::static_pointer_cast(state); + + if (oldState == nullptr) { + // Calculate the initial picker measurements + [self updateMeasurements]; + } +} + +- (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { + const auto &oldPickerProps = *std::static_pointer_cast(_props); const auto &newPickerProps = *std::static_pointer_cast(props); + Boolean needsToUpdateMeasurements = false; if (oldPickerProps.date != newPickerProps.date) { - _datePickerView.date = convertJSTimeToDate(newPickerProps.date); + picker.date = convertJSTimeToDate(newPickerProps.date); + needsToUpdateMeasurements = true; } if (oldPickerProps.minimumDate != newPickerProps.minimumDate) { - _datePickerView.minimumDate = convertJSTimeToDate(newPickerProps.minimumDate); + picker.minimumDate = convertJSTimeToDate(newPickerProps.minimumDate); } if (oldPickerProps.maximumDate != newPickerProps.maximumDate) { - _datePickerView.maximumDate = convertJSTimeToDate(newPickerProps.maximumDate); + picker.maximumDate = convertJSTimeToDate(newPickerProps.maximumDate); } if (oldPickerProps.locale != newPickerProps.locale) { NSString *convertedLocale = RCTNSStringFromString(newPickerProps.locale); NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:convertedLocale]; - - _datePickerView.locale = locale; + + picker.locale = locale; + needsToUpdateMeasurements = true; } if (oldPickerProps.mode != newPickerProps.mode) { switch(newPickerProps.mode) { case RNDateTimePickerMode::Time: - _datePickerView.datePickerMode = UIDatePickerModeTime; + picker.datePickerMode = UIDatePickerModeTime; break; case RNDateTimePickerMode::Datetime: - _datePickerView.datePickerMode = UIDatePickerModeDateAndTime; + picker.datePickerMode = UIDatePickerModeDateAndTime; break; case RNDateTimePickerMode::Countdown: - _datePickerView.datePickerMode = UIDatePickerModeCountDownTimer; + picker.datePickerMode = UIDatePickerModeCountDownTimer; break; default: - _datePickerView.datePickerMode = UIDatePickerModeDate; + picker.datePickerMode = UIDatePickerModeDate; } + needsToUpdateMeasurements = true; } if (@available(iOS 14.0, *)) { if (oldPickerProps.displayIOS != newPickerProps.displayIOS) { switch(newPickerProps.displayIOS) { case RNDateTimePickerDisplayIOS::Compact: - _datePickerView.preferredDatePickerStyle = UIDatePickerStyleCompact; + picker.preferredDatePickerStyle = UIDatePickerStyleCompact; break; case RNDateTimePickerDisplayIOS::Inline: - _datePickerView.preferredDatePickerStyle = UIDatePickerStyleInline; + picker.preferredDatePickerStyle = UIDatePickerStyleInline; break; case RNDateTimePickerDisplayIOS::Spinner: - _datePickerView.preferredDatePickerStyle = UIDatePickerStyleWheels; + picker.preferredDatePickerStyle = UIDatePickerStyleWheels; break; default: - _datePickerView.preferredDatePickerStyle = UIDatePickerStyleAutomatic; + picker.preferredDatePickerStyle = UIDatePickerStyleAutomatic; } + needsToUpdateMeasurements = true; } } if (oldPickerProps.minuteInterval != newPickerProps.minuteInterval) { - _datePickerView.minuteInterval = newPickerProps.minuteInterval; + picker.minuteInterval = newPickerProps.minuteInterval; } if (oldPickerProps.timeZoneOffsetInMinutes != newPickerProps.timeZoneOffsetInMinutes) { // JS standard for time zones is minutes. - _datePickerView.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:newPickerProps.timeZoneOffsetInMinutes * 60.0]; + picker.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:newPickerProps.timeZoneOffsetInMinutes * 60.0]; } if (oldPickerProps.accentColor != newPickerProps.accentColor) { UIColor *color = RCTUIColorFromSharedColor(newPickerProps.accentColor); if (color != nil) { - [_datePickerView setTintColor:color]; + [picker setTintColor:color]; } else { if (@available(iOS 15.0, *)) { - [_datePickerView setTintColor:[UIColor tintColor]]; + [picker setTintColor:[UIColor tintColor]]; } else { - [_datePickerView setTintColor:[UIColor systemBlueColor]]; + [picker setTintColor:[UIColor systemBlueColor]]; } } } if (oldPickerProps.textColor != newPickerProps.textColor) { - [self updateTextColor:RCTUIColorFromSharedColor(newPickerProps.textColor)]; + [self updateTextColorForPicker:picker color:RCTUIColorFromSharedColor(newPickerProps.textColor)]; } if (@available(iOS 13.0, *)) { if (oldPickerProps.themeVariant != newPickerProps.themeVariant) { switch (newPickerProps.themeVariant) { case RNDateTimePickerThemeVariant::Light: - _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight; + picker.overrideUserInterfaceStyle = UIUserInterfaceStyleLight; break; case RNDateTimePickerThemeVariant::Dark: - _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark; + picker.overrideUserInterfaceStyle = UIUserInterfaceStyleDark; break; default: - _datePickerView.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified; + picker.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified; } } } if (oldPickerProps.enabled != newPickerProps.enabled) { - _datePickerView.enabled = newPickerProps.enabled; + picker.enabled = newPickerProps.enabled; + } + + return needsToUpdateMeasurements; +} + +- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps +{ + // Updating the dummy first to calculate measurements + Boolean needsToUpdateMeasurements = [self updatePropsForPicker:_dummyPicker props:props oldProps:oldProps]; + + if (needsToUpdateMeasurements) { + [self updateMeasurements]; } + [self updatePropsForPicker:_picker props:props oldProps:oldProps]; [super updateProps:props oldProps:oldProps]; } From ad19c2f439482c9639aeec5b8012505abc1e5e3f Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Wed, 21 Sep 2022 15:44:29 -0400 Subject: [PATCH 4/7] Fix E2E config --- .circleci/config.yml | 6 ------ example/ios/Podfile | 3 ++- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 668a9b56..eaa20ac7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,9 +52,6 @@ jobs: name: bundle js - rn/pod_install: pod_install_directory: 'example/ios' - - run: - command: curl https://raw.githubusercontent.com/facebook/react-native/6334ac35ac3cbc2c84b2d46d46ec118bf9bf714d/scripts/find-node.sh > node_modules/react-native/scripts/find-node.sh - name: fix issue with nvm # will be fixed in RN 67 (https://github.com/react-native-community/upgrade-support/issues/138) - run: command: yarn detox:ios:build:release name: build app for e2e tests @@ -88,9 +85,6 @@ jobs: name: bundle js - rn/pod_install: pod_install_directory: 'example/ios' - - run: - command: curl https://raw.githubusercontent.com/facebook/react-native/6334ac35ac3cbc2c84b2d46d46ec118bf9bf714d/scripts/find-node.sh > node_modules/react-native/scripts/find-node.sh - name: fix issue with nvm # will be fixed in RN 67 (https://github.com/react-native-community/upgrade-support/issues/138) - run: command: yarn detox:ios:build:release name: build app for e2e tests diff --git a/example/ios/Podfile b/example/ios/Podfile index 4f115443..d41b848e 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,6 +1,7 @@ require_relative '../../node_modules/react-native-test-app/test_app' -use_flipper! +# Disabling flipper to prevent this issue https://github.com/facebook/react-native/issues/33764 +use_flipper!(false) workspace 'date-time-picker-example.xcworkspace' diff --git a/package.json b/package.json index 122cf661..aacb6303 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "react": "18.1.0", "react-native": "^0.70.0", "react-native-localize": "^2.2.0", - "react-native-test-app": "^1.6.13", + "react-native-test-app": "^1.6.16", "react-native-windows": "^0.70.0-preview.2", "react-test-renderer": "18.1.0", "semantic-release": "^19.0.3" diff --git a/yarn.lock b/yarn.lock index efb2b4b3..985e5486 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9366,10 +9366,10 @@ react-native-localize@^2.2.0: resolved "https://registry.yarnpkg.com/react-native-localize/-/react-native-localize-2.2.1.tgz#6fe646833691c6ee8a474df3c8b069402cb1dba8" integrity sha512-BuPaQWvxLZG1NrCDGqgAnecDrNQu3LED9/Pyl4H2LwTMHcEngXpE5PfVntW2GiLumdr6nUOkWmMnh8PynZqrsw== -react-native-test-app@^1.6.13: - version "1.6.13" - resolved "https://registry.yarnpkg.com/react-native-test-app/-/react-native-test-app-1.6.13.tgz#8e8359af9975f8c60e79e9b2f7a4575929380ba1" - integrity sha512-x4rkaOUMuP3zzl++uhmEfOyTnJytOf67XRvwQ/N6ZikfnIs2DSSBdmmIhFw4tvfMDm/ptnQ0D+XyfULSebH/VQ== +react-native-test-app@^1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/react-native-test-app/-/react-native-test-app-1.6.16.tgz#258bf3485b40666cee5a16ad1105bd08263684bb" + integrity sha512-Yl3uCIUrbyxnegamsTe9Am5qeftVG63z0sHsk420RqMog6wj+ttvSgrQ/15XXrxWeagNGtme0ub2YZRxTbFY7g== dependencies: ajv "^8.0.0" chalk "^4.1.0" From 8909520f21c310dcba01217d9e67750e6e0ff95b Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Thu, 22 Sep 2022 06:10:43 -0400 Subject: [PATCH 5/7] Update Xcode in CI --- .circleci/config.yml | 37 +------------------------------------ package.json | 6 +++--- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index eaa20ac7..e4492363 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,40 +33,7 @@ jobs: e2e_release_ios: executor: name: rn/macos - xcode_version: '13.1.0' - steps: - - checkout - - run: - name: install applesimutils - command: | - HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null - HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils >/dev/null - - rn/ios_simulator_start: - device: 'iPhone 11' - # - rn/yarn_install - - run: - command: yarn install --frozen-lockfile - name: yarn install - - run: - command: yarn bundle:ios - name: bundle js - - rn/pod_install: - pod_install_directory: 'example/ios' - - run: - command: yarn detox:ios:build:release - name: build app for e2e tests - - run: - command: yarn detox:ios:test:release - name: run e2e tests - - store_artifacts: - path: ./artifacts - - e2e_release_ios_fabric: - executor: - name: rn/macos - xcode_version: '13.1.0' - environment: - RCT_NEW_ARCH_ENABLED: 1 + xcode_version: '14.0.0' steps: - checkout - run: @@ -155,13 +122,11 @@ workflows: jobs: - analyse_js - e2e_release_ios - - e2e_release_ios_fabric - e2e_release_android - publish: requires: - e2e_release_android - e2e_release_ios - - e2e_release_ios_fabric filters: branches: only: master diff --git a/package.json b/package.json index aacb6303..7a16312d 100644 --- a/package.json +++ b/package.json @@ -105,10 +105,10 @@ "configurations": { "ios.sim.debug": { "binaryPath": "example/ios/build/Build/Products/Debug-iphonesimulator/ReactTestApp.app", - "build": "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace example/ios/date-time-picker-example.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 11' -scheme date-time-picker-example -configuration Debug -derivedDataPath example/ios/build -UseModernBuildSystem=YES", + "build": "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace example/ios/date-time-picker-example.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 13' -scheme date-time-picker-example -configuration Debug -derivedDataPath example/ios/build -UseModernBuildSystem=YES", "type": "ios.simulator", "device": { - "type": "iPhone 11" + "type": "iPhone 13" } }, "ios.sim.release": { @@ -116,7 +116,7 @@ "build": "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace example/ios/date-time-picker-example.xcworkspace -sdk iphonesimulator -scheme date-time-picker-example -configuration Release -derivedDataPath example/ios/build -UseModernBuildSystem=YES", "type": "ios.simulator", "device": { - "type": "iPhone 11" + "type": "iPhone 13" } }, "android.emu.debug": { From 963ea83d3017547a989e63f1102287a5dc8e53b3 Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Thu, 22 Sep 2022 09:55:55 -0400 Subject: [PATCH 6/7] Adding more comments --- .../RNDateTimePicker/ComponentDescriptors.h | 6 ++---- .../RNDateTimePickerState.cpp | 5 +++++ .../RNDateTimePicker/RNDateTimePickerState.h | 5 +++++ .../RNDateTimePicker/ShadowNodes.cpp | 6 ++---- .../components/RNDateTimePicker/ShadowNodes.h | 6 ++---- example/ios/Podfile | 5 +++-- ios/fabric/RNDateTimePickerComponentView.h | 4 ++++ ios/fabric/RNDateTimePickerComponentView.mm | 19 ++++++++++++++++++- 8 files changed, 41 insertions(+), 15 deletions(-) diff --git a/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h b/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h index 80e8c6e5..4a1a8e24 100644 --- a/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h +++ b/cpp/react/renderer/components/RNDateTimePicker/ComponentDescriptors.h @@ -1,10 +1,8 @@ /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). - * - * Do not edit this file as changes may cause incorrect behavior and will be lost - * once the code is regenerated. - * + * and copied to the cpp directory to override the adopt function and set the size of the shadow node based + * on the state. * @generated by codegen project: GenerateComponentDescriptorH.js */ diff --git a/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp index e1cd3444..d5177a9e 100644 --- a/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp +++ b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.cpp @@ -1,3 +1,8 @@ +/** + * Custom state to store frameSize that the component descriptor will use to modify the + * shadow node layout. + */ + #include "RNDateTimePickerState.h" namespace facebook { diff --git a/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h index 661da78c..6215e17b 100644 --- a/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h +++ b/cpp/react/renderer/components/RNDateTimePicker/RNDateTimePickerState.h @@ -1,3 +1,8 @@ +/** + * Custom state to store frameSize that the component descriptor will use to modify the + * shadow node layout. + */ + #pragma once #include diff --git a/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp index da16aad1..192850e3 100644 --- a/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp +++ b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.cpp @@ -1,9 +1,7 @@ /** - * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). - * - * Do not edit this file as changes may cause incorrect behavior and will be lost - * once the code is regenerated. + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen) + * and copied to the cpp directory to add custom state and set shadow node trait as a LeafYogaNode. * * @generated by codegen project: GenerateShadowNodeCpp.js */ diff --git a/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h index 4b176437..dd2b6efc 100644 --- a/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h +++ b/cpp/react/renderer/components/RNDateTimePicker/ShadowNodes.h @@ -1,9 +1,7 @@ /** - * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). - * - * Do not edit this file as changes may cause incorrect behavior and will be lost - * once the code is regenerated. + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen) + * and copied to the cpp directory to add custom state and set shadow node trait as a LeafYogaNode. * * @generated by codegen project: GenerateShadowNodeH.js */ diff --git a/example/ios/Podfile b/example/ios/Podfile index d41b848e..cb397779 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,7 +1,8 @@ require_relative '../../node_modules/react-native-test-app/test_app' -# Disabling flipper to prevent this issue https://github.com/facebook/react-native/issues/33764 -use_flipper!(false) +# Flipper causes the build to fail on release when fabric is enabled +# https://github.com/facebook/react-native/issues/33764 +use_flipper!() workspace 'date-time-picker-example.xcworkspace' diff --git a/ios/fabric/RNDateTimePickerComponentView.h b/ios/fabric/RNDateTimePickerComponentView.h index 9b54089e..738a97d3 100644 --- a/ios/fabric/RNDateTimePickerComponentView.h +++ b/ios/fabric/RNDateTimePickerComponentView.h @@ -1,3 +1,7 @@ +/** + * RNDateTimePickerComponentView is only be available when fabric is enabled. + */ + #import NS_ASSUME_NONNULL_BEGIN diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm index 91ed8072..81045c1a 100644 --- a/ios/fabric/RNDateTimePickerComponentView.mm +++ b/ios/fabric/RNDateTimePickerComponentView.mm @@ -1,3 +1,7 @@ +/** + * RNDateTimePickerComponentView is only be available when fabric is enabled. + */ + #import "RNDateTimePickerComponentView.h" #import @@ -64,6 +68,10 @@ -(void)onChange:(RNDateTimePicker *)sender ->onChange(event); } +/** + * Updates the shadow node state with the dummyPicker size. This will update the shadow node size. + * (see adopt method in ComponentDescriptors.h) + */ - (void) updateMeasurements { if (_state == nullptr) { return; @@ -109,6 +117,9 @@ -(void)updateTextColorForPicker:(UIDatePicker *)picker color:(UIColor *)color [picker setValue:@(NO) forKey:@"highlightsToday"]; } +/** + * override update state to update shadow node size once the state is available + */ - (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState { _state = std::static_pointer_cast(state); @@ -118,6 +129,12 @@ - (void)updateState:(const State::Shared &)state oldState:(const State::Shared & } } +/** + * Updates picker properties based on prop changes and returns a boolean that indicates if the shadow node size needs + * to be updated. This boolean helpful when we update the dummy picker to know if we need to update the shadow node + * size before updating the actual picker. + * Props that will to update measurements: date, locale, mode, displayIOS. + */ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { const auto &oldPickerProps = *std::static_pointer_cast(_props); @@ -232,7 +249,7 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - // Updating the dummy first to calculate measurements + // Updating the dummy first to check if we need to update measurements Boolean needsToUpdateMeasurements = [self updatePropsForPicker:_dummyPicker props:props oldProps:oldProps]; if (needsToUpdateMeasurements) { From fbd403682af5a9a049e2ba38757885773b8a3311 Mon Sep 17 00:00:00 2001 From: Alfonso Curbelo Date: Fri, 23 Sep 2022 14:14:08 -0400 Subject: [PATCH 7/7] cleanup --- .gitignore | 1 + example/SegmentedControl.js | 4 ++-- example/ios/Podfile.lock | 1 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 -------- ios/fabric/RNDateTimePickerComponentView.mm | 12 +++++++----- src/datetimepicker.ios.js | 8 +------- src/utils.js | 2 -- 7 files changed, 12 insertions(+), 24 deletions(-) delete mode 100644 example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/.gitignore b/.gitignore index 8d5bc0b8..2145c4cf 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace +IDEWorkspaceChecks.plist #Detox # diff --git a/example/SegmentedControl.js b/example/SegmentedControl.js index 9186d263..01869141 100644 --- a/example/SegmentedControl.js +++ b/example/SegmentedControl.js @@ -1,7 +1,7 @@ -import {isFabricEnabled} from '../src/utils'; - import SegmentedControl from '@react-native-segmented-control/segmented-control'; import JSSegmentedControl from '@react-native-segmented-control/segmented-control/js/SegmentedControl.js'; +const isFabricEnabled = global.nativeFabricUIManager !== null; + // Forcing the JS implementation for Fabric as the native module is not compatible with Fabric yet. export default isFabricEnabled ? JSSegmentedControl : SegmentedControl; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e6a644b4..115e07b7 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -263,6 +263,7 @@ PODS: - React-logger (= 0.70.0) - React-perflogger (= 0.70.0) - React-runtimeexecutor (= 0.70.0) + - RCT-Folly/Fabric (= 2021.07.22.00) - React-jsi (0.70.0): - boost (= 1.76.0) - DoubleConversion diff --git a/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/example/ios/date-time-picker-example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm index 81045c1a..bf0194f9 100644 --- a/ios/fabric/RNDateTimePickerComponentView.mm +++ b/ios/fabric/RNDateTimePickerComponentView.mm @@ -103,18 +103,20 @@ -(void)updateTextColorForPicker:(UIDatePicker *)picker color:(UIColor *)color return; } } - - if (color == nil) { + + if (color) { + [picker setValue:color forKey:@"textColor"]; + [picker setValue:@(NO) forKey:@"highlightsToday"]; + } else { // Default Text color if (@available(iOS 13.0, *)) { color = [UIColor labelColor]; } else { color = [UIColor blackColor]; } + [picker setValue:color forKey:@"textColor"]; + [picker setValue:@(YES) forKey:@"highlightsToday"]; } - - [picker setValue:color forKey:@"textColor"]; - [picker setValue:@(NO) forKey:@"highlightsToday"]; } /** diff --git a/src/datetimepicker.ios.js b/src/datetimepicker.ios.js index b4b7dc10..a75a91b9 100644 --- a/src/datetimepicker.ios.js +++ b/src/datetimepicker.ios.js @@ -10,11 +10,7 @@ * @flow strict-local */ import RNDateTimePicker from './picker'; -import { - dateToMilliseconds, - isFabricEnabled, - sharedPropsValidation, -} from './utils'; +import {dateToMilliseconds, sharedPropsValidation} from './utils'; import {IOS_DISPLAY, ANDROID_MODE, EVENT_TYPE_SET} from './constants'; import invariant from 'invariant'; import * as React from 'react'; @@ -62,7 +58,6 @@ export default function Picker({ }: IOSNativeProps): React.Node { sharedPropsValidation({value}); - const _picker = React.useRef(null); const display = getDisplaySafe(providedDisplay); const _onChange = (event: NativeEventIOS) => { @@ -81,7 +76,6 @@ export default function Picker({ // $FlowFixMe - dozen of flow errors