Skip to content
This repository was archived by the owner on Mar 25, 2025. It is now read-only.

Commit 0a912a4

Browse files
committed
fix #117
1 parent 010bd9d commit 0a912a4

24 files changed

Lines changed: 420 additions & 96 deletions

README.md

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,35 @@ Snowflake ![snowflake](https://cloud.githubusercontent.com/assets/1282364/115993
1010
------------
1111
##Notes
1212

13-
Navigation is handled with [React Native Router Flux](https://github.com/aksonov/react-native-router-flux)
13+
Navigation is handled with [React Native Router Flux](https://github.com/aksonov/react-native-router-flux). Multiple scenes support **Login, Register, and Reset Password**. Once successfully logged in, there are 3 more scenes: **Logout, Subview, and Profile**.
1414

15-
Using [Redux](https://github.com/reactjs/react-redux) and [Immutable](https://facebook.github.io/immutable-js/), the state of the application is testable with
16-
[Jest](https://facebook.github.io/jest/), which includes [Snapshot tests](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html) currently with 85 tests and 90% coverage!!!
15+
A user can **change** their **Email Address** and **User Name** once they are logged in using the **Profile** form.
1716

18-
Snowflake supports Hot Reloading of its state. Snowflake uses CI with [Bitrise.io]( https://www.bitrise.io) and has extensive docs and 45+ min of video demonstating implementation.
17+
The icons used throughout the app are from [React Native Vector Icons](https://github.com/oblador/react-native-vector-icons), namely using **FontAwesome**
1918

20-
Note: Snowflake now (Jan 3, 2016) has a **choice of servers**, either
19+
**Form building** is extremely easy and consistent by using [Tcomb Form Library](https://github.com/gcanti/tcomb-form-native) by using **domain models** and writing less code.
2120

22-
* The original Parse.com
21+
Using [Redux](https://github.com/reactjs/react-redux) and [Immutable](https://facebook.github.io/immutable-js/), the state of the application is **testable** with [Jest](https://facebook.github.io/jest/), which includes [Snapshot tests](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html) currently with 85 tests and ~90% coverage!!!
22+
23+
To ease the pain of Redux Action definitions, Snowflake uses [Key Mirror](https://github.com/STRML/keyMirror).
24+
25+
Using the [Validate.JS](https://validatejs.org/) Library, all **user input is validated**. Appropriate messages are displayed to the user guiding them in the input requirements.
26+
27+
Once a user is logged in, their **Session State is stored** in [AsyncStorage](https://github.com/jasonmerino/react-native-simple-store) so that subsequent usage does not require logging in again.
28+
29+
Snowflake supports **multiple languages** using [I18n](https://github.com/AlexanderZaytsev/react-native-i18n) with English, French and Spanish.
30+
31+
Snowflake supports **Hot Reloading** of its state.
32+
33+
Snowflake uses CI with [Bitrise.io]( https://www.bitrise.io) and has **extensive docs and 45+ min of video** demonstating implementation.
34+
35+
Snowflake has a **choice of servers**, either
36+
37+
* The original **Parse.com**
2338
or
24-
* Hapi Server that runs on Openshift and locally.
39+
* **Hapi Server** that runs on **RedHat Openshift** and **locally**.
2540

26-
See [https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.com/bartonhammond/snowflake-hapi-openshift) for more information about the OpenShift Hapi server. The setup instructions below describe how to select the server you desire.
41+
See [https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.com/bartonhammond/snowflake-hapi-openshift) for more information about the OpenShift Hapi server. The setup instructions below describe how to select the server you desire.
2742

2843
---------------
2944
# Content
@@ -62,16 +77,19 @@ See [https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.c
6277
1. **All state changes*** are actions to the Redux store.
6378
1. The backend is provided by Parse.com using the **Rest API**
6479
1. **Every action** performed by the UI interfaces with the **Redux actions** and subsequently to the Redux Store. This **reduces the complexity** of the JSX Components **tremendously**and makes them easily testable.
65-
1. **Jest Unit Tests cover 86%** of the application statements.
66-
1. Demonstrates how to **setup React-Native to perform Jest testing** with Babel.
67-
1. Includes ability to **debug Jest unit tests**with Chrome
80+
1. **Jest Unit Tests cover ~90%** of the application statements.
81+
1. Demonstrates how to **setup React-Native to perform Jest testing** with Mock modules
82+
1. Includes ability to **debug Jest unit tests** with Chrome
6883
1. Instructions and videos for **continuous integration with Bitrise.io**
6984

7085
----------
7186
## Quotes
7287

7388
Some quotes from users of **Snowflake**
7489

90+
**Pepperoni App Kit** (see [Credits](https://github.com/futurice/pepperoni-app-kit#credits) )
91+
>This project was initially motivated by Snowflake....you should check it out to see if it's a good fit for your app.
92+
7593
**Viktor**
7694
>Just saw the tweets, still watching the vids. It's awesome!! It's really really high quality, I'm truly amazed
7795
@@ -99,7 +117,6 @@ Some quotes from users of **Snowflake**
99117
**Jim**
100118
>Neat project
101119
102-
103120
----------
104121

105122
## Technologies
@@ -534,6 +551,10 @@ alt="Snowflake Hot Loading" width="240" height="180" border="10" /></a>
534551

535552
## Faq
536553

554+
### How do I change the Language of the App?
555+
556+
Just use the Settings of your Device or Simulator.
557+
537558
### Why did you use the RestAPI instead of the (JS-sdk | ParseReact)
538559

539560
I looked at it initially and, imo, it had too much magic. For example, I couldn't see how I was going to isolate my JSX components easily and keep all my "state" in Redux. ParseReact is right there in the middle of your JSX component which would tie me to a very particular vendor and implementation detail. If I decided to later move away from ParseReact I'd have to rewrite a lot of code and tests.

android/app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ android {
126126
}
127127

128128
dependencies {
129+
129130
compile fileTree(dir: "libs", include: ["*.jar"])
130131
compile "com.android.support:appcompat-v7:23.0.1"
131132
compile "com.facebook.react:react-native:+" // From node_modules
@@ -136,6 +137,8 @@ dependencies {
136137
// Added this line:
137138
compile project(':RNSimpleAlertDialogModule')
138139

140+
// Added this line:
141+
compile project(':react-native-i18n')
139142
}
140143

141144
// Run this once to be able to run the application with BUCK

android/app/src/main/java/com/snowflake/MainActivity.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.snowflake;
22

33
import com.facebook.react.ReactActivity;
4-
import com.burnweb.rnsimplealertdialog.RNSimpleAlertDialogPackage;
5-
import com.burnweb.rnsimplealertdialog.RNSimpleAlertDialogPackage;
64

75
public class MainActivity extends ReactActivity {
86

android/app/src/main/java/com/snowflake/MainApplication.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import com.oblador.vectoricons.VectorIconsPackage;
1717
// Add this line:
1818
import com.burnweb.rnsimplealertdialog.RNSimpleAlertDialogPackage;
19+
// Add this line:
20+
import com.i18n.reactnativei18n.ReactNativeI18n;
1921

2022
public class MainApplication extends Application implements ReactApplication {
2123

@@ -31,7 +33,8 @@ protected List<ReactPackage> getPackages() {
3133
new MainReactPackage(),
3234
//Added two lines
3335
new RNSimpleAlertDialogPackage(),
34-
new VectorIconsPackage()
36+
new VectorIconsPackage(),
37+
new ReactNativeI18n()
3538
);
3639
}
3740
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<resources>
22

33

4+
45
<string name="app_name">snowflake</string>
56
</resources>

android/settings.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
rootProject.name = 'snowflake'
22

33
include ':app'
4+
include ':react-native-i18n'
5+
project(':react-native-i18n').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-i18n/android')
46

57

68
//Added the following two lines

ios/snowflake.xcodeproj/project.pbxproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
D60BBC7120524A08A4BD8E53 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AC5CF81FFC444E83B6A2AB82 /* libRNVectorIcons.a */; };
3232
F96123F511D043FC80FBC817 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 292DE9BEB3A54B9FA10D6AAF /* Ionicons.ttf */; };
3333
FFEAA9091B0249258A5B3384 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5FDC7A8EFBC848329F28211D /* EvilIcons.ttf */; };
34+
16D41BDB6D0C4ACAA2AE8608 /* libRNI18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7612040BEE949EDA2756B37 /* libRNI18n.a */; };
3435
/* End PBXBuildFile section */
3536

3637
/* Begin PBXContainerItemProxy section */
@@ -152,6 +153,8 @@
152153
D9132581E9B74234AFB70B52 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
153154
D9139C5B6281455D8B157E96 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; };
154155
E7A88F38CE3D4C92ADB9AC57 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = "<group>"; };
156+
8A5574F7663340CC82B1B935 /* RNI18n.xcodeproj */ = {isa = PBXFileReference; name = "RNI18n.xcodeproj"; path = "../node_modules/react-native-i18n/RNI18n.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
157+
E7612040BEE949EDA2756B37 /* libRNI18n.a */ = {isa = PBXFileReference; name = "libRNI18n.a"; path = "libRNI18n.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
155158
/* End PBXFileReference section */
156159

157160
/* Begin PBXFrameworksBuildPhase section */
@@ -178,6 +181,7 @@
178181
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
179182
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
180183
D60BBC7120524A08A4BD8E53 /* libRNVectorIcons.a in Frameworks */,
184+
16D41BDB6D0C4ACAA2AE8608 /* libRNI18n.a in Frameworks */,
181185
);
182186
runOnlyForDeploymentPostprocessing = 0;
183187
};
@@ -324,6 +328,7 @@
324328
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
325329
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
326330
16A3B0EB86DC41129E391241 /* RNVectorIcons.xcodeproj */,
331+
8A5574F7663340CC82B1B935 /* RNI18n.xcodeproj */,
327332
);
328333
name = Libraries;
329334
sourceTree = "<group>";
@@ -656,6 +661,7 @@
656661
LIBRARY_SEARCH_PATHS = (
657662
"$(inherited)",
658663
"\"$(SRCROOT)/$(TARGET_NAME)\"",
664+
"\"$(SRCROOT)/$(TARGET_NAME)\"",
659665
);
660666
PRODUCT_NAME = "$(TARGET_NAME)";
661667
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/snowflake.app/snowflake";
@@ -673,6 +679,7 @@
673679
LIBRARY_SEARCH_PATHS = (
674680
"$(inherited)",
675681
"\"$(SRCROOT)/$(TARGET_NAME)\"",
682+
"\"$(SRCROOT)/$(TARGET_NAME)\"",
676683
);
677684
PRODUCT_NAME = "$(TARGET_NAME)";
678685
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/snowflake.app/snowflake";
@@ -690,6 +697,7 @@
690697
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
691698
"$(SRCROOT)/../node_modules/react-native/React/**",
692699
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
700+
"$(SRCROOT)/../node_modules/react-native-i18n/RNI18n",
693701
);
694702
INFOPLIST_FILE = snowflake/Info.plist;
695703
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -713,6 +721,7 @@
713721
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
714722
"$(SRCROOT)/../node_modules/react-native/React/**",
715723
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
724+
"$(SRCROOT)/../node_modules/react-native-i18n/RNI18n",
716725
);
717726
INFOPLIST_FILE = snowflake/Info.plist;
718727
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -765,6 +774,7 @@
765774
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
766775
"$(SRCROOT)/../node_modules/react-native/React/**",
767776
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
777+
"$(SRCROOT)/../node_modules/react-native-i18n/RNI18n",
768778
);
769779
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
770780
MTL_ENABLE_DEBUG_INFO = YES;
@@ -806,6 +816,7 @@
806816
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
807817
"$(SRCROOT)/../node_modules/react-native/React/**",
808818
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
819+
"$(SRCROOT)/../node_modules/react-native-i18n/RNI18n",
809820
);
810821
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
811822
MTL_ENABLE_DEBUG_INFO = NO;

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "snowflake",
3-
"version": "0.1.4",
3+
"version": "0.1.5",
44
"private": true,
55
"jest": {
66
"preset": "jest-react-native",
@@ -25,13 +25,16 @@
2525
"immutable": "3.8.1",
2626
"key-mirror": "1.0.1",
2727
"react": "15.2.1",
28+
"react-mixin": "^2.0.2",
2829
"react-native": "0.30.0",
30+
"react-native-i18n": "0.0.8",
2931
"react-native-navbar": "1.5.0",
3032
"react-native-router-flux": "3.31.1",
3133
"react-native-simple-store": "1.0.1",
3234
"react-native-simpledialog-android": "1.0.7",
3335
"react-native-vector-icons": "2.0.3",
3436
"react-redux": "4.4.5",
37+
"react-timer-mixin": "^0.13.3",
3538
"redux": "3.5.2",
3639
"redux-thunk": "2.1.0",
3740
"regenerator": "0.8.46",

src/components/Header.js

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,20 @@ var styles = StyleSheet.create({
4646
container: {
4747
flexDirection: 'column',
4848
flex: 1,
49-
margin: 20
49+
marginTop: 10
5050
},
5151
header: {
52-
marginTop: 10,
52+
marginTop: 20,
5353
justifyContent: 'center',
5454
alignItems: 'center',
5555
backgroundColor: 'transparent'
5656
},
5757
mark: {
58-
width: 50,
59-
height: 50
58+
height:100,
59+
width: 100
6060
}
61+
62+
6163
});
6264

6365
var Header = React.createClass({
@@ -125,55 +127,56 @@ var Header = React.createClass({
125127
*
126128
*/
127129
render() {
128-
let showState = <Text> </Text>;
130+
129131
if (this.props.showState) {
130132
let displayText = JSON.stringify(this.props.currentState);
131133

132134
console.log(displayText);
133135

134-
showState =
135-
<View style={styles.container}>
136-
<Text>Current State (see console)</Text>
137-
<TextInput style={{height: 100, borderColor: 'gray', borderWidth: 1}}
138-
value={displayText}
139-
editable={true}
140-
multiline={true}
141-
onChangeText={(text) => this._onChangeText(text)}
142-
numberOfLines={20}>
143-
</TextInput>
144-
<View style={{
145-
marginTop: 10
146-
}}>
147-
<FormButton isDisabled={this.state.isDisabled}
148-
onPress={this._updateStateButtonPress}
149-
buttonText={'Update State'}>
150-
</FormButton>
151-
152-
</View>
153-
</View>
154136
}
155137

156-
let spinner = <Text> </Text>;
157-
if (this.props.isFetching) {
158-
spinner = <ActivityIndicator
159-
animating={true}
160-
size="large"
161-
/>
162-
}
138+
163139

164140
return (
165141
<View>
166142
<View style={styles.header}>
167143

168144
<TouchableHighlight onPress={this._onPressMark}>
169145

170-
<Image style={styles.mark} source={{uri:
171-
'http://i.imgur.com/da4G0Io.png'}}
146+
<Image style={styles.mark}
147+
source={require('../images/Snowflake.png')}
172148
/>
173149
</TouchableHighlight>
174-
{spinner}
150+
{this.props.isFetching ?
151+
<ActivityIndicator animating={true} size="large" />
152+
:
153+
null
154+
}
155+
156+
175157
</View>
176-
{showState}
158+
{this.props.showState ?
159+
<View style={styles.container}>
160+
<Text>{I18n.t("Header.current_state")} ({I18n.t("Header.see_console")})</Text>
161+
<TextInput style={{height: 100, borderColor: 'gray', borderWidth: 1}}
162+
value={displayText}
163+
editable={true}
164+
multiline={true}
165+
onChangeText={(text) => this._onChangeText(text)}
166+
numberOfLines={20}>
167+
</TextInput>
168+
<View style={{
169+
marginTop: 10
170+
}}>
171+
<FormButton isDisabled={this.state.isDisabled}
172+
onPress={this._updateStateButtonPress}
173+
buttonText={'Update State'}>
174+
</FormButton>
175+
176+
</View>
177+
</View>
178+
:
179+
null}
177180
</View>
178181
);
179182
}

0 commit comments

Comments
 (0)