Merge pull request #102 from realm/sk-example-app

Improve ReactExample app
This commit is contained in:
Scott Kyle 2015-10-28 14:03:12 -07:00
commit 5d59040d3d
13 changed files with 681 additions and 288 deletions

View File

@ -0,0 +1,20 @@
{
"env": {
"commonjs": true,
"es6": true
},
"ecmaFeatures": {
"jsx": true
},
"plugins": [
"react"
],
"rules": {
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 2,
"react/jsx-uses-react": 2,
"react/no-direct-mutation-state": 1,
"react/prefer-es6-class": 1,
"react/react-in-jsx-scope": 2
}
}

View File

@ -133,6 +133,13 @@
remoteGlobalIDString = 02B29A151B7CF7C9008A7E6B; remoteGlobalIDString = 02B29A151B7CF7C9008A7E6B;
remoteInfo = RealmReact; remoteInfo = RealmReact;
}; };
F6EA29001BDEEC4B00ECDC7B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 83CBBA2D1A601D0E00E9B192;
remoteInfo = React;
};
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@ -342,9 +349,9 @@
027798471BBB2F1000C96559 /* ReactExampleTests */, 027798471BBB2F1000C96559 /* ReactExampleTests */,
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
); );
indentWidth = 2; indentWidth = 4;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2; tabWidth = 4;
}; };
83CBBA001A601CBA00E9B192 /* Products */ = { 83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
@ -389,6 +396,7 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
F6EA29011BDEEC4B00ECDC7B /* PBXTargetDependency */,
F636F6E31BCDB72D0023F35C /* PBXTargetDependency */, F636F6E31BCDB72D0023F35C /* PBXTargetDependency */,
); );
name = ReactExample; name = ReactExample;
@ -403,7 +411,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0700; LastUpgradeCheck = 0700;
ORGANIZATIONNAME = Facebook; ORGANIZATIONNAME = Realm;
TargetAttributes = { TargetAttributes = {
027798451BBB2F1000C96559 = { 027798451BBB2F1000C96559 = {
CreatedOnToolsVersion = 7.0.1; CreatedOnToolsVersion = 7.0.1;
@ -637,6 +645,11 @@
name = RealmReact; name = RealmReact;
targetProxy = F636F6E21BCDB72D0023F35C /* PBXContainerItemProxy */; targetProxy = F636F6E21BCDB72D0023F35C /* PBXContainerItemProxy */;
}; };
F6EA29011BDEEC4B00ECDC7B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = React;
targetProxy = F6EA29001BDEEC4B00ECDC7B /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */

View File

@ -1,10 +1,5 @@
/** /* Copyright 2015 Realm Inc - All Rights Reserved
* Copyright (c) 2015-present, Facebook, Inc. * Proprietary and Confidential
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@ -24,47 +19,46 @@
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{ {
if (test(view)) { if (test(view)) {
return YES; return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
} }
} for (UIView *subview in [view subviews]) {
return NO; if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
} }
- (void)testLaunched - (void)testLaunched
{ {
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO; BOOL foundElement = NO;
__block NSString *redboxError = nil; __block NSString *redboxError = nil;
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) { RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) { if (level >= RCTLogLevelError) {
redboxError = message; redboxError = message;
}
});
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
} }
});
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { RCTSetLogFunction(RCTDefaultLogFunction);
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { //XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
return YES;
}
return NO;
}];
}
RCTSetLogFunction(RCTDefaultLogFunction);
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
//XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
} }
@end @end

View File

@ -0,0 +1,26 @@
/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const Realm = require('realm');
module.exports = new Realm({
schema: [
{
name: 'Todo',
properties: [
{name: 'done', type: Realm.Types.BOOL, default: false},
{name: 'text', type: Realm.Types.STRING, default: ''},
]
},
{
name: 'TodoList',
properties: [
{name: 'name', type: Realm.Types.STRING},
{name: 'items', type: Realm.Types.LIST, objectType: 'Todo'},
]
},
],
});

View File

@ -0,0 +1,79 @@
/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const React = require('react-native');
module.exports = React.StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'stretch',
backgroundColor: '#ffffff',
},
navigator: {
flex: 1,
},
listItem: {
borderColor: "#c8c7cc",
borderBottomWidth: 0.5,
alignItems: 'stretch',
alignSelf: 'stretch',
flexDirection: 'row',
flex: 1,
height: 44,
},
listItemLeftSide: {
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
width: 36,
},
listItemCheckbox: {
borderColor: '#ccc',
borderWidth: 1,
textAlign: 'center',
width: 16,
height: 16,
lineHeight: 14,
},
listItemCount: {
borderColor: '#ccc',
borderWidth: 1,
borderRadius: 8,
textAlign: 'center',
fontSize: 12,
width: 24,
height: 18,
lineHeight: 16,
},
listItemInput: {
fontFamily: 'System',
fontSize: 15,
flexDirection: 'column',
flex: 1,
},
listItemText: {
fontFamily: 'System',
fontSize: 15,
flexDirection: 'column',
flex: 1,
lineHeight: 30,
},
listItemTextSpecial: {
fontStyle: 'italic',
},
listItemDelete: {
paddingLeft: 12,
paddingRight: 12,
flexDirection: 'column',
justifyContent: 'center',
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
}
});

View File

@ -0,0 +1,125 @@
/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const React = require('react-native');
const TodoItem = require('./todo-item');
const TodoListView = require('./todo-listview');
const realm = require('./realm');
const styles = require('./styles');
const { NavigatorIOS } = React;
class TodoApp extends React.Component {
constructor(props) {
super(props);
let todoLists = realm.objects('TodoList');
if (todoLists.length < 1) {
realm.write(() => {
realm.create('TodoList', {name: 'Todo List', items: []});
});
}
// This is a Results object, which will live-update.
this.todoLists = todoLists;
this.state = {};
}
get currentListView() {
let refs = this.refs.nav.refs;
return refs.listItemView || refs.listView;
}
render() {
let extraItems = [
{name: 'Complete', items: realm.objects('Todo', 'done = true')},
{name: 'Incomplete', items: realm.objects('Todo', 'done = false')},
];
let route = {
title: 'My Todo Lists',
component: TodoListView,
passProps: {
ref: 'listView',
items: this.todoLists,
extraItems: extraItems,
onPressItem: (list) => this._onPressTodoList(list),
},
backButtonTitle: 'Lists',
rightButtonTitle: 'Add',
onRightButtonPress: () => this._addNewTodoList(),
};
return (
<NavigatorIOS ref="nav" initialRoute={route} style={styles.navigator} />
);
}
_addNewTodoItem(list) {
let items = list.items;
if (!this._shouldAddNewItem(items)) {
return;
}
realm.write(() => {
items.push({text: ''});
});
this._setEditingRow(items.length - 1);
}
_addNewTodoList() {
let items = this.todoLists;
if (!this._shouldAddNewItem(items)) {
return;
}
realm.write(() => {
realm.create('TodoList', {name: '', items: []});
});
this._setEditingRow(items.length - 1);
}
_onPressTodoList(list) {
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);
}
_shouldAddNewItem(items) {
let editingRow = this.currentListView.state.editingRow;
let editingItem = editingRow != null && items[editingRow];
// Don't allow adding a new item if the one being edited is empty.
return !editingItem || !!editingItem.text || !!editingItem.name;
}
_setEditingRow(rowIndex) {
// Update the state on the currently displayed TodoList to edit this new item.
this.currentListView.setState({editingRow: rowIndex});
}
}
module.exports = TodoApp;

View File

@ -0,0 +1,58 @@
/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const React = require('react-native');
const TodoListItem = require('./todo-list-item');
const realm = require('./realm');
const styles = require('./styles');
const { Text, TouchableWithoutFeedback, View } = React;
class TodoItem extends TodoListItem {
constructor(props) {
super(props);
this._onPressCheckbox = this._onPressCheckbox.bind(this);
}
get done() {
return this.props.item.done;
}
set done(done) {
this.props.item.done = done;
}
get text() {
return this.props.item.text;
}
set text(text) {
this.props.item.text = text;
}
renderLeftSide() {
return (
<TouchableWithoutFeedback onPress={this._onPressCheckbox}>
<View style={styles.listItemLeftSide}>
<Text style={styles.listItemCheckbox}>
{this.done ? '✓' : ''}
</Text>
</View>
</TouchableWithoutFeedback>
);
}
_onPressCheckbox() {
realm.write(() => {
this.done = !this.done;
});
this.forceUpdate();
}
}
module.exports = TodoItem;

View File

@ -0,0 +1,119 @@
/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const React = require('react-native');
const realm = require('./realm');
const styles = require('./styles');
const { Text, TextInput, TouchableWithoutFeedback, View } = React;
class TodoListItem extends React.Component {
constructor(props) {
super(props);
this._onChangeText = this._onChangeText.bind(this);
}
get done() {
let items = this.props.item.items;
return items.length > 0 && Array.prototype.every.call(items, (item) => item.done);
}
get text() {
return this.props.item.name;
}
set text(text) {
this.props.item.name = text;
}
componentDidMount() {
// The autoFocus prop on TextInput was not working for us :(
this._focusInputIfNecessary();
}
componentDidUpdate() {
this._focusInputIfNecessary();
}
render() {
return (
<View style={styles.listItem}>
{this.renderLeftSide()}
{this.renderText()}
{this.renderRightSide()}
</View>
);
}
renderLeftSide() {
return (
<View style={styles.listItemLeftSide}>
<Text>{this.done ? '✓' : ''}</Text>
</View>
);
}
renderRightSide() {
// Only show the delete button while not editing the text.
return this.props.editing ? null : this.renderDelete();
}
renderText(extraStyle) {
if (this.props.editing) {
return (
<TextInput
ref="input"
value={this.text}
placeholder="Todo…"
style={[styles.listItemInput, extraStyle]}
onChangeText={this._onChangeText}
onEndEditing={this.props.onEndEditing}
enablesReturnKeyAutomatically={true} />
);
} else {
return (
<Text
style={[styles.listItemText, extraStyle]}
onPress={this.props.onPress}
suppressHighlighting={true}>
{this.text}
</Text>
);
}
}
renderDelete() {
return (
<TouchableWithoutFeedback onPress={this.props.onPressDelete}>
<View style={styles.listItemDelete}>
<Text>𐄂</Text>
</View>
</TouchableWithoutFeedback>
);
}
_onChangeText(text) {
realm.write(() => {
this.text = text;
});
this.forceUpdate();
}
_focusInputIfNecessary() {
if (!this.props.editing) {
return;
}
let input = this.refs.input;
if (!input.isFocused()) {
input.focus();
}
}
}
module.exports = TodoListItem;

View File

@ -0,0 +1,157 @@
/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const React = require('react-native');
const TodoListItem = require('./todo-list-item');
const realm = require('./realm');
const styles = require('./styles');
const { ListView, Text, View } = React;
class TodoListView extends React.Component {
constructor(props) {
super(props);
this.dataSource = new ListView.DataSource({
sectionHeaderHasChanged: () => false,
rowHasChanged: (row1, row2) => row1 !== row2
});
this.state = {};
this.renderRow = this.renderRow.bind(this);
}
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() {
// Clone the items into a new Array to prevent unexpected errors from changes in length.
let sections = [Array.from(this.props.items)];
let extraItems = this.props.extraItems;
if (extraItems && extraItems.length) {
sections.push(extraItems);
}
let dataSource = this.dataSource.cloneWithRowsAndSections(sections);
return (
<View style={styles.container}>
<ListView style={styles.listView} dataSource={dataSource} renderRow={this.renderRow} />
<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 (
<RowClass
item={item}
editing={editing}
onPress={() => this._onPressRow(item, sectionIndex, rowIndex)}
onPressDelete={() => this._onPressDeleteRow(item)}
onEndEditing={() => this._onEndEditingRow(item, rowIndex)} />
);
}
_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.forceUpdate();
}
_onEndEditingRow(item, rowIndex) {
this._deleteItemIfEmpty(item);
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;
}
}
class TodoListExtraItem extends TodoListItem {
renderText() {
return super.renderText(styles.listItemTextSpecial);
}
renderLeftSide() {
return (
<View style={styles.listItemLeftSide}>
<Text style={styles.listItemCount}>
{this.props.item.items.length}
</Text>
</View>
);
}
renderRightSide() {
return null;
}
}
module.exports = TodoListView;

View File

@ -1,10 +1,5 @@
/** /* Copyright 2015 Realm Inc - All Rights Reserved
* Copyright (c) 2015-present, Facebook, Inc. * Proprietary and Confidential
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>

View File

@ -1,10 +1,5 @@
/** /* Copyright 2015 Realm Inc - All Rights Reserved
* Copyright (c) 2015-present, Facebook, Inc. * Proprietary and Confidential
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import "AppDelegate.h" #import "AppDelegate.h"
@ -15,47 +10,47 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
NSURL *jsCodeLocation; NSURL *jsCodeLocation;
/** /**
* Loading JavaScript code - uncomment the one you want. * Loading JavaScript code - uncomment the one you want.
* *
* OPTION 1 * OPTION 1
* Load from development server. Start the server from the repository root: * Load from development server. Start the server from the repository root:
* *
* $ npm start * $ npm start
* *
* To run on device, change `localhost` to the IP address of your computer * To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the * (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are * `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network. * on the same Wi-Fi network.
*/ */
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"]; jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
/** /**
* OPTION 2 * OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle * Load from pre-bundled file on disk. To re-generate the static bundle
* from the root of your project directory, run * from the root of your project directory, run
* *
* $ react-native bundle --minify * $ react-native bundle --minify
* *
* see http://facebook.github.io/react-native/docs/runningondevice.html * see http://facebook.github.io/react-native/docs/runningondevice.html
*/ */
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ReactExample" moduleName:@"ReactExample"
initialProperties:nil initialProperties:nil
launchOptions:launchOptions]; launchOptions:launchOptions];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init]; UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view = rootView; rootViewController.view = rootView;
self.window.rootViewController = rootViewController; self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];
return YES; return YES;
} }
@end @end

View File

@ -12,7 +12,7 @@
#import "AppDelegate.h" #import "AppDelegate.h"
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
@autoreleasepool { @autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
} }
} }

View File

@ -1,198 +1,10 @@
/** /* Copyright 2015 Realm Inc - All Rights Reserved
* Sample React Native App * Proprietary and Confidential
* https://github.com/facebook/react-native
*/ */
'use strict'; 'use strict';
var Realm = require('realm'); const React = require('react-native');
var React = require('react-native'); const TodoApp = require('./components/todo-app');
var { React.AppRegistry.registerComponent('ReactExample', () => TodoApp);
AppRegistry,
StyleSheet,
NavigatorIOS,
AlertIOS,
ListView,
TouchableHighlight,
Text,
TextInput,
View,
} = React;
var TodoItemSchema = {
name: 'Todo',
properties: [
{name: 'text', type: Realm.Types.STRING},
]
};
var TodoListSchmea = {
name: 'TodoList',
properties: [
{name: 'name', type: Realm.Types.STRING},
{name: 'items', type: Realm.Types.LIST, objectType: 'Todo'}
]
};
console.log(Realm.defaultPath);
var realm = new Realm({schema: [TodoItemSchema, TodoListSchmea]});
class Edit extends React.Component {
componentWillMount() {
this.setState({text: this.props.text});
}
save() {
realm.write(function () {
if (this.props.todoId == this.props.list.items.length) {
this.props.list.items.push({text: this.state.text});
}
else {
var todoItem = this.props.list.items[this.props.todoId];
todoItem.text = this.state.text;
}
}.bind(this));
// should not be needed once we have notifications
this.props.parent.updateDataSource();
this.props.navigator.pop();
}
render() {
return (
<View style={{flex:1, justifyContent: 'flex-start'}}>
<TextInput multiline={true} style={styles.textInput}
placeholder='Enter Todo' autoFocus={true}
onChangeText={(text) => this.setState({text})} value={this.state.text}/>
<TouchableHighlight
style={styles.button}
onPress={this.save.bind(this)}
underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Save</Text>
</TouchableHighlight>
</View>
)
}
};
class TodoList extends React.Component {
componentWillMount() {
this.lists = realm.objects('TodoList');
if (this.lists.length < 1) {
realm.write(function() {
realm.create('TodoList', ['List', []]);
});
}
this.list = this.lists[0];
this.menu = this.menu.bind(this);
this.delete = this.delete.bind(this);
var dataSource = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
});
this.updateDataSource(dataSource);
}
updateDataSource(oldDataSource) {
if (!oldDataSource) {
oldDataSource = this.state.dataSource;
}
this.setState({dataSource: oldDataSource.cloneWithRows(this.list.items)});
}
menu(todo, todoID) {
AlertIOS.alert(
todo.text,
todoID,
[
{text: 'Complete', onPress: () => this.delete(todoID)},
{text: 'Edit', onPress: () => this.edit(todoID, todo.text)},
{text: 'Cancel'}
]
)
}
delete(todoID) {
var item = this.list.items[todoID];
realm.write(function() {
realm.delete(item);
})
this.updateDataSource();
}
edit(todoId, text) {
this.props.navigator.push({
title: text,
component: Edit,
passProps: {list: this.list, todoId: todoId, text: text, parent: this}
});
}
render() {
return (
<View style={styles.container}>
<ListView style={styles.listView} dataSource={this.state.dataSource} renderRow={(rowData, sectionID, rowID) =>
<TouchableHighlight style={styles.listItem} onPress={() => this.menu(rowData, rowID)}>
<Text>{rowData.text}</Text>
</TouchableHighlight>
}/>
<TouchableHighlight style={styles.button}
onPress={() => this.edit(this.list.items.length, "")}>
<Text style={styles.buttonText}>+</Text>
</TouchableHighlight>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+Control+Z for dev menu
</Text>
</View>
);
}
};
class Navigator extends React.Component {
render() {
return (
<NavigatorIOS initialRoute={{component: TodoList, title: 'Todo Items'}} style={{flex:1}}/>
);
}
};
AppRegistry.registerComponent('ReactExample', () => Navigator);
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'stretch',
backgroundColor: '#ffffff',
},
listItem: {
marginTop: 3,
padding: 6,
backgroundColor:'#ACACAC',
alignSelf: 'stretch',
flexDirection: 'row',
flex:1,
},
textInput: {
alignSelf: 'stretch',
borderWidth: 0.5,
borderColor: '#0f0f0f',
height: 200,
fontSize: 13,
margin: 6,
marginTop: 70,
padding: 4,
},
button: {
height: 36,
backgroundColor: '#48BBEC',
alignSelf: 'stretch',
justifyContent: 'center'
},
buttonText: {
alignSelf: 'center',
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
}
});