├── .watchmanconfig ├── screenshots ├── pw-1.PNG ├── pw-2.PNG ├── pw-3.PNG ├── pw-4.PNG ├── rw-1.PNG ├── rw-2.PNG ├── rw-3.PNG └── rw-4.PNG ├── js ├── components │ ├── img │ │ ├── fog.png │ │ ├── cloudy.png │ │ ├── rain.png │ │ ├── snow.png │ │ ├── sunny.gif │ │ ├── sunny.png │ │ ├── rain_light.png │ │ ├── partly_cloudy.png │ │ ├── rain_s_cloudy.png │ │ ├── thunderstorms.png │ │ ├── sunny_s_cloudy.png │ │ └── header-background.png │ ├── settings │ │ ├── userguide.js │ │ ├── separator.js │ │ ├── styles.js │ │ ├── section.js │ │ ├── sectiontitle.js │ │ ├── settings.js │ │ ├── navigationbuttonrow.js │ │ ├── about.js │ │ ├── locations.js │ │ └── addlocation.js │ ├── loading.js │ ├── pager.js │ ├── forecastimage.js │ ├── forecast.js │ ├── forecastitem.js │ ├── footer.js │ ├── weather.js │ └── header.js ├── reducers │ ├── index.js │ ├── postcode.js │ ├── location.js │ └── weather.js ├── actions │ ├── index.js │ ├── postcode.js │ ├── types.js │ ├── weather.js │ └── location.js ├── realm │ ├── index.js │ ├── schema.js │ ├── configure.js │ ├── schema-v1.js │ └── schema-v2.js ├── store │ ├── promise.js │ └── configure.js ├── config.js ├── app.js ├── dependencies │ ├── parallaxview │ │ ├── styles.js │ │ └── index.js │ ├── swipeout │ │ ├── styles.js │ │ └── index.js │ └── swiper │ │ └── index.js ├── models │ └── view.js ├── services │ ├── postcode.js │ ├── location.js │ ├── weather.js │ └── testdata.js ├── main.js └── navigators │ ├── application.js │ └── settings.js ├── android ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── app │ ├── src │ │ └── main │ │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Entypo.ttf │ │ │ │ ├── Zocial.ttf │ │ │ │ ├── EvilIcons.ttf │ │ │ │ ├── Foundation.ttf │ │ │ │ ├── Ionicons.ttf │ │ │ │ ├── Octicons.ttf │ │ │ │ ├── FontAwesome.ttf │ │ │ │ └── MaterialIcons.ttf │ │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── reactweather │ │ │ └── MainActivity.java │ ├── BUCK │ ├── proguard-rules.pro │ ├── build.gradle │ └── app.iml ├── keystores │ ├── debug.keystore.properties │ └── BUCK ├── settings.gradle ├── build.gradle ├── gradle.properties ├── ReactWeather.iml ├── gradlew.bat └── gradlew ├── jsconfig.json ├── tsconfig.json ├── .buckconfig ├── index.ios.js ├── index.android.js ├── ios ├── ReactWeather │ ├── AppDelegate.h │ ├── main.m │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── AppDelegate.m │ └── Base.lproj │ │ └── LaunchScreen.xib ├── ReactWeatherTests │ ├── Info.plist │ └── ReactWeatherTests.m └── ReactWeather.xcodeproj │ └── xcshareddata │ └── xcschemes │ └── ReactWeather.xcscheme ├── package.json ├── api ├── suburbs.json ├── location.js ├── weather.json ├── forecast-daily.json └── postcode.js ├── .gitignore ├── LICENSE ├── .flowconfig └── README.md /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /screenshots/pw-1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/pw-1.PNG -------------------------------------------------------------------------------- /screenshots/pw-2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/pw-2.PNG -------------------------------------------------------------------------------- /screenshots/pw-3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/pw-3.PNG -------------------------------------------------------------------------------- /screenshots/pw-4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/pw-4.PNG -------------------------------------------------------------------------------- /screenshots/rw-1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/rw-1.PNG -------------------------------------------------------------------------------- /screenshots/rw-2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/rw-2.PNG -------------------------------------------------------------------------------- /screenshots/rw-3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/rw-3.PNG -------------------------------------------------------------------------------- /screenshots/rw-4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/screenshots/rw-4.PNG -------------------------------------------------------------------------------- /js/components/img/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/fog.png -------------------------------------------------------------------------------- /js/components/img/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/cloudy.png -------------------------------------------------------------------------------- /js/components/img/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/rain.png -------------------------------------------------------------------------------- /js/components/img/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/snow.png -------------------------------------------------------------------------------- /js/components/img/sunny.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/sunny.gif -------------------------------------------------------------------------------- /js/components/img/sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/sunny.png -------------------------------------------------------------------------------- /js/components/img/rain_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/rain_light.png -------------------------------------------------------------------------------- /js/components/img/partly_cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/partly_cloudy.png -------------------------------------------------------------------------------- /js/components/img/rain_s_cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/rain_s_cloudy.png -------------------------------------------------------------------------------- /js/components/img/thunderstorms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/thunderstorms.png -------------------------------------------------------------------------------- /js/components/img/sunny_s_cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/sunny_s_cloudy.png -------------------------------------------------------------------------------- /js/components/img/header-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/js/components/img/header-background.png -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ReactWeather 6 | 7 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6" 4 | }, 5 | "exclude": [ 6 | "node_modules" 7 | ] 8 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true 4 | }, 5 | "exclude": [ 6 | "node_modules" 7 | ] 8 | } -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stage88/react-weather/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = 'debug', 3 | store = 'debug.keystore', 4 | properties = 'debug.keystore.properties', 5 | visibility = [ 6 | 'PUBLIC', 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const { AppRegistry } = require('react-native'); 8 | const setup = require('./js/main'); 9 | 10 | AppRegistry.registerComponent('ReactWeather', setup); 11 | -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const { AppRegistry } = require('react-native'); 8 | const setup = require('./js/main'); 9 | 10 | AppRegistry.registerComponent('ReactWeather', setup); 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jun 16 22:28:01 EST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /js/reducers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var { combineReducers } = require('redux'); 8 | 9 | module.exports = combineReducers({ 10 | weather: require('./weather'), 11 | location: require('./location'), 12 | postcode: require('./postcode') 13 | }); 14 | -------------------------------------------------------------------------------- /js/actions/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const weather = require('./weather'); 8 | const location = require('./location'); 9 | const postcode = require('./postcode'); 10 | 11 | module.exports = { 12 | ...weather, 13 | ...location, 14 | ...postcode 15 | }; 16 | -------------------------------------------------------------------------------- /js/realm/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import Realm from 'realm'; 8 | import Schema from './schema'; 9 | 10 | function getCurrent() { 11 | var schema = new Schema(); 12 | var current = schema.current(); 13 | 14 | return new Realm(current); 15 | } 16 | 17 | module.exports = { 18 | current: getCurrent 19 | } 20 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ReactWeather' 2 | 3 | include ':app' 4 | include ':realm' 5 | project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android') 6 | include ':react-native-vector-icons' 7 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 8 | -------------------------------------------------------------------------------- /js/store/promise.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | function warn(error) { 8 | console.warn(error.message || error); 9 | throw error; // To let the caller handle the rejection 10 | } 11 | 12 | module.exports = (store: any) => (next: any) => (action: any) => 13 | typeof action.then === 'function' 14 | ? Promise.resolve(action).then(next, warn) 15 | : next(action); 16 | -------------------------------------------------------------------------------- /js/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | // TODO: find out how to handle errors from require 8 | try { 9 | var { weatherApiKey } = require('../release/keys'); 10 | } catch (e) {} 11 | 12 | module.exports = { 13 | weatherApiKey: weatherApiKey, 14 | weatherApiUrl: 'http://api.openweathermap.org/data/2.5', 15 | postcodeApiUrl: 'http://v0.postcodeapi.com.au/suburbs.json', 16 | isDebugData: false 17 | }; 18 | -------------------------------------------------------------------------------- /js/realm/schema.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import schema_v1 from './schema-v1'; 8 | import schema_v2 from './schema-v2'; 9 | 10 | class Schema { 11 | schemas: Array; 12 | 13 | constructor() { 14 | this.schemas = [ 15 | schema_v1, 16 | schema_v2 17 | ]; 18 | } 19 | 20 | current() { 21 | return this.schemas[this.schemas.length - 1]; 22 | } 23 | } 24 | 25 | module.exports = Schema; 26 | -------------------------------------------------------------------------------- /js/components/settings/userguide.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | Text 12 | } from 'react-native'; 13 | 14 | class UserGuide extends Component { 15 | render() { 16 | return ( 17 | 18 | User Guide 19 | 20 | ); 21 | } 22 | } 23 | 24 | module.exports = UserGuide; 25 | -------------------------------------------------------------------------------- /ios/ReactWeather/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /js/components/settings/separator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | } from 'react-native'; 12 | 13 | class Separator extends Component { 14 | render() { 15 | return ( 16 | 17 | ); 18 | } 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | separator: { 23 | height: StyleSheet.hairlineWidth, 24 | backgroundColor: '#C8C7CC', 25 | marginLeft: 14 26 | }, 27 | }); 28 | 29 | module.exports = Separator; -------------------------------------------------------------------------------- /ios/ReactWeather/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /js/actions/postcode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { Action } from './types'; 8 | 9 | import PostcodeService from '../services/postcode'; 10 | const service = new PostcodeService(); 11 | 12 | async function searchPostcodes(text: string) { 13 | var result = await service.getPostcodes(text); 14 | return { 15 | type: 'POSTCODE_SEARCH', 16 | data: result 17 | }; 18 | } 19 | 20 | function clearPostcodes() { 21 | return { 22 | type: 'POSTCODE_CLEAR' 23 | } 24 | } 25 | 26 | module.exports = { 27 | searchPostcodes, 28 | clearPostcodes 29 | }; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactWeather", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start" 7 | }, 8 | "dependencies": { 9 | "react": "15.0.2", 10 | "react-native": "^0.26.3", 11 | "react-native-vector-icons": "^2.0.3", 12 | "react-redux": "^4.4.5", 13 | "react-tween-state": "^0.1.5", 14 | "realm": "^0.13.2", 15 | "redux": "^3.5.2", 16 | "redux-logger": "^2.6.1", 17 | "redux-thunk": "^2.1.0" 18 | }, 19 | "devDependencies": { 20 | "rnpm-plugin-upgrade": "^0.26.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /js/actions/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { WeatherModel, Location, Postcode } from '../models/view'; 8 | 9 | export type Action = 10 | { type: 'WEATHER_SET_LOADING' } 11 | | { type: 'WEATHER_SET_REFRESHING' } 12 | | { type: 'WEATHER_GET_ALL', data: Array } 13 | | { type: 'LOCATION_INITIALISED' } 14 | | { type: 'LOCATION_GET_ALL', data: Array } 15 | | { type: 'LOCATION_CLEAR_ALL_DATA' } 16 | | { type: 'LOCATION_DELETE' } 17 | | { type: 'LOCATION_ADD' } 18 | | { type: 'POSTCODE_SEARCH', data: Array } 19 | | { type: 'POSTCODE_CLEAR' }; 20 | -------------------------------------------------------------------------------- /js/realm/configure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import Realm from 'realm'; 8 | import Schema from './schema'; 9 | 10 | function configureRealm() { 11 | var schema = new Schema(); 12 | 13 | var next = Realm.schemaVersion(Realm.defaultPath); 14 | if (next > 0) { 15 | while (next < schema.schemas.length) { 16 | var migratedSchema = schema.schemas[next++]; 17 | var migratedRealm = new Realm(migratedSchema); 18 | migratedRealm.close(); 19 | } 20 | } 21 | 22 | var current = schema.current(); 23 | var realm = new Realm(current); 24 | realm.close(); 25 | } 26 | 27 | module.exports = configureRealm; 28 | -------------------------------------------------------------------------------- /api/suburbs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Queanbeyan East", 4 | "postcode": 2620, 5 | "state": { 6 | "name": "New South Wales", 7 | "abbreviation": "NSW" 8 | }, 9 | "locality": "CANBERRA", 10 | "latitude": -35.342799999999997, 11 | "longitude": 149.24440000000001 12 | }, 13 | { 14 | "name": "Queanbeyan", 15 | "postcode": 2620, 16 | "state": { 17 | "name": "New South Wales", 18 | "abbreviation": "NSW" 19 | }, 20 | "locality": "CANBERRA", 21 | "latitude": -35.354399999999998, 22 | "longitude": 149.2321 23 | } 24 | ] -------------------------------------------------------------------------------- /js/reducers/postcode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { Action } from '../actions/types'; 8 | import type { Postcode } from '../models/view'; 9 | 10 | export type State = { 11 | data: Array; 12 | }; 13 | 14 | const initial: State = { 15 | data: [] 16 | }; 17 | 18 | function postcode(state: State = initial, action: Action): State { 19 | switch (action.type) { 20 | case 'POSTCODE_SEARCH': 21 | return { 22 | data: action.data 23 | }; 24 | case 'POSTCODE_CLEAR': 25 | return { 26 | data: [] 27 | }; 28 | default: 29 | return state; 30 | } 31 | } 32 | 33 | module.exports = postcode; 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | 36 | # BUCK 37 | buck-out/ 38 | \.buckd/ 39 | android/app/libs 40 | android/keystores/debug.keystore 41 | 42 | # Custom 43 | # 44 | /release 45 | /logs 46 | /.vscode 47 | /ios/main.jsbundle 48 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import StatusBar from 'StatusBar'; 8 | import React, { Component } from 'react'; 9 | import { 10 | StyleSheet, 11 | View, 12 | } from 'react-native'; 13 | 14 | import ApplicationNavigator from './navigators/application'; 15 | 16 | class App extends Component { 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | } 26 | 27 | const styles = StyleSheet.create({ 28 | container: { 29 | flex: 1 30 | } 31 | }); 32 | 33 | module.exports = App; 34 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$projectDir/../../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /js/store/configure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import { applyMiddleware, createStore } from 'redux'; 8 | import thunk from 'redux-thunk'; 9 | import createLogger from 'redux-logger'; 10 | 11 | import reducers from '../reducers'; 12 | import promise from './promise'; 13 | 14 | var isDebuggingInChrome = __DEV__ && !!window.navigator.userAgent; 15 | 16 | var logger = createLogger({ 17 | predicate: (getState, action) => isDebuggingInChrome, 18 | collapsed: true, 19 | duration: true, 20 | }); 21 | 22 | var store = applyMiddleware(thunk, promise, logger)(createStore)(reducers); 23 | 24 | function configureStore() { 25 | if (isDebuggingInChrome) { 26 | window.store = store; 27 | } 28 | 29 | return store; 30 | } 31 | 32 | module.exports = configureStore; 33 | -------------------------------------------------------------------------------- /js/reducers/location.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { Action } from '../actions/types'; 8 | import type { Location } from '../models/view'; 9 | 10 | export type State = { 11 | isInitalised: bool; 12 | data: Array; 13 | }; 14 | 15 | const initial: State = { 16 | isInitalised: false, 17 | data: [] 18 | }; 19 | 20 | function location(state: State = initial, action: Action): State { 21 | switch (action.type) { 22 | case 'LOCATION_INITIALISED': 23 | return { 24 | ...state, 25 | isInitalised: true 26 | }; 27 | case 'LOCATION_GET_ALL': 28 | return { 29 | ...state, 30 | data: action.data 31 | } 32 | default: 33 | return state; 34 | } 35 | } 36 | 37 | module.exports = location; 38 | -------------------------------------------------------------------------------- /ios/ReactWeather/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /js/dependencies/parallaxview/styles.js: -------------------------------------------------------------------------------- 1 | const StyleSheet = require('react-native').StyleSheet; 2 | 3 | const styles = StyleSheet.create({ 4 | container: { 5 | flex: 1, 6 | backgroundColor: 'transparent' 7 | }, 8 | parallaxHeaderContainer: { 9 | backgroundColor: 'transparent', 10 | overflow: 'hidden' 11 | }, 12 | parallaxHeader: { 13 | backgroundColor: 'transparent', 14 | overflow: 'hidden' 15 | }, 16 | backgroundImage: { 17 | position: 'absolute', 18 | backgroundColor: 'transparent', 19 | overflow: 'hidden', 20 | top: 0 21 | }, 22 | stickyHeader: { 23 | backgroundColor: 'transparent', 24 | position: 'absolute', 25 | overflow: 'hidden', 26 | top: 0, 27 | left: 0 28 | }, 29 | scrollView: { 30 | backgroundColor: 'transparent' 31 | } 32 | }); 33 | 34 | module.exports = styles; 35 | -------------------------------------------------------------------------------- /js/components/settings/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import { StyleSheet } from 'react-native'; 8 | 9 | const styles = StyleSheet.create({ 10 | button: { 11 | width: 200, 12 | height: 35, 13 | backgroundColor: '#007AFF', 14 | borderRadius: 2, 15 | flexDirection: 'row', 16 | justifyContent: 'center', 17 | alignItems: 'center' 18 | }, 19 | redButton: { 20 | width: 200, 21 | height: 35, 22 | backgroundColor: '#D0021B', 23 | borderRadius: 2, 24 | flexDirection: 'row', 25 | justifyContent: 'center', 26 | alignItems: 'center' 27 | }, 28 | buttonText: { 29 | color: '#fff' 30 | }, 31 | listItem: { 32 | backgroundColor: '#fff', 33 | borderColor: '#C8C7CC', 34 | borderBottomWidth: StyleSheet.hairlineWidth, 35 | } 36 | }); 37 | 38 | module.exports = styles; 39 | -------------------------------------------------------------------------------- /js/models/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | export type WeatherForecast = { 8 | day: string; 9 | forecast: string; 10 | low: string; 11 | high: string; 12 | icon: string; 13 | }; 14 | 15 | export type WeatherObservation = { 16 | location: string; 17 | forecast: string; 18 | feelsLike: string; 19 | current: string; 20 | low: string; 21 | high: string; 22 | icon: string; 23 | }; 24 | 25 | export type WeatherModel = { 26 | freshness: Date; 27 | observation: WeatherObservation; 28 | forecast: Array; 29 | }; 30 | 31 | export type Location = { 32 | name: string; 33 | postcode: string; 34 | state: string; 35 | openWeatherId: string; 36 | observation: WeatherObservation; 37 | }; 38 | 39 | export type Postcode = { 40 | name: string; 41 | postcode: number; 42 | state: string; 43 | } 44 | -------------------------------------------------------------------------------- /js/components/settings/section.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | } from 'react-native'; 12 | 13 | type Props = { 14 | children: any; 15 | style: any; 16 | }; 17 | 18 | class Section extends Component { 19 | props: Props; 20 | 21 | constructor(props: Props) { 22 | super(props); 23 | } 24 | 25 | render() { 26 | return ( 27 | 28 | { this.props.children } 29 | 30 | ); 31 | } 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | section: { 36 | backgroundColor: '#fff', 37 | borderColor: '#C8C7CC', 38 | borderTopWidth: StyleSheet.hairlineWidth, 39 | borderBottomWidth: StyleSheet.hairlineWidth 40 | }, 41 | }); 42 | 43 | module.exports = Section; -------------------------------------------------------------------------------- /ios/ReactWeatherTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /api/location.js: -------------------------------------------------------------------------------- 1 | { 2 | "message":"accurate", 3 | "cod":"200", 4 | "count":1, 5 | "list":[ 6 | { 7 | "id":2147714, 8 | "name":"Sydney", 9 | "coord":{ 10 | "lon":151.207321, 11 | "lat":-33.867851 12 | }, 13 | "main":{ 14 | "temp":13.84, 15 | "humidity":58, 16 | "pressure":1029, 17 | "temp_min":11.67, 18 | "temp_max":16.67 19 | }, 20 | "dt":1465979431, 21 | "wind":{ 22 | "speed":1.03, 23 | "gust":1.54, 24 | "deg":0 25 | }, 26 | "sys":{ 27 | "country":"AU" 28 | }, 29 | "clouds":{ 30 | "all":0 31 | }, 32 | "weather":[ 33 | { 34 | "id":800, 35 | "main":"Clear", 36 | "description":"Sky is Clear", 37 | "icon":"01n" 38 | } 39 | ] 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /js/components/settings/sectiontitle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | Text, 12 | } from 'react-native'; 13 | 14 | type Props = { 15 | text: string; 16 | }; 17 | 18 | class SectionTitle extends Component { 19 | props: Props; 20 | 21 | constructor(props: Props) { 22 | super(props); 23 | } 24 | 25 | render() { 26 | return ( 27 | 28 | { this.props.text } 29 | 30 | ); 31 | } 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | sectionTitle: { 36 | paddingLeft: 14, 37 | paddingBottom: 8 38 | }, 39 | sectionTitleText: { 40 | paddingTop: 36, 41 | color: '#6D6D72', 42 | fontSize: 12 43 | }, 44 | }); 45 | 46 | module.exports = SectionTitle; -------------------------------------------------------------------------------- /api/weather.json: -------------------------------------------------------------------------------- 1 | { 2 | "coord": { 3 | "lon": 149.11, 4 | "lat": -35.22 5 | }, 6 | "weather": [ 7 | { 8 | "id": 804, 9 | "main": "Clouds", 10 | "description": "overcast clouds", 11 | "icon": "04d" 12 | } 13 | ], 14 | "base": "stations", 15 | "main": { 16 | "temp": 21.34, 17 | "pressure": 1016, 18 | "humidity": 88, 19 | "temp_min": 19, 20 | "temp_max": 24 21 | }, 22 | "visibility": 16093, 23 | "wind": { 24 | "speed": 2.6, 25 | "deg": 200 26 | }, 27 | "clouds": { 28 | "all": 90 29 | }, 30 | "dt": 1463812680, 31 | "sys": { 32 | "type": 1, 33 | "id": 1688, 34 | "message": 0.014, 35 | "country": "AU", 36 | "sunrise": 1463777765, 37 | "sunset": 1463814240 38 | }, 39 | "id": 6619483, 40 | "name": "Kaleen", 41 | "cod": 200 42 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /js/components/loading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | Image, 12 | Text, 13 | } from 'react-native'; 14 | 15 | class Loading extends Component { 16 | render() { 17 | return ( 18 | 19 | 20 | Loading... 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | 28 | const styles = StyleSheet.create({ 29 | loadingView: { 30 | backgroundColor: '#fff', 31 | flex: 1 32 | }, 33 | loadingHeader: { 34 | height: 290, 35 | backgroundColor: '#589BC7', 36 | justifyContent: 'center', 37 | alignItems: 'center', 38 | }, 39 | loadingText: { 40 | color: '#fff', 41 | marginBottom: 18 42 | } 43 | }); 44 | 45 | module.exports = Loading; -------------------------------------------------------------------------------- /android/ReactWeather.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /js/dependencies/swipeout/styles.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var {StyleSheet} = React 3 | 4 | var styles = StyleSheet.create({ 5 | swipeout: { 6 | backgroundColor: '#dbddde', 7 | flex: 1, 8 | overflow: 'hidden', 9 | }, 10 | swipeoutBtnTouchable: { 11 | flex: 1, 12 | }, 13 | swipeoutBtn: { 14 | alignItems: 'center', 15 | backgroundColor: '#b6bec0', 16 | flex: 1, 17 | justifyContent: 'center', 18 | overflow: 'hidden', 19 | }, 20 | swipeoutBtnText: { 21 | color: '#fff', 22 | textAlign: 'center', 23 | }, 24 | swipeoutBtns: { 25 | bottom: 0, 26 | flex: 1, 27 | flexDirection: 'row', 28 | position: 'absolute', 29 | right: 0, 30 | top: 0, 31 | }, 32 | swipeoutContent: { 33 | flex: 1, 34 | }, 35 | colorDelete: { 36 | backgroundColor: '#fb3d38', 37 | }, 38 | colorPrimary: { 39 | backgroundColor: '#006fff' 40 | }, 41 | colorSecondary: { 42 | backgroundColor: '#fd9427' 43 | }, 44 | }) 45 | 46 | module.exports = styles -------------------------------------------------------------------------------- /js/realm/schema-v1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const Observation = { 8 | name: 'Observation', 9 | properties: { 10 | forecast: 'string', 11 | feelsLike: 'string', 12 | current: 'string', 13 | low: 'string', 14 | high: 'string', 15 | icon: 'string' 16 | } 17 | }; 18 | 19 | const Forecast = { 20 | name: 'Forecast', 21 | properties: { 22 | day: 'string', 23 | forecast: 'string', 24 | low: 'string', 25 | high: 'string', 26 | icon: 'string' 27 | } 28 | }; 29 | 30 | const Location = { 31 | name: 'Location', 32 | properties: { 33 | name: 'string', 34 | postcode: 'string', 35 | state: 'string', 36 | openWeatherId: 'string', 37 | freshness: 'date', 38 | observation: 'Observation', 39 | forecast: { 40 | type: 'list', 41 | objectType: 'Forecast' 42 | } 43 | } 44 | }; 45 | 46 | module.exports = { 47 | schema: [Observation, Forecast, Location], 48 | schemaVersion: 1, 49 | migration: () => {} 50 | }; 51 | -------------------------------------------------------------------------------- /js/reducers/weather.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { Action } from '../actions/types'; 8 | import type { WeatherModel } from '../models/view'; 9 | 10 | export type State = { 11 | isLoading: bool; 12 | isRefreshing: bool; 13 | data: Array; 14 | current: number; 15 | }; 16 | 17 | const initial: State = { 18 | isLoading: false, 19 | isRefreshing: false, 20 | data: [], 21 | current: 0 22 | }; 23 | 24 | function weather(state: State = initial, action: Action): State { 25 | switch (action.type) { 26 | case 'WEATHER_SET_LOADING': 27 | return { 28 | ...state, 29 | isLoading: true 30 | }; 31 | case 'WEATHER_SET_REFRESHING': 32 | return { 33 | ...state, 34 | isRefreshing: true 35 | }; 36 | case 'WEATHER_GET_ALL': 37 | return { 38 | ...state, 39 | isLoading: false, 40 | isRefreshing: false, 41 | data: action.data, 42 | }; 43 | default: 44 | return state; 45 | } 46 | } 47 | 48 | module.exports = weather; 49 | -------------------------------------------------------------------------------- /js/components/pager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View 11 | } from 'react-native'; 12 | 13 | import Icon from 'react-native-vector-icons/FontAwesome'; 14 | 15 | type Props = { 16 | current: number; 17 | count: number; 18 | }; 19 | 20 | class Pager extends Component { 21 | props: Props; 22 | 23 | render() { 24 | var items = new Array(this.props.count).fill(0); 25 | var dots = items.map((item, index) => { 26 | var key = `pager-dot-${index}`; 27 | var colour = index === this.props.current ? '#000000' : '#c7c7c7'; 28 | 29 | return ( 30 | 31 | ); 32 | }); 33 | 34 | return ( 35 | { dots } 36 | ); 37 | } 38 | } 39 | 40 | const styles = StyleSheet.create({ 41 | pager: { 42 | flexDirection: 'row' 43 | }, 44 | dot: { 45 | marginLeft: 3, 46 | marginRight: 3 47 | } 48 | }); 49 | 50 | module.exports = Pager; 51 | -------------------------------------------------------------------------------- /js/services/postcode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import realm from '../realm'; 8 | import { postcodeApiUrl } from '../config'; 9 | 10 | class PostcodeService { 11 | async getPostcodes(text: string) { 12 | var data = []; 13 | var postcodes = await this.getPostcodesFromApi(text); 14 | 15 | if (postcodes) { 16 | for (var i = 0; i < postcodes.length; i++) { 17 | var postcode = postcodes[i]; 18 | var item = { 19 | name: postcode.name, 20 | postcode: postcode.postcode, 21 | state: postcode.state.abbreviation 22 | } 23 | 24 | data.push(item); 25 | } 26 | } 27 | 28 | return data; 29 | } 30 | 31 | async getPostcodesFromApi(text: string) { 32 | var url = `${postcodeApiUrl}?q=${text}`; 33 | 34 | try { 35 | let response = await fetch(url); 36 | const result = await response.json(); 37 | 38 | return result; 39 | } catch(error) { 40 | // Handle error 41 | global.log(error); 42 | } 43 | } 44 | } 45 | 46 | module.exports = PostcodeService; 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sam Ilic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /js/realm/schema-v2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const Observation = { 8 | name: 'Observation', 9 | properties: { 10 | forecast: 'string', 11 | feelsLike: 'string', 12 | current: 'string', 13 | low: 'string', 14 | high: 'string', 15 | icon: 'string' 16 | } 17 | }; 18 | 19 | const Forecast = { 20 | name: 'Forecast', 21 | properties: { 22 | day: 'string', 23 | forecast: 'string', 24 | low: 'string', 25 | high: 'string', 26 | icon: 'string' 27 | } 28 | }; 29 | 30 | const Weather = { 31 | name: 'Weather', 32 | properties: { 33 | freshness: 'date', 34 | observation: 'Observation', 35 | forecast: { 36 | type: 'list', 37 | objectType: 'Forecast' 38 | } 39 | } 40 | } 41 | 42 | const Location = { 43 | name: 'Location', 44 | properties: { 45 | name: 'string', 46 | postcode: 'string', 47 | state: 'string', 48 | openWeatherId: 'string', 49 | weather: 'Weather' 50 | } 51 | }; 52 | 53 | module.exports = { 54 | schema: [Observation, Forecast, Weather, Location], 55 | schemaVersion: 2, 56 | migration: () => {} 57 | }; 58 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { Provider } from 'react-redux'; 9 | import App from './app'; 10 | 11 | import configureRealm from './realm/configure'; 12 | import configureStore from './store/configure'; 13 | import { setWeatherLoading, initaliseLocations } from './actions'; 14 | 15 | type State = { 16 | store: any; 17 | }; 18 | 19 | function setup() { 20 | 21 | class Root extends Component { 22 | state: State; 23 | 24 | constructor() { 25 | super(); 26 | configureRealm(); 27 | 28 | this.state = { 29 | store: configureStore() 30 | }; 31 | 32 | this.state.store.dispatch(setWeatherLoading()); 33 | this.state.store.dispatch(initaliseLocations()); 34 | } 35 | 36 | render() { 37 | return ( 38 | 39 | 40 | 41 | ); 42 | } 43 | } 44 | 45 | return Root; 46 | } 47 | 48 | global.log = (...args) => { 49 | console.log('------------------------------'); 50 | console.log(...args); 51 | console.log('------------------------------'); 52 | return args[args.length - 1]; 53 | }; 54 | 55 | module.exports = setup; 56 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactweather/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactweather; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import io.realm.react.RealmReactPackage; 5 | import com.oblador.vectoricons.VectorIconsPackage; 6 | import com.facebook.react.ReactPackage; 7 | import com.facebook.react.shell.MainReactPackage; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | public class MainActivity extends ReactActivity { 13 | 14 | /** 15 | * Returns the name of the main component registered from JavaScript. 16 | * This is used to schedule rendering of the component. 17 | */ 18 | @Override 19 | protected String getMainComponentName() { 20 | return "ReactWeather"; 21 | } 22 | 23 | /** 24 | * Returns whether dev mode should be enabled. 25 | * This enables e.g. the dev menu. 26 | */ 27 | @Override 28 | protected boolean getUseDeveloperSupport() { 29 | return BuildConfig.DEBUG; 30 | } 31 | 32 | /** 33 | * A list of packages used by the app. If the app uses additional views 34 | * or modules besides the default ones, add more packages here. 35 | */ 36 | @Override 37 | protected List getPackages() { 38 | return Arrays.asList( 39 | new MainReactPackage(), 40 | new RealmReactPackage(), 41 | new VectorIconsPackage() 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /js/actions/weather.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { Action } from './types'; 8 | 9 | import WeatherService from '../services/weather'; 10 | const service = new WeatherService(); 11 | 12 | const maxAgeInSeconds = (10 * 60); // 10 minutes 13 | 14 | function getAllWeather() { 15 | return (dispatch: any) => { 16 | service.getAllWeather(false).then( 17 | (result) => { 18 | dispatch({ 19 | type: 'WEATHER_GET_ALL', 20 | data: result 21 | }); 22 | 23 | var dates = result.map((item) => item.freshness); 24 | var freshness = new Date(Math.min(...dates)); 25 | if (getAgeInSeconds(freshness) > maxAgeInSeconds) { 26 | dispatch(setWeatherRefreshing()); 27 | dispatch(forceWeatherUpdate()); 28 | } 29 | } 30 | ); 31 | }; 32 | } 33 | 34 | async function forceWeatherUpdate() { 35 | var result = await service.getAllWeather(true); 36 | return { 37 | type: 'WEATHER_GET_ALL', 38 | data: result 39 | }; 40 | } 41 | 42 | function getAgeInSeconds(freshness: Date) { 43 | return Math.floor((Date.now() - freshness.getTime()) / 1000); 44 | } 45 | 46 | function setWeatherLoading() { 47 | return { 48 | type: 'WEATHER_SET_LOADING' 49 | }; 50 | } 51 | 52 | function setWeatherRefreshing() { 53 | return { 54 | type: 'WEATHER_SET_REFRESHING' 55 | }; 56 | } 57 | 58 | module.exports = { 59 | getAllWeather, 60 | setWeatherLoading, 61 | setWeatherRefreshing 62 | } 63 | -------------------------------------------------------------------------------- /js/components/forecastimage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { Image } from 'react-native'; 9 | 10 | function renderForecastImage(icon: string, width: number, height: number) { 11 | var image: number; 12 | switch (icon) { 13 | case '01d': 14 | case '01n': 15 | image = require('./img/sunny.png'); 16 | break; 17 | case '02d': 18 | case '02n': 19 | image = require('./img/sunny_s_cloudy.png'); 20 | break; 21 | case '03d': 22 | case '03n': 23 | image = require('./img/partly_cloudy.png'); 24 | break; 25 | case '04d': 26 | case '04n': 27 | image = require('./img/cloudy.png'); 28 | break; 29 | case '09d': 30 | case '09n': 31 | image = require('./img/rain.png'); 32 | break; 33 | case '10d': 34 | case '10n': 35 | image = require('./img/rain_s_cloudy.png'); 36 | break; 37 | case '11d': 38 | case '11n': 39 | image = require('./img/thunderstorms.png'); 40 | break; 41 | case '13d': 42 | case '13n': 43 | image = require('./img/snow.png'); 44 | break; 45 | case '50d': 46 | case '50n': 47 | image = require('./img/fog.png'); 48 | break; 49 | } 50 | 51 | const imageStyle = { 52 | width: width, 53 | height: height 54 | }; 55 | 56 | return ( 57 | 58 | ); 59 | } 60 | 61 | module.exports = renderForecastImage; 62 | -------------------------------------------------------------------------------- /js/components/settings/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | StatusBar, 12 | } from 'react-native'; 13 | 14 | import Icon from 'react-native-vector-icons/Ionicons'; 15 | 16 | import SectionTitle from './sectiontitle'; 17 | import Section from './section'; 18 | import Separator from './separator'; 19 | import NavigationButtonRow from './navigationbuttonrow'; 20 | import AboutSettings from './about'; 21 | import UserGuide from './userguide'; 22 | 23 | type Props = { 24 | navigator: any; 25 | }; 26 | 27 | class Settings extends Component { 28 | props: Props; 29 | 30 | constructor(props: Props) { 31 | super(props); 32 | 33 | (this: any).close = this.close.bind(this); 34 | } 35 | 36 | render() { 37 | return ( 38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | 46 |
47 |
48 | ); 49 | } 50 | 51 | close() { 52 | this.props.navigator.close(); 53 | } 54 | } 55 | 56 | const styles = StyleSheet.create({ 57 | container: { 58 | flex: 1, 59 | flexDirection: 'column', 60 | backgroundColor: '#f8f8f8', 61 | marginTop: 64, 62 | } 63 | }); 64 | 65 | module.exports = Settings; 66 | -------------------------------------------------------------------------------- /js/components/settings/navigationbuttonrow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | Text, 12 | StatusBar, 13 | TouchableHighlight 14 | } from 'react-native'; 15 | 16 | import Icon from 'react-native-vector-icons/Ionicons'; 17 | 18 | type Props = { 19 | text: string; 20 | navigator: any; 21 | component: any; 22 | style: any; 23 | }; 24 | 25 | class NavigationButtonRow extends Component { 26 | props: Props; 27 | 28 | constructor(props: Props) { 29 | super(props); 30 | } 31 | 32 | render() { 33 | return ( 34 | this.props.navigator.navigateTo({ 38 | title: this.props.text, 39 | component: this.props.component 40 | })}> 41 | 42 | { this.props.text } 43 | 44 | 45 | 46 | ); 47 | } 48 | } 49 | 50 | const styles = StyleSheet.create({ 51 | navigationButtonRow: { 52 | height: 44, 53 | flexDirection: 'column', 54 | justifyContent: 'center', 55 | backgroundColor: '#fff' 56 | }, 57 | navigationButtonView: { 58 | flexDirection: 'row', 59 | justifyContent: 'space-between', 60 | paddingRight: 14, 61 | paddingLeft: 14 62 | }, 63 | navigationButtonText: { 64 | fontSize: 16 65 | }, 66 | }); 67 | 68 | module.exports = NavigationButtonRow; -------------------------------------------------------------------------------- /js/components/settings/about.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | View, 11 | Text, 12 | TouchableHighlight, 13 | ActionSheetIOS 14 | } from 'react-native'; 15 | 16 | import { connect } from 'react-redux'; 17 | 18 | import defaultStyles from './styles'; 19 | import { clearAllLocationData } from '../../actions'; 20 | 21 | type Props = { 22 | dispatch: any; 23 | navigator: any; 24 | }; 25 | 26 | class AboutSettings extends Component { 27 | props: any; 28 | 29 | constructor(props: any) { 30 | super(props); 31 | 32 | (this: any).clearAllData = this.clearAllData.bind(this); 33 | } 34 | 35 | render() { 36 | return ( 37 | 38 | About 39 | 40 | Clear all data 41 | 42 | 43 | ); 44 | } 45 | 46 | clearAllData() { 47 | ActionSheetIOS.showActionSheetWithOptions({ 48 | message: 'Are you sure you want to delete all app data?', 49 | options: [ 50 | 'Delete', 51 | 'Cancel' 52 | ], 53 | cancelButtonIndex: 1, 54 | destructiveButtonIndex: 0, 55 | }, 56 | (buttonIndex) => { 57 | if(buttonIndex === 0) { 58 | this.props.dispatch(clearAllLocationData()); 59 | this.props.navigator.close(); 60 | } 61 | }); 62 | } 63 | } 64 | 65 | const styles = StyleSheet.create({ 66 | container: { 67 | } 68 | }); 69 | 70 | module.exports = connect()(AboutSettings); 71 | -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # To learn about Buck see [Docs](https://buckbuild.com/). 4 | # To run your application with Buck: 5 | # - install Buck 6 | # - `npm start` - to start the packager 7 | # - `cd android` 8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US` 9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 10 | # - `buck install -r android/app` - compile, install and run application 11 | # 12 | 13 | lib_deps = [] 14 | for jarfile in glob(['libs/*.jar']): 15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) 16 | lib_deps.append(':' + name) 17 | prebuilt_jar( 18 | name = name, 19 | binary_jar = jarfile, 20 | ) 21 | 22 | for aarfile in glob(['libs/*.aar']): 23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) 24 | lib_deps.append(':' + name) 25 | android_prebuilt_aar( 26 | name = name, 27 | aar = aarfile, 28 | ) 29 | 30 | android_library( 31 | name = 'all-libs', 32 | exported_deps = lib_deps 33 | ) 34 | 35 | android_library( 36 | name = 'app-code', 37 | srcs = glob([ 38 | 'src/main/java/**/*.java', 39 | ]), 40 | deps = [ 41 | ':all-libs', 42 | ':build_config', 43 | ':res', 44 | ], 45 | ) 46 | 47 | android_build_config( 48 | name = 'build_config', 49 | package = 'com.reactweather', 50 | ) 51 | 52 | android_resource( 53 | name = 'res', 54 | res = 'src/main/res', 55 | package = 'com.reactweather', 56 | ) 57 | 58 | android_binary( 59 | name = 'app', 60 | package_type = 'debug', 61 | manifest = 'src/main/AndroidManifest.xml', 62 | keystore = '//android/keystores:debug', 63 | deps = [ 64 | ':app-code', 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /js/actions/location.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import type { Action } from './types'; 8 | import { getAllWeather } from './weather'; 9 | 10 | import LocationService from '../services/location'; 11 | const service = new LocationService(); 12 | 13 | function initaliseLocations() { 14 | return (dispatch: any) => { 15 | service.initialise().then( 16 | (result) => { 17 | dispatch({ 18 | type: 'LOCATION_INITIALISED' 19 | }); 20 | dispatch(getAllWeather()); 21 | } 22 | ); 23 | }; 24 | } 25 | 26 | function getAllLocations() { 27 | var result = service.getAllLocations(); 28 | return { 29 | type: 'LOCATION_GET_ALL', 30 | data: result 31 | }; 32 | } 33 | 34 | function clearAllLocationData() { 35 | return (dispatch: any) => { 36 | service.clearAllData() 37 | dispatch({ 38 | type: 'LOCATION_CLEAR_ALL_DATA' 39 | }); 40 | dispatch(initaliseLocations()); 41 | }; 42 | } 43 | 44 | function deleteLocation(openWeatherId: string) { 45 | return (dispatch: any) => { 46 | service.deleteLocation(openWeatherId); 47 | dispatch({ 48 | type: 'LOCATION_DELETE' 49 | }); 50 | dispatch(getAllLocations()); 51 | dispatch(getAllWeather()); 52 | }; 53 | } 54 | 55 | function addLocation(name: string, postcode: string, state: string) { 56 | return (dispatch: any) => { 57 | service.addLocation(name, postcode, state).then( 58 | (result) => { 59 | dispatch({ 60 | type: 'LOCATION_ADD' 61 | }); 62 | dispatch(getAllLocations()); 63 | dispatch(getAllWeather()); 64 | } 65 | ); 66 | }; 67 | } 68 | 69 | module.exports = { 70 | initaliseLocations, 71 | getAllLocations, 72 | clearAllLocationData, 73 | deleteLocation, 74 | addLocation 75 | }; 76 | -------------------------------------------------------------------------------- /ios/ReactWeather/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | NSLocationWhenInUseUsageDescription 31 | 32 | UIAppFonts 33 | 34 | Entypo.ttf 35 | EvilIcons.ttf 36 | FontAwesome.ttf 37 | Foundation.ttf 38 | Ionicons.ttf 39 | MaterialIcons.ttf 40 | Octicons.ttf 41 | Zocial.ttf 42 | 43 | UILaunchStoryboardName 44 | LaunchScreen 45 | UIRequiredDeviceCapabilities 46 | 47 | armv7 48 | 49 | UISupportedInterfaceOrientations 50 | 51 | UIInterfaceOrientationPortrait 52 | 53 | UIViewControllerBasedStatusBarAppearance 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /js/components/forecast.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | } from 'react-native'; 13 | 14 | import ForecastItem from './forecastitem'; 15 | import type { WeatherForecast } from '../models/view'; 16 | 17 | type Props = { 18 | forecast: Array; 19 | }; 20 | 21 | class Forecast extends Component { 22 | props: Props; 23 | 24 | constructor(props: Props) { 25 | super(props); 26 | 27 | (this: any).renderForecastItems = this.renderForecastItems.bind(this); 28 | } 29 | 30 | render() { 31 | return ( 32 | 33 | 34 | { this.renderForecastItems() } 35 | 36 | 37 | ); 38 | } 39 | 40 | renderForecastItems() { 41 | return ( 42 | this.props.forecast.map((item, index) => { 43 | if (index === 0) { 44 | return null; 45 | }; 46 | 47 | if (index < this.props.forecast.length - 1) { 48 | var separator = { 49 | borderColor: '#F4F4F4', 50 | borderBottomWidth: StyleSheet.hairlineWidth, 51 | }; 52 | } 53 | 54 | return ( 55 | 56 | ); 57 | }) 58 | ); 59 | } 60 | } 61 | 62 | const styles = StyleSheet.create({ 63 | forecastView: { 64 | marginLeft: 5, 65 | marginRight: 5, 66 | flexDirection: 'row', 67 | borderColor: '#e2e2e2', 68 | backgroundColor: '#fff', 69 | borderWidth: 1, 70 | borderRadius: 3 71 | }, 72 | forecastList: { 73 | flex: 1, 74 | borderColor: '#E2E2E2', 75 | paddingLeft: 12, 76 | paddingRight: 12 77 | }, 78 | }); 79 | 80 | module.exports = Forecast; 81 | -------------------------------------------------------------------------------- /js/navigators/application.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Navigator 11 | } from 'react-native'; 12 | 13 | import Weather from '../components/weather'; 14 | import Settings from '../components/settings/settings'; 15 | import Locations from '../components/settings/locations'; 16 | import SettingsNavigator from '../navigators/settings'; 17 | 18 | type Props = {}; 19 | 20 | class ApplicationNavigator extends Component { 21 | constructor(props: Props) { 22 | super(props); 23 | 24 | (this: any).renderScene = this.renderScene.bind(this); 25 | } 26 | 27 | render() { 28 | return ( 29 | { 33 | return Navigator.SceneConfigs.FloatFromBottom; 34 | }} 35 | initialRoute={{}} 36 | renderScene={this.renderScene} 37 | /> 38 | ); 39 | } 40 | 41 | push(route: any) { 42 | this.refs.navigator.push(route); 43 | } 44 | 45 | pop() { 46 | this.refs.navigator.pop(); 47 | } 48 | 49 | renderScene(route: any, navigator: Navigator) { 50 | if (route.settings) { 51 | return ( 52 | 56 | ); 57 | } 58 | 59 | if (route.locations) { 60 | return ( 61 | 65 | ); 66 | } 67 | 68 | return ( 69 | 70 | ); 71 | } 72 | } 73 | 74 | const styles = StyleSheet.create({ 75 | container: { 76 | flex: 1, 77 | backgroundColor: '#000', 78 | } 79 | }); 80 | 81 | module.exports = ApplicationNavigator; 82 | -------------------------------------------------------------------------------- /js/components/forecastitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Image, 13 | } from 'react-native'; 14 | 15 | const renderForecastImage = require('./forecastimage'); 16 | 17 | type Props = { 18 | index: number; 19 | day: string; 20 | icon: string; 21 | low: string; 22 | high: string; 23 | separator: any; 24 | }; 25 | 26 | class ForecastItem extends Component { 27 | props: Props; 28 | 29 | constructor(props: Props) { 30 | super(props); 31 | } 32 | 33 | render() { 34 | var day = this.props.index === 1 ? 'Tomorrow' : this.props.day; 35 | return ( 36 | 37 | 38 | { day } 39 | 40 | 41 | { renderForecastImage(this.props.icon, 22, 22) } 42 | { this.props.low } 43 | { this.props.high } 44 | 45 | 46 | ); 47 | } 48 | } 49 | 50 | const styles = StyleSheet.create({ 51 | forecastItem: { 52 | paddingTop: 14, 53 | paddingBottom: 12, 54 | flexDirection: 'row' 55 | }, 56 | forecastItemDayView: { 57 | flex: 1 58 | }, 59 | forecastItemDataView: { 60 | flex: 1, 61 | flexDirection: 'row', 62 | justifyContent: 'flex-end' 63 | }, 64 | dayText: { 65 | fontSize: 16 66 | }, 67 | forecastItemTempLow: { 68 | textAlign: 'right', 69 | marginLeft: 16, 70 | width: 20, 71 | color: '#B0B5BF', 72 | fontSize: 16 73 | }, 74 | forecastItemTempHigh: { 75 | textAlign: 'right', 76 | marginLeft: 16, 77 | width: 20, 78 | fontSize: 16 79 | } 80 | }); 81 | 82 | module.exports = ForecastItem; -------------------------------------------------------------------------------- /js/navigators/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | NavigatorIOS, 11 | View 12 | } from 'react-native'; 13 | 14 | import Icon from 'react-native-vector-icons/Ionicons'; 15 | import Settings from '../components/settings/settings'; 16 | 17 | type Props = { 18 | route: any; 19 | navigator: any; 20 | }; 21 | type State = { 22 | backIcon: any; 23 | }; 24 | 25 | class SettingsNavigator extends Component { 26 | props: Props; 27 | state: State; 28 | 29 | constructor(props: Props) { 30 | super(props); 31 | 32 | this.state = { 33 | backIcon: null 34 | }; 35 | } 36 | 37 | componentWillMount() { 38 | Icon.getImageSource('ios-close', 40, '#007AFF').then( 39 | (source) => this.setState({ backIcon: source }) 40 | ); 41 | } 42 | 43 | render() { 44 | if (!this.state.backIcon) { 45 | return null; 46 | } 47 | 48 | return ( 49 | 62 | ); 63 | } 64 | 65 | navigateTo(route: any) { 66 | this.push({ 67 | ...route, 68 | passProps: { 69 | navigator: this 70 | } 71 | }); 72 | } 73 | 74 | push(route: any) { 75 | this.refs.navigator.push(route); 76 | } 77 | 78 | pop() { 79 | this.refs.navigator.pop(); 80 | } 81 | 82 | close() { 83 | this.props.navigator.pop(); 84 | } 85 | } 86 | 87 | const styles = StyleSheet.create({ 88 | container: { 89 | flex: 1 90 | }, 91 | content: { 92 | flex: 1 93 | } 94 | }); 95 | 96 | module.exports = SettingsNavigator; 97 | -------------------------------------------------------------------------------- /ios/ReactWeather/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTRootView.h" 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | NSURL *jsCodeLocation; 19 | 20 | /** 21 | * Loading JavaScript code - uncomment the one you want. 22 | * 23 | * OPTION 1 24 | * Load from development server. Start the server from the repository root: 25 | * 26 | * $ npm start 27 | * 28 | * To run on device, change `localhost` to the IP address of your computer 29 | * (you can get this by typing `ifconfig` into the terminal and selecting the 30 | * `inet` value under `en0:`) and make sure your computer and iOS device are 31 | * on the same Wi-Fi network. 32 | */ 33 | 34 | //jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; 35 | 36 | /** 37 | * OPTION 2 38 | * Load from pre-bundled file on disk. The static bundle is automatically 39 | * generated by the "Bundle React Native code and images" build step when 40 | * running the project on an actual device or running the project on the 41 | * simulator in the "Release" build configuration. 42 | */ 43 | 44 | jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 45 | 46 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 47 | moduleName:@"ReactWeather" 48 | initialProperties:nil 49 | launchOptions:launchOptions]; 50 | 51 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 52 | UIViewController *rootViewController = [UIViewController new]; 53 | rootViewController.view = rootView; 54 | self.window.rootViewController = rootViewController; 55 | [self.window makeKeyAndVisible]; 56 | return YES; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ios/ReactWeatherTests/ReactWeatherTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import "RCTLog.h" 14 | #import "RCTRootView.h" 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface ReactWeatherTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation ReactWeatherTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | 30 | # Do not strip any method/class that is annotated with @DoNotStrip 31 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 32 | -keepclassmembers class * { 33 | @com.facebook.proguard.annotations.DoNotStrip *; 34 | } 35 | 36 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 37 | void set*(***); 38 | *** get*(); 39 | } 40 | 41 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 42 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 43 | -keepclassmembers,includedescriptorclasses class * { native ; } 44 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 45 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 46 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 47 | 48 | -dontwarn com.facebook.react.** 49 | 50 | # okhttp 51 | 52 | -keepattributes Signature 53 | -keepattributes *Annotation* 54 | -keep class com.squareup.okhttp.** { *; } 55 | -keep interface com.squareup.okhttp.** { *; } 56 | -dontwarn com.squareup.okhttp.** 57 | 58 | # okio 59 | 60 | -keep class sun.misc.Unsafe { *; } 61 | -dontwarn java.nio.file.* 62 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 63 | -dontwarn okio.** 64 | -------------------------------------------------------------------------------- /js/components/footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Image, 13 | TouchableHighlight 14 | } from 'react-native'; 15 | 16 | import Icon from 'react-native-vector-icons/Ionicons'; 17 | 18 | import Pager from './pager'; 19 | import Settings from './settings/settings'; 20 | 21 | type Props = { 22 | current: number; 23 | count: number; 24 | navigator: any; 25 | }; 26 | 27 | class Footer extends Component { 28 | constructor(props: Props) { 29 | super(props); 30 | 31 | (this: any).navigateToSettings = this.navigateToSettings.bind(this); 32 | (this: any).navigateToLocations = this.navigateToLocations.bind(this); 33 | } 34 | 35 | render() { 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | } 54 | 55 | navigateToSettings() { 56 | this.props.navigator.push({ 57 | settings: true 58 | }); 59 | } 60 | 61 | navigateToLocations() { 62 | this.props.navigator.push({ 63 | locations: true 64 | }); 65 | } 66 | } 67 | 68 | const styles = StyleSheet.create({ 69 | footer: { 70 | position: 'absolute', 71 | left: 0, 72 | right: 0, 73 | bottom: 0, 74 | height: 40, 75 | paddingLeft: 15, 76 | paddingRight: 15, 77 | flexDirection: 'row', 78 | justifyContent: 'center', 79 | alignItems: 'center', 80 | backgroundColor: '#f8f8f8', 81 | borderTopWidth: StyleSheet.hairlineWidth, 82 | borderColor: '#c4c4c4' 83 | }, 84 | left: { 85 | flex: 1, 86 | }, 87 | center: { 88 | flex: 1, 89 | justifyContent: 'center', 90 | alignItems: 'center' 91 | }, 92 | right: { 93 | flex: 1, 94 | justifyContent: 'flex-end', 95 | flexDirection: 'row' 96 | } 97 | }); 98 | 99 | module.exports = Footer; 100 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /js/components/weather.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | 'use strict'; 6 | 7 | import StatusBar from 'StatusBar'; 8 | import React, { Component } from 'react'; 9 | import { 10 | StyleSheet, 11 | View, 12 | Animated, 13 | Image, 14 | Text, 15 | Dimensions 16 | } from 'react-native'; 17 | 18 | import { connect } from 'react-redux'; 19 | import Swiper from '../dependencies/swiper'; 20 | 21 | import Header from './header'; 22 | import Footer from './footer'; 23 | import Forecast from './forecast'; 24 | import Loading from './loading'; 25 | 26 | import type { WeatherModel } from '../models/view' 27 | 28 | const SCREEN_WIDTH = Dimensions.get('window').width; 29 | 30 | type Props = { 31 | dispatch: any; 32 | isLoading: bool; 33 | weather: Array; 34 | count: number; 35 | navigator: any; 36 | }; 37 | 38 | type State = { 39 | shift: Animated.Value; 40 | current: number; 41 | }; 42 | 43 | class Weather extends Component { 44 | props: Props; 45 | state: State; 46 | 47 | constructor(props: Props) { 48 | super(props); 49 | 50 | this.state = { 51 | shift: new Animated.Value(0), 52 | current: 0 53 | }; 54 | 55 | (this: any).onScroll = this.onScroll.bind(this); 56 | (this: any).onSelectedIndexChange = this.onSelectedIndexChange.bind(this); 57 | } 58 | 59 | render() { 60 | if (this.props.isLoading === true) { 61 | return ( 62 | 63 | ); 64 | } 65 | 66 | var forecastItems = this.props.weather.map((item, index) => { 67 | return ( 68 | 69 | ); 70 | }); 71 | 72 | var swiper = ( 73 | 79 | { forecastItems } 80 | 81 | ); 82 | 83 | return ( 84 | 85 |
86 | { swiper } 87 |
88 |