diff --git a/examples/ReactExample/android/app/build.gradle b/examples/ReactExample/android/app/build.gradle index 9ecce5be..a9b4e033 100644 --- a/examples/ReactExample/android/app/build.gradle +++ b/examples/ReactExample/android/app/build.gradle @@ -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 diff --git a/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java b/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java index fd45f4fc..b220a0d8 100644 --- a/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java +++ b/examples/ReactExample/android/app/src/main/java/io/realm/react/example/MainApplication.java @@ -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 getPackages() { return Arrays.asList( new MainReactPackage(), + new RNExitAppPackage(), new RealmReactPackage() ); } diff --git a/examples/ReactExample/android/settings.gradle b/examples/ReactExample/android/settings.gradle index 96ce9242..28f31ada 100644 --- a/examples/ReactExample/android/settings.gradle +++ b/examples/ReactExample/android/settings.gradle @@ -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' diff --git a/examples/ReactExample/components/items-screen.js b/examples/ReactExample/components/items-screen.js new file mode 100644 index 00000000..02d2f095 --- /dev/null +++ b/examples/ReactExample/components/items-screen.js @@ -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 ; + } + + // renderScene(route) { + // console.log(this.todoLists); + // return + // } + + + + // _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 }) + // } +} \ No newline at end of file diff --git a/examples/ReactExample/components/params.json b/examples/ReactExample/components/params.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/examples/ReactExample/components/params.json @@ -0,0 +1 @@ +{} diff --git a/examples/ReactExample/components/styles.js b/examples/ReactExample/components/styles.js index 67c5d11b..32f8f00b 100644 --- a/examples/ReactExample/components/styles.js +++ b/examples/ReactExample/components/styles.js @@ -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', diff --git a/examples/ReactExample/components/todo-app.js b/examples/ReactExample/components/todo-app.js index e51b62e9..cd8b4bd6 100644 --- a/examples/ReactExample/components/todo-app.js +++ b/examples/ReactExample/components/todo-app.js @@ -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 = ( - - ); - - return ( - - ); + return ; } 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 ( - navigator.pop()}> - - - - {prevRoute.backButtonTitle || prevRoute.title || 'Back'} - - - - ); - }, - - RightButton(route) { - if (!route.rightButtonTitle) { - return null; - } - - return ( - - - - {route.rightButtonTitle} - - - - ); - }, - - Title(route) { - return ( - - - {route.title} - - - ); - }, -}; +export default SimpleApp; \ No newline at end of file diff --git a/examples/ReactExample/components/todo-itemsview.js b/examples/ReactExample/components/todo-itemsview.js new file mode 100644 index 00000000..404c1e0c --- /dev/null +++ b/examples/ReactExample/components/todo-itemsview.js @@ -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 ( + + + + Press Cmd+R to reload,{'\n'} + Cmd+D for dev menu + + + ); + } + + 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 ( + 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; + } +} diff --git a/examples/ReactExample/components/todo-list-item.js b/examples/ReactExample/components/todo-list-item.js index b82b823f..06137ac8 100644 --- a/examples/ReactExample/components/todo-list-item.js +++ b/examples/ReactExample/components/todo-list-item.js @@ -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() { diff --git a/examples/ReactExample/components/todo-listview.js b/examples/ReactExample/components/todo-listview.js index 4f94612a..f3904cc6 100644 --- a/examples/ReactExample/components/todo-listview.js +++ b/examples/ReactExample/components/todo-listview.js @@ -73,7 +73,7 @@ export default class TodoListView extends React.Component { render() { return ( - + Press Cmd+R to reload,{'\n'} Cmd+D for dev menu diff --git a/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj b/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj index ffc6720b..193e603d 100644 --- a/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj +++ b/examples/ReactExample/ios/ReactExample.xcodeproj/project.pbxproj @@ -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 = ""; }; 8573DE501E23DDA700914396 /* ReactExampleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactExampleTests.m; sourceTree = ""; }; 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 = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + F25C98ECB41447FB9BA11E19 /* libRNExitApp.a */ = {isa = PBXFileReference; name = "libRNExitApp.a"; path = "libRNExitApp.a"; sourceTree = ""; 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 = ""; @@ -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"; diff --git a/examples/ReactExample/package.json b/examples/ReactExample/package.json index 8fa0b93b..0c61f8ce 100644 --- a/examples/ReactExample/package.json +++ b/examples/ReactExample/package.json @@ -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": { diff --git a/scripts/test.sh b/scripts/test.sh index 96c29ba0..df92e5fc 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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