Skip to content

Commit

Permalink
add metro web support to NCL (#27439)
Browse files Browse the repository at this point in the history
# Why

- migrate to metro web for NCL since webpack support is deprecated.
- squeezed in some class component to function component migrations so
we can use NCL to test compiler and RSC
  • Loading branch information
EvanBacon committed Mar 5, 2024
1 parent d739975 commit 6adedee
Show file tree
Hide file tree
Showing 80 changed files with 2,473 additions and 2,996 deletions.
3 changes: 2 additions & 1 deletion apps/native-component-list/App.tsx
Expand Up @@ -5,14 +5,15 @@ import { Platform, StatusBar } from 'react-native';
import RootNavigation from './src/navigation/RootNavigation';
import loadAssetsAsync from './src/utilities/loadAssetsAsync';

SplashScreen.preventAutoHideAsync();

function useSplashScreen(loadingFunction: () => void) {
const [isLoadingCompleted, setLoadingComplete] = React.useState(false);

// Load any resources or data that we need prior to rendering the app
React.useEffect(() => {
async function loadAsync() {
try {
await SplashScreen.preventAutoHideAsync();
await loadingFunction();
} catch (e) {
// We might want to provide this error information to an error reporting service
Expand Down
3 changes: 3 additions & 0 deletions apps/native-component-list/app.json
Expand Up @@ -30,6 +30,9 @@
"updates": {
"url": "https://u.expo.dev/2c28de10-a2cd-11e6-b8ce-59d1587e6774"
},
"web": {
"bundler": "metro"
},
"facebookScheme": "fb1696089354000816",
"facebookAppId": "1696089354000816",
"facebookDisplayName": "Expo APIs",
Expand Down
1 change: 1 addition & 0 deletions apps/native-component-list/index.js
@@ -1,3 +1,4 @@
import '@expo/metro-runtime';
import { registerRootComponent } from 'expo';

import App from './App';
Expand Down
11 changes: 4 additions & 7 deletions apps/native-component-list/package.json
Expand Up @@ -6,14 +6,13 @@
"private": true,
"main": "index.js",
"scripts": {
"build:web": "expo export:web",
"web": "expo start --web --https",
"eject": "SDK_VERSION=43.0.0 expo prebuild",
"build:web": "expo export -p web",
"web": "expo start --web",
"lint": "eslint .",
"tsc": "tsc --noEmit -p ./tsconfig.json",
"start": "expo start",
"ios": "react-native run-ios",
"android": "react-native run-android"
"ios": "expo run:ios",
"android": "expo run:android"
},
"expo": {
"autolinking": {
Expand Down Expand Up @@ -153,15 +152,13 @@
},
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/plugin-transform-export-namespace-from": "^7.23.4",
"@types/fbemitter": "^2.0.32",
"@types/i18n-js": "^3.0.1",
"@types/pixi.js": "^4.8.6",
"@types/react": "~18.0.14",
"@types/three": "^0.137.0",
"@types/victory": "^31.0.14",
"babel-jest": "^29.2.1",
"babel-preset-expo": "~10.0.0",
"expo-module-scripts": "^3.0.0",
"jest": "^29.2.1",
"react-test-renderer": "18.2.0"
Expand Down
@@ -1,5 +1,4 @@
import Ionicons from '@expo/vector-icons/build/Ionicons';
import React from 'react';
import Ionicons from '@expo/vector-icons/Ionicons';
import { StyleSheet, TouchableOpacity, TouchableOpacityProps } from 'react-native';

type Props = TouchableOpacityProps & {
Expand Down
@@ -1,4 +1,4 @@
import Ionicons from '@expo/vector-icons/build/Ionicons';
import Ionicons from '@expo/vector-icons/Ionicons';
import { useNavigation } from '@react-navigation/native';
import React from 'react';
import {
Expand Down
2 changes: 1 addition & 1 deletion apps/native-component-list/src/components/SearchBar.tsx
@@ -1,4 +1,4 @@
import Ionicons from '@expo/vector-icons/build/Ionicons';
import Ionicons from '@expo/vector-icons/Ionicons';
import React from 'react';
import { StyleSheet, TextInput, TextStyle, TouchableOpacity, View, Platform } from 'react-native';

Expand Down
79 changes: 32 additions & 47 deletions apps/native-component-list/src/components/ShowActionSheetButton.tsx
@@ -1,45 +1,33 @@
import { ActionSheetOptions } from '@expo/react-native-action-sheet';
import MaterialCommunityIcons from '@expo/vector-icons/build/MaterialCommunityIcons';
import React from 'react';
import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';
import { Text, TextStyle, View } from 'react-native';

const icon = (name: string) => <MaterialCommunityIcons key={name} name={name as any} size={24} />;

interface Props {
// A custom button that shows examples of different share sheet configurations
export default function ShowActionSheetButton({
title,
withTitle = false,
withMessage = false,
withIcons = false,
withSeparators = false,
withCustomStyles = false,
onSelection = null,
showActionSheetWithOptions,
}: {
title: string;
showActionSheetWithOptions: (
options: ActionSheetOptions,
onSelection: (index: number) => void
) => void;
onSelection: (index: number) => void;
onSelection: ((index: number) => void) | null;
withTitle?: boolean;
withMessage?: boolean;
withIcons?: boolean;
withSeparators?: boolean;
withCustomStyles?: boolean;
}

// A custom button that shows examples of different share sheet configurations
export default class ShowActionSheetButton extends React.PureComponent<Props> {
static defaultProps = {
withTitle: false,
withMessage: false,
withIcons: false,
withSeparators: false,
withCustomStyles: false,
onSelection: null,
};

_showActionSheet = () => {
const {
withTitle,
withMessage,
withIcons,
withSeparators,
withCustomStyles,
onSelection,
showActionSheetWithOptions,
} = this.props;
}) {
const showActionSheet = () => {
// Same interface as https://facebook.github.io/react-native/docs/actionsheetios.html
const options = ['Delete', 'Save', 'Share', 'Cancel'];
const icons = withIcons
Expand Down Expand Up @@ -82,28 +70,25 @@ export default class ShowActionSheetButton extends React.PureComponent<Props> {
},
(buttonIndex) => {
// Do something here depending on the button index selected
onSelection(buttonIndex);
onSelection?.(buttonIndex);
}
);
};

render() {
const { title } = this.props;
return (
<View style={{ margin: 6 }}>
<MaterialCommunityIcons.Button
name="code-tags"
backgroundColor="#3e3e3e"
onPress={this._showActionSheet}>
<Text
style={{
fontSize: 15,
color: '#fff',
}}>
{title}
</Text>
</MaterialCommunityIcons.Button>
</View>
);
}
return (
<View style={{ margin: 6 }}>
<MaterialCommunityIcons.Button
name="code-tags"
backgroundColor="#3e3e3e"
onPress={showActionSheet}>
<Text
style={{
fontSize: 15,
color: '#fff',
}}>
{title}
</Text>
</MaterialCommunityIcons.Button>
</View>
);
}
2 changes: 1 addition & 1 deletion apps/native-component-list/src/components/TabIcon.tsx
@@ -1,4 +1,4 @@
import MaterialCommunityIcons from '@expo/vector-icons/build/MaterialCommunityIcons';
import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';
import React from 'react';
import { Platform } from 'react-native';

Expand Down
2 changes: 1 addition & 1 deletion apps/native-component-list/src/navigation/StackConfig.tsx
@@ -1,4 +1,4 @@
import Ionicons from '@expo/vector-icons/build/Ionicons';
import Ionicons from '@expo/vector-icons/Ionicons';
import { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
import { HeaderStyleInterpolators } from '@react-navigation/stack';
import * as React from 'react';
Expand Down
2 changes: 1 addition & 1 deletion apps/native-component-list/src/screens/AV/Player.tsx
@@ -1,4 +1,4 @@
import Ionicons from '@expo/vector-icons/build/Ionicons';
import Ionicons from '@expo/vector-icons/Ionicons';
import Slider from '@react-native-community/slider';
import SegmentedControl from '@react-native-segmented-control/segmented-control';
import { AVMetadata } from 'expo-av';
Expand Down
2 changes: 1 addition & 1 deletion apps/native-component-list/src/screens/AV/Recorder.tsx
@@ -1,4 +1,4 @@
import Ionicons from '@expo/vector-icons/build/Ionicons';
import Ionicons from '@expo/vector-icons/Ionicons';
import { Audio } from 'expo-av';
import React from 'react';
import {
Expand Down
59 changes: 23 additions & 36 deletions apps/native-component-list/src/screens/AV/RecordingScreen.tsx
@@ -1,46 +1,33 @@
import React from 'react';
import { PixelRatio, ScrollView, StyleSheet } from 'react-native';
import { useState } from 'react';
import { ScrollView, StyleSheet } from 'react-native';

import AudioModeSelector from './AudioModeSelector';
import Player from './AudioPlayer';
import Recorder from './Recorder';
import HeadingText from '../../components/HeadingText';

interface State {
recordingUri?: string;
export default function RecordingScreen() {
const [recordingUri, setRecordingUri] = useState<string | undefined>(undefined);

return (
<ScrollView contentContainerStyle={styles.contentContainer}>
<HeadingText>Audio mode</HeadingText>
<AudioModeSelector />
<HeadingText>Recorder</HeadingText>
<Recorder onDone={(recordingUri: string) => setRecordingUri(recordingUri)} />
{recordingUri && (
<>
<HeadingText>Last recording</HeadingText>
<Player source={{ uri: recordingUri }} />
</>
)}
</ScrollView>
);
}

// See: https://github.com/expo/expo/pull/10229#discussion_r490961694
// eslint-disable-next-line @typescript-eslint/ban-types
export default class RecordingScreen extends React.Component<{}, State> {
static navigationOptions = {
title: 'Audio Recording',
};

readonly state: State = {};

_handleRecordingFinished = (recordingUri: string) => this.setState({ recordingUri });

_maybeRenderLastRecording = () =>
this.state.recordingUri ? (
<>
<HeadingText>Last recording</HeadingText>
<Player source={{ uri: this.state.recordingUri }} />
</>
) : null;

render() {
return (
<ScrollView contentContainerStyle={styles.contentContainer}>
<HeadingText>Audio mode</HeadingText>
<AudioModeSelector />
<HeadingText>Recorder</HeadingText>
<Recorder onDone={this._handleRecordingFinished} />
{this._maybeRenderLastRecording()}
</ScrollView>
);
}
}
RecordingScreen.navigationOptions = {
title: 'Audio Recording',
};

const styles = StyleSheet.create({
contentContainer: {
Expand All @@ -65,7 +52,7 @@ const styles = StyleSheet.create({
marginHorizontal: 30,
},
player: {
borderBottomWidth: 1.0 / PixelRatio.get(),
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#cccccc',
},
});
61 changes: 16 additions & 45 deletions apps/native-component-list/src/screens/AppearanceScreen.tsx
@@ -1,54 +1,25 @@
import React from 'react';
import { Appearance, StyleSheet, Text, View } from 'react-native';
import type { NativeEventSubscription } from 'react-native';
import { StyleSheet, Text, View, useColorScheme } from 'react-native';

type ColorSchemeName = Appearance.AppearancePreferences['colorScheme'];
export default function AppearanceScreen() {
const colorScheme = useColorScheme();

interface State {
colorScheme: ColorSchemeName;
}

// See: https://github.com/expo/expo/pull/10229#discussion_r490961694
// eslint-disable-next-line @typescript-eslint/ban-types
export default class AppearanceScreen extends React.Component<{}, State> {
static navigationOptions = {
title: 'Appearance',
};

subscription?: NativeEventSubscription;

state: State = {
colorScheme: Appearance.getColorScheme(),
};
const isDark = colorScheme === 'dark';

componentDidMount() {
this.subscription = Appearance.addChangeListener(
({ colorScheme }: { colorScheme: ColorSchemeName }) => {
this.setState({ colorScheme });
}
);
}
return (
<View style={[styles.screen, isDark ? styles.darkScreen : styles.lightScreen]}>
<Text style={isDark ? styles.darkText : styles.lightText}>
{`Current color scheme: `}

componentWillUnmount() {
if (this.subscription) this.subscription.remove();
}

render() {
const { colorScheme } = this.state;
const isDark = colorScheme === 'dark';

return (
<View style={[styles.screen, isDark ? styles.darkScreen : styles.lightScreen]}>
<Text style={isDark ? styles.darkText : styles.lightText}>
{`Current color scheme: `}

<Text style={styles.boldText}>{this.state.colorScheme}</Text>
</Text>
</View>
);
}
<Text style={styles.boldText}>{colorScheme}</Text>
</Text>
</View>
);
}

AppearanceScreen.navigationOptions = {
title: 'Appearance',
};

const styles = StyleSheet.create({
screen: {
flex: 1,
Expand Down

0 comments on commit 6adedee

Please sign in to comment.