├── .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 |
74 |
75 |
79 |
80 |
81 | );
82 | };
83 |
84 | HomeScreen.navigationOptions = {
85 | title: 'Home',
86 | };
87 |
88 | export default HomeScreen;
89 |
90 | const styles = StyleSheet.create({
91 | container: {
92 | flex: 1,
93 | backgroundColor: '#fff',
94 | },
95 | contentContainer: {
96 | paddingTop: 30,
97 | minHeight: '100%',
98 | },
99 | welcomeContainer: {
100 | alignItems: 'center',
101 | marginBottom: 20,
102 | },
103 | welcomeText: {
104 | fontSize: 20,
105 | color: 'rgba(70,70,70, 1)',
106 | fontWeight: 'bold',
107 | textAlign: 'center',
108 | },
109 | buttonContainer: {
110 | paddingHorizontal: 15,
111 | },
112 | buttonShape: {
113 | borderRadius: 10,
114 | marginTop: 10,
115 | marginBottom: 10,
116 | },
117 | });
118 |
119 | const darkstyles = styleSheetFactory((theme) => ({
120 | container: {
121 | flex: 1,
122 | backgroundColor: theme.backgroundColor,
123 | },
124 | contentContainer: {
125 | paddingTop: 30,
126 | minHeight: '100%',
127 | },
128 | welcomeContainer: {
129 | alignItems: 'center',
130 | marginBottom: 20,
131 | },
132 | welcomeText: {
133 | fontSize: 20,
134 | color: theme.textColor,
135 | fontWeight: 'bold',
136 | textAlign: 'center',
137 | },
138 | }));
139 |
--------------------------------------------------------------------------------
/components/PillCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Platform, StyleSheet, Text, View } from 'react-native';
4 | import { styleSheetFactory } from "../themes/themes"
5 | import { useTheme } from "react-native-themed-styles"
6 |
7 | export function PillCard(props) {
8 | const { name, formattedTimeLeft, dosage } = props;
9 | const [styles] = useTheme(darkstyles)
10 | return (
11 |
12 |
13 |
14 | {name}: {formattedTimeLeft}
15 |
16 |
17 | Dosage: {dosage}
18 |
19 |
20 |
21 | Log Pill
22 |
23 | Dismiss
24 |
25 |
26 | );
27 | }
28 |
29 | PillCard.propTypes = {
30 | /**
31 | * The name of the pill to take.
32 | */
33 | name: PropTypes.string,
34 | /**
35 | * The amount of time left before the user needs to take their next medication.
36 | */
37 | formattedTimeLeft: PropTypes.string,
38 | /**
39 | * The quantity of pills to be taken.
40 | */
41 | dosage: PropTypes.string,
42 | };
43 |
44 | const styles = StyleSheet.create({
45 | pillCardInfoContainer: {
46 | marginTop: 10,
47 | marginBottom: 10,
48 | marginLeft: 10,
49 | marginRight: 10,
50 | height: 125,
51 | ...Platform.select({
52 | ios: {
53 | shadowColor: 'black',
54 | shadowOffset: { width: 0, height: -3 },
55 | shadowOpacity: 0.1,
56 | shadowRadius: 3,
57 | },
58 | android: {
59 | elevation: 5,
60 | },
61 | }),
62 | alignItems: 'flex-start',
63 | backgroundColor: '#fbfbfb',
64 | borderRadius: 10,
65 | paddingVertical: 15,
66 | paddingHorizontal: 15,
67 | },
68 | pillCardTitleText: {
69 | fontSize: 17,
70 | color: 'rgba(70,70,70, 1)',
71 | textAlign: 'left',
72 | },
73 | pillCardDosageText: {
74 | fontSize: 13,
75 | color: 'rgba(96,100,109, 0.8)',
76 | textAlign: 'left',
77 | },
78 | pillCardLogPillText: {
79 | fontSize: 18,
80 | fontWeight: 'bold',
81 | color: 'rgba(121,51,153, 1)',
82 | textAlign: 'left',
83 | },
84 | pillCardDismissText: {
85 | marginLeft: 60,
86 | fontSize: 18,
87 | fontWeight: 'bold',
88 | color: 'rgba(121,51,153, 1)',
89 | textAlign: 'left',
90 | },
91 | });
92 |
93 | const darkstyles = styleSheetFactory(theme => ({
94 | pillCardInfoContainer: {
95 | marginTop: 10,
96 | marginBottom: 10,
97 | marginLeft: 10,
98 | marginRight: 10,
99 | height: 125,
100 | ...Platform.select({
101 | ios: {
102 | shadowColor: 'black',
103 | shadowOffset: { width: 0, height: -3 },
104 | shadowOpacity: 0.1,
105 | shadowRadius: 3,
106 | },
107 | android: {
108 | elevation: 5,
109 | },
110 | }),
111 | alignItems: 'flex-start',
112 | backgroundColor:'#fbfbfb',
113 | borderRadius: 10,
114 | paddingVertical: 15,
115 | paddingHorizontal: 15,
116 | },
117 | pillCardTitleText: {
118 | fontSize: 17,
119 | color: 'rgba(70,70,70, 1)',
120 | textAlign: 'left',
121 | },
122 | pillCardDosageText: {
123 | fontSize: 13,
124 | color: 'rgba(96,100,109, 0.8)',
125 | textAlign: 'left',
126 | },
127 | pillCardLogPillText: {
128 | fontSize: 18,
129 | fontWeight: 'bold',
130 | color: 'rgba(121,51,153, 1)',
131 | textAlign: 'left',
132 | },
133 | pillCardDismissText: {
134 | marginLeft: 60,
135 | fontSize: 18,
136 | fontWeight: 'bold',
137 | color: 'rgba(121,51,153, 1)',
138 | textAlign: 'left',
139 | },
140 | }));
141 |
--------------------------------------------------------------------------------
/screens/EditPillsScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dimensions, ScrollView, StyleSheet, View, Button } from 'react-native';
3 | import { EditPill } from '../components/EditPill';
4 | import { TouchableOpacity } from 'react-native-gesture-handler';
5 | import useTakes from '../hooks/useTakes';
6 | import useAuth from '../hooks/useAuth';
7 | import { styleSheetFactory } from '../themes/themes';
8 | import { useTheme } from 'react-native-themed-styles';
9 |
10 | const { width: WIDTH } = Dimensions.get('window');
11 |
12 | const EditPillsScreen = (props) => {
13 | const [styles] = useTheme(darkstyles);
14 | const userSettings = useAuth();
15 | let userID = userSettings.user ? userSettings.user.ID : null;
16 |
17 | const { takes } = useTakes(userID);
18 | const { navigate } = props.navigation;
19 | return (
20 |
21 |
25 |
26 |
38 |
39 | {takes.map((pill) => {
40 | return (
41 |
44 | navigate('EditPillData', {
45 | selectedPill: [
46 | [pill.Display_Name],
47 | [pill.Amount_Prescribed],
48 | [pill.Refills],
49 | [pill.User_ID],
50 | [pill.Medication_ID],
51 | ],
52 | })
53 | }
54 | >
55 |
61 |
62 | );
63 | })}
64 |
65 |
66 | );
67 | };
68 |
69 | EditPillsScreen.navigationOptions = {
70 | title: 'Edit Pills',
71 | };
72 |
73 | export default EditPillsScreen;
74 |
75 | const styles = StyleSheet.create({
76 | container: {
77 | flex: 1,
78 | backgroundColor: '#FFFFFF',
79 | marginHorizontal: 4,
80 | },
81 | textContainer: {
82 | alignItems: 'center',
83 | marginTop: 8,
84 | marginBottom: 20,
85 | },
86 | titleText: {
87 | fontSize: 20,
88 | color: 'rgba(70,70,70, 1)',
89 | fontWeight: 'bold',
90 | textAlign: 'center',
91 | },
92 | bodyText: {
93 | fontSize: 16,
94 | color: 'rgba(70,70,70, 1)',
95 | fontWeight: 'normal',
96 | textAlign: 'left',
97 | marginTop: 8,
98 | },
99 | buttonContainer: {
100 | paddingHorizontal: 40,
101 | marginTop: 15,
102 | marginLeft: WIDTH - 200,
103 | },
104 | buttonShape: {
105 | borderRadius: 10,
106 | marginTop: 10,
107 | marginBottom: 10,
108 | },
109 | });
110 |
111 | const darkstyles = styleSheetFactory((theme) => ({
112 | container: {
113 | flex: 1,
114 | backgroundColor: theme.backgroundColor,
115 | marginHorizontal: 4,
116 | },
117 | textContainer: {
118 | alignItems: 'center',
119 | marginTop: 8,
120 | marginBottom: 20,
121 | },
122 | titleText: {
123 | fontSize: 20,
124 | color: 'rgba(70,70,70, 1)',
125 | fontWeight: 'bold',
126 | textAlign: 'center',
127 | },
128 | bodyText: {
129 | fontSize: 16,
130 | color: 'rgba(70,70,70, 1)',
131 | fontWeight: 'normal',
132 | textAlign: 'left',
133 | marginTop: 8,
134 | },
135 | buttonContainer: {
136 | marginTop: 8,
137 | marginBottom: 20,
138 | marginLeft: WIDTH - 150,
139 | width: 120,
140 | },
141 | }));
142 |
--------------------------------------------------------------------------------
/screens/LogAndChartsScreen.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Dimensions, ScrollView, StyleSheet, Text, View } from 'react-native';
3 | import { LineChart, StackedBarChart } from 'react-native-chart-kit';
4 | const screenWidth = Dimensions.get('window').width;
5 | import { styleSheetFactory } from "../themes/themes"
6 | import { useTheme } from "react-native-themed-styles"
7 |
8 | const emotionData = {
9 | labels: ['Worried', 'Stressed', 'Sad', 'Tired'],
10 | datasets: [
11 | {
12 | data: [70, 45, 28, 20],
13 | },
14 | ],
15 | };
16 |
17 | const weightData = {
18 | labels: ['3/11', '3/14', '3/17', '3/20'],
19 | datasets: [
20 | {
21 | data: [194, 192, 188, 184],
22 | },
23 | ],
24 | };
25 |
26 | const LogAndChartsScreen = (props) => {
27 | const [styles] = useTheme(darkstyles)
28 | return (
29 |
30 |
31 |
35 |
36 | Emotion Frequency
37 |
38 |
39 | `rgba(255, 255, 255, ${opacity})`,
55 | labelColor: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
56 | style: {
57 | borderRadius: 16,
58 | },
59 | propsForDots: {
60 | r: '6',
61 | strokeWidth: '2',
62 | stroke: '#ffa726',
63 | },
64 | }}
65 | fromZero={true}
66 | showLegend={false}
67 | />
68 |
69 | Weight Fluctuation
70 |
71 | `rgba(255, 255, 255, ${opacity})`,
84 | labelColor: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
85 | style: {
86 | borderRadius: 16,
87 | },
88 | propsForDots: {
89 | r: '6',
90 | strokeWidth: '2',
91 | stroke: '#ffa726',
92 | },
93 | }}
94 | bezier
95 | />
96 |
97 |
98 | );
99 | }
100 |
101 | LogAndChartsScreen.navigationOptions = {
102 | title: 'Log/Charts',
103 | };
104 |
105 | export default LogAndChartsScreen;
106 |
107 | const styles = StyleSheet.create({
108 | container: {
109 | flex: 1,
110 | backgroundColor: "black",
111 | },
112 | chartH_Container: {
113 | alignItems: 'center',
114 | marginBottom: 20,
115 | },
116 | chartH_Text: {
117 | fontSize: 20,
118 | color: "white",
119 | fontWeight: 'bold',
120 | textAlign: 'center',
121 | },
122 | chartH_Container: {
123 | alignItems: 'center',
124 | marginBottom: 20,
125 | },
126 | chartH_Text: {
127 | fontSize: 20,
128 | color: 'rgba(70,70,70, 1)',
129 | fontWeight: 'bold',
130 | textAlign: 'center',
131 | },
132 | });
133 |
134 | const darkstyles = styleSheetFactory(theme => ({
135 | container: {
136 | flex: 1,
137 | backgroundColor: theme.backgroundColor,
138 | },
139 | chartH_Container: {
140 | alignItems: 'center',
141 | marginBottom: 20,
142 | },
143 | chartH_Text: {
144 | fontSize: 20,
145 | color: theme.textColor,
146 | fontWeight: 'bold',
147 | textAlign: 'center',
148 | }
149 | }))
--------------------------------------------------------------------------------
/screens/NewPillDataScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | Dimensions,
4 | ScrollView,
5 | StyleSheet,
6 | Text,
7 | View,
8 | TextInput,
9 | Button,
10 | } from 'react-native';
11 | import useMedication from '../hooks/useMedication';
12 | import { styleSheetFactory } from '../themes/themes';
13 | import { useTheme } from 'react-native-themed-styles';
14 | const { width: WIDTH } = Dimensions.get('window');
15 |
16 | const NewPillDataScreen = (props) => {
17 | const { medication } = useMedication();
18 |
19 | let userID = props.navigation.state.params.selectedPill[0][0];
20 | let medication_ID = -1;
21 | let selectedPill = 'name';
22 | let pillDosage = 0;
23 | let pillRefills = 0;
24 |
25 | const [inputPillName, setInputPillName] = useState(selectedPill);
26 | const [inputDosage, setInputDosage] = useState(pillDosage);
27 | const [inputRefills, setInputRefills] = useState(pillRefills);
28 |
29 | setState = (anObject) => {
30 | if (anObject.hasOwnProperty('inputPillName')) {
31 | setInputPillName(anObject.inputPillName);
32 | } else if (anObject.hasOwnProperty('inputDosage')) {
33 | setInputDosage(anObject.inputDosage);
34 | } else if (anObject.hasOwnProperty('inputRefills')) {
35 | setInputRefills(anObject.inputRefills);
36 | } else {
37 | console.log('Not a valid state option');
38 | }
39 | };
40 |
41 | setPillName = (value) => {
42 | this.setState({ inputPillName: value });
43 | };
44 |
45 | setDosage = (value) => {
46 | this.setState({ inputDosage: value });
47 | };
48 |
49 | setRefills = (value) => {
50 | this.setState({ inputRefills: value });
51 | };
52 |
53 | function saveChangesToDatabase() {
54 | console.log('med:', medication);
55 | for (let i = 0; i < medication.length; i++) {
56 | if (medication[i].Display_Name == inputPillName) {
57 | medication_ID = medication[i].ID;
58 | }
59 | }
60 |
61 | fetch(`https://pillpal-app.de/Takes`, {
62 | method: 'POST',
63 | headers: {
64 | Accept: 'application/json',
65 | 'Content-Type': 'application/json',
66 | },
67 | body: JSON.stringify({
68 | User_ID: userID,
69 | Medication_ID: medication_ID,
70 | Amount_Prescribed: inputDosage,
71 | Refills: inputRefills,
72 | }),
73 | });
74 | }
75 |
76 | return (
77 |
78 |
82 |
83 |
91 |
92 |
93 | Pill Name
94 |
95 |
96 | {selectedPill}
97 |
98 |
99 |
100 | Dosage
101 |
102 |
103 | {pillDosage}
104 |
105 |
106 |
107 | Refills
108 |
109 |
110 | {pillRefills}
111 |
112 |
113 |
114 | );
115 | };
116 |
117 | NewPillDataScreen.navigationOptions = {
118 | title: 'Add New Pill',
119 | };
120 |
121 | export default NewPillDataScreen;
122 |
123 | const styles = StyleSheet.create({
124 | container: {
125 | flex: 1,
126 | backgroundColor: '#FFFFFF',
127 | marginHorizontal: 4,
128 | },
129 | textContainer: {
130 | marginTop: 8,
131 | marginBottom: 20,
132 | },
133 | mainHeaderText: {
134 | fontSize: 20,
135 | color: 'rgba(70,70,70, 1)',
136 | fontWeight: 'bold',
137 | marginTop: 8,
138 | },
139 | headerText: {
140 | fontSize: 18,
141 | color: 'rgba(70,70,70, 1)',
142 | fontWeight: 'normal',
143 | marginTop: 8,
144 | },
145 | inputContainer: {
146 | flexDirection: 'row',
147 | justifyContent: 'space-between',
148 | alignItems: 'center',
149 | marginVertical: 10,
150 | width: WIDTH - 55,
151 | height: 45,
152 | borderRadius: 25,
153 | fontSize: 16,
154 | marginTop: 2,
155 | paddingLeft: 12,
156 | paddingRight: 5,
157 | backgroundColor: 'rgba(204, 255, 255, 0.7)',
158 | },
159 | buttonContainer: {
160 | paddingHorizontal: 40,
161 | marginTop: 15,
162 | marginLeft: WIDTH - 200,
163 | },
164 | buttonShape: {
165 | borderRadius: 10,
166 | marginTop: 10,
167 | marginBottom: 10,
168 | },
169 | });
170 |
--------------------------------------------------------------------------------
/components/CalendarNote.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Dimensions, Platform, StyleSheet, Text, 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 | const { width: WIDTH } = Dimensions.get('window');
9 |
10 | export function CalendarNote(props) {
11 | const { infoArray } = props;
12 | const [styles] = useTheme(darkstyles)
13 | return (
14 |
15 | {infoArray.map((entry) => (
16 |
17 |
18 |
19 | Pills Taken: {entry.pillsTaken}
20 |
21 |
22 |
23 |
24 |
25 | Symptoms: {entry.symptoms}
26 |
27 |
28 |
29 |
30 |
31 | Feelings: {entry.feelings}
32 |
33 |
34 |
35 |
36 |
37 | Additional Details: {entry.additionalDetails}
38 |
39 |
40 |
41 | ))}
42 |
43 |
44 |
45 | Edit
46 |
50 |
51 |
52 |
53 | );
54 | }
55 |
56 | CalendarNote.propTypes = {
57 | /**
58 | * The name and quantity of every type of pill taken on the selected date.
59 | */
60 | pillsTaken: PropTypes.string,
61 | /**
62 | * The list of symptoms that the user logged for the selected date.
63 | */
64 | symptoms: PropTypes.string,
65 | /**
66 | * The list of feelings that the user logged for the selected date.
67 | */
68 | feelings: PropTypes.string,
69 | /**
70 | * Any additional information that the user added for the selected date.
71 | */
72 | additionalDetails: PropTypes.string,
73 | };
74 |
75 | const styles = StyleSheet.create({
76 | calendarNoteInfoContainer: {
77 | marginTop: 10,
78 | marginBottom: 10,
79 | marginLeft: 15,
80 | marginRight: 15,
81 | width: WIDTH - 25,
82 | ...Platform.select({
83 | ios: {
84 | shadowColor: 'black',
85 | shadowOffset: { width: 0, height: -3 },
86 | shadowOpacity: 0.1,
87 | shadowRadius: 3,
88 | },
89 | android: {
90 | elevation: 5,
91 | },
92 | }),
93 | backgroundColor: '#fbfbfb',
94 | borderRadius: 5,
95 | paddingVertical: 15,
96 | paddingHorizontal: 15,
97 | flexDirection: 'column',
98 | alignSelf: 'flex-start',
99 | },
100 | calendarNoteTitleText: {
101 | fontSize: 12,
102 | color: 'rgba(70,70,70, 1)',
103 | textAlign: 'right',
104 | },
105 | calendarNoteText: {
106 | fontSize: 12,
107 | paddingVertical: 2,
108 | color: 'rgba(70,70,70, 1)',
109 | textAlign: 'left',
110 | },
111 | calendarNoteEditContainer: {
112 | alignItems: 'flex-end',
113 | },
114 | calendarNoteEditText: {
115 | fontSize: 14,
116 | fontWeight: 'bold',
117 | paddingTop: 20,
118 | color: 'rgba(121,51,153, 1)',
119 | textAlign: 'left',
120 | },
121 | calendarNoteEditTextIcon: {
122 | fontSize: 14,
123 | fontWeight: 'bold',
124 | color: 'rgba(121,51,153, 1)',
125 | textAlign: 'left',
126 | marginTop: 22,
127 | marginLeft: 5,
128 | },
129 | });
130 |
131 | const darkstyles = styleSheetFactory(theme => ({
132 | calendarNoteInfoContainer: {
133 | marginTop: 10,
134 | marginBottom: 10,
135 | marginLeft: 15,
136 | marginRight: 15,
137 | width: WIDTH - 25,
138 | ...Platform.select({
139 | ios: {
140 | shadowColor: 'black',
141 | shadowOffset: { width: 0, height: -3 },
142 | shadowOpacity: 0.1,
143 | shadowRadius: 3,
144 | },
145 | android: {
146 | elevation: 5,
147 | },
148 | }),
149 | backgroundColor: '#fbfbfb',
150 | borderRadius: 5,
151 | paddingVertical: 15,
152 | paddingHorizontal: 15,
153 | flexDirection: 'column',
154 | alignSelf: 'flex-start',
155 | },
156 | calendarNoteTitleText: {
157 | fontSize: 12,
158 | color: 'rgba(70,70,70, 1)',
159 | textAlign: 'right',
160 | },
161 | calendarNoteText: {
162 | fontSize: 12,
163 | paddingVertical: 2,
164 | color: 'rgba(70,70,70, 1)',
165 | textAlign: 'left',
166 | },
167 | calendarNoteEditContainer: {
168 | alignItems: 'flex-end',
169 | },
170 | calendarNoteEditText: {
171 | fontSize: 14,
172 | fontWeight: 'bold',
173 | paddingTop: 20,
174 | color: 'rgba(121,51,153, 1)',
175 | textAlign: 'left',
176 | },
177 | calendarNoteEditTextIcon: {
178 | fontSize: 14,
179 | fontWeight: 'bold',
180 | color: 'rgba(121,51,153, 1)',
181 | textAlign: 'left',
182 | marginTop: 22,
183 | marginLeft: 5,
184 | },
185 | }));
186 |
--------------------------------------------------------------------------------
/screens/SettingsScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import {
3 | ScrollView,
4 | StyleSheet,
5 | Text,
6 | TouchableOpacity,
7 | View,
8 | } from 'react-native';
9 | import { SettingsTitleBox } from '../components/SettingsTitleBox';
10 | import { SettingsOption } from '../components/SettingsOption';
11 | import SwitchButton from '../components/SwitchButton';
12 | import useAuth from '../hooks/useAuth';
13 | import { styleSheetFactory } from "../themes/themes"
14 | import { useTheme } from "react-native-themed-styles"
15 |
16 | const titleTexts = {
17 | title1: 'My Account',
18 | title2: 'App Support',
19 | };
20 |
21 | const optionTexts = {
22 | option1: 'Account Details',
23 | option2: 'Notifications',
24 | option3: 'Edit Pills',
25 | option4: 'Alexa',
26 | option5: 'Dark Mode',
27 | option6: 'Logout',
28 | option7: 'Help',
29 | option8: 'App Version',
30 | };
31 |
32 | const faIcons = {
33 | icon1: 'address-card',
34 | icon2: 'bell',
35 | icon3: 'plus-square',
36 | icon4: 'database',
37 | icon5: 'square',
38 | icon6: 'sign-out',
39 | icon7: 'question',
40 | icon8: 'mobile',
41 | };
42 |
43 | const SettingsScreen = (props) => {
44 | const { navigate } = props.navigation;
45 | const userSettings = useAuth();
46 | const { login, isLoggedIn, isLoading, logout } = useAuth();
47 |
48 | state = {
49 | switch1Value: false,
50 | };
51 |
52 | const [switch1Value, setSwitch1Value] = useState(false);
53 |
54 | setState = (anObject) => {
55 | if (anObject.hasOwnProperty('switch1Value')) {
56 | setSwitch1Value(anObject.switch1Value);
57 | } else {
58 | console.log('Not a valid state option');
59 | }
60 | };
61 |
62 | toggleSwitch1 = (value) => {
63 | this.setState({ switch1Value: value });
64 | };
65 |
66 | //HERE IS WHAT I TRIED DOING TO SWITCH IT, BUT TO NO AVAIL
67 | if (switch1Value == false) {
68 | const [styles] = useTheme(darkstyles, "dark")
69 | }
70 | else{
71 | const [styles] = useTheme(darkstyles, "light")
72 | }
73 |
74 | useEffect(() => {
75 | if (!isLoggedIn) {
76 | navigate('Auth');
77 | }
78 | }, [isLoggedIn]);
79 | return (
80 |
81 |
85 |
86 | navigate('AccountDetails')}>
87 | >}
91 | />
92 |
93 |
94 | >}
98 | />
99 |
100 | navigate('EditPills')}>
101 | >}
105 | />
106 |
107 |
108 | navigate('Alexa')}>
109 | >}
113 | />
114 |
115 |
116 |
121 |
126 |
127 |
128 | }
129 | />
130 |
131 | logout()}>
132 | >}
136 | />
137 |
138 |
139 |
140 |
141 | navigate('Help')}>
142 | >}
146 | />
147 |
148 |
149 | 1.0}
153 | />
154 |
155 |
156 | );
157 | };
158 |
159 | SettingsScreen.navigationOptions = {
160 | title: 'Settings',
161 | };
162 |
163 | export default SettingsScreen;
164 |
165 | const styles = StyleSheet.create({
166 | container: {
167 | flex: 1,
168 | backgroundColor: '#FFFFFF',
169 | },
170 | contentContainer: {
171 | minHeight: '100%',
172 | },
173 | });
174 |
175 | const darkstyles = styleSheetFactory(theme =>({
176 | container: {
177 | flex: 1,
178 | backgroundColor: theme.backgroundColor,
179 | },
180 | contentContainer: {
181 | minHeight: '100%',
182 | },
183 | }));
184 |
185 |
186 |
--------------------------------------------------------------------------------
/screens/EditPillDataScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | Dimensions,
4 | ScrollView,
5 | StyleSheet,
6 | Text,
7 | View,
8 | TextInput,
9 | Button,
10 | TouchableOpacity,
11 | } from 'react-native';
12 | import { styleSheetFactory } from '../themes/themes';
13 | import { useTheme } from 'react-native-themed-styles';
14 | const { width: WIDTH } = Dimensions.get('window');
15 |
16 | const EditPillDataScreen = (props) => {
17 | let userID = props.navigation.state.params.selectedPill[3][0];
18 | let medication_ID = props.navigation.state.params.selectedPill[4][0];
19 | let selectedPill = props.navigation.state.params.selectedPill[0][0];
20 | let pillDosage = props.navigation.state.params.selectedPill[1][0];
21 | let pillRefills = props.navigation.state.params.selectedPill[2][0];
22 |
23 | const [inputPillName, setInputPillName] = useState(selectedPill);
24 | const [inputDosage, setInputDosage] = useState(pillDosage);
25 | const [inputRefills, setInputRefills] = useState(pillRefills);
26 |
27 | setState = (anObject) => {
28 | if (anObject.hasOwnProperty('inputPillName')) {
29 | setInputPillName(anObject.inputPillName);
30 | } else if (anObject.hasOwnProperty('inputDosage')) {
31 | setInputDosage(anObject.inputDosage);
32 | } else if (anObject.hasOwnProperty('inputRefills')) {
33 | setInputRefills(anObject.inputRefills);
34 | } else {
35 | console.log('Not a valid state option');
36 | }
37 | };
38 |
39 | setPillName = (value) => {
40 | this.setState({ inputPillName: value });
41 | };
42 |
43 | setDosage = (value) => {
44 | this.setState({ inputDosage: value });
45 | };
46 |
47 | setRefills = (value) => {
48 | this.setState({ inputRefills: value });
49 | };
50 |
51 | function saveChangesToDatabase() {
52 | fetch(`https://pillpal-app.de/Takes/${userID}`, {
53 | method: 'POST',
54 | headers: {
55 | Accept: 'application/json',
56 | 'Content-Type': 'application/json',
57 | },
58 | body: JSON.stringify({
59 | User_ID: userID,
60 | Medication_ID: medication_ID,
61 | Amount_Prescribed: inputDosage,
62 | Refills: inputRefills,
63 | Display_Name: inputPillName,
64 | }),
65 | });
66 | }
67 |
68 | function deleteFromDatabase() {
69 | fetch(`https://pillpal-app.de/Takes/${userID}/${medication_ID}`, {
70 | method: 'DELETE',
71 | headers: {
72 | Accept: 'application/json',
73 | 'Content-Type': 'application/json',
74 | },
75 | });
76 | }
77 |
78 | return (
79 |
80 |
84 |
85 |
86 | Editing Pill: {selectedPill}
87 |
88 |
89 |
90 |
91 |
99 |
100 |
101 | Dosage
102 |
103 |
104 | {pillDosage}
105 |
106 |
107 |
108 | Refills
109 |
110 |
111 | {pillRefills}
112 |
113 |
114 |
122 |
123 |
124 | );
125 | };
126 |
127 | EditPillDataScreen.navigationOptions = {
128 | title: 'Edit A Pill',
129 | };
130 |
131 | export default EditPillDataScreen;
132 |
133 | const styles = StyleSheet.create({
134 | container: {
135 | flex: 1,
136 | backgroundColor: '#FFFFFF',
137 | marginHorizontal: 4,
138 | },
139 | textContainer: {
140 | marginTop: 8,
141 | marginBottom: 20,
142 | },
143 | mainHeaderText: {
144 | fontSize: 20,
145 | color: 'rgba(70,70,70, 1)',
146 | fontWeight: 'bold',
147 | marginTop: 8,
148 | },
149 | headerText: {
150 | fontSize: 18,
151 | color: 'rgba(70,70,70, 1)',
152 | fontWeight: 'normal',
153 | marginTop: 8,
154 | },
155 | inputContainer: {
156 | flexDirection: 'row',
157 | justifyContent: 'space-between',
158 | alignItems: 'center',
159 | marginVertical: 10,
160 | width: WIDTH - 55,
161 | height: 45,
162 | borderRadius: 25,
163 | fontSize: 16,
164 | marginTop: 2,
165 | paddingLeft: 12,
166 | paddingRight: 5,
167 | backgroundColor: 'rgba(204, 255, 255, 0.7)',
168 | },
169 | buttonContainer: {
170 | paddingHorizontal: 40,
171 | marginTop: 15,
172 | marginLeft: WIDTH - 200,
173 | },
174 | buttonShape: {
175 | borderRadius: 10,
176 | marginTop: 10,
177 | marginBottom: 10,
178 | },
179 | });
180 |
--------------------------------------------------------------------------------
/components/PillsLoggedTodayCard.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 | import { styleSheetFactory } from "../themes/themes"
6 | import { useTheme } from "react-native-themed-styles"
7 |
8 | export function PillsLoggedTodayCard(props) {
9 | const { title, infoArray } = props;
10 | const [styles] = useTheme(darkstyles)
11 |
12 | return (
13 |
14 |
15 |
23 | {title}
24 |
25 |
32 |
33 | Edit
34 |
38 |
39 |
40 |
41 | {infoArray.map(entry => (
42 |
50 |
57 | {entry.name}
58 |
59 |
60 |
67 | {entry.dosage}
68 |
69 |
70 |
77 |
78 | {entry.formattedTimeTaken}
79 |
80 |
81 |
82 | ))}
83 |
84 | );
85 | }
86 |
87 | PillsLoggedTodayCard.propTypes = {
88 | /**
89 | * The header that is displayed at the top to describe the purpose of the card.
90 | */
91 | title: PropTypes.string,
92 | /**
93 | * An array containing the information for the card.
94 | */
95 | infoArray: PropTypes.arrayOf(
96 | PropTypes.shape({
97 | name: PropTypes.string,
98 | dosage: PropTypes.string,
99 | formattedTimeTaken: PropTypes.string,
100 | })
101 | ),
102 | };
103 |
104 | const styles = StyleSheet.create({
105 | pillsLoggedInfoContainer: {
106 | marginTop: 10,
107 | marginBottom: 10,
108 | marginLeft: 10,
109 | marginRight: 10,
110 | height: 115,
111 | ...Platform.select({
112 | ios: {
113 | shadowColor: 'black',
114 | shadowOffset: { width: 0, height: -3 },
115 | shadowOpacity: 0.1,
116 | shadowRadius: 3,
117 | },
118 | android: {
119 | elevation: 5,
120 | },
121 | }),
122 | backgroundColor: '#fbfbfb',
123 | borderRadius: 10,
124 | paddingVertical: 15,
125 | paddingHorizontal: 15,
126 | },
127 | pillsLoggedTitleText: {
128 | marginBottom: 10,
129 | fontSize: 15,
130 | color: 'rgba(70,70,70, 1)',
131 | textAlign: 'center',
132 | },
133 | pillsLoggedInfoText: {
134 | fontSize: 13,
135 | color: 'rgba(90,90,90, 1)',
136 | textAlign: 'left',
137 | },
138 | pillsLoggedEditText: {
139 | fontSize: 14,
140 | fontWeight: 'bold',
141 | color: 'rgba(121,51,153, 1)',
142 | textAlign: 'left',
143 | },
144 | pillsLoggedEditTextIcon: {
145 | fontSize: 14,
146 | fontWeight: 'bold',
147 | color: 'rgba(121,51,153, 1)',
148 | textAlign: 'left',
149 | marginTop: 2,
150 | marginLeft: 5,
151 | },
152 | });
153 |
154 | const darkstyles = styleSheetFactory(theme => ({
155 | pillsLoggedInfoContainer: {
156 | marginTop: 10,
157 | marginBottom: 10,
158 | marginLeft: 10,
159 | marginRight: 10,
160 | height: 115,
161 | ...Platform.select({
162 | ios: {
163 | shadowColor: 'black',
164 | shadowOffset: { width: 0, height: -3 },
165 | shadowOpacity: 0.1,
166 | shadowRadius: 3,
167 | },
168 | android: {
169 | elevation: 5,
170 | },
171 | }),
172 | backgroundColor: '#fbfbfb',
173 | borderRadius: 10,
174 | paddingVertical: 15,
175 | paddingHorizontal: 15,
176 | },
177 | pillsLoggedTitleText: {
178 | marginBottom: 10,
179 | fontSize: 15,
180 | color: 'rgba(70,70,70, 1)',
181 | textAlign: 'center',
182 | },
183 | pillsLoggedInfoText: {
184 | fontSize: 13,
185 | color: 'rgba(90,90,90, 1)',
186 | textAlign: 'left',
187 | },
188 | pillsLoggedEditText: {
189 | fontSize: 14,
190 | fontWeight: 'bold',
191 | color: 'rgba(121,51,153, 1)',
192 | textAlign: 'left',
193 | },
194 | pillsLoggedEditTextIcon: {
195 | fontSize: 14,
196 | fontWeight: 'bold',
197 | color: 'rgba(121,51,153, 1)',
198 | textAlign: 'left',
199 | marginTop: 2,
200 | marginLeft: 5,
201 | },
202 | }));
--------------------------------------------------------------------------------
/screens/CalendarScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { ScrollView, StyleSheet, View } from 'react-native';
3 | import CalendarPicker from 'react-native-calendar-picker';
4 | import { CalendarNote } from '../components/CalendarNote';
5 | import { styleSheetFactory } from "../themes/themes"
6 | import { useTheme } from "react-native-themed-styles"
7 | import moment from 'moment';
8 | import useAuth from '../hooks/useAuth';
9 | import useCalendar from '../hooks/useCalendar';
10 | import useLog_Symptom from '../hooks/useLog_Symptom';
11 | import useLog_Feeling from '../hooks/useLog_Feeling';
12 | import useLog_Pills from '../hooks/useLog_Pills';
13 | import useMedication from '../hooks/useMedication';
14 |
15 | const CalendarScreen = (props) => {
16 | const [styles] = useTheme(darkstyles)
17 | const userSettings = useAuth();
18 | let userID = userSettings.user ? userSettings.user.ID : null;
19 | const { calendar } = useCalendar(userID);
20 | const { symptom } = useLog_Symptom(userID);
21 | const { feeling } = useLog_Feeling(userID);
22 | const { logPills } = useLog_Pills(userID);
23 | const { medication } = useMedication();
24 |
25 |
26 | let availableMeds = medication.map((medication) => {
27 | return [medication.ID, medication.Display_Name];
28 | });
29 |
30 | let userPillHistory = logPills.map((logPills) => {
31 | return [logPills.Datetime, logPills.Medication_ID];
32 | });
33 |
34 | let userSymptomsHistory = symptom.map((symptom) => {
35 | return [symptom.Date, symptom.Display_Name];
36 | });
37 |
38 | let userFeelingsHistory = feeling.map((feeling) => {
39 | return [feeling.Date, feeling.Display_Name];
40 | });
41 |
42 | let userNotesHistory = calendar.map((calendar) => {
43 | return [calendar.Date, calendar.Note_Text];
44 | });
45 |
46 | state = {
47 | selectedStartDate: '',
48 | };
49 |
50 | const [selectedStartDate, setSelectedStartDate] = useState('');
51 |
52 | setState = (anObject) => {
53 | if (anObject.hasOwnProperty('selectedStartDate')) {
54 | setSelectedStartDate(anObject.selectedStartDate);
55 | } else {
56 | console.log('Not a valid state option');
57 | }
58 | };
59 |
60 | const onDateChange = (date) => {
61 | if (date != '') {
62 | setState({
63 | selectedStartDate: date,
64 | });
65 | }
66 | };
67 |
68 | const findPillsTakenToday = (tempStartDate) => {
69 | let medicationDisplayNames = '';
70 | for (let i = 0; i < userPillHistory.length; i++) {
71 | if (userPillHistory[i][0].includes(tempStartDate)) {
72 | for (let j = 0; j < availableMeds.length; j++) {
73 | if (userPillHistory[i][1] == availableMeds[j][0]) {
74 | if (medicationDisplayNames == '') {
75 | medicationDisplayNames += availableMeds[j][1];
76 | } else {
77 | medicationDisplayNames += ', ' + availableMeds[j][1];
78 | }
79 | }
80 | }
81 | }
82 | }
83 |
84 | if (medicationDisplayNames == '') {
85 | medicationDisplayNames = 'none';
86 | }
87 | return medicationDisplayNames;
88 | };
89 |
90 | const findFeelingsToday = (tempStartDate) => {
91 | let feelingDisplayNames = '';
92 | for (let i = 0; i < userFeelingsHistory.length; i++) {
93 | if (userFeelingsHistory[i][0].includes(tempStartDate)) {
94 | if (feelingDisplayNames == '') {
95 | feelingDisplayNames += userFeelingsHistory[i][1];
96 | } else {
97 | feelingDisplayNames += ', ' + userFeelingsHistory[i][1];
98 | }
99 | }
100 | }
101 |
102 | if (feelingDisplayNames == '') {
103 | feelingDisplayNames = 'none';
104 | }
105 | return feelingDisplayNames;
106 | };
107 |
108 | const findSymptomsToday = (tempStartDate) => {
109 | let symptomDisplayNames = '';
110 | for (let i = 0; i < userSymptomsHistory.length; i++) {
111 | if (userSymptomsHistory[i][0].includes(tempStartDate)) {
112 | if (symptomDisplayNames == '') {
113 | symptomDisplayNames += userSymptomsHistory[i][1];
114 | } else {
115 | symptomDisplayNames += ', ' + userSymptomsHistory[i][1];
116 | }
117 | }
118 | }
119 |
120 | if (symptomDisplayNames == '') {
121 | symptomDisplayNames = 'none';
122 | }
123 | return symptomDisplayNames;
124 | };
125 |
126 | const findNoteToday = (tempStartDate) => {
127 | let noteDisplayText = '';
128 | for (let i = 0; i < userNotesHistory.length; i++) {
129 | if (userNotesHistory[i][0].includes(tempStartDate)) {
130 | noteDisplayText = userNotesHistory[i][1];
131 | }
132 | }
133 |
134 | if (noteDisplayText == '') {
135 | noteDisplayText = 'No notes added. Tap here to add a new note.';
136 | }
137 | return noteDisplayText;
138 | };
139 |
140 | function setCurrentNote(startDate) {
141 | let copyOfStartDate = startDate;
142 | let tempStartDate = moment(copyOfStartDate).format('YYYY-MM-DD');
143 |
144 | // Find pills from today
145 | let medicationDisplayNames = findPillsTakenToday(tempStartDate);
146 |
147 | // Find feelings from today
148 | let feelingDisplayNames = findFeelingsToday(tempStartDate);
149 |
150 | // Find symptoms from today
151 | let symptomDisplayNames = findSymptomsToday(tempStartDate);
152 |
153 | // Find note from today
154 | let noteDisplayText = findNoteToday(tempStartDate);
155 |
156 | const note = [
157 | {
158 | id: 0,
159 | date: startDate,
160 | pillsTaken: medicationDisplayNames,
161 | symptoms: symptomDisplayNames,
162 | feelings: feelingDisplayNames,
163 | additionalDetails: noteDisplayText,
164 | },
165 | ];
166 |
167 | currentNote = note.filter((note) => note.id == 0);
168 |
169 | return currentNote;
170 | }
171 |
172 | let startDate = selectedStartDate ? selectedStartDate : moment();
173 | return (
174 |
175 |
179 |
180 |
181 |
182 |
183 |
184 | );
185 |
186 | }
187 | CalendarScreen.navigationOptions = {
188 | title: 'Calendar',
189 | };
190 |
191 | export default CalendarScreen;
192 | const styles = StyleSheet.create({
193 | container: {
194 | flex: 1,
195 | backgroundColor: '#FFFFFF',
196 | },
197 | });
198 |
199 | const darkstyles = styleSheetFactory(theme => ({
200 | container: {
201 | flex: 1,
202 | backgroundColor: '#FFFFFF',
203 | },
204 | }));
205 |
206 |
--------------------------------------------------------------------------------
/components/__tests__/__snapshots__/PillsLoggedTodayCard-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders correctly 1`] = `
4 |
26 |
33 |
43 |
53 | Pills Logged Today
54 |
55 |
56 |
65 |
72 |
82 | Edit
83 |
84 |
85 |
86 |
87 |
88 |
97 |
106 |
115 | Ibuprofen
116 |
117 |
118 |
127 |
136 | 2 pills
137 |
138 |
139 |
148 |
157 | 8:30 a.m.
158 |
159 |
160 |
161 |
170 |
179 |
188 | Ibuprofen
189 |
190 |
191 |
200 |
209 | 2 pills
210 |
211 |
212 |
221 |
230 | 1:07 p.m.
231 |
232 |
233 |
234 |
243 |
252 |
261 | Nitroglycerin
262 |
263 |
264 |
273 |
282 | 1 pill
283 |
284 |
285 |
294 |
303 | 3:45 p.m.
304 |
305 |
306 |
307 |
308 | `;
309 |
--------------------------------------------------------------------------------
/screens/LoginScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import {
3 | Dimensions,
4 | ScrollView,
5 | StyleSheet,
6 | Text,
7 | TextInput,
8 | TouchableOpacity,
9 | View,
10 | } from 'react-native';
11 | import { FontAwesome } from '@expo/vector-icons';
12 | import useAuth from '../hooks/useAuth';
13 | import { styleSheetFactory } from "../themes/themes"
14 | import { useTheme } from "react-native-themed-styles"
15 |
16 | const { width: WIDTH } = Dimensions.get('window');
17 |
18 | const LoginScreen = (props) => {
19 | const [styles] = useTheme(darkstyles)
20 | state = {
21 | validUsername: true,
22 | validPassword: true,
23 | errorTextColor: 'white',
24 | loginEnabled: false,
25 | showPass: true,
26 | press: false,
27 | };
28 |
29 | const [inputUsername, setInputUsername] = useState('');
30 | const [inputPassword, setInputPassword] = useState('');
31 | const [validUsername, setValidUsername] = useState(true);
32 | const [validPassword, setValidPassword] = useState(true);
33 | const [errorTextColor, setErrorTextColor] = useState('white');
34 | const [loginEnabled, setLoginEnabled] = useState(false);
35 | const [press, setPress] = useState(false);
36 |
37 | const { login, isLoggedIn, isLoading, logout } = useAuth();
38 |
39 | setState = (anObject) => {
40 | if (anObject.hasOwnProperty('inputUsername')) {
41 | setInputUsername(anObject.inputUsername);
42 | } else if (anObject.hasOwnProperty('inputPassword')) {
43 | setInputPassword(anObject.inputPassword);
44 | } else if (anObject.hasOwnProperty('validUsername')) {
45 | setValidUsername(anObject.validUsername);
46 | } else if (anObject.hasOwnProperty('validPassword')) {
47 | setValidPassword(anObject.validPassword);
48 | } else if (anObject.hasOwnProperty('errorTextColor')) {
49 | setErrorTextColor(anObject.errorTextColor);
50 | } else if (anObject.hasOwnProperty('loginEnabled')) {
51 | setLoginEnabled(anObject.loginEnabled);
52 | } else if (anObject.hasOwnProperty('press')) {
53 | setPress(anObject.press);
54 | } else {
55 | console.log('Not a valid state option');
56 | }
57 | };
58 |
59 | showPass = () => {
60 | if (this.state.press == false) {
61 | this.setState({ showPass: false, press: true });
62 | } else {
63 | this.setState({ showPass: true, press: false });
64 | }
65 | };
66 |
67 | setUsername = (value) => {
68 | this.setState({ inputUsername: value });
69 | if (value == '') {
70 | this.setState({ loginEnabled: false });
71 | } else {
72 | if (state.inputPassword !== '') {
73 | this.setState({ loginEnabled: true });
74 | }
75 | }
76 | };
77 |
78 | setPassword = (value) => {
79 | this.setState({ inputPassword: value });
80 | if (value == '') {
81 | this.setState({ loginEnabled: false });
82 | } else {
83 | if (this.state.inputUsername !== '') {
84 | this.setState({ loginEnabled: true });
85 | }
86 | }
87 | };
88 |
89 | const { navigate } = props.navigation;
90 |
91 | useEffect(() => {
92 | let mounted = true;
93 | if (mounted) {
94 | if (isLoggedIn) {
95 | navigate('Home');
96 | }
97 | }
98 |
99 | return () => (mounted = false);
100 | }, [isLoggedIn]);
101 |
102 | if (isLoading) {
103 | return (
104 |
105 | Loading...
106 |
107 | );
108 | }
109 |
110 | return (
111 |
112 |
116 |
117 | Pill Pal
118 |
119 |
120 |
121 | Wrong user/password combination. Please try again!
122 |
123 |
124 |
125 |
126 |
127 |
128 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
149 |
150 |
154 |
158 |
159 |
160 |
161 | login(inputUsername, inputPassword)}
167 | >
168 | Login
169 |
170 |
171 | Don't have an account?
172 | navigate('SignUp')}>
173 | Sign Up Now!
174 |
175 |
176 |
177 | );
178 | };
179 |
180 | LoginScreen.navigationOptions = {
181 | header: null,
182 | };
183 |
184 | export default LoginScreen;
185 |
186 | const styles = StyleSheet.create({
187 | container: {
188 | flex: 1,
189 | width: null,
190 | height: null,
191 | backgroundColor: '#fff',
192 | },
193 | mainContainer: {
194 | alignItems: 'center',
195 | flexDirection: 'column',
196 | justifyContent: 'center',
197 | minHeight: '100%',
198 | },
199 | logoContainer: {
200 | alignItems: 'center',
201 | marginBottom: 10,
202 | },
203 | logo: {
204 | fontSize: 80,
205 | },
206 | iconContainer: {
207 | width: 20,
208 | },
209 | inputContainer: {
210 | flexDirection: 'row',
211 | justifyContent: 'space-between',
212 | alignItems: 'center',
213 | marginVertical: 10,
214 | width: WIDTH - 55,
215 | height: 45,
216 | borderRadius: 25,
217 | fontSize: 16,
218 | marginTop: 10,
219 | paddingLeft: 12,
220 | paddingRight: 5,
221 | backgroundColor: 'rgba(0, 112, 26, 0.7)',
222 | // backgroundColor: 'rgba(0, 0, 0, 0.35)'
223 | },
224 | icon: {
225 | fontSize: 18,
226 | color: 'rgba(0, 0, 0, 0.55)',
227 | backgroundColor: 'rgba(255, 255, 255, 0)',
228 | },
229 | input: {
230 | flexGrow: 1,
231 | color: 'white',
232 | },
233 | btnLoginEnabled: {
234 | width: WIDTH - 55,
235 | height: 45,
236 | borderRadius: 25,
237 | backgroundColor: '#432577',
238 | justifyContent: 'center',
239 | marginTop: 20,
240 | marginBottom: 10,
241 | },
242 | btnLoginDisabled: {
243 | width: WIDTH - 55,
244 | height: 45,
245 | borderRadius: 25,
246 | backgroundColor: 'grey',
247 | justifyContent: 'center',
248 | marginTop: 20,
249 | marginBottom: 10,
250 | },
251 | btnText: {
252 | color: 'white',
253 | fontSize: 16,
254 | textAlign: 'center',
255 | },
256 | });
257 |
258 | const darkstyles = styleSheetFactory(theme => ({
259 | container: {
260 | flex: 1,
261 | width: null,
262 | height: null,
263 | backgroundColor: theme.backgroundColor,
264 | },
265 | mainContainer: {
266 | alignItems: 'center',
267 | flexDirection: 'column',
268 | justifyContent: 'center',
269 | minHeight: '100%',
270 | },
271 | logoContainer: {
272 | alignItems: 'center',
273 | marginBottom: 10,
274 | },
275 | logo: {
276 | fontSize: 80,
277 | color: theme.textColor
278 | },
279 | iconContainer: {
280 | width: 20,
281 | },
282 | inputContainer: {
283 | flexDirection: 'row',
284 | justifyContent: 'space-between',
285 | alignItems: 'center',
286 | marginVertical: 10,
287 | width: WIDTH - 55,
288 | height: 45,
289 | borderRadius: 25,
290 | fontSize: 16,
291 | marginTop: 10,
292 | paddingLeft: 12,
293 | paddingRight: 5,
294 | backgroundColor: 'rgba(0, 112, 26, 0.7)',
295 | // backgroundColor: 'rgba(0, 0, 0, 0.35)'
296 | },
297 | icon: {
298 | fontSize: 18,
299 | color: 'rgba(0, 0, 0, 0.55)',
300 | backgroundColor: 'rgba(255, 255, 255, 0)',
301 | },
302 | input: {
303 | flexGrow: 1,
304 | color: 'white',
305 | },
306 | btnLoginEnabled: {
307 | width: WIDTH - 55,
308 | height: 45,
309 | borderRadius: 25,
310 | backgroundColor: '#432577',
311 | justifyContent: 'center',
312 | marginTop: 20,
313 | marginBottom: 10,
314 | },
315 | btnLoginDisabled: {
316 | width: WIDTH - 55,
317 | height: 45,
318 | borderRadius: 25,
319 | backgroundColor: 'grey',
320 | justifyContent: 'center',
321 | marginTop: 20,
322 | marginBottom: 10,
323 | },
324 | btnText: {
325 | color: 'white',
326 | fontSize: 16,
327 | textAlign: 'center',
328 | },
329 | }));
330 |
--------------------------------------------------------------------------------
/screens/SignUpScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dimensions, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
3 | import { FontAwesome } from '@expo/vector-icons';
4 |
5 | const { width: WIDTH } = Dimensions.get('window');
6 |
7 | export default class SignUpScreen extends React.Component {
8 | constructor() {
9 | super();
10 | this.showPass = this.showPass.bind(this);
11 | this.showConfirmPass = this.showConfirmPass.bind(this);
12 | this.haveFullName = this.haveFullName.bind(this);
13 | this.haveEmail = this.haveEmail.bind(this);
14 | this.haveUsername = this.haveUsername.bind(this);
15 | this.havePassword = this.havePassword.bind(this);
16 | this.haveConfirmPassword = this.haveConfirmPassword.bind(this);
17 | this.validateInput = this.validateInput.bind(this);
18 | this.state = {
19 | inputFullName: '',
20 | inputEmail: '',
21 | inputUsername: '',
22 | inputPassword: '',
23 | inputConfirmPassword: '',
24 | validFullName: true,
25 | validPassword: true,
26 | validUsername: true,
27 | validPassword: true,
28 | validConfirmPassword: true,
29 | errorTextColor: 'white',
30 | signUpEnabled: false,
31 | showPass: true,
32 | showConfirmPass: true,
33 | press: false,
34 | pressConfirm: false
35 | }
36 | }
37 |
38 | showPass = () => {
39 | if (this.state.press == false) {
40 | this.setState({ showPass: false, press: true });
41 | } else {
42 | this.setState({ showPass: true, press: false });
43 | }
44 | }
45 |
46 | showConfirmPass = () => {
47 | if (this.state.pressConfirm == false) {
48 | this.setState({ showConfirmPass: false, pressConfirm: true });
49 | } else {
50 | this.setState({ showConfirmPass: true, pressConfirm: false });
51 | }
52 | }
53 |
54 | haveFullName = (value) => {
55 | this.setState({inputFullName: value});
56 | if (value == '') {
57 | this.setState({signUpEnabled: false});
58 | } else {
59 | if (this.state.inputEmail !== '' && this.state.inputUsername !== '' && this.state.inputPassword !== '' && this.state.inputConfirmPassword != '')
60 | {
61 | this.setState({signUpEnabled: true});
62 | }
63 | }
64 | }
65 |
66 | haveEmail = (value) => {
67 | this.setState({inputEmail: value});
68 | if (value == '') {
69 | this.setState({signUpEnabled: false});
70 | } else {
71 | if (this.state.inputFullName !== '' && this.state.inputUsername !== '' && this.state.inputPassword !== '' && this.state.inputConfirmPassword != '')
72 | {
73 | this.setState({signUpEnabled: true});
74 | }
75 | }
76 | }
77 |
78 | haveUsername = (value) => {
79 | this.setState({inputUsername: value});
80 | if (value == '') {
81 | this.setState({signUpEnabled: false});
82 | } else {
83 | if (this.state.inputFullName !== '' && this.state.inputEmail !== '' && this.state.inputPassword !== '' && this.state.inputConfirmPassword != '')
84 | {
85 | this.setState({signUpEnabled: true});
86 | }
87 | }
88 | }
89 |
90 | havePassword = (value) => {
91 | this.setState({inputPassword: value});
92 | if (value == '') {
93 | this.setState({signUpEnabled: false});
94 | } else {
95 | if (this.state.inputFullName !== '' && this.state.inputEmail !== '' && this.state.inputUsername !== '' && this.state.inputConfirmPassword != '')
96 | {
97 | this.setState({signUpEnabled: true});
98 | }
99 | }
100 | }
101 |
102 | haveConfirmPassword = (value) => {
103 | this.setState({inputConfirmPassword: value});
104 | if (value == '') {
105 | this.setState({signUpEnabled: false});
106 | } else {
107 | if (this.state.inputFullName !== '' && this.state.inputEmail !== '' && this.state.inputUsername !== '' && this.state.inputPassword != '')
108 | {
109 | this.setState({signUpEnabled: true});
110 | }
111 | }
112 | }
113 |
114 | validateInput = () => {
115 | const { navigate } = this.props.navigation;
116 | if (!this.state.validUsername || !this.state.validPassword) {
117 | this.setState({errorTextColor: 'red'});
118 | } else {
119 | this.setState({errorTextColor: 'white'});
120 | navigate('Login');
121 | }
122 | }
123 |
124 | render() {
125 | const { navigate } = this.props.navigation;
126 | return (
127 |
128 |
132 |
133 |
134 | Pill Pal
135 |
136 |
137 | Invalid information. Please try again!
138 |
139 |
140 |
141 |
142 |
143 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
207 |
208 |
209 |
210 |
211 |
212 |
216 | Sign Up
217 |
218 |
219 | Already have an account?
220 | navigate('Login')}>Login!
221 |
222 |
223 | );
224 | }
225 | }
226 |
227 | SignUpScreen.navigationOptions = {
228 | header: null,
229 | };
230 |
231 | const styles = StyleSheet.create({
232 | container: {
233 | flex: 1,
234 | width: null,
235 | height: null,
236 | backgroundColor: '#fff',
237 | },
238 | mainContainer: {
239 | alignItems: 'center',
240 | flexDirection: 'column',
241 | justifyContent: 'center',
242 | minHeight: '100%',
243 | },
244 | logoContainer: {
245 | alignItems: 'center',
246 | marginBottom: 10,
247 | },
248 | logo: {
249 | fontSize: 80
250 | },
251 | iconContainer: {
252 | width: 20
253 | },
254 | inputContainer: {
255 | flexDirection: 'row',
256 | justifyContent: 'space-between',
257 | alignItems: 'center',
258 | marginVertical: 10,
259 | width: WIDTH - 55,
260 | height: 45,
261 | borderRadius: 25,
262 | fontSize: 16,
263 | marginTop: 10,
264 | paddingLeft: 12,
265 | paddingRight: 5,
266 | backgroundColor: 'rgba(0, 112, 26, 0.7)'
267 | },
268 | icon: {
269 | fontSize: 18,
270 | color: 'rgba(0, 0, 0, 0.55)',
271 | backgroundColor: 'rgba(255, 255, 255, 0)'
272 | },
273 | input: {
274 | flexGrow: 1,
275 | color: 'white'
276 | },
277 | btnSignUpEnabled: {
278 | width: WIDTH - 55,
279 | height: 45,
280 | borderRadius: 25,
281 | backgroundColor: '#432577',
282 | justifyContent: 'center',
283 | marginTop: 20,
284 | marginBottom: 10
285 | },
286 | btnSignUpDisabled: {
287 | width: WIDTH - 55,
288 | height: 45,
289 | borderRadius: 25,
290 | backgroundColor: 'grey',
291 | justifyContent: 'center',
292 | marginTop: 20,
293 | marginBottom: 10
294 | },
295 | btnText: {
296 | color: 'white',
297 | fontSize: 16,
298 | textAlign: 'center'
299 | }
300 | });
301 |
--------------------------------------------------------------------------------
/screens/AddFeelingScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Divider } from 'react-native-elements';
3 | import {
4 | Platform,
5 | Dimensions,
6 | ScrollView,
7 | StyleSheet,
8 | View,
9 | Button,
10 | TouchableOpacity,
11 | } from 'react-native';
12 | import moment from 'moment';
13 | import { AddSymptomFeelingOption } from '../components/AddSymptomFeelingOption';
14 | import { IntensityOption } from '../components/IntensityOption';
15 | import useFeeling from '../hooks/useFeeling';
16 | import useAuth from '../hooks/useAuth';
17 | import { styleSheetFactory } from '../themes/themes';
18 | import { useTheme } from 'react-native-themed-styles';
19 |
20 | const { width: WIDTH } = Dimensions.get('window');
21 |
22 | const AddFeelingScreen = (props) => {
23 | let todaysFeelings = props.navigation.state.params.listOfTodaysFeelings;
24 | let originalTodaysFeelings = todaysFeelings;
25 | let todaysDate = moment().format('YYYY-MM-DD');
26 | const userSettings = useAuth();
27 | let userID = userSettings.user ? userSettings.user.ID : null;
28 |
29 | const { updateFeeling, feeling } = useFeeling(todaysFeelings);
30 |
31 | const severityMatches = (severityToMatch, individualFeeling) =>
32 | individualFeeling.severity === severityToMatch ? 'bold' : 'normal';
33 |
34 | function saveChangesToDatabase() {
35 | let feelingsToSave = feeling;
36 | let filteredFeelingsToSave = [];
37 | for (let i = 0; i < feelingsToSave.length; i++) {
38 | if (feelingsToSave[i].severity != '') {
39 | for (let j = 0; j < originalTodaysFeelings.length; j++) {
40 | if (feelingsToSave[i].Display_Name != originalTodaysFeelings[j][0]) {
41 | if (feelingsToSave[i].severity == 'low') {
42 | feelingsToSave[i].severity = 1;
43 | } else if (feelingsToSave[i].severity == 'medium') {
44 | feelingsToSave[i].severity = 2;
45 | } else {
46 | feelingsToSave[i].severity = 3;
47 | }
48 | filteredFeelingsToSave.push([feelingsToSave[i]]);
49 | }
50 | }
51 | }
52 | }
53 |
54 | for (let i = 0; i < filteredFeelingsToSave.length; i++) {
55 | fetch(`https://pillpal-app.de/Log_Feelings`, {
56 | method: 'POST',
57 | headers: {
58 | Accept: 'application/json',
59 | 'Content-Type': 'application/json',
60 | },
61 | body: JSON.stringify({
62 | User_ID: userID,
63 | Date: todaysDate,
64 | Feeling_ID: filteredFeelingsToSave[i][0].ID,
65 | Feeling_Intensity: filteredFeelingsToSave[i][0].severity,
66 | }),
67 | });
68 | }
69 | }
70 |
71 | //const [styles] = useTheme(darkstyles);
72 | return (
73 |
74 |
78 |
79 |
87 |
88 | {feeling.map((individualFeeling) => {
89 | return (
90 |
91 |
92 |
96 |
99 |
100 | updateFeeling('low', individualFeeling)}
104 | >
105 |
106 |
114 |
115 |
116 |
117 |
118 |
119 | updateFeeling('medium', individualFeeling)}
123 | >
124 |
125 |
133 |
134 |
135 |
136 |
137 |
138 | updateFeeling('high', individualFeeling)}
142 | >
143 |
144 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | );
160 | })}
161 |
162 |
163 | );
164 | };
165 |
166 | AddFeelingScreen.navigationOptions = {
167 | title: 'Add Feelings',
168 | };
169 |
170 | export default AddFeelingScreen;
171 |
172 | const styles = StyleSheet.create({
173 | container: {
174 | flex: 1,
175 | backgroundColor: '#FFFFFF',
176 | marginHorizontal: 4,
177 | },
178 | textContainer: {
179 | marginTop: 8,
180 | marginBottom: 20,
181 | },
182 | saveButtonContainer: {
183 | marginTop: 8,
184 | marginBottom: 20,
185 | marginLeft: WIDTH - 130,
186 | width: 100,
187 | },
188 | headerText: {
189 | fontSize: 18,
190 | color: 'rgba(70,70,70, 1)',
191 | fontWeight: 'normal',
192 | marginTop: 8,
193 | },
194 | inputContainer: {
195 | flexDirection: 'row',
196 | justifyContent: 'space-between',
197 | alignItems: 'center',
198 | marginVertical: 10,
199 | width: WIDTH - 55,
200 | height: 45,
201 | borderRadius: 25,
202 | fontSize: 16,
203 | marginTop: 2,
204 | paddingLeft: 12,
205 | paddingRight: 5,
206 | backgroundColor: 'rgba(204, 255, 255, 0.7)',
207 | },
208 | optionContainer: {
209 | flex: 1,
210 | flexDirection: 'row',
211 | justifyContent: 'space-between',
212 | paddingTop: 10,
213 | paddingBottom: 10,
214 | paddingHorizontal: 10,
215 | ...Platform.select({
216 | ios: {
217 | shadowColor: 'black',
218 | shadowOffset: { width: 0, height: -3 },
219 | shadowOpacity: 0.1,
220 | shadowRadius: 3,
221 | },
222 | android: {
223 | elevation: 5,
224 | },
225 | }),
226 | },
227 | displayNameContainer: {
228 | flex: 1,
229 | flexDirection: 'row',
230 | //justifyContent: 'flex-start',
231 | justifyContent: 'center',
232 | alignItems: 'center',
233 | textAlignVertical: 'center',
234 | },
235 | displayNameText: {
236 | fontSize: 16,
237 | color: 'black',
238 | },
239 | divider: {
240 | backgroundColor: 'black',
241 | height: 1,
242 | },
243 | intensityContainer: {
244 | paddingHorizontal: 10,
245 | },
246 | intensityButton: {
247 | backgroundColor: 'white',
248 | borderRadius: 10,
249 | paddingHorizontal: 10,
250 | justifyContent: 'center',
251 | alignItems: 'center',
252 | ...Platform.select({
253 | ios: {
254 | shadowColor: 'black',
255 | shadowOffset: { width: 0, height: -3 },
256 | shadowOpacity: 0.1,
257 | shadowRadius: 3,
258 | },
259 | android: {
260 | elevation: 5,
261 | },
262 | }),
263 | },
264 | intensityTextContainer: {
265 | flex: 1,
266 | justifyContent: 'center',
267 | alignItems: 'center',
268 | textAlignVertical: 'center',
269 | },
270 | intensityText: {
271 | fontSize: 14,
272 | color: 'rgba(70, 70, 70, 1)',
273 | paddingVertical: 10,
274 | textAlignVertical: 'center',
275 | },
276 | divider: {
277 | backgroundColor: 'black',
278 | height: 1,
279 | },
280 | buttonContainer: {
281 | paddingHorizontal: 15,
282 | marginTop: 15,
283 | },
284 | buttonShape: {
285 | borderRadius: 10,
286 | marginTop: 10,
287 | marginBottom: 10,
288 | },
289 | });
290 |
291 | const darkstyles = styleSheetFactory((theme) => ({
292 | container: {
293 | flex: 1,
294 | backgroundColor: theme.backgroundColor,
295 | marginHorizontal: 4,
296 | },
297 | textContainer: {
298 | marginTop: 8,
299 | marginBottom: 20,
300 | },
301 | saveButtonContainer: {
302 | marginTop: 8,
303 | marginBottom: 20,
304 | marginLeft: WIDTH - 130,
305 | width: 100,
306 | },
307 | headerText: {
308 | fontSize: 18,
309 | color: theme.textColor,
310 | fontWeight: 'normal',
311 | marginTop: 8,
312 | },
313 | inputContainer: {
314 | flexDirection: 'row',
315 | justifyContent: 'space-between',
316 | alignItems: 'center',
317 | marginVertical: 10,
318 | width: WIDTH - 55,
319 | height: 45,
320 | borderRadius: 25,
321 | fontSize: 16,
322 | marginTop: 2,
323 | paddingLeft: 12,
324 | paddingRight: 5,
325 | backgroundColor: 'rgba(204, 0, 0, 0.7)',
326 | },
327 | }));
328 |
--------------------------------------------------------------------------------
/screens/AddSymptomScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Divider } from 'react-native-elements';
3 | import {
4 | Platform,
5 | Dimensions,
6 | ScrollView,
7 | StyleSheet,
8 | View,
9 | Button,
10 | TouchableOpacity,
11 | } from 'react-native';
12 | import moment from 'moment';
13 | import { AddSymptomFeelingOption } from '../components/AddSymptomFeelingOption';
14 | import { IntensityOption } from '../components/IntensityOption';
15 | import useSymptom from '../hooks/useSymptom';
16 | import useAuth from '../hooks/useAuth';
17 | import { styleSheetFactory } from '../themes/themes';
18 | import { useTheme } from 'react-native-themed-styles';
19 |
20 | const { width: WIDTH } = Dimensions.get('window');
21 |
22 | const AddSymptomScreen = (props) => {
23 | let todaysSymptoms = props.navigation.state.params.listOfTodaysSymptoms;
24 | let originalTodaysSymptoms = todaysSymptoms;
25 | let todaysDate = moment().format('YYYY-MM-DD');
26 | const userSettings = useAuth();
27 | let userID = userSettings.user ? userSettings.user.ID : null;
28 |
29 | const { updateSymptom, symptom } = useSymptom(todaysSymptoms);
30 |
31 | const severityMatches = (severityToMatch, individualSymptom) =>
32 | individualSymptom.severity === severityToMatch ? 'bold' : 'normal';
33 |
34 | function saveChangesToDatabase() {
35 | let symptomsToSave = symptom;
36 | let filteredSymptomsToSave = [];
37 | for (let i = 0; i < symptomsToSave.length; i++) {
38 | if (symptomsToSave[i].severity != '') {
39 | for (let j = 0; j < originalTodaysSymptoms.length; j++) {
40 | if (symptomsToSave[i].Display_Name != originalTodaysSymptoms[j][0]) {
41 | if (symptomsToSave[i].severity == 'low') {
42 | symptomsToSave[i].severity = 1;
43 | } else if (symptomsToSave[i].severity == 'medium') {
44 | symptomsToSave[i].severity = 2;
45 | } else {
46 | symptomsToSave[i].severity = 3;
47 | }
48 | filteredSymptomsToSave.push([symptomsToSave[i]]);
49 | }
50 | }
51 | }
52 | }
53 |
54 | for (let i = 0; i < filteredSymptomsToSave.length; i++) {
55 | fetch(`https://pillpal-app.de/Log_Symptoms`, {
56 | method: 'POST',
57 | headers: {
58 | Accept: 'application/json',
59 | 'Content-Type': 'application/json',
60 | },
61 | body: JSON.stringify({
62 | User_ID: userID,
63 | Date: todaysDate,
64 | Symptom_ID: filteredSymptomsToSave[i][0].ID,
65 | Symptom_Intensity: filteredSymptomsToSave[i][0].severity,
66 | }),
67 | });
68 | }
69 | }
70 |
71 | //const [styles] = useTheme(darkstyles);
72 | return (
73 |
74 |
78 |
79 |
87 |
88 | {symptom.map((individualSymptom) => {
89 | return (
90 |
91 |
92 |
96 |
99 |
100 | updateSymptom('low', individualSymptom)}
104 | >
105 |
106 |
114 |
115 |
116 |
117 |
118 |
119 | updateSymptom('medium', individualSymptom)}
123 | >
124 |
125 |
133 |
134 |
135 |
136 |
137 |
138 | updateSymptom('high', individualSymptom)}
142 | >
143 |
144 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | );
160 | })}
161 |
162 |
163 | );
164 | };
165 |
166 | AddSymptomScreen.navigationOptions = {
167 | title: 'Add Symptoms',
168 | };
169 |
170 | export default AddSymptomScreen;
171 |
172 | const styles = StyleSheet.create({
173 | container: {
174 | flex: 1,
175 | backgroundColor: '#FFFFFF',
176 | marginHorizontal: 4,
177 | },
178 | textContainer: {
179 | marginTop: 8,
180 | marginBottom: 20,
181 | },
182 | saveButtonContainer: {
183 | marginTop: 8,
184 | marginBottom: 20,
185 | marginLeft: WIDTH - 130,
186 | width: 100,
187 | },
188 | deleteButtonContainer: {
189 | marginTop: 8,
190 | marginBottom: 20,
191 | width: 100,
192 | },
193 | headerText: {
194 | fontSize: 18,
195 | color: 'rgba(70,70,70, 1)',
196 | fontWeight: 'normal',
197 | marginTop: 8,
198 | },
199 | inputContainer: {
200 | flexDirection: 'row',
201 | justifyContent: 'space-between',
202 | alignItems: 'center',
203 | marginVertical: 10,
204 | width: WIDTH - 55,
205 | height: 45,
206 | borderRadius: 25,
207 | fontSize: 16,
208 | marginTop: 2,
209 | paddingLeft: 12,
210 | paddingRight: 5,
211 | backgroundColor: 'rgba(204, 255, 255, 0.7)',
212 | },
213 | optionContainer: {
214 | flex: 1,
215 | flexDirection: 'row',
216 | justifyContent: 'space-between',
217 | paddingTop: 10,
218 | paddingBottom: 10,
219 | paddingHorizontal: 10,
220 | ...Platform.select({
221 | ios: {
222 | shadowColor: 'black',
223 | shadowOffset: { width: 0, height: -3 },
224 | shadowOpacity: 0.1,
225 | shadowRadius: 3,
226 | },
227 | android: {
228 | elevation: 5,
229 | },
230 | }),
231 | },
232 | displayNameContainer: {
233 | flex: 1,
234 | flexDirection: 'row',
235 | //justifyContent: 'flex-start',
236 | justifyContent: 'center',
237 | alignItems: 'center',
238 | textAlignVertical: 'center',
239 | },
240 | displayNameText: {
241 | fontSize: 16,
242 | color: 'black',
243 | },
244 | divider: {
245 | backgroundColor: 'black',
246 | height: 1,
247 | },
248 | intensityContainer: {
249 | paddingHorizontal: 10,
250 | },
251 | intensityButton: {
252 | backgroundColor: 'white',
253 | borderRadius: 10,
254 | paddingHorizontal: 10,
255 | justifyContent: 'center',
256 | alignItems: 'center',
257 | ...Platform.select({
258 | ios: {
259 | shadowColor: 'black',
260 | shadowOffset: { width: 0, height: -3 },
261 | shadowOpacity: 0.1,
262 | shadowRadius: 3,
263 | },
264 | android: {
265 | elevation: 5,
266 | },
267 | }),
268 | },
269 | intensityTextContainer: {
270 | flex: 1,
271 | justifyContent: 'center',
272 | alignItems: 'center',
273 | textAlignVertical: 'center',
274 | },
275 | intensityText: {
276 | fontSize: 14,
277 | color: 'rgba(70, 70, 70, 1)',
278 | paddingVertical: 10,
279 | textAlignVertical: 'center',
280 | },
281 | buttonContainer: {
282 | paddingHorizontal: 15,
283 | marginTop: 15,
284 | },
285 | buttonShape: {
286 | borderRadius: 10,
287 | marginTop: 10,
288 | marginBottom: 10,
289 | },
290 | divider: {
291 | backgroundColor: 'black',
292 | height: 1,
293 | },
294 | });
295 |
296 | const darkstyles = styleSheetFactory((theme) => ({
297 | container: {
298 | flex: 1,
299 | backgroundColor: theme.backgroundColor,
300 | marginHorizontal: 4,
301 | },
302 | textContainer: {
303 | marginTop: 8,
304 | marginBottom: 20,
305 | },
306 | saveButtonContainer: {
307 | marginTop: 8,
308 | marginBottom: 20,
309 | marginLeft: WIDTH - 130,
310 | width: 100,
311 | },
312 | deleteButtonContainer: {
313 | marginTop: 8,
314 | marginBottom: 20,
315 | width: 100,
316 | },
317 | headerText: {
318 | fontSize: 18,
319 | color: theme.textColor,
320 | fontWeight: 'normal',
321 | marginTop: 8,
322 | },
323 | inputContainer: {
324 | flexDirection: 'row',
325 | justifyContent: 'space-between',
326 | alignItems: 'center',
327 | marginVertical: 10,
328 | width: WIDTH - 55,
329 | height: 45,
330 | borderRadius: 25,
331 | fontSize: 16,
332 | marginTop: 2,
333 | paddingLeft: 12,
334 | paddingRight: 5,
335 | backgroundColor: 'rgba(204, 0, 0, 0.7)',
336 | },
337 | }));
338 |
--------------------------------------------------------------------------------
/screens/TodaysNoteScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Platform,
4 | ScrollView,
5 | StyleSheet,
6 | Text,
7 | TextInput,
8 | View,
9 | Button,
10 | } from 'react-native';
11 | import moment from 'moment';
12 | import { SymptomsFeelings } from '../components/SymptomsFeelings';
13 | import useLog_Symptom from '../hooks/useLog_Symptom';
14 | import useLog_Feeling from '../hooks/useLog_Feeling';
15 | import useAuth from '../hooks/useAuth';
16 |
17 | const TodaysNoteScreen = (props) => {
18 | const userSettings = useAuth();
19 | let userID = userSettings.user ? userSettings.user.ID : null;
20 |
21 | let todaysDate = moment().format('YYYY-MM-DD');
22 | const { symptom } = useLog_Symptom(userID);
23 | const { feeling } = useLog_Feeling(userID);
24 |
25 | let userSymptomsHistory = symptom.map((symptom) => {
26 | return [symptom.Date, symptom.Symptom_ID, symptom.Display_Name];
27 | });
28 |
29 | let userFeelingsHistory = feeling.map((feeling) => {
30 | return [
31 | feeling.Date,
32 | feeling.Feeling_ID,
33 | feeling.Display_Name,
34 | feeling.Feeling_Intensity,
35 | ];
36 | });
37 |
38 | const findSymptomsToday = (tempStartDate) => {
39 | let symptomIDsAndDisplayNames = [];
40 | for (let i = 0; i < userSymptomsHistory.length; i++) {
41 | if (userSymptomsHistory[i][0].includes(tempStartDate)) {
42 | symptomIDsAndDisplayNames.push(userSymptomsHistory[i][2]);
43 | }
44 | }
45 |
46 | if (symptomIDsAndDisplayNames == '') {
47 | symptomIDsAndDisplayNames.push('none');
48 | }
49 | return symptomIDsAndDisplayNames;
50 | };
51 |
52 | const findSymptomIDsToday = (tempStartDate) => {
53 | let symptomIDsAndDisplayNames = [];
54 | for (let i = 0; i < userSymptomsHistory.length; i++) {
55 | if (userSymptomsHistory[i][0].includes(tempStartDate)) {
56 | symptomIDsAndDisplayNames.push(userSymptomsHistory[i][1]);
57 | }
58 | }
59 |
60 | if (symptomIDsAndDisplayNames == '') {
61 | symptomIDsAndDisplayNames.push(-1);
62 | }
63 | return symptomIDsAndDisplayNames;
64 | };
65 |
66 | const findFeelingsToday = (tempStartDate) => {
67 | let feelingIDsAndDisplayNames = [];
68 | for (let i = 0; i < userFeelingsHistory.length; i++) {
69 | if (userFeelingsHistory[i][0].includes(tempStartDate)) {
70 | feelingIDsAndDisplayNames.push(userFeelingsHistory[i][2]);
71 | }
72 | }
73 |
74 | if (feelingIDsAndDisplayNames == '') {
75 | feelingIDsAndDisplayNames.push('none');
76 | }
77 | return feelingIDsAndDisplayNames;
78 | };
79 |
80 | const findFeelingIDsToday = (tempStartDate) => {
81 | let feelingIDsAndDisplayNames = [];
82 | for (let i = 0; i < userFeelingsHistory.length; i++) {
83 | if (userFeelingsHistory[i][0].includes(tempStartDate)) {
84 | feelingIDsAndDisplayNames.push(userFeelingsHistory[i][1]);
85 | }
86 | }
87 |
88 | if (feelingIDsAndDisplayNames == '') {
89 | feelingIDsAndDisplayNames.push(-1);
90 | }
91 | return feelingIDsAndDisplayNames;
92 | };
93 |
94 | const findFeelingIntensitysToday = (tempStartDate) => {
95 | let feelingIDsAndDisplayNames = [];
96 | for (let i = 0; i < userFeelingsHistory.length; i++) {
97 | if (userFeelingsHistory[i][0].includes(tempStartDate)) {
98 | feelingIDsAndDisplayNames.push(userFeelingsHistory[i][3]);
99 | }
100 | }
101 |
102 | if (feelingIDsAndDisplayNames == '') {
103 | feelingIDsAndDisplayNames.push(-1);
104 | }
105 | return feelingIDsAndDisplayNames;
106 | };
107 |
108 | const findSymptomIntensitysToday = (tempStartDate) => {
109 | let symptomIDsAndDisplayNames = [];
110 | for (let i = 0; i < userSymptomsHistory.length; i++) {
111 | if (userSymptomsHistory[i][0].includes(tempStartDate)) {
112 | symptomIDsAndDisplayNames.push(userSymptomsHistory[i][3]);
113 | }
114 | }
115 |
116 | if (symptomIDsAndDisplayNames == '') {
117 | symptomIDsAndDisplayNames.push(-1);
118 | }
119 | return symptomIDsAndDisplayNames;
120 | };
121 |
122 | // Find symptoms from today
123 | let symptomDisplayNames = findSymptomsToday(todaysDate);
124 | let symptomIDs = findSymptomIDsToday(todaysDate);
125 | let symptomIntensitys = findSymptomIntensitysToday(todaysDate);
126 |
127 | // Find feelings from today
128 | let feelingDisplayNames = findFeelingsToday(todaysDate);
129 | let feelingIDs = findFeelingIDsToday(todaysDate);
130 | let feelingIntensitys = findFeelingIntensitysToday(todaysDate);
131 |
132 | let todaysFeelings = [];
133 | for (let i = 0; i < feelingDisplayNames.length; i++) {
134 | if (feelingIntensitys[i] == 1) {
135 | feelingIntensitys[i] = 'low';
136 | } else if (feelingIntensitys[i] == 2) {
137 | feelingIntensitys[i] = 'medium';
138 | } else {
139 | feelingIntensitys[i] = 'high';
140 | }
141 | todaysFeelings.push([feelingDisplayNames[i], feelingIntensitys[i]]);
142 | }
143 |
144 | let todaysSymptoms = [];
145 | for (let i = 0; i < symptomDisplayNames.length; i++) {
146 | if (symptomIntensitys[i] == 1) {
147 | symptomIntensitys[i] = 'low';
148 | } else if (symptomIntensitys[i] == 2) {
149 | symptomIntensitys[i] = 'medium';
150 | } else {
151 | symptomIntensitys[i] = 'high';
152 | }
153 | todaysSymptoms.push([symptomDisplayNames[i], symptomIntensitys[i]]);
154 | }
155 |
156 | state = {
157 | isFocused: false,
158 | listOfTodaysFeelings: todaysFeelings,
159 | listOfTodaysSymptoms: todaysSymptoms,
160 | };
161 | handleInputFocus = () => this.setState({ isFocused: true });
162 | handleInputBlur = () => this.setState({ isFocused: false });
163 | const { isFocused } = this.state;
164 |
165 | const { navigate } = props.navigation;
166 | return (
167 |
168 |
172 |
173 | Additional Details
174 |
181 |
190 |
191 |
192 |
193 | Symptoms
194 |
195 |
207 |
208 |
209 |
210 | {symptomDisplayNames.map((symptomDisplayNames) => {
211 | return (
212 |
216 | );
217 | })}
218 |
219 |
220 |
221 | Feelings
222 |
223 |
235 |
236 |
237 |
238 | {feelingDisplayNames.map((feelingDisplayNames) => {
239 | return (
240 |
244 | );
245 | })}
246 |
247 |
248 |
249 |
250 | );
251 | };
252 |
253 | TodaysNoteScreen.navigationOptions = {
254 | title: "Today's Note",
255 | };
256 |
257 | export default TodaysNoteScreen;
258 |
259 | const styles = StyleSheet.create({
260 | container: {
261 | flex: 1,
262 | backgroundColor: '#fff',
263 | },
264 | contentContainer: {
265 | paddingTop: 15,
266 | minHeight: '100%',
267 | },
268 | headerContainer: {
269 | marginLeft: 20,
270 | marginBottom: 20,
271 | },
272 | headerText: {
273 | fontSize: 20,
274 | color: 'rgba(70,70,70, 1)',
275 | fontWeight: 'bold',
276 | textAlign: 'left',
277 | },
278 | symptomsFeelingsContainer: {
279 | flexDirection: 'row',
280 | flexWrap: 'wrap',
281 | justifyContent: 'flex-start',
282 | },
283 | addDetContainerBlurred: {
284 | backgroundColor: 'white',
285 | borderColor: 'white',
286 | borderRadius: 10,
287 | borderWidth: 1,
288 | marginRight: 10,
289 | marginVertical: 15,
290 | padding: 10,
291 | ...Platform.select({
292 | ios: {
293 | shadowColor: 'black',
294 | shadowOffset: { width: 0, height: -3 },
295 | shadowOpacity: 0.1,
296 | shadowRadius: 3,
297 | },
298 | android: {
299 | elevation: 5,
300 | },
301 | }),
302 | },
303 | addDetContainerFocused: {
304 | backgroundColor: 'white',
305 | borderColor: 'rgb(26, 178, 255)',
306 | borderRadius: 10,
307 | borderWidth: 1,
308 | marginRight: 10,
309 | marginVertical: 15,
310 | padding: 10,
311 | ...Platform.select({
312 | ios: {
313 | shadowColor: 'black',
314 | shadowOffset: { width: 0, height: -3 },
315 | shadowOpacity: 0.1,
316 | shadowRadius: 3,
317 | },
318 | android: {
319 | elevation: 5,
320 | },
321 | }),
322 | },
323 | addDet: {
324 | textAlignVertical: 'top',
325 | justifyContent: 'flex-start',
326 | },
327 | titleContainer: {
328 | flexDirection: 'row',
329 | flexWrap: 'wrap',
330 | justifyContent: 'space-between',
331 | alignItems: 'flex-start',
332 | paddingVertical: 7,
333 | },
334 | buttonContainer: {
335 | paddingHorizontal: 15,
336 | width: 75,
337 | },
338 | buttonShape: {
339 | borderRadius: 10,
340 | marginTop: 10,
341 | marginBottom: 10,
342 | },
343 | });
344 |
--------------------------------------------------------------------------------