Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CH13 - Render Expo-App using React On Rails #11

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fe7fc31
Add Expo App
pulkitkkr May 13, 2021
9ffaef9
Update Root Packages to be in sync with expo-app
pulkitkkr May 13, 2021
e94ef03
Add React-Native Alias and register ExpoApp component
pulkitkkr May 13, 2021
c44128c
Create Rails Controller and View
pulkitkkr May 13, 2021
9e5ea2b
Address Self Review
pulkitkkr May 13, 2021
0ba7ab7
Enable SSR & Update Rails Route, Controller and View
pulkitkkr May 16, 2021
96e008a
Add Expo With Navigation
pulkitkkr May 16, 2021
2aa44aa
Sync Packages
pulkitkkr May 16, 2021
d473358
Webpack Fixes for React Native imports
pulkitkkr May 16, 2021
2b0e5d6
Disable not Working Modules [Comment] & Run React Navigation App
pulkitkkr May 16, 2021
8328c8d
Enable SSR
pulkitkkr May 16, 2021
979ce2b
[WIP] SSR for Navigation
pulkitkkr May 17, 2021
66f99f9
Get SSR with React Navigation Working
pulkitkkr May 24, 2021
a3fbb8f
Add Base Expo App with Navigation and SSR to add Helmet Functionality
pulkitkkr May 25, 2021
fd4e9d2
Add Rails Side Code for Expo WIth Helmet Bundle
pulkitkkr May 25, 2021
89f416d
Try Fix iOS not launching using getProjectRoots()
pulkitkkr May 25, 2021
8637ce0
Add React-Helmet
pulkitkkr May 26, 2021
c51c78c
Add Meta & Title on Server Side Using React Helmet
pulkitkkr May 26, 2021
1565455
Add Meta & Title on Server Side Using React Helmet & Navigation Options
pulkitkkr May 26, 2021
e9af04c
Final Self Review for SSR
pulkitkkr May 26, 2021
2a07250
Fix iOS and Android Apps not starting
pulkitkkr May 26, 2021
e6ca51f
CH-42 - Refactor Config & Get Hot Reloading Working for Expo Android …
pulkitkkr Jun 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions app/controllers/expo_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class ExpoController < ApplicationController
layout "expo"

def index
@expo_props = { foo: "This is a prop sent from Rails Application" }
end

def hello_world
@expo_props = { foo: "This is a prop sent from Rails Application" }
end

def navigation
@expo_props = { foo: "This is a prop sent to Expo Navigation App" }
end

def helmet
@expo_props = { foo: "This is a prop sent to Expo Helmet App" }
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true,
"af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true,
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules/
.expo/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/

# macOS
.DS_Store
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { StatusBar } from 'expo-status-bar'
import * as React from 'react'
import { SafeAreaProvider } from 'react-native-safe-area-context'

import useCachedResources from './hooks/useCachedResources'
import useColorScheme from './hooks/useColorScheme'
import Navigation from './navigation'
import WebWrapper from './components/WebWrapper'
import {Platform } from 'react-native'

const App = () => {
const isLoadingComplete = useCachedResources()
const colorScheme = useColorScheme()
const isWeb = Platform.OS === 'web'

if (!isLoadingComplete && !isWeb) {
return null
}

const Wrapper = isWeb ? WebWrapper : SafeAreaProvider

return (
<Wrapper>
<Navigation colorScheme={isWeb ? 'dark' : colorScheme} />
<StatusBar />
</Wrapper>
)
}

export default App
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react'
import { renderToString } from 'react-dom/server'
import { Helmet } from 'react-helmet'
import ClientApp from './App'
import { ServerContainer } from '@react-navigation/native'

export default (
props: any,
railsContext: { pathname: string; search: string }
) => {
const ref = React.createRef()

const componentHtml = renderToString(
<ServerContainer
// @ts-ignore
ref={ref}
location={{
pathname: railsContext.pathname,
search: railsContext.search || ''
}}
>
<ClientApp />
</ServerContainer>
)

const componentWithWrapper = `
<div id="root" style="display: flex; min-height: 100vh">
${componentHtml}
</div>
`

// @ts-ignore
const options = ref.current?.getCurrentOptions()
const helmet = Helmet.renderStatic()

const renderedHtml = {
componentHtml: componentWithWrapper,
meta: helmet.meta.toString(),
title: `<title>${options?.title}</title>`
}

return { renderedHtml }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"expo": {
"name": "expo-with-helmet-and-navigation",
"slug": "expo-with-helmet-and-navigation",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/images/favicon.png"
}
}
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'],
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as WebBrowser from 'expo-web-browser';
import React from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';

import Colors from '../constants/Colors';
import { MonoText } from './StyledText';
import { Text, View } from './Themed';

export default function EditScreenInfo({ path }: { path: string }) {
return (
<View>
<View style={styles.getStartedContainer}>
<Text
style={styles.getStartedText}
lightColor="rgba(0,0,0,0.8)"
darkColor="rgba(255,255,255,0.8)">
Open up the code for this screen:
</Text>

<View
style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
darkColor="rgba(255,255,255,0.05)"
lightColor="rgba(0,0,0,0.05)">
<MonoText>{path}</MonoText>
</View>

<Text
style={styles.getStartedText}
lightColor="rgba(0,0,0,0.8)"
darkColor="rgba(255,255,255,0.8)">
Change any of the text, save the file, and your app will automatically update.
</Text>
</View>

<View style={styles.helpContainer}>
<TouchableOpacity onPress={handleHelpPress} style={styles.helpLink}>
<Text style={styles.helpLinkText} lightColor={Colors.light.tint}>
Tap here if your app doesn't automatically update after making changes
</Text>
</TouchableOpacity>
</View>
</View>
);
}

function handleHelpPress() {
WebBrowser.openBrowserAsync(
'https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet'
);
}

const styles = StyleSheet.create({
getStartedContainer: {
alignItems: 'center',
marginHorizontal: 50,
},
homeScreenFilename: {
marginVertical: 7,
},
codeHighlightContainer: {
borderRadius: 3,
paddingHorizontal: 4,
},
getStartedText: {
fontSize: 17,
lineHeight: 24,
textAlign: 'center',
},
helpContainer: {
marginTop: 15,
marginHorizontal: 20,
alignItems: 'center',
},
helpLink: {
paddingVertical: 15,
},
helpLinkText: {
textAlign: 'center',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as React from 'react';

import { Text, TextProps } from './Themed';

export function MonoText(props: TextProps) {
return <Text {...props} style={[props.style, { fontFamily: 'space-mono' }]} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Learn more about Light and Dark modes:
* https://docs.expo.io/guides/color-schemes/
*/

import * as React from 'react';
import { Text as DefaultText, View as DefaultView } from 'react-native';

import Colors from '../constants/Colors';
import useColorScheme from '../hooks/useColorScheme';

export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme();
const colorFromProps = props[theme];

if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}

type ThemeProps = {
lightColor?: string;
darkColor?: string;
};

export type TextProps = ThemeProps & DefaultText['props'];
export type ViewProps = ThemeProps & DefaultView['props'];

export function Text(props: TextProps) {
const { style, lightColor, darkColor, ...otherProps } = props;
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');

return <DefaultText style={[{ color }, style]} {...otherProps} />;
}

export function View(props: ViewProps) {
const { style, lightColor, darkColor, ...otherProps } = props;
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');

return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react'
import { Dimensions, View } from 'react-native'

type props = {
children: React.ReactNode
}

const WebWrapper = ({ children }: props) => (
<View
// @ts-ignore: These Style are web specific & are not in React native Prop Types
style={{
minWidth: 500,
minHeight: 600
}}
>
{children}
</View>
)

export default WebWrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from 'react';
import renderer from 'react-test-renderer';

import { MonoText } from '../StyledText';

it(`renders correctly`, () => {
const tree = renderer.create(<MonoText>Snapshot test!</MonoText>).toJSON();

expect(tree).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const tintColorLight = '#2f95dc';
const tintColorDark = '#fff';

export default {
light: {
text: '#000',
background: '#fff',
tint: tintColorLight,
tabIconDefault: '#ccc',
tabIconSelected: tintColorLight,
},
dark: {
text: '#fff',
background: '#000',
tint: tintColorDark,
tabIconDefault: '#ccc',
tabIconSelected: tintColorDark,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Dimensions } from 'react-native';

const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;

export default {
window: {
width,
height,
},
isSmallDevice: width < 375,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// import { Ionicons } from '@expo/vector-icons';
// import * as Font from 'expo-font';
// import * as SplashScreen from 'expo-splash-screen';
import * as React from 'react';

export default function useCachedResources() {
const [isLoadingComplete, setLoadingComplete] = React.useState(false);

// Load any resources or data that we need prior to rendering the app
React.useEffect(() => {
async function loadResourcesAndDataAsync() {
try {
// SplashScreen.preventAutoHideAsync();

// Load fonts
// await Font.loadAsync({
// // ...Ionicons.font,
// 'space-mono': require('../assets/fonts/SpaceMono-Regular.ttf'),
// });
} catch (e) {
// We might want to provide this error information to an error reporting service
console.warn(e);
} finally {
setLoadingComplete(true);
// SplashScreen.hideAsync();
}
}

loadResourcesAndDataAsync();
}, []);

return isLoadingComplete;
}
Loading