├── .watchmanconfig ├── .gitattributes ├── .babelrc ├── app.json ├── assets ├── back.png ├── logo.png ├── back@2x.png ├── back@3x.png ├── close.png ├── loginID.png ├── loginPW.png ├── logo@2x.png ├── logo@3x.png ├── splash.jpg ├── vitalON.png ├── calendar.png ├── close@2x.png ├── close@3x.png ├── profileON.png ├── symptomON.png ├── vitalOFF.png ├── calendar@2x.png ├── calendar@3x.png ├── loginID@2x.png ├── loginID@3x.png ├── loginPW@2x.png ├── loginPW@3x.png ├── profileOFF.png ├── profileON@2x.png ├── profileON@3x.png ├── symptomOFF.png ├── symptomON@2x.png ├── symptomON@3x.png ├── vitalOFF@2x.png ├── vitalOFF@3x.png ├── vitalON@2x.png ├── vitalON@3x.png ├── bluetoothSync.png ├── calendarScroll.png ├── profileOFF@2x.png ├── profileOFF@3x.png ├── symptomOFF@2x.png ├── symptomOFF@3x.png ├── bluetoothSync@2x.png ├── bluetoothSync@3x.png ├── calendarScroll@2x.png └── calendarScroll@3x.png ├── README ├── Landing.jpg └── Pages.jpg ├── android ├── app │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ └── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Entypo.ttf │ │ │ │ ├── Feather.ttf │ │ │ │ ├── Zocial.ttf │ │ │ │ ├── EvilIcons.ttf │ │ │ │ ├── Foundation.ttf │ │ │ │ ├── Ionicons.ttf │ │ │ │ ├── Octicons.ttf │ │ │ │ ├── FontAwesome.ttf │ │ │ │ ├── MaterialIcons.ttf │ │ │ │ ├── SimpleLineIcons.ttf │ │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ │ └── MaterialCommunityIcons.ttf │ │ │ ├── java │ │ │ └── com │ │ │ │ └── signalus │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── AndroidManifest.xml │ ├── proguard-rules.pro │ ├── BUCK │ └── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── keystores │ ├── debug.keystore.properties │ └── BUCK ├── gradle.properties ├── build.gradle ├── settings.gradle ├── gradlew.bat └── gradlew ├── ios ├── Signalus │ ├── Images.xcassets │ │ ├── Contents.json │ │ ├── Image.imageset │ │ │ ├── logo.png │ │ │ ├── logo@2x.png │ │ │ ├── logo@3x.png │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 앱 아이콘 배수 40.png │ │ │ ├── 앱 아이콘 배수 58.png │ │ │ ├── 앱 아이콘 배수 60.png │ │ │ ├── 앱 아이콘 배수 80.png │ │ │ ├── 앱 아이콘 배수 87.png │ │ │ ├── 앱 아이콘 배수 1024.png │ │ │ ├── 앱 아이콘 배수 120.png │ │ │ ├── 앱 아이콘 배수 180.png │ │ │ ├── 앱 아이콘 배수 120-1.png │ │ │ └── Contents.json │ ├── AppDelegate.h │ ├── main.m │ ├── Info.plist │ ├── Base.lproj │ │ └── LaunchScreen.xib │ └── AppDelegate.m ├── Signalus-Bridging-Header.h ├── ReactNativeBle.swift ├── SignalusTests │ ├── Info.plist │ └── SignalusTests.m ├── Signalus-tvOSTests │ └── Info.plist ├── View.xib ├── Signalus-tvOS │ └── Info.plist └── Signalus.xcodeproj │ └── xcshareddata │ └── xcschemes │ ├── Signalus-tvOS.xcscheme │ └── Signalus.xcscheme ├── .buckconfig ├── index.js ├── src ├── reducers │ ├── bluetooth │ │ ├── actionTypes.js │ │ ├── actions.js │ │ └── reducer.js │ ├── index.js │ ├── auth │ │ ├── actionTypes.js │ │ ├── API.js │ │ ├── reducer.js │ │ └── actions.js │ ├── contact │ │ ├── actionTypes.js │ │ ├── API.js │ │ ├── reducer.js │ │ └── actions.js │ ├── nav │ │ ├── actionTypes.js │ │ └── reducer.js │ └── symptom │ │ ├── actionTypes.js │ │ ├── API.js │ │ ├── reducer.js │ │ └── actions.js ├── styles │ ├── CalendarStyle.js │ ├── SplashStyle.js │ ├── SettingStyle.js │ ├── PickerModal.js │ ├── SymptomLogStyle.js │ ├── SymptomDetailPatchStyle.js │ ├── VitalStyle.js │ ├── SymptomDetailUserStyle.js │ ├── SymptomStyle.js │ ├── ConsentStyle.js │ ├── RegisterStyle.js │ ├── SignInStyle.js │ ├── ProfileStyle.js │ └── BluetoothStyle.js ├── constants │ ├── dimens.js │ ├── model.js │ ├── color.js │ ├── utils.js │ └── string.js ├── components │ ├── CustomHeaderButton.js │ ├── CustomFAB.js │ ├── CustomSimpleTouchableText.js │ ├── CustomDevicesItem.js │ ├── CustomFilledButton.js │ ├── CustomBorderedButton.js │ ├── CustomChart.js │ ├── CustomContactItem.js │ ├── CustomSymptomCheckBox.js │ ├── CustomSymptomItem.js │ ├── CustomCheckBox.js │ ├── CustomFormPicker.js │ ├── CustomHealthStatusBar.js │ ├── CustomPicker.js │ └── CustomFormInput.js ├── navigators │ ├── AppNavigator.js │ ├── RootNavigator.js │ └── MainTabNavigator.js └── screens │ ├── SymptomDetailUserScreen.js │ ├── ConsentHTMLScreen.js │ ├── CalendarScreen.js │ ├── SplashScreen.js │ ├── SymptomDetailPatchScreen.js │ ├── ConsentScreen.js │ ├── SymptomScreen.js │ ├── SignInScreen.js │ ├── SettingScreen.js │ ├── BluetoothScreen.js │ └── SymptomLogScreen.js ├── App.js ├── .gitignore ├── package.json ├── README.md └── .flowconfig /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Signalus", 3 | "displayName": "Signalus" 4 | } -------------------------------------------------------------------------------- /assets/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/back.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/logo.png -------------------------------------------------------------------------------- /README/Landing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/README/Landing.jpg -------------------------------------------------------------------------------- /README/Pages.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/README/Pages.jpg -------------------------------------------------------------------------------- /assets/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/back@2x.png -------------------------------------------------------------------------------- /assets/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/back@3x.png -------------------------------------------------------------------------------- /assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/close.png -------------------------------------------------------------------------------- /assets/loginID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/loginID.png -------------------------------------------------------------------------------- /assets/loginPW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/loginPW.png -------------------------------------------------------------------------------- /assets/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/logo@2x.png -------------------------------------------------------------------------------- /assets/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/logo@3x.png -------------------------------------------------------------------------------- /assets/splash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/splash.jpg -------------------------------------------------------------------------------- /assets/vitalON.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/vitalON.png -------------------------------------------------------------------------------- /assets/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/calendar.png -------------------------------------------------------------------------------- /assets/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/close@2x.png -------------------------------------------------------------------------------- /assets/close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/close@3x.png -------------------------------------------------------------------------------- /assets/profileON.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/profileON.png -------------------------------------------------------------------------------- /assets/symptomON.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/symptomON.png -------------------------------------------------------------------------------- /assets/vitalOFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/vitalOFF.png -------------------------------------------------------------------------------- /assets/calendar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/calendar@2x.png -------------------------------------------------------------------------------- /assets/calendar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/calendar@3x.png -------------------------------------------------------------------------------- /assets/loginID@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/loginID@2x.png -------------------------------------------------------------------------------- /assets/loginID@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/loginID@3x.png -------------------------------------------------------------------------------- /assets/loginPW@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/loginPW@2x.png -------------------------------------------------------------------------------- /assets/loginPW@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/loginPW@3x.png -------------------------------------------------------------------------------- /assets/profileOFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/profileOFF.png -------------------------------------------------------------------------------- /assets/profileON@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/profileON@2x.png -------------------------------------------------------------------------------- /assets/profileON@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/profileON@3x.png -------------------------------------------------------------------------------- /assets/symptomOFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/symptomOFF.png -------------------------------------------------------------------------------- /assets/symptomON@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/symptomON@2x.png -------------------------------------------------------------------------------- /assets/symptomON@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/symptomON@3x.png -------------------------------------------------------------------------------- /assets/vitalOFF@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/vitalOFF@2x.png -------------------------------------------------------------------------------- /assets/vitalOFF@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/vitalOFF@3x.png -------------------------------------------------------------------------------- /assets/vitalON@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/vitalON@2x.png -------------------------------------------------------------------------------- /assets/vitalON@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/vitalON@3x.png -------------------------------------------------------------------------------- /assets/bluetoothSync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/bluetoothSync.png -------------------------------------------------------------------------------- /assets/calendarScroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/calendarScroll.png -------------------------------------------------------------------------------- /assets/profileOFF@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/profileOFF@2x.png -------------------------------------------------------------------------------- /assets/profileOFF@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/profileOFF@3x.png -------------------------------------------------------------------------------- /assets/symptomOFF@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/symptomOFF@2x.png -------------------------------------------------------------------------------- /assets/symptomOFF@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/symptomOFF@3x.png -------------------------------------------------------------------------------- /assets/bluetoothSync@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/bluetoothSync@2x.png -------------------------------------------------------------------------------- /assets/bluetoothSync@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/bluetoothSync@3x.png -------------------------------------------------------------------------------- /assets/calendarScroll@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/calendarScroll@2x.png -------------------------------------------------------------------------------- /assets/calendarScroll@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/assets/calendarScroll@3x.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Signalus 3 | 4 | -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Feather.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/Feather.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /.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/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /ios/Signalus-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/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/TaeJoongYoon/Signalus/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/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/Image.imageset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/Image.imageset/logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-xxxhdpi/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 | -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/Image.imageset/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/Image.imageset/logo@2x.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/Image.imageset/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/Image.imageset/logo@3x.png -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 40.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 58.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 60.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 80.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 87.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 1024.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 120.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 180.png -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaeJoongYoon/Signalus/HEAD/ios/Signalus/Images.xcassets/AppIcon.appiconset/앱 아이콘 배수 120-1.png -------------------------------------------------------------------------------- /android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import {AppRegistry} from 'react-native'; 4 | import App from './App'; 5 | import {name as appName} from './app.json'; 6 | AppRegistry.registerComponent(appName, () => App); 7 | -------------------------------------------------------------------------------- /ios/ReactNativeBle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReactNativeBle.swift 3 | // Signalus 4 | // 5 | // Created by Tae joong Yoon on 25/08/2018. 6 | // Copyright © 2018 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip 6 | -------------------------------------------------------------------------------- /src/reducers/bluetooth/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const CONNECT_PENDING = 'CONNECT_PENDING'; 2 | export const CONNECT_SUCCESS = 'CONNECT_SUCCESS'; 3 | export const CONNECT_FAILURE = 'CONNECT_FAILURE'; 4 | 5 | export const DISCONNECT_PENDING = 'DISCONNECT_PENDING'; 6 | export const DISCONNECT_SUCCESS = 'DISCONNECT_SUCCESS'; 7 | export const DISCONNECT_FAILURE = 'DISCONNECT_FAILURE'; 8 | -------------------------------------------------------------------------------- /src/styles/CalendarStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT } from '../constants/dimens'; 3 | import { backgroundColor } from '../constants/color'; 4 | 5 | const styles = StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | backgroundColor: backgroundColor, 9 | }, 10 | calendar: { 11 | }, 12 | }); 13 | 14 | export default styles; -------------------------------------------------------------------------------- /ios/Signalus/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow *window; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /src/styles/SplashStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT } from '../constants/dimens'; 3 | 4 | const styles = StyleSheet.create({ 5 | container: { 6 | flex: 1, 7 | justifyContent: 'center', 8 | alignItems: 'center', 9 | }, 10 | image: { 11 | flex:1 , 12 | width: WIDTH, 13 | height: HEIGHT, 14 | } 15 | }); 16 | 17 | export default styles; -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import auth from './auth/reducer'; 3 | import nav from './nav/reducer'; 4 | import bluetooth from './bluetooth/reducer'; 5 | import contact from './contact/reducer'; 6 | import symptom from './symptom/reducer'; 7 | 8 | const reducer = combineReducers({ 9 | auth, 10 | nav, 11 | bluetooth, 12 | contact, 13 | symptom, 14 | }); 15 | 16 | export default reducer; -------------------------------------------------------------------------------- /ios/Signalus/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/constants/dimens.js: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native'; 2 | 3 | export const WIDTH = Dimensions.get('window').width; 4 | export const HEIGHT = Dimensions.get('window').height; 5 | 6 | export const defaultMinLength = 8; 7 | export const defaultMaxLength = 20; 8 | 9 | export const checkboxSize = 30; 10 | 11 | export const modalOffset = 0; 12 | export const modalDuration = 300; 13 | 14 | export const borderRadius = 10; 15 | export const dividerViewHeight = 10; -------------------------------------------------------------------------------- /android/app/src/main/java/com/signalus/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.signalus; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "Signalus"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "filename" : "logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "filename" : "logo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /src/reducers/auth/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const REGISTER_PENDING = 'REGISTER_PENDING'; 2 | export const REGISTER_SUCCESS = 'REGISTER_SUCCESS'; 3 | export const REGISTER_FAILURE = 'REGISTER_FAILURE'; 4 | 5 | export const SIGNIN_PENDING = 'SIGNIN_PENDING'; 6 | export const SIGNIN_SUCCESS = 'SIGNIN_SUCCESS'; 7 | export const SIGNIN_FAILURE = 'SIGNIN_FAILURE'; 8 | 9 | export const WITHDRAWAL_PENDING = 'WITHDRAWAL_PENDING'; 10 | export const WITHDRAWAL_SUCCESS = 'WITHDRAWAL_SUCCESS'; 11 | export const WITHDRAWAL_FAILURE = 'WITHDRAWAL_FAILURE'; -------------------------------------------------------------------------------- /src/constants/model.js: -------------------------------------------------------------------------------- 1 | import { ModeAge, ModeSex, ModeHeight, ModeWeight } from './string'; 2 | 3 | export const ages = [{value: ModeAge},{value:'10'},{value:'20'},{value:'30'},{value:'40'},{value:'50'},{value:'60'},{value:'70'}]; 4 | export const sex = [{value: ModeSex},{value:'F'},{value:'M'}]; 5 | export const heights = [{value: ModeHeight},{value:'160'},{value:'165'},{value:'170'},{value:'175'},{value:'180'},{value:'185'},{value:'190'}]; 6 | export const weights = [{value: ModeWeight},{value:'45'},{value:'50'},{value:'55'},{value:'60'},{value:'65'},{value:'70'}]; -------------------------------------------------------------------------------- /src/constants/color.js: -------------------------------------------------------------------------------- 1 | export const mainColor = '#1c9bff'; 2 | export const highlightColor = '#e9f0f4'; 3 | export const placeholderText = '#899ba3'; 4 | export const disable = '#dde3e8'; 5 | export const divider = '#4b565e'; 6 | export const patch = '#72e9ff'; 7 | 8 | export const activeTintColor = 'blue'; 9 | export const inactiveTintColor = 'gray'; 10 | export const backgroundColor = 'white'; 11 | export const headerTintColor = 'white'; 12 | 13 | export const pickerBackgroundColor = 'white'; 14 | export const closeButtonColor = '#027afe'; 15 | export const closeButtonBorderColor = '#e2e2e2'; -------------------------------------------------------------------------------- /src/components/CustomHeaderButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { TouchableOpacity, Image } from 'react-native'; 3 | 4 | class CustomHeaderButton extends Component { 5 | constructor(props){ 6 | super(props) 7 | } 8 | 9 | render(){ 10 | const { onPress, style, source } = this.props; 11 | 12 | return( 13 | 15 | 19 | 20 | ); 21 | } 22 | } 23 | 24 | export default CustomHeaderButton; -------------------------------------------------------------------------------- /src/reducers/contact/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const CONTACT_REGISTER_PENDING = 'CONTACT_REGISTER_PENDING'; 2 | export const CONTACT_REGISTER_SUCCESS = 'CONTACT_REGISTER_SUCCESS'; 3 | export const CONTACT_REGISTER_FAILURE = 'CONTACT_REGISTER_FAILURE'; 4 | 5 | export const CONTACT_DELETE_PENDING = 'CONTACT_DELETE_PENDING'; 6 | export const CONTACT_DELETE_SUCCESS = 'CONTACT_DELETE_SUCCESS'; 7 | export const CONTACT_DELETE_FAILURE = 'CONTACT_DELETE_FAILURE'; 8 | 9 | export const CONTACT_GET_PENDING = 'CONTACT_GET_PENDING'; 10 | export const CONTACT_GET_SUCCESS = 'CONTACT_GET_SUCCESS'; 11 | export const CONTACT_GET_FAILURE = 'CONTACT_GET_FAILURE'; -------------------------------------------------------------------------------- /src/navigators/AppNavigator.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { reduxifyNavigator, createReactNavigationReduxMiddleware } from 'react-navigation-redux-helpers'; 3 | 4 | import RootNavigator from './RootNavigator'; 5 | 6 | const middleware = createReactNavigationReduxMiddleware( 7 | 'root', 8 | state => state.nav 9 | ); 10 | 11 | const AppWithNavigationState = reduxifyNavigator(RootNavigator, 'root'); 12 | 13 | const mapStateToProps = state => ({ 14 | state: state.nav, 15 | }); 16 | 17 | const AppNavigator = connect(mapStateToProps)(AppWithNavigationState); 18 | 19 | export { RootNavigator, AppNavigator, middleware }; -------------------------------------------------------------------------------- /src/reducers/nav/actionTypes.js: -------------------------------------------------------------------------------- 1 | // SIGNED 2 | export const NOT_SIGNED = 'NOT_SIGNED'; 3 | export const ON_CONSENT = 'ON_CONSENT'; 4 | export const ON_REGISTER = 'ON_REGISTER'; 5 | export const SIGNED = 'SIGNED'; 6 | export const SIGNOUT = 'SIGNOUT'; 7 | 8 | // BLUETOOTH CONNECTED 9 | export const NOT_CONNECTED = 'NOT_CONNECTED'; 10 | export const CONNECTED = 'CONNECTED'; 11 | 12 | // MAIN PAGE 13 | export const ON_CALENDAR = 'ON_CALENDAR'; 14 | export const ON_LOG = 'ON_LOG'; 15 | export const LOGGED = 'LOGGED'; 16 | export const ON_DETAIL_PATCH = 'ON_DETAIL_PATCH'; 17 | export const ON_DETAIL_USER = 'ON_DETAIL_USER'; 18 | export const ON_SETTING = 'ON_SETTING'; -------------------------------------------------------------------------------- /src/components/CustomFAB.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import FAB from 'react-native-fab'; 3 | 4 | class CustomFAB extends Component { 5 | constructor(props){ 6 | super(props) 7 | } 8 | 9 | render(){ 10 | const {buttonColor, iconTextColor, onClickAction, visible, iconTextComponent } = this.props; 11 | return( 12 | 20 | ); 21 | } 22 | } 23 | 24 | export default CustomFAB; -------------------------------------------------------------------------------- /src/components/CustomSimpleTouchableText.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity, Text } from 'react-native'; 3 | 4 | class CustomSimpleTouchableText extends Component { 5 | constructor(props){ 6 | super(props) 7 | } 8 | 9 | render(){ 10 | const { style, text, title, onPress } = this.props; 11 | 12 | return( 13 | 14 | 15 | 17 | {title} 18 | 19 | 20 | 21 | ); 22 | } 23 | } 24 | 25 | export default CustomSimpleTouchableText; -------------------------------------------------------------------------------- /src/styles/SettingStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { normalize } from '../constants/utils'; 3 | import { WIDTH, HEIGHT } from '../constants/dimens'; 4 | import { backgroundColor, divider, mainColor } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | padding: 30, 12 | }, 13 | textView: { 14 | marginBottom: 20, 15 | }, 16 | text: { 17 | color: divider, 18 | fontSize: normalize(18), 19 | }, 20 | highlightText: { 21 | color: mainColor, 22 | fontSize: normalize(18), 23 | fontWeight: 'bold', 24 | }, 25 | }); 26 | 27 | export default styles; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/styles/PickerModal.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | import { pickerBackgroundColor, closeButtonColor, closeButtonBorderColor } from '../constants/color'; 3 | 4 | var styles = StyleSheet.create({ 5 | background: { 6 | backgroundColor: pickerBackgroundColor 7 | }, 8 | closeButtonContainer: { 9 | flexDirection: 'row', 10 | justifyContent: 'flex-end', 11 | borderTopColor: closeButtonBorderColor, 12 | borderTopWidth: 1, 13 | borderBottomColor: closeButtonBorderColor, 14 | borderBottomWidth:1 15 | }, 16 | closeButton: { 17 | paddingRight:10, 18 | paddingTop:10, 19 | paddingBottom:10 20 | }, 21 | closeButtonText: { 22 | color: closeButtonColor 23 | }, 24 | }); 25 | 26 | export default styles; -------------------------------------------------------------------------------- /src/reducers/symptom/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SYMPTOM_ADD_PENDING = 'SYMPTOM_ADD_PENDING'; 2 | export const SYMPTOM_ADD_SUCCESS = 'SYMPTOM_ADD_SUCCESS'; 3 | export const SYMPTOM_ADD_FAILURE = 'SYMPTOM_ADD_FAILURE'; 4 | 5 | export const SYMPTOM_GET_PENDING = 'SYMPTOM_GET_PENDING'; 6 | export const SYMPTOM_GET_SUCCESS = 'SYMPTOM_GET_SUCCESS'; 7 | export const SYMPTOM_GET_FAILURE = 'SYMPTOM_GET_FAILURE'; 8 | 9 | export const SIGNAL_ADD_PENDING = 'SIGNAL_ADD_PENDING'; 10 | export const SIGNAL_ADD_SUCCESS = 'SIGNAL_ADD_SUCCESS'; 11 | export const SIGNAL_ADD_FAILURE = 'SIGNAL_ADD_FAILURE'; 12 | 13 | export const SIGNAL_GET_PENDING = 'SIGNAL_GET_PENDING'; 14 | export const SIGNAL_GET_SUCCESS = 'SIGNAL_GET_SUCCESS'; 15 | export const SIGNAL_GET_FAILURE = 'SIGNAL_GET_FAILURE'; 16 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { createStore, applyMiddleware } from 'redux'; 4 | import ReduxThunk from 'redux-thunk'; 5 | import promiseMiddleware from 'redux-promise-middleware'; 6 | 7 | import reducers from './src/reducers'; 8 | import { AppNavigator, middleware } from './src/navigators/AppNavigator'; 9 | 10 | const customizedPromiseMiddleware = promiseMiddleware({ 11 | promiseTypeSuffixes: ['LOADING', 'SUCCESS', 'FAILURE'] 12 | }); 13 | const store = createStore(reducers, applyMiddleware(middleware,ReduxThunk)); 14 | 15 | export default class App extends Component { 16 | render() { 17 | return ( 18 | 19 | 20 | 21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /src/components/CustomDevicesItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity, Text } from 'react-native'; 3 | import { Divider } from 'react-native-elements'; 4 | 5 | class CustomDevicesItem extends Component { 6 | constructor(props){ 7 | super(props) 8 | } 9 | 10 | render(){ 11 | const { style, text, divider, title, onPress } = this.props; 12 | 13 | return( 14 | 15 | 16 | 18 | {title} 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | } 26 | 27 | export default CustomDevicesItem; -------------------------------------------------------------------------------- /src/components/CustomFilledButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity, Text } from 'react-native'; 3 | 4 | class CustomFilledButton extends Component { 5 | constructor(props){ 6 | super(props) 7 | } 8 | 9 | render(){ 10 | const { style, title, disabled, onPress } = this.props; 11 | 12 | return( 13 | 14 | 17 | 20 | {title} 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | 28 | export default CustomFilledButton; -------------------------------------------------------------------------------- /src/reducers/bluetooth/actions.js: -------------------------------------------------------------------------------- 1 | import { AsyncStorage } from 'react-native'; 2 | import { 3 | CONNECT_PENDING, CONNECT_SUCCESS, CONNECT_FAILURE, 4 | DISCONNECT_PENDING, DISCONNECT_SUCCESS, DISCONNECT_FAILURE, 5 | } from './actionTypes'; 6 | 7 | export const connect = (device) => dispatch => { 8 | const pending = () => { return {type: CONNECT_PENDING}}; 9 | const success = (device) => { return {type: CONNECT_SUCCESS, payload: device}}; 10 | const failure = (error) => { return {type: CONNECT_FAILURE, payload: error}}; 11 | 12 | dispatch(pending()); // Dispatch Action Starting 13 | 14 | dispatch(success(device)) // return ConnectedDevice 15 | saveData(device) 16 | } 17 | 18 | 19 | const saveData = (device) => { 20 | AsyncStorage.multiSet([ 21 | ["device", device], 22 | ]) 23 | } -------------------------------------------------------------------------------- /src/components/CustomBorderedButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity, Text } from 'react-native'; 3 | import { mainColor } from '../constants/color'; 4 | 5 | class CustomBorderedButton extends Component { 6 | constructor(props){ 7 | super(props) 8 | } 9 | 10 | render(){ 11 | const { style, title, onPress } = this.props; 12 | 13 | return( 14 | 15 | 17 | 20 | {title} 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | 28 | export default CustomBorderedButton; -------------------------------------------------------------------------------- /src/styles/SymptomLogStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT } from '../constants/dimens'; 3 | import { backgroundColor, device, divider } from '../constants/color'; 4 | 5 | const styles = StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | flexDirection: 'column', 9 | backgroundColor: backgroundColor, 10 | }, 11 | header:{ 12 | flexDirection: 'column', 13 | backgroundColor: backgroundColor, 14 | paddingTop: 20, 15 | paddingLeft: 20, 16 | paddingRight: 20, 17 | }, 18 | date:{ 19 | color: divider, 20 | fontSize: 16, 21 | fontWeight: 'bold', 22 | marginBottom: 5, 23 | }, 24 | title:{ 25 | color: divider, 26 | fontSize: 16, 27 | marginBottom: 20, 28 | }, 29 | }); 30 | 31 | export default styles; -------------------------------------------------------------------------------- /src/styles/SymptomDetailPatchStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { normalize } from '../constants/utils'; 3 | import { WIDTH, HEIGHT } from '../constants/dimens'; 4 | import { backgroundColor, divider } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | padding: 30, 12 | }, 13 | date: { 14 | fontSize: normalize(20), 15 | color: divider, 16 | marginBottom: 15, 17 | }, 18 | time: { 19 | fontSize: normalize(15), 20 | color: divider, 21 | }, 22 | ChartTitle:{ 23 | fontSize: normalize(15), 24 | fontWeight: 'bold', 25 | color: divider, 26 | paddingBottom: 10, 27 | }, 28 | chart: { 29 | height: HEIGHT*0.3 + 60 30 | }, 31 | }); 32 | 33 | export default styles; -------------------------------------------------------------------------------- /ios/SignalusTests/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 | -------------------------------------------------------------------------------- /ios/Signalus-tvOSTests/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 | -------------------------------------------------------------------------------- /src/styles/VitalStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT, borderRadius } from '../constants/dimens'; 3 | import { backgroundColor, mainColor, highlightColor } from '../constants/color'; 4 | 5 | const styles = StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | flexDirection: 'column', 9 | backgroundColor: backgroundColor, 10 | paddingBottom: 15, 11 | }, 12 | card: { 13 | flex: 1, 14 | }, 15 | cardView:{ 16 | flex: 1, 17 | justifyContent: 'center', 18 | borderColor: 'white', 19 | borderRadius: borderRadius, 20 | shadowColor: '#000', 21 | shadowOffset: { 22 | width: 0, 23 | height: 2, 24 | }, 25 | shadowOpacity: 0.25, 26 | shadowRadius: 3.84, 27 | elevation: 5, 28 | }, 29 | bpmStatus:{ 30 | flexDirection: 'row', 31 | alignItems:'center', 32 | height:HEIGHT * 0.14 33 | }, 34 | }); 35 | 36 | export default styles; -------------------------------------------------------------------------------- /src/styles/SymptomDetailUserStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { normalize } from '../constants/utils'; 3 | import { WIDTH, HEIGHT } from '../constants/dimens'; 4 | import { backgroundColor, divider } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | padding: 30, 12 | }, 13 | date: { 14 | fontSize: normalize(20), 15 | color: divider, 16 | marginBottom: 15, 17 | }, 18 | time: { 19 | fontSize: normalize(15), 20 | color: divider, 21 | }, 22 | symptomListTitle:{ 23 | fontSize: normalize(15), 24 | fontWeight: 'bold', 25 | color: divider, 26 | paddingBottom: 10, 27 | }, 28 | symptomList:{ 29 | fontSize: normalize(12), 30 | color: divider, 31 | paddingLeft: 10, 32 | paddingTop: 5, 33 | }, 34 | }); 35 | 36 | export default styles; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/reducers/bluetooth/reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | CONNECT_PENDING, CONNECT_SUCCESS, CONNECT_FAILURE, 3 | DISCONNECT_PENDING, DISCONNECT_SUCCESS, DISCONNECT_FAILURE, 4 | } from './actionTypes'; 5 | 6 | const initialState = { 7 | pending: false, 8 | error: false, 9 | isConnected: false, 10 | device: {}, 11 | } 12 | 13 | export default bluetooth = (state=initialState, action) => { 14 | switch(action.type){ 15 | 16 | case CONNECT_PENDING: 17 | return { 18 | ...state, 19 | pending: true, 20 | error: false 21 | }; 22 | 23 | case CONNECT_SUCCESS: 24 | return { 25 | ...state, 26 | pending: false, 27 | isConnected: true, 28 | device: action.payload 29 | }; 30 | 31 | case DISCONNECT_PENDING: 32 | return { 33 | ...state, 34 | pending: true, 35 | error: false, 36 | }; 37 | 38 | case DISCONNECT_SUCCESS: 39 | return { 40 | ...state, 41 | pending: false, 42 | isConnected: false, 43 | }; 44 | 45 | default: 46 | return state; 47 | } 48 | } -------------------------------------------------------------------------------- /src/components/CustomChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import LinearGradient from 'react-native-linear-gradient' 3 | import { LineChart } from 'react-native-svg-charts' 4 | import * as shape from 'd3-shape' 5 | import { HEIGHT } from '../constants/dimens'; 6 | import { mainColor, highlightColor } from '../constants/color' 7 | 8 | class CustomChart extends React.PureComponent { 9 | constructor(props){ 10 | super(props) 11 | } 12 | 13 | render() { 14 | const { data } = this.props; 15 | 16 | return ( 17 | 22 | 32 | 33 | 34 | ) 35 | } 36 | } 37 | 38 | export default CustomChart -------------------------------------------------------------------------------- /.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/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # private 59 | /src/constants/URL.js 60 | /src/constants/html.js 61 | *.html 62 | -------------------------------------------------------------------------------- /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 | maven { 7 | url 'https://maven.google.com/' 8 | name 'Google' 9 | } 10 | } 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:2.3.3' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | mavenLocal() 22 | jcenter() 23 | maven { 24 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 25 | url "$rootDir/../node_modules/react-native/android" 26 | } 27 | maven { 28 | url 'https://maven.google.com/' 29 | name 'Google' 30 | } 31 | } 32 | } 33 | 34 | ext { 35 | buildToolsVersion = "26.0.3" 36 | minSdkVersion = 18 37 | compileSdkVersion = 26 38 | targetSdkVersion = 26 39 | supportLibVersion = "26.1.0" 40 | } 41 | -------------------------------------------------------------------------------- /src/components/CustomContactItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity, Text } from 'react-native'; 3 | import { Icon } from 'react-native-elements'; 4 | import { normalize } from '../constants/utils'; 5 | 6 | 7 | class CustomContactItem extends Component { 8 | constructor(props){ 9 | super(props) 10 | } 11 | 12 | render(){ 13 | const { style, name, phoneNumber, onPress } = this.props; 14 | 15 | return( 16 | 17 | 18 | {name} 19 | {phoneNumber} 20 | 21 | 22 | 26 | 27 | 28 | ); 29 | } 30 | } 31 | 32 | export default CustomContactItem; -------------------------------------------------------------------------------- /ios/View.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/CustomSymptomCheckBox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Text, TouchableOpacity } from 'react-native'; 3 | import { CheckBox } from 'react-native-elements' 4 | import { checkboxSize } from '../constants/dimens' 5 | import { mainColor, divider } from '../constants/color'; 6 | 7 | class CustomSymptomCheckBox extends Component { 8 | constructor(props){ 9 | super(props) 10 | } 11 | 12 | render(){ 13 | const { checked, onPress, onTouch, title } = this.props; 14 | return( 15 | 16 | 27 | 29 | 30 | {title} 31 | 32 | 33 | 34 | ); 35 | } 36 | } 37 | 38 | export default CustomSymptomCheckBox; -------------------------------------------------------------------------------- /src/reducers/contact/API.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { CONTACT_URL } from '../../constants/URL'; 3 | 4 | export const registerAPI = (id, name, phoneNumber, token) => { // Register 5 | let data = JSON.stringify({ 6 | id: id, 7 | name: name, 8 | phoneNumber: phoneNumber, 9 | }); 10 | 11 | let headerConfig = { 12 | headers: { 13 | 'Content-Type': 'application/json;charset=UTF-8', 14 | 'x-access-token': token, 15 | } 16 | }; 17 | 18 | return axios.put(`${CONTACT_URL}/add`,data, headerConfig); 19 | } 20 | 21 | export const deleteAPI = (id, phoneNumber, token) => { // Delete 22 | let data = { 23 | 'data': { 24 | id: id, 25 | phoneNumber: phoneNumber, 26 | } 27 | } 28 | 29 | let headerConfig = { 30 | headers: { 31 | 'Content-Type': 'application/json;charset=UTF-8', 32 | 'x-access-token': token, 33 | } 34 | }; 35 | 36 | let config = Object.assign({}, data, headerConfig); 37 | 38 | return axios.delete(`${CONTACT_URL}/delete`, config); 39 | } 40 | 41 | export const getContactsAPI = (id, token) => { // Get Contacts 42 | 43 | let headerConfig = { 44 | headers: { 45 | 'Content-Type': 'application/json;charset=UTF-8', 46 | 'x-access-token': token, 47 | } 48 | }; 49 | 50 | return axios.get(`${CONTACT_URL}/list/${id}`, headerConfig); 51 | } -------------------------------------------------------------------------------- /src/styles/SymptomStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { normalize } from '../constants/utils'; 3 | import { WIDTH, HEIGHT, borderRadius } from '../constants/dimens'; 4 | import { backgroundColor, divider, mainColor, disable, highlightColor, placeholderText, patch } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | paddingLeft: 10, 12 | paddingRight: 10, 13 | }, 14 | itemView: { 15 | flexDirection: 'row', 16 | justifyContent: 'space-between', 17 | height: HEIGHT * 0.08, 18 | alignItems: 'center', 19 | paddingLeft: 20, 20 | paddingRight: 20, 21 | marginTop: 15, 22 | marginBottom: 15, 23 | }, 24 | fromPatch:{ 25 | color: patch, 26 | fontSize: normalize(13), 27 | marginBottom: 10, 28 | }, 29 | fromUser:{ 30 | color: placeholderText, 31 | fontSize: normalize(13), 32 | marginBottom: 10, 33 | }, 34 | titleStyle:{ 35 | color: divider, 36 | fontSize: normalize(16), 37 | fontWeight: 'bold', 38 | marginBottom: 10, 39 | }, 40 | dateStyle:{ 41 | color: divider, 42 | fontSize: normalize(16), 43 | }, 44 | divider:{ 45 | backgroundColor: disable, 46 | }, 47 | }); 48 | 49 | export default styles; -------------------------------------------------------------------------------- /src/components/CustomSymptomItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity, Text } from 'react-native'; 3 | import { Divider } from 'react-native-elements'; 4 | import { LabelSymptomFromPatch, LabelSymptomFromUser } from '../constants/string'; 5 | 6 | class CustomSymptomItem extends Component { 7 | constructor(props){ 8 | super(props) 9 | } 10 | 11 | render(){ 12 | const { style, type, typeStyle, titleStyle, dateStyle, divider, title, date, onPress } = this.props; 13 | 14 | return( 15 | 16 | 17 | 18 | 19 | 20 | {type == "patch" ? LabelSymptomFromPatch : LabelSymptomFromUser} 21 | 22 | 23 | {title} 24 | 25 | 26 | 27 | 28 | 29 | {date} 30 | 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | } 38 | } 39 | 40 | export default CustomSymptomItem; -------------------------------------------------------------------------------- /src/navigators/RootNavigator.js: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from 'react-navigation'; 2 | 3 | import SplashScreen from '../screens/SplashScreen'; // Splash 4 | import SignInScreen from '../screens/SignInScreen'; // Sign 5 | import ConsentScreen from '../screens/ConsentScreen'; // Consent 6 | import ConsentHTMLScreen from '../screens/ConsentHTMLScreen'; // Consent HTML 7 | import RegisterScreen from '../screens/RegisterScreen'; // Register 8 | import BluetoothScreen from '../screens/BluetoothScreen'; // Bluetooth 9 | import MainScreen from './MainTabNavigator'; // Main 10 | 11 | import { headerTintColor, mainColor } from '../constants/color'; 12 | 13 | const RootNavigator = createStackNavigator({ 14 | Splash: { screen: SplashScreen }, 15 | SignIn: { screen: SignInScreen}, 16 | Consent: { screen: ConsentScreen}, 17 | ConsentHTML: { screen: ConsentHTMLScreen}, 18 | Register: { screen: RegisterScreen }, 19 | Bluetooth: { screen: BluetoothScreen }, 20 | Main: { screen: MainScreen }, 21 | },{ 22 | initialRouteName: 'Splash', 23 | headerMode: 'screen', 24 | navigationOptions: { 25 | title: 'Signalus', 26 | headerStyle: { 27 | backgroundColor: mainColor, 28 | borderBottomWidth: 0, 29 | }, 30 | headerTintColor: headerTintColor, 31 | headerTitleStyle: { 32 | fontWeight: 'bold', 33 | }, 34 | }, 35 | }); 36 | 37 | export default RootNavigator; -------------------------------------------------------------------------------- /src/reducers/auth/API.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { AUTH_URL } from '../../constants/URL'; 3 | 4 | export const registerAPI = (id, password, age, gender, height, weight) => { // Register 5 | let data = JSON.stringify({ 6 | id: id, 7 | password: password, 8 | age: age, 9 | gender: gender, 10 | height: height, 11 | weight: weight 12 | }); 13 | 14 | let headerConfig = { 15 | headers: { 16 | 'Content-Type': 'application/json;charset=UTF-8', 17 | } 18 | }; 19 | 20 | return axios.post(`${AUTH_URL}/signup`,data, headerConfig); 21 | } 22 | 23 | export const loginAPI = (id, password) => { // Login 24 | let data = JSON.stringify({ 25 | id: id, 26 | password: password 27 | }); 28 | 29 | let headerConfig = { 30 | headers: { 31 | 'Content-Type': 'application/json;charset=UTF-8', 32 | } 33 | }; 34 | 35 | return axios.post(`${AUTH_URL}/signin`,data, headerConfig); 36 | } 37 | 38 | export const withdrawAPI = (id, token) => { // Withdraw 39 | let data = { 40 | 'data': { 41 | id: id 42 | } 43 | } 44 | 45 | let headerConfig = { 46 | headers: { 47 | 'Content-Type': 'application/json;charset=UTF-8', 48 | 'x-access-token': token, 49 | } 50 | }; 51 | 52 | let config = Object.assign({}, data, headerConfig); 53 | 54 | return axios.delete(`${AUTH_URL}/withdrawal`, config); 55 | } -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Signalus' 2 | include ':react-native-fs' 3 | project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android') 4 | include ':react-native-notifications' 5 | project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android') 6 | include ':react-native-sms' 7 | project(':react-native-sms').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sms/android') 8 | include ':react-native-contacts' 9 | project(':react-native-contacts').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-contacts/android') 10 | include ':react-native-linear-gradient' 11 | project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android') 12 | include ':react-native-ble-plx' 13 | project(':react-native-ble-plx').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-ble-plx/android') 14 | include ':react-native-svg' 15 | project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android') 16 | include ':react-native-vector-icons' 17 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 18 | 19 | include ':app' 20 | -------------------------------------------------------------------------------- /src/styles/ConsentStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT, borderRadius, dividerViewHeight } from '../constants/dimens'; 3 | import { backgroundColor, divider, mainColor, disable } from '../constants/color'; 4 | 5 | const styles = StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | flexDirection: 'column', 9 | backgroundColor: backgroundColor, 10 | paddingTop: 20, 11 | }, 12 | dividerView: { 13 | flexDirection: 'column', 14 | alignItems: 'center', 15 | justifyContent: 'center', 16 | height: dividerViewHeight, 17 | }, 18 | divider: { 19 | width: WIDTH * 0.9, 20 | backgroundColor: divider, 21 | }, 22 | agreeButtonView: { 23 | marginTop: HEIGHT * 0.05, 24 | flexDirection: 'column', 25 | alignItems: 'center', 26 | justifyContent: 'center', 27 | } 28 | , 29 | agreeEnable: { 30 | flexDirection: 'column', 31 | alignItems: 'center', 32 | justifyContent: 'center', 33 | backgroundColor: mainColor, 34 | width: WIDTH * 0.85, 35 | height: HEIGHT * 0.08, 36 | borderRadius:borderRadius, 37 | }, 38 | agreeDisable: { 39 | flexDirection: 'column', 40 | alignItems: 'center', 41 | justifyContent: 'center', 42 | backgroundColor: disable, 43 | width: WIDTH * 0.85, 44 | height: HEIGHT * 0.08, 45 | borderRadius:borderRadius, 46 | }, 47 | }); 48 | 49 | export default styles; -------------------------------------------------------------------------------- /src/styles/RegisterStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { normalize } from '../constants/utils'; 3 | import { WIDTH, HEIGHT, borderRadius } from '../constants/dimens'; 4 | import { backgroundColor, mainColor, disable } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | }, 12 | title:{ 13 | paddingTop: 35, 14 | paddingLeft: 20, 15 | paddingBottom: 20, 16 | fontSize: normalize(16), 17 | fontWeight: 'bold', 18 | }, 19 | contents: { 20 | flex: 1, 21 | flexDirection: 'column', 22 | alignItems: 'center', 23 | }, 24 | input: { 25 | alignItems: 'center', 26 | width: WIDTH * 0.85, 27 | height: HEIGHT * 0.09, 28 | }, 29 | registerEnable: { 30 | flexDirection: 'column', 31 | alignItems: 'center', 32 | justifyContent: 'center', 33 | backgroundColor: mainColor, 34 | width: WIDTH * 0.85, 35 | height: HEIGHT * 0.08, 36 | borderRadius: borderRadius, 37 | }, 38 | registerDisable: { 39 | flexDirection: 'column', 40 | alignItems: 'center', 41 | justifyContent: 'center', 42 | backgroundColor: disable, 43 | width: WIDTH * 0.85, 44 | height: HEIGHT * 0.08, 45 | borderRadius: borderRadius, 46 | }, 47 | error: { 48 | color: 'red', 49 | }, 50 | }); 51 | 52 | export default styles; -------------------------------------------------------------------------------- /src/reducers/auth/reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | REGISTER_PENDING, REGISTER_SUCCESS, REGISTER_FAILURE, 3 | SIGNIN_PENDING, SIGNIN_SUCCESS, SIGNIN_FAILURE, 4 | WITHDRAWAL_PENDING, WITHDRAWAL_SUCCESS, WITHDRAWAL_FAILURE 5 | } from './actionTypes'; 6 | import { SIGNOUT, ON_CONSENT } from '../nav/actionTypes'; 7 | 8 | const initialState = { 9 | pending: false, 10 | error: false, 11 | isLoggedIn: false, 12 | } 13 | 14 | export default auth = (state=initialState, action) => { 15 | switch(action.type){ 16 | 17 | case REGISTER_PENDING: 18 | case SIGNIN_PENDING: 19 | case WITHDRAWAL_PENDING: 20 | return { 21 | ...state, 22 | pending: true, 23 | error: false 24 | }; 25 | 26 | case SIGNIN_SUCCESS: 27 | case REGISTER_SUCCESS: 28 | const {msg, token} = action.payload.data; 29 | return { 30 | ...state, 31 | pending: false, 32 | isLoggedIn: true 33 | }; 34 | 35 | case SIGNIN_FAILURE: 36 | case REGISTER_FAILURE: 37 | case WITHDRAWAL_FAILURE: 38 | return { 39 | ...state, 40 | pending: false, 41 | error: true 42 | }; 43 | 44 | case WITHDRAWAL_SUCCESS: 45 | case SIGNOUT: 46 | return { 47 | ...state, 48 | isLoggedIn: false 49 | }; 50 | 51 | case ON_CONSENT: 52 | state = initialState; 53 | return state; 54 | 55 | default: 56 | return state; 57 | } 58 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Signalus", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.18.1", 11 | "buffer": "^5.2.1", 12 | "lodash": ">=4.17.11", 13 | "react": "16.4.1", 14 | "react-addons-update": "^15.6.2", 15 | "react-native": "^0.56.0", 16 | "react-native-ble-plx": "^0.10.0", 17 | "react-native-calendars": "^1.21.0", 18 | "react-native-contacts": "^2.2.4", 19 | "react-native-elements": "^1.0.0-beta5", 20 | "react-native-fab": "^1.0.8", 21 | "react-native-fs": "^2.12.1", 22 | "react-native-linear-gradient": "^2.4.2", 23 | "react-native-notifications": "^1.1.21", 24 | "react-native-progress": "^3.5.0", 25 | "react-native-sms": "^1.8.0", 26 | "react-native-svg": "^6.5.2", 27 | "react-native-svg-charts": "^5.2.0", 28 | "react-native-vector-icons": "^5.0.0", 29 | "react-navigation": "^2.8.0", 30 | "react-navigation-redux-helpers": "^2.0.3", 31 | "react-redux": "^5.0.7", 32 | "redux": "^4.0.0", 33 | "redux-promise-middleware": "^5.1.1", 34 | "redux-thunk": "^2.3.0" 35 | }, 36 | "devDependencies": { 37 | "babel-jest": "23.4.0", 38 | "babel-preset-react-native": "^5", 39 | "jest": "23.4.1", 40 | "react-test-renderer": "16.4.1" 41 | }, 42 | "jest": { 43 | "preset": "react-native" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/CustomCheckBox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Text, TouchableOpacity } from 'react-native'; 3 | import { CheckBox } from 'react-native-elements' 4 | import { checkboxSize } from '../constants/dimens' 5 | import { placeholderText, mainColor } from '../constants/color'; 6 | 7 | class CustomCheckBox extends Component { 8 | constructor(props){ 9 | super(props) 10 | } 11 | 12 | render(){ 13 | const { checked, onPress, onTouch, title, underline } = this.props; 14 | return( 15 | 16 | 27 | 29 | 33 | {title} 34 | 35 | 36 | 37 | ); 38 | } 39 | } 40 | 41 | export default CustomCheckBox; -------------------------------------------------------------------------------- /src/components/CustomFormPicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, TouchableOpacity } from 'react-native'; 3 | import { Input } from 'react-native-elements' 4 | 5 | import { mainColor, placeholderText } from '../constants/color' 6 | 7 | class CustomFormPicker extends Component { 8 | constructor(props){ 9 | super(props) 10 | this.state= { 11 | focus: false, 12 | changed: false, 13 | } 14 | } 15 | 16 | _onFocus = () => { 17 | this.setState({focus: true}) 18 | } 19 | 20 | _onBlur = () => { 21 | this.setState({focus: false}) 22 | } 23 | 24 | render(){ 25 | const { style, onPress, placeholder,value } = this.props; 26 | customValue = value ? String(value) : '' 27 | 28 | return( 29 | 30 | 31 | this._onFocus()} 34 | onBlur={ () => this._onBlur() } 35 | inputContainerStyle={{borderBottomColor: this.state.focus ? mainColor : placeholderText}} 36 | inputStyle={{color: placeholderText}} 37 | pointerEvents='none' 38 | placeholder={placeholder} 39 | editable={false} 40 | autoCorrect={false} 41 | errorStyle={{ color: 'red' }} 42 | value={customValue} 43 | /> 44 | 45 | 46 | ); 47 | } 48 | } 49 | 50 | export default CustomFormPicker; -------------------------------------------------------------------------------- /ios/Signalus/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "앱 아이콘 배수 40.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "앱 아이콘 배수 60.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "앱 아이콘 배수 58.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "앱 아이콘 배수 87.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "앱 아이콘 배수 80.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "앱 아이콘 배수 120-1.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "앱 아이콘 배수 120.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "앱 아이콘 배수 180.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "1024x1024", 53 | "idiom" : "ios-marketing", 54 | "filename" : "앱 아이콘 배수 1024.png", 55 | "scale" : "1x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /src/reducers/contact/reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | CONTACT_REGISTER_PENDING, CONTACT_REGISTER_SUCCESS, CONTACT_REGISTER_FAILURE, 3 | CONTACT_DELETE_PENDING, CONTACT_DELETE_SUCCESS, CONTACT_DELETE_FAILURE, 4 | CONTACT_GET_PENDING, CONTACT_GET_SUCCESS, CONTACT_GET_FAILURE 5 | } from './actionTypes'; 6 | 7 | const initialState = { 8 | pending: false, 9 | error: false, 10 | isRegisterd: false, 11 | isDeleted: false, 12 | contacts: [], 13 | } 14 | 15 | export default contact = (state=initialState, action) => { 16 | switch(action.type){ 17 | 18 | case CONTACT_REGISTER_PENDING: 19 | case CONTACT_DELETE_PENDING: 20 | case CONTACT_GET_PENDING: 21 | return { 22 | ...state, 23 | pending: true, 24 | error: false 25 | }; 26 | 27 | case CONTACT_REGISTER_SUCCESS: 28 | return { 29 | ...state, 30 | pending: false, 31 | isRegisterd: true 32 | }; 33 | 34 | case CONTACT_DELETE_SUCCESS: 35 | return { 36 | ...state, 37 | pending: false, 38 | isDeleted: true 39 | }; 40 | 41 | case CONTACT_GET_SUCCESS: 42 | const {contacts} = action.payload.data; 43 | return{ 44 | ...state, 45 | pending: false, 46 | isRegisterd: false, 47 | isDeleted: false, 48 | contacts: contacts 49 | }; 50 | 51 | case CONTACT_REGISTER_FAILURE: 52 | case CONTACT_DELETE_FAILURE: 53 | case CONTACT_GET_FAILURE: 54 | return { 55 | ...state, 56 | pending: false, 57 | error: true 58 | }; 59 | 60 | default: 61 | return state; 62 | } 63 | } -------------------------------------------------------------------------------- /src/reducers/symptom/API.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { SYMPTOM_URL, SIGNAL_URL } from '../../constants/URL'; 3 | 4 | export const addSymptomAPI = (id, symptoms, time, type, token) => { // Register 5 | let data = JSON.stringify({ 6 | id: id, 7 | symptoms: symptoms, 8 | time: time, 9 | type: type, 10 | }); 11 | 12 | let headerConfig = { 13 | headers: { 14 | 'Content-Type': 'application/json;charset=UTF-8', 15 | 'x-access-token': token, 16 | } 17 | }; 18 | 19 | return axios.put(`${SYMPTOM_URL}/add`,data, headerConfig); 20 | } 21 | 22 | export const getSymptomAPI = (id, token) => { // Get Contacts 23 | 24 | let headerConfig = { 25 | headers: { 26 | 'Content-Type': 'application/json;charset=UTF-8', 27 | 'x-access-token': token, 28 | } 29 | }; 30 | 31 | return axios.get(`${SYMPTOM_URL}/list/${id}`, headerConfig); 32 | } 33 | 34 | export const addSignalAPI = (id, signalFile, time, token) => { // Register 35 | let data = new FormData(); 36 | data.append("id", id); 37 | data.append("signalFile", { uri: signalFile, name: `${time}.txt`, type: 'text/plain' }); 38 | data.append("time", time); 39 | 40 | let headerConfig = { 41 | headers: { 42 | 'Content-Type': 'multipart/form-data', 43 | 'x-access-token': token, 44 | } 45 | }; 46 | 47 | return axios.post(`${SIGNAL_URL}`,data, headerConfig); 48 | } 49 | 50 | export const getSignalAPI = (id, time, token) => { // Get Contacts 51 | 52 | let headerConfig = { 53 | headers: { 54 | 'Content-Type': 'application/json;charset=UTF-8', 55 | 'x-access-token': token, 56 | } 57 | }; 58 | 59 | return axios.get(`${SIGNAL_URL}/${id}/${time}`, headerConfig); 60 | } -------------------------------------------------------------------------------- /src/styles/SignInStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT, borderRadius } from '../constants/dimens'; 3 | import { backgroundColor, mainColor, disable } from '../constants/color'; 4 | 5 | const styles = StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | flexDirection: 'column', 9 | alignItems: 'center', 10 | backgroundColor: backgroundColor, 11 | }, 12 | image: { 13 | marginTop: '8%', 14 | width: WIDTH * 0.3, 15 | }, 16 | input: { 17 | alignItems: 'center', 18 | width: WIDTH * 0.85, 19 | height: HEIGHT * 0.1, 20 | }, 21 | icon: { 22 | width: 20, 23 | }, 24 | loginEnable: { 25 | flexDirection: 'column', 26 | alignItems: 'center', 27 | justifyContent: 'center', 28 | backgroundColor: mainColor, 29 | width: WIDTH * 0.85, 30 | height: HEIGHT * 0.08, 31 | borderRadius: borderRadius, 32 | }, 33 | loginDisable: { 34 | flexDirection: 'column', 35 | alignItems: 'center', 36 | justifyContent: 'center', 37 | backgroundColor: disable, 38 | width: WIDTH * 0.85, 39 | height: HEIGHT * 0.08, 40 | borderRadius: borderRadius, 41 | }, 42 | find:{ 43 | flexDirection: 'column', 44 | alignItems: 'center', 45 | justifyContent: 'center', 46 | height: HEIGHT * 0.08, 47 | }, 48 | register: { 49 | marginTop: HEIGHT * 0.04, 50 | flexDirection: 'column', 51 | alignItems: 'center', 52 | justifyContent: 'center', 53 | width: WIDTH * 0.85, 54 | height: HEIGHT * 0.08, 55 | borderRadius: borderRadius, 56 | borderWidth: 1, 57 | borderColor: mainColor 58 | }, 59 | error: { 60 | color: 'red', 61 | } 62 | }); 63 | 64 | export default styles; -------------------------------------------------------------------------------- /ios/Signalus-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/reducers/symptom/reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | SYMPTOM_ADD_PENDING, SYMPTOM_ADD_SUCCESS, SYMPTOM_ADD_FAILURE, 3 | SYMPTOM_GET_PENDING, SYMPTOM_GET_SUCCESS, SYMPTOM_GET_FAILURE, 4 | SIGNAL_ADD_PENDING, SIGNAL_ADD_SUCCESS, SIGNAL_ADD_FAILURE, 5 | SIGNAL_GET_PENDING, SIGNAL_GET_SUCCESS, SIGNAL_GET_FAILURE, 6 | } from './actionTypes'; 7 | 8 | const initialState = { 9 | pending: false, 10 | error: false, 11 | isRegisterd: false, 12 | symptoms: [], 13 | signal: "", 14 | } 15 | 16 | export default symptom = (state=initialState, action) => { 17 | switch(action.type){ 18 | 19 | case SIGNAL_ADD_PENDING: 20 | case SIGNAL_GET_PENDING: 21 | case SYMPTOM_ADD_PENDING: 22 | case SYMPTOM_GET_PENDING: 23 | return { 24 | ...state, 25 | pending: true, 26 | error: false 27 | }; 28 | 29 | case SIGNAL_ADD_SUCCESS: 30 | case SYMPTOM_ADD_SUCCESS: 31 | return { 32 | ...state, 33 | pending: false, 34 | isRegisterd: true 35 | }; 36 | 37 | case SYMPTOM_GET_SUCCESS: 38 | const { symptom_list } = action.payload.data; 39 | return{ 40 | ...state, 41 | pending: false, 42 | isRegisterd: false, 43 | symptoms: symptom_list, 44 | }; 45 | 46 | case SIGNAL_GET_SUCCESS: 47 | return{ 48 | ...state, 49 | pending: false, 50 | isRegisterd: false, 51 | signal: action.payload.data, 52 | }; 53 | 54 | case SIGNAL_ADD_FAILURE: 55 | case SIGNAL_GET_FAILURE: 56 | case SYMPTOM_ADD_FAILURE: 57 | case SYMPTOM_GET_FAILURE: 58 | return { 59 | ...state, 60 | pending: false, 61 | error: true 62 | }; 63 | 64 | default: 65 | return state; 66 | } 67 | } -------------------------------------------------------------------------------- /src/styles/ProfileStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { normalize } from '../constants/utils'; 3 | import { WIDTH, HEIGHT, borderRadius } from '../constants/dimens'; 4 | import { backgroundColor, divider, mainColor, disable, highlightColor } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | paddingLeft: 10, 12 | paddingRight: 10, 13 | }, 14 | upper: { 15 | flex: 2, 16 | flexDirection: 'row', 17 | justifyContent: 'space-between', 18 | alignItems: 'center', 19 | paddingLeft: 20, 20 | paddingRight: 20, 21 | }, 22 | sectionTitle:{ 23 | color: divider, 24 | fontSize: normalize(18), 25 | fontWeight: 'bold', 26 | marginBottom: 10, 27 | }, 28 | alertStatusOn:{ 29 | color: mainColor, 30 | fontSize: normalize(15), 31 | fontWeight: 'bold' 32 | }, 33 | alertStatusOff:{ 34 | color: disable, 35 | fontSize: normalize(15), 36 | fontWeight: 'bold' 37 | }, 38 | registerContact:{ 39 | flex: 2.3, 40 | justifyContent: 'center', 41 | paddingLeft: 20, 42 | paddingRight: 20, 43 | }, 44 | registerView:{ 45 | flexDirection: 'row', 46 | height: HEIGHT * 0.06, 47 | }, 48 | registerInput:{ 49 | flex:2, 50 | backgroundColor: highlightColor, 51 | padding: 5, 52 | marginRight: 5, 53 | borderRadius: borderRadius, 54 | }, 55 | registerButton:{ 56 | flexDirection: 'column', 57 | alignItems: 'center', 58 | justifyContent: 'center', 59 | backgroundColor: mainColor, 60 | flex:1, 61 | borderRadius: borderRadius, 62 | }, 63 | phoneList:{ 64 | flex: 5, 65 | padding: 20, 66 | } 67 | }); 68 | 69 | export default styles; -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.signalus", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.signalus", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Signalus 2 | 3 | This application is "Arrhythmia Monitoring" with 24Patch. 4 | 5 | Once you wear our patch, That's all! 6 | 7 | Our patch monitor your heart signal 24hours. 8 | 9 | If you have arrhythmia, the patch sends the signal to your smartphone. 10 | 11 | Automatically, app report to the 119(911 in America) if you are in emergency situation. 12 | 13 | Also, you can check your heart status easily. 14 | 15 | 16 | 17 | ## View 18 | 19 | *All views* 20 | 21 | ![All Pages](./README/Landing.jpg) 22 | 23 | 24 | 25 | *Pages* 26 | 27 | ![All Pages](./README/Pages.jpg) 28 | 29 | 30 | 31 | ## Libraries 32 | 33 | | Name | Version | 34 | | ------------------------------ | :---------- | 35 | | axios | 0.18.0 | 36 | | buffer | 5.2.1 | 37 | | lodash | 4.17.10 | 38 | | react | 16.4.1 | 39 | | react-addons-update | 15.6.2 | 40 | | react-native | 0.56.0 | 41 | | react-native-ble-plx | 0.10.0 | 42 | | react-native-calendars | 1.21.0 | 43 | | react-native-contacts | 2.2.4 | 44 | | react-native-elements | 1.0.0-beta5 | 45 | | react-native-fab | 1.0.7 | 46 | | react-native-linear-gradient | 2.4.2 | 47 | | react-native-progress | 3.5.0 | 48 | | react-native-sms | 1.8.0 | 49 | | react-native-svg | 6.5.2 | 50 | | react-native-svg-charts | 5.2.0 | 51 | | react-native-vector-icons | 5.0.0 | 52 | | react-navigation | 2.8.0 | 53 | | react-navigation-redux-helpers | 2.0.3 | 54 | | react-redux | 5.0.7 | 55 | | redux | 4.0.0 | 56 | | redux-promise-middleware | 5.1.1 | 57 | | redux-thunk | 2.3.0 | 58 | 59 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/signalus/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.signalus; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.rnfs.RNFSPackage; 7 | import com.wix.reactnativenotifications.RNNotificationsPackage; 8 | import com.tkporter.sendsms.SendSMSPackage; 9 | import com.rt2zz.reactnativecontacts.ReactNativeContacts; 10 | import com.BV.LinearGradient.LinearGradientPackage; 11 | import com.polidea.reactnativeble.BlePackage; 12 | import com.horcrux.svg.SvgPackage; 13 | import com.oblador.vectoricons.VectorIconsPackage; 14 | import com.facebook.react.ReactNativeHost; 15 | import com.facebook.react.ReactPackage; 16 | import com.facebook.react.shell.MainReactPackage; 17 | import com.facebook.soloader.SoLoader; 18 | 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | public class MainApplication extends Application implements ReactApplication { 23 | 24 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 25 | @Override 26 | public boolean getUseDeveloperSupport() { 27 | return BuildConfig.DEBUG; 28 | } 29 | 30 | @Override 31 | protected List getPackages() { 32 | return Arrays.asList( 33 | new MainReactPackage(), 34 | new RNFSPackage(), 35 | new RNNotificationsPackage(), 36 | SendSMSPackage.getInstance(), 37 | new ReactNativeContacts(), 38 | new LinearGradientPackage(), 39 | new BlePackage(), 40 | new SvgPackage(), 41 | new VectorIconsPackage() 42 | ); 43 | } 44 | 45 | @Override 46 | protected String getJSMainModuleName() { 47 | return "index"; 48 | } 49 | }; 50 | 51 | @Override 52 | public ReactNativeHost getReactNativeHost() { 53 | return mReactNativeHost; 54 | } 55 | 56 | @Override 57 | public void onCreate() { 58 | super.onCreate(); 59 | SoLoader.init(this, /* native exopackage */ false); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/styles/BluetoothStyle.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, } from 'react-native'; 2 | import { WIDTH, HEIGHT } from '../constants/dimens'; 3 | import { normalize } from '../constants/utils'; 4 | import { backgroundColor, mainColor, disable, divider, placeholderText } from '../constants/color'; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | flexDirection: 'column', 10 | backgroundColor: backgroundColor, 11 | }, 12 | headerBox: { 13 | flexDirection: 'row', 14 | justifyContent: 'space-between', 15 | alignItems: 'center', 16 | width: WIDTH, 17 | height: HEIGHT * 0.1, 18 | padding: 25, 19 | backgroundColor: backgroundColor, 20 | shadowColor: '#000', 21 | shadowOffset: { 22 | width: 0, 23 | height: 2, 24 | }, 25 | shadowOpacity: 0.25, 26 | shadowRadius: 3.84, 27 | elevation: 5, 28 | }, 29 | textToggleOn:{ 30 | fontSize: normalize(13), 31 | color: mainColor, 32 | fontWeight: 'bold', 33 | }, 34 | textToggleOff:{ 35 | fontSize: normalize(13), 36 | color: divider, 37 | fontWeight: 'bold', 38 | }, 39 | toggleView:{ 40 | flexDirection: 'row', 41 | alignItems: 'center', 42 | }, 43 | listTitle:{ 44 | padding: 25, 45 | fontSize: normalize(15), 46 | color: divider, 47 | fontWeight: 'bold', 48 | }, 49 | listTitleError:{ 50 | padding: 25, 51 | fontSize: normalize(15), 52 | color: divider, 53 | fontWeight: 'bold', 54 | color: 'red', 55 | }, 56 | deviceContainer:{ 57 | flex: 1, 58 | flexDirection: 'column', 59 | alignItems: 'center', 60 | }, 61 | deviceList:{ 62 | flexDirection: 'column', 63 | width: WIDTH * 0.8, 64 | }, 65 | device:{ 66 | height: HEIGHT * 0.1, 67 | }, 68 | deviceTitle:{ 69 | margin: 20, 70 | fontSize: normalize(15), 71 | color: placeholderText, 72 | }, 73 | divider:{ 74 | backgroundColor: divider, 75 | }, 76 | next: { 77 | flex: 1, 78 | flexDirection: 'row', 79 | alignItems: 'flex-end', 80 | justifyContent: 'center', 81 | padding: 50, 82 | }, 83 | }); 84 | 85 | export default styles; -------------------------------------------------------------------------------- /src/reducers/contact/actions.js: -------------------------------------------------------------------------------- 1 | import { registerAPI, deleteAPI, getContactsAPI, } from './API'; 2 | import { 3 | CONTACT_REGISTER_PENDING, CONTACT_REGISTER_SUCCESS, CONTACT_REGISTER_FAILURE, 4 | CONTACT_DELETE_PENDING, CONTACT_DELETE_SUCCESS, CONTACT_DELETE_FAILURE, 5 | CONTACT_GET_PENDING, CONTACT_GET_SUCCESS, CONTACT_GET_FAILURE, 6 | } from './actionTypes'; 7 | 8 | export const register = (id, name, phoneNumber, token) => dispatch => { 9 | const pending = () => { return {type: CONTACT_REGISTER_PENDING}}; 10 | const success = (response) => { return {type: CONTACT_REGISTER_SUCCESS, payload: response}}; 11 | const failure = (error) => { return {type: CONTACT_REGISTER_FAILURE, payload: error}}; 12 | 13 | dispatch(pending()); // Dispatch Action Starting 14 | 15 | return registerAPI(id, name, phoneNumber, token).then( 16 | (response) => { // Success 17 | dispatch(success(response)); 18 | }).catch((error) => { // Failure 19 | dispatch(failure(error)) 20 | }) 21 | } 22 | 23 | export const deleteContact = (id, phoneNumber, token) => dispatch => { 24 | const pending = () => { return {type: CONTACT_DELETE_PENDING}}; 25 | const success = (response) => { return {type: CONTACT_DELETE_SUCCESS, payload: response}}; 26 | const failure = (error) => { return {type: CONTACT_DELETE_FAILURE, payload: error}}; 27 | 28 | dispatch(pending()); // Dispatch Action Starting 29 | 30 | return deleteAPI(id, phoneNumber, token).then( 31 | (response) => { // Success 32 | dispatch(success(response)); 33 | }).catch((error) => { // Failure 34 | dispatch(failure(error)) 35 | }) 36 | } 37 | 38 | export const getContacts = (id, token) => dispatch => { 39 | const pending = () => { return {type: CONTACT_GET_PENDING}}; 40 | const success = (response) => { return {type: CONTACT_GET_SUCCESS, payload: response}}; 41 | const failure = (error) => { return {type: CONTACT_GET_FAILURE, payload: error}}; 42 | 43 | dispatch(pending()); // Dispatch Action Starting 44 | 45 | return getContactsAPI(id, token).then( 46 | (response) => { // Success 47 | dispatch(success(response)); 48 | }).catch((error) => { // Failure 49 | dispatch(failure(error)) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /src/components/CustomHealthStatusBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Text } from 'react-native'; 3 | import * as Progress from 'react-native-progress'; 4 | import { HEIGHT } from '../constants/dimens'; 5 | import { LabelNormal, LabelWarning, LabelEmergency } from '../constants/string'; 6 | import { mainColor, divider } from '../constants/color'; 7 | import { normalize } from '../constants/utils'; 8 | 9 | class CustomHealthStatusBar extends Component { 10 | constructor(props){ 11 | super(props) 12 | this.state={ 13 | status:LabelNormal, 14 | color:mainColor, 15 | } 16 | } 17 | 18 | componentDidMount(){ 19 | const { percent } = this.props; 20 | 21 | if(percent > 95){ 22 | this.setState({status: LabelNormal, color: mainColor}) 23 | } else if(percent > 85 && percent <= 95){ 24 | this.setState({status: LabelWarning, color: '#e9e063'}) 25 | } else{ 26 | this.setState({status: LabelEmergency, color: 'red'}) 27 | } 28 | } 29 | 30 | render(){ 31 | const { title, percent } = this.props; 32 | 33 | return( 34 | 35 | 36 | {`${title}\t`} {this.state.status} 37 | 38 | 39 | 40 | {percent}% 41 | 42 | 43 | ); 44 | } 45 | 46 | componentWillReceiveProps(nextProps){ 47 | const { percent } = nextProps; 48 | 49 | if(percent > 95){ 50 | this.setState({status: LabelNormal, color: mainColor}) 51 | } else if(percent > 85 && percent <= 95){ 52 | this.setState({status: LabelWarning, color: '#e9e063'}) 53 | } else{ 54 | this.setState({status: LabelEmergency, color: 'red'}) 55 | } 56 | } 57 | } 58 | 59 | export default CustomHealthStatusBar; -------------------------------------------------------------------------------- /src/screens/SymptomDetailUserScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import _ from 'lodash'; 4 | // Elements 5 | import { 6 | View, Text 7 | } from 'react-native'; 8 | import { Divider } from 'react-native-elements' 9 | import styles from '../styles/SymptomDetailUserStyle'; 10 | // Actions 11 | 12 | // Strings 13 | import { HeaderSymptomDetailFromUser, } from '../constants/string'; 14 | // Colors 15 | import { disable } from '../constants/color'; 16 | 17 | class SymptomDetailUserScreen extends Component{ 18 | static navigationOptions = { 19 | title: HeaderSymptomDetailFromUser, 20 | }; 21 | 22 | constructor(props){ 23 | super(props) 24 | } 25 | 26 | _date = (time) =>{ 27 | const year = time.substring(0,4) 28 | const month = time.substring(5,7) 29 | const day = time.substring(8,10) 30 | 31 | return `${year}년 ${month}월 ${day}일` 32 | } 33 | 34 | _time = (time) => { 35 | const hour = time.substring(0,2) 36 | const minute = time.substring(3,5) 37 | const second = time.substring(6,8) 38 | 39 | return `${hour}시 ${minute}분 ${second}초` 40 | } 41 | 42 | // Functions 43 | 44 | // LifeCycle 45 | render(){ 46 | const { navigation } = this.props; 47 | const time = navigation.getParam('time', '2000.01.01 00:00:00'); 48 | const symptoms = navigation.getParam('symptoms', 'Invalid'); 49 | 50 | return( 51 | 52 | 53 | {/* HEADER */} 54 | {this._date(time)} 55 | 추가된 시간 : {this._time(time.substring(11))} 56 | 57 | 58 | 59 | {/* Symptom List */} 60 | 느낀 증상 목록 61 | {_.map(symptoms, symptom => { 62 | return ( 63 | ■ {symptom} 64 | ); 65 | })} 66 | 67 | 68 | ); 69 | } 70 | } 71 | 72 | export default connect( 73 | (state) => ({ 74 | 75 | }), 76 | (dispatch) => ({ 77 | 78 | }) 79 | )(SymptomDetailUserScreen); -------------------------------------------------------------------------------- /ios/SignalusTests/SignalusTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 16 | 17 | @interface SignalusTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation SignalusTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 44 | if (level >= RCTLogLevelError) { 45 | redboxError = message; 46 | } 47 | }); 48 | 49 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 50 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 51 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 52 | 53 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 54 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 55 | return YES; 56 | } 57 | return NO; 58 | }]; 59 | } 60 | 61 | RCTSetLogFunction(RCTDefaultLogFunction); 62 | 63 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 64 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /src/screens/ConsentHTMLScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | // Elements 3 | import { 4 | View, WebView 5 | } from 'react-native'; 6 | // Strings 7 | import { 8 | HeaderConsentTerms, HeaderConsentPrivate, HeaderConsentTermsofGeo, HeaderConsentMarketing 9 | } from '../constants/string'; 10 | import { Terms, Private, TermsofGeo, Marketing } from '../constants/html' 11 | 12 | 13 | class ConsentHTMLScreen extends Component{ 14 | static navigationOptions = ({ navigation }) => { 15 | let header; 16 | switch(navigation.getParam('item')){ 17 | case "Terms": 18 | header = HeaderConsentTerms 19 | break; 20 | case "Private": 21 | header = HeaderConsentPrivate 22 | break; 23 | case "TermsofGeo": 24 | header = HeaderConsentTermsofGeo 25 | break; 26 | case "Marketing": 27 | header = HeaderConsentMarketing 28 | break; 29 | } 30 | return{ 31 | title: header 32 | }}; 33 | 34 | constructor(props){ 35 | super(props) 36 | this.state= { 37 | } 38 | } 39 | 40 | // LifeCyle 41 | render(){ 42 | const { navigation } = this.props; 43 | switch(navigation.getParam('item')){ 44 | case "Terms": 45 | return( 46 | 52 | ); 53 | 54 | case "Private": 55 | return( 56 | 62 | ); 63 | 64 | case "TermsofGeo": 65 | return( 66 | 72 | ); 73 | 74 | case "Marketing": 75 | return( 76 | 82 | ); 83 | } 84 | } 85 | } 86 | 87 | export default ConsentHTMLScreen; -------------------------------------------------------------------------------- /src/screens/CalendarScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import _ from 'lodash'; 4 | // Elements 5 | import { 6 | View, Text, ScrollView, 7 | } from 'react-native'; 8 | import { Calendar, CalendarList, Agenda } from 'react-native-calendars'; 9 | import styles from '../styles/CalendarStyle'; 10 | import { getToday } from '../constants/utils'; 11 | // Actions 12 | import { HISTORY } from '../reducers/nav/actionTypes' 13 | // Strings 14 | import { HeaderCalendar } from '../constants/string'; 15 | 16 | class CalendarScreen extends Component{ 17 | static navigationOptions = { 18 | title: HeaderCalendar, 19 | headerBackTitle: null, 20 | }; 21 | 22 | constructor(props) { 23 | super(props); 24 | this.state = { 25 | today: getToday(), 26 | eventDay: ['2018-10-25', '2018-10-26', '2018-11-01', '2018-10-31'], 27 | selected: getToday(), 28 | }; 29 | } 30 | 31 | // Functions 32 | _onDayPress = (day) => { 33 | this.setState({ 34 | selected: day.dateString 35 | }); 36 | 37 | console.log(day) 38 | } 39 | 40 | _mark = () => { 41 | 42 | // today 43 | let marked = {[this.state.selected]: {selected: true, selectedDotColor: 'orange'}} 44 | 45 | // event days 46 | _.map(this.state.eventDay, day => { 47 | marked[day] = {marked: true, dotColor: 'red'} 48 | }) 49 | 50 | return marked 51 | } 52 | 53 | // LifeCycle 54 | render(){ 55 | const { history } = this.props; 56 | return( 57 | 58 | {console.log('month changed', month)}} 69 | /> 70 | 71 | 72 | 73 | 74 | ); 75 | } 76 | } 77 | 78 | export default connect( 79 | (state) => ({ 80 | 81 | }), 82 | (dispatch) => ({ 83 | history: () => dispatch({ type: HISTORY}), 84 | }) 85 | )(CalendarScreen); -------------------------------------------------------------------------------- /src/screens/SplashScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from "redux"; 3 | import { connect } from 'react-redux'; 4 | // Elements 5 | import { 6 | View, AsyncStorage, Image 7 | } from 'react-native'; 8 | import styles from '../styles/SplashStyle'; 9 | // Actions 10 | import * as loginActions from '../reducers/auth/actions'; 11 | import { 12 | SIGNED, NOT_SIGNED, NOT_CONNECTED 13 | } from '../reducers/nav/actionTypes' 14 | 15 | 16 | class SplashScreen extends Component{ 17 | static navigationOptions = { 18 | header: null, 19 | headerBackTitle: null 20 | }; 21 | 22 | // Functions 23 | _login = async (id, password) => { 24 | const { LoginActions } = this.props; 25 | 26 | return await LoginActions.login(id, password); 27 | } 28 | 29 | // LifeCycle 30 | componentDidMount(){ 31 | const { goToSignIn } = this.props; 32 | 33 | let id; 34 | let password; 35 | let device; 36 | 37 | setTimeout(()=>{ 38 | AsyncStorage.multiGet(['id', 'pw', 'device']).then((value) => { // Check LocalStorage 39 | id = value[0][1]; 40 | password = value[1][1]; 41 | device = value[2][1]; 42 | 43 | if(id != null && password != null) { // Login 44 | this._login(id,password) 45 | .catch((e) =>{}) 46 | } else { 47 | goToSignIn(); 48 | } 49 | }) 50 | },2000) 51 | } 52 | 53 | componentWillReceiveProps(nextProps) { 54 | const { isLoggedIn, goToSignIn, goToBluetooth } = nextProps; 55 | 56 | isLoggedIn ? goToBluetooth() : goToSignIn() 57 | 58 | } 59 | 60 | render(){ 61 | return( 62 | 63 | 64 | {/* Splash */} 65 | 69 | 70 | ); 71 | } 72 | } 73 | 74 | // Redux Connect 75 | export default connect( 76 | (state) => ({ 77 | isLoggedIn : state.auth.isLoggedIn, 78 | }), 79 | (dispatch) => ({ 80 | LoginActions: bindActionCreators(loginActions, dispatch), 81 | goToMain: () => dispatch({ type: SIGNED}), 82 | goToBluetooth: () => dispatch({ type: NOT_CONNECTED }), 83 | goToSignIn: () => dispatch({ type: NOT_SIGNED}), 84 | }) 85 | )(SplashScreen); 86 | -------------------------------------------------------------------------------- /src/reducers/auth/actions.js: -------------------------------------------------------------------------------- 1 | import { AsyncStorage } from 'react-native'; 2 | import { registerAPI, loginAPI, withdrawAPI } from './API'; 3 | import { 4 | REGISTER_PENDING, REGISTER_SUCCESS, REGISTER_FAILURE, 5 | SIGNIN_PENDING, SIGNIN_SUCCESS, SIGNIN_FAILURE, 6 | WITHDRAWAL_PENDING, WITHDRAWAL_SUCCESS, WITHDRAWAL_FAILURE 7 | } from './actionTypes'; 8 | 9 | export const register = (id, password, age, gender, height, weight) => dispatch => { 10 | const pending = () => { return {type: REGISTER_PENDING}}; 11 | const success = (response) => { return {type: REGISTER_SUCCESS, payload: response}}; 12 | const failure = (error) => { return {type: REGISTER_FAILURE, payload: error}}; 13 | 14 | dispatch(pending()); // Dispatch Action Starting 15 | 16 | return registerAPI(id,password,age,gender,height,weight).then( 17 | (response) => { // Success 18 | dispatch(success(response)); 19 | saveData(id, password, response); 20 | }).catch((error) => { // Failure 21 | dispatch(failure(error)) 22 | }) 23 | } 24 | 25 | export const login = (id, password) => dispatch => { 26 | const pending = () => { return {type: SIGNIN_PENDING}}; 27 | const success = (response) => { return {type: SIGNIN_SUCCESS, payload: response}}; 28 | const failure = (error) => { return {type: SIGNIN_FAILURE, payload: error}}; 29 | 30 | dispatch(pending()); // Dispatch Action Starting 31 | 32 | return loginAPI(id,password).then( 33 | (response) => { // Success 34 | dispatch(success(response)); 35 | saveData(id, password, response); 36 | }).catch((error) => {// Failure 37 | dispatch(failure(error)) 38 | }) 39 | } 40 | 41 | export const withdraw = (id, token) => dispatch => { 42 | const pending = () => { return {type: WITHDRAWAL_PENDING}}; 43 | const success = (response) => { return {type: WITHDRAWAL_SUCCESS, payload: response}}; 44 | const failure = (error) => { return {type: WITHDRAWAL_FAILURE, payload: error}}; 45 | 46 | dispatch(pending()); // Dispatch Action Starting 47 | 48 | return withdrawAPI(id, token).then( 49 | (response) => { // Success 50 | dispatch(success(response)); 51 | }).catch((error) => {// Failure 52 | dispatch(failure(error)) 53 | }) 54 | } 55 | 56 | const saveData = (id, password, response) => { 57 | AsyncStorage.multiSet([ 58 | ["id", id], 59 | ["pw", password], 60 | ["token", response.data.token], 61 | ]) 62 | } 63 | -------------------------------------------------------------------------------- /src/components/CustomPicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Animated, TouchableHighlight, Text, Picker } from 'react-native'; 3 | import { LabelChoose, ModeAge, ModeSex, ModeHeight, ModeWeight } from '../constants/string'; 4 | import { HEIGHT, modalOffset, modalDuration } from '../constants/dimens' 5 | import styles from '../styles/PickerModal'; 6 | import { ages, sex, heights, weights } from '../constants/model'; 7 | 8 | let values = []; 9 | 10 | class CustomPicker extends Component { 11 | constructor(props){ 12 | super(props) 13 | } 14 | 15 | componentDidMount(){ 16 | const { offSet } = this.props; 17 | Animated.timing(offSet, { 18 | duration: modalDuration, 19 | toValue: modalOffset 20 | }).start() 21 | } 22 | 23 | closeModal = () => { 24 | const { offSet, closeModal } = this.props; 25 | 26 | Animated.timing(offSet, { 27 | duration: modalDuration, 28 | toValue: HEIGHT 29 | }).start(closeModal); 30 | } 31 | 32 | 33 | render() { 34 | const { offSet, showModal, changeValue, mode } = this.props; 35 | switch(mode){ 36 | case ModeAge: 37 | values = ages 38 | break; 39 | case ModeSex: 40 | values = sex 41 | break; 42 | case ModeHeight: 43 | values = heights 44 | break; 45 | case ModeWeight: 46 | values = weights; 47 | break; 48 | default: 49 | values = ages 50 | break; 51 | } 52 | return ( 53 | 54 | 55 | 56 | 57 | {LabelChoose} 58 | 59 | 60 | changeValue(value)}> 63 | {Object.keys(values).map((index) => ( 64 | 69 | ))} 70 | 71 | 72 | 73 | ) 74 | } 75 | } 76 | 77 | export default CustomPicker; 78 | -------------------------------------------------------------------------------- /ios/Signalus/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Signalus 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSContactsUsageDescription 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UIAppFonts 43 | 44 | Entypo.ttf 45 | EvilIcons.ttf 46 | Feather.ttf 47 | FontAwesome.ttf 48 | FontAwesome5_Brands.ttf 49 | FontAwesome5_Regular.ttf 50 | FontAwesome5_Solid.ttf 51 | Foundation.ttf 52 | Ionicons.ttf 53 | MaterialCommunityIcons.ttf 54 | MaterialIcons.ttf 55 | Octicons.ttf 56 | SimpleLineIcons.ttf 57 | Zocial.ttf 58 | 59 | UIBackgroundModes 60 | 61 | bluetooth-central 62 | 63 | UILaunchStoryboardName 64 | LaunchScreen 65 | UIRequiredDeviceCapabilities 66 | 67 | armv7 68 | 69 | UISupportedInterfaceOrientations 70 | 71 | UIInterfaceOrientationPortrait 72 | 73 | UIViewControllerBasedStatusBarAppearance 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | module.system=haste 33 | module.system.haste.use_name_reducers=true 34 | # get basename 35 | module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' 36 | # strip .js or .js.flow suffix 37 | module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' 38 | # strip .ios suffix 39 | module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' 40 | module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' 41 | module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' 42 | module.system.haste.paths.blacklist=.*/__tests__/.* 43 | module.system.haste.paths.blacklist=.*/__mocks__/.* 44 | module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* 45 | module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* 46 | 47 | munge_underscores=true 48 | 49 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 50 | 51 | module.file_ext=.js 52 | module.file_ext=.jsx 53 | module.file_ext=.json 54 | module.file_ext=.native.js 55 | 56 | suppress_type=$FlowIssue 57 | suppress_type=$FlowFixMe 58 | suppress_type=$FlowFixMeProps 59 | suppress_type=$FlowFixMeState 60 | 61 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 62 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 63 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 64 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 65 | 66 | [version] 67 | ^0.75.0 68 | -------------------------------------------------------------------------------- /src/components/CustomFormInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Image } from 'react-native'; 3 | import { Input } from 'react-native-elements' 4 | 5 | import { defaultMaxLength } from '../constants/dimens' 6 | import { mainColor, placeholderText } from '../constants/color' 7 | 8 | class CustomFormInput extends Component { 9 | constructor(props){ 10 | super(props) 11 | this.state= { 12 | focus: false, 13 | changed: false, 14 | } 15 | } 16 | 17 | _focus = () => { 18 | 19 | } 20 | 21 | _onFocus = () => { 22 | this.setState({focus: true}) 23 | } 24 | 25 | _onBlur = () => { 26 | this.setState({focus: false}) 27 | } 28 | 29 | _onChange = () => { 30 | this.setState({changed: true}) 31 | } 32 | 33 | render(){ 34 | const { childRef, onSubmitEditing, blurOnSubmit, style, iconStyle, placeholder, isIcon, type, onChangeText, maxLength, returnKeyType, clearButtonMode, secureTextEntry, error, errorMsg } = this.props; 35 | let iconSRC 36 | 37 | if(isIcon){ 38 | switch(type){ 39 | case "ID": iconSRC = require("../../assets/loginID.png") 40 | break 41 | case "PW": iconSRC = require("../../assets/loginPW.png") 42 | break 43 | default : 44 | break 45 | } 46 | } 47 | 48 | return( 49 | 50 | this._onFocus()} 57 | onBlur={ () => this._onBlur() } 58 | inputContainerStyle={{borderBottomColor: this.state.focus ? mainColor : placeholderText}} 59 | inputStyle={{color: placeholderText}} 60 | placeholder={placeholder} 61 | onChangeText={onChangeText} 62 | maxLength={maxLength ? maxLength : defaultMaxLength} 63 | secureTextEntry={secureTextEntry} 64 | returnKeyType={returnKeyType} 65 | clearButtonMode={clearButtonMode} 66 | shake={error} 67 | autoCorrect={false} 68 | errorStyle={{ color: 'red' }} 69 | errorMessage={error ? errorMsg : " "} 70 | onChange={() => this._onChange()} 71 | rightIcon={ 72 | isIcon && 73 | this.state.changed && 74 | 79 | } 80 | /> 81 | 82 | ); 83 | } 84 | } 85 | 86 | export default CustomFormInput; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Signalus/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /ios/Signalus/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"Signalus" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | // Required to register for notifications 38 | - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings 39 | { 40 | [RCTPushNotificationManager didRegisterUserNotificationSettings:notificationSettings]; 41 | } 42 | // Required for the register event. 43 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 44 | { 45 | [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 46 | } 47 | // Required for the notification event. You must call the completion handler after handling the remote notification. 48 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 49 | fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 50 | { 51 | [RCTPushNotificationManager didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; 52 | } 53 | // Required for the registrationError event. 54 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 55 | { 56 | [RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error]; 57 | } 58 | // Required for the localNotification event. 59 | - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification 60 | { 61 | [RCTPushNotificationManager didReceiveLocalNotification:notification]; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /src/reducers/symptom/actions.js: -------------------------------------------------------------------------------- 1 | import { addSymptomAPI, getSymptomAPI, addSignalAPI, getSignalAPI } from './API'; 2 | import { 3 | SYMPTOM_ADD_PENDING, SYMPTOM_ADD_SUCCESS, SYMPTOM_ADD_FAILURE, 4 | SYMPTOM_GET_PENDING, SYMPTOM_GET_SUCCESS, SYMPTOM_GET_FAILURE, 5 | SIGNAL_ADD_PENDING, SIGNAL_ADD_SUCCESS, SIGNAL_ADD_FAILURE, 6 | SIGNAL_GET_PENDING, SIGNAL_GET_SUCCESS, SIGNAL_GET_FAILURE, 7 | } from './actionTypes'; 8 | 9 | export const addSymptom = (id, symptoms, time, type, token) => dispatch => { 10 | const pending = () => { return {type: SYMPTOM_ADD_PENDING}}; 11 | const success = (response) => { return {type: SYMPTOM_ADD_SUCCESS, payload: response}}; 12 | const failure = (error) => { return {type: SYMPTOM_ADD_FAILURE, payload: error}}; 13 | 14 | dispatch(pending()); // Dispatch Action Starting 15 | 16 | return addSymptomAPI(id, symptoms, time, type, token).then( 17 | (response) => { // Success 18 | console.log(response) 19 | dispatch(success(response)); 20 | }).catch((error) => { // Failure 21 | console.log(error) 22 | dispatch(failure(error)) 23 | }) 24 | } 25 | 26 | export const getSymptoms = (id, token) => dispatch => { 27 | const pending = () => { return {type: SYMPTOM_GET_PENDING}}; 28 | const success = (response) => { return {type: SYMPTOM_GET_SUCCESS, payload: response}}; 29 | const failure = (error) => { return {type: SYMPTOM_GET_FAILURE, payload: error}}; 30 | 31 | dispatch(pending()); // Dispatch Action Starting 32 | 33 | return getSymptomAPI(id, token).then( 34 | (response) => { // Success 35 | console.log(response) 36 | dispatch(success(response)); 37 | }).catch((error) => { // Failure 38 | console.log(error) 39 | dispatch(failure(error)) 40 | }) 41 | } 42 | 43 | export const addSignal = (id, signalFile, time, token) => dispatch => { 44 | const pending = () => { return {type: SIGNAL_ADD_PENDING}}; 45 | const success = (response) => { return {type: SIGNAL_ADD_SUCCESS, payload: response}}; 46 | const failure = (error) => { return {type: SIGNAL_ADD_FAILURE, payload: error}}; 47 | 48 | dispatch(pending()); // Dispatch Action Starting 49 | 50 | return addSignalAPI(id, signalFile, time, token).then( 51 | (response) => { // Success 52 | console.log(response) 53 | dispatch(success(response)); 54 | }).catch((error) => { // Failure 55 | console.log(error) 56 | dispatch(failure(error)) 57 | }) 58 | } 59 | 60 | export const getSignal = (id, time, token) => dispatch => { 61 | const pending = () => { return {type: SIGNAL_GET_PENDING}}; 62 | const success = (response) => { return {type: SIGNAL_GET_SUCCESS, payload: response}}; 63 | const failure = (error) => { return {type: SIGNAL_GET_FAILURE, payload: error}}; 64 | 65 | dispatch(pending()); // Dispatch Action Starting 66 | 67 | return getSignalAPI(id, time, token).then( 68 | (response) => { // Success 69 | console.log(response) 70 | dispatch(success(response)); 71 | }).catch((error) => { // Failure 72 | console.log(error) 73 | dispatch(failure(error)) 74 | }) 75 | } -------------------------------------------------------------------------------- /src/screens/SymptomDetailPatchScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators } from "redux"; 4 | // Elements 5 | import { 6 | View, Text, AsyncStorage 7 | } from 'react-native'; 8 | import { Divider } from 'react-native-elements' 9 | import styles from '../styles/SymptomDetailPatchStyle'; 10 | import { stringToSignal, getTimeForNowPath } from '../constants/utils'; 11 | import CustomChart from '../components/CustomChart'; 12 | // Actions 13 | import * as symptomActions from '../reducers/symptom/actions'; 14 | // Strings 15 | import { HeaderSymptomDetailFromDevice, } from '../constants/string'; 16 | // Colors 17 | import { disable } from '../constants/color'; 18 | 19 | class SymptomDetailDeviceScreen extends Component{ 20 | static navigationOptions = { 21 | title: HeaderSymptomDetailFromDevice, 22 | }; 23 | 24 | constructor(props){ 25 | super(props) 26 | this.state= { 27 | data: [0], 28 | } 29 | } 30 | 31 | // Functions 32 | _date = (time) =>{ 33 | const year = time.substring(0,4) 34 | const month = time.substring(5,7) 35 | const day = time.substring(8,10) 36 | 37 | return `${year}년 ${month}월 ${day}일` 38 | } 39 | 40 | _time = (time) => { 41 | const hour = time.substring(0,2) 42 | const minute = time.substring(3,5) 43 | const second = time.substring(6,8) 44 | 45 | return `${hour}시 ${minute}분 ${second}초` 46 | } 47 | 48 | _getsignal = async (id, time, token) => { 49 | const { SymptomActions } = this.props; 50 | 51 | return await SymptomActions.getSignal(id, time, token); 52 | } 53 | 54 | // LifeCycle 55 | componentDidMount(){ 56 | const { navigation } = this.props; 57 | const time = navigation.getParam('time', '2000.01.01 00:00:00'); 58 | 59 | AsyncStorage.multiGet(['id', 'token']).then((value) => { // Get Data From LocalStorage 60 | id = value[0][1]; 61 | token = value[1][1]; 62 | 63 | this._getsignal(id, getTimeForNowPath(time), token) 64 | .catch((e) =>{}) 65 | }) 66 | } 67 | 68 | componentWillReceiveProps(nextProps){ 69 | const { signal } = nextProps; 70 | 71 | this.setState({data: stringToSignal(signal)}); 72 | } 73 | 74 | render(){ 75 | const { navigation } = this.props; 76 | const time = navigation.getParam('time', '2000.01.01 00:00:00'); 77 | 78 | return( 79 | 80 | 81 | {/* HEADER */} 82 | {this._date(time)} 83 | 추가된 시간 : {this._time(time.substring(11))} 84 | 85 | 86 | 87 | {/* ECG Graph */} 88 | 수신된 신호 89 | 90 | 93 | 94 | 95 | 96 | ); 97 | } 98 | } 99 | 100 | export default connect( 101 | (state) => ({ 102 | signal: state.symptom.signal, 103 | }), 104 | (dispatch) => ({ 105 | SymptomActions: bindActionCreators(symptomActions, dispatch), 106 | }) 107 | )(SymptomDetailDeviceScreen); -------------------------------------------------------------------------------- /src/constants/utils.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer'; 2 | import { Dimensions, Platform, PixelRatio, PushNotificationIOS } from 'react-native'; 3 | import SendSMS from 'react-native-sms' 4 | 5 | const { 6 | width: SCREEN_WIDTH, 7 | height: SCREEN_HEIGHT, 8 | } = Dimensions.get('window'); 9 | 10 | // based on iphone 5s's scale 11 | const scale = SCREEN_WIDTH / 320; 12 | 13 | export const normalize = (size) => { 14 | const newSize = size * scale 15 | if (Platform.OS === 'ios') { 16 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) 17 | } else { 18 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2 19 | } 20 | } 21 | 22 | 23 | export const base64ToHex = (value) => new Buffer(value, 'base64').toString('hex') 24 | export const getDecValue = (c) => { 25 | const originHex = base64ToHex(c.value); 26 | let value = parseInt(originHex, 16) 27 | if((value & 0x8000) >> 15 == 1){ 28 | value = value & 0xffff; 29 | value = value - 0x10000; 30 | } 31 | 32 | return value 33 | } 34 | 35 | export const getToday = () => { 36 | var date = new Date(); 37 | var year = date.getFullYear(); 38 | var month = new String(date.getMonth()+1); 39 | var day = new String(date.getDate()); 40 | 41 | // 한자리수일 경우 0을 채워준다. 42 | if(month.length == 1){ 43 | month = "0" + month; 44 | } 45 | if(day.length == 1){ 46 | day = "0" + day; 47 | } 48 | 49 | return year + "-" + month + "-" + day; 50 | } 51 | 52 | export const getTimeForNow = () => { 53 | let date = new Date(); 54 | var year = date.getFullYear(); 55 | var month = new String(date.getMonth()+1); 56 | var day = new String(date.getDate()); 57 | let h = date.getHours(); 58 | let m = date.getMinutes(); 59 | let s = date.getSeconds(); 60 | 61 | // 한자리수일 경우 0을 채워준다. 62 | if(month.length == 1){ 63 | month = "0" + month; 64 | } 65 | if(day.length == 1){ 66 | day = "0" + day; 67 | } 68 | 69 | m = this._checkTime(m); 70 | s = this._checkTime(s); 71 | 72 | const now = `${year}.${month}.${day} ${h}:${m}:${s}` 73 | 74 | return now 75 | } 76 | 77 | export const getTimeForNowPath = (t) => { 78 | let time = t 79 | 80 | const year = time.substring(0,4) 81 | const month = time.substring(5,7) 82 | const day = time.substring(8,10) 83 | 84 | time = time.substring(11) 85 | 86 | const hour = time.substring(0,2) 87 | const minute = time.substring(3,5) 88 | const second = time.substring(6,8) 89 | 90 | const now = `${year}.${month}.${day}_${hour}:${minute}:${second}` 91 | return now 92 | } 93 | 94 | _checkTime = (i) => { 95 | if (i < 10) {i = "0" + i}; 96 | return i; 97 | } 98 | 99 | export const send = (message, contacts) => { 100 | 101 | SendSMS.send({ 102 | body: message, 103 | recipients: contacts, 104 | }, (completed, cancelled, error) => { 105 | 106 | console.log('SMS Callback: completed: ' + completed + ' cancelled: ' + cancelled + 'error: ' + error); 107 | }); 108 | } 109 | 110 | export const notification = () => { 111 | PushNotificationIOS.scheduleLocalNotification({ 112 | fireDate: new Date(Date.now() + 60 * 1000).getTime(), 113 | alertTitle: '위험상황 감지!', 114 | alertBody: '부정맥으로 의심되는 신호가 감지되었습니다!' 115 | }); 116 | } 117 | 118 | export const signalToString = (data) => { 119 | let signalString = '' 120 | for(i in data){ 121 | signalString += `${data[i]}\n` 122 | } 123 | 124 | return signalString 125 | } 126 | 127 | export const stringToSignal = (data) => { 128 | var signal = data.split('\n').map((item) => { 129 | return parseInt(item, 10); 130 | }); 131 | 132 | return signal.slice(0,-1) 133 | } -------------------------------------------------------------------------------- /src/reducers/nav/reducer.js: -------------------------------------------------------------------------------- 1 | import { NavigationActions, StackActions } from 'react-navigation'; 2 | import { RootNavigator } from '../../navigators/AppNavigator'; 3 | 4 | import { 5 | NOT_SIGNED, ON_CONSENT, ON_REGISTER, SIGNED, SIGNOUT, 6 | CONNECTED, NOT_CONNECTED, 7 | ON_CALENDAR, ON_LOG, LOGGED, ON_DETAIL_PATCH, ON_DETAIL_USER, ON_SETTING 8 | } from './actionTypes'; 9 | 10 | // Start with two routes: The Main screen, with the Login screen on top. 11 | const router = RootNavigator.router; 12 | const firstAction =router.getActionForPathAndParams('Splash'); 13 | const initialNavState = router.getStateForAction(firstAction); 14 | 15 | 16 | export default nav = (state = initialNavState, action) => { 17 | let nextState; 18 | switch (action.type) { 19 | case NOT_SIGNED: 20 | nextState = router.getStateForAction( 21 | StackActions.reset({ 22 | index: 0, 23 | actions: [NavigationActions.navigate({ routeName: 'SignIn' })] 24 | }), 25 | state 26 | ); 27 | break; 28 | case ON_CONSENT: 29 | nextState = router.getStateForAction( 30 | NavigationActions.navigate({ routeName: 'Consent' }), 31 | state 32 | ); 33 | break; 34 | case ON_REGISTER: 35 | nextState = router.getStateForAction( 36 | NavigationActions.navigate({ routeName: 'Register' }), 37 | state 38 | ); 39 | break; 40 | case SIGNED: 41 | nextState = router.getStateForAction( 42 | StackActions.reset({ 43 | index: 0, 44 | actions: [NavigationActions.navigate({ routeName: 'Main' })] 45 | }), 46 | state 47 | ); 48 | break; 49 | case CONNECTED: 50 | nextState = router.getStateForAction( 51 | StackActions.reset({ 52 | index: 0, 53 | actions: [NavigationActions.navigate({ routeName: 'Main' })] 54 | }), 55 | state 56 | ); 57 | break; 58 | case NOT_CONNECTED: 59 | nextState = router.getStateForAction( 60 | StackActions.reset({ 61 | index: 0, 62 | key: null, 63 | actions: [NavigationActions.navigate({ routeName: 'Bluetooth' })] 64 | }), 65 | state 66 | ); 67 | break; 68 | 69 | case ON_CALENDAR: 70 | nextState = router.getStateForAction( 71 | NavigationActions.navigate({ routeName: 'Calendar' }), 72 | state 73 | ); 74 | break; 75 | case ON_LOG: 76 | nextState = router.getStateForAction( 77 | NavigationActions.navigate({ routeName: 'SymptomLog'}), 78 | state 79 | ); 80 | break; 81 | case LOGGED: 82 | nextState = router.getStateForAction( 83 | NavigationActions.back(), 84 | state 85 | ); 86 | break; 87 | case ON_DETAIL_PATCH: 88 | nextState = router.getStateForAction( 89 | NavigationActions.navigate({ routeName: 'SymptomDetailPatch'}), 90 | state 91 | ); 92 | break; 93 | case ON_DETAIL_USER: 94 | nextState = router.getStateForAction( 95 | NavigationActions.navigate({ routeName: 'SymptomDetailUser'}), 96 | state 97 | ); 98 | break; 99 | case ON_SETTING: 100 | nextState = router.getStateForAction( 101 | NavigationActions.navigate({ routeName: 'Setting'}), 102 | state 103 | ); 104 | break; 105 | 106 | case SIGNOUT: 107 | nextState = router.getStateForAction( 108 | StackActions.reset({ 109 | index: 0, 110 | key: null, 111 | actions: [NavigationActions.navigate({ routeName: 'SignIn' })] 112 | }), 113 | state 114 | ); 115 | break; 116 | default: 117 | nextState = RootNavigator.router.getStateForAction(action, state); 118 | break; 119 | } 120 | return nextState || state; 121 | }; -------------------------------------------------------------------------------- /src/screens/ConsentScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | // Elements 4 | import { 5 | View, 6 | } from 'react-native'; 7 | import { Divider } from 'react-native-elements'; 8 | import CustomCheckBox from '../components/CustomCheckBox'; 9 | import CustomFilledButton from '../components/CustomFilledButton'; 10 | import styles from '../styles/ConsentStyle'; 11 | // Actions 12 | import { 13 | ON_REGISTER, ON_CONSENT_HTML 14 | } from '../reducers/nav/actionTypes' 15 | // Strings 16 | import { 17 | HeaderConsent, LabelConsentTerms, LabelConsentPrivate, LabelConsentTermsofGeo, LabelConsentMarketing, 18 | LabelAgreementAll, LabelAgreement 19 | } from '../constants/string'; 20 | 21 | class ConsentScreen extends Component{ 22 | static navigationOptions = { 23 | title: HeaderConsent, 24 | headerBackTitle: null, 25 | }; 26 | 27 | constructor(props){ 28 | super(props) 29 | this.state= { 30 | checked_1:false, 31 | checked_2:false, 32 | checked_3:false, 33 | checked_4:false, 34 | } 35 | } 36 | 37 | // Functions 38 | _viewHTML =(item) => { 39 | this.props.navigation.navigate('ConsentHTML', { 40 | item: item, 41 | }) 42 | } 43 | 44 | // LifeCyle 45 | render(){ 46 | const { goToRegister } = this.props; 47 | return( 48 | 49 | 50 | {/* Agreement All */} 51 | this.setState({checked_1: true, checked_2: true, checked_3: true, checked_4: true})} 54 | title={LabelAgreementAll} 55 | /> 56 | 57 | {/* Divider */} 58 | 59 | 60 | 61 | 62 | {/* Agreement 1 */} 63 | this.setState({checked_1: !this.state.checked_1})} 66 | onTouch={() => this._viewHTML("Terms")} 67 | title={LabelConsentTerms} 68 | underline={true} 69 | /> 70 | 71 | {/* Agreement 2 */} 72 | this.setState({checked_2: !this.state.checked_2})} 75 | onTouch={() => this._viewHTML("Private")} 76 | title={LabelConsentPrivate} 77 | underline={true} 78 | /> 79 | 80 | {/* Agreement 3 */} 81 | this.setState({checked_3: !this.state.checked_3})} 84 | onTouch={() => this._viewHTML("TermsofGeo")} 85 | title={LabelConsentTermsofGeo} 86 | underline={true} 87 | /> 88 | 89 | {/* Agreement 4 */} 90 | this.setState({checked_4: !this.state.checked_4})} 93 | onTouch={() => this._viewHTML("Marketing")} 94 | title={LabelConsentMarketing} 95 | underline={true} 96 | /> 97 | 98 | {/* Next Button */} 99 | 100 | 106 | 107 | 108 | ); 109 | } 110 | } 111 | 112 | // Redux Connect 113 | export default connect( 114 | (state) => ({ 115 | 116 | }), 117 | (dispatch) => ({ 118 | goToRegister: () => dispatch({ type: ON_REGISTER }), 119 | goToHTML: () => dispatch({ type: ON_CONSENT_HTML }), 120 | }) 121 | )(ConsentScreen); 122 | -------------------------------------------------------------------------------- /src/navigators/MainTabNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStackNavigator, createBottomTabNavigator } from 'react-navigation'; 3 | import { Image } from 'react-native'; 4 | 5 | import VitalScreen from '../screens/VitalScreen'; // Main-Vital 6 | import CalendarScreen from '../screens/CalendarScreen'; // Vital-Calendar 7 | import SymptomScreen from '../screens/SymptomScreen'; // Main-Symptom 8 | import SymptomLogScreen from '../screens/SymptomLogScreen'; // Symptom-Log 9 | import SymptomDetailPatchScreen from '../screens/SymptomDetailPatchScreen'; //Symptom-DetailFromDevice 10 | import SymptomDetailUserScreen from '../screens/SymptomDetailUserScreen'; //Symptom-DetailFromUser 11 | import ProfileScreen from '../screens/ProfileScreen'; // Main-Profile 12 | import SettingScreen from '../screens/SettingScreen'; // Profile-Setting 13 | 14 | import { activeTintColor, inactiveTintColor, backgroundColor, mainColor, headerTintColor } from '../constants/color'; 15 | 16 | const VitalStack = createStackNavigator({ 17 | Vital: VitalScreen, 18 | Calendar: CalendarScreen 19 | },{ 20 | navigationOptions: { 21 | title: 'Signalus', 22 | headerStyle: { 23 | backgroundColor: mainColor, 24 | borderBottomWidth: 0, 25 | }, 26 | headerTintColor: headerTintColor, 27 | headerTitleStyle: { 28 | fontWeight: 'bold', 29 | }, 30 | }, 31 | }); 32 | 33 | VitalStack.navigationOptions = ({ navigation }) => { 34 | let tabBarVisible = true; 35 | if (navigation.state.index > 0) { 36 | tabBarVisible = false; 37 | } 38 | 39 | return { 40 | tabBarVisible, 41 | }; 42 | }; 43 | 44 | const SymptomStack = createStackNavigator({ 45 | Symptom: SymptomScreen, 46 | SymptomLog: SymptomLogScreen, 47 | SymptomDetailPatch: SymptomDetailPatchScreen, 48 | SymptomDetailUser: SymptomDetailUserScreen, 49 | },{ 50 | navigationOptions: { 51 | title: 'Signalus', 52 | headerStyle: { 53 | backgroundColor: mainColor, 54 | borderBottomWidth: 0, 55 | }, 56 | headerTintColor: headerTintColor, 57 | headerTitleStyle: { 58 | fontWeight: 'bold', 59 | }, 60 | }, 61 | }); 62 | 63 | SymptomStack.navigationOptions = ({ navigation }) => { 64 | let tabBarVisible = true; 65 | if (navigation.state.index > 0) { 66 | tabBarVisible = false; 67 | } 68 | 69 | return { 70 | tabBarVisible, 71 | }; 72 | }; 73 | 74 | const ProfileStack = createStackNavigator({ 75 | Profile: ProfileScreen, 76 | Setting: SettingScreen, 77 | },{ 78 | navigationOptions: { 79 | title: 'Signalus', 80 | headerStyle: { 81 | backgroundColor: mainColor, 82 | borderBottomWidth: 0, 83 | }, 84 | headerTintColor: headerTintColor, 85 | headerTitleStyle: { 86 | fontWeight: 'bold', 87 | }, 88 | }, 89 | }); 90 | 91 | const MainScreen = createBottomTabNavigator({ 92 | Vital: { 93 | screen: VitalStack, 94 | navigationOptions:{ 95 | tabBarIcon: ({ focused }) => { 96 | let src = focused ? require('../../assets/vitalON.png') : require('../../assets/vitalOFF.png') 97 | return ; 101 | }, 102 | } 103 | }, 104 | Symptom: { 105 | screen: SymptomStack, 106 | navigationOptions:{ 107 | tabBarIcon: ({ focused }) => { 108 | let src = focused ? require('../../assets/symptomON.png') : require('../../assets/symptomOFF.png') 109 | return ; 113 | }, 114 | } 115 | }, 116 | Profile: { 117 | screen: ProfileStack, 118 | navigationOptions:{ 119 | tabBarIcon: ({ focused }) => { 120 | let src = focused ? require('../../assets/profileON.png') : require('../../assets/profileOFF.png') 121 | return ; 125 | }, 126 | } 127 | }, 128 | },{ 129 | initialRouteName: 'Vital', 130 | tabBarOptions:{ 131 | activeTintColor: activeTintColor, 132 | inactiveTintColor: inactiveTintColor, 133 | showLabel: false, 134 | style: { 135 | backgroundColor: backgroundColor, 136 | }, 137 | }, 138 | }); 139 | 140 | MainScreen.navigationOptions = { 141 | header: null, 142 | }; 143 | 144 | 145 | export default MainScreen; -------------------------------------------------------------------------------- /src/constants/string.js: -------------------------------------------------------------------------------- 1 | // Bluetooth 2 | export const targetDeviceName = "24Patch"; 3 | export const ecgRawUUID = "00002a06-0000-1000-8000-00805f9b34fb"; 4 | export const emergencyUUID = "00002a3f-0000-1000-8000-00805f9b34fb"; 5 | export const ppgHeartRateUUID = "00002a37-0000-1000-8000-00805f9b34fb"; 6 | export const ppgSpO2UUID = "00002a39-0000-1000-8000-00805f9b34fb"; 7 | // Header 8 | export const HeaderConsent = "회원가입 1/2"; 9 | export const HeaderRegister = "회원가입 2/2"; 10 | export const HeaderConsentTerms = "이용 약관"; 11 | export const HeaderConsentPrivate = "개인정보 처리방침"; 12 | export const HeaderConsentTermsofGeo = "위치기반 서비스 이용약관"; 13 | export const HeaderConsentMarketing = "마케팅정보 수신동의"; 14 | export const HeaderBluetooth = "블루투스"; 15 | export const HeaderVital = "생체신호"; 16 | export const HeaderSymptom = "증상 기록"; 17 | export const HeaderProfile = "설정"; 18 | export const HeaderCalendar = "달력"; 19 | export const HeaderSymptomLog = "증상 목록"; 20 | export const HeaderSymptomDetailFromDevice = "수신된 증상 기록"; 21 | export const HeaderSymptomDetailFromUser = "추가한 증상 기록"; 22 | export const HeaderSetting = "세부 정보"; 23 | 24 | // Label 25 | export const LabelId = "ID"; 26 | export const LabelPassword = "Password"; 27 | export const LabelSignIn = "로그인"; 28 | export const LabelRegister = "회원가입"; 29 | export const LabelFind = "아이디 / 비밀번호 찾기"; 30 | export const LabelConsentTerms = "이용약관 동의 (필수)"; 31 | export const LabelConsentPrivate = "개인정보 처리방침 동의 (필수)"; 32 | export const LabelConsentTermsofGeo = "위치기반 서비스 이용약관 동의 (필수)"; 33 | export const LabelConsentMarketing = "마케팅정보 수신동의 (선택)"; 34 | export const LabelAgreementAll = "전체동의"; 35 | export const LabelAgreement = "다음"; 36 | export const LabelRegisterTitle = "회원정보"; 37 | export const LabelAge = "Age"; 38 | export const LabelSex = "Sex"; 39 | export const LabelHeight = "Height"; 40 | export const LabelWeight = "Weight"; 41 | export const LabelChoose = "선택"; 42 | export const LabelBluetoothToggleOn = "사용 중"; 43 | export const LabelBluetoothToggleOff = "사용 안 함"; 44 | export const LabelBluetoothListTitle = "연결 가능한 디바이스 목록"; 45 | export const LabelBluetoothNotConnect = "건너 뛰기"; 46 | export const LabelNowBPM = "현재 심박수" 47 | export const LabelBPMHighLow = "오늘 최고, 최저 심박수"; 48 | export const LabelSpO2 = "산소포화도"; 49 | export const LabelStress = "스트레스 건강 지수"; 50 | export const LabelNormal = "정상"; 51 | export const LabelWarning = "주의"; 52 | export const LabelEmergency = "위험"; 53 | export const LabelSymptomFromPatch = "수신된 증상"; 54 | export const LabelSymptomFromUser = "내가 추가한 증상"; 55 | export const LabelAddSymptom= "추가"; 56 | export const LabelSelectSymptom = "확인된 증상을 선택 해주세요"; 57 | export const LabelAnxious = "불안함"; 58 | export const LabelArmNeckPain = "팔, 목 통증"; 59 | export const LabelChestPain = "흉통"; 60 | export const LabelDizziness = "어지러움"; 61 | export const LabelFainted = "기절"; 62 | export const LabelFluttering = "두근거림"; 63 | export const LabelLightHeaded = "현기증"; 64 | export const LabelVomiting = "구토(메스꺼움)"; 65 | export const LabelAlertTitle = "위험상황 시 소리 알림"; 66 | export const LabelAlertStatusOn = "사용 중"; 67 | export const LabelAlertStatusOff = "사용 안함"; 68 | export const LabelRegisterContact = "긴급번호 등록하기"; 69 | 70 | export const LabelRegisterButton = "등록"; 71 | export const LabelPhoneListTitle = "등록된 전화번호"; 72 | export const LabelAccountSetting = "계정 관리"; 73 | export const LabelAppSetting = "환경 설정"; 74 | export const LabelSupport = "고객 센터"; 75 | export const LabelReport = "문제 신고"; 76 | export const LabelPrivateInfo = "개인정보 처리방침"; 77 | export const LabelTerms = "이용 약관"; 78 | export const LabelLogout = "로그아웃"; 79 | export const LabelWithdraw = "회원탈퇴"; 80 | 81 | // Placeholder 82 | export const PlaceholderId = "아이디"; 83 | export const PlaceholderPassword = "비밀번호"; 84 | export const PlaceholderPasswordRegister = "비밀번호 : 영문+숫자 8~20자"; 85 | export const PlaceholderPasswordCheck = "비밀번호 확인"; 86 | export const PlaceholderAge = "연령대"; 87 | export const PlaceholderSex = "성별"; 88 | export const PlaceholderHeight = "키"; 89 | export const PlaceholderWeight = "몸무게"; 90 | export const PlaceholderContact = "이름을 입력해주세요"; 91 | 92 | // Error 93 | export const ErrorMsgId = "아이디는 8자 이상이어야 합니다"; 94 | export const ErrorMsgPassword = "비밀번호는 8자 이상이어야 합니다"; 95 | export const ErrorMsgPasswordRegister = "공백을 제외한 8~20자, 영문+숫자 조합으로 설정해주세요"; 96 | export const ErrorMsgPasswordCheck = "위와 동일하게 비밀번호를 입력해주세요"; 97 | export const ErrorMsgLogin = "일치하는 정보가 없습니다. 다시 입력해주세요"; 98 | export const ErrorMsgRegister = "존재하는 아이디입니다"; 99 | 100 | 101 | // Mode 102 | export const ModeAge = "연령대"; 103 | export const ModeSex = "성별"; 104 | export const ModeHeight = "키"; 105 | export const ModeWeight = "몸무게"; -------------------------------------------------------------------------------- /src/screens/SymptomScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import _ from 'lodash'; 4 | import { bindActionCreators } from "redux"; 5 | // Elements 6 | import { 7 | View, ScrollView, AsyncStorage, RefreshControl, PushNotificationIOS 8 | } from 'react-native'; 9 | import { Icon } from 'react-native-elements' 10 | import CustomSymptomItem from '../components/CustomSymptomItem'; 11 | import CustomFAB from '../components/CustomFAB'; 12 | import styles from '../styles/SymptomStyle'; 13 | // Actions 14 | import * as symptomActions from '../reducers/symptom/actions'; 15 | import { 16 | ON_LOG, ON_DETAIL_PATCH, ON_DETAIL_USER 17 | } from '../reducers/nav/actionTypes' 18 | // Strings 19 | import { 20 | HeaderSymptom, 21 | } from '../constants/string'; 22 | // Colors 23 | import { mainColor } from '../constants/color'; 24 | 25 | class SymptomScreen extends Component{ 26 | static navigationOptions = { 27 | title: HeaderSymptom, 28 | headerBackTitle: null, 29 | }; 30 | 31 | constructor(props){ 32 | super(props) 33 | this.state= { 34 | id:'', 35 | token:'', 36 | symptoms:[], 37 | refreshing: false, 38 | } 39 | } 40 | 41 | // Functions 42 | _onRefresh = () => { 43 | this.setState({refreshing: true}); 44 | this._getSymptoms(this.state.id, this.state.token) 45 | .then(this.setState({refreshing: false})) 46 | .catch((e) => {}); 47 | } 48 | 49 | _getSymptoms = async (id, token) => { 50 | const { SymptomActions } = this.props; 51 | 52 | return await SymptomActions.getSymptoms(id, token); 53 | } 54 | 55 | _goToLog = () => { 56 | this.props.goToLog(); 57 | } 58 | 59 | _goToDetailPatch = (symptom) => { 60 | this.props.navigation.navigate('SymptomDetailPatch', { 61 | time: symptom.time, 62 | }); 63 | } 64 | 65 | _goToDetailUser = (symptom) => { 66 | this.props.navigation.navigate('SymptomDetailUser', { 67 | time: symptom.time, 68 | symptoms: symptom.symptoms, 69 | }); 70 | } 71 | 72 | 73 | // LifeCyle 74 | componentDidMount(){ 75 | const { navigation } = this.props; 76 | 77 | AsyncStorage.multiGet(['id', 'token']).then((value) => { // Get Data From LocalStorage 78 | id = value[0][1]; 79 | token = value[1][1]; 80 | 81 | this.setState({id: id, token: token}) 82 | this._getSymptoms(id,token) 83 | .catch((e) =>{}) 84 | }) 85 | } 86 | 87 | render(){ 88 | const { isConnected, symptoms } = this.props; 89 | return( 90 | 91 | 92 | {/* Symptom List */} 93 | 99 | } 100 | > 101 | {_.map(symptoms, symptom => { 102 | _title = symptom.symptoms.length > 1 ? `${symptom.symptoms[0]} 외 ${symptom.symptoms.length-1}개` : `${symptom.symptoms[0]}`; 103 | return ( 104 | symptom.type === "patch" ? this._goToDetailPatch(symptom) : this._goToDetailUser(symptom)} 115 | /> 116 | ); 117 | })} 118 | 119 | 120 | 121 | {/* Floating Action Button */} 122 | {isConnected ? 123 | this._goToLog()} 127 | visible={true} 128 | iconTextComponent={ 129 | 133 | } 134 | /> : null} 135 | 136 | ); 137 | } 138 | } 139 | 140 | export default connect( 141 | (state) => ({ 142 | isConnected: state.bluetooth.isConnected, 143 | symptoms: state.symptom.symptoms, 144 | }), 145 | (dispatch) => ({ 146 | SymptomActions: bindActionCreators(symptomActions, dispatch), 147 | goToLog: () => dispatch({ type: ON_LOG}), 148 | goToDetailPatch: () => dispatch({ type: ON_DETAIL_PATCH}), 149 | goToDetailUser: () => dispatch({ type: ON_DETAIL_USER}), 150 | }) 151 | )(SymptomScreen); -------------------------------------------------------------------------------- /ios/Signalus.xcodeproj/xcshareddata/xcschemes/Signalus-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /ios/Signalus.xcodeproj/xcshareddata/xcschemes/Signalus.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 107 | 108 | 109 | 110 | 111 | 112 | 118 | 120 | 126 | 127 | 128 | 129 | 131 | 132 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/screens/SignInScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from "redux"; 3 | import { connect } from 'react-redux'; 4 | // Elements 5 | import { 6 | Image, View, Text, TouchableOpacity, TouchableWithoutFeedback, Keyboard 7 | } from 'react-native'; 8 | import CustomFormInput from '../components/CustomFormInput'; 9 | import CustomFilledButton from '../components/CustomFilledButton'; 10 | import CustomBorderedButton from '../components/CustomBorderedButton'; 11 | import styles from '../styles/SignInStyle'; 12 | import { defaultMinLength } from '../constants/dimens'; 13 | import { normalize } from '../constants/utils'; 14 | // Actions 15 | import * as loginActions from '../reducers/auth/actions'; 16 | import { 17 | NOT_CONNECTED, ON_CONSENT 18 | } from '../reducers/nav/actionTypes' 19 | // Strings 20 | import { 21 | LabelSignIn, LabelRegister, LabelFind, PlaceholderId, PlaceholderPassword, ErrorMsgId, ErrorMsgPassword, ErrorMsgLogin, HeaderBluetooth 22 | } from '../constants/string'; 23 | // Colors 24 | import { mainColor } from '../constants/color'; 25 | 26 | class SignInScreen extends Component{ 27 | static navigationOptions = { 28 | header: null, 29 | headerBackTitle: null 30 | }; 31 | 32 | constructor(props){ 33 | super(props) 34 | this.state= { 35 | id:'', 36 | password:'', 37 | idError:false, 38 | pwError:false, 39 | } 40 | } 41 | 42 | // Functions 43 | _login = () => { 44 | const { LoginActions } = this.props; 45 | this.setState({idError:false, pwError:false}) 46 | 47 | if(this.state.id.length >= 8 && this.state.password.length >= 8){ // Check Id and Password 48 | try{ 49 | LoginActions.login(this.state.id, this.state.password); 50 | }catch(e){} 51 | } else if(this.state.id.length < defaultMinLength){ 52 | this.setState({idError: true}) 53 | } else if(this.state.password.length < defaultMinLength){ 54 | this.setState({pwError: true}) 55 | } 56 | } 57 | 58 | _find = () => { 59 | 60 | } 61 | 62 | // LifeCycle 63 | componentWillReceiveProps(nextProps) { 64 | const { goToBluetooth, isLoggedIn } = nextProps; 65 | 66 | if(isLoggedIn){ 67 | goToBluetooth(); 68 | } 69 | } 70 | 71 | render(){ 72 | const { error, goToConsent } = this.props; 73 | 74 | return( 75 | 76 | 77 | 78 | {/* Logo */} 79 | 84 | 85 | {/* Error Message */} 86 | 87 | {error && {ErrorMsgLogin}} 88 | 89 | 90 | {/* ID Form */} 91 | this.refs.passwordForm.refs.passwordTextInput.focus()} 93 | blurOnSubmit={false} 94 | style={styles.input} 95 | iconStyle={styles.icon} 96 | isIcon={true} 97 | type="ID" 98 | placeholder={PlaceholderId} 99 | onChangeText={(id) => this.setState({id})} 100 | maxLength={20} 101 | returnKeyType="next" 102 | clearButtonMode="never" 103 | error={this.state.idError} 104 | errorMsg={ErrorMsgId} 105 | /> 106 | 107 | {/* PW Form */} 108 | this._login()} 112 | blurOnSubmit={false} 113 | style={styles.input} 114 | iconStyle={styles.icon} 115 | isIcon={true} 116 | type="PW" 117 | placeholder={PlaceholderPassword} 118 | onChangeText={(password) => this.setState({password})} 119 | maxLength={20} 120 | returnKeyType="done" 121 | clearButtonMode="always" 122 | secureTextEntry={true} 123 | error={this.state.pwError} 124 | errorMsg={ErrorMsgPassword} 125 | /> 126 | 127 | {/* SignIn Button */} 128 | this._login()} 133 | /> 134 | 135 | {/* Find Button */} 136 | 137 | this._find()}> 139 | 142 | {LabelFind} 143 | 144 | 145 | 146 | 147 | {/* Register Button */} 148 | 153 | 154 | 155 | 156 | ); 157 | } 158 | } 159 | 160 | // Redux Connect 161 | export default connect( 162 | (state) => ({ 163 | loading: state.auth.pending, 164 | isLoggedIn : state.auth.isLoggedIn, 165 | error: state.auth.error 166 | }), 167 | (dispatch) => ({ 168 | LoginActions: bindActionCreators(loginActions, dispatch), 169 | goToBluetooth: () => dispatch({ type: NOT_CONNECTED}), 170 | goToConsent: () => dispatch({ type: ON_CONSENT}), 171 | }) 172 | )(SignInScreen); 173 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/screens/SettingScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from "redux"; 3 | import { connect } from 'react-redux'; 4 | // Elements 5 | import { 6 | View, AsyncStorage, PushNotificationIOS 7 | } from 'react-native'; 8 | import { Divider } from 'react-native-elements' 9 | import CustomSimpleTouchableText from '../components/CustomSimpleTouchableText'; 10 | import styles from '../styles/SettingStyle'; 11 | // Actions 12 | import * as withdrawActions from '../reducers/auth/actions'; 13 | import { SIGNOUT } from '../reducers/nav/actionTypes' 14 | import { DISCONNECT_SUCCESS } from '../reducers/bluetooth/actionTypes'; 15 | // Strings 16 | import { 17 | HeaderSetting, 18 | LabelAccountSetting, LabelAppSetting, LabelSupport, LabelReport, 19 | LabelPrivateInfo, LabelTerms, LabelLogout, LabelWithdraw, 20 | } from '../constants/string'; 21 | // Colors 22 | import { disable } from '../constants/color'; 23 | 24 | class Settingscreen extends Component{ 25 | static navigationOptions = { 26 | title: HeaderSetting, 27 | }; 28 | 29 | constructor(props){ 30 | super(props) 31 | this.state= { 32 | id:'', 33 | password:'', 34 | token:'', 35 | } 36 | } 37 | 38 | // Functions 39 | _accountSetting = () => { 40 | 41 | } 42 | 43 | _appSetting = () => { 44 | 45 | } 46 | 47 | _support = () => { 48 | 49 | } 50 | 51 | _report = () => { 52 | 53 | } 54 | 55 | _privateInfo = () => { 56 | 57 | } 58 | 59 | _terms = () => { 60 | 61 | } 62 | 63 | _logout = () => { 64 | const { device, isConnected, disconnect, logout } = this.props; 65 | 66 | if(isConnected){ 67 | device.cancelConnection() 68 | disconnect(); 69 | } 70 | 71 | AsyncStorage.multiRemove(['id', 'pw', 'token', 'device']).then(() => { // Clear LocalStorage 72 | logout(); 73 | }) 74 | } 75 | 76 | _withdraw = () => { 77 | const { device, isConnected, disconnect, WithdrawActions } = this.props; 78 | 79 | if(isConnected){ 80 | device.cancelConnection() 81 | disconnect(); 82 | } 83 | 84 | AsyncStorage.multiRemove(['id', 'pw', 'token', 'device']).then(() => { // Clear LocalStorage 85 | try{ 86 | WithdrawActions.withdraw(this.state.id, this.state.token); // Withdraw the Account 87 | }catch(e){} 88 | }) 89 | } 90 | 91 | // LifeCycle 92 | componentDidMount(){ 93 | AsyncStorage.multiGet(['id', 'pw', 'token']).then((value) => { // Get Data From LocalStorage 94 | id = value[0][1]; 95 | password = value[1][1]; 96 | token = value[2][1]; 97 | 98 | this.setState({id: id, password: password, token: token}) 99 | }) 100 | 101 | PushNotificationIOS.checkPermissions((currentPermissions) => { 102 | console.log('Badges enabled: ' + !!currentPermissions.badge); 103 | console.log('Sounds enabled: ' + !!currentPermissions.sound); 104 | console.log('Alerts enabled: ' + !!currentPermissions.alert); 105 | }); 106 | } 107 | 108 | componentWillReceiveProps(nextProps) { 109 | const { logout, isLoggedIn } = nextProps; 110 | if(!isLoggedIn){ 111 | logout(); 112 | } 113 | } 114 | 115 | render(){ 116 | return( 117 | 118 | 119 | {/* Account Setting */} 120 | this._accountSetting()} 125 | /> 126 | 127 | {/* App Setting */} 128 | this._appSetting()} 133 | /> 134 | 135 | 136 | 137 | {/* Support */} 138 | this._support()} 143 | /> 144 | 145 | {/* Report */} 146 | this._report()} 151 | /> 152 | 153 | {/* Private Infomation */} 154 | this._privateInfo()} 159 | /> 160 | 161 | {/* Terms */} 162 | this._terms()} 167 | /> 168 | 169 | 170 | 171 | {/* Logout */} 172 | this._logout()} 177 | /> 178 | 179 | {/* Withdraw */} 180 | this._withdraw()} 185 | /> 186 | 187 | ); 188 | } 189 | } 190 | 191 | export default connect( 192 | (state) => ({ 193 | device: state.bluetooth.device, 194 | isConnected: state.bluetooth.isConnected, 195 | loading: state.auth.pending, 196 | isLoggedIn : state.auth.isLoggedIn, 197 | error: state.auth.error 198 | }), 199 | (dispatch) => ({ 200 | WithdrawActions: bindActionCreators(withdrawActions, dispatch), 201 | logout: () => dispatch({ type: SIGNOUT}), 202 | disconnect: () => dispatch({ type: DISCONNECT_SUCCESS }) 203 | }) 204 | )(Settingscreen); -------------------------------------------------------------------------------- /src/screens/BluetoothScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from "redux"; 3 | import { connect } from 'react-redux'; 4 | import update from 'react-addons-update'; 5 | import _ from 'lodash'; 6 | import { BleManager } from 'react-native-ble-plx'; 7 | // Elements 8 | import { 9 | View, Text, Switch, Image, TouchableOpacity 10 | } from 'react-native'; 11 | import { Divider } from 'react-native-elements'; 12 | import styles from '../styles/BluetoothStyle'; 13 | import CustomDevicesItem from '../components/CustomDevicesItem'; 14 | import { normalize } from '../constants/utils'; 15 | // Actions 16 | import * as connectActions from '../reducers/bluetooth/actions'; 17 | import { CONNECTED } from '../reducers/nav/actionTypes' 18 | // Strings 19 | import { 20 | HeaderBluetooth, targetDeviceName, 21 | LabelBluetoothToggleOn, LabelBluetoothToggleOff, LabelBluetoothListTitle, LabelBluetoothNotConnect 22 | } from '../constants/string'; 23 | // Colors 24 | import { mainColor, placeholderText } from '../constants/color'; 25 | 26 | 27 | class BluetoothScreen extends Component{ 28 | static navigationOptions = { 29 | title: HeaderBluetooth, 30 | }; 31 | 32 | constructor(props) { 33 | super(props) 34 | this.manager = new BleManager() 35 | this.timeOut 36 | this.state = { 37 | isON: false, 38 | isScanning: false, 39 | isScanned: false, 40 | deviceNames: [], 41 | deviceList: [], 42 | error: false, 43 | errorMsg: "", 44 | } 45 | } 46 | 47 | // Functions 48 | _toggleBluetooth = (value) => { 49 | this.setState({isON: value}) 50 | value ? this._scan() : this._stop() 51 | } 52 | 53 | _scan = () => { 54 | this.setState({isScanning: true, isScanned: false, deviceNames: [], deviceList: []}) 55 | this.timeOut = setTimeout(this._stop, 5000) 56 | this.manager.startDeviceScan(null, 57 | null, (error, device) => { 58 | 59 | if(device){ 60 | console.log("scan : "+device.name) 61 | if(device.name && device.name.startsWith(targetDeviceName)){ 62 | if(this.state.deviceNames.indexOf(device.name) == -1){ 63 | this.setState({ 64 | deviceNames: update( 65 | this.state.deviceNames, 66 | { 67 | $push: [device.name], 68 | }), 69 | deviceList: update( 70 | this.state.deviceList, 71 | { 72 | $push: [device], 73 | }), 74 | }) 75 | } 76 | } 77 | } 78 | 79 | if (error) { 80 | this.manager.stopDeviceScan() 81 | clearTimeout(this.timeOut) 82 | this.setState({isON: false, isScanning:false, isScanned:false, error: true, errorMsg: error.message}) 83 | return 84 | } 85 | }); 86 | } 87 | 88 | _stop = () => { 89 | this.manager.stopDeviceScan() 90 | this.setState({isScanning: false, isScanned: true, error: false}) 91 | clearTimeout(this.timeOut) 92 | console.log('scan stop') 93 | } 94 | 95 | _connect = (device) => { 96 | const { ConnectActions } = this.props; 97 | 98 | this._stop(); 99 | try{ 100 | ConnectActions.connect(device); 101 | }catch(e){} 102 | } 103 | 104 | // LifeCycle 105 | componentDidMount(){ 106 | 107 | } 108 | 109 | componentWillReceiveProps(nextProps) { 110 | const { goToMain, isConnected, error } = nextProps; 111 | 112 | if(error) this.setState({isON: false, isScanning:false, isScanned:false, error: true, errorMsg: error.message}) 113 | else if(isConnected) goToMain(); 114 | } 115 | 116 | render(){ 117 | const { goToMain } = this.props; 118 | 119 | return( 120 | 121 | 122 | {/* Header View */} 123 | 124 | 125 | {this.state.isON ? LabelBluetoothToggleOn : LabelBluetoothToggleOff} 126 | 127 | 128 | {this.state.isScanning ? 129 | : null 133 | } 134 | 139 | 140 | 141 | 142 | {/* ListTitle */} 143 | 144 | {this.state.error ? this.state.errorMsg : LabelBluetoothListTitle } 145 | 146 | 147 | {/* Device List */} 148 | {this.state.isON ? 149 | 150 | 151 | {_.map(this.state.deviceList, device => { 152 | return ( 153 | this._connect(device)} 160 | /> 161 | ); 162 | })} 163 | 164 | : null 165 | } 166 | 167 | {/* Next */} 168 | {!this.state.isON ? 169 | 170 | 172 | 175 | {LabelBluetoothNotConnect} 176 | 177 | 178 | 179 | : null 180 | } 181 | 182 | 183 | ); 184 | } 185 | } 186 | 187 | export default connect( 188 | (state) => ({ 189 | isConnected: state.bluetooth.isConnected, 190 | loading: state.bluetooth.pending, 191 | error: state.bluetooth.error, 192 | }), 193 | (dispatch) => ({ 194 | ConnectActions: bindActionCreators(connectActions, dispatch), 195 | goToMain: () => dispatch({ type: CONNECTED}), 196 | }) 197 | )(BluetoothScreen); -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion rootProject.ext.compileSdkVersion 98 | buildToolsVersion rootProject.ext.buildToolsVersion 99 | 100 | defaultConfig { 101 | applicationId "com.signalus" 102 | minSdkVersion rootProject.ext.minSdkVersion 103 | targetSdkVersion rootProject.ext.targetSdkVersion 104 | versionCode 1 105 | versionName "1.0" 106 | ndk { 107 | abiFilters "armeabi-v7a", "x86" 108 | } 109 | } 110 | splits { 111 | abi { 112 | reset() 113 | enable enableSeparateBuildPerCPUArchitecture 114 | universalApk false // If true, also generate a universal APK 115 | include "armeabi-v7a", "x86" 116 | } 117 | } 118 | buildTypes { 119 | release { 120 | minifyEnabled enableProguardInReleaseBuilds 121 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 122 | } 123 | } 124 | // applicationVariants are e.g. debug, release 125 | applicationVariants.all { variant -> 126 | variant.outputs.each { output -> 127 | // For each separate APK per architecture, set a unique version code as described here: 128 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 129 | def versionCodes = ["armeabi-v7a":1, "x86":2] 130 | def abi = output.getFilter(OutputFile.ABI) 131 | if (abi != null) { // null for the universal-debug, universal-release variants 132 | output.versionCodeOverride = 133 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 134 | } 135 | } 136 | } 137 | } 138 | 139 | dependencies { 140 | compile project(':react-native-fs') 141 | compile project(':react-native-notifications') 142 | compile project(':react-native-sms') 143 | compile project(':react-native-contacts') 144 | compile project(':react-native-linear-gradient') 145 | compile project(':react-native-ble-plx') 146 | compile project(':react-native-svg') 147 | compile project(':react-native-vector-icons') 148 | compile fileTree(dir: "libs", include: ["*.jar"]) 149 | compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" 150 | compile "com.facebook.react:react-native:+" // From node_modules 151 | } 152 | 153 | // Run this once to be able to run the application with BUCK 154 | // puts all compile dependencies into folder libs for BUCK to use 155 | task copyDownloadableDepsToLibs(type: Copy) { 156 | from configurations.compile 157 | into 'libs' 158 | } 159 | -------------------------------------------------------------------------------- /src/screens/SymptomLogScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import _ from "lodash"; 3 | import { bindActionCreators } from "redux"; 4 | import update from 'react-addons-update'; 5 | import { connect } from 'react-redux'; 6 | // Elements 7 | import { 8 | View, Text, TouchableOpacity, AsyncStorage, 9 | } from 'react-native'; 10 | import { Divider } from 'react-native-elements' 11 | import CustomSymptomCheckBox from '../components/CustomSymptomCheckBox'; 12 | import styles from '../styles/SymptomLogStyle'; 13 | // Actions 14 | import * as symptomActions from '../reducers/symptom/actions'; 15 | import { DISCONNECT_SUCCESS } from '../reducers/bluetooth/actionTypes'; 16 | // Strings 17 | import { 18 | HeaderSymptomLog, LabelAddSymptom, LabelSelectSymptom, 19 | LabelAnxious, LabelArmNeckPain, LabelChestPain, LabelDizziness, 20 | LabelFainted, LabelFluttering, LabelLightHeaded, LabelVomiting, 21 | } from '../constants/string'; 22 | // Colors 23 | import { disable } from '../constants/color'; 24 | import { getToday, getTimeForNow } from '../constants/utils'; 25 | 26 | class SymptomLogScreen extends Component{ 27 | static navigationOptions = ({ navigation }) => { 28 | return{ 29 | title: HeaderSymptomLog, 30 | headerBackTitle: null, 31 | headerRight: ( 32 | 33 | 34 | {LabelAddSymptom} 35 | 36 | 37 | ), 38 | }}; 39 | 40 | constructor(props){ 41 | super(props) 42 | this.state= { 43 | id:'', 44 | token:'', 45 | anxious: false, 46 | armNeckPain: false, 47 | chestPain: false, 48 | dizziness: false, 49 | fainted: false, 50 | fluttering: false, 51 | lightHeaded: false, 52 | vomiting: false, 53 | } 54 | } 55 | 56 | // Functions 57 | _addSymptom = () => { 58 | const { SymptomActions } = this.props; 59 | 60 | let symptoms = [] 61 | 62 | if(this.state.anxious){ 63 | symptoms.push(LabelAnxious) 64 | } 65 | 66 | if(this.state.armNeckPain){ 67 | symptoms.push(LabelArmNeckPain) 68 | } 69 | 70 | if(this.state.chestPain){ 71 | symptoms.push(LabelChestPain) 72 | } 73 | 74 | if(this.state.dizziness){ 75 | symptoms.push(LabelDizziness) 76 | } 77 | 78 | if(this.state.fainted){ 79 | symptoms.push(LabelFainted) 80 | } 81 | 82 | if(this.state.fluttering){ 83 | symptoms.push(LabelFluttering) 84 | } 85 | 86 | if(this.state.lightHeaded){ 87 | symptoms.push(LabelLightHeaded) 88 | } 89 | 90 | if(this.state.vomiting){ 91 | symptoms.push(LabelVomiting) 92 | } 93 | 94 | if(symptoms.length > 0){ 95 | try{ 96 | SymptomActions.addSymptom(this.state.id, symptoms, getTimeForNow(), "device", this.state.token) 97 | }catch(e){} 98 | } 99 | else{ 100 | alert("증상을 최소 1개 이상 선택해주세요!") 101 | } 102 | }; 103 | 104 | _getSymptoms = async (id, token) => { 105 | const { SymptomActions } = this.props; 106 | 107 | return await SymptomActions.getSymptoms(id, token); 108 | } 109 | 110 | // LifeCycle 111 | componentDidMount(){ 112 | const { navigation } = this.props; 113 | console.log(this.props) 114 | 115 | AsyncStorage.multiGet(['id', 'token']).then((value) => { // Get Data From LocalStorage 116 | id = value[0][1]; 117 | token = value[1][1]; 118 | 119 | this.setState({id: id, token: token}) 120 | }) 121 | 122 | navigation.setParams({ addSymptom: this._addSymptom }); 123 | } 124 | 125 | componentWillReceiveProps(nextProps) { 126 | const { navigation, device, isConnected, error, isRegisterd, } = nextProps; 127 | if(isRegisterd){ 128 | this._getSymptoms(this.state.id,this.state.token) 129 | .catch((e) =>{}) 130 | navigation.pop(); 131 | alert("등록하였습니다!") 132 | } 133 | if(error){ 134 | alert("등록에 실패하였습니다..") 135 | } 136 | } 137 | 138 | render(){ 139 | return( 140 | 141 | 142 | {/* Header */} 143 | 144 | {getToday()} 145 | {LabelSelectSymptom} 146 | 147 | 148 | 149 | 150 | {/* Symptom CheckBox List */} 151 | this.setState({anxious: !this.state.anxious})} 154 | onTouch={() => console.log("CLICK")} 155 | title={LabelAnxious} 156 | /> 157 | 158 | this.setState({armNeckPain: !this.state.armNeckPain})} 161 | onTouch={() => console.log("CLICK")} 162 | title={LabelArmNeckPain} 163 | /> 164 | 165 | this.setState({chestPain: !this.state.chestPain})} 168 | onTouch={() => console.log("CLICK")} 169 | title={LabelChestPain} 170 | /> 171 | 172 | this.setState({dizziness: !this.state.dizziness})} 175 | onTouch={() => console.log("CLICK")} 176 | title={LabelDizziness} 177 | /> 178 | 179 | this.setState({fainted: !this.state.fainted})} 182 | onTouch={() => console.log("CLICK")} 183 | title={LabelFainted} 184 | /> 185 | 186 | this.setState({fluttering: !this.state.fluttering})} 189 | onTouch={() => console.log("CLICK")} 190 | title={LabelFluttering} 191 | /> 192 | 193 | this.setState({lightHeaded: !this.state.lightHeaded})} 196 | onTouch={() => console.log("CLICK")} 197 | title={LabelLightHeaded} 198 | /> 199 | 200 | this.setState({vomiting: !this.state.vomiting})} 203 | onTouch={() => console.log("CLICK")} 204 | title={LabelVomiting} 205 | /> 206 | 207 | 208 | ); 209 | } 210 | } 211 | 212 | export default connect( 213 | (state) => ({ 214 | device: state.bluetooth.device, 215 | isConnected: state.bluetooth.isConnected, 216 | error: state.symptom.error, 217 | isRegisterd: state.symptom.isRegisterd, 218 | }), 219 | (dispatch) => ({ 220 | SymptomActions: bindActionCreators(symptomActions, dispatch), 221 | disconnect: () => dispatch({ type: DISCONNECT_SUCCESS }) 222 | }) 223 | )(SymptomLogScreen); --------------------------------------------------------------------------------