├── .watchmanconfig ├── documentation ├── StyledText.md ├── TabBarIcon.md ├── ViewEditNote.md ├── PillCard.md ├── PillsLoggedTodayCard.md └── CalendarNote.md ├── assets ├── images │ ├── icon.png │ ├── splash.png │ ├── pill-temp.png │ ├── robot-dev.png │ └── robot-prod.png └── fonts │ └── SpaceMono-Regular.ttf ├── babel.config.js ├── .gitignore ├── components ├── StyledText.js ├── __tests__ │ ├── StyledText-test.js │ ├── __snapshots__ │ │ ├── StyledText-test.js.snap │ │ ├── PillCard-test.js.snap │ │ └── PillsLoggedTodayCard-test.js.snap │ ├── PillCard-test.js │ └── PillsLoggedTodayCard-test.js ├── TabBarIcon.js ├── IntensityOption.js ├── HelpQuestionAnswer.js ├── SwitchButton.js ├── HelpTextBox.js ├── AccountDetailsOption.js ├── SymptomsFeelings.js ├── ViewEditAccountInfoTextInput.js ├── SettingsTitleBox.js ├── AddSymptomFeelingOption.js ├── ChangePasswordTextInput.js ├── SettingsOption.js ├── EditPill.js ├── PillCard.js ├── CalendarNote.js └── PillsLoggedTodayCard.js ├── constants ├── Layout.js └── Colors.js ├── .expo-shared └── assets.json ├── themes └── themes.ts ├── __tests__ ├── __snapshots__ │ └── App-test.js.snap └── App-test.js ├── app.json ├── navigation ├── AppNavigator.js ├── AppNavigator.web.js ├── AuthNavigator.js └── MainTabNavigator.js ├── hooks ├── useLog_Feeling.js ├── useLog_Symptom.js ├── useTakes.js ├── useCalendar.js ├── useName.js ├── useMedication.js ├── useLog_Pills.js ├── useFeeling.js ├── useSymptom.js └── useAuth.js ├── screens ├── AuthLoadingScreen.js ├── AccountDetailsScreen.js ├── HelpScreen.js ├── AlexaScreen.js ├── ViewEditAccountInfoScreen.js ├── ChangePasswordScreen.js ├── HomeScreen.js ├── EditPillsScreen.js ├── LogAndChartsScreen.js ├── NewPillDataScreen.js ├── SettingsScreen.js ├── EditPillDataScreen.js ├── CalendarScreen.js ├── LoginScreen.js ├── SignUpScreen.js ├── AddFeelingScreen.js ├── AddSymptomScreen.js └── TodaysNoteScreen.js ├── README.md ├── package.json ├── create-doc.js └── App.js /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /documentation/StyledText.md: -------------------------------------------------------------------------------- 1 | ## MonoText 2 | 3 | This component does not have any props. 4 | 5 | -------------------------------------------------------------------------------- /documentation/TabBarIcon.md: -------------------------------------------------------------------------------- 1 | ## TabBarIcon 2 | 3 | This component does not have any props. 4 | 5 | -------------------------------------------------------------------------------- /documentation/ViewEditNote.md: -------------------------------------------------------------------------------- 1 | ## ViewEditNote 2 | 3 | This component does not have any props. 4 | 5 | -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompSciLauren/pill-pal/master/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompSciLauren/pill-pal/master/assets/images/splash.png -------------------------------------------------------------------------------- /assets/images/pill-temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompSciLauren/pill-pal/master/assets/images/pill-temp.png -------------------------------------------------------------------------------- /assets/images/robot-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompSciLauren/pill-pal/master/assets/images/robot-dev.png -------------------------------------------------------------------------------- /assets/images/robot-prod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompSciLauren/pill-pal/master/assets/images/robot-prod.png -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompSciLauren/pill-pal/master/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | web-report/ 12 | -------------------------------------------------------------------------------- /components/StyledText.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text } from 'react-native'; 3 | 4 | export function MonoText(props) { 5 | return ( 6 | 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /constants/Layout.js: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native'; 2 | 3 | const width = Dimensions.get('window').width; 4 | const height = Dimensions.get('window').height; 5 | 6 | export default { 7 | window: { 8 | width, 9 | height, 10 | }, 11 | isSmallDevice: width < 375, 12 | }; 13 | -------------------------------------------------------------------------------- /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "0cae4d70c6df3e5e96ee8b5c442b59d55c8ab8deb466992ab9abc523822f2a1b": true, 3 | "e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true, 4 | "af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true, 5 | "e7fc0741cc6562975a990e3d9ef820571588dab20aba97032df9f00caa9cd57a": true 6 | } -------------------------------------------------------------------------------- /components/__tests__/StyledText-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { MonoText } from '../StyledText'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer.create(Snapshot test!).toJSON(); 8 | 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /components/__tests__/__snapshots__/StyledText-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders correctly 1`] = ` 4 | 14 | Snapshot test! 15 | 16 | `; 17 | -------------------------------------------------------------------------------- /constants/Colors.js: -------------------------------------------------------------------------------- 1 | const tintColor = '#2f95dc'; 2 | 3 | export default { 4 | tintColor, 5 | tabIconDefault: '#ccc', 6 | tabIconSelected: tintColor, 7 | tabBar: '#fefefe', 8 | errorBackground: 'red', 9 | errorText: '#fff', 10 | warningBackground: '#EAEB5E', 11 | warningText: '#666804', 12 | noticeBackground: tintColor, 13 | noticeText: '#fff', 14 | }; 15 | -------------------------------------------------------------------------------- /themes/themes.ts: -------------------------------------------------------------------------------- 1 | import { registerThemes } from "react-native-themed-styles" 2 | 3 | const light = { backgroundColor: "white", textColor: "black", shadow:"black" } 4 | const dark = { backgroundColor: "black", textColor: "white", shadow: "white" } 5 | 6 | const styleSheetFactory = registerThemes( 7 | { light, dark }, 8 | () => "light" 9 | ) 10 | 11 | export { styleSheetFactory } -------------------------------------------------------------------------------- /components/__tests__/PillCard-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { PillCard } from '../PillCard'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer 8 | .create() 9 | .toJSON(); 10 | 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /documentation/PillCard.md: -------------------------------------------------------------------------------- 1 | ## PillCard 2 | 3 | prop | type | default | required | description 4 | ---- | :----: | :-------: | :--------: | ----------- 5 | **dosage** | `dosage` | | :x: | The quantity of pills to be taken. 6 | **formattedTimeLeft** | `formattedTimeLeft` | | :x: | The amount of time left before the user needs to take their next medication. 7 | **name** | `name` | | :x: | The name of the pill to take. 8 | 9 | -------------------------------------------------------------------------------- /components/TabBarIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Ionicons } from '@expo/vector-icons'; 3 | 4 | import Colors from '../constants/Colors'; 5 | 6 | export default function TabBarIcon(props) { 7 | return ( 8 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/App-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`App renders the loading screen 1`] = ` 4 | 9 | `; 10 | 11 | exports[`App renders the root without loading screen 1`] = ` 12 | 20 | 21 | 22 | `; 23 | -------------------------------------------------------------------------------- /components/IntensityOption.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text } from 'react-native'; 3 | 4 | export function IntensityOption(props) { 5 | const { intensityValue, intensitySelected } = props; 6 | 7 | return ( 8 | 17 | {intensityValue} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /documentation/PillsLoggedTodayCard.md: -------------------------------------------------------------------------------- 1 | ## PillsLoggedTodayCard 2 | 3 | prop | type | default | required | description 4 | ---- | :----: | :-------: | :--------: | ----------- 5 | **infoArray** | `Array[]` | | :x: | An array containing the information for the card. 6 | **infoArray[].dosage** | `String` | | :x: | 7 | **infoArray[].formattedTimeTaken** | `String` | | :x: | 8 | **infoArray[].name** | `String` | | :x: | 9 | **title** | `String` | | :x: | The header that is displayed at the top to describe the purpose of the card. 10 | 11 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "PillPal", 4 | "slug": "pill-pal", 5 | "privacy": "public", 6 | "platforms": [ 7 | "ios", 8 | "android", 9 | "web" 10 | ], 11 | "version": "1.0.0", 12 | "orientation": "portrait", 13 | "icon": "./assets/images/icon.png", 14 | "splash": { 15 | "image": "./assets/images/splash.png", 16 | "resizeMode": "contain", 17 | "backgroundColor": "#ffffff" 18 | }, 19 | "updates": { 20 | "fallbackToCacheTimeout": 0 21 | }, 22 | "assetBundlePatterns": [ 23 | "**/*" 24 | ], 25 | "ios": { 26 | "supportsTablet": true 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /components/HelpQuestionAnswer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Platform, 4 | StyleSheet, 5 | Text, 6 | View, 7 | } from 'react-native'; 8 | 9 | export function HelpQuestionAnswer(props) { 10 | const { question, answer } = props; 11 | return ( 12 | 13 | Q: { question } 14 | A: { answer } 15 | 16 | ); 17 | } 18 | 19 | const styles = StyleSheet.create({ 20 | container: { 21 | paddingBottom: 10 22 | }, 23 | question: { 24 | fontWeight: 'bold' 25 | }, 26 | answer: { 27 | fontStyle: 'italic' 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /navigation/AppNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createAppContainer, createSwitchNavigator } from 'react-navigation'; 3 | 4 | import MainTabNavigator from './MainTabNavigator'; 5 | import AuthNavigator from './AuthNavigator'; 6 | import AuthLoadingScreen from '../screens/AuthLoadingScreen'; 7 | 8 | export default createAppContainer( 9 | createSwitchNavigator( 10 | { 11 | // You could add another route here for authentication. 12 | // Read more at https://reactnavigation.org/docs/en/auth-flow.html 13 | AuthLoading: AuthLoadingScreen, 14 | Main: MainTabNavigator, 15 | Auth: AuthNavigator, 16 | }, 17 | { 18 | initialRouteName: 'AuthLoading', 19 | } 20 | ) 21 | ); 22 | -------------------------------------------------------------------------------- /documentation/CalendarNote.md: -------------------------------------------------------------------------------- 1 | ## CalendarNote 2 | 3 | prop | type | default | required | description 4 | ---- | :----: | :-------: | :--------: | ----------- 5 | **additionalDetails** | `PropTypes.string` | | :x: | Any additional information that the user added for the selected date. 6 | **feelings** | `PropTypes.string` | | :x: | The list of feelings that the user logged for the selected date. 7 | **pillsTaken** | `PropTypes.string` | | :x: | The name and quantity of every type of pill taken on the selected date. 8 | **symptoms** | `PropTypes.string` | | :x: | The list of symptoms that the user logged for the selected date. 9 | **todaysDate** | `PropTypes.string` | | :x: | The header that is displayed at the top to indicate what the date is for the selected calendar note. 10 | 11 | -------------------------------------------------------------------------------- /navigation/AppNavigator.web.js: -------------------------------------------------------------------------------- 1 | import { createBrowserApp } from '@react-navigation/web'; 2 | import { createSwitchNavigator } from 'react-navigation'; 3 | 4 | import MainTabNavigator from './MainTabNavigator'; 5 | import AuthNavigator from './AuthNavigator'; 6 | import AuthLoadingScreen from '../screens/AuthLoadingScreen'; 7 | 8 | const switchNavigator = createSwitchNavigator( 9 | { 10 | // You could add another route here for authentication. 11 | // Read more at https://reactnavigation.org/docs/en/auth-flow.html 12 | AuthLoading: AuthLoadingScreen, 13 | Main: MainTabNavigator, 14 | Auth: AuthNavigator, 15 | }, 16 | { 17 | initialRouteName: 'AuthLoading', 18 | } 19 | ); 20 | switchNavigator.path = ''; 21 | 22 | export default createBrowserApp(switchNavigator, { history: 'hash' }); 23 | -------------------------------------------------------------------------------- /components/SwitchButton.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { View, Switch, StyleSheet } from "react-native"; 3 | 4 | 5 | export default function App() { 6 | const [isEnabled, setIsEnabled] = useState(false); 7 | const toggleSwitch = () => setIsEnabled(previousState => !previousState); 8 | 9 | return ( 10 | 11 | 18 | 19 | ); 20 | } 21 | 22 | const styles = StyleSheet.create({ 23 | container: { 24 | flex: 1, 25 | alignItems: "center", 26 | justifyContent: "center" 27 | } 28 | }); -------------------------------------------------------------------------------- /__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import NavigationTestUtils from 'react-navigation/NavigationTestUtils'; 3 | import renderer from 'react-test-renderer'; 4 | 5 | import App from '../App'; 6 | 7 | jest.mock('expo', () => ({ 8 | AppLoading: 'AppLoading', 9 | })); 10 | 11 | jest.mock('../navigation/AppNavigator', () => 'AppNavigator'); 12 | 13 | describe('App', () => { 14 | jest.useFakeTimers(); 15 | 16 | beforeEach(() => { 17 | NavigationTestUtils.resetInternalState(); 18 | }); 19 | 20 | it(`renders the loading screen`, () => { 21 | const tree = renderer.create().toJSON(); 22 | expect(tree).toMatchSnapshot(); 23 | }); 24 | 25 | it(`renders the root without loading screen`, () => { 26 | const tree = renderer.create().toJSON(); 27 | expect(tree).toMatchSnapshot(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /hooks/useLog_Feeling.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useLog_Feeling(personIdentifier) { 4 | const [feeling, setFeeling] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | setIsLoading(true); 9 | 10 | fetch(`https://pillpal-app.de/Log_Feelings/${personIdentifier}`, { 11 | method: 'GET', 12 | }) 13 | .then((response) => response.json()) 14 | //If response is in json then in success 15 | .then((responseJson) => { 16 | //Success 17 | setIsLoading(false); 18 | setFeeling(responseJson); 19 | }) 20 | //If response is not in json then in error 21 | .catch((error) => { 22 | //Error 23 | console.error(error); 24 | }); 25 | }, [personIdentifier]); 26 | 27 | return { 28 | feeling, 29 | isLoading, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /hooks/useLog_Symptom.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useLog_Symptom(personIdentifier) { 4 | const [symptom, setSymptom] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | setIsLoading(true); 9 | 10 | fetch(`https://pillpal-app.de/Log_Symptoms/${personIdentifier}`, { 11 | method: 'GET', 12 | }) 13 | .then((response) => response.json()) 14 | //If response is in json then in success 15 | .then((responseJson) => { 16 | //Success 17 | setIsLoading(false); 18 | setSymptom(responseJson); 19 | }) 20 | //If response is not in json then in error 21 | .catch((error) => { 22 | //Error 23 | console.error(error); 24 | }); 25 | }, [personIdentifier]); 26 | 27 | return { 28 | symptom, 29 | isLoading, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /hooks/useTakes.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useTakes(personIdentifier) { 4 | const [takes, setTakes] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | if (personIdentifier !== null) { 9 | setIsLoading(true); 10 | 11 | fetch(`https://pillpal-app.de/Takes/${personIdentifier}`, { 12 | method: 'GET', 13 | }) 14 | .then((response) => response.json()) 15 | //If response is in json then in success 16 | .then((responseJson) => { 17 | //Success 18 | setIsLoading(false); 19 | setTakes(responseJson); 20 | }) 21 | //If response is not in json then in error 22 | .catch((error) => { 23 | //Error 24 | console.error(error); 25 | }); 26 | } 27 | }, [personIdentifier]); 28 | 29 | return { 30 | takes, 31 | isLoading, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /hooks/useCalendar.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useCalendar(personIdentifier) { 4 | const [calendar, setCalendar] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | if (personIdentifier !== null) { 9 | setIsLoading(true); 10 | 11 | fetch(`https://pillpal-app.de/Log_Notes/${personIdentifier}`, { 12 | method: 'GET', 13 | }) 14 | .then((response) => response.json()) 15 | //If response is in json then in success 16 | .then((responseJson) => { 17 | //Success 18 | setIsLoading(false); 19 | setCalendar(responseJson); 20 | }) 21 | //If response is not in json then in error 22 | .catch((error) => { 23 | //Error 24 | console.error(error); 25 | }); 26 | } 27 | }, [personIdentifier]); 28 | 29 | return { 30 | calendar, 31 | isLoading, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /components/__tests__/PillsLoggedTodayCard-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { PillsLoggedTodayCard } from '../PillsLoggedTodayCard'; 5 | 6 | // mock data to represent pills the user has logged so far today 7 | const pillsLoggedToday = [ 8 | { 9 | id: 0, 10 | name: 'Ibuprofen', 11 | dosage: '2 pills', 12 | formattedTimeTaken: '8:30 a.m.', 13 | }, 14 | { 15 | id: 1, 16 | name: 'Ibuprofen', 17 | dosage: '2 pills', 18 | formattedTimeTaken: '1:07 p.m.', 19 | }, 20 | { 21 | id: 2, 22 | name: 'Nitroglycerin', 23 | dosage: '1 pill', 24 | formattedTimeTaken: '3:45 p.m.', 25 | }, 26 | ]; 27 | 28 | it(`renders correctly`, () => { 29 | const tree = renderer 30 | .create( 31 | 35 | ) 36 | .toJSON(); 37 | 38 | expect(tree).toMatchSnapshot(); 39 | }); 40 | -------------------------------------------------------------------------------- /components/HelpTextBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Platform, 4 | StyleSheet, 5 | Text, 6 | View, 7 | } from 'react-native'; 8 | 9 | export function HelpTextBox(props) { 10 | const { titleText } = props; 11 | return ( 12 | 13 | { titleText } 14 | 15 | ); 16 | } 17 | 18 | const styles = StyleSheet.create({ 19 | titleContainer: { 20 | backgroundColor: 'rgb(205, 121, 209)', 21 | paddingTop: 10, 22 | paddingBottom: 10, 23 | paddingHorizontal: 10, 24 | ...Platform.select({ 25 | ios: { 26 | shadowColor: 'black', 27 | shadowOffset: { width: 0, height: -3 }, 28 | shadowOpacity: 0.1, 29 | shadowRadius: 1, 30 | }, 31 | android: { 32 | elevation: 3, 33 | }, 34 | }), 35 | }, 36 | titleText: { 37 | fontSize: 20, 38 | color: 'rgba(70, 70, 70, 1)', 39 | textAlign: 'left', 40 | fontWeight: 'bold', 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /screens/AuthLoadingScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ActivityIndicator, AsyncStorage, StatusBar, StyleSheet, Text, View } from 'react-native'; 3 | 4 | export default class AuthLoadingScreen extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this._loadData = this._loadData.bind(this); 8 | this._loadData(); 9 | } 10 | 11 | _loadData = async() => { 12 | const { navigate } = this.props.navigation; 13 | const isLoggedIn = await AsyncStorage.getItem('isLoggedIn'); 14 | navigate((isLoggedIn !== '1') ? 'Auth' : 'Main'); 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | } 26 | 27 | AuthLoadingScreen.navigationOptions = { 28 | header: null, 29 | }; 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | flex: 1, 34 | justifyContent: 'center', 35 | alignItems: 'center' 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /hooks/useName.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useName(personIdentifier) { 4 | const [name, setName] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | if (personIdentifier !== null) { 9 | setIsLoading(true); 10 | 11 | if (personIdentifier !== null) { 12 | fetch(`https://pillpal-app.de/User/${personIdentifier}`, { 13 | method: 'GET', 14 | }) 15 | .then((response) => response.json()) 16 | //If response is in json then in success 17 | .then((responseJson) => { 18 | //Success 19 | setIsLoading(false); 20 | setName(responseJson[0]); 21 | }) 22 | //If response is not in json then in error 23 | .catch((error) => { 24 | //Error 25 | console.error(error); 26 | }); 27 | } 28 | } 29 | }, [personIdentifier]); 30 | 31 | return { 32 | name, 33 | isLoading, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /hooks/useMedication.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useMedication() { 4 | const [medication, setMedication] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | const emptyJson = { 8 | ID: -1, 9 | Display_Name: 'none', 10 | }; 11 | 12 | useEffect(() => { 13 | setIsLoading(true); 14 | 15 | fetch(`https://pillpal-app.de/Medication`, { 16 | method: 'GET', 17 | }) 18 | .then((response) => response.json()) 19 | //If response is in json then in success 20 | .then((responseJson) => { 21 | //Success 22 | setIsLoading(false); 23 | if (responseJson[0] != null) { 24 | setMedication(responseJson); 25 | } else { 26 | setMedication(emptyJson); 27 | } 28 | }) 29 | //If response is not in json then in error 30 | .catch((error) => { 31 | //Error 32 | console.error(error); 33 | }); 34 | }, []); 35 | 36 | return { 37 | medication, 38 | isLoading, 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /navigation/AuthNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Platform } from 'react-native'; 3 | import { 4 | createStackNavigator, 5 | createBottomTabNavigator, 6 | } from 'react-navigation'; 7 | 8 | import LoginScreen from '../screens/LoginScreen'; 9 | import SignUpScreen from '../screens/SignUpScreen'; 10 | 11 | const config = Platform.select({ 12 | web: { headerMode: 'screen' }, 13 | default: {}, 14 | }); 15 | 16 | const LoginStack = createStackNavigator( 17 | { 18 | Login: LoginScreen, 19 | SignUp: SignUpScreen, 20 | }, 21 | config 22 | ); 23 | 24 | LoginStack.navigationOptions = { 25 | header: null 26 | } 27 | 28 | LoginStack.path = ''; 29 | 30 | const SignUpStack = createStackNavigator( 31 | { 32 | SignUp: SignUpScreen, 33 | Login: LoginScreen, 34 | }, 35 | config 36 | ); 37 | 38 | SignUpStack.navigationOptions = { 39 | header: null 40 | } 41 | 42 | SignUpStack.path = ''; 43 | 44 | const tabNavigator = createStackNavigator({ 45 | Login: {screen: LoginStack}, 46 | SignUp: {screen: SignUpStack} 47 | }); 48 | 49 | tabNavigator.path = ''; 50 | 51 | export default tabNavigator; 52 | -------------------------------------------------------------------------------- /hooks/useLog_Pills.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useLog_Pills(personIdentifier) { 4 | const [logPills, setLogPills] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | const emptyJson = { 8 | Medication_ID: -1, 9 | Datetime: -1, 10 | Amount: -1, 11 | Taken: -1, 12 | }; 13 | 14 | useEffect(() => { 15 | if (personIdentifier !== null) { 16 | setIsLoading(true); 17 | 18 | fetch(`https://pillpal-app.de/Log_Pills/${personIdentifier}`, { 19 | method: 'GET', 20 | }) 21 | .then((response) => response.json()) 22 | //If response is in json then in success 23 | .then((responseJson) => { 24 | //Success 25 | setIsLoading(false); 26 | if (responseJson != null) { 27 | setLogPills(responseJson); 28 | } else { 29 | setLogPills(emptyJson); 30 | } 31 | }) 32 | //If response is not in json then in error 33 | .catch((error) => { 34 | //Error 35 | console.error(error); 36 | }); 37 | } 38 | }, [personIdentifier]); 39 | 40 | return { 41 | logPills, 42 | isLoading, 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /screens/AccountDetailsScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, TouchableOpacity, View } from 'react-native'; 3 | import AccountDetailsOption from '../components/AccountDetailsOption'; 4 | import useAuth from '../hooks/useAuth'; 5 | 6 | const AccountDetailsScreen = (props) => { 7 | const { login, isLoggedIn, isLoading, logout } = useAuth(); 8 | 9 | const { navigate } = props.navigation; 10 | return ( 11 | 12 | navigate('ViewEditAccountInfo')}> 13 | 14 | 15 | navigate('ChangePassword')}> 16 | 17 | 18 | logout()}> 19 | 20 | 21 | 22 | ); 23 | }; 24 | 25 | AccountDetailsScreen.navigationOptions = { 26 | title: 'Account Details', 27 | }; 28 | 29 | export default AccountDetailsScreen; 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | flex: 1, 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /components/AccountDetailsOption.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Divider } from 'react-native-elements'; 3 | import { Platform, StyleSheet, Text, View } from 'react-native'; 4 | 5 | export default class ViewEditAccountInfoTextInput extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | { this.props.option } 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | 24 | const styles = StyleSheet.create({ 25 | optionContainer: { 26 | paddingHorizontal: 15, 27 | }, 28 | optionContainerWarning: { 29 | paddingHorizontal: 15, 30 | backgroundColor: 'rgb(212, 47, 118)', 31 | }, 32 | leftSide: { 33 | flex: 1, 34 | flexDirection: 'row', 35 | justifyContent: 'flex-start', 36 | }, 37 | info: { 38 | paddingVertical: 15, 39 | }, 40 | text: { 41 | fontSize: 18 42 | }, 43 | textWarning: { 44 | fontSize: 18, 45 | color: 'white' 46 | }, 47 | divider: { 48 | backgroundColor: 'rgba(0, 0, 0, 0.4)', 49 | height: 1, 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PillPal 2 | Medical app that reminds people to take their medication and makes medication management simple. 3 | 4 | ## What is PillPal? 5 | PillPal has been undertaken to help individuals keep track of their daily medications. Many people have multiple prescriptions and it can be easy to lose track of them. This app aims to help manage that load. PillPal’s medium will be reminders set on a mobile device and optionally will work through Alexa. These reminders will tell a person or caregiver when the next time and dosage is along with an image of the medication and if a dose has been missed. This will be done through a series of text-based, mobile notification and/or vocal alerts. Furthermore, the PillPal will allow the user and/or caregiver to monitor usage and record any side effects. It will also have reminders of contraindications and when supply is running low for the given medication. An additional function would be a journal to allow people to recall any information regarding any changes in health such as vital signs, symptoms, sleep, reasoning for withheld medication, etc. PillPal would allow people or caregivers to take their health into their own hands, including a better method to communicate information. 6 | 7 | ## Quick Start 8 | 9 | 1. Clone or download the repo 10 | 2. Run `npm install` 11 | 3. Run `npm start` 12 | 13 | ## Presentation 14 | 15 | https://drive.google.com/file/d/1LsDeMeXUBJeSpBp4mGFLBYRrhL9-T9uG/view?usp=sharing 16 | -------------------------------------------------------------------------------- /components/SymptomsFeelings.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Platform, 5 | StyleSheet, 6 | Text, 7 | TouchableOpacity, 8 | View, 9 | } from 'react-native'; 10 | 11 | export function SymptomsFeelings(props) { 12 | const { 13 | symptomOrFeeling 14 | } = props; 15 | return ( 16 | 17 | 18 | {symptomOrFeeling} 19 | 20 | 21 | ); 22 | } 23 | 24 | SymptomsFeelings.propTypes = { 25 | /** 26 | * The symptom or feeling corresponding to this component. 27 | */ 28 | symptomOrFeeling: PropTypes.string, 29 | }; 30 | 31 | const styles = StyleSheet.create({ 32 | symptomsFeelingsContainer: { 33 | paddingHorizontal: 10, 34 | }, 35 | symptomsFeelingsButton: { 36 | backgroundColor: 'white', 37 | borderRadius: 10, 38 | marginTop: 15, 39 | marginBottom: 15, 40 | paddingHorizontal: 10, 41 | justifyContent: 'center', 42 | alignItems: 'center', 43 | ...Platform.select({ 44 | ios: { 45 | shadowColor: 'black', 46 | shadowOffset: { width: 0, height: -3 }, 47 | shadowOpacity: 0.1, 48 | shadowRadius: 3, 49 | }, 50 | android: { 51 | elevation: 5, 52 | }, 53 | }), 54 | }, 55 | symptomsFeelingsText: { 56 | fontSize: 16, 57 | color: 'black', 58 | paddingVertical: 10, 59 | }, 60 | }); 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject", 9 | "test": "jest --watchAll" 10 | }, 11 | "jest": { 12 | "preset": "jest-expo" 13 | }, 14 | "dependencies": { 15 | "@expo/samples": "~3.0.3", 16 | "@expo/vector-icons": "^10.0.0", 17 | "@material-ui/core": "^4.9.11", 18 | "@react-navigation/web": "^1.0.0-alpha.9", 19 | "expo": "^37.0.0", 20 | "expo-asset": "~8.1.4", 21 | "expo-constants": "~9.0.0", 22 | "expo-font": "~8.1.0", 23 | "expo-web-browser": "~8.1.0", 24 | "jest": "^24.9.0", 25 | "moment": "^2.24.0", 26 | "react": "16.9.0", 27 | "react-dom": "16.9.0", 28 | "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.0.tar.gz", 29 | "react-native-calendar-picker": "^6.0.5", 30 | "react-native-chart-kit": "^5.1.1", 31 | "react-native-elements": "^1.2.7", 32 | "react-native-gesture-handler": "~1.6.0", 33 | "react-native-screens": "^2.4.0", 34 | "react-native-size-matters": "^0.3.0", 35 | "react-native-svg": "11.0.1", 36 | "react-native-web": "^0.11.7", 37 | "react-navigation": "^3.12.0", 38 | "styled-components": "^5.0.1", 39 | "yarn": "^1.22.0" 40 | }, 41 | "devDependencies": { 42 | "babel-preset-expo": "^8.1.0", 43 | "glob": "^7.1.6", 44 | "jest-expo": "^37.0.0", 45 | "react-docgen": "^4.1.1", 46 | "react-docgen-markdown-renderer": "^2.1.3", 47 | "react-native-themed-styles": "0.0.4" 48 | }, 49 | "private": true 50 | } 51 | -------------------------------------------------------------------------------- /components/ViewEditAccountInfoTextInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Divider } from 'react-native-elements'; 3 | import { Dimensions, Platform, StyleSheet, Text, TextInput, View } from 'react-native'; 4 | 5 | const { width: WIDTH } = Dimensions.get('window'); 6 | 7 | export default class ViewEditAccountInfoTextInput extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | render() { 13 | return ( 14 | 15 | 16 | 17 | { this.props.field }: 18 | 19 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | } 31 | 32 | const styles = StyleSheet.create({ 33 | optionContainer: { 34 | paddingHorizontal: 15, 35 | }, 36 | info: { 37 | paddingVertical: 15, 38 | color: 'rgba(70, 70, 70, 1)', 39 | }, 40 | inputContainer: { 41 | flexDirection: 'row', 42 | justifyContent: 'space-between', 43 | alignItems: 'center', 44 | marginVertical: 10, 45 | width: WIDTH - 55, 46 | height: 45, 47 | borderRadius: 10, 48 | fontSize: 16, 49 | marginTop: 10, 50 | paddingHorizontal: 15, 51 | backgroundColor: 'rgb(245, 200, 66)' 52 | }, 53 | input: { 54 | flexGrow: 1, 55 | }, 56 | divider: { 57 | backgroundColor: 'grey', 58 | height: 1, 59 | } 60 | }); 61 | -------------------------------------------------------------------------------- /hooks/useFeeling.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useFeeling(todaysFeelings) { 4 | const [feeling, setFeeling] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | setIsLoading(true); 9 | 10 | fetch(`https://pillpal-app.de/Feeling`, { 11 | method: 'GET', 12 | }) 13 | .then((response) => response.json()) 14 | //If response is in json then in success 15 | .then((responseJson) => { 16 | //Success 17 | setIsLoading(false); 18 | 19 | const feelingsWithSeverity = responseJson.map((feeling) => { 20 | let severity = ''; 21 | 22 | for (let i = 0; i < todaysFeelings.length; i++) { 23 | if (feeling.Display_Name == todaysFeelings[i][0]) { 24 | severity = todaysFeelings[i][1]; 25 | } 26 | } 27 | 28 | return { 29 | ...feeling, 30 | severity, 31 | }; 32 | }); 33 | setFeeling(feelingsWithSeverity); 34 | }) 35 | //If response is not in json then in error 36 | .catch((error) => { 37 | //Error 38 | console.error(error); 39 | }); 40 | }, []); 41 | 42 | const updateFeeling = (severity, individualFeeling) => { 43 | const updatedFeelings = feeling.map((feel) => { 44 | if (feel.ID !== individualFeeling.ID) { 45 | return feel; 46 | } 47 | 48 | return { 49 | ...individualFeeling, 50 | severity, 51 | }; 52 | }); 53 | 54 | setFeeling(updatedFeelings); 55 | }; 56 | 57 | return { 58 | updateFeeling, 59 | feeling, 60 | isLoading, 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /hooks/useSymptom.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useSymptom(todaysSymptoms) { 4 | const [symptom, setSymptom] = useState([]); 5 | const [isLoading, setIsLoading] = useState(true); 6 | 7 | useEffect(() => { 8 | setIsLoading(true); 9 | 10 | fetch(`https://pillpal-app.de/Symptom`, { 11 | method: 'GET', 12 | }) 13 | .then((response) => response.json()) 14 | //If response is in json then in success 15 | .then((responseJson) => { 16 | //Success 17 | setIsLoading(false); 18 | 19 | const symptomsWithSeverity = responseJson.map((symptom) => { 20 | let severity = ''; 21 | 22 | for (let i = 0; i < todaysSymptoms.length; i++) { 23 | if (symptom.Display_Name == todaysSymptoms[i][0]) { 24 | severity = todaysSymptoms[i][1]; 25 | } 26 | } 27 | 28 | return { 29 | ...symptom, 30 | severity, 31 | }; 32 | }); 33 | setSymptom(symptomsWithSeverity); 34 | }) 35 | //If response is not in json then in error 36 | .catch((error) => { 37 | //Error 38 | console.error(error); 39 | }); 40 | }, []); 41 | 42 | const updateSymptom = (severity, individualSymptom) => { 43 | const updatedSymptoms = symptom.map((symp) => { 44 | if (symp.ID !== individualSymptom.ID) { 45 | return symp; 46 | } 47 | 48 | return { 49 | ...individualSymptom, 50 | severity, 51 | }; 52 | }); 53 | 54 | setSymptom(updatedSymptoms); 55 | }; 56 | 57 | return { 58 | updateSymptom, 59 | symptom, 60 | isLoading, 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /create-doc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const reactDocgen = require('react-docgen'); 4 | const ReactDocGenMarkdownRenderer = require('react-docgen-markdown-renderer'); 5 | const glob = require('glob'); 6 | 7 | const renderer = new ReactDocGenMarkdownRenderer(/* constructor options object */); 8 | 9 | function main() { 10 | glob('components/**/*.js', (error, files) => { 11 | if (error) { 12 | console.log('Error occurred'); 13 | console.log(error); 14 | return; 15 | } 16 | 17 | const srcFiles = files.filter(file => !file.includes('test')); 18 | 19 | srcFiles.forEach(file => { 20 | console.log(`Creating documentation for "${file}"`); 21 | const componentPath = path.resolve(path.join(__dirname, file)); 22 | createDocForFile(componentPath); 23 | }); 24 | }); 25 | } 26 | 27 | function createDocForFile(componentPath) { 28 | fs.readFile(componentPath, (error, content) => { 29 | if (error) { 30 | console.log(`Error reading file ${componentPath}`); 31 | console.log(error); 32 | } 33 | 34 | const documentationPath = path.join( 35 | __dirname, 36 | 'documentation', 37 | path.basename(componentPath, path.extname(componentPath)) + 38 | renderer.extension 39 | ); 40 | const doc = reactDocgen.parse(content, null, null, { 41 | configFile: false, 42 | }); 43 | 44 | fs.writeFile( 45 | documentationPath, 46 | renderer.render( 47 | /* The path to the component, used for linking to the file. */ 48 | componentPath, 49 | /* The actual react-docgen AST */ 50 | doc, 51 | /* Array of component ASTs that this component composes */ 52 | [] 53 | ) 54 | ); 55 | }); 56 | } 57 | 58 | main(); 59 | -------------------------------------------------------------------------------- /components/SettingsTitleBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Platform, 4 | StyleSheet, 5 | Text, 6 | View, 7 | } from 'react-native'; 8 | import { styleSheetFactory } from "../themes/themes" 9 | import { useTheme } from "react-native-themed-styles" 10 | 11 | export function SettingsTitleBox(props) { 12 | const [styles] = useTheme(darkstyles) 13 | const { titleText } = props; 14 | return ( 15 | 16 | { titleText } 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | titleContainer: { 23 | backgroundColor: 'rgba(245, 219, 120, 1)', 24 | paddingTop: 10, 25 | paddingBottom: 10, 26 | paddingHorizontal: 10, 27 | ...Platform.select({ 28 | ios: { 29 | shadowColor: 'black', 30 | shadowOffset: { width: 0, height: -3 }, 31 | shadowOpacity: 0.1, 32 | shadowRadius: 1, 33 | }, 34 | android: { 35 | elevation: 3, 36 | }, 37 | }), 38 | }, 39 | titleText: { 40 | fontSize: 20, 41 | color: 'rgba(70, 70, 70, 1)', 42 | textAlign: 'left', 43 | fontWeight: 'bold', 44 | }, 45 | }); 46 | 47 | const darkstyles = styleSheetFactory(theme => ({ 48 | titleContainer: { 49 | backgroundColor: theme.backgroundColor, 50 | paddingTop: 10, 51 | paddingBottom: 10, 52 | paddingHorizontal: 10, 53 | ...Platform.select({ 54 | ios: { 55 | shadowColor: theme.shadow, 56 | shadowOffset: { width: 0, height: -3 }, 57 | shadowOpacity: 0.1, 58 | shadowRadius: 1, 59 | }, 60 | android: { 61 | elevation: 3, 62 | }, 63 | }), 64 | }, 65 | titleText: { 66 | fontSize: 20, 67 | color: theme.textColor, 68 | textAlign: 'left', 69 | fontWeight: 'bold', 70 | }, 71 | })); 72 | -------------------------------------------------------------------------------- /screens/HelpScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, Text, View } from 'react-native'; 3 | import { HelpTextBox } from '../components/HelpTextBox'; 4 | import { HelpQuestionAnswer } from '../components/HelpQuestionAnswer'; 5 | 6 | const titleTexts = { 7 | title1: 'FAQ', 8 | title2: 'Contact', 9 | }; 10 | 11 | const questions = [ 12 | 'How can I keep track of my progress?', 13 | 'How can I connect my account to Alexa?', 14 | 'PillPal sounds too good to be true. Are you sure there aren\'t any hidden subscriptions or premium-only features?', 15 | ] 16 | 17 | const answers = [ 18 | 'PillPal allows you to easily view your progress with logs charts! Tap on the "Log/Charts" option at the bottom of the screen.', 19 | 'Go back to "Settings" and follow the instructions under the "Alexa" option.', 20 | 'Nope! All of PillPal\'s features are 100% free.', 21 | ] 22 | 23 | export default class HelpScreen extends React.Component { 24 | constructor(props) { 25 | super(props); 26 | } 27 | 28 | render() { 29 | return ( 30 | 31 | 32 | 33 | {questions.map((q, i) => )} 34 | 35 | 36 | 37 | 38 | You can reach us at support@pillpal.com! 39 | 40 | 41 | ); 42 | } 43 | } 44 | 45 | HelpScreen.navigationOptions = { 46 | title: 'Help', 47 | }; 48 | 49 | const styles = StyleSheet.create({ 50 | container: { 51 | flex: 1, 52 | }, 53 | textContent: { 54 | margin: 15, 55 | fontSize: 15, 56 | }, 57 | question: { 58 | fontWeight: 'bold' 59 | }, 60 | answer: { 61 | fontStyle: 'italic' 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /hooks/useAuth.js: -------------------------------------------------------------------------------- 1 | import React, { useState, createContext, useContext } from 'react'; 2 | 3 | const AuthContext = createContext(null); 4 | 5 | const useAuth = (props) => { 6 | const [isLoggedIn, setIsLoggedIn] = useState(false); 7 | const [user, setUser] = useState(''); 8 | const [isLoading, setIsLoading] = useState(false); 9 | 10 | const login = async (username, password) => { 11 | console.log('Logging in...'); 12 | setIsLoading(true); 13 | 14 | try { 15 | const response = await fetch( 16 | `https://pillpal-app.de/User/${username}/${password}`, 17 | { 18 | method: 'GET', 19 | } 20 | ); 21 | 22 | const json = await response.json(); 23 | if (json[0]) { 24 | setUser(json[0]); 25 | setIsLoggedIn(true); 26 | } else { 27 | setUser(null); 28 | } 29 | setIsLoading(false); 30 | } catch (error) { 31 | console.error(error); 32 | setUser(null); 33 | setIsLoggedIn(false); 34 | setIsLoading(false); 35 | } 36 | }; 37 | 38 | const logout = async () => { 39 | console.log('Logging out...'); 40 | setIsLoading(true); 41 | 42 | try { 43 | setUser(null); 44 | setIsLoggedIn(false); 45 | setIsLoading(false); 46 | } catch (error) { 47 | console.error(error); 48 | setUser(null); 49 | setIsLoggedIn(false); 50 | setIsLoading(false); 51 | } 52 | }; 53 | 54 | return { 55 | login, 56 | isLoggedIn, 57 | user, 58 | logout, 59 | }; 60 | }; 61 | 62 | const AuthProvider = (props) => { 63 | const authSettings = useAuth(); 64 | 65 | return ( 66 | 67 | {props.children} 68 | 69 | ); 70 | }; 71 | 72 | const useAuthContext = () => { 73 | const authSettings = useContext(AuthContext); 74 | return authSettings; 75 | }; 76 | 77 | export default useAuthContext; 78 | export { AuthProvider }; 79 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import { AppLoading } from 'expo'; 2 | import { Asset } from 'expo-asset'; 3 | import * as Font from 'expo-font'; 4 | import React, { useState } from 'react'; 5 | import { Platform, StatusBar, StyleSheet, View } from 'react-native'; 6 | import { Ionicons } from '@expo/vector-icons'; 7 | 8 | import AppNavigator from './navigation/AppNavigator'; 9 | import { AuthProvider } from './hooks/useAuth'; 10 | 11 | export default function App(props) { 12 | const [isLoadingComplete, setLoadingComplete] = useState(false); 13 | 14 | if (!isLoadingComplete && !props.skipLoadingScreen) { 15 | return ( 16 | handleFinishLoading(setLoadingComplete)} 20 | /> 21 | ); 22 | } else { 23 | return ( 24 | 25 | 26 | {Platform.OS === 'ios' && } 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | async function loadResourcesAsync() { 35 | await Promise.all([ 36 | Asset.loadAsync([ 37 | require('./assets/images/robot-dev.png'), 38 | require('./assets/images/robot-prod.png'), 39 | ]), 40 | Font.loadAsync({ 41 | // This is the font that we are using for our tab bar 42 | ...Ionicons.font, 43 | // We include SpaceMono because we use it in HomeScreen.js. Feel free to 44 | // remove this if you are not using it in your app 45 | 'space-mono': require('./assets/fonts/SpaceMono-Regular.ttf'), 46 | }), 47 | ]); 48 | } 49 | 50 | function handleLoadingError(error) { 51 | // In this case, you might want to report the error to your error reporting 52 | // service, for example Sentry 53 | console.warn(error); 54 | } 55 | 56 | function handleFinishLoading(setLoadingComplete) { 57 | setLoadingComplete(true); 58 | } 59 | 60 | const styles = StyleSheet.create({ 61 | container: { 62 | flex: 1, 63 | backgroundColor: '#fff', 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /components/__tests__/__snapshots__/PillCard-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders correctly 1`] = ` 4 | 27 | 34 | 43 | name 44 | : 45 | time 46 | 47 | 56 | Dosage: 57 | dosage 58 | 59 | 60 | 68 | 78 | Log Pill 79 | 80 | 91 | Dismiss 92 | 93 | 94 | 95 | `; 96 | -------------------------------------------------------------------------------- /components/AddSymptomFeelingOption.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Platform, StyleSheet, Text, View } from 'react-native'; 3 | import { styleSheetFactory } from '../themes/themes'; 4 | import { useTheme } from 'react-native-themed-styles'; 5 | 6 | export function AddSymptomFeelingOption(props) { 7 | const { optionText } = props; 8 | return ( 9 | 10 | 11 | {optionText} 12 | 13 | 14 | ); 15 | } 16 | 17 | const styles = StyleSheet.create({ 18 | optionContainer: { 19 | flex: 1, 20 | flexDirection: 'row', 21 | justifyContent: 'space-between', 22 | paddingTop: 10, 23 | paddingBottom: 10, 24 | paddingHorizontal: 10, 25 | ...Platform.select({ 26 | ios: { 27 | shadowColor: 'black', 28 | shadowOffset: { width: 0, height: -3 }, 29 | shadowOpacity: 0.1, 30 | shadowRadius: 3, 31 | }, 32 | android: { 33 | elevation: 5, 34 | }, 35 | }), 36 | }, 37 | displayNameContainer: { 38 | flex: 1, 39 | flexDirection: 'row', 40 | //justifyContent: 'flex-start', 41 | justifyContent: 'center', 42 | alignItems: 'center', 43 | textAlignVertical: 'center', 44 | }, 45 | displayNameText: { 46 | fontSize: 16, 47 | color: 'black', 48 | }, 49 | divider: { 50 | backgroundColor: 'black', 51 | height: 1, 52 | }, 53 | intensityContainer: { 54 | paddingHorizontal: 10, 55 | }, 56 | intensityButton: { 57 | backgroundColor: 'white', 58 | borderRadius: 10, 59 | paddingHorizontal: 10, 60 | justifyContent: 'center', 61 | alignItems: 'center', 62 | ...Platform.select({ 63 | ios: { 64 | shadowColor: 'black', 65 | shadowOffset: { width: 0, height: -3 }, 66 | shadowOpacity: 0.1, 67 | shadowRadius: 3, 68 | }, 69 | android: { 70 | elevation: 5, 71 | }, 72 | }), 73 | }, 74 | intensityTextContainer: { 75 | flex: 1, 76 | justifyContent: 'center', 77 | alignItems: 'center', 78 | textAlignVertical: 'center', 79 | }, 80 | intensityText: { 81 | fontSize: 14, 82 | color: 'rgba(70, 70, 70, 1)', 83 | paddingVertical: 10, 84 | textAlignVertical: 'center', 85 | }, 86 | }); 87 | -------------------------------------------------------------------------------- /screens/AlexaScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { ScrollView, StyleSheet, Text, View, FlatList } from 'react-native'; 3 | import { styleSheetFactory } from "../themes/themes" 4 | import { useTheme } from "react-native-themed-styles" 5 | 6 | const DATA = [ 7 | { 8 | id: '0', 9 | title: '- Get a list of your current pills', 10 | }, 11 | { 12 | id: '1', 13 | title: '- Add a new pill', 14 | }, 15 | { 16 | id: '2', 17 | title: '- Delete a pill', 18 | }, 19 | ]; 20 | 21 | function Item({ title }) { 22 | return ( 23 | 24 | {title} 25 | 26 | ); 27 | } 28 | 29 | export default class AlexaScreen extends Component { 30 | render() { 31 | return ( 32 | 33 | 37 | 38 | 39 | PillPal has an Alexa skill available. The skill allows you to 40 | manage your medications and accomplish many of the same tasks you 41 | might do in the app. You can do things such as: 42 | 43 | } 47 | keyExtractor={(item) => item.id} 48 | /> 49 | 50 | To get started, search for the PillPal skill in your Alexa app. 51 | 52 | 53 | 54 | 55 | ); 56 | } 57 | } 58 | 59 | AlexaScreen.navigationOptions = { 60 | title: 'Alexa', 61 | }; 62 | 63 | const styles = StyleSheet.create({ 64 | container: { 65 | flex: 1, 66 | backgroundColor: '#FFFFFF', 67 | marginHorizontal: 4, 68 | }, 69 | textContainer: { 70 | alignItems: 'center', 71 | marginTop: 8, 72 | marginBottom: 20, 73 | }, 74 | titleText: { 75 | fontSize: 20, 76 | color: 'rgba(70,70,70, 1)', 77 | fontWeight: 'bold', 78 | textAlign: 'center', 79 | }, 80 | bodyText: { 81 | fontSize: 16, 82 | color: 'rgba(70,70,70, 1)', 83 | fontWeight: 'normal', 84 | textAlign: 'left', 85 | marginTop: 8, 86 | }, 87 | }); 88 | -------------------------------------------------------------------------------- /components/ChangePasswordTextInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Divider } from 'react-native-elements'; 3 | import { Dimensions, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'; 4 | import { FontAwesome } from '@expo/vector-icons'; 5 | 6 | const { width: WIDTH } = Dimensions.get('window'); 7 | 8 | export default class ChangePasswordTextInput extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.showPass = this.showPass.bind(this); 12 | this.state = { 13 | showPass: true, 14 | press: false 15 | } 16 | } 17 | 18 | showPass = () => { 19 | if (this.state.press == false) { 20 | this.setState({ showPass: false, press: true }); 21 | } else { 22 | this.setState({ showPass: true, press: false }); 23 | } 24 | } 25 | 26 | render() { 27 | return ( 28 | 29 | 30 | 31 | { this.props.field }: 32 | 33 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | } 48 | 49 | const styles = StyleSheet.create({ 50 | optionContainer: { 51 | paddingHorizontal: 15, 52 | }, 53 | info: { 54 | paddingVertical: 15, 55 | color: 'rgba(70, 70, 70, 1)', 56 | }, 57 | inputContainer: { 58 | flexDirection: 'row', 59 | justifyContent: 'space-between', 60 | alignItems: 'center', 61 | marginVertical: 10, 62 | width: WIDTH - 55, 63 | height: 45, 64 | borderRadius: 10, 65 | fontSize: 16, 66 | marginTop: 10, 67 | paddingHorizontal: 15, 68 | backgroundColor: 'rgb(245, 200, 66)' 69 | }, 70 | input: { 71 | flexGrow: 1, 72 | }, 73 | icon: { 74 | fontSize: 18, 75 | color: 'rgba(0, 0, 0, 0.55)', 76 | backgroundColor: 'rgba(255, 255, 255, 0)' 77 | }, 78 | divider: { 79 | backgroundColor: 'grey', 80 | height: 1, 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /components/SettingsOption.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Divider } from 'react-native-elements'; 3 | import { Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; 4 | import { FontAwesome } from '@expo/vector-icons'; 5 | import { styleSheetFactory } from "../themes/themes" 6 | import { useTheme } from "react-native-themed-styles" 7 | 8 | export function SettingsOption(props) { 9 | const { optionText, faIcon, rhs } = props; 10 | const [styles] = useTheme(darkstyles) 11 | return ( 12 | 13 | 14 | 15 | 16 | { optionText } 17 | 18 | 19 | { rhs } 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | 27 | const styles = StyleSheet.create({ 28 | optionContainer: { 29 | flex: 1, 30 | flexDirection: 'row', 31 | justifyContent: 'space-between', 32 | paddingTop: 10, 33 | paddingBottom: 10, 34 | paddingHorizontal: 10, 35 | ...Platform.select({ 36 | ios: { 37 | shadowColor: 'black', 38 | shadowOffset: { width: 0, height: -3 }, 39 | shadowOpacity: 0.1, 40 | shadowRadius: 3, 41 | }, 42 | android: { 43 | elevation: 5, 44 | }, 45 | }), 46 | }, 47 | leftSide: { 48 | flex: 1, 49 | flexDirection: 'row', 50 | justifyContent: 'flex-start', 51 | }, 52 | faStyle: { 53 | paddingTop: 3, 54 | fontSize: 17, 55 | }, 56 | optionText: { 57 | fontSize: 15, 58 | color: 'rgba(70, 70, 70, 1)', 59 | }, 60 | divider: { 61 | backgroundColor: 'black', 62 | height: 1, 63 | } 64 | }); 65 | 66 | const darkstyles = styleSheetFactory(theme => ({ 67 | optionContainer: { 68 | flex: 1, 69 | flexDirection: 'row', 70 | justifyContent: 'space-between', 71 | paddingTop: 10, 72 | paddingBottom: 10, 73 | paddingHorizontal: 10, 74 | ...Platform.select({ 75 | ios: { 76 | shadowColor: theme.shadow, 77 | shadowOffset: { width: 0, height: -3 }, 78 | shadowOpacity: 0.1, 79 | shadowRadius: 3, 80 | }, 81 | android: { 82 | elevation: 5, 83 | }, 84 | }), 85 | }, 86 | leftSide: { 87 | flex: 1, 88 | flexDirection: 'row', 89 | justifyContent: 'flex-start', 90 | }, 91 | faStyle: { 92 | paddingTop: 3, 93 | fontSize: 17, 94 | }, 95 | optionText: { 96 | fontSize: 15, 97 | color: theme.textColor, 98 | }, 99 | divider: { 100 | backgroundColor: theme.shadow, 101 | height: 1, 102 | } 103 | })); -------------------------------------------------------------------------------- /components/EditPill.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Platform, StyleSheet, Text, View } from 'react-native'; 4 | import { FontAwesome } from '@expo/vector-icons'; 5 | 6 | export function EditPill(props) { 7 | const { name, dosage, refill } = props; 8 | return ( 9 | 10 | 11 | Name: {name} 12 | 13 | 14 | 15 | Dosage: {dosage} 16 | 17 | 18 | 19 | Refills: {refill} 20 | 21 | 22 | 23 | 24 | Edit 25 | 29 | 30 | 31 | 32 | ); 33 | } 34 | 35 | EditPill.propTypes = { 36 | /** 37 | * The name of the pill to take. 38 | */ 39 | name: PropTypes.string, 40 | /** 41 | * The amount of time left before the user needs to take their next medication. 42 | */ 43 | dosage: PropTypes.string, 44 | /** 45 | * The quantity of pills to be taken. 46 | */ 47 | refill: PropTypes.number, 48 | }; 49 | 50 | const styles = StyleSheet.create({ 51 | pillCardInfoContainer: { 52 | marginTop: 10, 53 | marginBottom: 10, 54 | marginLeft: 10, 55 | marginRight: 10, 56 | height: 125, 57 | ...Platform.select({ 58 | ios: { 59 | shadowColor: 'black', 60 | shadowOffset: { width: 0, height: -3 }, 61 | shadowOpacity: 0.1, 62 | shadowRadius: 3, 63 | }, 64 | android: { 65 | elevation: 5, 66 | }, 67 | }), 68 | alignItems: 'flex-start', 69 | backgroundColor: '#fbfbfb', 70 | borderRadius: 10, 71 | paddingVertical: 15, 72 | paddingHorizontal: 15, 73 | // flexDirection: 'column', 74 | // alignSelf: 'flex-start', 75 | }, 76 | pillCardText: { 77 | fontSize: 16, 78 | color: 'rgba(70,70,70, 1)', 79 | textAlign: 'left', 80 | }, 81 | pillCardLogPillText: { 82 | fontSize: 18, 83 | fontWeight: 'bold', 84 | color: 'rgba(121,51,153, 1)', 85 | textAlign: 'left', 86 | }, 87 | pillCardDismissText: { 88 | marginLeft: 60, 89 | fontSize: 18, 90 | fontWeight: 'bold', 91 | color: 'rgba(121,51,153, 1)', 92 | textAlign: 'left', 93 | }, 94 | calendarNoteEditContainer: { 95 | alignItems: 'flex-end', 96 | }, 97 | calendarNoteEditText: { 98 | fontSize: 14, 99 | fontWeight: 'bold', 100 | paddingTop: 20, 101 | color: 'rgba(121,51,153, 1)', 102 | textAlign: 'left', 103 | }, 104 | calendarNoteEditTextIcon: { 105 | fontSize: 14, 106 | fontWeight: 'bold', 107 | color: 'rgba(121,51,153, 1)', 108 | textAlign: 'left', 109 | marginTop: 22, 110 | marginLeft: 5, 111 | }, 112 | }); 113 | -------------------------------------------------------------------------------- /screens/ViewEditAccountInfoScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useState } from 'react'; 3 | import { 4 | Dimensions, 5 | ScrollView, 6 | StyleSheet, 7 | Text, 8 | TouchableOpacity, 9 | View, 10 | } from 'react-native'; 11 | import ViewEditAccountInfoTextInput from '../components/ViewEditAccountInfoTextInput'; 12 | import useName from '../hooks/useName'; 13 | import useAuth from '../hooks/useAuth'; 14 | 15 | const { width: WIDTH } = Dimensions.get('window'); 16 | 17 | const ViewEditAccountInfoScreen = (props) => { 18 | const userSettings = useAuth(); 19 | let userID = userSettings.user ? userSettings.user.ID : null; 20 | 21 | const { name } = useName(userID); 22 | const fn = name.First_Name; 23 | const ln = name.Last_Name; 24 | const eml = name.Email; 25 | 26 | const [state, setState] = useState({ 27 | firstName: fn, 28 | lastName: ln, 29 | email: eml, 30 | }); 31 | 32 | const updateFirstName = (val) => { 33 | setState({ firstName: val, lastName: state.lastName, email: state.email }); 34 | }; 35 | 36 | const updateLastName = (val) => { 37 | setState({ firstName: state.firstName, lastName: val, email: state.email }); 38 | }; 39 | 40 | const updateEmail = (val) => { 41 | setState({ 42 | firstName: state.firstName, 43 | lastName: state.lastName, 44 | email: val, 45 | }); 46 | }; 47 | 48 | const submit = () => { 49 | alert('Submitted!'); 50 | }; 51 | 52 | return ( 53 | 54 | 58 | 63 | 68 | 73 | 78 | Update Info 79 | 80 | 81 | 82 | ); 83 | }; 84 | 85 | ViewEditAccountInfoScreen.navigationOptions = { 86 | title: 'View/Edit Account Info', 87 | }; 88 | 89 | export default ViewEditAccountInfoScreen; 90 | 91 | const styles = StyleSheet.create({ 92 | container: { 93 | flex: 1, 94 | }, 95 | mainContainer: { 96 | alignItems: 'center', 97 | flexDirection: 'column', 98 | justifyContent: 'center', 99 | minHeight: '100%', 100 | }, 101 | btnUpdate: { 102 | width: WIDTH - 55, 103 | height: 45, 104 | borderRadius: 25, 105 | backgroundColor: '#432577', 106 | justifyContent: 'center', 107 | marginTop: 20, 108 | marginBottom: 10, 109 | }, 110 | btnText: { 111 | color: 'white', 112 | fontSize: 16, 113 | textAlign: 'center', 114 | }, 115 | }); 116 | -------------------------------------------------------------------------------- /screens/ChangePasswordScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useState } from 'react'; 3 | import { Dimensions, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; 4 | import ChangePasswordTextInput from '../components/ChangePasswordTextInput'; 5 | import { styleSheetFactory } from "../themes/themes" 6 | import { useTheme } from "react-native-themed-styles" 7 | const { width: WIDTH } = Dimensions.get('window'); 8 | 9 | const ChangePasswordScreen = (props) => { 10 | 11 | const [styles] = useTheme(darkstyles) 12 | const [state, setState] = useState({ 13 | currPassword: '', 14 | newPassword: '', 15 | confirmNewPassword: '', 16 | }); 17 | 18 | const updateCurrPassword = (val) => { 19 | setState({currPassword: val, newPassword: state.newPassword, confirmNewPassword: state.confirmNewPassword}); 20 | } 21 | 22 | const updateNewPassword = (val) => { 23 | setState({currPassword: state.currPassword, newPassword: val, confirmNewPassword: state.confirmNewPassword}); 24 | } 25 | 26 | const updateConfirmNewPassword = (val) => { 27 | setState({currPassword: state.currPassword, newPassword: state.newPassword, confirmNewPassword: val}); 28 | } 29 | 30 | const submit = () => { 31 | alert("Submitted!"); 32 | } 33 | 34 | return ( 35 | 36 | 40 | 41 | 42 | 43 | 47 | Change Password 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | ChangePasswordScreen.navigationOptions = { 55 | title: 'Change Password', 56 | }; 57 | 58 | export default ChangePasswordScreen; 59 | 60 | const styles = StyleSheet.create({ 61 | container: { 62 | flex: 1, 63 | }, 64 | mainContainer: { 65 | alignItems: 'center', 66 | flexDirection: 'column', 67 | justifyContent: 'center', 68 | minHeight: '100%', 69 | }, 70 | btnUpdate: { 71 | width: WIDTH - 55, 72 | height: 45, 73 | borderRadius: 25, 74 | backgroundColor: '#432577', 75 | justifyContent: 'center', 76 | marginTop: 20, 77 | marginBottom: 10 78 | }, 79 | btnText: { 80 | color: 'white', 81 | fontSize: 16, 82 | textAlign: 'center' 83 | } 84 | }); 85 | 86 | const darkstyles = styleSheetFactory(theme => ({ 87 | container: { 88 | flex: 1, 89 | }, 90 | mainContainer: { 91 | alignItems: 'center', 92 | flexDirection: 'column', 93 | justifyContent: 'center', 94 | minHeight: '100%', 95 | }, 96 | btnUpdate: { 97 | width: WIDTH - 55, 98 | height: 45, 99 | borderRadius: 25, 100 | backgroundColor: '#432577', 101 | justifyContent: 'center', 102 | marginTop: 20, 103 | marginBottom: 10 104 | }, 105 | btnText: { 106 | color: theme.backgroundColor, 107 | fontSize: 16, 108 | textAlign: 'center' 109 | } 110 | })); 111 | -------------------------------------------------------------------------------- /navigation/MainTabNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Platform } from 'react-native'; 3 | import { 4 | createStackNavigator, 5 | createBottomTabNavigator, 6 | } from 'react-navigation'; 7 | 8 | import TabBarIcon from '../components/TabBarIcon'; 9 | import HomeScreen from '../screens/HomeScreen'; 10 | import CalendarScreen from '../screens/CalendarScreen'; 11 | import LogAndChartsScreen from '../screens/LogAndChartsScreen'; 12 | import SettingsScreen from '../screens/SettingsScreen'; 13 | import TodaysNoteScreen from '../screens/TodaysNoteScreen'; 14 | import AddFeelingScreen from '../screens/AddFeelingScreen'; 15 | import AddSymptomScreen from '../screens/AddSymptomScreen'; 16 | import EditPillsScreen from '../screens/EditPillsScreen'; 17 | import AlexaScreen from '../screens/AlexaScreen'; 18 | import EditPillDataScreen from '../screens/EditPillDataScreen'; 19 | import NewPillDataScreen from '../screens/NewPillDataScreen'; 20 | import HelpScreen from '../screens/HelpScreen'; 21 | import AccountDetailsScreen from '../screens/AccountDetailsScreen'; 22 | import ViewEditAccountInfoScreen from '../screens/ViewEditAccountInfoScreen'; 23 | import ChangePasswordScreen from '../screens/ChangePasswordScreen'; 24 | 25 | const config = Platform.select({ 26 | web: { headerMode: 'screen' }, 27 | default: {}, 28 | }); 29 | 30 | const HomeStack = createStackNavigator( 31 | { 32 | Home: HomeScreen, 33 | TodaysNote: TodaysNoteScreen, 34 | AddFeeling: AddFeelingScreen, 35 | AddSymptom: AddSymptomScreen, 36 | }, 37 | config 38 | ); 39 | 40 | HomeStack.navigationOptions = { 41 | tabBarLabel: 'Home', 42 | tabBarIcon: ({ focused }) => ( 43 | 47 | ), 48 | }; 49 | 50 | HomeStack.path = ''; 51 | 52 | const CalendarStack = createStackNavigator( 53 | { 54 | Calendar: CalendarScreen, 55 | }, 56 | config 57 | ); 58 | 59 | CalendarStack.navigationOptions = { 60 | tabBarLabel: 'Calendar', 61 | tabBarIcon: ({ focused }) => ( 62 | 66 | ), 67 | }; 68 | 69 | CalendarStack.path = ''; 70 | 71 | const LogAndChartsStack = createStackNavigator( 72 | { 73 | LogAndCharts: LogAndChartsScreen, 74 | }, 75 | config 76 | ); 77 | 78 | LogAndChartsStack.navigationOptions = { 79 | tabBarLabel: 'Log/Charts', 80 | tabBarIcon: ({ focused }) => ( 81 | 85 | ), 86 | }; 87 | 88 | LogAndChartsStack.path = ''; 89 | 90 | const SettingsStack = createStackNavigator( 91 | { 92 | Settings: SettingsScreen, 93 | EditPills: EditPillsScreen, 94 | AccountDetails: AccountDetailsScreen, 95 | ViewEditAccountInfo: ViewEditAccountInfoScreen, 96 | ChangePassword: ChangePasswordScreen, 97 | Alexa: AlexaScreen, 98 | EditPillData: EditPillDataScreen, 99 | NewPillData: NewPillDataScreen, 100 | Help: HelpScreen, 101 | }, 102 | config 103 | ); 104 | 105 | SettingsStack.navigationOptions = { 106 | tabBarLabel: 'Settings', 107 | tabBarIcon: ({ focused }) => ( 108 | 112 | ), 113 | }; 114 | 115 | SettingsStack.path = ''; 116 | 117 | const TabNavigator = createBottomTabNavigator({ 118 | Home: { screen: HomeStack }, 119 | Calendar: { screen: CalendarStack }, 120 | LogAndCharts: { screen: LogAndChartsStack }, 121 | Settings: { screen: SettingsStack }, 122 | }); 123 | 124 | TabNavigator.path = ''; 125 | 126 | export default TabNavigator; 127 | -------------------------------------------------------------------------------- /screens/HomeScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ScrollView, StyleSheet, Text, View, Button } from 'react-native'; 3 | import { PillCard } from '../components/PillCard'; 4 | import { PillsLoggedTodayCard } from '../components/PillsLoggedTodayCard'; 5 | import useName from '../hooks/useName'; 6 | import useTakes from '../hooks/useTakes'; 7 | import useAuth from '../hooks/useAuth'; 8 | import { styleSheetFactory } from '../themes/themes'; 9 | import { useTheme } from 'react-native-themed-styles'; 10 | 11 | /** 12 | * Mock data to represent pills the user has logged so far today. 13 | */ 14 | const pillsLoggedToday = [ 15 | { 16 | id: 0, 17 | name: 'Ibuprofen', 18 | dosage: '2 pills', 19 | formattedTimeTaken: '8:30 a.m.', 20 | }, 21 | { 22 | id: 1, 23 | name: 'Ibuprofen', 24 | dosage: '2 pills', 25 | formattedTimeTaken: '1:07 p.m.', 26 | }, 27 | { 28 | id: 2, 29 | name: 'Nitroglycerin', 30 | dosage: '1 pill', 31 | formattedTimeTaken: '3:45 p.m.', 32 | }, 33 | ]; 34 | 35 | const HomeScreen = (props) => { 36 | //const [styles] = useTheme(darkstyles); 37 | const userSettings = useAuth(); 38 | let userID = userSettings.user ? userSettings.user.ID : null; 39 | 40 | const { name } = useName(userID); 41 | const { takes } = useTakes(userID); 42 | 43 | const { navigate } = props.navigation; 44 | 45 | return ( 46 | 47 | 51 | 52 | Hello, {name.First_Name}! 53 | 54 | {takes.map((pill) => { 55 | return ( 56 | 62 | ); 63 | })} 64 | 65 | 66 |