feat: bump up fabric example react-native iOS (#3951)

* feat: 🔥 version bump react native for ios FabricExample
* feat:  copied newly created basic example TS files
This commit is contained in:
yungblud 2024-07-01 00:20:28 +09:00 committed by GitHub
parent 322d7e993d
commit e5a2ee3bd3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 2733 additions and 1473 deletions

View File

@ -1,6 +1,6 @@
module.exports = {
root: true,
extends: '@react-native-community',
extends: '@react-native',
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
overrides: [

View File

@ -62,3 +62,6 @@ buck-out/
# Ruby / CocoaPods
/ios/Pods/
/vendor/bundle/
# testing
/coverage

View File

@ -1,6 +1,7 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '2.7.5'
ruby ">= 2.6.10"
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
gem 'cocoapods', '~> 1.13'
gem 'activesupport', '>= 6.1.7.3', '< 7.1.0'

View File

@ -1,26 +1,29 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.6)
CFPropertyList (3.0.7)
base64
nkf
rexml
activesupport (7.0.4.3)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
base64 (0.2.0)
claide (1.1.0)
cocoapods (1.12.0)
cocoapods (1.15.2)
addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.12.0)
cocoapods-core (= 1.15.2)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.6.0, < 2.0)
cocoapods-downloader (>= 2.1, < 3.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.6.0, < 2.0)
@ -32,8 +35,8 @@ GEM
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (>= 2.3.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.12.0)
xcodeproj (>= 1.23.0, < 2.0)
cocoapods-core (1.15.2)
activesupport (>= 5.0, < 8)
addressable (~> 2.8)
algoliasearch (~> 1.0)
@ -44,7 +47,7 @@ GEM
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.6.3)
cocoapods-downloader (2.1)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
@ -57,27 +60,30 @@ GEM
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
ffi (1.15.5)
ffi (1.17.0)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
json (2.6.3)
json (2.7.2)
minitest (5.18.0)
molinillo (0.8.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
nkf (0.2.0)
public_suffix (4.0.7)
rexml (3.2.5)
rexml (3.2.9)
strscan
ruby-macho (2.5.1)
typhoeus (1.4.0)
strscan (3.1.0)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
xcodeproj (1.22.0)
xcodeproj (1.24.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
@ -89,7 +95,8 @@ PLATFORMS
ruby
DEPENDENCIES
cocoapods (~> 1.11, >= 1.11.2)
activesupport (>= 6.1.7.3, < 7.1.0)
cocoapods (~> 1.13)
RUBY VERSION
ruby 2.7.5p203

View File

@ -6,6 +6,9 @@ import 'react-native';
import React from 'react';
import App from '../App';
// Note: import explicitly to use the types shipped with jest.
import {it} from '@jest/globals';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';

View File

@ -1 +0,0 @@
16

View File

@ -2,7 +2,7 @@ const path = require('path');
const pak = require('../../package.json');
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
presets: ['module:@react-native/babel-preset'],
plugins: [
[
'module-resolver',

View File

@ -437,7 +437,7 @@
"$(inherited)",
);
INFOPLIST_FILE = FabricExampleTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -461,7 +461,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
INFOPLIST_FILE = FabricExampleTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -535,7 +535,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -580,7 +580,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
@ -592,18 +592,25 @@
);
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = (
"$(inherited)",
"-DRN_FABRIC_ENABLED",
);
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG",
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
"-DRN_FABRIC_ENABLED",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
"-Wl",
"-ld_classic",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
};
name = Debug;
};
@ -612,7 +619,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -653,7 +660,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
@ -664,18 +671,25 @@
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = (
"$(inherited)",
"-DRN_FABRIC_ENABLED",
);
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG",
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
"-DRN_FABRIC_ENABLED",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
"-Wl",
"-ld_classic",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
};
name = Release;

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,8 +1,6 @@
#import <React/RCTBridgeDelegate.h>
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
@property (nonatomic, strong) UIWindow *window;
@interface AppDelegate : RCTAppDelegate
@end

View File

@ -1,88 +1,25 @@
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application, true);
self.moduleName = @"FabricExample";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"FabricExample", initProps, true);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [self getBundleURL];
}
- (NSURL *)getBundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
@ -91,43 +28,4 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
#endif
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end

View File

@ -26,17 +26,13 @@
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<string></string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>

View File

@ -1,28 +1,25 @@
ENV['RCT_NEW_ARCH_ENABLED'] = "1"
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '12.4'
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
platform :ios, '13.4'
install! 'cocoapods', :deterministic_uuids => false
target 'FabricExample' do
config = use_native_modules!
# Flags change depending on the env values.
flags = get_default_flags()
use_react_native!(
:path => config[:reactNativePath],
# Hermes is now enabled by default. Disable by setting this flag to false.
# Upcoming versions of React Native may rely on get_default_flags(), but
# we make it explicit here to aid in the React Native upgrade process.
:hermes_enabled => true,
:fabric_enabled => flags[:fabric_enabled],
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable the next line.
:flipper_configuration => FlipperConfiguration.enabled,
# :flipper_configuration => FlipperConfiguration.enabled,
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
@ -35,10 +32,8 @@ target 'FabricExample' do
post_install do |installer|
react_native_post_install(
installer,
# Set `mac_catalyst_enabled` to `true` in order to apply patches
# necessary for Mac Catalyst builds
config[:reactNativePath],
:mac_catalyst_enabled => false
)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
end
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
module.exports = {
preset: 'react-native',
};

View File

@ -2,6 +2,7 @@ const path = require('path');
const escape = require('escape-string-regexp');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const pak = require('../../package.json');
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const root = path.resolve(__dirname, '../../');
@ -9,7 +10,7 @@ const modules = Object.keys({
...pak.peerDependencies,
});
module.exports = {
const config = {
projectRoot: __dirname,
watchFolders: [root],
@ -37,4 +38,6 @@ module.exports = {
},
}),
},
};
}
module.exports = mergeConfig(getDefaultConfig(__dirname), config);

View File

@ -11,27 +11,33 @@
"pod-install": "bundle exec npx pod-install --project-directory=ios --verbose"
},
"dependencies": {
"@react-native-picker/picker": "2.7.5",
"react": "18.1.0",
"react-native": "^0.71.6"
"react-native": "0.73.2"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"@react-native/babel-preset": "0.73.19",
"@react-native/eslint-config": "0.73.2",
"@react-native/metro-config": "0.73.3",
"@react-native/typescript-config": "0.73.1",
"@tsconfig/react-native": "^2.0.2",
"@types/jest": "^26.0.23",
"@types/react": "^18.0.21",
"@types/react": "^18.2.6",
"@types/react-native": "^0.70.6",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"babel-jest": "^26.6.3",
"babel-jest": "^29.6.3",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^7.32.0",
"jest": "^26.6.3",
"jest": "^29.6.3",
"metro-react-native-babel-preset": "0.72.3",
"prettier": "2.8.8",
"react-test-renderer": "18.1.0",
"typescript": "^4.8.3"
"typescript": "5.0.4"
},
"jest": {
"preset": "react-native",
@ -43,5 +49,8 @@
"json",
"node"
]
},
"engines": {
"node": ">=18"
}
}

View File

@ -0,0 +1,75 @@
import React, {FunctionComponent} from 'react';
import {
StyleSheet,
Text,
TextStyle,
TouchableOpacity,
View,
} from 'react-native';
import {ResizeMode} from 'react-native-video';
export type MultiValueControlPropType = number | string | ResizeMode;
/*
* MultiValueControl displays a list clickable text view
*/
interface MultiValueControlType<T> {
// a list a string or number to be displayed
values: Array<T>;
// The selected value in values
selected?: T;
// callback to press onPress
onPress: (arg: MultiValueControlPropType) => void;
}
const MultiValueControl: FunctionComponent<
MultiValueControlType<MultiValueControlPropType>
> = ({values, selected, onPress}) => {
const selectedStyle: TextStyle = StyleSheet.flatten([
styles.option,
{fontWeight: 'bold'},
]);
const unselectedStyle: TextStyle = StyleSheet.flatten([
styles.option,
{fontWeight: 'normal'},
]);
return (
<View style={styles.container}>
{values.map((value: MultiValueControlPropType) => {
const _style = value === selected ? selectedStyle : unselectedStyle;
return (
<TouchableOpacity
key={value}
onPress={() => {
onPress?.(value);
}}>
<Text style={_style}>{value}</Text>
</TouchableOpacity>
);
})}
</View>
);
};
const styles = StyleSheet.create({
option: {
alignSelf: 'center',
fontSize: 11,
color: 'white',
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
});
export default MultiValueControl;

View File

@ -0,0 +1,73 @@
import React from 'react';
import {
StyleSheet,
Text,
TextStyle,
TouchableOpacity,
View,
} from 'react-native';
/*
* ToggleControl displays a 2 states clickable text
*/
interface ToggleControlType {
// boolean indicating if text is selected state
isSelected?: boolean;
// value of text when selected
selectedText?: string;
// value of text when NOT selected
unselectedText?: string;
// default text if no only one text field is needed
text?: string;
// callback called when pressing the component
onPress: () => void;
}
const ToggleControl = ({
isSelected,
selectedText,
unselectedText,
text,
onPress,
}: ToggleControlType) => {
const selectedStyle: TextStyle = StyleSheet.flatten([
styles.controlOption,
{fontWeight: 'bold'},
]);
const unselectedStyle: TextStyle = StyleSheet.flatten([
styles.controlOption,
{fontWeight: 'normal'},
]);
const style = isSelected ? selectedStyle : unselectedStyle;
const _text = text ? text : isSelected ? selectedText : unselectedText;
return (
<View style={styles.resizeModeControl}>
<TouchableOpacity onPress={onPress}>
<Text style={style}>{_text}</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
controlOption: {
alignSelf: 'center',
fontSize: 11,
color: 'white',
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
resizeModeControl: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
});
export default ToggleControl;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
import {Picker} from '@react-native-picker/picker';
import {Text} from 'react-native';
import {AudioTrack, SelectedTrack} from 'react-native-video';
import styles from '../styles';
import React from 'react';
export interface AudioTrackSelectorType {
audioTracks: Array<AudioTrack>;
selectedAudioTrack: SelectedTrack | undefined;
onValueChange: (arg0: string) => void;
}
const AudioTrackSelector = ({
audioTracks,
selectedAudioTrack,
onValueChange,
}: AudioTrackSelectorType) => {
return (
<>
<Text style={styles.controlOption}>AudioTrack</Text>
<Picker
style={styles.picker}
itemStyle={styles.pickerItem}
selectedValue={selectedAudioTrack?.value}
onValueChange={itemValue => {
if (itemValue !== 'empty') {
console.log('on audio value change ' + itemValue);
onValueChange(`${itemValue}`);
}
}}>
{audioTracks?.length <= 0 ? (
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
) : (
<Picker.Item label={'none'} value={'none'} key={'none'} />
)}
{audioTracks.map(track => {
if (!track) {
return;
}
return (
<Picker.Item
label={`${track.language} - ${track.title} - ${track.selected}`}
value={`${track.index}`}
key={`${track.index}`}
/>
);
})}
</Picker>
</>
);
};
export default AudioTrackSelector;

View File

@ -0,0 +1,154 @@
import React, {useCallback, useEffect, useState} from 'react';
import {PanResponder, View} from 'react-native';
import styles from '../styles';
interface SeekerProps {
currentTime: number;
duration: number;
isLoading: boolean;
isUISeeking: boolean;
videoSeek: (arg0: number) => void;
}
const Seeker = ({
currentTime,
duration,
isLoading,
isUISeeking,
videoSeek,
}: SeekerProps) => {
const [seeking, setSeeking] = useState(false);
const [seekerPosition, setSeekerPosition] = useState(0);
const [seekerWidth, setSeekerWidth] = useState(0);
/**
* Set the position of the seekbar's components
* (both fill and handle) according to the
* position supplied.
*
* @param {float} position position in px of seeker handle}
*/
const updateSeekerPosition = useCallback(
(position = 0) => {
if (position <= 0) {
position = 0;
} else if (position >= seekerWidth) {
position = seekerWidth;
}
setSeekerPosition(position);
},
[seekerWidth],
);
/**
* Return the time that the video should be at
* based on where the seeker handle is.
*
* @return {float} time in ms based on seekerPosition.
*/
const calculateTimeFromSeekerPosition = () => {
const percent = seekerPosition / seekerWidth;
return duration * percent;
};
/**
* Get our seekbar responder going
*/
const seekPanResponder = PanResponder.create({
// Ask to be the responder.
onStartShouldSetPanResponder: (_evt, _gestureState) => true,
onMoveShouldSetPanResponder: (_evt, _gestureState) => true,
/**
* When we start the pan tell the machine that we're
* seeking. This stops it from updating the seekbar
* position in the onProgress listener.
*/
onPanResponderGrant: (evt, _gestureState) => {
const position = evt.nativeEvent.locationX;
updateSeekerPosition(position);
setSeeking(true);
},
/**
* When panning, update the seekbar position, duh.
*/
onPanResponderMove: (evt, _gestureState) => {
const position = evt.nativeEvent.locationX;
updateSeekerPosition(position);
},
/**
* On release we update the time and seek to it in the video.
* If you seek to the end of the video we fire the
* onEnd callback
*/
onPanResponderRelease: (_evt, _gestureState) => {
const time = calculateTimeFromSeekerPosition();
if (time >= duration && !isLoading) {
// FIXME ...
// state.paused = true;
// this.onEnd();
} else {
videoSeek(time);
setSeeking(false);
}
},
});
useEffect(() => {
if (!isLoading && !seeking && !isUISeeking) {
const percent = currentTime / duration;
const position = seekerWidth * percent;
updateSeekerPosition(position);
}
}, [
currentTime,
duration,
isLoading,
seekerWidth,
seeking,
isUISeeking,
updateSeekerPosition,
]);
if (!seekPanResponder) {
return null;
}
const seekerStyle = [
styles.seekbarFill,
{
width: seekerPosition > 0 ? seekerPosition : 0,
backgroundColor: '#FFF',
},
];
const seekerPositionStyle = [
styles.seekbarHandle,
{
left: seekerPosition > 0 ? seekerPosition : 0,
},
];
const seekerPointerStyle = [styles.seekbarCircle, {backgroundColor: '#FFF'}];
return (
<View
style={styles.seekbarContainer}
{...seekPanResponder.panHandlers}
{...styles.generalControls}>
<View
style={styles.seekbarTrack}
onLayout={event => setSeekerWidth(event.nativeEvent.layout.width)}
pointerEvents={'none'}>
<View style={seekerStyle} pointerEvents={'none'} />
</View>
<View style={seekerPositionStyle} pointerEvents={'none'}>
<View style={seekerPointerStyle} pointerEvents={'none'} />
</View>
</View>
);
};
export default Seeker;

View File

@ -0,0 +1,64 @@
import {Picker} from '@react-native-picker/picker';
import {Text} from 'react-native';
import {TextTrack, SelectedTrack} from 'react-native-video';
import styles from '../styles';
import React from 'react';
export interface TextTrackSelectorType {
textTracks: Array<TextTrack>;
selectedTextTrack: SelectedTrack | undefined;
onValueChange: (arg0: string) => void;
textTracksSelectionBy: string;
}
const TextTrackSelector = ({
textTracks,
selectedTextTrack,
onValueChange,
textTracksSelectionBy,
}: TextTrackSelectorType) => {
return (
<>
<Text style={styles.controlOption}>TextTrack</Text>
<Picker
style={styles.picker}
itemStyle={styles.pickerItem}
selectedValue={`${selectedTextTrack?.value}`}
onValueChange={itemValue => {
if (itemValue !== 'empty') {
onValueChange(itemValue);
}
}}>
{textTracks?.length <= 0 ? (
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
) : (
<Picker.Item label={'none'} value={'none'} key={'none'} />
)}
{textTracks.map(track => {
if (!track) {
return;
}
if (textTracksSelectionBy === 'index') {
return (
<Picker.Item
label={`${track.index}`}
value={track.index}
key={track.index}
/>
);
} else {
return (
<Picker.Item
label={track.language}
value={track.language}
key={track.language}
/>
);
}
})}
</Picker>
</>
);
};
export default TextTrackSelector;

View File

@ -0,0 +1,64 @@
import {Picker} from '@react-native-picker/picker';
import {Text} from 'react-native';
import {
SelectedVideoTrack,
SelectedVideoTrackType,
VideoTrack,
} from 'react-native-video';
import styles from '../styles';
import React from 'react';
export interface VideoTrackSelectorType {
videoTracks: Array<VideoTrack>;
selectedVideoTrack: SelectedVideoTrack | undefined;
onValueChange: (arg0: string) => void;
}
const VideoTrackSelector = ({
videoTracks,
selectedVideoTrack,
onValueChange,
}: VideoTrackSelectorType) => {
return (
<>
<Text style={styles.controlOption}>VideoTrack</Text>
<Picker
style={styles.picker}
itemStyle={styles.pickerItem}
selectedValue={
selectedVideoTrack === undefined ||
selectedVideoTrack?.type === SelectedVideoTrackType.AUTO
? 'auto'
: `${selectedVideoTrack?.value}`
}
onValueChange={itemValue => {
if (itemValue !== 'empty') {
onValueChange(itemValue);
}
}}>
<Picker.Item label={'auto'} value={'auto'} key={'auto'} />
{videoTracks?.length <= 0 || videoTracks?.length <= 0 ? (
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
) : (
<Picker.Item label={'none'} value={'none'} key={'none'} />
)}
{videoTracks?.map(track => {
if (!track) {
return;
}
return (
<Picker.Item
label={`${track.width}x${track.height} ${Math.floor(
(track.bitrate || 0) / 8 / 1024,
)} Kbps`}
value={`${track.index}`}
key={track.index}
/>
);
})}
</Picker>
</>
);
};
export default VideoTrackSelector;

Binary file not shown.

View File

@ -0,0 +1,167 @@
import {StyleSheet} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black',
},
halfScreen: {
position: 'absolute',
top: 50,
left: 50,
bottom: 100,
right: 100,
},
fullScreen: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
bottomControls: {
backgroundColor: 'transparent',
borderRadius: 5,
position: 'absolute',
bottom: 20,
left: 20,
right: 20,
},
leftControls: {
backgroundColor: 'transparent',
borderRadius: 5,
position: 'absolute',
top: 20,
bottom: 20,
left: 20,
},
rightControls: {
backgroundColor: 'transparent',
borderRadius: 5,
position: 'absolute',
top: 20,
bottom: 20,
right: 20,
},
topControls: {
backgroundColor: 'transparent',
borderRadius: 4,
position: 'absolute',
top: 20,
left: 20,
right: 20,
flex: 1,
flexDirection: 'row',
overflow: 'hidden',
paddingBottom: 10,
},
generalControls: {
flex: 1,
flexDirection: 'row',
borderRadius: 4,
overflow: 'hidden',
paddingBottom: 10,
},
rateControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
volumeControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
resizeModeControl: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
leftRightControlOption: {
alignSelf: 'center',
fontSize: 11,
color: 'white',
padding: 10,
lineHeight: 12,
},
controlOption: {
alignSelf: 'center',
fontSize: 11,
color: 'white',
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
pickerContainer: {
width: 100,
alignSelf: 'center',
color: 'white',
borderWidth: 1,
borderColor: 'red',
},
IndicatorStyle: {
flex: 1,
justifyContent: 'center',
},
seekbarContainer: {
flex: 1,
flexDirection: 'row',
borderRadius: 4,
height: 30,
},
seekbarTrack: {
backgroundColor: '#333',
height: 1,
position: 'relative',
top: 14,
width: '100%',
},
seekbarFill: {
backgroundColor: '#FFF',
height: 1,
width: '100%',
},
seekbarHandle: {
position: 'absolute',
marginLeft: -7,
height: 28,
width: 28,
},
seekbarCircle: {
borderRadius: 12,
position: 'relative',
top: 8,
left: 8,
height: 12,
width: 12,
},
picker: {
flex: 1,
color: 'white',
flexDirection: 'row',
justifyContent: 'center',
width: 100,
height: 40,
},
pickerItem: {
color: 'white',
width: 100,
height: 40,
},
emptyPickerItem: {
color: 'white',
marginTop: 20,
marginLeft: 20,
flex: 1,
width: 100,
height: 40,
},
topControlsContainer: {
paddingTop: 30,
},
});
export default styles;

View File

@ -1,6 +1,6 @@
// prettier-ignore
{
"extends": "@tsconfig/react-native/tsconfig.json", /* Recommended React Native TSConfig base */
"extends": "@react-native/typescript-config/tsconfig.json",
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
"paths": {