From f5fee1162cafe73c983700f06df862424996563e Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Sun, 4 Oct 2020 15:58:52 +0200 Subject: [PATCH 01/43] more expo updates options --- android/app/src/main/AndroidManifest.xml | 10 ++++++++-- ios/Supporting/Expo.plist | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 57d4636..4765b01 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,10 +17,16 @@ android:value="https://exp.host/@my-expo-username/my-app-slug" /> + android:value="39.0.0" /> + android:value="false"/> + + Date: Mon, 5 Oct 2020 00:07:42 +0200 Subject: [PATCH 02/43] removing old files --- srcMobX/App.ts | 90 ----------- srcMobX/components/listItem.tsx | 49 ------ srcMobX/components/subredditInput.tsx | 73 --------- srcMobX/screens/Empty.tsx | 63 -------- srcMobX/screens/Home.tsx | 143 ---------------- srcMobX/screens/Land.tsx | 79 --------- srcMobX/screens/index.ts | 3 - srcMobX/screens/types.ts | 3 - srcMobX/store/index.tsx | 24 --- srcMobX/store/redditStore.ts | 136 ---------------- srcRedux/App.ts | 85 ---------- srcRedux/components/listItem.tsx | 49 ------ srcRedux/components/subredditInput.tsx | 73 --------- srcRedux/screens/Empty.tsx | 68 -------- srcRedux/screens/Home.tsx | 161 ------------------- srcRedux/screens/Land.tsx | 100 ------------ srcRedux/screens/index.ts | 3 - srcRedux/screens/types.ts | 3 - srcRedux/store/index.tsx | 34 ---- srcRedux/store/postsBySubreddit/actions.ts | 38 ----- srcRedux/store/postsBySubreddit/reducers.ts | 67 -------- srcRedux/store/postsBySubreddit/sagas.ts | 25 --- srcRedux/store/postsBySubreddit/types.ts | 53 ------ srcRedux/store/rootReducer.ts | 13 -- srcRedux/store/rootSaga.ts | 12 -- srcRedux/store/selectedSubreddit/actions.ts | 8 - srcRedux/store/selectedSubreddit/reducers.ts | 19 --- srcRedux/store/selectedSubreddit/types.ts | 12 -- srcRedux/store/subreddits/actions.ts | 16 -- srcRedux/store/subreddits/reducers.ts | 45 ------ srcRedux/store/subreddits/sagas.ts | 12 -- srcRedux/store/subreddits/types.ts | 24 --- srcRedux/store/types.ts | 5 - 33 files changed, 1588 deletions(-) delete mode 100644 srcMobX/App.ts delete mode 100644 srcMobX/components/listItem.tsx delete mode 100644 srcMobX/components/subredditInput.tsx delete mode 100644 srcMobX/screens/Empty.tsx delete mode 100644 srcMobX/screens/Home.tsx delete mode 100644 srcMobX/screens/Land.tsx delete mode 100644 srcMobX/screens/index.ts delete mode 100644 srcMobX/screens/types.ts delete mode 100644 srcMobX/store/index.tsx delete mode 100644 srcMobX/store/redditStore.ts delete mode 100644 srcRedux/App.ts delete mode 100644 srcRedux/components/listItem.tsx delete mode 100644 srcRedux/components/subredditInput.tsx delete mode 100644 srcRedux/screens/Empty.tsx delete mode 100644 srcRedux/screens/Home.tsx delete mode 100644 srcRedux/screens/Land.tsx delete mode 100644 srcRedux/screens/index.ts delete mode 100644 srcRedux/screens/types.ts delete mode 100644 srcRedux/store/index.tsx delete mode 100644 srcRedux/store/postsBySubreddit/actions.ts delete mode 100644 srcRedux/store/postsBySubreddit/reducers.ts delete mode 100644 srcRedux/store/postsBySubreddit/sagas.ts delete mode 100644 srcRedux/store/postsBySubreddit/types.ts delete mode 100644 srcRedux/store/rootReducer.ts delete mode 100644 srcRedux/store/rootSaga.ts delete mode 100644 srcRedux/store/selectedSubreddit/actions.ts delete mode 100644 srcRedux/store/selectedSubreddit/reducers.ts delete mode 100644 srcRedux/store/selectedSubreddit/types.ts delete mode 100644 srcRedux/store/subreddits/actions.ts delete mode 100644 srcRedux/store/subreddits/reducers.ts delete mode 100644 srcRedux/store/subreddits/sagas.ts delete mode 100644 srcRedux/store/subreddits/types.ts delete mode 100644 srcRedux/store/types.ts diff --git a/srcMobX/App.ts b/srcMobX/App.ts deleted file mode 100644 index 759cf46..0000000 --- a/srcMobX/App.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Navigation } from 'react-native-navigation'; -import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; -import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; -import 'mobx-react-lite/batchingForReactNative'; - -import { HOME, LAND, EMPTY } from './screens'; -import HomeScreen from './screens/Home'; -import LandScreen from './screens/Land'; -import EmptyScreen from './screens/Empty'; - -import { withStoreProvider, hydrateStores } from './store'; - -const Screens = new Map>(); - -Screens.set(HOME, HomeScreen); -Screens.set(LAND, LandScreen); -Screens.set(EMPTY, EmptyScreen); - -// Register screens -Screens.forEach((C, key) => { - Navigation.registerComponent( - key, - () => gestureHandlerRootHOC(withStoreProvider(C)), - () => C, - ); -}); - -// Here some global listeners could be placed -// ... - -export const startApp = () => { - // 1st - we hydrate all stores - // 2nd - we get icons - // 3rd - we launch the app - Promise.all(hydrateStores).then(() => { - Promise.all([ - FontAwesome5.getImageSource('reddit', 25), - FontAwesome5.getImageSource('react', 25), - ]).then(([redditIcon, reactIcon]) => { - Navigation.setRoot({ - root: { - bottomTabs: { - options: { - bottomTabs: { - titleDisplayMode: 'alwaysShow', - }, - }, - children: [{ - stack: { - children: [{ - component: { - name: HOME , - }, - }], - options: { - bottomTab: { - text: 'Subreddits', - icon: redditIcon, - }, - }, - }, - }, { - stack: { - children: [{ - component: { - name: EMPTY, - options: { - topBar: { - visible: true, - title: { - text: 'Emptiness', - }, - }, - }, - }, - }], - options: { - bottomTab: { - text: 'MobX App', - icon: reactIcon, - }, - }, - }, - }], - }, - }, - }); - }); - }); -}; diff --git a/srcMobX/components/listItem.tsx b/srcMobX/components/listItem.tsx deleted file mode 100644 index 97f04d0..0000000 --- a/srcMobX/components/listItem.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import { - View, - Text, - Platform, - GestureResponderEvent, -} from 'react-native'; -import { - TouchableOpacity, - TouchableNativeFeedback, -} from 'react-native-gesture-handler'; - -interface TouchableProps { - onPress: (event: GestureResponderEvent) => void; - onLongPress?: (event: GestureResponderEvent) => void; -} - -const Touchable = Platform.OS === 'ios' ? - (props: React.PropsWithChildren) => - {props.children} : - (props: React.PropsWithChildren) => - {props.children}; - -interface Props { - title: string; - data: string; - textSize: number; - onPressed: (sr: string) => void; - onLongPressed?: (data: string) => void; -} - -const ListItem: React.FC = ({ - title, - data, - textSize, - onPressed, - onLongPressed, -}): JSX.Element => ( - onPressed(data)} - onLongPress={() => onLongPressed && onLongPressed(data)} - > - - {title} - - -); - -export default ListItem; diff --git a/srcMobX/components/subredditInput.tsx b/srcMobX/components/subredditInput.tsx deleted file mode 100644 index e9ab993..0000000 --- a/srcMobX/components/subredditInput.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { - View, - Text, - StyleSheet, -} from 'react-native'; -import { - TouchableOpacity, - TextInput, -} from 'react-native-gesture-handler'; - -interface Props { - onAddSubreddit: (sr: string) => void; -} - -const SubredditInput: React.FC = ({ - onAddSubreddit, -}): JSX.Element => { - const [value, onChangeText] = React.useState(''); - - const onAddButtonPressed = () => { - const subreddit = value.trim(); - - if (subreddit !== '') { - onAddSubreddit(subreddit.toLowerCase().replace(/ /g, '')); - onChangeText(''); - } - }; - - return ( - - onChangeText(text)} - value={value} - placeholder={'new subreddit goes here...'} - autoCapitalize={'none'} - /> - - + - - - ); -}; - -const styles = StyleSheet.create({ - inputContainer: { - flexDirection: 'row', - padding: 16, - justifyContent: 'center', - alignContent: 'center', - alignItems: 'center', - }, - input: { - flex: 1, - borderColor: 'lightgray', - borderRadius: 10, - padding: 8, - borderWidth: 1, - fontSize: 22, - }, - buttonContainer: { - marginLeft: 16, - }, - buttonText: { - fontSize: 32, - }, -}); - -export default SubredditInput; diff --git a/srcMobX/screens/Empty.tsx b/srcMobX/screens/Empty.tsx deleted file mode 100644 index 56b1811..0000000 --- a/srcMobX/screens/Empty.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - SafeAreaView, - Text, - StyleSheet, -} from 'react-native'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { useObserver } from 'mobx-react'; -import { NavigationFunctionComponent } from 'react-native-navigation'; - -// EXPO modules -import { Constants } from 'react-native-unimodules'; -import * as Network from 'expo-network'; - -import { useStore } from '../store'; - -interface Props { } - -const Empty: NavigationFunctionComponent = ({ - componentId, -}) => { - const [networkType, setNetworkType] = useState(); - const { redditStore } = useStore(); - - useEffect(() => { - start(); - }, [componentId]); - - const start = async () => { - try { - const networkState = await Network.getNetworkStateAsync(); - - setNetworkType(networkState.type); - } catch (e) { - console.log(e); - } - }; - - return useObserver(() => ( - - - Selected subreddit: {redditStore.selectedSubreddit} - {'\n\n'}FROM EXPO MODULES - Device ID: {Constants.deviceId} - Network type: {networkType} - - )); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - text: { - fontSize: 22, - margin: 16, - textAlign: 'center', - }, -}); - -export default Empty; diff --git a/srcMobX/screens/Home.tsx b/srcMobX/screens/Home.tsx deleted file mode 100644 index 0f0bf4b..0000000 --- a/srcMobX/screens/Home.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import React from 'react'; -import { - SafeAreaView, - FlatList, - KeyboardAvoidingView, - Dimensions, - Platform, - Alert, -} from 'react-native'; -import { Navigation, NavigationFunctionComponent } from 'react-native-navigation'; -import { useNavigationButtonPress } from 'react-native-navigation-hooks'; -import { useObserver } from 'mobx-react'; - -import { useStore } from '../store'; -import { LAND } from '../screens'; -import Item from '../components/listItem'; -import SubredditInput from '../components/subredditInput'; -import { HomeLandPassProps } from './types'; - -interface Props { } - -const Home: NavigationFunctionComponent = ({ - componentId, -}): JSX.Element => { - const listRef = React.useRef>(null); - const [keyboardVerticalOffset, setKeyboardVerticalOffset] = React.useState(0); - const { redditStore } = useStore(); - - // equivalent to componentDidMount - // see - https://stackoverflow.com/questions/53945763/componentdidmount-equivalent-on-a-react-function-hooks-component - // and - https://stackoverflow.com/questions/53120972/how-to-call-loading-function-with-react-useeffect-only-once - React.useEffect(() => { - Dimensions.addEventListener('change', () => { - getStatusBarHeight(); - }); - - getStatusBarHeight(); - - // equivalent to componentWillUnmount - // return () => {}; - }, [componentId]); - - useNavigationButtonPress((_) => { - Alert.alert('This is just a simple button'); - }, componentId, 'hi_button_id'); - - const getStatusBarHeight = async () => { - const navConstants = await Navigation.constants(); - - // for more info - https://stackoverflow.com/a/48759750 - if (Platform.OS === 'ios') { - setKeyboardVerticalOffset(navConstants.statusBarHeight + navConstants.topBarHeight); - } - }; - - const onItemPressed = (subreddit: string) => { - redditStore.selectSubreddit(subreddit); - - Navigation.push(componentId, { - component: { - name: LAND, - passProps: { - title: subreddit, - }, - }, - }); - }; - - const onItemLongPressed = (subreddit: string) => { - Alert.alert( - 'Action required', - `Would you like to delete ${subreddit} subreddit?`, - [{ - text: 'Yes', - onPress: () => { - redditStore.deleteSubreddit(subreddit); - // maybe it is better to subs to onAction(deleteSubreddit) - // and then deleteSubredditPosts - redditStore.deleteSubredditPosts(subreddit); - }, - }, { - text: 'Cancel', - style: 'cancel', - }], - ); - }; - - const onAddSubredditPressed = (subreddit: string) => - redditStore.addSubreddit(subreddit); - - const listScrollToBottom = () => - listRef.current && listRef.current.scrollToEnd({animated: true}); - - return useObserver(() => ( - - - item.title } - renderItem={({ item }) => - - } - /> - - - - - )); -}; - -Home.options = () => ({ - topBar: { - visible: true, - title: { - text: 'Subreddits', - }, - largeTitle: { - visible: false, - }, - rightButtons: [{ - id: 'hi_button_id', - text: 'Hi', - }], - }, -}); - -export default Home; diff --git a/srcMobX/screens/Land.tsx b/srcMobX/screens/Land.tsx deleted file mode 100644 index 3ec4993..0000000 --- a/srcMobX/screens/Land.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import { - SafeAreaView, - Text, - FlatList, - Linking, -} from 'react-native'; -import { NavigationFunctionComponent } from 'react-native-navigation'; -import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks'; -import { useObserver } from 'mobx-react'; - -import { useStore } from '../store'; -import Item from '../components/listItem'; -import { HomeLandPassProps } from './types'; - -interface Props { } - -const Land: NavigationFunctionComponent = ({ - componentId, -}): JSX.Element => { - const { redditStore } = useStore(); - - // equivalent to componentDidMount - React.useEffect(() => { - redditStore.fetchSubredditPosts(); - }, [componentId]); - - useNavigationComponentDidAppear((e) => { - console.log(`${e.componentName} appeared`); - }, componentId); - - const onItemPressed = (url: string) => { - Linking.canOpenURL(url) - .then((supported) => { - if (supported) { - Linking.openURL(url); - } - }); - }; - - const onRefresh = () => { - redditStore.fetchSubredditPosts(); - }; - - return useObserver(() => ( - - { redditStore.postsForSelectedSubreddit.error && - Error {redditStore.postsForSelectedSubreddit.error} } - item.id } - refreshing={redditStore.postsForSelectedSubreddit.isFetching} - onRefresh={onRefresh} - renderItem={({ item }) => - - } - /> - - )); -}; - -Land.options = (passProps: HomeLandPassProps) => ({ - topBar: { - visible: true, - title: { - text: `r/${passProps.title}`, - }, - largeTitle: { - visible: false, - }, - }, -}); - -export default Land; diff --git a/srcMobX/screens/index.ts b/srcMobX/screens/index.ts deleted file mode 100644 index e7e6e54..0000000 --- a/srcMobX/screens/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const HOME = 'rnn-starter.Home'; -export const LAND = 'rnn-starter.Land'; -export const EMPTY = 'rnn-starter.Empty'; diff --git a/srcMobX/screens/types.ts b/srcMobX/screens/types.ts deleted file mode 100644 index 56cdaa1..0000000 --- a/srcMobX/screens/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface HomeLandPassProps { - title?: string; -} diff --git a/srcMobX/store/index.tsx b/srcMobX/store/index.tsx deleted file mode 100644 index c5ac737..0000000 --- a/srcMobX/store/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import RedditStore from './redditStore'; - -const store = { - redditStore: RedditStore, -}; - -const storeContext = React.createContext(store); - -export const withStoreProvider = (C: React.FC) => (props: any) => { - return ( - - - - ); -}; - -export const useStore = () => React.useContext(storeContext); - -// list of hydrate functions from stores needed to be performed before app start -export const hydrateStores = [ - store.redditStore.hydrate(), -]; diff --git a/srcMobX/store/redditStore.ts b/srcMobX/store/redditStore.ts deleted file mode 100644 index 0e8219a..0000000 --- a/srcMobX/store/redditStore.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { types, flow, applySnapshot, onSnapshot } from 'mobx-state-tree'; -import AsyncStorage from '@react-native-community/async-storage'; -import debounce from 'lodash/debounce'; - -const storageID = 'RedditStore'; - -async function fetchPostsApi(reddit: string) { - const response = await fetch(`https://www.reddit.com/r/${reddit}.json`); - return await response.json(); -} - -const Subreddit = types - .model('Subreddit', { - title: types.string, - }); - -const SubredditPosts = types - .model('SubredditPosts', { - subreddit: types.identifier, - isFetching: types.boolean, - error: types.maybeNull(types.string), - items: types.array(types.frozen()), - }) - .actions((self) => { - const fetchState = () => { - self.error = null; - self.isFetching = true; - }; - - const errorState = (e: string) => { - self.error = e; - self.isFetching = false; - }; - - const receiveState = (items: any) => { - self.error = null; - self.isFetching = false; - self.items = items; - }; - - return { - fetchState, - errorState, - receiveState, - }; - }); - -const RedditStore = types - .model('RedditStore', { - selectedSubreddit: types.string, - subreddits: types.array(Subreddit), - postsBySubreddit: types.map(SubredditPosts), - }) - .views((self) => ({ - get postsForSelectedSubreddit() { - return self.postsBySubreddit.get(self.selectedSubreddit) || - { subreddit: self.selectedSubreddit, isFetching: false, error: null, items: [] }; - }, - })) - .actions((self) => { - const selectSubreddit = (subreddit: string) => { - self.selectedSubreddit = subreddit; - }; - - const addSubreddit = (subreddit: string) => { - // check on duplicates - if (!self.subreddits.find((sr) => sr.title === subreddit)) { - self.subreddits.push({ title: subreddit }); - } - }; - const deleteSubreddit = (subreddit: string) => { - const filtered = self.subreddits.filter((sr) => sr.title !== subreddit); - self.subreddits = filtered as any; - }; - - const fetchSubredditPosts = flow(function*(subreddit: string = self.selectedSubreddit) { - if (!self.postsBySubreddit.has(subreddit)) { - self.postsBySubreddit.put({ subreddit, isFetching: false, error: null, items: [] }); - } - - self.postsBySubreddit.get(subreddit)!.fetchState(); - - try { - const json = yield fetchPostsApi(subreddit); - const posts = json.data.children.map((child: any) => child.data); - self.postsBySubreddit.get(subreddit)!.receiveState(posts); - } catch (e) { - self.postsBySubreddit.get(subreddit)!.errorState(e.toString()); - } - - return self.postsBySubreddit.get(subreddit)!.items.length; - }); - const deleteSubredditPosts = (subreddit: string) => { - self.postsBySubreddit.delete(subreddit); - }; - - const hydrate = () => { - return flow(function*() { - const data = yield AsyncStorage.getItem(storageID); - if (data) { - applySnapshot(RedditStore, JSON.parse(data)); - } - })(); - }; - - return { - selectSubreddit, - - addSubreddit, - deleteSubreddit, - - fetchSubredditPosts, - deleteSubredditPosts, - - hydrate, - }; - }) - .create({ - selectedSubreddit: '', - subreddits: [ - { title: 'mobx' }, - { title: 'reactjs' }, - { title: 'reactnative' }, - { title: 'golang' }, - { title: 'rust' }, - ], - postsBySubreddit: {}, - }); - -// persisting the stores -onSnapshot(RedditStore, debounce( - (snapshot) => AsyncStorage.setItem(storageID, JSON.stringify(snapshot)), - 1000, -)); - -export default RedditStore; diff --git a/srcRedux/App.ts b/srcRedux/App.ts deleted file mode 100644 index 4b8f73d..0000000 --- a/srcRedux/App.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Navigation } from 'react-native-navigation'; -import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; -import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; - -import { HOME, LAND, EMPTY } from './screens'; - -import HomeScreen from './screens/Home'; -import LandScreen from './screens/Land'; -import EmptyScreen from './screens/Empty'; - -import { withReduxProvider } from './store'; - -const Screens = new Map>(); - -Screens.set(HOME, HomeScreen); -Screens.set(LAND, LandScreen); -Screens.set(EMPTY, EmptyScreen); - -// Register screens -Screens.forEach((C, key) => { - Navigation.registerComponent( - key, - () => gestureHandlerRootHOC(withReduxProvider(C)), - () => C, - ); -}); - -// Here some global listeners could be placed -// ... - -export const startApp = () => { - Promise.all([ - FontAwesome5.getImageSource('reddit', 25), - FontAwesome5.getImageSource('react', 25), - ]).then(([redditIcon, reactIcon]) => { - Navigation.setRoot({ - root: { - bottomTabs: { - options: { - bottomTabs: { - titleDisplayMode: 'alwaysShow', - }, - }, - children: [{ - stack: { - children: [{ - component: { - name: HOME , - }, - }], - options: { - bottomTab: { - text: 'Subreddits', - icon: redditIcon, - }, - }, - }, - }, { - stack: { - children: [{ - component: { - name: EMPTY, - options: { - topBar: { - visible: true, - title: { - text: 'Emptiness', - }, - }, - }, - }, - }], - options: { - bottomTab: { - text: 'Redux App', - icon: reactIcon, - }, - }, - }, - }], - }, - }, - }); - }); -}; diff --git a/srcRedux/components/listItem.tsx b/srcRedux/components/listItem.tsx deleted file mode 100644 index 97f04d0..0000000 --- a/srcRedux/components/listItem.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import { - View, - Text, - Platform, - GestureResponderEvent, -} from 'react-native'; -import { - TouchableOpacity, - TouchableNativeFeedback, -} from 'react-native-gesture-handler'; - -interface TouchableProps { - onPress: (event: GestureResponderEvent) => void; - onLongPress?: (event: GestureResponderEvent) => void; -} - -const Touchable = Platform.OS === 'ios' ? - (props: React.PropsWithChildren) => - {props.children} : - (props: React.PropsWithChildren) => - {props.children}; - -interface Props { - title: string; - data: string; - textSize: number; - onPressed: (sr: string) => void; - onLongPressed?: (data: string) => void; -} - -const ListItem: React.FC = ({ - title, - data, - textSize, - onPressed, - onLongPressed, -}): JSX.Element => ( - onPressed(data)} - onLongPress={() => onLongPressed && onLongPressed(data)} - > - - {title} - - -); - -export default ListItem; diff --git a/srcRedux/components/subredditInput.tsx b/srcRedux/components/subredditInput.tsx deleted file mode 100644 index e9ab993..0000000 --- a/srcRedux/components/subredditInput.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { - View, - Text, - StyleSheet, -} from 'react-native'; -import { - TouchableOpacity, - TextInput, -} from 'react-native-gesture-handler'; - -interface Props { - onAddSubreddit: (sr: string) => void; -} - -const SubredditInput: React.FC = ({ - onAddSubreddit, -}): JSX.Element => { - const [value, onChangeText] = React.useState(''); - - const onAddButtonPressed = () => { - const subreddit = value.trim(); - - if (subreddit !== '') { - onAddSubreddit(subreddit.toLowerCase().replace(/ /g, '')); - onChangeText(''); - } - }; - - return ( - - onChangeText(text)} - value={value} - placeholder={'new subreddit goes here...'} - autoCapitalize={'none'} - /> - - + - - - ); -}; - -const styles = StyleSheet.create({ - inputContainer: { - flexDirection: 'row', - padding: 16, - justifyContent: 'center', - alignContent: 'center', - alignItems: 'center', - }, - input: { - flex: 1, - borderColor: 'lightgray', - borderRadius: 10, - padding: 8, - borderWidth: 1, - fontSize: 22, - }, - buttonContainer: { - marginLeft: 16, - }, - buttonText: { - fontSize: 32, - }, -}); - -export default SubredditInput; diff --git a/srcRedux/screens/Empty.tsx b/srcRedux/screens/Empty.tsx deleted file mode 100644 index bb2a135..0000000 --- a/srcRedux/screens/Empty.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - SafeAreaView, - Text, - StyleSheet, -} from 'react-native'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { useSelector } from 'react-redux'; -import { NavigationFunctionComponent } from 'react-native-navigation'; - -// EXPO modules -import { Constants } from 'react-native-unimodules'; -import * as Network from 'expo-network'; - -interface Props { } - -const Empty: NavigationFunctionComponent = ({ - componentId, -}) => { - const [networkType, setNetworkType] = useState(); - - // Redux Hooks - // =========== - // selectors - const selectedSubreddit = useSelector((s: GlobalState) => s.selectedSubreddit); - - // actions - // =========== - - useEffect(() => { - start(); - }, [componentId]); - - const start = async () => { - try { - const networkState = await Network.getNetworkStateAsync(); - - setNetworkType(networkState.type); - } catch (e) { - console.log(e); - } - }; - - return ( - - RNN - Selected subreddit: {selectedSubreddit} - {'\n\n'}FROM EXPO MODULES - Device ID: {Constants.deviceId} - Network type: {networkType} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - text: { - fontSize: 22, - margin: 16, - textAlign: 'center', - }, -}); - -export default Empty; diff --git a/srcRedux/screens/Home.tsx b/srcRedux/screens/Home.tsx deleted file mode 100644 index e007f80..0000000 --- a/srcRedux/screens/Home.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { useCallback } from 'react'; -import { - SafeAreaView, - FlatList, - KeyboardAvoidingView, - Dimensions, - Platform, - Alert, -} from 'react-native'; -import { Navigation, NavigationFunctionComponent } from 'react-native-navigation'; -import { useNavigationButtonPress } from 'react-native-navigation-hooks'; -import { useSelector, useDispatch } from 'react-redux'; - -import { selectSubreddit } from '../store/selectedSubreddit/actions'; -import { addSubreddit, deleteSubreddit } from '../store/subreddits/actions'; - -import { LAND } from '../screens'; -import Item from '../components/listItem'; -import SubredditInput from '../components/subredditInput'; -import { HomeLandPassProps } from './types'; - -interface Props { } - -const Home: NavigationFunctionComponent = ({ - componentId, -}): JSX.Element => { - // Redux Hooks - // =========== - // selectors - const subreddits = useSelector((s: GlobalState) => s.subreddits); - - // actions - const dispatch = useDispatch(); - const onSelectSubreddit = useCallback( - (sr: string) => dispatch(selectSubreddit(sr)), - [dispatch], - ); - const onDeleteSubreddit = useCallback( - (sr: string) => dispatch(deleteSubreddit(sr)), - [dispatch], - ); - const onAddSubreddit = useCallback( - (sr: string) => dispatch(addSubreddit(sr)), - [dispatch], - ); - // =========== - - const listRef = React.useRef>(null); - const [keyboardVerticalOffset, setKeyboardVerticalOffset] = React.useState(0); - - // equivalent to componentDidMount - // see - https://stackoverflow.com/questions/53945763/componentdidmount-equivalent-on-a-react-function-hooks-component - // and - https://stackoverflow.com/questions/53120972/how-to-call-loading-function-with-react-useeffect-only-once - React.useEffect(() => { - Dimensions.addEventListener('change', () => { - getStatusBarHeight(); - }); - - getStatusBarHeight(); - - // equivalent to componentWillUnmount - // return () => {}; - }, [componentId]); - - useNavigationButtonPress((_) => { - Alert.alert('This is just a simple button'); - }, componentId, 'hi_button_id'); - - const getStatusBarHeight = async () => { - const navConstants = await Navigation.constants(); - - // for more info - https://stackoverflow.com/a/48759750 - if (Platform.OS === 'ios') { - setKeyboardVerticalOffset(navConstants.statusBarHeight + navConstants.topBarHeight); - } - }; - - const onItemPressed = (subreddit: string) => { - onSelectSubreddit(subreddit); - - Navigation.push(componentId, { - component: { - name: LAND, - passProps: { - title: subreddit, - }, - }, - }); - }; - - const onItemLongPressed = (subreddit: string) => { - Alert.alert( - 'Action required', - `Would you like to delete ${subreddit} subreddit?`, - [{ - text: 'Yes', - onPress: () => onDeleteSubreddit(subreddit), - }, { - text: 'Cancel', - style: 'cancel', - }], - ); - }; - - const onAddSubredditPressed = (subreddit: string) => { - onAddSubreddit(subreddit); - }; - - const listScrollToBottom = () => - listRef.current && listRef.current.scrollToEnd({animated: true}); - - return ( - - - item.title } - renderItem={({ item }) => - - } - /> - - - - - ); -}; - -Home.options = () => ({ - topBar: { - visible: true, - title: { - text: 'Subreddits', - }, - largeTitle: { - visible: false, - }, - rightButtons: [{ - id: 'hi_button_id', - text: 'Hi', - }], - }, -}); - -export default Home; diff --git a/srcRedux/screens/Land.tsx b/srcRedux/screens/Land.tsx deleted file mode 100644 index 51ce799..0000000 --- a/srcRedux/screens/Land.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React, { useCallback } from 'react'; -import { - SafeAreaView, - Text, - FlatList, - Linking, -} from 'react-native'; -import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks'; -import { useSelector, useDispatch } from 'react-redux'; - -import { requestPosts } from '../store/postsBySubreddit/actions'; - -import Item from '../components/listItem'; -import { NavigationFunctionComponent } from 'react-native-navigation'; -import { HomeLandPassProps } from './types'; - -interface Props { } - -const Land: NavigationFunctionComponent = ({ - componentId, -}): JSX.Element => { - // Redux Hooks - // =========== - // selectors - const selectedSubreddit = useSelector((s: GlobalState) => s.selectedSubreddit); - const postsBySubreddit = useSelector((s: GlobalState) => s.postsBySubreddit[selectedSubreddit]); - const { - isFetching, - items: posts, - error, - } = postsBySubreddit || { - isFetching: true, - items: [], - error: null, - } - - // actions - const dispatch = useDispatch(); - const fetchPosts = useCallback( - (sr: string) => dispatch(requestPosts(sr)), - [dispatch], - ); - // =========== - - // equivalent to componentDidMount - React.useEffect(() => { - fetchPosts(selectedSubreddit); - }, [componentId]); - - useNavigationComponentDidAppear((e) => { - console.log(`${e.componentName} appeared`); - }, componentId); - - const onItemPressed = (url: string) => { - Linking.canOpenURL(url) - .then((supported) => { - if (supported) { - Linking.openURL(url); - } - }); - }; - - const onRefresh = () => { - fetchPosts(selectedSubreddit); - }; - - return ( - - { error && Error {error.toString()} } - item.id } - refreshing={isFetching} - onRefresh={onRefresh} - renderItem={({ item }) => - - } - /> - - ); -}; - -Land.options = (passProps: HomeLandPassProps) => ({ - topBar: { - visible: true, - title: { - text: `r/${passProps.title}`, - }, - largeTitle: { - visible: false, - }, - }, -}); - -export default Land; diff --git a/srcRedux/screens/index.ts b/srcRedux/screens/index.ts deleted file mode 100644 index e7e6e54..0000000 --- a/srcRedux/screens/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const HOME = 'rnn-starter.Home'; -export const LAND = 'rnn-starter.Land'; -export const EMPTY = 'rnn-starter.Empty'; diff --git a/srcRedux/screens/types.ts b/srcRedux/screens/types.ts deleted file mode 100644 index 56cdaa1..0000000 --- a/srcRedux/screens/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface HomeLandPassProps { - title?: string; -} diff --git a/srcRedux/store/index.tsx b/srcRedux/store/index.tsx deleted file mode 100644 index f18f8ba..0000000 --- a/srcRedux/store/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import AsyncStorage from '@react-native-community/async-storage'; -import { createStore, applyMiddleware } from 'redux'; -import { Provider } from 'react-redux'; -import { persistStore, persistReducer } from 'redux-persist'; -import { PersistGate } from 'redux-persist/integration/react'; -import createSagaMiddleware from 'redux-saga'; - -import reducer from './rootReducer'; -import saga from './rootSaga'; - -const persistConfig = { - key: 'rootKeyPersist', - storage: AsyncStorage, -}; -const persistedReducer = persistReducer(persistConfig, reducer); - -const sagaMiddleware = createSagaMiddleware(); - -const store = createStore( - persistedReducer, - applyMiddleware(sagaMiddleware), -); -const persistor = persistStore(store); - -sagaMiddleware.run(saga); - -export const withReduxProvider = (C: React.FC) => (props: any) => ( - - - - - -); diff --git a/srcRedux/store/postsBySubreddit/actions.ts b/srcRedux/store/postsBySubreddit/actions.ts deleted file mode 100644 index fb49f1c..0000000 --- a/srcRedux/store/postsBySubreddit/actions.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const REQUEST_POSTS = 'REQUEST_POSTS'; -export const DELETE_POSTS = 'DELETE_POSTS'; -export const RECEIVE_POSTS = 'RECEIVE_POSTS'; -export const FAIL_RECEIVE_POSTS = 'FAIL_RECEIVE_POSTS'; - -export const requestPosts = ( - subreddit: string, -): RequestPostsAction => ({ - type: REQUEST_POSTS, - subreddit, -}); - -export const deletePosts = ( - subreddit: string, -): DeletePostsAction => ({ - type: DELETE_POSTS, - subreddit, -}); - -export const receivePosts = ( - subreddit: string, - json: any, -): ReceivePostsAction => ({ - type: RECEIVE_POSTS, - subreddit, - posts: json.data.children.map((child: any) => child.data), - receivedAt: Date.now(), -}); - -export const failReceivePosts = ( - subreddit: string, - error: Error, -): FailReceivePostsAction => ({ - type: FAIL_RECEIVE_POSTS, - subreddit, - error, - receivedAt: Date.now(), -}); diff --git a/srcRedux/store/postsBySubreddit/reducers.ts b/srcRedux/store/postsBySubreddit/reducers.ts deleted file mode 100644 index 23c186a..0000000 --- a/srcRedux/store/postsBySubreddit/reducers.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - REQUEST_POSTS, - RECEIVE_POSTS, - FAIL_RECEIVE_POSTS, - DELETE_POSTS, -} from './actions'; - -const initialStatePostsBySubreddit: PostsBySubredditState = {}; -const initialStatePosts: PostsState = { - isFetching: false, - items: [], - error: null, - lastUpdated: 0, -}; - -const posts = ( - state = initialStatePosts, - action: PostsActionTypes_I, -): PostsState => { - switch (action.type) { - case REQUEST_POSTS: - return { - ...state, - isFetching: true, - error: null, - }; - case DELETE_POSTS: - return initialStatePosts; - case FAIL_RECEIVE_POSTS: - return { - ...state, - isFetching: false, - error: action.error, - lastUpdated: action.receivedAt, - }; - case RECEIVE_POSTS: - return { - ...state, - isFetching: false, - items: action.posts, - error: null, - lastUpdated: action.receivedAt, - }; - default: - return state; - } -}; - -const postsBySubreddit = ( - state = initialStatePostsBySubreddit, - action: PostsActionTypes_I, -): PostsBySubredditState => { - switch (action.type) { - case RECEIVE_POSTS: - case DELETE_POSTS: - case FAIL_RECEIVE_POSTS: - case REQUEST_POSTS: - return { - ...state, - [action.subreddit]: posts(state[action.subreddit], action), - }; - default: - return state; - } -}; - -export default postsBySubreddit; diff --git a/srcRedux/store/postsBySubreddit/sagas.ts b/srcRedux/store/postsBySubreddit/sagas.ts deleted file mode 100644 index 9b00b3e..0000000 --- a/srcRedux/store/postsBySubreddit/sagas.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { call, put, takeEvery } from 'redux-saga/effects'; -import { - REQUEST_POSTS, - - receivePosts, - failReceivePosts, -} from './actions'; - -async function fetchPostsApi(reddit: string) { - const response = await fetch(`https://www.reddit.com/r/${reddit}.json`); - return await response.json(); -} - -function* fetchPosts({ subreddit }: RequestPostsAction) { - try { - const json = yield call(fetchPostsApi, subreddit); - yield put(receivePosts(subreddit, json)); - } catch (e) { - yield put(failReceivePosts(subreddit, e)); - } -} - -export default function* postsBySubreddit() { - yield takeEvery(REQUEST_POSTS, fetchPosts); -} diff --git a/srcRedux/store/postsBySubreddit/types.ts b/srcRedux/store/postsBySubreddit/types.ts deleted file mode 100644 index ef84eae..0000000 --- a/srcRedux/store/postsBySubreddit/types.ts +++ /dev/null @@ -1,53 +0,0 @@ -// ================= -// ACTIONS -// ================= -interface RequestPostsAction { - type: string; - subreddit: string; -} - -interface DeletePostsAction { - type: string; - subreddit: string; -} - -interface ReceivePostsAction { - type: string; - subreddit: string; - posts: any[]; - receivedAt: number; -} - -interface FailReceivePostsAction { - type: string; - subreddit: string; - error: Error | null; - receivedAt: number; -} - -type PostsActionTypes_U = ( - RequestPostsAction | - ReceivePostsAction | - FailReceivePostsAction | - DeletePostsAction -); // Union Types -type PostsActionTypes_I = ( - RequestPostsAction & - ReceivePostsAction & - FailReceivePostsAction & - DeletePostsAction -); // Intersection Types - -// ================= -// REDUCERS -// ================= -interface PostsBySubredditState { - [subreddit: string]: PostsState; -} - -interface PostsState { - isFetching: boolean; - items: any[]; - error: Error | null; - lastUpdated: number; -} diff --git a/srcRedux/store/rootReducer.ts b/srcRedux/store/rootReducer.ts deleted file mode 100644 index 9fec971..0000000 --- a/srcRedux/store/rootReducer.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { combineReducers } from 'redux'; - -import selectedSubreddit from './selectedSubreddit/reducers'; -import subreddits from './subreddits/reducers'; -import postsBySubreddit from './postsBySubreddit/reducers'; - -const rootReducer = combineReducers({ - selectedSubreddit, - subreddits, - postsBySubreddit, -}); - -export default rootReducer; diff --git a/srcRedux/store/rootSaga.ts b/srcRedux/store/rootSaga.ts deleted file mode 100644 index a6553a7..0000000 --- a/srcRedux/store/rootSaga.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { all } from 'redux-saga/effects'; - -import postsBySubreddit from './postsBySubreddit/sagas'; -import subreddits from './subreddits/sagas'; - -export default function* rootSaga() { - yield all([ - postsBySubreddit(), - subreddits(), - // some more - ]); -} diff --git a/srcRedux/store/selectedSubreddit/actions.ts b/srcRedux/store/selectedSubreddit/actions.ts deleted file mode 100644 index 240e205..0000000 --- a/srcRedux/store/selectedSubreddit/actions.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'; - -export const selectSubreddit = ( - subreddit: string, -): SelectedSubredditAction => ({ - type: SELECT_SUBREDDIT, - subreddit, -}); diff --git a/srcRedux/store/selectedSubreddit/reducers.ts b/srcRedux/store/selectedSubreddit/reducers.ts deleted file mode 100644 index c291b01..0000000 --- a/srcRedux/store/selectedSubreddit/reducers.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - SELECT_SUBREDDIT, -} from './actions'; - -const initialState: SelectedSubredditState = ''; - -const selectedSubreddit = ( - state = initialState, - action: SelectedSubredditAction, -): SelectedSubredditState => { - switch (action.type) { - case SELECT_SUBREDDIT: - return action.subreddit; - default: - return state; - } -}; - -export default selectedSubreddit; diff --git a/srcRedux/store/selectedSubreddit/types.ts b/srcRedux/store/selectedSubreddit/types.ts deleted file mode 100644 index fc2c904..0000000 --- a/srcRedux/store/selectedSubreddit/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -// ================= -// ACTIONS -// ================= -interface SelectedSubredditAction { - type: string; - subreddit: string; -} - -// ================= -// REDUCERS -// ================= -type SelectedSubredditState = string; diff --git a/srcRedux/store/subreddits/actions.ts b/srcRedux/store/subreddits/actions.ts deleted file mode 100644 index 0a61ee3..0000000 --- a/srcRedux/store/subreddits/actions.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ADD_SUBREDDIT = 'ADD_SUBREDDIT'; -export const DELETE_SUBREDDIT = 'DELETE_SUBREDDIT'; - -export const addSubreddit = ( - subreddit: string, -): AddSubredditAction => ({ - type: ADD_SUBREDDIT, - subreddit, -}); - -export const deleteSubreddit = ( - subreddit: string, -): DeleteSubredditAction => ({ - type: DELETE_SUBREDDIT, - subreddit, -}); diff --git a/srcRedux/store/subreddits/reducers.ts b/srcRedux/store/subreddits/reducers.ts deleted file mode 100644 index 2aac02e..0000000 --- a/srcRedux/store/subreddits/reducers.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - ADD_SUBREDDIT, - DELETE_SUBREDDIT, -} from './actions'; - -const initialState: SubredditsState = [ - { - title: 'redux', - }, - { - title: 'reactjs', - }, - { - title: 'reactnative', - }, - { - title: 'golang', - }, - { - title: 'rust', - }, -]; - -const subreddits = ( - state = initialState, - action: SubredditsActionTypes_I, -): SubredditsState => { - switch (action.type) { - case ADD_SUBREDDIT: - // check on duplicates - if (!state.find((sr) => sr.title === action.subreddit)) { - return [ - ...state, - { title: action.subreddit }, - ]; - } - return state; - case DELETE_SUBREDDIT: - return state.filter((v) => v.title !== action.subreddit); - default: - return state; - } -}; - -export default subreddits; diff --git a/srcRedux/store/subreddits/sagas.ts b/srcRedux/store/subreddits/sagas.ts deleted file mode 100644 index f44b5eb..0000000 --- a/srcRedux/store/subreddits/sagas.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { put, takeEvery } from 'redux-saga/effects'; -import { DELETE_SUBREDDIT } from './actions'; - -import { deletePosts } from '../postsBySubreddit/actions'; - -function* deletePostsForSubreddit({ subreddit }: DeleteSubredditAction) { - yield put(deletePosts(subreddit)); -} - -export default function* subreddits() { - yield takeEvery(DELETE_SUBREDDIT, deletePostsForSubreddit); -} diff --git a/srcRedux/store/subreddits/types.ts b/srcRedux/store/subreddits/types.ts deleted file mode 100644 index dfce8bc..0000000 --- a/srcRedux/store/subreddits/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -// ================= -// ACTIONS -// ================= -interface AddSubredditAction { - type: string; - subreddit: string; -} - -interface DeleteSubredditAction { - type: string; - subreddit: string; -} - -type SubredditsActionTypes_U = (AddSubredditAction | DeleteSubredditAction); // Union Types -type SubredditsActionTypes_I = (AddSubredditAction & DeleteSubredditAction); // Intersection Types - -// ================= -// REDUCERS -// ================= -interface SubredditInfo { - title: string; -} - -type SubredditsState = SubredditInfo[]; diff --git a/srcRedux/store/types.ts b/srcRedux/store/types.ts deleted file mode 100644 index d8d2325..0000000 --- a/srcRedux/store/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -interface GlobalState { - selectedSubreddit: SelectedSubredditState; - postsBySubreddit: PostsBySubredditState; - subreddits: SubredditsState; -} From 7e259912c20ec6f0fc9d8f2dd9e2327fb826471e Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:08:25 +0200 Subject: [PATCH 03/43] mobx store --- src/store/counterStore.ts | 18 ++++++++++++++++++ src/store/index.tsx | 33 +++++++++++++++++++++++++++++++++ src/store/uiStore.ts | 10 ++++++++++ 3 files changed, 61 insertions(+) create mode 100644 src/store/counterStore.ts create mode 100644 src/store/index.tsx create mode 100644 src/store/uiStore.ts diff --git a/src/store/counterStore.ts b/src/store/counterStore.ts new file mode 100644 index 0000000..cb87c1b --- /dev/null +++ b/src/store/counterStore.ts @@ -0,0 +1,18 @@ +import { observable, action, computed } from 'mobx'; +import { persist } from 'mobx-persist'; + +class CounterStore implements IStore { + STORAGE_ID = 'CounterStore'; + + @persist @observable value = 0; + + @action increment = async () => { + this.value += 1; + } + + @action decrement = async () => { + this.value -= 1; + } +} + +export default new CounterStore(); \ No newline at end of file diff --git a/src/store/index.tsx b/src/store/index.tsx new file mode 100644 index 0000000..0ad1eb0 --- /dev/null +++ b/src/store/index.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import AsyncStorage from '@react-native-community/async-storage'; +import { create } from 'mobx-persist'; + +import CounterStore from './counterStore'; +import UIStore from './uiStore'; + +const stores = { + counter: CounterStore, + ui: UIStore, +}; + +const storeContext = React.createContext(stores); + +export const withStoreProvider = (C: React.FC) => (props: any) => { + return ( + + + + ); +}; + +export const useStores = () => React.useContext(storeContext); + +// list of hydrate functions from stores needed to be performed before app start +const hydrate = create({ + storage: AsyncStorage, + debounce: 500, +}); +export const hydrateStores = async () => { + await hydrate(stores.counter.STORAGE_ID, stores.counter); + // await hydrate(stores.ui.STORAGE_ID, stores.ui); +}; diff --git a/src/store/uiStore.ts b/src/store/uiStore.ts new file mode 100644 index 0000000..22489b6 --- /dev/null +++ b/src/store/uiStore.ts @@ -0,0 +1,10 @@ +import { observable } from 'mobx'; +import { persist } from 'mobx-persist'; + +class UIStore implements IStore { + STORAGE_ID = 'UIStore'; + + @observable networkType: string | undefined = ''; +} + +export default new UIStore(); \ No newline at end of file From 9ae9ce8d6a6655dd2dca2bde4fcdfd6fe44815ce Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:08:40 +0200 Subject: [PATCH 04/43] one src folder --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index abde648..ad7de85 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,7 @@ import { Navigation } from 'react-native-navigation'; // because RNN registers screens for both of them if two imports are presented // import { startApp } from './srcRedux/App'; -import { startApp } from './srcMobX/App'; +import { startApp } from './src/App'; Navigation.events().registerAppLaunchedListener(() => { startApp(); From 697bd85ac144227cc5ff4cb05da05b35ca9ebc80 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:09:00 +0200 Subject: [PATCH 05/43] Create App.ts --- src/App.ts | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/App.ts diff --git a/src/App.ts b/src/App.ts new file mode 100644 index 0000000..07db892 --- /dev/null +++ b/src/App.ts @@ -0,0 +1,113 @@ +import { Navigation } from 'react-native-navigation'; +import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; +import Ionicons from 'react-native-vector-icons/Ionicons'; +import 'mobx-react-lite/batchingForReactNative'; + +import Constants from './utils/constants'; + +import CounterScreen from './screens/CounterScreen'; +import ExpoScreen from './screens/ExpoScreen'; + +import { withStoreProvider, hydrateStores } from './store'; + +const Screens = new Map>(); + +Screens.set(Constants.ScreenNames.CounterScreen, CounterScreen); +Screens.set(Constants.ScreenNames.ExpoScreen, ExpoScreen); +Screens.set(Constants.ScreenNames.AboutScreen, CounterScreen); + +// Register screens +Screens.forEach((C, key) => { + Navigation.registerComponent( + key, + () => + gestureHandlerRootHOC( + withStoreProvider(C)), + () => C, + ); +}); + +// Here some global listeners could be placed +// ... + +export const startApp = async () => { + // rehydrate stores + await hydrateStores(); + + // getting icons for tabs as they have to be as image sources + const [tab1, tab2, tab3] = await Promise.all([ + Ionicons.getImageSource('ios-duplicate-outline', 25), + Ionicons.getImageSource('ios-rocket-outline', 25), + Ionicons.getImageSource('ios-information-circle-outline', 25), + ]); + const [tab1Selected, tab2Selected, tab3Selected] = await Promise.all([ + Ionicons.getImageSource('ios-duplicate', 25), + Ionicons.getImageSource('ios-rocket', 25), + Ionicons.getImageSource('ios-information-circle', 25), + ]); + + // settings default options for navigation, like colors + Navigation.setDefaultOptions({ + layout: { + orientation: ['portrait'], + // backgroundColor: 'black', + // componentBackgroundColor: 'black', + }, + bottomTabs: { + titleDisplayMode: 'alwaysShow', + } + }); + + Navigation.setRoot({ + root: { + bottomTabs: { + children: [{ + stack: { + children: [{ + component: { + name: Constants.ScreenNames.CounterScreen, + }, + }], + options: { + bottomTab: { + text: Constants.BottomTabsTitles.tab1, + icon: tab1, + selectedIcon: tab1Selected, + }, + }, + }, + }, { + stack: { + children: [{ + component: { + name: Constants.ScreenNames.ExpoScreen, + }, + }], + options: { + bottomTab: { + text: Constants.BottomTabsTitles.tab2, + icon: tab2, + selectedIcon: tab2Selected, + }, + }, + }, + }, { + stack: { + children: [{ + component: { + name: Constants.ScreenNames.AboutScreen, + }, + }], + options: { + bottomTab: { + text: Constants.BottomTabsTitles.tab3, + icon: tab3, + selectedIcon: tab3Selected, + }, + }, + }, + }], + }, + }, + }); +}; From 45862c18257476d427c0ed2d05eb38cfc5ff06ca Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:09:19 +0200 Subject: [PATCH 06/43] mobx-persist --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 62d4498..b095c88 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hermes-engine": "0.5.2-rc1", "lodash": "^4.17.20", "mobx": "^5.15.7", + "mobx-persist": "^0.4.1", "mobx-react": "^6.3.0", "mobx-state-tree": "^3.17.2", "react": "^16.13.1", @@ -39,6 +40,7 @@ }, "devDependencies": { "@babel/core": "^7.11.6", + "@babel/plugin-proposal-decorators": "^7.10.5", "@babel/runtime": "^7.11.2", "@react-native-community/eslint-config": "^2.0.0", "@types/jest": "^26.0.14", From 10bb4510699c49a37375a843a01c2f747344a885 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:09:28 +0200 Subject: [PATCH 07/43] expo-updates --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b095c88..e546fb5 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@react-native-community/async-storage": "^1.12.0", "expo": "^39.0.3", "expo-network": "^2.3.0", - "expo-updates": "^0.3.4", + "expo-updates": "^0.3.5", "hermes-engine": "0.5.2-rc1", "lodash": "^4.17.20", "mobx": "^5.15.7", From 9710e8cb06ffcf53813a40015bb53e88afc0ff17 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:09:38 +0200 Subject: [PATCH 08/43] reanimated v2 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e546fb5..12e6272 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "react-native-gesture-handler": "^1.8.0", "react-native-navigation": "^7.1.0", "react-native-navigation-hooks": "^6.2.0", + "react-native-reanimated": "^2.0.0-alpha.7", "react-native-unimodules": "^0.11.0", "react-native-vector-icons": "^7.1.0", "react-redux": "^7.2.1", From b056241f14c76b0faa459b909443947b6ce1f312 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:09:48 +0200 Subject: [PATCH 09/43] reanimated example added --- src/components/Reanimated2.tsx | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/components/Reanimated2.tsx diff --git a/src/components/Reanimated2.tsx b/src/components/Reanimated2.tsx new file mode 100644 index 0000000..e1a160c --- /dev/null +++ b/src/components/Reanimated2.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import Animated, { withSpring, useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; +import { StyleSheet, View } from 'react-native'; +import { ButtonTitle } from './Button'; + +const Reanimated2: React.FC = () => { + const offset = useSharedValue(0); + + const animatedStyles = useAnimatedStyle(() => { + return { + transform: [{ translateX: offset.value * 255 }] + } + }); + + return ( + + + { + offset.value = withSpring(Math.random()); + }} + /> + + ) +} + +const styles = StyleSheet.create({ + container: { + padding: 8, + justifyContent: 'center', + }, + box: { + width: 100, + height: 100, + borderRadius: 20, + margin: 8, + backgroundColor: '#001a72', + }, +}) + +export default Reanimated2; \ No newline at end of file From be3b0f278fb63e5f080040e541897ff69e1576cd Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:09:57 +0200 Subject: [PATCH 10/43] global app constants --- src/utils/constants.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/utils/constants.ts diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 0000000..077379f --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,20 @@ +class Contants { + ScreenNames = { + CounterScreen: 'rnn_starter.CounterScreen', + ExpoScreen: 'rnn_starter.ExpoScreen', + AboutScreen: 'rnn_starter.AboutScreen', + } + BottomTabsTitles = { + tab1: 'Counter', + tab2: 'Expo', + tab3: 'About', + } + CounterScreen = { + decButtonId: 'decButtonId', + decButtonTitle: 'Dec', + incButtonId: 'incButtonId', + incButtonTitle: 'Inc', + } +} + +export default new Contants(); \ No newline at end of file From ee7cf5460a8d84997aea27059fa797bce890c729 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:10:04 +0200 Subject: [PATCH 11/43] global app types --- src/utils/types.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/utils/types.d.ts diff --git a/src/utils/types.d.ts b/src/utils/types.d.ts new file mode 100644 index 0000000..fd678ad --- /dev/null +++ b/src/utils/types.d.ts @@ -0,0 +1,6 @@ + +interface IStore { + STORAGE_ID: string; +} + +// export {} \ No newline at end of file From 59c0e1fddde9f84adcccf03255033d38dc229f25 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:10:14 +0200 Subject: [PATCH 12/43] experimentalDecorators --> true --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index d8a90ff..1713929 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -42,7 +42,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ @@ -52,7 +52,7 @@ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ }, "exclude": [ From b48e6e14c94b9a41e026c2343b710c1ac46e5b2f Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:10:25 +0200 Subject: [PATCH 13/43] packages lock --- yarn.lock | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index eecb59e..2f4de08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -773,7 +773,7 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-proposal-decorators@^7.6.0": +"@babel/plugin-proposal-decorators@^7.10.5", "@babel/plugin-proposal-decorators@^7.6.0": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ== @@ -1383,6 +1383,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-object-assign@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.10.4.tgz#f7c8f54ce8052ccd8b9da9b3358848423221c338" + integrity sha512-6zccDhYEICfMeQqIjuY5G09/yhKzG30DKHJeYBQUHIsJH7c2jXSGvgwRalufLAXAq432OSlsEfAOLlzEsQzxVw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-object-super@^7.0.0": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" @@ -4600,10 +4607,10 @@ expo-sqlite@~8.4.0: "@expo/websql" "^1.0.1" lodash "^4.17.15" -expo-updates@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.3.4.tgz#6892bd244ec3f4fd07041d5a7175ab484c1c4d11" - integrity sha512-8/V0v1gp4elQvhBp8N40h+L0k5ldaHxmEKDJinQgn5NBi6YZ53MVNA3QjGhAfhKLUkrlCyaLt4RKUk5o1cvtZw== +expo-updates@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.3.5.tgz#cd9aafeb5cbe16399df7d39243d00d330d99e674" + integrity sha512-O+W0MfDZPhhCYKFtLL85ifd+YAm3rFKALWzmyKOSGBb/L+8CBPQsbbB3mdvji0V2NCqq0+jrBvSCmNa5LRD41w== dependencies: "@expo/metro-config" "^0.1.16" fbemitter "^2.1.1" @@ -7125,6 +7132,13 @@ mkdirp@^0.5.3: dependencies: minimist "^1.2.5" +mobx-persist@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/mobx-persist/-/mobx-persist-0.4.1.tgz#fe5790eda04e0ee0b91c512d1347104e09363f9a" + integrity sha512-cPxVuZTY5neesxOV/Z25SF4+/NbC6wfHOe9HT5XoSEkAALNxNE41yDK48peJQ3iK14m0G2ywQRklCXeV3wftnQ== + dependencies: + serializr "^1.1.11" + mobx-react-lite@>=2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.2.1.tgz#9c05dd799005d29ec1671ae86ca30b3ab5411055" @@ -7969,6 +7983,15 @@ react-native-navigation@^7.1.0: react-lifecycles-compat "2.0.0" tslib "1.9.3" +react-native-reanimated@^2.0.0-alpha.7: + version "2.0.0-alpha.7" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.0.0-alpha.7.tgz#20b9852df6aeab9fc0efb938527bf47ae954370a" + integrity sha512-dAOkt087c3uSm9oTe8cDIKHbZ+D6X951OG/SWzGAOWaR9GH/JW5N++YjmxQX2IK1vFlYNCMfBnxjTuWT6QHfmQ== + dependencies: + "@babel/plugin-transform-object-assign" "^7.10.4" + fbjs "^1.0.0" + string-hash-64 "^1.0.3" + react-native-safe-area-context@3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-3.1.4.tgz#9b7f883a5ae8da6218d17467a350434005893602" @@ -8537,6 +8560,11 @@ serialize-error@^2.1.0: resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= +serializr@^1.1.11: + version "1.5.4" + resolved "https://registry.yarnpkg.com/serializr/-/serializr-1.5.4.tgz#3ddf1cb7b4465c9c3f72bab0cbc41e09dbe50c33" + integrity sha512-ImLlkNfNSSv9d7Ah1j/yrPk1FHbRC1zkD7URcgx/8M1eYHGUxGf/Ig/d8ML04ifqN7QesyYKsfLYA4xjAVAR/g== + serve-static@^1.13.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -8860,6 +8888,11 @@ stream-buffers@~2.2.0: resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= +string-hash-64@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322" + integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw== + string-length@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" From 0d1449e536b8d92b44facf94778040ccf37ddd66 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:10:52 +0200 Subject: [PATCH 14/43] excluding some packages --- android/app/build.gradle | 2 +- .../main/java/com/rnn_starter/generated/BasePackageList.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2943c92..0e1e638 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -193,7 +193,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - addUnimodulesDependencies([exclude: ['expo-splash-screen']]) + addUnimodulesDependencies([exclude: ['expo-splash-screen', 'expo-location', 'expo-file-system']]) implementation 'com.android.support:multidex:1.0.3' debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { diff --git a/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java b/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java index edaf066..17d50ea 100644 --- a/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java +++ b/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java @@ -9,12 +9,10 @@ public List getPackageList() { return Arrays.asList( new expo.modules.constants.ConstantsPackage(), new expo.modules.errorrecovery.ErrorRecoveryPackage(), - new expo.modules.filesystem.FileSystemPackage(), new expo.modules.font.FontLoaderPackage(), new expo.modules.imageloader.ImageLoaderPackage(), new expo.modules.keepawake.KeepAwakePackage(), new expo.modules.lineargradient.LinearGradientPackage(), - new expo.modules.location.LocationPackage(), new expo.modules.network.NetworkPackage(), new expo.modules.permissions.PermissionsPackage(), new expo.modules.sqlite.SQLitePackage(), From 5ab01b7b5b12f30948bbe8c6cd3c2ee2c924ca30 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:11:04 +0200 Subject: [PATCH 15/43] new gradle version --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index c01cd13..acf9299 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,7 +14,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61" - classpath("com.android.tools.build:gradle:3.5.3") + classpath('com.android.tools.build:gradle:4.0.0') // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } From c641c4d7e6eb79d814e9b34ef67f11698b19ff86 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:12:02 +0200 Subject: [PATCH 16/43] reanimated v2 integration --- .../java/com/rnn_starter/MainApplication.java | 8 ++ babel.config.js | 8 +- ios/Podfile.lock | 37 ++++++++- ios/rnn_starter.xcodeproj/project.pbxproj | 14 ++-- .../{AppDelegate.m => AppDelegate.mm} | 81 ++++++++++++++++++- ios/rnn_starter/Info.plist | 29 +++---- 6 files changed, 153 insertions(+), 24 deletions(-) rename ios/rnn_starter/{AppDelegate.m => AppDelegate.mm} (56%) diff --git a/android/app/src/main/java/com/rnn_starter/MainApplication.java b/android/app/src/main/java/com/rnn_starter/MainApplication.java index 2f944d9..af67137 100644 --- a/android/app/src/main/java/com/rnn_starter/MainApplication.java +++ b/android/app/src/main/java/com/rnn_starter/MainApplication.java @@ -23,6 +23,9 @@ import expo.modules.updates.UpdatesController; import javax.annotation.Nullable; +import com.facebook.react.bridge.JSIModulePackage; +import com.swmansion.reanimated.ReanimatedJSIModulePackage; + public class MainApplication extends NavigationApplication { private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(new BasePackageList().getPackageList(), null); @@ -56,6 +59,11 @@ protected String getJSMainModuleName() { return "index"; } + @Override + protected JSIModulePackage getJSIModulePackage() { + return new ReanimatedJSIModulePackage(); + } + @Override protected @Nullable String getJSBundleFile() { if (BuildConfig.DEBUG) { diff --git a/babel.config.js b/babel.config.js index f842b77..e820992 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,9 @@ module.exports = { - presets: ['module:metro-react-native-babel-preset'], + presets: [ + 'module:metro-react-native-babel-preset', + ], + plugins: [ + ["@babel/plugin-proposal-decorators", { "legacy": true }], + 'react-native-reanimated/plugin', + ] }; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d2bb2b0..032b1e3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -34,7 +34,7 @@ PODS: - EXSQLite (8.4.0): - UMCore - UMFileSystemInterface - - EXUpdates (0.3.4): + - EXUpdates (0.3.5): - React - UMCore - FBLazyVector (0.63.2) @@ -339,6 +339,35 @@ PODS: - React - RNGestureHandler (1.8.0): - React + - RNReanimated (2.0.0-alpha.7): + - DoubleConversion + - FBLazyVector + - FBReactNativeSpec + - Folly + - glog + - RCTRequired + - RCTTypeSafety + - React + - React-callinvoker + - React-Core + - React-Core/DevSupport + - React-Core/RCTWebSocket + - React-CoreModules + - React-cxxreact + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-RCTActionSheet + - React-RCTAnimation + - React-RCTBlob + - React-RCTImage + - React-RCTLinking + - React-RCTNetwork + - React-RCTSettings + - React-RCTText + - React-RCTVibration + - ReactCommon/turbomodule/core + - Yoga - RNVectorIcons (7.1.0): - React - UMAppLoader (1.3.0) @@ -424,6 +453,7 @@ DEPENDENCIES: - ReactNativeNavigation (from `../node_modules/react-native-navigation`) - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) + - RNReanimated (from `../node_modules/react-native-reanimated`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) - UMAppLoader (from `../node_modules/unimodules-app-loader/ios`) - UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`) @@ -536,6 +566,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/async-storage" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" + RNReanimated: + :path: "../node_modules/react-native-reanimated" RNVectorIcons: :path: "../node_modules/react-native-vector-icons" UMAppLoader: @@ -583,7 +615,7 @@ SPEC CHECKSUMS: EXNetwork: 05c1b87ba7077cb3eea7a3968640e5007e32eb6b EXPermissions: 30cbe5b72bd209b65c00884180ad058a60bb413d EXSQLite: d0d8677c13e60d5db2812387f059c697be27d3fb - EXUpdates: 9ed0693ab1c8df9f0472cfb4549d5edccdf05849 + EXUpdates: 74b39409f68eca207075d87b0077bdf37865a8bf FBLazyVector: 3ef4a7f62e7db01092f9d517d2ebc0d0677c4a37 FBReactNativeSpec: dc7fa9088f0f2a998503a352b0554d69a4391c5a Flipper: 33585e2d9810fe5528346be33bcf71b37bb7ae13 @@ -619,6 +651,7 @@ SPEC CHECKSUMS: ReactNativeNavigation: 65025dab27b404053678593b2450ed7a022e3173 RNCAsyncStorage: 2a692bcb9b69b76a2f1a95f33db057129700af64 RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39 + RNReanimated: c40c29429b22a1564e505b789132cfcef456c6bd RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59 UMAppLoader: 5db5dc03176c6238da9c8b3657a370137882a4ad UMBarCodeScannerInterface: 017672479f93de88d94c3a50dfb66b5348b65989 diff --git a/ios/rnn_starter.xcodeproj/project.pbxproj b/ios/rnn_starter.xcodeproj/project.pbxproj index 232c9d7..b587cee 100644 --- a/ios/rnn_starter.xcodeproj/project.pbxproj +++ b/ios/rnn_starter.xcodeproj/project.pbxproj @@ -9,10 +9,10 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* rnn_starterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* rnn_starterTests.m */; }; 0CD3219B24E30A1B002DB652 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0CD3219A24E30A1B002DB652 /* Expo.plist */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2DCD954D1E0B4F2C00145EB5 /* rnn_starterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* rnn_starterTests.m */; }; @@ -50,7 +50,7 @@ 0CD3219A24E30A1B002DB652 /* Expo.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Expo.plist; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* rnn_starter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rnn_starter.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = rnn_starter/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = rnn_starter/AppDelegate.m; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = rnn_starter/AppDelegate.mm; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = rnn_starter/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = rnn_starter/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = rnn_starter/main.m; sourceTree = ""; }; @@ -138,7 +138,7 @@ 0CD3219724E30999002DB652 /* Supporting */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 13B07FB01A68108700A75B9A /* AppDelegate.mm */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, @@ -644,7 +644,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -654,7 +654,7 @@ buildActionMask = 2147483647; files = ( 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, - 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -746,6 +746,7 @@ PRODUCT_NAME = rnn_starter; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -769,6 +770,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = rnn_starter; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/rnn_starter/AppDelegate.m b/ios/rnn_starter/AppDelegate.mm similarity index 56% rename from ios/rnn_starter/AppDelegate.m rename to ios/rnn_starter/AppDelegate.mm index c21aa35..10d013f 100644 --- a/ios/rnn_starter/AppDelegate.m +++ b/ios/rnn_starter/AppDelegate.mm @@ -8,6 +8,20 @@ #import #import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + #ifdef FB_SONARKIT_ENABLED #import #import @@ -27,7 +41,9 @@ static void InitializeFlipper(UIApplication *application) { } #endif -@interface AppDelegate () +@interface AppDelegate () { + RCTTurboModuleManager *_turboModuleManager; +} @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; @property (nonatomic, strong) NSDictionary *launchOptions; @@ -38,6 +54,8 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + RCTEnableTurboModule(YES); + #ifdef FB_SONARKIT_ENABLED InitializeFlipper(application); #endif @@ -105,4 +123,65 @@ - (void)appController:(EXUpdatesAppController *)appController didStartWithSucces #endif } +// pragma mark - Reanimated 2 setup + +- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge +{ + _bridge_reanimated = bridge; + _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge + delegate:self + jsInvoker:bridge.jsCallInvoker]; + #if RCT_DEV + [_turboModuleManager moduleForName:"RCTDevMenu"]; // <- add + #endif + __weak __typeof(self) weakSelf = self; + return std::make_unique([weakSelf, bridge](facebook::jsi::Runtime &runtime) { + if (!bridge) { + return; + } + __typeof(self) strongSelf = weakSelf; + if (strongSelf) { + [strongSelf->_turboModuleManager installJSBindingWithRuntime:&runtime]; + } + }); +} + +- (Class)getModuleClassFromName:(const char *)name +{ + return facebook::react::REATurboModuleClassProvider(name); +} + +- (std::shared_ptr)getTurboModule:(const std::string &)name + jsInvoker:(std::shared_ptr)jsInvoker +{ + return facebook::react::REATurboModuleProvider(name, jsInvoker); +} + +- (std::shared_ptr)getTurboModule:(const std::string &)name + instance:(id)instance + jsInvoker:(std::shared_ptr)jsInvoker +{ + return facebook::react::REATurboModuleProvider(name, instance, jsInvoker); +} + +- (id)getModuleInstanceFromClass:(Class)moduleClass +{ + if (moduleClass == RCTImageLoader.class) { + return [[moduleClass alloc] initWithRedirectDelegate:nil loadersProvider:^NSArray> *{ + return @[[RCTLocalAssetImageLoader new]]; + } decodersProvider:^NSArray> *{ + return @[[RCTGIFImageDecoder new]]; + }]; + } else if (moduleClass == RCTNetworking.class) { + return [[moduleClass alloc] initWithHandlersProvider:^NSArray> *{ + return @[ + [RCTHTTPRequestHandler new], + [RCTDataRequestHandler new], + [RCTFileRequestHandler new], + ]; + }]; + } + return [moduleClass new]; +} + @end diff --git a/ios/rnn_starter/Info.plist b/ios/rnn_starter/Info.plist index 7054f6d..68f1685 100644 --- a/ios/rnn_starter/Info.plist +++ b/ios/rnn_starter/Info.plist @@ -39,20 +39,6 @@ NSLocationWhenInUseUsageDescription - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - UIAppFonts AntDesign.ttf @@ -72,5 +58,20 @@ Zocial.ttf Fontisto.ttf + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown + + UIViewControllerBasedStatusBarAppearance + From ff5b4ff19d99f5ae05414e2507817ef6198f3f95 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:12:14 +0200 Subject: [PATCH 17/43] help button components added --- src/components/Button.tsx | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/components/Button.tsx diff --git a/src/components/Button.tsx b/src/components/Button.tsx new file mode 100644 index 0000000..a5c4e50 --- /dev/null +++ b/src/components/Button.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { + Text, + View, + StyleSheet, +} from 'react-native'; +import AntIcon from 'react-native-vector-icons/AntDesign'; +import { TouchableOpacity } from 'react-native-gesture-handler'; + +type ButtonTitleProps = { + title: string; + onPress: () => void; +} + +type ButtonIconProps = { + icon: string; + onPress: () => void; +} + +export const ButtonTitle: React.FC = ({ + title, + onPress, +}) => { + return ( + <> + + + {title} + + + + ) +} + +export const ButtonIcon: React.FC = ({ + icon, + onPress, +}) => { + return ( + <> + + + + + + + ) +} + +const styles = StyleSheet.create({ + buttonContainer: { + margin: 8, + }, + buttonIcon: { + fontSize: 32, + }, + text: { + fontSize: 24, + margin: 8, + textAlign: 'center', + }, +}); \ No newline at end of file From 5017f61a5f56a13091dcc4553604045c00c62c8f Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:12:24 +0200 Subject: [PATCH 18/43] couter & expo screens --- src/screens/CounterScreen.tsx | 82 +++++++++++++++++++++++++++++++++ src/screens/ExpoScreen.tsx | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 src/screens/CounterScreen.tsx create mode 100644 src/screens/ExpoScreen.tsx diff --git a/src/screens/CounterScreen.tsx b/src/screens/CounterScreen.tsx new file mode 100644 index 0000000..2ff7a01 --- /dev/null +++ b/src/screens/CounterScreen.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { + SafeAreaView, + Text, + View, + StyleSheet, + Platform, +} from 'react-native'; +import { observer } from 'mobx-react'; +import Icon from 'react-native-vector-icons/AntDesign'; +import { NavigationFunctionComponent } from 'react-native-navigation'; +import { useNavigationButtonPress } from 'react-native-navigation-hooks/dist/hooks'; +import { TouchableOpacity } from 'react-native-gesture-handler'; + +import { useStores } from '../store'; +import Constants from '../utils/constants'; +import { ButtonIcon } from '../components/Button'; + +const CounterScreen: NavigationFunctionComponent = observer(({ + componentId, +}) => { + const { counter } = useStores(); + + useNavigationButtonPress(counter.decrement, componentId, Constants.CounterScreen.decButtonId); + useNavigationButtonPress(counter.increment, componentId, Constants.CounterScreen.incButtonId); + + return ( + + + + + { counter.value } + + + + + ); +}); + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + counterContainer: { + padding: 8, + flexDirection: 'row', + width: '80%', + justifyContent: 'space-between', + alignItems: 'center', + }, + text: { + fontSize: 80, + margin: 8, + textAlign: 'center', + }, +}); + +CounterScreen.options = props => ({ + topBar: { + leftButtons: Platform.OS === 'ios' ? [{ + id: Constants.CounterScreen.decButtonId, + text: Constants.CounterScreen.decButtonTitle, + }] : [], + rightButtons: Platform.OS === 'ios' ? [{ + id: Constants.CounterScreen.incButtonId, + text: Constants.CounterScreen.incButtonTitle, + }] : [{ + id: Constants.CounterScreen.incButtonId, + text: Constants.CounterScreen.incButtonTitle, + }, { + id: Constants.CounterScreen.decButtonId, + text: Constants.CounterScreen.decButtonTitle, + }], + title: { + text: 'Counter', + }, + }, +}); + +export default CounterScreen; diff --git a/src/screens/ExpoScreen.tsx b/src/screens/ExpoScreen.tsx new file mode 100644 index 0000000..e5249c4 --- /dev/null +++ b/src/screens/ExpoScreen.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { + SafeAreaView, + Text, + View, + StyleSheet, +} from 'react-native'; +import { observer } from 'mobx-react'; +import { NavigationFunctionComponent } from 'react-native-navigation'; +import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks/dist/hooks'; + +// EXPO modules +import { Constants as ExpoConstants } from 'react-native-unimodules'; +import * as Network from 'expo-network'; + +import Constants from '../utils/constants'; +import { useStores } from '../store'; +import Reanimated2 from '../components/Reanimated2'; + + +const ExpoScreen: NavigationFunctionComponent = observer(({ + componentId, +}) => { + const { ui } = useStores(); + + useNavigationComponentDidAppear(() => { + getNetworkType(); + }, componentId); + + const getNetworkType = async () => { + try { + const networkState = await Network.getNetworkStateAsync(); + + ui.networkType = networkState.type; + } catch (e) { + console.log(e); + } + } + + return ( + + + + { 'From Expo SDK' } + + Device ID: {ExpoConstants.deviceId} + Network type: {ui.networkType} + + + + + { 'Reanimated 2' } + + + + + + ); +}); + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + section: { + padding: 16, + }, + header: { + fontSize: 32, + fontWeight: 'bold', + }, + text: { + fontSize: 22, + margin: 8, + } +}); + +ExpoScreen.options = props => ({ + topBar: { + title: { + text: 'Expo', + }, + }, +}); + +export default ExpoScreen; From e91579d35d83c22600dc0cd9cef6f701b9ec9024 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Mon, 5 Oct 2020 00:40:04 +0200 Subject: [PATCH 19/43] code formatting --- src/App.ts | 22 ++-------------------- src/screens/CounterScreen.tsx | 2 -- src/utils/constants.ts | 9 +++++++-- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/App.ts b/src/App.ts index 07db892..e37ab9f 100644 --- a/src/App.ts +++ b/src/App.ts @@ -14,7 +14,6 @@ const Screens = new Map>(); Screens.set(Constants.ScreenNames.CounterScreen, CounterScreen); Screens.set(Constants.ScreenNames.ExpoScreen, ExpoScreen); -Screens.set(Constants.ScreenNames.AboutScreen, CounterScreen); // Register screens Screens.forEach((C, key) => { @@ -35,15 +34,13 @@ export const startApp = async () => { await hydrateStores(); // getting icons for tabs as they have to be as image sources - const [tab1, tab2, tab3] = await Promise.all([ + const [tab1, tab2] = await Promise.all([ Ionicons.getImageSource('ios-duplicate-outline', 25), Ionicons.getImageSource('ios-rocket-outline', 25), - Ionicons.getImageSource('ios-information-circle-outline', 25), ]); - const [tab1Selected, tab2Selected, tab3Selected] = await Promise.all([ + const [tab1Selected, tab2Selected] = await Promise.all([ Ionicons.getImageSource('ios-duplicate', 25), Ionicons.getImageSource('ios-rocket', 25), - Ionicons.getImageSource('ios-information-circle', 25), ]); // settings default options for navigation, like colors @@ -91,21 +88,6 @@ export const startApp = async () => { }, }, }, - }, { - stack: { - children: [{ - component: { - name: Constants.ScreenNames.AboutScreen, - }, - }], - options: { - bottomTab: { - text: Constants.BottomTabsTitles.tab3, - icon: tab3, - selectedIcon: tab3Selected, - }, - }, - }, }], }, }, diff --git a/src/screens/CounterScreen.tsx b/src/screens/CounterScreen.tsx index 2ff7a01..81e0bdf 100644 --- a/src/screens/CounterScreen.tsx +++ b/src/screens/CounterScreen.tsx @@ -7,10 +7,8 @@ import { Platform, } from 'react-native'; import { observer } from 'mobx-react'; -import Icon from 'react-native-vector-icons/AntDesign'; import { NavigationFunctionComponent } from 'react-native-navigation'; import { useNavigationButtonPress } from 'react-native-navigation-hooks/dist/hooks'; -import { TouchableOpacity } from 'react-native-gesture-handler'; import { useStores } from '../store'; import Constants from '../utils/constants'; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 077379f..fc5c091 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -2,13 +2,18 @@ class Contants { ScreenNames = { CounterScreen: 'rnn_starter.CounterScreen', ExpoScreen: 'rnn_starter.ExpoScreen', - AboutScreen: 'rnn_starter.AboutScreen', } + BottomTabsTitles = { tab1: 'Counter', tab2: 'Expo', - tab3: 'About', } + + ScreenTitles = { + CounterScreen: 'Counter', + ExpoScreen: 'Expo', + } + CounterScreen = { decButtonId: 'decButtonId', decButtonTitle: 'Dec', From 6ed2358ea83b3d7b9ee15ee9daad528a944a4f58 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:42:56 +0200 Subject: [PATCH 20/43] excluded deps back --- android/app/build.gradle | 2 +- .../main/java/com/rnn_starter/generated/BasePackageList.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 0e1e638..2943c92 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -193,7 +193,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - addUnimodulesDependencies([exclude: ['expo-splash-screen', 'expo-location', 'expo-file-system']]) + addUnimodulesDependencies([exclude: ['expo-splash-screen']]) implementation 'com.android.support:multidex:1.0.3' debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { diff --git a/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java b/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java index 17d50ea..edaf066 100644 --- a/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java +++ b/android/app/src/main/java/com/rnn_starter/generated/BasePackageList.java @@ -9,10 +9,12 @@ public List getPackageList() { return Arrays.asList( new expo.modules.constants.ConstantsPackage(), new expo.modules.errorrecovery.ErrorRecoveryPackage(), + new expo.modules.filesystem.FileSystemPackage(), new expo.modules.font.FontLoaderPackage(), new expo.modules.imageloader.ImageLoaderPackage(), new expo.modules.keepawake.KeepAwakePackage(), new expo.modules.lineargradient.LinearGradientPackage(), + new expo.modules.location.LocationPackage(), new expo.modules.network.NetworkPackage(), new expo.modules.permissions.PermissionsPackage(), new expo.modules.sqlite.SQLitePackage(), From 75fdcc713ce6baede44c5ce5e7c5c4b5408b5630 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:43:21 +0200 Subject: [PATCH 21/43] nav setup --- src/App.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/App.ts b/src/App.ts index e37ab9f..9a45490 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,14 +1,14 @@ import { Navigation } from 'react-native-navigation'; import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; import Ionicons from 'react-native-vector-icons/Ionicons'; -import 'mobx-react-lite/batchingForReactNative'; import Constants from './utils/constants'; import CounterScreen from './screens/CounterScreen'; import ExpoScreen from './screens/ExpoScreen'; -import { withStoreProvider, hydrateStores } from './store'; +import { withStoreProvider, hydrateStores } from './stores'; +import { withServicesProvider, initServices } from './services'; const Screens = new Map>(); @@ -21,7 +21,8 @@ Screens.forEach((C, key) => { key, () => gestureHandlerRootHOC( - withStoreProvider(C)), + withStoreProvider( + withServicesProvider(C))), () => C, ); }); @@ -33,6 +34,9 @@ export const startApp = async () => { // rehydrate stores await hydrateStores(); + // init services + await initServices(); + // getting icons for tabs as they have to be as image sources const [tab1, tab2] = await Promise.all([ Ionicons.getImageSource('ios-duplicate-outline', 25), @@ -43,18 +47,6 @@ export const startApp = async () => { Ionicons.getImageSource('ios-rocket', 25), ]); - // settings default options for navigation, like colors - Navigation.setDefaultOptions({ - layout: { - orientation: ['portrait'], - // backgroundColor: 'black', - // componentBackgroundColor: 'black', - }, - bottomTabs: { - titleDisplayMode: 'alwaysShow', - } - }); - Navigation.setRoot({ root: { bottomTabs: { @@ -70,6 +62,10 @@ export const startApp = async () => { text: Constants.BottomTabsTitles.tab1, icon: tab1, selectedIcon: tab1Selected, + iconColor: Constants.colors.blue, + textColor: Constants.colors.blue, + selectedIconColor: Constants.colors.blue, + selectedTextColor: Constants.colors.blue, }, }, }, @@ -85,6 +81,10 @@ export const startApp = async () => { text: Constants.BottomTabsTitles.tab2, icon: tab2, selectedIcon: tab2Selected, + iconColor: Constants.colors.blue, + textColor: Constants.colors.blue, + selectedIconColor: Constants.colors.blue, + selectedTextColor: Constants.colors.blue, }, }, }, From c207a84a58758d9ed752a3a2141553580682a34c Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:43:47 +0200 Subject: [PATCH 22/43] more types --- src/components/Button.tsx | 21 +++++++++++++++------ src/utils/types.d.ts | 16 ++++++++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index a5c4e50..c383e56 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -7,8 +7,11 @@ import { import AntIcon from 'react-native-vector-icons/AntDesign'; import { TouchableOpacity } from 'react-native-gesture-handler'; +import useStyles, { ThemedStylesFuncType } from '../utils/useStyles'; + type ButtonTitleProps = { title: string; + centered?: boolean; onPress: () => void; } @@ -19,13 +22,16 @@ type ButtonIconProps = { export const ButtonTitle: React.FC = ({ title, + centered = false, onPress, }) => { + const styles = useStyles(_styles); + return ( <> - {title} + {title} @@ -36,6 +42,8 @@ export const ButtonIcon: React.FC = ({ icon, onPress, }) => { + const styles = useStyles(_styles); + return ( <> @@ -47,16 +55,17 @@ export const ButtonIcon: React.FC = ({ ) } -const styles = StyleSheet.create({ +const _styles: ThemedStylesFuncType = theme => StyleSheet.create({ buttonContainer: { - margin: 8, + margin: theme.sizes.s, }, buttonIcon: { fontSize: 32, + color: theme.colors.text, }, text: { - fontSize: 24, - margin: 8, - textAlign: 'center', + fontSize: 18, + margin: theme.sizes.s, + color: theme.colors.text, }, }); \ No newline at end of file diff --git a/src/utils/types.d.ts b/src/utils/types.d.ts index fd678ad..ec12dd7 100644 --- a/src/utils/types.d.ts +++ b/src/utils/types.d.ts @@ -1,6 +1,18 @@ - interface IStore { STORAGE_ID: string; } -// export {} \ No newline at end of file +interface IService { + init: () => Promise; +} + +type ThemeNameType = 'dark' | 'light'; + +type ThemeType = { + colors: any; + sizes: any; +} + +type ThemesType = { + [key in ThemeNameType]: ThemeType; +}; \ No newline at end of file From 61b9dac6674bbfee4ecb0f9d9f0bd9f0b7fe8298 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:43:54 +0200 Subject: [PATCH 23/43] color & sizes --- src/utils/constants.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index fc5c091..68e3eae 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -20,6 +20,21 @@ class Contants { incButtonId: 'incButtonId', incButtonTitle: 'Inc', } + + // Styles + colors = { + black: '#000', + white: '#fff', + lightGrey: '#dcdde1', + blue: '#4d7198', + yellow: '#fbc531', + } + sizes = { + s: 8, + m: 16, + l: 32, + xl: 40, + } } export default new Contants(); \ No newline at end of file From 85311df26cd08e2e50c257bafc4154a7fd4bf212 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:44:18 +0200 Subject: [PATCH 24/43] /store --> /stores --- src/store/uiStore.ts | 10 ---------- src/{store => stores}/counterStore.ts | 0 src/{store => stores}/index.tsx | 4 ++-- src/stores/uiStore.ts | 18 ++++++++++++++++++ 4 files changed, 20 insertions(+), 12 deletions(-) delete mode 100644 src/store/uiStore.ts rename src/{store => stores}/counterStore.ts (100%) rename src/{store => stores}/index.tsx (93%) create mode 100644 src/stores/uiStore.ts diff --git a/src/store/uiStore.ts b/src/store/uiStore.ts deleted file mode 100644 index 22489b6..0000000 --- a/src/store/uiStore.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { observable } from 'mobx'; -import { persist } from 'mobx-persist'; - -class UIStore implements IStore { - STORAGE_ID = 'UIStore'; - - @observable networkType: string | undefined = ''; -} - -export default new UIStore(); \ No newline at end of file diff --git a/src/store/counterStore.ts b/src/stores/counterStore.ts similarity index 100% rename from src/store/counterStore.ts rename to src/stores/counterStore.ts diff --git a/src/store/index.tsx b/src/stores/index.tsx similarity index 93% rename from src/store/index.tsx rename to src/stores/index.tsx index 0ad1eb0..f41c1fd 100644 --- a/src/store/index.tsx +++ b/src/stores/index.tsx @@ -5,11 +5,11 @@ import { create } from 'mobx-persist'; import CounterStore from './counterStore'; import UIStore from './uiStore'; -const stores = { +export const stores = { counter: CounterStore, ui: UIStore, }; - +// const f_stores = () => stores; const storeContext = React.createContext(stores); export const withStoreProvider = (C: React.FC) => (props: any) => { diff --git a/src/stores/uiStore.ts b/src/stores/uiStore.ts new file mode 100644 index 0000000..80d2dff --- /dev/null +++ b/src/stores/uiStore.ts @@ -0,0 +1,18 @@ +import { observable, action } from 'mobx'; +import { persist } from 'mobx-persist'; + +import { generateTheme } from '../utils/useStyles'; + +class UIStore implements IStore { + STORAGE_ID = 'UIStore'; + + @observable networkType: string | undefined = ''; + @observable theme = generateTheme(); // current theme, set in services.darkmode + @observable componentId = ''; // current component id + + @action toggleThemeTo = (theme: ThemeType) => { + this.theme = theme; + } +} + +export default new UIStore(); \ No newline at end of file From 8a8bcbe04d94701212410124fceb012fbdbaf064 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:44:35 +0200 Subject: [PATCH 25/43] services --- src/services/darkmode.ts | 20 +++++++++++++ src/services/index.tsx | 28 ++++++++++++++++++ src/services/navigation.ts | 60 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/services/darkmode.ts create mode 100644 src/services/index.tsx create mode 100644 src/services/navigation.ts diff --git a/src/services/darkmode.ts b/src/services/darkmode.ts new file mode 100644 index 0000000..b45b948 --- /dev/null +++ b/src/services/darkmode.ts @@ -0,0 +1,20 @@ +import { Appearance } from 'react-native'; + +import { stores } from '../stores'; +import { generateTheme } from '../utils/useStyles'; + +class DarkModeService implements IService { + init = async () => { + await this.setListener(); + } + + private setListener = async () => { + Appearance.addChangeListener(ap => { + const cs = ap.colorScheme as ThemeNameType; + + stores.ui.toggleThemeTo(generateTheme(cs)); + }) + } +} + +export default new DarkModeService(); \ No newline at end of file diff --git a/src/services/index.tsx b/src/services/index.tsx new file mode 100644 index 0000000..49b5666 --- /dev/null +++ b/src/services/index.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +import NavigationService from './navigation'; +import DarkmodeService from './darkmode'; + +export const services = { + navigation: NavigationService, + // darkmode: DarkmodeService, +}; + +const servicesContext = React.createContext(services); + +export const withServicesProvider = (C: React.FC) => (props: any) => { + return ( + + + + ); +}; + +export const useServices = () => React.useContext(servicesContext); + +// one method to init all services, you should add it manually +// you can use services for having one for metrics or handling navigation actions +export const initServices = async () => { + await services.navigation.init(); + // await services.darkmode.init(); +}; \ No newline at end of file diff --git a/src/services/navigation.ts b/src/services/navigation.ts new file mode 100644 index 0000000..1913eb8 --- /dev/null +++ b/src/services/navigation.ts @@ -0,0 +1,60 @@ +import { Navigation } from 'react-native-navigation'; +import Constants from '../utils/constants'; +import { stores } from '../stores'; + +class NavigationService implements IService { + init = async () => { + await this.setUpComponentIdListener(); + await this.setDefaultOptions(); + } + + dismissModal = (cId: string) => { + Navigation.dismissModal(cId); + } + + dismissAllModals = () => { + Navigation.dismissAllModals(); + } + + pushExpo = (cId: string) => { + this.push(cId, Constants.ScreenNames.ExpoScreen); + } + + showExpo = () => { + this.show(Constants.ScreenNames.ExpoScreen); + } + + private push = (cId: string, cName: string) => { + Navigation.push(cId, { + component:{name: cName} + }) + } + + private show = (cName: string) => { + Navigation.showModal({ + stack: { + children: [{component:{name: cName}}] + } + }); + } + + // Listeners + private setUpComponentIdListener = async () => { + Navigation.events().registerComponentDidAppearListener(async e => { + stores.ui.componentId = e.componentId; + }); + } + + private setDefaultOptions = async () => { + Navigation.setDefaultOptions({ + layout: { + orientation: ['portrait'], + }, + bottomTabs: { + titleDisplayMode: 'alwaysShow', + } + }); + } +} + +export default new NavigationService(); \ No newline at end of file From 3ce84d483e5ea76262d65814e611b1f71e6b5a18 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:44:50 +0200 Subject: [PATCH 26/43] centered button --- src/components/Reanimated2.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Reanimated2.tsx b/src/components/Reanimated2.tsx index e1a160c..c69f41d 100644 --- a/src/components/Reanimated2.tsx +++ b/src/components/Reanimated2.tsx @@ -16,6 +16,7 @@ const Reanimated2: React.FC = () => { { offset.value = withSpring(Math.random()); From ef666ea5dd236ca37e48948c93c648b5a5394bb1 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:45:01 +0200 Subject: [PATCH 27/43] improvements --- src/screens/CounterScreen.tsx | 14 +++++++---- src/screens/ExpoScreen.tsx | 46 +++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/screens/CounterScreen.tsx b/src/screens/CounterScreen.tsx index 81e0bdf..beb7743 100644 --- a/src/screens/CounterScreen.tsx +++ b/src/screens/CounterScreen.tsx @@ -10,14 +10,16 @@ import { observer } from 'mobx-react'; import { NavigationFunctionComponent } from 'react-native-navigation'; import { useNavigationButtonPress } from 'react-native-navigation-hooks/dist/hooks'; -import { useStores } from '../store'; +import { useStores } from '../stores'; import Constants from '../utils/constants'; import { ButtonIcon } from '../components/Button'; +import useStyles, { ThemedStylesFuncType } from '../utils/useStyles'; const CounterScreen: NavigationFunctionComponent = observer(({ componentId, }) => { const { counter } = useStores(); + const styles = useStyles(_styles); useNavigationButtonPress(counter.decrement, componentId, Constants.CounterScreen.decButtonId); useNavigationButtonPress(counter.increment, componentId, Constants.CounterScreen.incButtonId); @@ -35,14 +37,15 @@ const CounterScreen: NavigationFunctionComponent = observer(({ ); }); -const styles = StyleSheet.create({ +const _styles: ThemedStylesFuncType = theme => StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', + backgroundColor: theme.colors.bg, }, counterContainer: { - padding: 8, + padding: theme.sizes.s, flexDirection: 'row', width: '80%', justifyContent: 'space-between', @@ -50,8 +53,9 @@ const styles = StyleSheet.create({ }, text: { fontSize: 80, - margin: 8, + margin: theme.sizes.s, textAlign: 'center', + color: theme.colors.text, }, }); @@ -72,7 +76,7 @@ CounterScreen.options = props => ({ text: Constants.CounterScreen.decButtonTitle, }], title: { - text: 'Counter', + text: Constants.ScreenTitles.CounterScreen, }, }, }); diff --git a/src/screens/ExpoScreen.tsx b/src/screens/ExpoScreen.tsx index e5249c4..c8249c2 100644 --- a/src/screens/ExpoScreen.tsx +++ b/src/screens/ExpoScreen.tsx @@ -14,14 +14,19 @@ import { Constants as ExpoConstants } from 'react-native-unimodules'; import * as Network from 'expo-network'; import Constants from '../utils/constants'; -import { useStores } from '../store'; +import { useStores } from '../stores'; +import { useServices } from '../services'; import Reanimated2 from '../components/Reanimated2'; +import { ButtonTitle } from '../components/Button'; +import useStyles, { ThemedStylesFuncType } from '../utils/useStyles'; const ExpoScreen: NavigationFunctionComponent = observer(({ componentId, }) => { const { ui } = useStores(); + const { navigation } = useServices(); + const styles = useStyles(_styles); useNavigationComponentDidAppear(() => { getNetworkType(); @@ -32,9 +37,7 @@ const ExpoScreen: NavigationFunctionComponent = observer(({ const networkState = await Network.getNetworkStateAsync(); ui.networkType = networkState.type; - } catch (e) { - console.log(e); - } + } catch (e) { } } return ( @@ -43,6 +46,7 @@ const ExpoScreen: NavigationFunctionComponent = observer(({ { 'From Expo SDK' } + Device ID: {ExpoConstants.deviceId} Network type: {ui.networkType} @@ -54,31 +58,53 @@ const ExpoScreen: NavigationFunctionComponent = observer(({ + + + + { 'Navigation' } + + + navigation.pushExpo(componentId)} + /> + navigation.showExpo()} + /> + navigation.dismissModal(componentId)} + /> + ); }); -const styles = StyleSheet.create({ +const _styles: ThemedStylesFuncType = theme => StyleSheet.create({ container: { flex: 1, + backgroundColor: theme.colors.bg, }, section: { - padding: 16, + padding: theme.sizes.m, }, header: { - fontSize: 32, + fontSize: 26, fontWeight: 'bold', + color: theme.colors.text, }, text: { - fontSize: 22, - margin: 8, + fontSize: 18, + margin: theme.sizes.s, + color: theme.colors.text, } }); ExpoScreen.options = props => ({ topBar: { title: { - text: 'Expo', + text: Constants.ScreenTitles.ExpoScreen, }, }, }); From 7e414915235e0077e976cfcad6fcc58aebb53f6a Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 01:45:07 +0200 Subject: [PATCH 28/43] useStyles hook --- src/utils/useStyles.ts | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/utils/useStyles.ts diff --git a/src/utils/useStyles.ts b/src/utils/useStyles.ts new file mode 100644 index 0000000..49bbfec --- /dev/null +++ b/src/utils/useStyles.ts @@ -0,0 +1,73 @@ +import React, { useEffect, useState } from 'react'; +import { StyleSheet, Appearance } from 'react-native'; +import Constants from './constants'; + +export type ThemedStylesFuncType = (theme: ThemeType) => StyleSheet.NamedStyles; + +// TODOs +// [ ] add correct styles.[] behaviour in components +// [ ] normalization of each number. +// [ ] set or init function? so where we can set normalization and other options + +const { colors, sizes } = Constants; + +const themes: ThemesType = { + light: { + colors: { + ...colors, + bg: colors.white, + text: colors.black, + }, + sizes, + }, + dark: { + colors: { + ...colors, + bg: colors.black, + text: colors.lightGrey, + }, + sizes, + } +} + +export const generateTheme = (themeName: ThemeNameType = generateThemeName()): ThemeType => themes[themeName]; +export const generateThemeName = (): ThemeNameType => { + return Appearance.getColorScheme() === 'dark' ? 'dark' : 'light'; +} + +const useStyles = (themedStylesFunc: ThemedStylesFuncType) => { + const [themeName, setThemeName] = useState(generateThemeName()); // here we should ger the mode + const themedStyles = themedStylesFunc(generateTheme(themeName)); + + useEffect(() => { + const onModeChange = (_cs: any) => { + const cs = _cs.colorScheme as ThemeNameType; + + setThemeName(cs); + }; + + Appearance.addChangeListener(onModeChange); + return () => { + Appearance.removeChangeListener(onModeChange); + } + }); + + // NORMALIZATION FUNCTION + // const excludeProperties = ['flex']; + // for (const key in styles) { + // if (Object.prototype.hasOwnProperty.call(styles, key)) { + // for (const key2 in styles[key]) { + // if (Object.prototype.hasOwnProperty.call(styles[key], key2)) { + // const element = styles[key][key2]; + // if (typeof element === 'number' && !excludeProperties.includes(key2)) { + // console.log(element); + // } + // } + // } + // } + // } + + return themedStyles; +} + +export default useStyles; \ No newline at end of file From 4a07268c87a8b2747d9fc482ca0cc2b957c73560 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 02:08:35 +0200 Subject: [PATCH 29/43] correct styles.[] behaviour in components --- src/screens/CounterScreen.tsx | 2 +- src/screens/ExpoScreen.tsx | 2 +- src/services/index.tsx | 4 ++-- src/utils/useStyles.ts | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/screens/CounterScreen.tsx b/src/screens/CounterScreen.tsx index beb7743..189e923 100644 --- a/src/screens/CounterScreen.tsx +++ b/src/screens/CounterScreen.tsx @@ -37,7 +37,7 @@ const CounterScreen: NavigationFunctionComponent = observer(({ ); }); -const _styles: ThemedStylesFuncType = theme => StyleSheet.create({ +const _styles = (theme: ThemeType) => StyleSheet.create({ container: { flex: 1, alignItems: 'center', diff --git a/src/screens/ExpoScreen.tsx b/src/screens/ExpoScreen.tsx index c8249c2..cb1ed87 100644 --- a/src/screens/ExpoScreen.tsx +++ b/src/screens/ExpoScreen.tsx @@ -81,7 +81,7 @@ const ExpoScreen: NavigationFunctionComponent = observer(({ ); }); -const _styles: ThemedStylesFuncType = theme => StyleSheet.create({ +const _styles = (theme: ThemeType) => StyleSheet.create({ container: { flex: 1, backgroundColor: theme.colors.bg, diff --git a/src/services/index.tsx b/src/services/index.tsx index 49b5666..717dc04 100644 --- a/src/services/index.tsx +++ b/src/services/index.tsx @@ -1,11 +1,11 @@ import React from 'react'; import NavigationService from './navigation'; -import DarkmodeService from './darkmode'; +// import DarkmodeService from './darkmode'; export const services = { navigation: NavigationService, - // darkmode: DarkmodeService, + // darkmode: DarkmodeService, // not really needed }; const servicesContext = React.createContext(services); diff --git a/src/utils/useStyles.ts b/src/utils/useStyles.ts index 49bbfec..9ebfeea 100644 --- a/src/utils/useStyles.ts +++ b/src/utils/useStyles.ts @@ -1,11 +1,11 @@ import React, { useEffect, useState } from 'react'; -import { StyleSheet, Appearance } from 'react-native'; +import { Appearance } from 'react-native'; import Constants from './constants'; -export type ThemedStylesFuncType = (theme: ThemeType) => StyleSheet.NamedStyles; +export type ThemedStylesFuncType = (theme: ThemeType) => T; // TODOs -// [ ] add correct styles.[] behaviour in components +// [X] add correct styles.[] behaviour in components // [ ] normalization of each number. // [ ] set or init function? so where we can set normalization and other options @@ -35,7 +35,7 @@ export const generateThemeName = (): ThemeNameType => { return Appearance.getColorScheme() === 'dark' ? 'dark' : 'light'; } -const useStyles = (themedStylesFunc: ThemedStylesFuncType) => { +function useStyles(themedStylesFunc: ThemedStylesFuncType) { const [themeName, setThemeName] = useState(generateThemeName()); // here we should ger the mode const themedStyles = themedStylesFunc(generateTheme(themeName)); From 12a35dbe70dc89f9a196090ce15456b918408e96 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 12:23:32 +0200 Subject: [PATCH 30/43] react-native-device-info added --- ios/Podfile.lock | 6 ++++++ package.json | 1 + yarn.lock | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 032b1e3..dacd6a6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -337,6 +337,8 @@ PODS: - ReactNativeNavigation/Core (= 7.1.0) - RNCAsyncStorage (1.12.0): - React + - RNDeviceInfo (6.2.0): + - React-Core - RNGestureHandler (1.8.0): - React - RNReanimated (2.0.0-alpha.7): @@ -452,6 +454,7 @@ DEPENDENCIES: - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - ReactNativeNavigation (from `../node_modules/react-native-navigation`) - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" + - RNDeviceInfo (from `../node_modules/react-native-device-info`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) @@ -564,6 +567,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-navigation" RNCAsyncStorage: :path: "../node_modules/@react-native-community/async-storage" + RNDeviceInfo: + :path: "../node_modules/react-native-device-info" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNReanimated: @@ -650,6 +655,7 @@ SPEC CHECKSUMS: ReactCommon: a0a1edbebcac5e91338371b72ffc66aa822792ce ReactNativeNavigation: 65025dab27b404053678593b2450ed7a022e3173 RNCAsyncStorage: 2a692bcb9b69b76a2f1a95f33db057129700af64 + RNDeviceInfo: eb63f95c93ff1aae15353d113c130aebc6127915 RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39 RNReanimated: c40c29429b22a1564e505b789132cfcef456c6bd RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59 diff --git a/package.json b/package.json index 12e6272..85e4678 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "mobx-state-tree": "^3.17.2", "react": "^16.13.1", "react-native": "^0.63.2", + "react-native-device-info": "^6.2.0", "react-native-gesture-handler": "^1.8.0", "react-native-navigation": "^7.1.0", "react-native-navigation-hooks": "^6.2.0", diff --git a/yarn.lock b/yarn.lock index 2f4de08..6de6178 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7955,6 +7955,11 @@ react-lifecycles-compat@2.0.0: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-2.0.0.tgz#71d9c4cde47114c4102454f76da055c2bc48c948" integrity sha512-txfpPCQYiazVdcbMRhatqWKcAxJweUu2wDXvts5/7Wyp6+Y9cHojqXHsLPEckzutfHlxZhG8Oiundbmp8Fd6eQ== +react-native-device-info@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-6.2.0.tgz#6eda29dee048b73b29b57b7ec0c21545bd89dada" + integrity sha512-UtAk/ZAdCVCepEmRNM/3Kx7dWbLwmEPh527HTjoQUQy1utpoXm3y4sYtCrP2tjczSIyeMs5nLP4Wf3c8bc0fwQ== + react-native-gesture-handler@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.8.0.tgz#18f61f51da50320f938957b0ee79bc58f47449dc" From 3ff29688348541008ff9c7b26c4ace6c41a92fb0 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 12:23:43 +0200 Subject: [PATCH 31/43] setOptionsForUseStyles example --- src/App.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/App.ts b/src/App.ts index 9a45490..7a1037a 100644 --- a/src/App.ts +++ b/src/App.ts @@ -9,6 +9,7 @@ import ExpoScreen from './screens/ExpoScreen'; import { withStoreProvider, hydrateStores } from './stores'; import { withServicesProvider, initServices } from './services'; +import { setOptionsForUseStyles } from './utils/useStyles'; const Screens = new Map>(); @@ -47,6 +48,12 @@ export const startApp = async () => { Ionicons.getImageSource('ios-rocket', 25), ]); + // (optional) set options for useStyles + // setOptionsForUseStyles({ + // normalize: false, + // darkmode: false, + // }); + Navigation.setRoot({ root: { bottomTabs: { From 545a9533b1e1deda624f9efcd78813fc55f7fa1e Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 12:24:04 +0200 Subject: [PATCH 32/43] styles --- src/components/Button.tsx | 6 +-- src/screens/CounterScreen.tsx | 4 +- src/screens/ExpoScreen.tsx | 77 ++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index c383e56..e149b11 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -7,7 +7,7 @@ import { import AntIcon from 'react-native-vector-icons/AntDesign'; import { TouchableOpacity } from 'react-native-gesture-handler'; -import useStyles, { ThemedStylesFuncType } from '../utils/useStyles'; +import useStyles from '../utils/useStyles'; type ButtonTitleProps = { title: string; @@ -55,12 +55,12 @@ export const ButtonIcon: React.FC = ({ ) } -const _styles: ThemedStylesFuncType = theme => StyleSheet.create({ +const _styles = (theme: ThemeType) => StyleSheet.create({ buttonContainer: { margin: theme.sizes.s, }, buttonIcon: { - fontSize: 32, + fontSize: 28, color: theme.colors.text, }, text: { diff --git a/src/screens/CounterScreen.tsx b/src/screens/CounterScreen.tsx index 189e923..070a1d1 100644 --- a/src/screens/CounterScreen.tsx +++ b/src/screens/CounterScreen.tsx @@ -13,7 +13,7 @@ import { useNavigationButtonPress } from 'react-native-navigation-hooks/dist/hoo import { useStores } from '../stores'; import Constants from '../utils/constants'; import { ButtonIcon } from '../components/Button'; -import useStyles, { ThemedStylesFuncType } from '../utils/useStyles'; +import useStyles from '../utils/useStyles'; const CounterScreen: NavigationFunctionComponent = observer(({ componentId, @@ -52,7 +52,7 @@ const _styles = (theme: ThemeType) => StyleSheet.create({ alignItems: 'center', }, text: { - fontSize: 80, + fontSize: 64, margin: theme.sizes.s, textAlign: 'center', color: theme.colors.text, diff --git a/src/screens/ExpoScreen.tsx b/src/screens/ExpoScreen.tsx index cb1ed87..564633e 100644 --- a/src/screens/ExpoScreen.tsx +++ b/src/screens/ExpoScreen.tsx @@ -19,6 +19,7 @@ import { useServices } from '../services'; import Reanimated2 from '../components/Reanimated2'; import { ButtonTitle } from '../components/Button'; import useStyles, { ThemedStylesFuncType } from '../utils/useStyles'; +import { ScrollView } from 'react-native-gesture-handler'; const ExpoScreen: NavigationFunctionComponent = observer(({ @@ -42,41 +43,43 @@ const ExpoScreen: NavigationFunctionComponent = observer(({ return ( - - - { 'From Expo SDK' } - - - Device ID: {ExpoConstants.deviceId} - Network type: {ui.networkType} - - - - - { 'Reanimated 2' } - - - - - - - - { 'Navigation' } - - - navigation.pushExpo(componentId)} - /> - navigation.showExpo()} - /> - navigation.dismissModal(componentId)} - /> - + + + + { 'From Expo SDK' } + + + Device ID: {ExpoConstants.deviceId} + Network type: {ui.networkType} + + + + + { 'Reanimated 2' } + + + + + + + + { 'Navigation' } + + + navigation.pushExpo(componentId)} + /> + navigation.showExpo()} + /> + navigation.dismissModal(componentId)} + /> + + ); }); @@ -90,12 +93,12 @@ const _styles = (theme: ThemeType) => StyleSheet.create({ padding: theme.sizes.m, }, header: { - fontSize: 26, + fontSize: 24, fontWeight: 'bold', color: theme.colors.text, }, text: { - fontSize: 18, + fontSize: 16, margin: theme.sizes.s, color: theme.colors.text, } From 15e59f2a75aff1ff1f467ef7e6f16781cb12104f Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 12:24:17 +0200 Subject: [PATCH 33/43] options & normalization added --- src/utils/useStyles.ts | 97 +++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/src/utils/useStyles.ts b/src/utils/useStyles.ts index 9ebfeea..68e909b 100644 --- a/src/utils/useStyles.ts +++ b/src/utils/useStyles.ts @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react'; -import { Appearance } from 'react-native'; -import Constants from './constants'; +import { Appearance, Dimensions, Platform, PixelRatio } from 'react-native'; +import { isTablet } from 'react-native-device-info'; -export type ThemedStylesFuncType = (theme: ThemeType) => T; +import Constants from './constants'; // TODOs // [X] add correct styles.[] behaviour in components @@ -10,6 +10,10 @@ export type ThemedStylesFuncType = (theme: ThemeType) => T; // [ ] set or init function? so where we can set normalization and other options const { colors, sizes } = Constants; +let options: UseStylesOptionsType = { + normalize: true, + darkmode: true, +}; const themes: ThemesType = { light: { @@ -32,12 +36,20 @@ const themes: ThemesType = { export const generateTheme = (themeName: ThemeNameType = generateThemeName()): ThemeType => themes[themeName]; export const generateThemeName = (): ThemeNameType => { - return Appearance.getColorScheme() === 'dark' ? 'dark' : 'light'; + return options.darkmode + ? Appearance.getColorScheme() === 'dark' ? 'dark' : 'light' + : 'light'; +} +export const setOptionsForUseStyles = (_options: UseStylesOptionsType) => { + options = { + ...options, + ..._options, + }; } function useStyles(themedStylesFunc: ThemedStylesFuncType) { const [themeName, setThemeName] = useState(generateThemeName()); // here we should ger the mode - const themedStyles = themedStylesFunc(generateTheme(themeName)); + let themedStyles = themedStylesFunc(generateTheme(themeName)); useEffect(() => { const onModeChange = (_cs: any) => { @@ -46,28 +58,71 @@ function useStyles(themedStylesFunc: ThemedStylesFuncType) { setThemeName(cs); }; - Appearance.addChangeListener(onModeChange); + if (options.darkmode) { + Appearance.addChangeListener(onModeChange); + } return () => { - Appearance.removeChangeListener(onModeChange); + if (options.darkmode) { + Appearance.removeChangeListener(onModeChange); + } } }); - // NORMALIZATION FUNCTION - // const excludeProperties = ['flex']; - // for (const key in styles) { - // if (Object.prototype.hasOwnProperty.call(styles, key)) { - // for (const key2 in styles[key]) { - // if (Object.prototype.hasOwnProperty.call(styles[key], key2)) { - // const element = styles[key][key2]; - // if (typeof element === 'number' && !excludeProperties.includes(key2)) { - // console.log(element); - // } - // } - // } - // } - // } + // Normalization + if (options.normalize) { + const excludeProperties = ['flex']; + for (const key in themedStyles) { + if (Object.prototype.hasOwnProperty.call(themedStyles, key)) { + for (const key2 in themedStyles[key]) { + if (Object.prototype.hasOwnProperty.call(themedStyles[key], key2)) { + const value = themedStyles[key][key2]; + if (typeof value === 'number' && !excludeProperties.includes(key2)) { + themedStyles = { + ...themedStyles, + [key]: { + ...themedStyles[key], + [key2]: normalize(value), + } + } + } + } + } + } + } + } return themedStyles; } +const normalize = ( + size: number, + based: 'width' | 'height' = 'width', +): number => { + const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get( + 'window', + ); + + let w_value = 375; // x1.25 -- 468.75; x1.5 -- 562.5; x2 -- 750 + let h_value = 667; // x1.25 -- 833.75; x1.5 -- 1000.5; x2 -- 1334 + + if (isTablet()) { + w_value *= 2.5; + h_value *= 2.5; + } + + // TODO + // and here we can check on stores.ui.app_scale (1..0.5) + + // based on iPhone 8's scale + const wscale: number = SCREEN_WIDTH / w_value; + const hscale: number = SCREEN_HEIGHT / h_value; + + const newSize = based === 'height' ? size * hscale : size * wscale; + if (Platform.OS === 'ios') { + return Math.round(PixelRatio.roundToNearestPixel(newSize)); + } else { + return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2; + } +}; + export default useStyles; \ No newline at end of file From 3f01e602a68cfcd64a68b2ebd1b5d084adb18a53 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 12:24:26 +0200 Subject: [PATCH 34/43] some types added --- src/utils/types.d.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/utils/types.d.ts b/src/utils/types.d.ts index ec12dd7..0f424f9 100644 --- a/src/utils/types.d.ts +++ b/src/utils/types.d.ts @@ -15,4 +15,11 @@ type ThemeType = { type ThemesType = { [key in ThemeNameType]: ThemeType; -}; \ No newline at end of file +}; + +type ThemedStylesFuncType = (theme: ThemeType) => T; + +type UseStylesOptionsType = { + normalize?: boolean; + darkmode?: boolean; +} \ No newline at end of file From 20fd57c218fa59f992e896c770f3e45670c59220 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 13:08:41 +0200 Subject: [PATCH 35/43] mobx --> 6.0 --- package.json | 4 ++-- yarn.lock | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 85e4678..8afdd53 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,9 @@ "expo-updates": "^0.3.5", "hermes-engine": "0.5.2-rc1", "lodash": "^4.17.20", - "mobx": "^5.15.7", + "mobx": "^6.0.1", "mobx-persist": "^0.4.1", - "mobx-react": "^6.3.0", + "mobx-react": "^7.0.0", "mobx-state-tree": "^3.17.2", "react": "^16.13.1", "react-native": "^0.63.2", diff --git a/yarn.lock b/yarn.lock index 6de6178..d45d434 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7139,27 +7139,27 @@ mobx-persist@^0.4.1: dependencies: serializr "^1.1.11" -mobx-react-lite@>=2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.2.1.tgz#9c05dd799005d29ec1671ae86ca30b3ab5411055" - integrity sha512-SxOuV7Q1MLdj9uRUG8N1x9lBa4cS9c+YwlcrvrBVuCTlNrgRrrHuydfBHW/0f3pALhvGhcg+JEvf1gMWOuej4A== +mobx-react-lite@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.0.0.tgz#f39b1cb23262ce539829d47217551e04bf14a8b7" + integrity sha512-SJgrTD9mfClFOsamB+0y6zjteSMr4gkp9usnpIeEi8E+lW3lMgDa3hnD4PJgLGoENpJ8/9OmO3vrkA50SNy0mw== -mobx-react@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.3.0.tgz#7d11799f988bbdadc49e725081993b18baa20329" - integrity sha512-C14yya2nqEBRSEiJjPkhoWJLlV8pcCX3m2JRV7w1KivwANJqipoiPx9UMH4pm6QNMbqDdvJqoyl+LqNu9AhvEQ== +mobx-react@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.0.0.tgz#c2eb0c3f79ade93ffb07b1ffa07cbaeb5f04e9b6" + integrity sha512-yxbTG+ovn/GbgF3+QdQahkL5sXiITtwvkoXM6PNqd0V4jvO53cnr/YM4PYuA+0Ig95IW1nojA3qViJx9ufJm6w== dependencies: - mobx-react-lite ">=2.2.0" + mobx-react-lite "^3.0.0" mobx-state-tree@^3.17.2: version "3.17.2" resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-3.17.2.tgz#130ab4d450fc4dd6e2571b7385af1350c82def37" integrity sha512-X0+xO7yEvBvPmLU2Z6cp81sD0xQaMXgLD4lGZoKSasOhNJSQPyKlu/LFxJTS6Wgw7J3gkWq9MMfQ4pO6wGMVfw== -mobx@^5.15.7: - version "5.15.7" - resolved "https://registry.yarnpkg.com/mobx/-/mobx-5.15.7.tgz#b9a5f2b6251f5d96980d13c78e9b5d8d4ce22665" - integrity sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw== +mobx@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.0.1.tgz#ec23520848527bf7834d7b4c0b54b2a8eb6e7c14" + integrity sha512-Pk6uJXZ34yqd661yRmS6z/9avm4FOGXpFpVjnEfiYYOsZXnAxv1fpYjxTCEZ9tuwk0Xe1qnUUlgm+rJtGe0YJA== ms@2.0.0: version "2.0.0" From e2e53c25a6e16b3618d636c0d6689de64cee0f62 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 13:09:07 +0200 Subject: [PATCH 36/43] making stores observable using decorators --- src/stores/counterStore.ts | 3 ++- src/stores/uiStore.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/stores/counterStore.ts b/src/stores/counterStore.ts index cb87c1b..c177dbb 100644 --- a/src/stores/counterStore.ts +++ b/src/stores/counterStore.ts @@ -1,8 +1,9 @@ -import { observable, action, computed } from 'mobx'; +import { observable, action, makeObservable } from 'mobx'; import { persist } from 'mobx-persist'; class CounterStore implements IStore { STORAGE_ID = 'CounterStore'; + constructor() { makeObservable(this) } @persist @observable value = 0; diff --git a/src/stores/uiStore.ts b/src/stores/uiStore.ts index 80d2dff..758a53e 100644 --- a/src/stores/uiStore.ts +++ b/src/stores/uiStore.ts @@ -1,14 +1,18 @@ -import { observable, action } from 'mobx'; +import { observable, action, makeObservable } from 'mobx'; import { persist } from 'mobx-persist'; import { generateTheme } from '../utils/useStyles'; class UIStore implements IStore { STORAGE_ID = 'UIStore'; + constructor() { makeObservable(this) } @observable networkType: string | undefined = ''; @observable theme = generateTheme(); // current theme, set in services.darkmode - @observable componentId = ''; // current component id + @observable componentId: string = ''; // current component id + + @action setNetworkType = (value: string | undefined) => this.networkType = value; + @action setComponentId = (value: string) => this.componentId = value; @action toggleThemeTo = (theme: ThemeType) => { this.theme = theme; From 6fe6e4a40b6e3ae9e476fddc25ff115139da2837 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 13:09:26 +0200 Subject: [PATCH 37/43] prop assign --> setFunc() --- src/screens/ExpoScreen.tsx | 2 +- src/services/navigation.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/screens/ExpoScreen.tsx b/src/screens/ExpoScreen.tsx index 564633e..12f9c1a 100644 --- a/src/screens/ExpoScreen.tsx +++ b/src/screens/ExpoScreen.tsx @@ -37,7 +37,7 @@ const ExpoScreen: NavigationFunctionComponent = observer(({ try { const networkState = await Network.getNetworkStateAsync(); - ui.networkType = networkState.type; + ui.setNetworkType(networkState.type); } catch (e) { } } diff --git a/src/services/navigation.ts b/src/services/navigation.ts index 1913eb8..6a988f1 100644 --- a/src/services/navigation.ts +++ b/src/services/navigation.ts @@ -4,7 +4,7 @@ import { stores } from '../stores'; class NavigationService implements IService { init = async () => { - await this.setUpComponentIdListener(); + await this.setComponentIdListener(); await this.setDefaultOptions(); } @@ -39,9 +39,9 @@ class NavigationService implements IService { } // Listeners - private setUpComponentIdListener = async () => { + private setComponentIdListener = async () => { Navigation.events().registerComponentDidAppearListener(async e => { - stores.ui.componentId = e.componentId; + stores.ui.setComponentId(e.componentId); }); } From aa49d75cb62871b621988b790babc3d87fdf6d32 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 14:13:27 +0200 Subject: [PATCH 38/43] withStoresProvider --- src/App.ts | 4 ++-- src/stores/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.ts b/src/App.ts index 7a1037a..75a0bcd 100644 --- a/src/App.ts +++ b/src/App.ts @@ -7,7 +7,7 @@ import Constants from './utils/constants'; import CounterScreen from './screens/CounterScreen'; import ExpoScreen from './screens/ExpoScreen'; -import { withStoreProvider, hydrateStores } from './stores'; +import { withStoresProvider, hydrateStores } from './stores'; import { withServicesProvider, initServices } from './services'; import { setOptionsForUseStyles } from './utils/useStyles'; @@ -22,7 +22,7 @@ Screens.forEach((C, key) => { key, () => gestureHandlerRootHOC( - withStoreProvider( + withStoresProvider( withServicesProvider(C))), () => C, ); diff --git a/src/stores/index.tsx b/src/stores/index.tsx index f41c1fd..642679c 100644 --- a/src/stores/index.tsx +++ b/src/stores/index.tsx @@ -12,7 +12,7 @@ export const stores = { // const f_stores = () => stores; const storeContext = React.createContext(stores); -export const withStoreProvider = (C: React.FC) => (props: any) => { +export const withStoresProvider = (C: React.FC) => (props: any) => { return ( From 0483df592cfbff9eab7babffaf32f6d8f3b6368c Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 14:14:06 +0200 Subject: [PATCH 39/43] docs --- EXPO_CONFIGURATION.md | 21 +++++++++++++ LICENSE.md | 2 +- README.md | 70 +++++++++++++++++-------------------------- 3 files changed, 49 insertions(+), 44 deletions(-) create mode 100644 EXPO_CONFIGURATION.md diff --git a/EXPO_CONFIGURATION.md b/EXPO_CONFIGURATION.md new file mode 100644 index 0000000..5c91ff6 --- /dev/null +++ b/EXPO_CONFIGURATION.md @@ -0,0 +1,21 @@ +## 🎛 Expo Configuration +#### Prerequisites +1. Get familiar with [Expo](https://expo.io) and [their documentation](https://docs.expo.io/) if you haven't yet. +2. Create new account at [Expo.io](https://expo.io/signup) +3. Install [expo-cli](https://docs.expo.io/workflow/expo-cli/). +4. Login using expo-cli in the terminal. + +### expo-updates +If you decided to use this module, then you will need to follow steps below carefully: +1. Decide how your app's `slug` will look like. For example, for the starter I am using `expo-rnn-starter`. +2. Open `app.json` from the root of the project and change `"expo" --> "slug"` property to your own. +3. We need to define `EXPO UPDATE URL` for the project which we will put in to iOS and Android config files. It has following pattern -- `https://exp.host/@my-expo-username/my-app-slug`. For example, for the starter it would be `https://exp.host/@kanzitelli/expo-rnn-starter`. +4. Open `ios/Supporting/Expo.plist` and change `EXUpdatesURL` property to your own. +5. Open `android/app/src/main/AndroidManifest.xml` and change `EXPO_UPDATE_URL` meta-data property to your own. +6. Run `expo publish` from the root of the project. It will take some time. +7. Now you should be able to see the app in [Expo dashboard](https://expo.io/dashboard). +8. In order to test it, you will need to build the app in Release mode for both platforms. Once you do some changes in your code, don't forget to run `expo publish`. + +If you would like to enable or disable `expo-updates`, open `ios/Supporting/Expo.plist` and change `EXUpdatesEnabled `; and for Android open `android/app/src/main/AndroidManifest.xml` and change `ENABLED` meta-data property. + +Before using `expo-updates` in production, make sure you have read the documentation about this module, release channels etc. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 6d3e509..2ed605f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Batr Kanzitelli +Copyright (c) 2020 Batyr Kanzitdinov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0e918ad..257a207 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ -## 🦥 Motivation +## Table of contents +[🦥 Motivation](#motivation) +[🏃‍♂️ Getting Started](#getting-started) +[📖 What's inside](#whats-inside) +[🧙‍♂️ Enhancements](#enhancements) +[⚠️ Known issues (warnings)](#known-issues-warnings) + +## Motivation 1. I love [React Native](https://reactnative.dev/) 💚 2. I love [Expo](https://expo.io/) 💙 3. I love [React Native Navigation](https://wix.github.io/react-native-navigation) ❤️ @@ -9,7 +16,7 @@ So why not put them all together? 😏 Big love and gratitude to all people who are working on React Native, Expo and React Native Navigation! -## 🏃‍♂️ Getting Started +## Getting Started 1. Clone the repo ```bash @@ -27,61 +34,38 @@ yarn ios yarn android ``` -👁‍🗨 If you are planning to use Expo modules, such as preconfigured `expo-updates` and others, or add new ones, what I strongly recommend, then proceed to Expo Configuration section and follow those steps carefully. +👁‍🗨 If you are planning to use Expo modules, such as preconfigured `expo-updates` and others, or add new ones, what I strongly recommend, then proceed to [Expo Configuration](/EXPO_CONFIGURATION.md) and follow the steps carefully. If you would like to rename the app, you can use [react-native-rename](https://github.com/junedomingo/react-native-rename). Don't forget to run `yarn ios:pods` after the process is finished. Also keep in mind that bundle identifier must be valid for both platforms or change it manually. -## 🎛 Expo Configuration -#### Prerequisites -1. Get familiar with [Expo](https://expo.io) and [their documentation](https://docs.expo.io/) if you haven't yet. -2. Create new account at [Expo.io](https://expo.io/signup) -3. Install [expo-cli](https://docs.expo.io/workflow/expo-cli/). -4. Login using expo-cli in the terminal. - -### expo-updates -If you decided to use this module, then you will need to follow steps below carefully: -1. Decide how your app's `slug` will look like. For example, for the starter I am using `expo-rnn-starter`. -2. Open `app.json` from the root of the project and change `"expo" --> "slug"` property to your own. -3. We need to define `EXPO UPDATE URL` for the project which we will put in to iOS and Android config files. It has following pattern -- `https://exp.host/@my-expo-username/my-app-slug`. For example, for the starter it would be `https://exp.host/@kanzitelli/expo-rnn-starter`. -4. Open `ios/Supporting/Expo.plist` and change `EXUpdatesURL` property to your own. -5. Open `android/app/src/main/AndroidManifest.xml` and change `EXPO_UPDATE_URL` meta-data property to your own. -6. Run `expo publish` from the root of the project. It will take some time. -7. Now you should be able to see the app in [Expo dashboard](https://expo.io/dashboard). -8. In order to test it, you will need to build the app in Release mode for both platforms. Once you do some changes in your code, don't forget to run `expo publish`. - -If you would like to disable `expo-updates`, open `ios/Supporting/Expo.plist` and change `EXUpdatesEnabled ` to `NO`; and for Android open `android/app/src/main/AndroidManifest.xml` and change `ENABLED` meta-data property to `false`. - -## 📖 What's inside -- [Expo Unimodules](https://github.com/expo/expo) - universal set of amazing libraries which are needed for every app. -- [Expo Updates]() - continuous delivery tool maintained by Expo team and is part of Expo Ecosystem. Best alternative to CodePush. +## What's inside +- [Expo SDK](https://github.com/expo/expo) - universal set of amazing libraries (such as `expo-updates`) which are needed for building sustainable apps - [React Native Navigation](https://github.com/wix/react-native-navigation) - truly native navigation experience for iOS and Android +- [Reanimated 2](https://github.com/software-mansion/react-native-reanimated) - React Native's Animated library reimplemented +- [MobX](https://github.com/mobxjs/mobx) - simple, scalable state management, with [mobx-persist](https://github.com/pinqy520/mobx-persist) for persisting your stores +### Extra helpful libraries - [React Native Navigation Hooks](https://github.com/underscopeio/react-native-navigation-hooks) - a set of React hooks for React Native Navigation -- [React Native Vector Icons](https://github.com/oblador/react-native-vector-icons) - customizable icons for React Native +- [React Native Vector Icons](https://github.com/oblador/react-native-vector-icons) - customizable icons for React Native. Even though Expo SDK includes `expo-icons` which you can still use, but the reason of adding `react-native-vector-icons` is to get icons as sources before launching tab based app - [React Native Gesture Handler](https://github.com/kmagiera/react-native-gesture-handler) - native touches and gesture system for React Native +- [React Native Defice Info](https://github.com/react-native-community/react-native-device-info) - device information for React Native iOS and Android - [Typescript](https://www.typescriptlang.org/) - strict syntactical superset of JavaScript +### Small useful libraries/hooks from me +- `useStyles()` - a hook that takes care of dark mode in your app. Supports toggling modes while you are in app. No dependencies (needs only `react-native`, so could be reusable). It is not a theme provider but could be used as one. It only has two themes light or dark -and pick your favourite state management tool (examples inside) - -| MOBX | REDUX | -| :---: | :---: | -| [MobX](https://github.com/mobxjs/mobx) - simple, scalable state management | [Redux](https://github.com/reduxjs/redux) - a predictable state container | -| [MobX React](https://github.com/mobxjs/mobx-react) - official React bindings for MobX | [React Redux](https://github.com/reduxjs/react-redux) - official React bindings for Redux | -| [MobX State Tree (MST)](https://github.com/mobxjs/mobx-state-tree) - opinionated, transactional, MobX powered state container | [Redux Saga](https://github.com/redux-saga/redux-saga) - side effect model for Redux apps | -| [Persistence](https://github.com/kanzitelli/react-native-navigation-starter/blob/master/srcMobX/store/redditStore.ts#L130) and [Hydration](https://github.com/kanzitelli/react-native-navigation-starter/blob/master/srcMobX/store/redditStore.ts#L97) are done by our hands | [Redux Persist](https://github.com/rt2zz/redux-persist) - persist and rehydrate a Redux store | - -## 🧙‍♂️ Enhancements +## Enhancements There are still some things I would like to add to the starter: -- [Expo Notifications](https://docs.expo.io/versions/v38.0.0/sdk/notifications/). -- Dark Mode support. -- Localization via [i18next](https://github.com/i18next/i18next/). +- 🔳 Dark Mode support. +- ⬜️ Localization via [i18next](https://github.com/i18next/i18next/). +- ⬜️ [Expo Notifications](https://docs.expo.io/versions/v39.0.0/sdk/notifications/). Feel free to open an issue for suggestions. -## ⚠️ Known issues (warnings) -* Expo splash screen. There is some weird behavior using `expo-splash-screen` with `react-native-navigation`. That is why I have excluded this module from `react-native-unimodules`. +## Known issues (warnings) +- Expo splash screen. There is some weird behavior using `expo-splash-screen` with `react-native-navigation`. That is why this module has been excluded. +- Dark Mode in Android. React Native Navigation doesn't toggle navigation and tab bars' background color to dark when dark mode is toggled on. However it does so on iOS. As a workaround, we can subscribe to toggle events and then using `Navigation.mergeOptions` & `Navigation.setDefaultOptions` to change stylings for navigations and tab bars. Anyways, it needs some time to dive into it and come up with better solution. Feel free to open an issue for any other warning or problems. -## 📃 License +## License This project is [MIT licensed](/LICENSE.md) \ No newline at end of file From 51bdce52623d7916a3c1eaa16e6ad2dd584cca9f Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 14:15:24 +0200 Subject: [PATCH 40/43] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 257a207..b5e5dec 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ ## Table of contents -[🦥 Motivation](#motivation) -[🏃‍♂️ Getting Started](#getting-started) -[📖 What's inside](#whats-inside) -[🧙‍♂️ Enhancements](#enhancements) -[⚠️ Known issues (warnings)](#known-issues-warnings) +- [🦥 Motivation](#motivation) +- [🏃‍♂️ Getting Started](#getting-started) +- [📖 What's inside](#whats-inside) +- [🧙‍♂️ Enhancements](#enhancements) +- [⚠️ Known issues (warnings)](#known-issues-warnings) ## Motivation 1. I love [React Native](https://reactnative.dev/) 💚 From ec9015fa527670a1323aa938bc028b8339ad82a1 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 15:24:47 +0200 Subject: [PATCH 41/43] logo image --> gif --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5e5dec..428507c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + ## Table of contents - [🦥 Motivation](#motivation) From dd195b521d822a4f03f0f28a260897563858e278 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 15:28:45 +0200 Subject: [PATCH 42/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 428507c..d15f896 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ There are still some things I would like to add to the starter: Feel free to open an issue for suggestions. ## Known issues (warnings) -- Expo splash screen. There is some weird behavior using `expo-splash-screen` with `react-native-navigation`. That is why this module has been excluded. +- Expo splash screen. There is some weird behavior using `expo-splash-screen` with `react-native-navigation`. That is why this module has been excluded: [ios](https://github.com/kanzitelli/expo-rnn-starter/blob/master/ios/Podfile#L8) & [android](https://github.com/kanzitelli/expo-rnn-starter/blob/mater/android/app/build.gradle#L196). - Dark Mode in Android. React Native Navigation doesn't toggle navigation and tab bars' background color to dark when dark mode is toggled on. However it does so on iOS. As a workaround, we can subscribe to toggle events and then using `Navigation.mergeOptions` & `Navigation.setDefaultOptions` to change stylings for navigations and tab bars. Anyways, it needs some time to dive into it and come up with better solution. Feel free to open an issue for any other warning or problems. From 40a4d55b5eb3db8fb1f02ef6e94f9fe1e1b9a522 Mon Sep 17 00:00:00 2001 From: Batr Kanzitelli Date: Tue, 6 Oct 2020 15:41:43 +0200 Subject: [PATCH 43/43] redundant code removed --- index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/index.js b/index.js index ad7de85..4dd3235 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,6 @@ import 'expo-asset'; import { Navigation } from 'react-native-navigation'; -// Note: -// in order to test Redux and MobX separately, -// please comment unnecessary import, this is important -// because RNN registers screens for both of them if two imports are presented - -// import { startApp } from './srcRedux/App'; import { startApp } from './src/App'; Navigation.events().registerAppLaunchedListener(() => {