diff --git a/android/app/build.gradle b/android/app/build.gradle index 1e5a0de..5da5bd0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -78,9 +78,9 @@ import com.android.build.OutputFile */ project.ext.vectoricons = [ - iconFontNames: [ 'FontAwesome.ttf' ] // Name of the font files you want to copy + iconFontNames: ['FontAwesome.ttf'] ] - + apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" project.ext.react = [ diff --git a/app/components/SettingsCell.js b/app/components/SettingsCell.js index 071e554..dad73fd 100644 --- a/app/components/SettingsCell.js +++ b/app/components/SettingsCell.js @@ -10,6 +10,7 @@ const SettingsCell = ({ title, onCellPress, onSwitchValueChange, + switchValue, titleNumLines, iconName, }) => { @@ -29,7 +30,7 @@ const SettingsCell = ({ {onCellPress == null ? ( - + ) : ( )} diff --git a/app/components/TimerButton.js b/app/components/TimerButton.js new file mode 100644 index 0000000..80ed19e --- /dev/null +++ b/app/components/TimerButton.js @@ -0,0 +1,95 @@ +import React, { useState, useEffect } from 'react' +import { View, Text, StyleSheet } from 'react-native' +import config, { colors, vectorIcons } from '../config' +import { AnimatedCircularProgress } from 'react-native-circular-progress' +import { useDispatch } from 'react-redux' +import { addWashTime } from '../state/WashTimeHistory' + +const TimerButton = ({ timerStart, image, text }) => { + const dispatch = useDispatch() + const timerDefault = config.timerDefault + const [timer, setCounter] = useState(timerDefault) + const [bottomText, setBottomText] = useState(text) + const [timerStartNew, setTimerStart] = useState(timerStart) + + const timerCounter = () => setCounter(timer - 1) + + useEffect(() => { + if (timerStartNew) { + if (timer <= 0) { + setBottomText('Well Done') + setTimerStart(false) + dispatch(addWashTime({ datetime: Date.now() })) + return + } + const id = setInterval(timerCounter, 1000) + return () => { + clearInterval(id) + } + } + }, [timer]) + + const fill = timerStart ? 100 - (timer * 100) / timerDefault : 0 + + const { FontAwesome } = vectorIcons + const { white, green } = colors + const { + container, + textSeconds, + textSecondName, + viewStyle, + progressStyle, + } = styles + + return ( + + + {() => ( + + {timerStartNew ? ( + {timer} + ) : ( + + )} + + {bottomText} + + )} + + + ) +} + +export default TimerButton + +const { white, circleBackground } = colors +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + textSeconds: { fontSize: 30, color: white }, + textSecondName: { color: white, marginTop: 25, fontSize: 18 }, + viewStyle: { alignItems: 'center', justifyContent: 'center' }, + progressStyle: { + backgroundColor: circleBackground, + borderRadius: 100, + }, + circularView: { + height: 190, + width: 190, + borderRadius: 100, + backgroundColor: circleBackground, + alignItems: 'center', + justifyContent: 'center', + }, +}) diff --git a/app/components/index.js b/app/components/index.js index a3b1110..e570c22 100644 --- a/app/components/index.js +++ b/app/components/index.js @@ -1,5 +1,6 @@ -import InstructionCell from './InstructionCell' +import TimerButton from './TimerButton' import MedicBleepLogo from './MedicBleepLogo' import SettingsCell from './SettingsCell' +import InstructionCell from './InstructionCell' -export { MedicBleepLogo, SettingsCell, InstructionCell } +export { MedicBleepLogo, SettingsCell, TimerButton, InstructionCell } diff --git a/app/config/index.js b/app/config/index.js index 232c42d..b19130e 100644 --- a/app/config/index.js +++ b/app/config/index.js @@ -4,9 +4,11 @@ import production from './production' import colors from './colors' import { store, persistor } from './store' import routes from './routes' +import vectorIcons from './vectorIcons' let config = { colors, + timerDefault: 20, } if (__DEV__) { @@ -23,5 +25,5 @@ if (__DEV__) { } } -export { colors, store, persistor, routes } +export { colors, routes, store, vectorIcons, persistor } export default config diff --git a/app/config/vectorIcons.js b/app/config/vectorIcons.js new file mode 100644 index 0000000..1501bce --- /dev/null +++ b/app/config/vectorIcons.js @@ -0,0 +1,7 @@ +import FontAwesome from 'react-native-vector-icons/FontAwesome' + +const vectorIcons = { + FontAwesome, +} + +export default vectorIcons diff --git a/app/navigations/MainNavigation.js b/app/navigations/MainNavigation.js index 4ec0817..cfbd287 100644 --- a/app/navigations/MainNavigation.js +++ b/app/navigations/MainNavigation.js @@ -1,6 +1,6 @@ import { createStackNavigator } from '@react-navigation/stack' import React from 'react' -import { Settings, TimerScreen, HomeScreen } from '../screens' +import { HomeScreen, SettingsScreen, TimerScreen } from '../screens' import { routes, colors } from '../config' const { SETTINGS_SCREEN, TIMER_SCREEN, HOME_SCREEN } = routes @@ -28,7 +28,7 @@ const MainNavigation = () => ( /> { + const { FontAwesome } = vectorIcons + const { white } = colors + const { settingButtonStyle } = styles + navigation.setOptions({ + headerRight: () => ( + + {}}> + + + + ), + }) + const dispatch = useDispatch() const started = useSelector(selectShiftStarted) const washTimes = useSelector(getWashTimes) @@ -54,7 +71,7 @@ export const HomeScreen = ({ navigation }) => { style={washButtonViewStyle} onPress={washButtonClicked} > - + HISTORY @@ -79,6 +96,7 @@ export default HomeScreen const styles = StyleSheet.create({ mainViewStyle: { flex: 1, + backgroundColor: colors.white, }, washButtonBgViewStyle: { marginTop: 25, @@ -109,4 +127,5 @@ const styles = StyleSheet.create({ marginTop: 15, color: colors.white, }, + settingButtonStyle: { marginRight: 15 }, }) diff --git a/app/screens/Settings.js b/app/screens/SettingsScreen.js similarity index 55% rename from app/screens/Settings.js rename to app/screens/SettingsScreen.js index 311ede5..ff11bb0 100644 --- a/app/screens/Settings.js +++ b/app/screens/SettingsScreen.js @@ -1,16 +1,28 @@ import React from 'react' -import { View, StyleSheet, SafeAreaView } from 'react-native' +import { View, StyleSheet, SafeAreaView, Linking } from 'react-native' import MedicBleepLogo from '../components/MedicBleepLogo' import { SettingsCell } from '../components' import { colors } from '../config' +import { storeHistory, shiftReminders } from '../state/Settings' +import { useSelector, useDispatch } from 'react-redux' +import { selectStoreHistory, selectShiftReminders } from 'app/state/Settings' const { white } = colors -const Settings = ({ started }) => { - const storeHistoryValueChanged = (value) => {} - const shiftReminderValueChanged = (value) => {} - const aboutMbCellPressed = () => {} - const followCellPressed = () => {} +const medicBleepAboutUsUrl = 'https://www.medicbleep.com/about.html' +const medicBleepFollowTwitterUrl = 'https://twitter.com/MedicBleep' + +const SettingsScreen = ({}) => { + const dispatch = useDispatch() + const isStoreHistory = useSelector(selectStoreHistory) + const isShiftRemindersAllow = useSelector(selectShiftReminders) + + const storeHistoryValueChanged = (value) => + isStoreHistory !== value ? dispatch(storeHistory(value)) : null + const shiftReminderValueChanged = (value) => + isShiftRemindersAllow !== value ? dispatch(shiftReminders(value)) : null + const aboutMbCellPressed = () => Linking.openURL(medicBleepAboutUsUrl) + const followCellPressed = () => Linking.openURL(medicBleepFollowTwitterUrl) const { container, contentWrapper } = styles return ( @@ -20,6 +32,7 @@ const Settings = ({ started }) => { title={'Store History'} onCellPress={null} onSwitchValueChange={storeHistoryValueChanged} + switchValue={isStoreHistory} titleNumLines={1} iconName={null} /> @@ -27,6 +40,7 @@ const Settings = ({ started }) => { title={'Two Hour On Shift Reminders'} onCellPress={null} onSwitchValueChange={shiftReminderValueChanged} + switchValue={isShiftRemindersAllow} titleNumLines={2} iconName={null} /> @@ -50,7 +64,7 @@ const Settings = ({ started }) => { ) } -export default Settings +export default SettingsScreen const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: white }, diff --git a/app/screens/TimerScreen.js b/app/screens/TimerScreen.js index e3deaf5..1227af1 100644 --- a/app/screens/TimerScreen.js +++ b/app/screens/TimerScreen.js @@ -1,6 +1,7 @@ import React from 'react' import { View, SafeAreaView, StyleSheet, FlatList } from 'react-native' -import { InstructionCell } from '../components' +import { InstructionCell, TimerButton } from '../components' +import { colors } from '../config' const TimerScreen = ({ navigation }) => { const data = [ @@ -43,6 +44,7 @@ const TimerScreen = ({ navigation }) => { return ( + { ) } - export default TimerScreen +const { white } = colors const styles = StyleSheet.create({ container: { flex: 1, + backgroundColor: white, }, flatListStyle: { flex: 1, diff --git a/app/screens/index.js b/app/screens/index.js index b1e78a9..e179910 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -1,5 +1,5 @@ import TimerScreen from './TimerScreen' import HomeScreen from './HomeScreen' -import Settings from './Settings' +import SettingsScreen from './SettingsScreen' -export { HomeScreen, Settings, TimerScreen } +export { HomeScreen, SettingsScreen, TimerScreen } diff --git a/app/state/Settings.js b/app/state/Settings.js new file mode 100644 index 0000000..80f5e97 --- /dev/null +++ b/app/state/Settings.js @@ -0,0 +1,37 @@ +const initialState = { + isToStoreHistory: false, + isToAllowShiftReminders: false, +} + +// Types +export const STORE_HISTORY = 'store_history' +export const SHIFT_REMINDERS = 'shift_reminders' + +// Reducer +export default (state = initialState, action = {}) => { + switch (action.type) { + case STORE_HISTORY: + return { ...state, isToStoreHistory: action.payload } + case SHIFT_REMINDERS: + return { ...state, isToAllowShiftReminders: action.payload } + default: + return state + } +} + +// Action creators +export const storeHistory = (isStoreHistory) => ({ + type: STORE_HISTORY, + payload: isStoreHistory, +}) + +export const shiftReminders = (isShiftRemindersAllowed) => ({ + type: SHIFT_REMINDERS, + payload: isShiftRemindersAllowed, +}) + +// Selectors +export const selectStoreHistory = (state) => + state.settingsReducer.isToStoreHistory +export const selectShiftReminders = (state) => + state.settingsReducer.isToAllowShiftReminders diff --git a/app/state/index.js b/app/state/index.js index 6dee145..1f815a9 100644 --- a/app/state/index.js +++ b/app/state/index.js @@ -1,9 +1,11 @@ import { combineReducers } from 'redux' import Shift from './Shift' import WashTimeHistory from './WashTimeHistory' +import Settings from './Settings' const rootReducer = combineReducers({ shiftReducer: Shift, washTimeHistory: WashTimeHistory, + settingsReducer: Settings, }) export default rootReducer diff --git a/ios/HandWashTimer.xcodeproj/project.pbxproj b/ios/HandWashTimer.xcodeproj/project.pbxproj index 92d21cf..e5e1313 100644 --- a/ios/HandWashTimer.xcodeproj/project.pbxproj +++ b/ios/HandWashTimer.xcodeproj/project.pbxproj @@ -165,6 +165,7 @@ 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, CC978C4B58B4BDE357267E06 /* Pods */, + C0E11174777C47A7812291F7 /* Resources */, ); indentWidth = 2; sourceTree = ""; @@ -197,6 +198,14 @@ path = Pods; sourceTree = ""; }; + C0E11174777C47A7812291F7 /* Resources */ = { + isa = "PBXGroup"; + children = ( + ); + name = Resources; + sourceTree = ""; + path = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -552,6 +561,54 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + CB3F8E685B769FA20E94370F /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HandWashTimer/Pods-HandWashTimer-resources.sh", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HandWashTimer/Pods-HandWashTimer-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; DF228B8E9BFEB12E7C8E7828 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/ios/HandWashTimer/AppDelegate.m b/ios/HandWashTimer/AppDelegate.m index 94b0575..866f806 100644 --- a/ios/HandWashTimer/AppDelegate.m +++ b/ios/HandWashTimer/AppDelegate.m @@ -3,6 +3,7 @@ #import #import #import +#import #if DEBUG #import @@ -55,4 +56,11 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge #endif } +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + options:(NSDictionary *)options +{ + return [RCTLinkingManager application:application openURL:url options:options]; +} + @end diff --git a/ios/HandWashTimer/Info.plist b/ios/HandWashTimer/Info.plist index 01f31f8..5fc3adb 100644 --- a/ios/HandWashTimer/Info.plist +++ b/ios/HandWashTimer/Info.plist @@ -38,7 +38,7 @@ NSLocationWhenInUseUsageDescription - + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -57,5 +57,9 @@ UIViewControllerBasedStatusBarAppearance + UIAppFonts + + FontAwesome.ttf + diff --git a/ios/Podfile b/ios/Podfile index 4d12347..9fc90f0 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -78,6 +78,7 @@ target 'HandWashTimer' do pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons' + target 'HandWashTimerTests' do inherit! :complete diff --git a/package.json b/package.json index 61e5518..d0bcec1 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,12 @@ "moment": "^2.24.0", "react": "16.11.0", "react-native": "0.62.2", + "react-native-circular-progress": "^1.3.6", "react-native-gesture-handler": "^1.6.1", "react-native-reanimated": "^1.8.0", "react-native-safe-area-context": "^0.7.3", "react-native-screens": "^2.7.0", + "react-native-svg": "^12.1.0", "react-native-vector-icons": "^6.6.0", "react-redux": "^7.2.0", "reactotron-react-native": "^5.0.0",