├── .watchmanconfig ├── .vscode └── settings.json ├── assets ├── icon.png ├── splash.png ├── images │ ├── icon.png │ ├── splash.png │ ├── robot-dev.png │ └── robot-prod.png └── fonts │ └── SpaceMono-Regular.ttf ├── redux ├── api │ ├── index.js │ ├── reducer.js │ └── action.js ├── global-state │ ├── index.js │ ├── action.js │ └── reducer.js ├── Provider.js ├── store.js └── reducers.js ├── babel.config.js ├── .gitignore ├── layouts ├── dimensions.js ├── FrameBox.js ├── LinearBox.js ├── styles.js ├── FlexBox.js ├── ScreenRotate.js └── ResponsiveBox.js ├── components ├── CategoriesComp.js ├── MenuButton.js ├── StyledText.js ├── LoadingComp.js ├── __tests__ │ ├── StyledText-test.js │ └── __snapshots__ │ │ └── StyledText-test.js.snap ├── TabBarIcon.js ├── HomeComp.js ├── AppbarComp.js ├── PostsComp.js ├── PostComp.js ├── WpPageComp.js ├── DrawerComp.js ├── SubMenuComp.js └── MainMenuComp.js ├── constants ├── Layout.js └── Colors.js ├── .expo-shared └── assets.json ├── navigation ├── AppNavigator.js ├── AppNavigator.web.js ├── NavigationService.js ├── MainTabNavigator.js └── DrawerNavigator.js ├── __tests__ ├── __snapshots__ │ └── App-test.js.snap └── App-test.js ├── screens ├── EditorScreen.js ├── StartScreen.js ├── PostScreen.js ├── MenusScreen.js ├── LinksScreen.js ├── PagesScreen.js ├── SettingsScreen.js ├── PostsScreen.js ├── ThemeScreen.js ├── WpPageScreen.js └── HomeScreen.js ├── PaperProvider.js ├── app.json ├── App.js ├── package.json ├── customTheme.js ├── containers ├── Container.js ├── WpPageContainer.js ├── PostContainer.js ├── CategoriesContainer.js ├── MainMenuContainer.js ├── SubMenuContainer.js ├── HomeContainer.js ├── PostsContainer.js └── WordPressApi.js └── README.md /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.explorerKind": "external" 3 | } -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/splash.png -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/images/splash.png -------------------------------------------------------------------------------- /assets/images/robot-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/images/robot-dev.png -------------------------------------------------------------------------------- /redux/api/index.js: -------------------------------------------------------------------------------- 1 | import * as action from './action'; 2 | import reducer from './reducer'; 3 | 4 | export {action, reducer}; -------------------------------------------------------------------------------- /assets/images/robot-prod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/images/robot-prod.png -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielAlongE/wordpress-app-react-expo/HEAD/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /redux/global-state/index.js: -------------------------------------------------------------------------------- 1 | import * as action from './action'; 2 | import reducer from './reducer'; 3 | 4 | export {action, reducer}; 5 | export default action.default; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | web-report/ 12 | -------------------------------------------------------------------------------- /layouts/dimensions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | 4 | const { width, height } = Dimensions.get('window'); 5 | 6 | export { width, height }; -------------------------------------------------------------------------------- /components/CategoriesComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View} from 'react-native'; 3 | import CategoriesContainer from '../containers/CategoriesContainer'; 4 | 5 | export default CategoriesContainer(View); -------------------------------------------------------------------------------- /components/MenuButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {IconButton} from 'react-native-paper'; 3 | 4 | 5 | const MenuButton = ({...rest}) => (); 6 | 7 | export default MenuButton; -------------------------------------------------------------------------------- /components/StyledText.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text } from 'react-native'; 3 | 4 | export function MonoText(props) { 5 | return ( 6 | 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /redux/Provider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import store from './store'; 4 | 5 | const StoreProvider = ({children}) => {children} 6 | 7 | export default StoreProvider; -------------------------------------------------------------------------------- /components/LoadingComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ActivityIndicator, Colors } from 'react-native-paper'; 3 | 4 | const LoadingComp = ({...rest}) => 5 | 6 | export default LoadingComp; -------------------------------------------------------------------------------- /constants/Layout.js: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native'; 2 | 3 | const width = Dimensions.get('window').width; 4 | const height = Dimensions.get('window').height; 5 | 6 | export default { 7 | window: { 8 | width, 9 | height, 10 | }, 11 | isSmallDevice: width < 375, 12 | }; 13 | -------------------------------------------------------------------------------- /redux/store.js: -------------------------------------------------------------------------------- 1 | import {createStore, applyMiddleware} from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import rootReducer from './reducers'; 4 | 5 | //undefined, compose() 6 | 7 | const store = createStore(rootReducer, 8 | applyMiddleware(thunk), 9 | 10 | ); 11 | 12 | 13 | export default store; -------------------------------------------------------------------------------- /redux/reducers.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | 3 | import {reducer as api} from './api'; 4 | import {reducer as globalState} from './global-state'; 5 | 6 | //imported reducers comes here 7 | var obj = { 8 | api, 9 | globalState 10 | } 11 | 12 | 13 | export default combineReducers( 14 | { ...obj }); -------------------------------------------------------------------------------- /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "0cae4d70c6df3e5e96ee8b5c442b59d55c8ab8deb466992ab9abc523822f2a1b": true, 3 | "e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true, 4 | "af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true, 5 | "e7fc0741cc6562975a990e3d9ef820571588dab20aba97032df9f00caa9cd57a": true 6 | } -------------------------------------------------------------------------------- /components/__tests__/StyledText-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { MonoText } from '../StyledText'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer.create(Snapshot test!).toJSON(); 8 | 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /components/__tests__/__snapshots__/StyledText-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders correctly 1`] = ` 4 | 14 | Snapshot test! 15 | 16 | `; 17 | -------------------------------------------------------------------------------- /constants/Colors.js: -------------------------------------------------------------------------------- 1 | const tintColor = '#2f95dc'; 2 | 3 | export default { 4 | tintColor, 5 | tabIconDefault: '#ccc', 6 | tabIconSelected: tintColor, 7 | tabBar: '#fefefe', 8 | errorBackground: 'red', 9 | errorText: '#fff', 10 | warningBackground: '#EAEB5E', 11 | warningText: '#666804', 12 | noticeBackground: tintColor, 13 | noticeText: '#fff', 14 | }; 15 | -------------------------------------------------------------------------------- /layouts/FrameBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import styles from './styles'; 4 | 5 | 6 | 7 | const FrameBox = ({children,style={}}) => { 8 | 9 | var {frameBox} = styles; 10 | 11 | return ({children}); 14 | } 15 | 16 | export default FrameBox; -------------------------------------------------------------------------------- /layouts/LinearBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import styles from './styles'; 4 | 5 | 6 | 7 | const FrameBox = ({children,style={}}) => { 8 | 9 | var {linearBox} = styles; 10 | 11 | return ({children}); 14 | } 15 | 16 | export default FrameBox; -------------------------------------------------------------------------------- /components/TabBarIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Ionicons } from '@expo/vector-icons'; 3 | 4 | import Colors from '../constants/Colors'; 5 | 6 | export default function TabBarIcon(props) { 7 | return ( 8 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /navigation/AppNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createAppContainer, createSwitchNavigator } from 'react-navigation'; 3 | 4 | import DrawerNavigator from './DrawerNavigator'; 5 | 6 | export default createAppContainer( 7 | createSwitchNavigator({ 8 | // You could add another route here for authentication. 9 | // Read more at https://reactnavigation.org/docs/en/auth-flow.html 10 | Main: DrawerNavigator, 11 | }) 12 | ); 13 | -------------------------------------------------------------------------------- /layouts/styles.js: -------------------------------------------------------------------------------- 1 | //import React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import {width, height} from './dimensions'; 4 | 5 | const styles = StyleSheet.create({ 6 | frameBox:{flex:1, flexDirection:'column', alignItems:'center', justifyContent:'center'}, 7 | linearBox:{width}, 8 | borderLine:{backgroundColor:'#ccc',margin:1}, 9 | row:{flexDirection:'row'}, 10 | col:{flexDirection:'column'}, 11 | box:{} 12 | }); 13 | 14 | export {width, height}; 15 | export default styles; -------------------------------------------------------------------------------- /__tests__/__snapshots__/App-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`App renders the loading screen 1`] = ` 4 | 9 | `; 10 | 11 | exports[`App renders the root without loading screen 1`] = ` 12 | 20 | 21 | 22 | `; 23 | -------------------------------------------------------------------------------- /navigation/AppNavigator.web.js: -------------------------------------------------------------------------------- 1 | import { createBrowserApp } from '@react-navigation/web'; 2 | import { createSwitchNavigator } from 'react-navigation'; 3 | 4 | import DrawerNavigator from './DrawerNavigator'; 5 | 6 | const switchNavigator = createSwitchNavigator({ 7 | // You could add another route here for authentication. 8 | // Read more at https://reactnavigation.org/docs/en/auth-flow.html 9 | Main: DrawerNavigator, 10 | }); 11 | switchNavigator.path = ''; 12 | 13 | export default createBrowserApp(switchNavigator, { history: 'hash' }); 14 | -------------------------------------------------------------------------------- /navigation/NavigationService.js: -------------------------------------------------------------------------------- 1 | // NavigationService.js 2 | 3 | import { NavigationActions } from 'react-navigation'; 4 | 5 | let _navigator; 6 | 7 | function setTopLevelNavigator(navigatorRef) { 8 | _navigator = navigatorRef; 9 | } 10 | 11 | function navigate(routeName, params) { 12 | _navigator.dispatch( 13 | NavigationActions.navigate({ 14 | routeName, 15 | params, 16 | }) 17 | ); 18 | } 19 | 20 | // add other navigation functions that you need and export them 21 | 22 | export default { 23 | navigate, 24 | setTopLevelNavigator, 25 | }; -------------------------------------------------------------------------------- /screens/EditorScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {TestForm} from '../builder/TestForm'; 3 | import AppEditor from '../builder/containers/AppEditorContainer'; 4 | import { IconButton } from 'react-native-paper'; 5 | import { withNavigationFocus } from "react-navigation"; 6 | 7 | 8 | function EditorScreen({navigation, isFocused}) { 9 | 10 | if(isFocused){ 11 | return ; 12 | }else{ 13 | return null; 14 | } 15 | } 16 | 17 | 18 | 19 | export default withNavigationFocus(EditorScreen); -------------------------------------------------------------------------------- /redux/global-state/action.js: -------------------------------------------------------------------------------- 1 | export const SET_GLOBAL_STATE = 'SET_GLOBAL_STATE'; 2 | export const CLEAR_GLOBAL_STATE = 'CLEAR_GLOBAL_STATE'; 3 | 4 | export const setState = (obj) => ( 5 | { 6 | type: SET_GLOBAL_STATE, 7 | obj 8 | }); 9 | 10 | export const clearState = () => ( 11 | { 12 | type: CLEAR_GLOBAL_STATE 13 | }); 14 | 15 | export const clear = () => dispatch => dispatch(clearState()); 16 | 17 | const set = (obj) => dispatch => { 18 | dispatch(setState(obj)); 19 | 20 | return Promise.resolve() 21 | 22 | 23 | } 24 | 25 | export default set; 26 | 27 | -------------------------------------------------------------------------------- /screens/StartScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {TestForm} from '../builder/TestForm'; 3 | import AppManager from '../builder/containers/AppManagerContainer' 4 | //import AppManager from '../components/MainMenuComp'; 5 | import { withNavigationFocus } from "react-navigation"; 6 | 7 | function SplashScreen({navigation, isFocused}) { 8 | 9 | return ; 10 | 11 | } 12 | 13 | SplashScreen.navigationOptions = { 14 | title: 'Testing Form', 15 | mode: 'modal', 16 | headerMode: 'none', 17 | header: null 18 | }; 19 | 20 | export default withNavigationFocus(SplashScreen); -------------------------------------------------------------------------------- /screens/PostScreen.js: -------------------------------------------------------------------------------- 1 | //import * as WebBrowser from 'expo-web-browser'; 2 | import React from 'react'; 3 | import { ScrollView, View } from 'react-native'; 4 | import PostComp from '../components/PostComp'; 5 | import { withNavigationFocus } from "react-navigation"; 6 | 7 | function PostScreen({navigation, isFocused}) { 8 | 9 | const id = navigation.getParam('id', 0); 10 | 11 | const args = {navigation, isFocused, id}; 12 | 13 | return ( 14 | 15 | 16 | 17 | ); 18 | 19 | } 20 | 21 | export default withNavigationFocus(PostScreen); -------------------------------------------------------------------------------- /components/HomeComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, Image, ImageBackground} from 'react-native'; 3 | import HomeContainer from '../containers/HomeContainer'; 4 | import {default as Box} from '../layouts/ResponsiveBox'; 5 | import {Button, List, Avatar, Surface, Card, Text, Title, Paragraph } from 'react-native-paper'; 6 | import Loading from './LoadingComp'; 7 | import HTML from 'react-native-render-html'; 8 | import MainMenu from './MainMenuComp' 9 | 10 | const HomeComp = ({some}) => { 11 | 12 | return ( 13 | 14 | 15 | This is the Home Page o 16 | 17 | ); 18 | } 19 | 20 | export default HomeContainer(HomeComp); -------------------------------------------------------------------------------- /screens/MenusScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {TestForm} from '../builder/TestForm'; 3 | import AppMenus from '../builder/containers/AppMenusContainer'; 4 | import { IconButton } from 'react-native-paper'; 5 | import { withNavigationFocus } from "react-navigation"; 6 | 7 | function SettingsScreen({navigation, isFocused} ) { 8 | 9 | 10 | if(isFocused){ 11 | return ; 12 | }else{ 13 | return null; 14 | } 15 | } 16 | 17 | SettingsScreen.navigationOptions = { 18 | title: 'Menus', 19 | tabBarIcon: ({focused, horizontal, tintColor})=>{ 20 | return (); 21 | } 22 | }; 23 | 24 | export default withNavigationFocus(SettingsScreen); -------------------------------------------------------------------------------- /screens/LinksScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ScrollView, StyleSheet } from 'react-native'; 3 | import { ExpoLinksView } from '@expo/samples'; 4 | 5 | export default function LinksScreen() { 6 | return ( 7 | 8 | {/** 9 | * Go ahead and delete ExpoLinksView and replace it with your content; 10 | * we just wanted to provide you with some helpful links. 11 | */} 12 | 13 | 14 | ); 15 | } 16 | 17 | LinksScreen.navigationOptions = { 18 | title: 'Links', 19 | }; 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | paddingTop: 15, 25 | backgroundColor: '#fff', 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /screens/PagesScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {TestForm} from '../builder/TestForm'; 3 | import AppPages from '../builder/containers/AppPagesContainer'; 4 | import { IconButton } from 'react-native-paper'; 5 | import { withNavigationFocus } from "react-navigation"; 6 | 7 | 8 | function PagesScreen({navigation, isFocused}) { 9 | 10 | if(isFocused){ 11 | return ; 12 | }else{ 13 | return null; 14 | } 15 | } 16 | 17 | PagesScreen.navigationOptions = { 18 | title: 'Pages', 19 | tabBarIcon: ({focused, horizontal, tintColor})=>{ 20 | return (); 21 | } 22 | }; 23 | 24 | 25 | export default withNavigationFocus(PagesScreen); -------------------------------------------------------------------------------- /screens/SettingsScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {TestForm} from '../builder/TestForm'; 3 | import { withNavigationFocus } from "react-navigation"; 4 | import AppSettings from '../builder/containers/AppSettingsContainer'; 5 | import { IconButton } from 'react-native-paper'; 6 | 7 | 8 | function SettingsScreen({navigation, isFocused}) { 9 | 10 | if(isFocused){ 11 | return ; 12 | }else{ 13 | return null; 14 | } 15 | } 16 | 17 | SettingsScreen.navigationOptions = { 18 | title: 'Settings', 19 | tabBarIcon: ({focused, horizontal, tintColor})=>{ 20 | return (); 21 | } 22 | }; 23 | 24 | export default withNavigationFocus(SettingsScreen); 25 | -------------------------------------------------------------------------------- /layouts/FlexBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import styles from './styles'; 4 | 5 | 6 | 7 | const Row = ({children,style={}}) => { 8 | 9 | var {row} = styles; 10 | 11 | return ({children}); 14 | } 15 | 16 | const Col = ({children,style={}}) => { 17 | 18 | var {col} = styles; 19 | 20 | return ({children}); 23 | } 24 | 25 | const Box = ({children,style={}, ...rest}) => { 26 | 27 | var {box} = styles; 28 | 29 | return ({children}); 34 | } 35 | 36 | export {Row, Col, Box}; -------------------------------------------------------------------------------- /components/AppbarComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View} from 'react-native'; 3 | import {Appbar} from 'react-native-paper'; 4 | import MainMenu from './MainMenuComp'; 5 | 6 | const AppbarComp = ({navigation}) => { 7 | //subtitle={subtitle || ''} 8 | const title = navigation.getParam('title', 'Wordpress App'); 9 | return ( 10 | 11 | 12 | navigation.toggleDrawer()} /> 13 | 14 | {/* navigation.goBack()} />*/} 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | export default AppbarComp; -------------------------------------------------------------------------------- /screens/PostsScreen.js: -------------------------------------------------------------------------------- 1 | //import * as WebBrowser from 'expo-web-browser'; 2 | import React from 'react'; 3 | import { ScrollView, View } from 'react-native'; 4 | //import MenuButton from '../components/MenuButton'; 5 | import PostsComp from '../components/PostsComp'; 6 | import { withNavigationFocus } from "react-navigation"; 7 | 8 | function PostsScreen({navigation, isFocused}) { 9 | 10 | const categories = navigation.getParam('categories', 0); 11 | 12 | const args = {navigation, isFocused}; 13 | 14 | if(categories>0){ 15 | args.categories = categories; 16 | } 17 | 18 | return ( 19 | 20 | 21 | 22 | ); 23 | 24 | 25 | 26 | } 27 | 28 | 29 | export default withNavigationFocus(PostsScreen); -------------------------------------------------------------------------------- /screens/ThemeScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {TestForm} from '../builder/TestForm'; 3 | import AppTheme from '../builder/containers/AppThemeContainer'; 4 | //import AppThemeComp from '../builder/components/AppThemeComp'; 5 | import { IconButton } from 'react-native-paper'; 6 | import { withNavigationFocus } from "react-navigation"; 7 | 8 | function ThemeScreen({navigation, isFocused} ) { 9 | 10 | if(isFocused){ 11 | return ; 12 | }else{ 13 | return null; 14 | } 15 | 16 | 17 | } 18 | 19 | ThemeScreen.navigationOptions = { 20 | title: 'Theme', 21 | tabBarIcon: ({focused, horizontal, tintColor})=>{ 22 | return (); 23 | } 24 | }; 25 | 26 | export default withNavigationFocus(ThemeScreen) -------------------------------------------------------------------------------- /__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import NavigationTestUtils from 'react-navigation/NavigationTestUtils'; 3 | import renderer from 'react-test-renderer'; 4 | 5 | import App from '../App'; 6 | 7 | jest.mock('expo', () => ({ 8 | AppLoading: 'AppLoading', 9 | })); 10 | 11 | jest.mock('../navigation/AppNavigator', () => 'AppNavigator'); 12 | 13 | describe('App', () => { 14 | jest.useFakeTimers(); 15 | 16 | beforeEach(() => { 17 | NavigationTestUtils.resetInternalState(); 18 | }); 19 | 20 | it(`renders the loading screen`, () => { 21 | const tree = renderer.create().toJSON(); 22 | expect(tree).toMatchSnapshot(); 23 | }); 24 | 25 | it(`renders the root without loading screen`, () => { 26 | const tree = renderer.create().toJSON(); 27 | expect(tree).toMatchSnapshot(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /PaperProvider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Provider } from 'react-native-paper'; 4 | //import NavigationService from './navigation/NavigationService'; 5 | import { DefaultTheme } from 'react-native-paper'; 6 | 7 | 8 | function PaperProvider({children, theme=DefaultTheme, ...rest}) { 9 | 10 | 11 | return ( 12 | 13 | {children} 14 | 15 | ); 16 | 17 | } 18 | 19 | 20 | const mapStateToProps = state => { 21 | 22 | const appIndex = state.globalState.currentApp || 0; 23 | 24 | const apps = state.globalState.apps || []; 25 | 26 | const theme = apps && apps[appIndex] && apps[appIndex]['theme']; 27 | 28 | //console.log("PaperProvider", appIndex, theme) 29 | 30 | return ({theme, appIndex}); 31 | }; 32 | 33 | 34 | export default connect(mapStateToProps)(PaperProvider); 35 | -------------------------------------------------------------------------------- /components/PostsComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import {View, Image, ImageBackground} from 'react-native'; 3 | import PostsContainer from '../containers/PostsContainer'; 4 | import {default as Box} from '../layouts/ResponsiveBox'; 5 | import {Button, List, Avatar, Surface, Card, Text, Title, Paragraph, View } from 'react-native-paper'; 6 | import Loading from './LoadingComp'; 7 | //import HTML from 'react-native-render-html'; 8 | import {WordPressThumbnailList} from '../builder/components/WordPressPostsComp'; 9 | 10 | 11 | const Posts = ({posts=[], fetchMore, isFetching, navigation}) => { 12 | 13 | const args = {posts, isFetching, navigation}; 14 | 15 | return ( 16 | 17 | 18 | {isFetching ? : } 19 | ); 20 | } 21 | 22 | export default PostsContainer(Posts); -------------------------------------------------------------------------------- /layouts/ScreenRotate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import { ScreenOrientation } from 'expo'; 4 | 5 | 6 | export default class ScreenRotate extends React.Component { 7 | 8 | 9 | componentWillMount() { 10 | /* Dimensions.addEventListener('change', ()=>{ 11 | if(this.props.onChange){ 12 | this.props.onChange(); 13 | } 14 | }); 15 | */ 16 | } 17 | 18 | componentWillUnmount() { 19 | //Dimensions.removeEventListener('change'); 20 | } 21 | 22 | componentDidMount() { 23 | //ScreenOrientation.allow(ScreenOrientation.OrientationLock.ALL); 24 | //OrientationLock.ALL 25 | (async function changeScreenOrientation() { 26 | await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.ALL); 27 | })(); 28 | } 29 | 30 | render(){ 31 | const {children} = this.props; 32 | return( 33 | 34 | {children ? children : null} 35 | ) 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "Wordpress App Builder", 4 | "slug": "wordpress-app-builder", 5 | "privacy": "public", 6 | "sdkVersion": "34.0.0", 7 | "platforms": [ 8 | "ios", 9 | "android", 10 | "web" 11 | ], 12 | "version": "1.0.0", 13 | "orientation": "portrait", 14 | "icon": "./assets/images/icon.png", 15 | "splash": { 16 | "image": "./assets/images/splash.png", 17 | "resizeMode": "contain", 18 | "backgroundColor": "#ffffff" 19 | }, 20 | "updates": { 21 | "fallbackToCacheTimeout": 0 22 | }, 23 | "assetBundlePatterns": [ 24 | "**/*" 25 | ], 26 | "ios": { 27 | "supportsTablet": true 28 | }, 29 | "description": "In seconds your WordPress site becomes a Native App.\nThe WordPress Gutenberg editor is the inspiration behind my quest.\nImagine being able to add and manipulate components just as you would on using a visual editor on WordPress.\n\nCollaborators are welcomed!", 30 | "githubUrl": "https://github.com/DanielAlongE/wordpress-app-react-expo" 31 | } 32 | } -------------------------------------------------------------------------------- /screens/WpPageScreen.js: -------------------------------------------------------------------------------- 1 | //import * as WebBrowser from 'expo-web-browser'; 2 | import React from 'react'; 3 | import { 4 | ScrollView, 5 | StyleSheet, 6 | Text, 7 | View, 8 | } from 'react-native'; 9 | import MenuButton from '../components/MenuButton'; 10 | import WpPageComp from '../components/WpPageComp'; 11 | 12 | export default class WpPageScreen extends React.Component { 13 | 14 | static navigationOptions = ({ navigation }) => { 15 | 16 | return ({ 17 | headerTitle: navigation.getParam('title', 'Wordpress App'), 18 | headerLeft: ({scene})=>{ 19 | var {navigation} = scene.descriptor; 20 | return (navigation.toggleDrawer()} />)}, 21 | }); 22 | } 23 | 24 | 25 | render(){ 26 | const {navigation} = this.props; 27 | 28 | var id = navigation.getParam('id', 0); 29 | 30 | var args = {navigation, id}; 31 | 32 | return ( 33 | 34 | ) 35 | } 36 | } 37 | 38 | const styles = StyleSheet.create({ 39 | container: { 40 | flex: 1, 41 | backgroundColor: '#fff', 42 | }, 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | //import { AppLoading } from 'expo'; 2 | //import { Asset } from 'expo-asset'; 3 | //import * as Font from 'expo-font'; 4 | import React from 'react'; 5 | import { AppRegistry, Platform, StatusBar, StyleSheet, View, Text } from 'react-native'; 6 | //import { Ionicons } from '@expo/vector-icons'; 7 | 8 | import AppNavigator from './navigation/AppNavigator'; 9 | import Provider from './redux/Provider'; 10 | //import { Provider as PaperProvider } from 'react-native-paper'; 11 | //import NavigationService from './navigation/NavigationService'; 12 | //import theme from './customTheme'; 13 | //import { DefaultTheme } from 'react-native-paper'; 14 | import PaperProvider from './PaperProvider'; 15 | 16 | 17 | function App() { 18 | 19 | 20 | return ( 21 | 22 | 23 | {Platform.OS === 'ios' && } 24 | 25 | 26 | 27 | ); 28 | 29 | } 30 | 31 | 32 | export default App; 33 | 34 | AppRegistry.registerComponent('app', () => App); 35 | 36 | -------------------------------------------------------------------------------- /redux/global-state/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_GLOBAL_STATE, CLEAR_GLOBAL_STATE} from './action'; 2 | 3 | const initialState = { 4 | url:'https://www.premiumtimesng.com', title:'PremiumTimes', 5 | //url:'https://www.thecable.ng', title:'TheCable' 6 | //url: 'https://www.punchng.com' 7 | apps:[ 8 | { 9 | url:'https://www.premiumtimesng.com', title:'PremiumTimes', name:'PremiumTimes' 10 | }, 11 | { 12 | url:'https://www.thecable.ng', title:'TheCable', name:'TheCable', 13 | menus:{ 14 | main_menu:[ 15 | {name:"Home", type:'page', id:1, title:'This is the home page', icon:'home'}, 16 | {name:'About Us', type:'wp_page', id:2}, 17 | {name:'Exchange', type:'wp_page', title:'Ex', id:113154} 18 | ], 19 | sub_menu:[], 20 | custom_menu:[] 21 | } 22 | } 23 | ] 24 | }; 25 | 26 | const reducer = (state = initialState, action) => { 27 | 28 | const {type, obj} = action; 29 | 30 | switch (type) { 31 | case SET_GLOBAL_STATE: 32 | 33 | return {...state, ...obj} 34 | 35 | case CLEAR_GLOBAL_STATE: 36 | 37 | return {} 38 | 39 | default: 40 | return state; 41 | } 42 | } 43 | 44 | export default reducer; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject", 9 | "test": "jest --watchAll" 10 | }, 11 | "jest": { 12 | "preset": "jest-expo" 13 | }, 14 | "dependencies": { 15 | "@expo/samples": "~3.0.3", 16 | "@expo/vector-icons": "^10.0.3", 17 | "@react-navigation/web": "^1.0.0-alpha.9", 18 | "axios": "^0.19.0", 19 | "expo": "^34.0.1", 20 | "expo-asset": "^6.0.0", 21 | "expo-constants": "6.0.0", 22 | "expo-font": "~6.0.0", 23 | "expo-web-browser": "6.0.0", 24 | "moments": "^0.0.2", 25 | "react": "16.8.3", 26 | "react-dom": "^16.8.6", 27 | "react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz", 28 | "react-native-gesture-handler": "~1.3.0", 29 | "react-native-paper": "^2.16.0", 30 | "react-native-render-html": "^4.1.2", 31 | "react-native-web": "^0.11.4", 32 | "react-navigation": "^3.11.0", 33 | "react-redux": "^7.1.0", 34 | "redux": "^4.0.4", 35 | "redux-thunk": "^2.3.0" 36 | }, 37 | "devDependencies": { 38 | "babel-preset-expo": "^6.0.0", 39 | "jest-expo": "^34.0.0" 40 | }, 41 | "private": true 42 | } 43 | -------------------------------------------------------------------------------- /components/PostComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, Image, ImageBackground} from 'react-native'; 3 | import PostContainer from '../containers/PostContainer'; 4 | import {default as Box} from '../layouts/ResponsiveBox'; 5 | import {Button, List, Avatar, Surface, Card, Text, Title, Paragraph } from 'react-native-paper'; 6 | //import Loading from './LoadingComp'; 7 | import HTML from 'react-native-render-html'; 8 | 9 | 10 | 11 | const Post = ({post={},...rest}) => { 12 | 13 | //{key:`post-${key}-${id}`, id, title:purgeHtml(t), content:c, excerpt:purgeHtml(e), date:dateFromNow, media:{thumbnail, medium, full}}; 14 | 15 | const {title="Post not found", content, date, media} = post; 16 | 17 | return( 18 | 19 | 20 | {media && } 21 | 22 | {title} 23 | {date && {date}} 24 | 25 | 26 | 27 | {content && } 28 | 29 | 30 | 31 | ); 32 | 33 | 34 | 35 | } 36 | 37 | //const Post = ({id})=>I am a post {id} 38 | 39 | export default PostContainer(Post) -------------------------------------------------------------------------------- /components/WpPageComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, Image, ImageBackground} from 'react-native'; 3 | import WpPageContainer from '../containers/WpPageContainer'; 4 | import {default as Box} from '../layouts/ResponsiveBox'; 5 | import {Button, List, Avatar, Surface, Card, Text, Title, Paragraph } from 'react-native-paper'; 6 | //import Loading from './LoadingComp'; 7 | import HTML from 'react-native-render-html'; 8 | 9 | 10 | 11 | const Page = ({page={},...rest}) => { 12 | 13 | //{key:`post-${key}-${id}`, id, title:purgeHtml(t), content:c, excerpt:purgeHtml(e), date:dateFromNow, media:{thumbnail, medium, full}}; 14 | 15 | const {title="Page not found", content, date, media} = page; 16 | 17 | return ( 18 | {content && } 19 | ); 20 | /* 21 | return( 22 | 23 | 24 | {media && } 25 | 26 | {title} 27 | {date && {date}} 28 | 29 | 30 | 31 | {content && } 32 | 33 | 34 | 35 | ); 36 | */ 37 | 38 | 39 | } 40 | 41 | //const Post = ({id})=>I am a post {id} 42 | 43 | export default WpPageContainer(Page) -------------------------------------------------------------------------------- /components/DrawerComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import {ScrollView, StyleSheet, Text, View} from 'react-native'; 4 | //import { DrawerItems, SafeAreaView } from 'react-navigation'; 5 | import { Drawer, List, Button } from 'react-native-paper'; 6 | //import NavigationService from '../navigation/NavigationService.js'; 7 | import LoadingComp from './LoadingComp'; 8 | import theme from '../customTheme'; 9 | 10 | 11 | 12 | const DrawerComp = ({categories, navigation, ...rest}) => { 13 | 14 | const {navigate} = navigation; 15 | 16 | const data = categories && categories.data ? categories.data : []; 17 | 18 | let root = data.filter((cat)=>cat.parent === 0); 19 | 20 | //console.log('navigation', rest.navigation.push); 21 | 22 | var menu = root.map((n,i) => { 23 | 24 | const {name, id} = n; 25 | 26 | return ( 27 | { 30 | navigation.closeDrawer(); 31 | navigate('Posts', {title:name, categories:id}); 32 | }} 33 | />)} 34 | ); 35 | 36 | const {colors} = theme(); 37 | 38 | return ( 39 | 40 | 41 | 42 | {data.length===0 ? : menu} 43 | 44 | 45 | 46 | )}; 47 | 48 | const mapState = state => ({ 49 | categories:state.api.categories, 50 | gState: state.globalState 51 | }); 52 | 53 | export default connect(mapState)(DrawerComp); -------------------------------------------------------------------------------- /components/SubMenuComp.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {ScrollView, StyleSheet, Text, View} from 'react-native'; 3 | //import { DrawerItems, SafeAreaView } from 'react-navigation'; 4 | import { Drawer, List, Button } from 'react-native-paper'; 5 | import LoadingComp from './LoadingComp'; 6 | //import theme from '../customTheme'; 7 | import { DefaultTheme } from 'react-native-paper'; 8 | 9 | import SubMenuContainer from '../containers/SubMenuContainer'; 10 | 11 | 12 | const SubMenuComp = ({menu, navigation, theme=DefaultTheme}) => { 13 | 14 | const [active, setActive] = useState(-1) 15 | 16 | const showMenu = menu.map((item, index) => { 17 | 18 | const {key, name, icon, onPress} = item; 19 | 20 | const extra = {} 21 | extra.icon = icon; 22 | 23 | const isActive = index===active; 24 | 25 | return ( 26 | { 29 | setActive(index); 30 | navigation.closeDrawer(); 31 | onPress(); 32 | }} 33 | active={isActive} 34 | {...extra} 35 | />)} 36 | ); 37 | 38 | const {colors} = theme; 39 | 40 | return ( 41 | 42 | 43 | 44 | {menu.length===0 ? : showMenu} 45 | 46 | 47 | 48 | )}; 49 | 50 | 51 | export default SubMenuContainer(SubMenuComp); -------------------------------------------------------------------------------- /customTheme.js: -------------------------------------------------------------------------------- 1 | import { DefaultTheme, Colors } from 'react-native-paper'; 2 | import store from './redux/store'; 3 | 4 | /* 5 | primary - primary color for your app, usually your brand color. 6 | accent - secondary color for your app which complements the primary color. 7 | background - background color for pages, such as lists. 8 | surface - background color for elements containing content, such as cards. 9 | text - text color for content. 10 | disabled - color for disabled elements. 11 | placeholder - color for placeholder text, such as input placeholder. 12 | backdrop - color for backdrops of various components such as modals. 13 | primary: string; 14 | background: string; 15 | surface: string; 16 | accent: string; 17 | error: string; 18 | text: string; 19 | disabled: string; 20 | placeholder: string; 21 | backdrop: string; 22 | 23 | 24 | const theme = { 25 | ...DefaultTheme, 26 | // roundness: 10, 27 | colors: { 28 | ...DefaultTheme.colors, 29 | // primary: Colors.lightBlue900, //'#3498db', 30 | // accent: Colors.lightBlue500, //'#f1c40f', 31 | // background: Colors.lightBlue50 32 | }, 33 | dark:false 34 | }; 35 | */ 36 | 37 | const theme = () => { 38 | 39 | const state = store.getState(); 40 | 41 | const appIndex = state && state.globalstore && state.globalState.currentApp ? state.globalState.currentApp : 0; 42 | 43 | const apps = state && state.globalstore && state.globalstore.apps || []; 44 | 45 | const theme = apps[appIndex] && apps[appIndex]['theme'] || DefaultTheme; 46 | 47 | //console.log("appIndex", appIndex, "theme from state", theme); 48 | 49 | return theme; 50 | }; 51 | 52 | 53 | export default theme; 54 | -------------------------------------------------------------------------------- /containers/Container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | //import {URL, clearApi, getApi, addApi, editApi, deleteApi, cancelToken} from '../redux/actions'; 7 | //import {history} from '../redux/Store'; 8 | 9 | 10 | const CategoriesContainer = (Comp, rest={}) => class extends Component { 11 | 12 | 13 | offset=0; 14 | per_page=100; 15 | categories=0; 16 | //author=0; 17 | search=''; 18 | orderby='date'; 19 | order='desc'; 20 | status='publish'; 21 | 22 | 23 | constructor(props){ 24 | 25 | super(props); 26 | 27 | this.state = { 28 | 29 | } 30 | 31 | //desc, asc |author, date, id, include, modified, parent, relevance, slug, title 32 | //this.fetchMore = this.fetchMore.bind(this); 33 | 34 | 35 | } 36 | 37 | 38 | fetchMore(){ 39 | 40 | } 41 | 42 | 43 | componentWillMount(){ 44 | this._isMounted = false; 45 | //set cancelToken 46 | //this.cancelToken = this.props.cancelToken(); 47 | } 48 | 49 | componentWillUnmount() { 50 | this._isMounted = false; 51 | 52 | //if(this.cancelToken){ this.cancelToken.cancel('ComponenetWillUnmount');} 53 | } 54 | 55 | componentDidMount() { 56 | this._isMounted = true; 57 | 58 | } 59 | 60 | 61 | render() { 62 | 63 | 64 | return ( 65 | 66 | ) 67 | } 68 | 69 | } 70 | 71 | 72 | const mapStateToProps = state => ( 73 | { 74 | // addressBook:state.api.addressBook, 75 | 76 | }); 77 | 78 | 79 | export default compose( 80 | connect(mapStateToProps, {cancelToken}), 81 | CategoriesContainer 82 | ); -------------------------------------------------------------------------------- /containers/WpPageContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | 8 | import { WordPressClass } from '../builder/containers/WordPressPostsContainer'; 9 | 10 | const WpPageContainer = (Comp, rest={}) => class extends WordPressClass { 11 | 12 | //getPostById 13 | 14 | componentDidMount() { 15 | const {id} = this.props; 16 | 17 | const pageIndex = this.findPageIndex({id}); 18 | 19 | console.log({id, pageIndex}); 20 | 21 | //page not found in store, will attempt to download it 22 | if(pageIndex === -1){ 23 | this.fetchPages({id}).then(res=>console.log("done fetching page: ", id)); 24 | console.log('fetching pages id:', id) 25 | } 26 | 27 | // console.log("getSinglePost", ) 28 | 29 | } 30 | 31 | 32 | render() { 33 | 34 | const {id} = this.props; 35 | 36 | 37 | const data = this.getSinglePage({id}); 38 | 39 | //!!data.content 40 | 41 | const page = data ? this.preparePost(data) : {}; 42 | 43 | var args = {id, page}; 44 | 45 | return ( 46 | 47 | ) 48 | } 49 | 50 | } 51 | 52 | 53 | const mapStateToProps = state => { 54 | 55 | const appIndex = state.globalState.currentApp || 0; 56 | 57 | return ({ 58 | url: state.globalState.url, 59 | pages:state.api[`pages-${appIndex}`], 60 | // categories:state.api[`categories-${appIndex}`], 61 | // gState:state.globalState, 62 | appIndex 63 | 64 | }); 65 | }; 66 | 67 | 68 | // 69 | 70 | export default compose( 71 | connect(mapStateToProps, {getApi, cancelToken}), 72 | WpPageContainer 73 | ); -------------------------------------------------------------------------------- /navigation/MainTabNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Platform } from 'react-native'; 3 | import { createStackNavigator, createBottomTabNavigator } from 'react-navigation'; 4 | 5 | import TabBarIcon from '../components/TabBarIcon'; 6 | import HomeScreen from '../screens/HomeScreen'; 7 | import LinksScreen from '../screens/LinksScreen'; 8 | import SettingsScreen from '../screens/SettingsScreen'; 9 | 10 | const config = Platform.select({ 11 | web: { headerMode: 'screen' }, 12 | default: {}, 13 | }); 14 | 15 | const HomeStack = createStackNavigator( 16 | { 17 | Home: HomeScreen, 18 | }, 19 | config 20 | ); 21 | 22 | HomeStack.navigationOptions = { 23 | tabBarLabel: 'Home', 24 | tabBarIcon: ({ focused }) => ( 25 | 33 | ), 34 | }; 35 | 36 | HomeStack.path = ''; 37 | 38 | const LinksStack = createStackNavigator( 39 | { 40 | Links: LinksScreen, 41 | }, 42 | config 43 | ); 44 | 45 | LinksStack.navigationOptions = { 46 | tabBarLabel: 'Links', 47 | tabBarIcon: ({ focused }) => ( 48 | 49 | ), 50 | }; 51 | 52 | LinksStack.path = ''; 53 | 54 | const SettingsStack = createStackNavigator( 55 | { 56 | Settings: SettingsScreen, 57 | }, 58 | config 59 | ); 60 | 61 | SettingsStack.navigationOptions = { 62 | tabBarLabel: 'Settings', 63 | tabBarIcon: ({ focused }) => ( 64 | 65 | ), 66 | }; 67 | 68 | SettingsStack.path = ''; 69 | 70 | const tabNavigator = createBottomTabNavigator({ 71 | HomeStack, 72 | LinksStack, 73 | SettingsStack, 74 | }); 75 | 76 | tabNavigator.path = ''; 77 | 78 | export default tabNavigator; 79 | -------------------------------------------------------------------------------- /containers/PostContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | 8 | import { WordPressClass } from '../builder/containers/WordPressPostsContainer'; 9 | 10 | const PostContainer = (Comp, rest={}) => class extends WordPressClass { 11 | 12 | //getPostById 13 | 14 | componentDidMount() { 15 | const {id} = this.props; 16 | 17 | const postIndex = this.findPostIndex({id}); 18 | 19 | //post not found in store, will attempt to download it 20 | if(postIndex === -1){ 21 | this.fetchPosts({id}).then(res=>console.log('done fetching post id: ', id)); 22 | } 23 | 24 | 25 | } 26 | 27 | 28 | render() { 29 | 30 | const {id} = this.props; 31 | 32 | //const allData = posts ? posts.data : []; 33 | 34 | //const index = allData.findIndex(post=>post.id === id); 35 | 36 | //const post = index > -1 ? allData[index] : {}; 37 | 38 | //console.log('post ', index) 39 | 40 | const data = this.getSinglePost({id}); 41 | 42 | const post = data ? this.preparePost(data) : {}; 43 | 44 | var args = {id, post}; 45 | 46 | return ( 47 | 48 | ) 49 | } 50 | 51 | } 52 | 53 | 54 | const mapStateToProps = state => { 55 | 56 | const appIndex = state.globalState.currentApp || 0; 57 | 58 | return ({ 59 | url: state.globalState.url, 60 | posts:state.api[`posts-${appIndex}`], 61 | // categories:state.api[`categories-${appIndex}`], 62 | // gState:state.globalState, 63 | appIndex 64 | 65 | }); 66 | }; 67 | 68 | 69 | // 70 | 71 | export default compose( 72 | connect(mapStateToProps, {getApi, cancelToken}), 73 | PostContainer 74 | ); -------------------------------------------------------------------------------- /components/MainMenuComp.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {ScrollView, StyleSheet, Text, View} from 'react-native'; 3 | //import { DrawerItems, SafeAreaView } from 'react-navigation'; 4 | import { Drawer, List, Button } from 'react-native-paper'; 5 | //import LoadingComp from './LoadingComp'; 6 | //import theme from '../customTheme'; 7 | import { DefaultTheme } from 'react-native-paper'; 8 | import {default as Box} from '../layouts/ResponsiveBox'; 9 | import MainMenuContainer from '../containers/MainMenuContainer'; 10 | 11 | 12 | const MainMenuComp = ({menu=[], navigation, theme=DefaultTheme}) => { 13 | 14 | const [active, setActive] = useState(-1) 15 | 16 | const showMenu = menu.map((item, index) => { 17 | 18 | const {key, name, icon, onPress} = item; 19 | 20 | const extra = {} 21 | extra.icon = icon; 22 | extra.style = {flex:1} 23 | 24 | //if name not found 25 | if(name==='' && icon){ 26 | extra.style.flex = 0.2; 27 | } 28 | 29 | const isActive = index===active; 30 | 31 | return ( 32 | { 35 | setActive(index); 36 | navigation.closeDrawer(); 37 | onPress(); 38 | }} 39 | active={isActive} 40 | {...extra} 41 | />)} 42 | ); 43 | 44 | const {colors} = theme; 45 | //style={{flex:1}} 46 | /* 47 | 48 | 49 | */ 50 | 51 | if(menu.length===0){ 52 | return null; 53 | } 54 | 55 | return ( 56 | 57 | 58 | 59 | {showMenu} 60 | 61 | 62 | 63 | )}; 64 | 65 | // 66 | export default MainMenuContainer(MainMenuComp); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wordpress App Builder 2 | This App seeks to explore the posibility of creating a mobile app version of an existing Wordpress Blog. 3 | This is made easy because of the wordpress rest api. 4 | Here we are using React Native via Expo for ease! 5 | 6 | In seconds your WordPress site becomes a Native App. 7 | The WordPress Gutenberg editor is the inspiration behind my quest. 8 | Imagine being able to add and manipulate components just as you would on using a visual editor on WordPress. 9 | 10 | Collaborators are welcomed! 11 | 12 | ### Here are some sample wordpress site url you can try the app with. 13 | 14 | ``` 15 | http://www.premiumtimesng.com/ 16 | http://www.punchng.com/ 17 | http://www.sunnewsonline.com/ 18 | http://thecable.ng/ 19 | ``` 20 | 21 | ## Screenshots 22 | 23 | ![PremiumTimes](https://user-images.githubusercontent.com/10385516/64017189-eac9da80-cb20-11e9-9b02-879c162bb986.png) 24 | 25 | ![Drawer Menu](https://user-images.githubusercontent.com/10385516/64017333-45633680-cb21-11e9-9238-f7e9e0a832ef.png) 26 | 27 | ![TheCable](https://user-images.githubusercontent.com/10385516/64017258-0fbe4d80-cb21-11e9-9fec-749f2cf3bdf0.png) 28 | 29 | ![Donald Trump](https://user-images.githubusercontent.com/10385516/64017287-26fd3b00-cb21-11e9-9c89-25a600dd2c78.png) 30 | 31 | ![App Main](https://user-images.githubusercontent.com/10385516/64016962-51022d80-cb20-11e9-8209-43622ec7b71f.png) 32 | 33 | ![App Settings](https://user-images.githubusercontent.com/10385516/64017043-7ee77200-cb20-11e9-9de0-aa470e025fdb.png) 34 | 35 | ![Theme Settings](https://user-images.githubusercontent.com/10385516/64017098-a1798b00-cb20-11e9-978e-5d4d394aa916.png) 36 | 37 | ![Menu Settings](https://user-images.githubusercontent.com/10385516/64017155-cec63900-cb20-11e9-8e62-8407f3836a86.png) 38 | 39 | 40 | Get live prreview [here](https://exp.host/@danielalonge/wordpress-app-builder) 41 | 42 | ## Install 43 | 44 | ```sh 45 | npm install 46 | ``` 47 | 48 | Alternatively you may use `yarn`: 49 | 50 | ```sh 51 | yarn install 52 | ``` 53 | ## Available Scripts 54 | 55 | In the project directory, you can run: 56 | 57 | ### `expo start` 58 | -------------------------------------------------------------------------------- /screens/HomeScreen.js: -------------------------------------------------------------------------------- 1 | //import * as WebBrowser from 'expo-web-browser'; 2 | import React from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { 5 | ScrollView, 6 | StyleSheet, 7 | Text, 8 | View, 9 | } from 'react-native'; 10 | import MenuButton from '../components/MenuButton'; 11 | //import HomeComp from '../components/HomeComp'; 12 | //import PostsComp from '../components/PostsComp'; 13 | import theme from '../customTheme'; 14 | import ScreenRotate from '../layouts/ScreenRotate'; 15 | import { withNavigationFocus } from "react-navigation"; 16 | import WordPress from '../builder/components/WordPressPostsComp'; 17 | //import WordPress from '../components/HomeComp'; 18 | 19 | 20 | class HomeScreen extends React.Component { 21 | 22 | static navigationOptions = ({ navigation }) => { 23 | 24 | return ({ 25 | headerTitle: navigation.getParam('title', 'Wordpress App'), 26 | headerLeft: ({scene})=>{ 27 | var {navigation} = scene.descriptor; 28 | return (navigation.toggleDrawer()} />)}, 29 | }); 30 | } 31 | 32 | componentDidUpdate(prevProps) { 33 | if (prevProps.isFocused !== this.props.isFocused) { 34 | // Use the `this.props.isFocused` boolean 35 | // Call any action 36 | 37 | } 38 | //console.log("HomeScreen is Focused", prevProps.isFocused , this.props.isFocused) 39 | 40 | //let state = this.props.navigation.state.params; 41 | 42 | //console.log('HomeScreen',{state}); 43 | } 44 | 45 | componentDidMount() { 46 | 47 | //this.props.navigation.navigate('Home', {title:'I hope we are right'}); 48 | 49 | } 50 | 51 | render(){ 52 | 53 | const {navigation, isFocused} = this.props; 54 | 55 | const app = navigation.state.params || {}; 56 | 57 | const {colors} = theme(); 58 | 59 | return ( 60 | 61 | 62 | 63 | 64 | 65 | 66 | ) 67 | } 68 | } 69 | 70 | 71 | export default withNavigationFocus(HomeScreen); -------------------------------------------------------------------------------- /containers/CategoriesContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | 8 | 9 | const CategoriesContainer = (Comp, rest={}) => class extends Component { 10 | 11 | 12 | offset=0; 13 | per_page=50; 14 | categories=0; 15 | //author=0; 16 | search=''; 17 | orderby='count'; 18 | order='desc'; 19 | status='publish'; 20 | hide_empty=true 21 | 22 | 23 | constructor(props){ 24 | 25 | super(props); 26 | 27 | this.state = { 28 | 29 | } 30 | 31 | //desc, asc |author, date, id, include, modified, parent, relevance, slug, title 32 | //this.fetchMore = this.fetchMore.bind(this); 33 | 34 | 35 | } 36 | 37 | 38 | fetchMore(){ 39 | let {per_page, orderby, order, hide_empty} = this; 40 | let {getApi, url} = this.props; 41 | //console.log('static', this); 42 | getApi(`${url}/wp-json/wp/v2/categories`,{per_page, orderby, order, hide_empty},'categories') 43 | .then(res=>console.log('done fetching')); 44 | } 45 | 46 | 47 | componentWillMount(){ 48 | this._isMounted = false; 49 | //set cancelToken 50 | //this.cancelToken = this.props.cancelToken(); 51 | } 52 | 53 | componentWillUnmount() { 54 | this._isMounted = false; 55 | 56 | //if(this.cancelToken){ this.cancelToken.cancel('ComponenetWillUnmount');} 57 | } 58 | 59 | componentDidMount() { 60 | this._isMounted = true; 61 | 62 | const {categories} = this.props 63 | 64 | if(categories){}else{this.fetchMore()} 65 | 66 | } 67 | 68 | 69 | render() { 70 | 71 | //console.log(this.props.url); 72 | 73 | const {navigation} = this.props; 74 | 75 | var categories = this.props.categories ? this.props.categories.data : []; 76 | 77 | 78 | 79 | const args = {categories}; 80 | 81 | if(navigation){ 82 | args.navigation = navigation; 83 | } 84 | 85 | return ( 86 | 87 | ) 88 | } 89 | 90 | } 91 | 92 | 93 | const mapStateToProps = state => ( 94 | { 95 | url: state.globalState.url, 96 | categories:state.api.categories, 97 | api: state.api 98 | 99 | }); 100 | 101 | 102 | export default compose( 103 | connect(mapStateToProps, {getApi, cancelToken}), 104 | CategoriesContainer 105 | ); -------------------------------------------------------------------------------- /navigation/DrawerNavigator.js: -------------------------------------------------------------------------------- 1 | import {DrawerItems, createDrawerNavigator, createStackNavigator, createSwitchNavigator, createBottomTabNavigator} from 'react-navigation'; 2 | //import DrawerComp from '../components/DrawerComp'; 3 | import theme from '../customTheme'; 4 | import React from 'react'; 5 | import {View} from 'react-native'; 6 | 7 | import DrawerComp from '../components/SubMenuComp'; 8 | import AppbarComp from '../components/AppbarComp'; 9 | 10 | import HomeScreen from '../screens/HomeScreen'; 11 | import PostsScreen from '../screens/PostsScreen'; 12 | import PostScreen from '../screens/PostScreen'; 13 | import WpPageScreen from '../screens/WpPageScreen'; 14 | 15 | import StartScreen from '../screens/StartScreen'; 16 | import SettingsScreen from '../screens/SettingsScreen'; 17 | import MenusScreen from '../screens/MenusScreen'; 18 | import PagesScreen from '../screens/PagesScreen'; 19 | import EditorScreen from '../screens/EditorScreen'; 20 | import ThemeScreen from '../screens/ThemeScreen'; 21 | 22 | const {colors} = theme(); 23 | 24 | 25 | const drawerViewConfig = { 26 | contentComponent: DrawerComp, 27 | contentOptions: { 28 | activeTintColor: colors.backdrop, 29 | activeBackgroundColor: colors.primary, 30 | inactiveTintColor: colors.text, 31 | inactiveBackgroundColor: colors.surface, 32 | style: {}, 33 | labelStyle: {} 34 | }, 35 | drawerWidth : 300, 36 | drawerType: 'front',// 'front' | 'back' | 'slide'; 37 | drawerLockMode: "locked-closed", //'unlocked' | 'locked-closed' | 'locked-open'; 38 | edgeWidth: 1, 39 | drawerPosition: 'left' 40 | // hideStatusBar: false, 41 | // overlayColor: "#fff", 42 | }; 43 | 44 | 45 | const stackConfig = {mode: 'card', //modal| 'card' 46 | headerBackTitleVisible:false, 47 | headerLayoutPreset: 'center', 48 | cardOverlayEnabled: false, 49 | 50 | 51 | defaultNavigationOptions: ({ navigation }) => { 52 | return ({ 53 | header: ( 54 | 55 | ), 56 | }) 57 | } 58 | }; 59 | 60 | const PageEditor = createSwitchNavigator( 61 | { 62 | Editor: {screen: EditorScreen} 63 | } 64 | ); 65 | 66 | 67 | const SettingsNavigator = createBottomTabNavigator({ 68 | Settings: {screen: SettingsScreen}, 69 | Menus: {screen: MenusScreen}, 70 | Pages: {screen: PagesScreen}, 71 | Theme: {screen: ThemeScreen} 72 | 73 | },{ 74 | tabBarOptions: { 75 | activeTintColor: 'tomato', 76 | inactiveTintColor: 'gray', 77 | } 78 | }); 79 | 80 | 81 | const HomeNavigator = createStackNavigator({ 82 | Splash: {screen: StartScreen}, 83 | Home: {screen: HomeScreen}, 84 | Posts: {screen: PostsScreen}, 85 | Post: {screen: PostScreen}, 86 | WpPage: {screen: WpPageScreen}, 87 | }, 88 | {...stackConfig}); 89 | 90 | export default createDrawerNavigator({ 91 | App: HomeNavigator, 92 | Settings: SettingsNavigator, 93 | Editor: {screen: PageEditor} 94 | }, 95 | { 96 | initialRouteName: "App", 97 | 98 | ...drawerViewConfig, 99 | 100 | }); 101 | -------------------------------------------------------------------------------- /containers/MainMenuContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | //import HomeContainer from './HomeContainer'; 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | //import NavigationService from '../navigation/NavigationService.js'; 8 | //import purgeHtml from '../builder/containers/_purgeHtml'; 9 | import { WordPressClass } from '../builder/containers/WordPressPostsContainer'; 10 | import * as dotProp from '../builder/containers/_dotProp'; 11 | 12 | const MainMenuContainer = (Comp, rest={}) => class extends WordPressClass { 13 | 14 | 15 | offset=0; 16 | per_page=50; 17 | categories=0; 18 | //author=0; 19 | search=''; 20 | orderby='count'; 21 | order='desc'; 22 | status='publish'; 23 | hide_empty=true 24 | 25 | 26 | constructor(props){ 27 | 28 | super(props); 29 | 30 | this.state = { 31 | 32 | } 33 | 34 | //desc, asc |author, date, id, include, modified, parent, relevance, slug, title 35 | //this.fetchMore = this.fetchMore.bind(this); 36 | 37 | 38 | } 39 | 40 | getAppMenu(target="main_menu"){ 41 | 42 | const {currentApp, apps} = this.props.gState; 43 | const app = apps[currentApp] || {} ; 44 | 45 | return dotProp.get(app, `menus.${target}`, []);//app.menus && app.menus[target] ? app.menus[target] : [] 46 | } 47 | 48 | 49 | // super methods 50 | 51 | // fetchMore() 52 | 53 | //fetchCategories(obj={}) 54 | 55 | getMenuData(){ 56 | const menu = this.getAppMenu(); 57 | return this.prepareMenu(menu); 58 | } 59 | 60 | componentWillMount(){ 61 | this._isMounted = false; 62 | //set cancelToken 63 | //this.cancelToken = this.props.cancelToken(); 64 | } 65 | 66 | componentWillUnmount() { 67 | this._isMounted = false; 68 | 69 | //if(this.cancelToken){ this.cancelToken.cancel('ComponenetWillUnmount');} 70 | } 71 | 72 | componentDidMount() { 73 | this._isMounted = true; 74 | 75 | //console.log("getMenuData", this.getMenuData()) 76 | 77 | } 78 | 79 | 80 | render() { 81 | 82 | //console.log(this.props.url); 83 | 84 | const {navigation, theme} = this.props; 85 | 86 | 87 | //var categories = this.props.categories ? this.props.categories.data : []; 88 | 89 | 90 | 91 | const menu = this.getMenuData(); 92 | 93 | 94 | const args = {menu, theme}; 95 | 96 | if(navigation){ 97 | args.navigation = navigation; 98 | } 99 | 100 | return ( 101 | 102 | ) 103 | } 104 | 105 | } 106 | 107 | 108 | 109 | const mapStateToProps = state => { 110 | 111 | const appIndex = state.globalState.currentApp || 0; 112 | const apps = state.globalState.apps || []; 113 | const theme = apps && apps[appIndex] && apps[appIndex]['theme']; 114 | 115 | return ({ 116 | url: state.globalState.url, 117 | // posts:state.api[`posts-${appIndex}`], 118 | categories:state.api[`categories-${appIndex}`], 119 | gState:state.globalState, 120 | appIndex, 121 | theme 122 | 123 | }); 124 | }; 125 | 126 | 127 | export default compose( 128 | connect(mapStateToProps, {getApi, cancelToken}), 129 | MainMenuContainer 130 | ); -------------------------------------------------------------------------------- /containers/SubMenuContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | //import HomeContainer from './HomeContainer'; 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | //import NavigationService from '../navigation/NavigationService.js'; 8 | //import purgeHtml from '../builder/containers/_purgeHtml'; 9 | import { WordPressClass } from '../builder/containers/WordPressPostsContainer'; 10 | 11 | const SubMenuContainer = (Comp, rest={}) => class extends WordPressClass { 12 | 13 | 14 | offset=0; 15 | per_page=50; 16 | categories=0; 17 | //author=0; 18 | search=''; 19 | orderby='count'; 20 | order='desc'; 21 | status='publish'; 22 | hide_empty=true 23 | 24 | 25 | constructor(props){ 26 | 27 | super(props); 28 | 29 | this.state = { 30 | 31 | } 32 | 33 | //desc, asc |author, date, id, include, modified, parent, relevance, slug, title 34 | //this.fetchMore = this.fetchMore.bind(this); 35 | 36 | 37 | } 38 | 39 | getAppMenu(target="sub_menu"){ 40 | 41 | const {currentApp, apps} = this.props.gState; 42 | const app = apps[currentApp] || {} ; 43 | 44 | return app.menus && app.menus[target] ? app.menus[target] : [] 45 | } 46 | 47 | 48 | // super methods 49 | 50 | // fetchMore() 51 | 52 | //fetchCategories(obj={}) 53 | 54 | 55 | getMenuData(){ 56 | const app = this.getAppMenu(); 57 | var menu; 58 | 59 | if(app.length > 0){ 60 | menu = app; 61 | }else{ 62 | let data = this.props.categories ? this.props.categories.data : []; 63 | 64 | menu = data.filter((cat)=>cat.parent === 0); 65 | menu = this.prepareCategories(menu); 66 | } 67 | 68 | return this.prepareMenu(menu); 69 | } 70 | 71 | componentWillMount(){ 72 | this._isMounted = false; 73 | //set cancelToken 74 | //this.cancelToken = this.props.cancelToken(); 75 | } 76 | 77 | componentWillUnmount() { 78 | this._isMounted = false; 79 | 80 | //if(this.cancelToken){ this.cancelToken.cancel('ComponenetWillUnmount');} 81 | } 82 | 83 | componentDidMount() { 84 | this._isMounted = true; 85 | 86 | const {categories} = this.props 87 | 88 | } 89 | 90 | 91 | render() { 92 | 93 | //console.log(this.props.url); 94 | 95 | const {navigation, theme} = this.props; 96 | 97 | 98 | //var categories = this.props.categories ? this.props.categories.data : []; 99 | 100 | 101 | 102 | const menu = this.getMenuData(); 103 | 104 | 105 | const args = {menu, theme}; 106 | 107 | if(navigation){ 108 | args.navigation = navigation; 109 | } 110 | 111 | return ( 112 | 113 | ) 114 | } 115 | 116 | } 117 | 118 | 119 | 120 | const mapStateToProps = state => { 121 | 122 | const appIndex = state.globalState.currentApp || 0; 123 | const apps = state.globalState.apps || []; 124 | const theme = apps && apps[appIndex] && apps[appIndex]['theme']; 125 | 126 | return ({ 127 | url: state.globalState.url, 128 | // posts:state.api[`posts-${appIndex}`], 129 | categories:state.api[`categories-${appIndex}`], 130 | gState:state.globalState, 131 | appIndex, 132 | theme 133 | 134 | }); 135 | }; 136 | 137 | 138 | export default compose( 139 | connect(mapStateToProps, {getApi, cancelToken}), 140 | SubMenuContainer 141 | ); -------------------------------------------------------------------------------- /redux/api/reducer.js: -------------------------------------------------------------------------------- 1 | import {FETCH_API_REQUEST, FETCH_API_SUCCESS, FETCH_API_FAILURE, 2 | FETCH_API_CLEAR, FETCH_API_DELETE, FETCH_API_ADD, 3 | FETCH_API_PREPEND, FETCH_API_EDIT} from './action'; 4 | 5 | const initialState = {}; 6 | 7 | const reducer = (state = initialState, action) => { 8 | 9 | const {id, type, payload} = action; 10 | 11 | var oldData = state && state[id] && state[id].data ? state[id].data : []; 12 | var oldOffset = state && state[id] && state[id].offset ? state[id].offset : 0; 13 | 14 | 15 | if(typeof id === 'undefined'){ 16 | // console.log('id is undefined!'); 17 | }else{ 18 | //console.log('id is '+id); 19 | 20 | if(state[id] && state[id].data){ 21 | //console.log(state[id], state[id].data); 22 | //console.log('state[id] available'); 23 | }else{ 24 | state = {...state, [id] : { 25 | isFetching:true, 26 | data: [...oldData], 27 | offset:oldOffset 28 | } 29 | } 30 | 31 | //console.log('state[id] unavailable'); 32 | } 33 | } 34 | 35 | switch (type) { 36 | case FETCH_API_REQUEST: 37 | 38 | return {...state, [id] : { 39 | isFetching:true, 40 | data: [...oldData], 41 | offset:oldOffset 42 | } 43 | } 44 | 45 | case FETCH_API_SUCCESS: 46 | 47 | let count, newData; 48 | 49 | if(Array.isArray(payload)){ 50 | count = payload.length; 51 | newData = payload; 52 | }else{ 53 | count = 1; 54 | newData = [payload]; 55 | } 56 | 57 | 58 | return {...state, [id] : { 59 | isFetching:false, 60 | data:[...oldData, ...newData], 61 | offset: (oldOffset+count) 62 | } 63 | } 64 | 65 | case FETCH_API_ADD: 66 | 67 | if(Array.isArray(payload)){ 68 | count = payload.length; 69 | newData = payload; 70 | }else{ 71 | count = 1; 72 | newData = [payload]; 73 | } 74 | 75 | 76 | return {...state, [id] : { 77 | isFetching:false, 78 | data:[...oldData, ...newData], 79 | offset: (oldOffset+count) 80 | } 81 | } 82 | 83 | case FETCH_API_PREPEND: 84 | 85 | if(Array.isArray(payload)){ 86 | count = payload.length; 87 | newData = payload; 88 | }else{ 89 | count = 1; 90 | newData = [payload]; 91 | } 92 | 93 | 94 | return {...state, [id] : { 95 | isFetching:false, 96 | data:[...newData, ...oldData], 97 | offset: (oldOffset+count) 98 | } 99 | } 100 | 101 | case FETCH_API_EDIT: 102 | 103 | 104 | let objIndex = oldData.findIndex((data => data.id === payload.id)); 105 | 106 | if(objIndex>-1){ 107 | oldData[objIndex] = payload; 108 | } 109 | 110 | console.log(objIndex, payload); 111 | 112 | return {...state, [id] : { 113 | data:[...oldData] 114 | } 115 | } 116 | 117 | 118 | case FETCH_API_FAILURE: 119 | 120 | return {...state, [id] : { 121 | isFetching:false, 122 | error:payload, 123 | data:[...oldData], 124 | offset:oldOffset 125 | } 126 | } 127 | 128 | 129 | case FETCH_API_CLEAR: 130 | 131 | return {...state, [id] : { 132 | isFetching:false, 133 | data:[], 134 | offset:0 135 | } 136 | } 137 | 138 | case FETCH_API_DELETE: 139 | 140 | if(payload){ 141 | //count = payload.length; 142 | 143 | 144 | let value = payload.value ? payload.value : payload; 145 | let id = payload.id ? payload.id : 'id'; 146 | 147 | //let dataIndex = oldData.findIndex((item => item[id] === value)); 148 | newData = oldData.filter(item => (item[id] !== value)); 149 | //newData = payload; 150 | } 151 | 152 | 153 | return {...state, [id] : { 154 | isFetching:false, 155 | data:[...newData], 156 | offset: (oldOffset-1) 157 | } 158 | } 159 | 160 | default: 161 | return state; 162 | } 163 | } 164 | 165 | export default reducer; -------------------------------------------------------------------------------- /containers/HomeContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | import set from '../redux/global-state'; 8 | 9 | import { WordPressClass } from '../builder/containers/WordPressPostsContainer'; 10 | 11 | const HomeContainer = (Comp, rest={}) => class extends WordPressClass { 12 | apiId = 'posts'; 13 | offset=0; 14 | per_page=10; 15 | //categories=0; 16 | //author=0; 17 | search=''; 18 | orderby='date'; 19 | order='desc'; 20 | status='publish'; 21 | //hide_empty=true 22 | 23 | 24 | 25 | constructor(props){ 26 | 27 | super(props, {per_page:1, strange:'I am strange'}); 28 | 29 | //this.comp = comp; 30 | 31 | this.state = { 32 | 33 | } 34 | 35 | //desc, asc |author, date, id, include, modified, parent, relevance, slug, title 36 | //this.fetchMore = this.fetchMore.bind(this); 37 | 38 | 39 | } 40 | 41 | //getAvailablePostsId() 42 | 43 | //fetchCategories(obj={}) 44 | 45 | //fetchMore() 46 | 47 | //fetchPosts(obj={}) 48 | 49 | //getCategories() 50 | 51 | 52 | //oneByOne(objects_array, iterator, callback) 53 | 54 | //fetchPostsByCategory(ids=[]) 55 | 56 | //preparePost(post) 57 | 58 | //preparePosts(posts) 59 | 60 | //generateHome() 61 | 62 | 63 | componentWillMount(){ 64 | this._isMounted = false; 65 | 66 | } 67 | 68 | init(){ 69 | const {appIndex, url, posts} = this.props; 70 | 71 | //let collect = purgeHtml("

This is Html $2000 and %50

"); 72 | //console.log(`"${collect}"`); 73 | 74 | console.log('home', {appIndex, url}); 75 | 76 | //get app config 77 | //this.props.navigation.setParams({ title: gState.title || 'Kilode?' }); 78 | if(posts && posts.data){}else{ 79 | super.generateHome(); 80 | } 81 | } 82 | 83 | componentDidMount() { 84 | this._isMounted = true; 85 | 86 | console.log('Home, componentDidMount') 87 | this.init(); 88 | } 89 | 90 | componentWillReceiveProps(nextProps) { 91 | if(nextProps.appIndex !== this.props.appIndex){ 92 | this.init(); 93 | console.log('Home, componentWillReceiveProps', nextProps.appIndex, this.props.appIndex) 94 | } 95 | 96 | } 97 | 98 | componentDidUpdate(prevProps) { 99 | if (prevProps.isFocused !== this.props.isFocused) { 100 | this.init(); 101 | } 102 | 103 | 104 | } 105 | 106 | componentWillUnmount() { 107 | this._isMounted = false; 108 | 109 | //if(this.cancelToken){ this.cancelToken.cancel('ComponenetWillUnmount');} 110 | } 111 | 112 | render() { 113 | 114 | const {fetchMore} = this; 115 | 116 | const {navigation, appIndex, posts} = this.props; 117 | 118 | //console.log('Home-config',gState); 119 | 120 | var args = {fetchMore}; 121 | 122 | args.posts = posts && Array.isArray(posts.data) ? this.preparePosts(posts.data,appIndex) : []; 123 | 124 | if(navigation){ 125 | args.navigation = navigation; 126 | } 127 | 128 | 129 | return ( 130 | 131 | ) 132 | } 133 | } 134 | 135 | 136 | const mapStateToProps = state => { 137 | 138 | const appIndex = state.globalState.currentApp || 0; 139 | 140 | return ({ 141 | url: state.globalState.url, 142 | posts:state.api[`posts-${appIndex}`], 143 | categories:state.api[`categories-${appIndex}`], 144 | gState:state.globalState, 145 | appIndex 146 | 147 | }); 148 | }; 149 | 150 | // 151 | export default compose( 152 | connect(mapStateToProps, {set, getApi, cancelToken}), 153 | HomeContainer 154 | ); -------------------------------------------------------------------------------- /containers/PostsContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | 8 | import { WordPressClass } from '../builder/containers/WordPressPostsContainer'; 9 | 10 | 11 | const PostsContainer = (Comp, rest={}) => class extends WordPressClass { 12 | 13 | apiId = !!rest.id ? rest.id : 'posts'; 14 | offset=0; 15 | per_page=10; 16 | //categories=0; 17 | //author=0; 18 | search=''; 19 | orderby='date'; 20 | order='desc'; 21 | status='publish'; 22 | //hide_empty=true 23 | 24 | constructor(props){ 25 | 26 | super(props); 27 | 28 | this.state = { 29 | 30 | } 31 | 32 | //desc, asc |author, date, id, include, modified, parent, relevance, slug, title 33 | this.fetchMore = this.fetchMore.bind(this); 34 | 35 | 36 | } 37 | 38 | 39 | fetchMore(){ 40 | let obj ={}; 41 | const {categories} = this.props; 42 | 43 | if(categories){ 44 | obj.categories = categories; 45 | } 46 | 47 | this.fetchPosts(obj); 48 | } 49 | 50 | 51 | componentWillMount(){ 52 | this._isMounted = false; 53 | //set cancelToken 54 | //this.cancelToken = this.props.cancelToken(); 55 | } 56 | 57 | componentWillUnmount() { 58 | this._isMounted = false; 59 | 60 | //if(this.cancelToken){ this.cancelToken.cancel('ComponenetWillUnmount');} 61 | } 62 | 63 | shouldComponentUpdate(nextProps) { 64 | const oldCat = this.props.categories; 65 | const newCat = nextProps.categories; 66 | 67 | 68 | if(oldCat !== newCat){ 69 | this.init(); 70 | console.log("Posts Cat Changed", oldCat, newCat ); 71 | } 72 | return true; 73 | } 74 | 75 | init(){ 76 | const {categories, posts, navigation} = this.props; 77 | 78 | if(navigation){ 79 | //close Drawer 80 | navigation.closeDrawer(); 81 | } 82 | 83 | if(categories){ 84 | if(posts){ 85 | const check = posts.data.filter(post=>{ return post.categories.includes(categories); }); 86 | 87 | if(check.length>=5){ 88 | console.log(`post category:${categories} has ${check.length} data`); 89 | }else{ 90 | this.fetchMore(); 91 | console.log(`post category:${categories} has ${check.length} data`); 92 | } 93 | 94 | }else{ 95 | this.fetchMore(); 96 | } 97 | } 98 | else if(posts && posts.data){}else{this.fetchMore()} 99 | 100 | } 101 | 102 | componentDidMount() { 103 | this._isMounted = true; 104 | 105 | this.init(); 106 | } 107 | 108 | 109 | render() { 110 | 111 | const {fetchMore} = this; 112 | 113 | const {navigation, posts, categories, appIndex} = this.props; 114 | 115 | const args = {fetchMore}; 116 | 117 | //this.init(); 118 | 119 | if(posts){ 120 | //args.posts = posts; 121 | const {data=[]} = posts; 122 | 123 | args.isFetching = posts.isFetching; 124 | 125 | var collection = []; 126 | 127 | if(categories){ 128 | args.categories = categories; 129 | collection = data.filter(post=>{ return post.categories.includes(categories); }); 130 | 131 | }else{ 132 | collection = data; 133 | } 134 | 135 | //prepare post for render 136 | args.posts = Array.isArray(data) ? this.preparePosts(collection, appIndex) : []; 137 | 138 | } 139 | 140 | 141 | 142 | 143 | 144 | if(navigation){ 145 | args.navigation = navigation; 146 | } 147 | 148 | return ( 149 | 150 | ) 151 | } 152 | 153 | } 154 | 155 | 156 | 157 | const mapStateToProps = state => { 158 | 159 | const appIndex = state.globalState.currentApp || 0; 160 | 161 | return ({ 162 | url: state.globalState.url, 163 | posts:state.api[`posts-${appIndex}`], 164 | // categories:state.api[`categories-${appIndex}`], 165 | // gState:state.globalState, 166 | appIndex 167 | 168 | }); 169 | }; 170 | 171 | export default compose( 172 | connect(mapStateToProps, {getApi, cancelToken}), 173 | PostsContainer 174 | ); -------------------------------------------------------------------------------- /layouts/ResponsiveBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Dimensions } from 'react-native'; 3 | 4 | 5 | export const deviceIs = () => { 6 | var {width, height} = Dimensions.get('window'); 7 | var device; 8 | 9 | if(width <= 520){ 10 | device = "small"; 11 | } 12 | else if(width <= 1024){ 13 | device = "medium"; 14 | } 15 | else if(width > 1024){ 16 | device = "large"; 17 | } 18 | 19 | return device; 20 | 21 | } 22 | 23 | export default class ResBox extends React.Component { 24 | 25 | constructor(props){ 26 | super(props); 27 | 28 | this.state = { 29 | style:{}, 30 | width:360, 31 | height:640 32 | } 33 | } 34 | 35 | style = {}; 36 | 37 | hideBox(){ 38 | var hidden = this.props.hidden ? this.props.hidden : ''; 39 | var device = deviceIs(); 40 | 41 | return hidden.indexOf(device) > -1; 42 | } 43 | 44 | static getScreen(){ 45 | const { width, height } = Dimensions.get('window'); 46 | 47 | return { width, height } 48 | } 49 | 50 | getDimensions(){ 51 | const { width, height } = ResBox.getScreen();//Dimensions.get('window'); 52 | this._setState({width, height}); 53 | } 54 | 55 | resetBox(){ 56 | 57 | //var def = this.state.style; 58 | this.getDimensions(); 59 | 60 | //var { style, small, medium, large } = this.props; 61 | 62 | var style = this.props.style ? this.props.style : {}; 63 | var small = this.props.small ? this.props.small : {}; 64 | var medium = this.props.medium ? this.props.medium : {}; 65 | var large = this.props.large ? this.props.large : {}; 66 | 67 | var device = deviceIs(); 68 | 69 | if(small && device === 'small'){ 70 | //this._setState({style:{...style, ...small}}); 71 | this.style ={...style, ...small}; 72 | } 73 | else if(medium && device === 'medium'){ 74 | //this._setState({style:{...style, ...medium}}); 75 | this.style = {...style, ...medium}; 76 | } 77 | else if(large && device === 'large'){ 78 | //this._setState({style:{...style, ...large}}); 79 | this.style = {...style, ...large}; 80 | }else{ 81 | //this._setState({style}); 82 | this.style = {...style}; 83 | } 84 | 85 | //console.log({device, def}); 86 | } 87 | 88 | _setState(obj){ 89 | if(this._isMounted){ 90 | this.setState({...obj}); 91 | } 92 | } 93 | 94 | 95 | componentWillMount() { 96 | this._isMounted = false; 97 | 98 | this.resetBox(); 99 | 100 | Dimensions.addEventListener('change', ()=>this.resetBox()); 101 | 102 | } 103 | 104 | componentWillUnmount() { 105 | this._isMounted = false; 106 | Dimensions.removeEventListener('change'); 107 | } 108 | 109 | componentDidMount() { 110 | this._isMounted = true; 111 | 112 | this.resetBox(); 113 | } 114 | 115 | render(){ 116 | 117 | //const Comp = this.props.comp ? this.props.comp : View; 118 | 119 | const {children, style:s, small, medium, large, as:Comp=View, ...args} = this.props; 120 | 121 | var { //style, 122 | width, height} = this.state; 123 | 124 | var style = {...this.style}; 125 | 126 | //var {maxWidth, maxHeight} = this.props; 127 | 128 | //console.log('before', style) 129 | 130 | 131 | if(style.pWidth){ 132 | style.width = width * (style.pWidth/100); 133 | delete style.pWidth; 134 | } 135 | 136 | if(style.pHeight){ 137 | style.height = height * (style.pHeight/100); 138 | delete style.pHeight; 139 | } 140 | 141 | if(style.pMargin){ 142 | style.margin = width * (style.pMargin/100); 143 | delete style.pMargin; 144 | } 145 | 146 | if(style.pMarginRight){ 147 | style.marginRight = width * (style.pMarginRight/100); 148 | delete style.pMarginRight; 149 | } 150 | 151 | if(style.pMarginLeft){ 152 | style.marginLeft = width * (style.pMarginLeft/100); 153 | delete style.pMarginLeft; 154 | } 155 | 156 | if(style.pPadding){ 157 | style.padding = width * (style.pPadding/100); 158 | delete style.pPadding; 159 | } 160 | 161 | 162 | if(this.hideBox()){ 163 | return 164 | }else if(children){ 165 | return ( 166 | 167 | {children} 168 | ) 169 | }else{ 170 | //console.log('args', args) 171 | return 172 | } 173 | 174 | 175 | } 176 | 177 | 178 | } -------------------------------------------------------------------------------- /redux/api/action.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const FETCH_API_REQUEST = 'FETCH_API_REQUEST'; 4 | export const FETCH_API_SUCCESS = 'FETCH_API_SUCCESS'; 5 | export const FETCH_API_FAILURE = 'FETCH_API_FAILURE'; 6 | export const FETCH_API_DELETE = 'FETCH_API_DELETE'; 7 | export const FETCH_API_CLEAR = 'FETCH_API_CLEAR'; 8 | export const FETCH_API_ADD = 'FETCH_API_ADD'; 9 | export const FETCH_API_PREPEND = 'FETCH_API_PREPEND'; 10 | export const FETCH_API_EDIT = 'FETCH_API_EDIT'; 11 | 12 | 13 | export const fetchApiEdit = (id,json) => ( 14 | { 15 | "id":id, 16 | type: FETCH_API_EDIT, 17 | payload: json 18 | }); 19 | 20 | export const fetchApiAdd = (id,json) => ( 21 | { 22 | "id":id, 23 | type: FETCH_API_ADD, 24 | payload: json 25 | }); 26 | 27 | export const fetchApiPrepend = (id,json) => ( 28 | { 29 | "id":id, 30 | type: FETCH_API_PREPEND, 31 | payload: json 32 | }); 33 | 34 | 35 | export const fetchApiClear = (id) => ({ 36 | "id":id, 37 | type: FETCH_API_CLEAR}); 38 | 39 | 40 | export const fetchApiRequest = (id) => ({ 41 | id, 42 | type: FETCH_API_REQUEST}); 43 | 44 | export const fetchApiSuccess = (id,json) => ( 45 | { 46 | "id":id, 47 | type: FETCH_API_SUCCESS, 48 | payload: json 49 | }); 50 | 51 | export const fetchApiFailure = (id,error) => ( 52 | { 53 | "id":id, 54 | type: FETCH_API_FAILURE, 55 | payload: error 56 | }); 57 | 58 | 59 | export const fetchApiDelete = (id,payload) => ( 60 | { 61 | id, 62 | type: FETCH_API_DELETE, 63 | payload 64 | }); 65 | 66 | const argsSerialize = (args)=> { 67 | var params = ""; 68 | var i = 0; 69 | 70 | for(let key in args) { 71 | 72 | //add & to the string 73 | if(i>0){ 74 | params+='&'; 75 | } 76 | 77 | //check if key has a value 78 | if(args[key]===""){ 79 | params+=key; 80 | }else{ 81 | params+=key+'='+args[key]; 82 | } 83 | 84 | i++; 85 | } 86 | 87 | return params; 88 | 89 | } 90 | 91 | export const clearApi = (id) => dispatch => dispatch(fetchApiClear(id)); 92 | 93 | export const fetchApi = (url='',args={}, id='one') => { 94 | 95 | var params = argsSerialize(args); 96 | 97 | //async 98 | return dispatch => { 99 | var link = `${url}?${params}`; 100 | console.log(link); 101 | 102 | dispatch(fetchApiRequest(id)); 103 | 104 | return fetch(link) 105 | .then(res => res.json() 106 | // , error => { 107 | // console.log('An error occurred.', error) 108 | // dispatch(fetchApiFailure(id, error.message)) 109 | // } 110 | ).then(res => { 111 | //console.log(res); 112 | dispatch(fetchApiSuccess(id,res)); 113 | return res; 114 | }) 115 | .catch(error => { 116 | //console.log(error); 117 | dispatch(fetchApiFailure(id, error.message)); 118 | return error; 119 | }); 120 | } 121 | } 122 | 123 | export const postApi = (url='', obj={}, id="postApi") => { 124 | 125 | let headers = !!obj.headers ? obj.headers : {}; 126 | let data = !!obj.data ? obj.data : {}; 127 | 128 | return dispatch => { 129 | 130 | dispatch(fetchApiRequest(id)); 131 | 132 | return axios.post(`${URL}${url}`, {...data}, {headers}) 133 | .then(res=>{ 134 | 135 | dispatch(fetchApiSuccess(id,res.data)); 136 | 137 | return res; 138 | }).catch(error => { 139 | dispatch(fetchApiFailure(id, error.message)); 140 | return error; 141 | }); 142 | 143 | } 144 | } 145 | 146 | export const addApi = (id, json) => dispatch => dispatch(fetchApiAdd(id, json)); 147 | 148 | export const prependApi = (id, json) => dispatch => dispatch(fetchApiPrepend(id, json)); 149 | 150 | export const editApi = (id, json) => dispatch => dispatch(fetchApiEdit(id, json)); 151 | 152 | export const deleteApi = (id, json) => dispatch => dispatch(fetchApiDelete(id, json)); 153 | 154 | 155 | export const getApi = (url='', obj={}, id=null, cancel) => { 156 | 157 | //let headers = !!obj.headers ? obj.headers : {}; 158 | //url = "https://andela.com/wp-json"; 159 | //obj = {}; 160 | // Add User-agent 161 | const headers = obj.headers || {}; 162 | headers['User-Agent'] = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36`; 163 | headers['Sec-Fetch-Mode'] = 'cors'; 164 | 165 | headers['accept'] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"; 166 | headers['accept-encoding'] = 'gzip, deflate, br'; 167 | headers['accept-language'] = 'en-US,en;q=0.9'; 168 | headers['cookie'] = ""; 169 | 170 | let data = obj.data || obj; 171 | 172 | let params = argsSerialize(data); 173 | 174 | return dispatch => { 175 | 176 | id===null || dispatch(fetchApiRequest(id)); 177 | 178 | //check for cancelToken 179 | let args = cancel ? {cancelToken: cancel.token} : {}; 180 | 181 | //if(obj.headers){} 182 | args.headers = headers; 183 | 184 | 185 | //console.log(`${url}?${params}`,args) 186 | 187 | 188 | return axios.get(`${url}?${params}`, args) 189 | .then(res=>{ 190 | 191 | id===null || dispatch(fetchApiSuccess(id,res.data)); 192 | return res; 193 | }).catch(error => { 194 | id===null || dispatch(fetchApiFailure(id, error.message)); 195 | return error; 196 | }); 197 | 198 | } 199 | } 200 | 201 | export const cancelToken = () => { 202 | return dispatch =>{ 203 | //create axios cancelToken 204 | var CancelToken = axios.CancelToken; 205 | var source = CancelToken.source(); 206 | return source; 207 | } 208 | 209 | } -------------------------------------------------------------------------------- /containers/WordPressApi.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import PropTypes from 'prop-types'; 3 | //import {compose} from 'redux'; 4 | import { connect } from 'react-redux'; 5 | 6 | import {getApi, cancelToken} from '../redux/api/action'; 7 | import set from '../redux/global-state'; 8 | 9 | 10 | class WordpressApi extends Component { 11 | 12 | apiId = 'home'; 13 | offset=0; 14 | per_page=1; 15 | //categories=0; 16 | //author=0; 17 | search=''; 18 | orderby='date'; 19 | order='desc'; 20 | status='publish'; 21 | //hide_empty=true 22 | 23 | 24 | constructor(props){ 25 | this.props; 26 | } 27 | 28 | //here we will get id of all the posts that have been previously fetched in order to exclude the from future fetches 29 | getAvailablePostsId(){ 30 | const {posts} = this.props; 31 | 32 | return new Promise((resolve)=>{ 33 | if(posts && posts.data && Array.isArray(posts.data)){ 34 | var ids = posts.data.map(post=>post.id); 35 | resolve(ids) 36 | }else{ 37 | resolve([]) 38 | } 39 | 40 | 41 | }); 42 | 43 | 44 | } 45 | 46 | fetchMore(){ 47 | let {per_page, orderby, order, apiId} = this; 48 | let {getApi, url} = this.props; 49 | var offset = this.props.api && this.props.api[apiId] && this.props.api[apiId].offset ? this.props.api[apiId].offset : 0; 50 | //console.log('static', this); 51 | getApi(`${url}/wp-json/wp/v2/posts`,{per_page, orderby, offset, order, _embed:''}, apiId) 52 | .then(res=>console.log(apiId, 'done fetching >', offset)); 53 | } 54 | 55 | fetchCategories(obj={}){ 56 | var {per_page=50, orderby='count', order='desc', hide_empty=true} = obj; 57 | let {getApi, url} = this.props; 58 | //console.log('static', this); 59 | return getApi(`${url}/wp-json/wp/v2/categories`,{per_page, orderby, order, hide_empty},'categories').then(res=>res.data); 60 | } 61 | 62 | fetchPosts(obj={}){ 63 | let {per_page, orderby, order} = this; 64 | let {getApi, url} = this.props; 65 | 66 | //get already fetch posts 67 | return this.getAvailablePostsId().then(ids=>{ 68 | //console.log(`getAvailablePostsId (${id.length})`, id.join()) 69 | if(ids.length>0){ 70 | obj.exclude = ids.join(); 71 | } 72 | 73 | return getApi(`${url}/wp-json/wp/v2/posts`,{per_page, orderby, order, ...obj, _embed:''}, 'posts'); 74 | }); 75 | } 76 | 77 | getCategories(){ 78 | const {categories} = this.props; 79 | 80 | //use from store if available 81 | if(categories){ 82 | return categories.data; 83 | } 84 | else //fetch from rest api 85 | { 86 | return this.fetchCategories().then(res=>{ 87 | console.log('done fetching categories'); 88 | if(Array.isArray(res)){ 89 | //return only root categories 90 | return res.filter(cat=>cat.parent===0) 91 | }else{ 92 | return []; 93 | } 94 | }); 95 | 96 | 97 | } 98 | } 99 | 100 | 101 | oneByOne(objects_array, iterator, callback) { 102 | var start_promise = objects_array.reduce(function (prom, object) { 103 | return prom.then(function () { 104 | return iterator(object); 105 | }); 106 | }, Promise.resolve()); // initial 107 | if(callback){ 108 | start_promise.then(callback); 109 | }else{ 110 | return start_promise; 111 | } 112 | } 113 | 114 | fetchPostsByCategory(ids=[]){ 115 | 116 | if(ids.length>0){ 117 | 118 | ids.forEach(category =>{ 119 | this.fetchPosts({category}).then(res=>console.log('done fetching: ',typeof res, category)); 120 | }); 121 | 122 | 123 | }else{ 124 | 125 | var index = 0; 126 | 127 | this.getCategories().then(data=>{ 128 | console.log('Home - ids', data.length); 129 | 130 | 131 | const fetchPostsIter = (categoryObj)=>{ 132 | 133 | return new Promise((resolve)=>{ 134 | let categories = categoryObj.id; 135 | 136 | return this.fetchPosts({categories}) 137 | .then(res=>{ 138 | console.log(`Home ${index} done fetching: `,categoryObj.id); 139 | if(res.data){ 140 | console.log( res.data.length); 141 | } 142 | index++; 143 | 144 | resolve(res); 145 | }); 146 | }); 147 | 148 | } 149 | 150 | //fetch latests stories if available 151 | this.fetchPosts().then(res=>{ 152 | 153 | let CategoryList = data.slice(0,5); 154 | 155 | this.oneByOne(CategoryList, fetchPostsIter, (res)=>console.log('done')); 156 | 157 | 158 | }); 159 | 160 | 161 | 162 | 163 | 164 | }); 165 | 166 | 167 | 168 | //.then(ids=>{ 169 | // console.log('Home - ids', ids); 170 | //}) 171 | 172 | 173 | 174 | } 175 | 176 | } 177 | 178 | generateHome(){ 179 | const {gState} = this.props; 180 | 181 | //check if current_site exists 182 | if(gState.current_site){ 183 | const index = gState.current_site; 184 | const config = gState.sites[index]; 185 | 186 | if(config && config.home && config.home.categories && Array.isArray(config.home.categories)){ 187 | const ids = config.home.categories.map(cat=>cat.id); 188 | 189 | this.fetchPostsByCategory(ids); 190 | }else{ 191 | this.fetchPostsByCategory(); 192 | console.log("Home - no config found") 193 | } 194 | }else{ 195 | this.fetchPostsByCategory(); 196 | console.log("Home - no config found") 197 | } 198 | } 199 | 200 | 201 | 202 | } 203 | 204 | 205 | const mapStateToProps = state => ( 206 | { 207 | url: state.globalState.url, 208 | posts:state.api.posts, 209 | categories:state.api.categories, 210 | gState:state.globalState 211 | 212 | 213 | }); 214 | 215 | 216 | 217 | export default (WordpressApi); 218 | //connect(mapStateToProps, {set, getApi, cancelToken}) 219 | --------------------------------------------------------------------------------