fix RealmExample app to use the new RN navigation

This commit is contained in:
blagoev 2017-12-13 18:36:10 +02:00
parent 9f41664309
commit 72cb0d845e
13 changed files with 340 additions and 107 deletions

View File

@ -126,6 +126,7 @@ android {
}
dependencies {
compile project(':react-native-exit-app-no-history')
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules

View File

@ -4,6 +4,7 @@ import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.github.wumke.RNExitApp.RNExitAppPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
@ -27,6 +28,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNExitAppPackage(),
new RealmReactPackage()
);
}

View File

@ -1,4 +1,6 @@
rootProject.name = 'ReactExample'
include ':react-native-exit-app-no-history'
project(':react-native-exit-app-no-history').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exit-app-no-history/android')
include ':app'

View File

@ -0,0 +1,104 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
'use strict';
import React from 'react';
import {
Platform,
StatusBar,
Text,
TouchableOpacity,
View,
} from 'react-native';
import TodoItem from './todo-item';
import TodoListView from './todo-listview';
import TodoItemsView from './todo-itemsview';
import TodoListItem from './todo-list-item';
import realm from './realm';
import styles from './styles';
import { StackNavigator } from 'react-navigation';
export default class ItemsScreen extends React.Component {
static navigationOptions = {
title: 'Current list',
};
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {
if (Platform.OS == 'ios') {
StatusBar.setBarStyle('light-content');
}
}
render() {
// let objects = realm.objects('Todo');
// let extraItems = [
// { name: 'Complete', items: objects.filtered('done = true') },
// { name: 'Incomplete', items: objects.filtered('done = false') },
// ];
let properties = {
// ref: 'listView',
// extraItems: extraItems,
// onPressItem: this._onPressTodoList,
}
return <TodoItemsView items={this.props.navigation.state.params.items} {...properties} />;
}
// renderScene(route) {
// console.log(this.todoLists);
// return <route.component items={this.todoLists} {...route.passProps} />
// }
// _onPressTodoItem(list) {
// const { navigate } = this.props.navigation;
// let items = list.items;
// let route = {
// title: list.name,
// component: TodoListView,
// passProps: {
// ref: 'listItemView',
// items: items,
// rowClass: TodoItem,
// },
// };
// // Check if the items are mutable (i.e. List rather than Results).
// if (items.push) {
// Object.assign(route, {
// rightButtonTitle: 'Add',
// onRightButtonPress: () => this._addNewTodoItem(list),
// });
// }
// // this.refs.nav.push(route);
// navigate('TodoListItem', { items: items })
// }
}

View File

@ -0,0 +1 @@
{}

View File

@ -22,9 +22,9 @@ import {
Platform,
StyleSheet
} from 'react-native';
import NavigationExperimental from 'react-native-deprecated-custom-components';
// import NavigationExperimental from 'react-native-deprecated-custom-components';
const { NavBarHeight, TotalNavHeight } = NavigationExperimental.Navigator.NavigationBar.Styles.General;
// const { NavBarHeight, TotalNavHeight } = NavigationExperimental.Navigator.NavigationBar.Styles.General;
const iOS = (Platform.OS == 'ios');
@ -44,7 +44,7 @@ export default StyleSheet.create({
navBarView: {
alignItems: 'center',
flexDirection: 'row',
height: NavBarHeight,
height: 20,
},
navBarLeftArrow: {
color: '#fff',
@ -67,7 +67,7 @@ export default StyleSheet.create({
fontWeight: '500',
},
navScene: {
top: TotalNavHeight,
top: 20,
},
listItem: {
borderColor: '#c8c7cc',

View File

@ -30,13 +30,22 @@ import {
import TodoItem from './todo-item';
import TodoListView from './todo-listview';
import TodoListItem from './todo-list-item';
import ItemsScreen from './items-screen'
import realm from './realm';
import styles from './styles';
import NavigationExperimental from 'react-native-deprecated-custom-components';
// import NavigationExperimental from 'react-native-deprecated-custom-components';
import { StackNavigator } from 'react-navigation';
import RNExitApp from 'react-native-exit-app-no-history';
const params = require("./params.json");
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Todo Lists',
};
export default class TodoApp extends React.Component {
constructor(props) {
super(props);
@ -44,17 +53,21 @@ export default class TodoApp extends React.Component {
this.todoLists = realm.objects('TodoList').sorted('creationDate');
if (this.todoLists.length < 1) {
realm.write(() => {
realm.create('TodoList', {name: 'Todo List', creationDate: new Date()});
realm.create('TodoList', { name: 'Todo List', creationDate: new Date() });
});
}
this.todoLists.addListener((name, changes) => {
console.log("changed: " + JSON.stringify(changes));
if (params) {
console.error("params.json indicate a test run. Exiting application");
RNExitApp.exitApp();
}
});
console.log("registered listener");
// Bind all the methods that we will be passing as props.
this.renderScene = this.renderScene.bind(this);
// this.renderScene = this.renderScene.bind(this);
this._addNewTodoList = this._addNewTodoList.bind(this);
this._onPressTodoList = this._onPressTodoList.bind(this);
@ -75,37 +88,17 @@ export default class TodoApp extends React.Component {
render() {
let objects = realm.objects('Todo');
let extraItems = [
{name: 'Complete', items: objects.filtered('done = true')},
{name: 'Incomplete', items: objects.filtered('done = false')},
{ name: 'Complete', items: objects.filtered('done = true') },
{ name: 'Incomplete', items: objects.filtered('done = false') },
];
let route = {
title: 'My Todo Lists',
component: TodoListView,
passProps: {
ref: 'listView',
extraItems: extraItems,
onPressItem: this._onPressTodoList,
},
backButtonTitle: 'Lists',
rightButtonTitle: 'Add',
onRightButtonPress: this._addNewTodoList,
};
let properties = {
ref: 'listView',
extraItems: extraItems,
onPressItem: this._onPressTodoList,
}
let navigationBar = (
<NavigationExperimental.Navigator.NavigationBar routeMapper={RouteMapper} style={styles.navBar} />
);
return (
<NavigationExperimental.Navigator
ref="nav"
initialRoute={route}
navigationBar={navigationBar}
renderScene={this.renderScene}
sceneStyle={styles.navScene}
style={styles.navigator}
/>
);
return <TodoListView items={this.todoLists} {...properties} />;
}
renderScene(route) {
@ -120,7 +113,7 @@ export default class TodoApp extends React.Component {
}
realm.write(() => {
items.push({text: ''});
items.push({ text: '' });
});
this._setEditingRow(items.length - 1);
@ -133,34 +126,17 @@ export default class TodoApp extends React.Component {
}
realm.write(() => {
realm.create('TodoList', {name: '', creationDate: new Date()});
realm.create('TodoList', { name: '', creationDate: new Date() });
});
this._setEditingRow(items.length - 1);
}
_onPressTodoList(list) {
const { navigate } = this.props.navigation;
let items = list.items;
let route = {
title: list.name,
component: TodoListView,
passProps: {
ref: 'listItemView',
items: items,
rowClass: TodoItem,
},
};
// Check if the items are mutable (i.e. List rather than Results).
if (items.push) {
Object.assign(route, {
rightButtonTitle: 'Add',
onRightButtonPress: () => this._addNewTodoItem(list),
});
}
this.refs.nav.push(route);
navigate('ItemsScreen', { items: items })
}
_shouldAddNewItem(items) {
@ -175,53 +151,14 @@ export default class TodoApp extends React.Component {
let listView = this.currentListView;
// Update the state on the currently displayed TodoList to edit this new item.
listView.setState({editingRow: rowIndex});
listView.setState({ editingRow: rowIndex });
listView.updateDataSource();
}
}
const RouteMapper = {
LeftButton(route, navigator, index, navState) {
if (index == 0) {
return null;
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
ItemsScreen: { screen: ItemsScreen }
});
let prevRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity onPress={() => navigator.pop()}>
<View style={[styles.navBarView, styles.navBarLeftButton]}>
<Text style={styles.navBarLeftArrow}></Text>
<Text style={styles.navBarText}>
{prevRoute.backButtonTitle || prevRoute.title || 'Back'}
</Text>
</View>
</TouchableOpacity>
);
},
RightButton(route) {
if (!route.rightButtonTitle) {
return null;
}
return (
<TouchableOpacity onPress={route.onRightButtonPress}>
<View style={[styles.navBarView, styles.navBarRightButton]}>
<Text style={styles.navBarText}>
{route.rightButtonTitle}
</Text>
</View>
</TouchableOpacity>
);
},
Title(route) {
return (
<View style={styles.navBarView}>
<Text style={[styles.navBarText, styles.navBarTitleText]}>
{route.title}
</Text>
</View>
);
},
};
export default SimpleApp;

View File

@ -0,0 +1,172 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
'use strict';
import React from 'react';
import {
Text,
View,
} from 'react-native';
import { ListView } from 'realm/react-native';
import TodoItem from './todo-item';
import realm from './realm';
import styles from './styles';
export default class TodoItemsView extends React.Component {
constructor(props) {
super(props);
let dataSource = new ListView.DataSource({
rowHasChanged(a, b) {
// Always re-render TodoList items.
return a.done !== b.done || a.text !== b.text || a.items || b.items;
}
});
this.state = {
dataSource: this._cloneDataSource(dataSource, props),
};
this.renderRow = this.renderRow.bind(this);
}
componentWillReceiveProps(props) {
this.updateDataSource(props);
}
componentDidUpdate() {
let items = this.props.items;
let editingRow = this.state.editingRow;
for (let i = items.length; i--;) {
if (i == editingRow) {
continue;
}
if (this._deleteItemIfEmpty(items[i]) && i < editingRow) {
editingRow--;
}
}
if (editingRow != this.state.editingRow) {
this.setState({editingRow});
}
}
render() {
return (
<View style={styles.container}>
<ListView dataSource={this.state.dataSource} renderRow={this.renderRow} enableEmptySections/>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D for dev menu
</Text>
</View>
);
}
renderRow(item, sectionIndex, rowIndex) {
let RowClass;
let editing = false;
// if (sectionIndex == 0) {
// RowClass = this.props.rowClass || TodoListItem;
// editing = this.state.editingRow == rowIndex;
// } else if (sectionIndex == 1) {
// RowClass = TodoListExtraItem;
// }
return (
<TodoItem
item={item}
editing={editing}
onPress={() => this._onPressRow(item, sectionIndex, rowIndex)}
onPressDelete={() => this._onPressDeleteRow(item)}
onEndEditing={() => this._onEndEditingRow(item, rowIndex)} />
);
}
updateDataSource(props=this.props) {
this.setState({
dataSource: this._cloneDataSource(this.state.dataSource, props),
});
}
_cloneDataSource(dataSource, props) {
let items = props.items;
let extraItems = props.extraItems;
let sections = [items ? items.snapshot() : []];
if (extraItems && extraItems.length) {
sections.push(extraItems);
}
return dataSource.cloneWithRowsAndSections(sections);
}
_onPressRow(item, sectionIndex, rowIndex) {
let onPressItem = this.props.onPressItem;
if (onPressItem) {
onPressItem(item);
return;
}
// If no handler was provided, then default to editing the row.
if (sectionIndex == 0) {
this.setState({editingRow: rowIndex});
}
}
_onPressDeleteRow(item) {
this._deleteItem(item);
this.updateDataSource();
}
_onEndEditingRow(item, rowIndex) {
if (this._deleteItemIfEmpty(item)) {
this.updateDataSource();
}
if (this.state.editingRow == rowIndex) {
this.setState({editingRow: null});
}
}
_deleteItem(item) {
let items = item.items;
realm.write(() => {
// If the item is a TodoList, then delete all of its items.
if (items && items.length) {
realm.delete(items);
}
realm.delete(item);
});
}
_deleteItemIfEmpty(item) {
// The item could be a TodoList or a Todo.
if (!item.name && !item.text) {
this._deleteItem(item);
return true;
}
return false;
}
}

View File

@ -42,7 +42,12 @@ export default class TodoListItem extends React.Component {
get done() {
let items = this.props.item.items;
return items.length > 0 && items.every((item) => item.done);
if (items) {
return items.length > 0 && items.every((item) => item.done);
}
else {
return this.props.item.done;
}
}
get text() {

View File

@ -73,7 +73,7 @@ export default class TodoListView extends React.Component {
render() {
return (
<View style={styles.container}>
<ListView dataSource={this.state.dataSource} renderRow={this.renderRow} />
<ListView dataSource={this.state.dataSource} renderRow={this.renderRow} enableEmptySections/>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D for dev menu

View File

@ -5,7 +5,6 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
@ -28,6 +27,7 @@
8573DE511E23DDA700914396 /* ReactExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8573DE501E23DDA700914396 /* ReactExampleTests.m */; };
B06E5AD59A024665BD24C8C7 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C4594A7EF1647D68ADF0ED0 /* libz.tbd */; };
EF9CDEC26BA64438B1A9F856 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C202449017C94855B351AE73 /* libc++.tbd */; };
F5A48833F58A45B2A8E9F550 /* libRNExitApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F25C98ECB41447FB9BA11E19 /* libRNExitApp.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -253,6 +253,8 @@
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
8573DE501E23DDA700914396 /* ReactExampleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactExampleTests.m; sourceTree = "<group>"; };
C202449017C94855B351AE73 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
A07E5AD22EFC47D594326945 /* RNExitApp.xcodeproj */ = {isa = PBXFileReference; name = "RNExitApp.xcodeproj"; path = "../node_modules/react-native-exit-app-no-history/ios/RNExitApp.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
F25C98ECB41447FB9BA11E19 /* libRNExitApp.a */ = {isa = PBXFileReference; name = "libRNExitApp.a"; path = "libRNExitApp.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -282,6 +284,7 @@
146834051AC3E58100842450 /* libReact.a in Frameworks */,
70C063557D0D491D8F4D348F /* libRealmReact.a in Frameworks */,
B06E5AD59A024665BD24C8C7 /* libz.tbd in Frameworks */,
F5A48833F58A45B2A8E9F550 /* libRNExitApp.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -447,6 +450,7 @@
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
73AD103601A44EB291AC2117 /* RealmReact.xcodeproj */,
A07E5AD22EFC47D594326945 /* RNExitApp.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@ -891,6 +895,7 @@
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactExample.app/ReactExample";
@ -908,6 +913,7 @@
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactExample.app/ReactExample";

View File

@ -9,8 +9,8 @@
"dependencies": {
"react": "16.0.0",
"react-native": "0.50.4",
"react-native-deprecated-custom-components": "^0.1.1",
"react-navigation": "^1.0.0-beta.19",
"react-navigation": "^1.0.0-beta.21",
"react-native-exit-app-no-history": "^1.0.2",
"realm": "file:../.."
},
"devDependencies": {

View File

@ -255,8 +255,11 @@ case "$TARGET" in
open_chrome
start_packager
echo "{ \"test\" : true }" > $(pwd)/components/params.json
pushd ios
xctest ReactExample
popd
echo "{}" > $(pwd)/components/params.json
;;
"react-tests-android")
npm run check-environment