@hedia/recommendation-screen 2.1.47 → 2.1.48-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/RecommendationScreen.d.ts +8 -0
- package/dist/src/RecommendationScreen.js +42 -39
- package/dist/src/assets/fonts/Poppins-Bold.ttf +0 -0
- package/dist/src/assets/fonts/Poppins-SemiBold.ttf +0 -0
- package/dist/src/assets/icons/Edit.d.ts +4 -0
- package/dist/src/assets/icons/Edit.js +7 -0
- package/dist/src/assets/icons/Edit.tsx +22 -0
- package/dist/src/assets/icons/X.d.ts +4 -0
- package/dist/src/assets/icons/X.js +6 -0
- package/dist/src/assets/icons/X.tsx +14 -0
- package/dist/src/components/ForecastInfoBar.d.ts +117 -0
- package/dist/src/components/ForecastInfoBar.js +150 -0
- package/dist/src/components/Header.d.ts +3 -5
- package/dist/src/components/Header.js +38 -59
- package/dist/src/components/InfoBars.d.ts +3 -2
- package/dist/src/components/InfoBars.js +10 -8
- package/dist/src/components/RecentInsulin.d.ts +1 -1
- package/dist/src/components/RecentInsulin.js +34 -54
- package/dist/src/components/RecommendationModal.d.ts +1 -2
- package/dist/src/components/RecommendationModal.js +15 -10
- package/dist/src/components/RecommendedCarbs.js +21 -25
- package/dist/src/components/RecommendedInsulin.d.ts +2 -0
- package/dist/src/components/RecommendedInsulin.js +51 -41
- package/dist/src/components/Remeasure.js +4 -3
- package/dist/src/components/{TransferToLogbook.d.ts → SaveButton.d.ts} +1 -1
- package/dist/src/components/SaveButton.js +71 -0
- package/dist/src/components/TwoOptionModal.d.ts +1 -0
- package/dist/src/components/TwoOptionModal.js +10 -9
- package/dist/src/components/mood/Emotion.js +3 -1
- package/dist/src/components/mood/MoodIcon.js +3 -3
- package/dist/src/locale/da/messages.js +1 -1
- package/dist/src/locale/da/messages.po +81 -65
- package/dist/src/locale/de/messages.js +1 -1
- package/dist/src/locale/de/messages.po +81 -65
- package/dist/src/locale/en/messages.js +1 -1
- package/dist/src/locale/en/messages.po +81 -65
- package/dist/src/locale/es/messages.js +1 -1
- package/dist/src/locale/es/messages.po +81 -65
- package/dist/src/locale/fr/messages.js +1 -1
- package/dist/src/locale/fr/messages.po +81 -65
- package/dist/src/locale/it/messages.js +1 -1
- package/dist/src/locale/it/messages.po +81 -65
- package/dist/src/types/enum.d.ts +6 -3
- package/dist/src/types/enum.js +3 -0
- package/dist/src/utils/Constants.d.ts +12 -6
- package/dist/src/utils/Constants.js +12 -6
- package/package.json +5 -3
- package/dist/src/components/TransferToLogbook.js +0 -92
|
@@ -29,6 +29,14 @@ export interface IRecommendationProps {
|
|
|
29
29
|
latestLogbookFrom6Hours: Logbook.Types.ILogbookEntry | null;
|
|
30
30
|
/** The properties of the entered activity that are not used for making the calculation itself, but will be used on the recommendation screen. */
|
|
31
31
|
activityDisplayProps: IActivityDisplayProps | null;
|
|
32
|
+
/** The forecasted glucose value to display. */
|
|
33
|
+
forecastedGlucose: number | null;
|
|
34
|
+
/** Forecasted time value to display. */
|
|
35
|
+
forecastTime: string | null;
|
|
36
|
+
/** Latest CGM value to display. */
|
|
37
|
+
latestCGMReading: number | null;
|
|
38
|
+
/** Check if the forecasted glucose value is in very low range. */
|
|
39
|
+
isForecastedGlucoseVeryLow: boolean;
|
|
32
40
|
/**
|
|
33
41
|
* Callback function taking a single boolean argument and returning nothing.
|
|
34
42
|
* To be called when the user decides on a presented carbohydrate recommendation.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { AppState, BackHandler, LogBox, StyleSheet, View } from "react-native";
|
|
2
|
+
import { AppState, BackHandler, Dimensions, LogBox, ScrollView, StatusBar, StyleSheet, View, } from "react-native";
|
|
3
3
|
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
|
4
|
-
import {
|
|
4
|
+
import { colors } from "./utils/Constants";
|
|
5
|
+
import ForecastInfoBar from "./components/ForecastInfoBar";
|
|
5
6
|
import Header from "./components/Header";
|
|
6
7
|
import InfoBars, { infoStyles } from "./components/InfoBars";
|
|
7
8
|
import RecentInsulin from "./components/RecentInsulin";
|
|
@@ -12,7 +13,7 @@ import * as Calculator from "@hedia/recommendation-calculator";
|
|
|
12
13
|
import { Milliseconds, } from "@hedia/types";
|
|
13
14
|
import { I18nProvider } from "@lingui/react";
|
|
14
15
|
import Emotion from "./components/mood/Emotion";
|
|
15
|
-
import
|
|
16
|
+
import SaveButton from "./components/SaveButton";
|
|
16
17
|
import { t } from "@lingui/macro";
|
|
17
18
|
import Activity from "./components/activity/Activity";
|
|
18
19
|
import LimitationMessage from "./components/LimitationMessage";
|
|
@@ -284,7 +285,7 @@ export default class RecommendationScreen extends React.Component {
|
|
|
284
285
|
handleNoRecentInsulin = () => {
|
|
285
286
|
this.setState({ isRecommendationDisplayed: true }, () => {
|
|
286
287
|
setTimeout(() => {
|
|
287
|
-
this.scrollView?.scrollToEnd(true);
|
|
288
|
+
this.scrollView?.scrollToEnd({ animated: true });
|
|
288
289
|
}, 0);
|
|
289
290
|
});
|
|
290
291
|
};
|
|
@@ -453,56 +454,58 @@ export default class RecommendationScreen extends React.Component {
|
|
|
453
454
|
if (this.hasError) {
|
|
454
455
|
return <View style={containerStyles.container}/>;
|
|
455
456
|
}
|
|
456
|
-
const { activeInsulin,
|
|
457
|
-
const { BloodGlucose: BGValueTestID, BloodKetone: BKValueTestID, ActiveInsulin: ActiveInsulinValueTestID, } = InfoBarTestIds.Value;
|
|
458
|
-
const { ActiveInsulin: ActiveInsulinUnitTestID, BloodGlucose: BGUnitTestID, BloodKetone: BKUnitTestID, } = InfoBarTestIds.Unit;
|
|
457
|
+
const { activeInsulin, activityReduction, carbRecommendation: carbRecommendationProp, enteredCarbs, enteredInsulin, insulinRecommendation, isRecommendationDisplayed, recommendationModal, remeasureTime, selectedMood, showExitModal, showLimitationMessage, showTimeoutModal, } = this.state;
|
|
458
|
+
const { BloodGlucose: BGValueTestID, BloodKetone: BKValueTestID, ActiveInsulin: ActiveInsulinValueTestID, ForecastedGlucose: FGValueTestID, } = InfoBarTestIds.Value;
|
|
459
|
+
const { ActiveInsulin: ActiveInsulinUnitTestID, BloodGlucose: BGUnitTestID, BloodKetone: BKUnitTestID, ForecastedGlucose: FGUnitTestID, } = InfoBarTestIds.Unit;
|
|
459
460
|
const carbRecommendation = carbRecommendationProp !== null ? Math.round(carbRecommendationProp) : null;
|
|
460
461
|
const recommendedCarbs = enteredCarbs ?? carbRecommendation;
|
|
461
|
-
const
|
|
462
|
-
const {
|
|
462
|
+
const isModalOpen = showExitModal || showLimitationMessage || showTimeoutModal;
|
|
463
|
+
const { activityDisplayProps, bloodGlucoseUnit, bloodKetoneUnit, calculatorParams, currentBKL, injectionMethod, language, onRecentInsulinYes, forecastedGlucose, forecastTime, latestCGMReading, isForecastedGlucoseVeryLow, } = this.props;
|
|
464
|
+
const { activity, currentBGL, carbohydrates: providedCarbs } = calculatorParams;
|
|
463
465
|
const displayedBGL = currentBGL ? Utils.displayedBGLValue(currentBGL, bloodGlucoseUnit) : null;
|
|
464
466
|
const displayedBKL = currentBKL !== null && bloodKetoneUnit ? Utils.displayedBKLValue(currentBKL, bloodKetoneUnit) : null;
|
|
465
|
-
return (<I18nProvider language={
|
|
466
|
-
<KeyboardAwareScrollView
|
|
467
|
+
return (<I18nProvider language={language} i18n={i18n}>
|
|
468
|
+
<KeyboardAwareScrollView scrollEnabled={!isModalOpen} style={containerStyles.container}>
|
|
469
|
+
<StatusBar backgroundColor={colors.darkBlue}/>
|
|
470
|
+
<ScrollView testID={RecommendationScreenTestIds.RecommendationScrollView} style={containerStyles.container} ref={(view) => {
|
|
467
471
|
if (view !== null) {
|
|
468
472
|
this.scrollView = view;
|
|
469
473
|
}
|
|
470
474
|
}}>
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
borderColor: recommendedCarbs ? BORDER_COLOUR_TEAL : BORDER_COLOUR_GREY,
|
|
481
|
-
},
|
|
482
|
-
]}>
|
|
483
|
-
<RecommendedCarbs enteredCarbs={`${providedCarbs}`} changedRecommendedCarbs={this.updateCarbRecommendation} recommendedCarbs={`${recommendedCarbs}`} removeRecommendedCarbs={this.removeCarbRecommendation}/>
|
|
475
|
+
<Header exitCallback={this.onExit}/>
|
|
476
|
+
<InfoBars label={i18n._(t `Active Insulin`)} value={activeInsulin ? `${activeInsulin.toFixed(1)}` : null} unit={i18n._(t `units`)} showNullAsDash={false} testID={{ unitID: ActiveInsulinUnitTestID, valueID: ActiveInsulinValueTestID }}/>
|
|
477
|
+
{forecastedGlucose && !isForecastedGlucoseVeryLow ? (<ForecastInfoBar label={i18n._(t `Forecasted Glucose`)} value={forecastedGlucose} unit={bloodGlucoseUnit} forecastTime={forecastTime} latestCGMReading={latestCGMReading} testID={{ unitID: FGUnitTestID, valueID: FGValueTestID }}/>) : (<InfoBars label={i18n._(t `Blood Glucose Level`)} value={displayedBGL} unit={bloodGlucoseUnit} showNullAsDash={true} testID={{ unitID: BGUnitTestID, valueID: BGValueTestID }}/>)}
|
|
478
|
+
<InfoBars label={i18n._(t `Blood Ketone Level`)} value={displayedBKL} unit={bloodKetoneUnit} showNullAsDash={true} testID={{ unitID: BKUnitTestID, valueID: BKValueTestID }}/>
|
|
479
|
+
<View style={containerStyles.calcContainer}>
|
|
480
|
+
<View style={containerStyles.calcMargin}>
|
|
481
|
+
<View style={containerStyles.calcBorder}>
|
|
482
|
+
<RecommendedCarbs enteredCarbs={`${providedCarbs}`} changedRecommendedCarbs={this.updateCarbRecommendation} recommendedCarbs={`${recommendedCarbs}`} removeRecommendedCarbs={this.removeCarbRecommendation}/>
|
|
483
|
+
</View>
|
|
484
484
|
</View>
|
|
485
485
|
</View>
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
486
|
+
{activity && activityDisplayProps && (<Activity activity={activity} activityType={activityDisplayProps.activityType} activityTitle={activityDisplayProps.activityTitle} activityReduction={activityReduction}/>)}
|
|
487
|
+
<Remeasure onSliderChange={this.updateRemeasureTime} remeasureTime={remeasureTime}/>
|
|
488
|
+
{!isRecommendationDisplayed ? (<RecentInsulin onRecentInsulinYes={onRecentInsulinYes} onRecentInsulinNo={this.handleNoRecentInsulin}/>) : (<RecommendedInsulin injectionMethod={injectionMethod} insulinRecommendation={insulinRecommendation} enteredInsulin={enteredInsulin} activityReduction={activityReduction} updateRecommendedInsulin={this.updateInsulinRecommendation} isForecastedGlucoseVeryLow={isForecastedGlucoseVeryLow}/>)}
|
|
489
|
+
<Emotion moodSelected={this.handleMoodSelected} currentMood={selectedMood}/>
|
|
490
|
+
<SaveButton visible={isRecommendationDisplayed} transfer={this.handleTransfer}/>
|
|
491
|
+
<RecommendationModal isVisible={recommendationModal} suggestedCarbohydrates={carbRecommendationProp} attentionMessage={this.getBGLevelAttentionMessage()} limitationMessage={this.getLimitationAttentionMessage()} onClickOkButton={this.hideAttentionModal} onAcceptCarbohydrates={this.acceptCarbRecommendation} onDeclineCarbohydrates={this.declineCarbRecommendation}/>
|
|
492
|
+
</ScrollView>
|
|
493
|
+
{/* Modals */}
|
|
494
|
+
{showExitModal && (<TwoOptionModal title={i18n._(t `Save data before closing?`)} message={i18n._(t `Your saved data will be used for future calculations.`)} textFirstOption={i18n._(t `Save to logbook`)} textSecondOption={i18n._(t `Close calculation`)} isCancelable={true} rowAsButtonLayout={true} firstOption={this.handleTransfer} secondOption={this.closeCalculation} onClose={this.hideExitModal}/>)}
|
|
495
|
+
{showLimitationMessage && (<View style={modalStyle.container}>
|
|
496
|
+
<LimitationMessage limitationMessage={this.getLimitationAttentionMessage()} onPressNextButton={this.hideLimitationMessage}/>
|
|
497
|
+
</View>)}
|
|
498
|
+
{showTimeoutModal &&
|
|
499
|
+
(isRecommendationDisplayed ? (<TwoOptionModal title={Messages.TimeoutPrompTitle()} message={Messages.TimeoutRecommendationVisible(this.recommendationDate)} textFirstOption={i18n._(t `Yes, save to logbook`)} textSecondOption={i18n._(t `No, return to dashboard`)} rowAsButtonLayout={false} firstOption={this.handleTransfer} secondOption={this.closeCalculation}/>) : (<TwoOptionModal title={Messages.TimeoutPrompTitle()} message={Messages.TimeoutRecommendationNotVisible()} textFirstOption={i18n._(t `Start new calculation`)} textSecondOption={i18n._(t `Return to dashboard`)} rowAsButtonLayout={false} firstOption={this.restartCalculation} secondOption={this.closeCalculation}/>))}
|
|
493
500
|
</KeyboardAwareScrollView>
|
|
494
|
-
{this.state.showExitModal ? (<TwoOptionModal title={i18n._(t `Save data before closing?`)} message={i18n._(t `Your saved data will be used for future calculations.`)} textFirstOption={i18n._(t `Save to logbook`)} textSecondOption={i18n._(t `Close calculation`)} isCancelable={true} rowAsButtonLayout={true} firstOption={this.handleTransfer} secondOption={this.closeCalculation} onClose={this.hideExitModal}/>) : null}
|
|
495
|
-
{this.state.showLimitationMessage ? (<View style={modalStyle.container}>
|
|
496
|
-
<LimitationMessage limitationMessage={this.getLimitationAttentionMessage()} onPressNextButton={this.hideLimitationMessage}/>
|
|
497
|
-
</View>) : null}
|
|
498
|
-
{this.state.showTimeoutModal ? (this.state.isRecommendationDisplayed ? (<TwoOptionModal title={Messages.TimeoutPrompTitle()} message={Messages.TimeoutRecommendationVisible(this.recommendationDate)} textFirstOption={i18n._(t `Yes, save to logbook`)} textSecondOption={i18n._(t `No, return to dashboard`)} rowAsButtonLayout={false} firstOption={this.handleTransfer} secondOption={this.closeCalculation}/>) : (<TwoOptionModal title={Messages.TimeoutPrompTitle()} message={Messages.TimeoutRecommendationNotVisible()} textFirstOption={i18n._(t `Start new calculation`)} textSecondOption={i18n._(t `Return to dashboard`)} rowAsButtonLayout={false} firstOption={this.restartCalculation} secondOption={this.closeCalculation}/>)) : null}
|
|
499
501
|
</I18nProvider>);
|
|
500
502
|
}
|
|
501
503
|
}
|
|
504
|
+
const { width } = Dimensions.get(`screen`);
|
|
502
505
|
const containerStyles = StyleSheet.create({
|
|
503
506
|
container: {
|
|
504
507
|
flex: 1,
|
|
505
|
-
backgroundColor:
|
|
508
|
+
backgroundColor: colors.darkBlue,
|
|
506
509
|
},
|
|
507
510
|
calcContainer: {
|
|
508
511
|
flex: 1,
|
|
@@ -510,10 +513,10 @@ const containerStyles = StyleSheet.create({
|
|
|
510
513
|
calcMargin: {
|
|
511
514
|
flex: 1,
|
|
512
515
|
marginBottom: 0,
|
|
513
|
-
|
|
516
|
+
marginHorizontal: width / 17,
|
|
517
|
+
marginVertical: `3%`,
|
|
514
518
|
},
|
|
515
519
|
calcBorder: {
|
|
516
520
|
...infoStyles.border,
|
|
517
|
-
borderColor: BORDER_COLOUR_TEAL,
|
|
518
521
|
},
|
|
519
522
|
});
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Path, Svg } from "react-native-svg";
|
|
3
|
+
const EditIcon = (props) => (<Svg width="17" height="17" viewBox="0 0 17 17" fill="none" {...props}>
|
|
4
|
+
<Path fillRule="evenodd" clip-rule="evenodd" d="M5.66373 11.2399L5.97672 9.34945L10.659 4.66714L12.2365 6.2446L7.55418 10.9269L5.66373 11.2399Z" fill="#fff"/>
|
|
5
|
+
<Path opacity="0.6" fillRule="evenodd" clip-rule="evenodd" d="M8.521 16.125C12.8357 16.125 16.3335 12.6272 16.3335 8.3125C16.3335 3.99778 12.8357 0.5 8.521 0.5C4.20627 0.5 0.708496 3.99778 0.708496 8.3125C0.708496 12.6272 4.20627 16.125 8.521 16.125Z" stroke="#fff" strokeWidth="2"/>
|
|
6
|
+
</Svg>);
|
|
7
|
+
export default EditIcon;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Path, Svg, SvgProps } from "react-native-svg";
|
|
3
|
+
|
|
4
|
+
const EditIcon = (props: SvgProps) => (
|
|
5
|
+
<Svg width="17" height="17" viewBox="0 0 17 17" fill="none" {...props}>
|
|
6
|
+
<Path
|
|
7
|
+
fillRule="evenodd"
|
|
8
|
+
clip-rule="evenodd"
|
|
9
|
+
d="M5.66373 11.2399L5.97672 9.34945L10.659 4.66714L12.2365 6.2446L7.55418 10.9269L5.66373 11.2399Z"
|
|
10
|
+
fill="#fff"
|
|
11
|
+
/>
|
|
12
|
+
<Path
|
|
13
|
+
opacity="0.6"
|
|
14
|
+
fillRule="evenodd"
|
|
15
|
+
clip-rule="evenodd"
|
|
16
|
+
d="M8.521 16.125C12.8357 16.125 16.3335 12.6272 16.3335 8.3125C16.3335 3.99778 12.8357 0.5 8.521 0.5C4.20627 0.5 0.708496 3.99778 0.708496 8.3125C0.708496 12.6272 4.20627 16.125 8.521 16.125Z"
|
|
17
|
+
stroke="#fff"
|
|
18
|
+
strokeWidth="2"
|
|
19
|
+
/>
|
|
20
|
+
</Svg>
|
|
21
|
+
);
|
|
22
|
+
export default EditIcon;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Path, Svg } from "react-native-svg";
|
|
3
|
+
const XIcon = (props) => (<Svg width="20" height="20" viewBox="0 0 20 20" fill="none" {...props}>
|
|
4
|
+
<Path fill-rule="evenodd" clip-rule="evenodd" d="M4.29289 4.29289C4.68342 3.90237 5.31658 3.90237 5.70711 4.29289L10 8.58579L14.2929 4.29289C14.6834 3.90237 15.3166 3.90237 15.7071 4.29289C16.0976 4.68342 16.0976 5.31658 15.7071 5.70711L11.4142 10L15.7071 14.2929C16.0976 14.6834 16.0976 15.3166 15.7071 15.7071C15.3166 16.0976 14.6834 16.0976 14.2929 15.7071L10 11.4142L5.70711 15.7071C5.31658 16.0976 4.68342 16.0976 4.29289 15.7071C3.90237 15.3166 3.90237 14.6834 4.29289 14.2929L8.58579 10L4.29289 5.70711C3.90237 5.31658 3.90237 4.68342 4.29289 4.29289Z" fill={"#8388C8"}/>
|
|
5
|
+
</Svg>);
|
|
6
|
+
export default XIcon;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Path, Svg, SvgProps } from "react-native-svg";
|
|
3
|
+
|
|
4
|
+
const XIcon = (props: SvgProps) => (
|
|
5
|
+
<Svg width="20" height="20" viewBox="0 0 20 20" fill="none" {...props}>
|
|
6
|
+
<Path
|
|
7
|
+
fill-rule="evenodd"
|
|
8
|
+
clip-rule="evenodd"
|
|
9
|
+
d="M4.29289 4.29289C4.68342 3.90237 5.31658 3.90237 5.70711 4.29289L10 8.58579L14.2929 4.29289C14.6834 3.90237 15.3166 3.90237 15.7071 4.29289C16.0976 4.68342 16.0976 5.31658 15.7071 5.70711L11.4142 10L15.7071 14.2929C16.0976 14.6834 16.0976 15.3166 15.7071 15.7071C15.3166 16.0976 14.6834 16.0976 14.2929 15.7071L10 11.4142L5.70711 15.7071C5.31658 16.0976 4.68342 16.0976 4.29289 15.7071C3.90237 15.3166 3.90237 14.6834 4.29289 14.2929L8.58579 10L4.29289 5.70711C3.90237 5.31658 3.90237 4.68342 4.29289 4.29289Z"
|
|
10
|
+
fill={"#8388C8"}
|
|
11
|
+
/>
|
|
12
|
+
</Svg>
|
|
13
|
+
);
|
|
14
|
+
export default XIcon;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { BloodGlucoseUnit } from "@hedia/types";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Testing } from "../types/enum";
|
|
4
|
+
export interface IProps {
|
|
5
|
+
/** The display label that describes the value to the user. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** The value to display. */
|
|
8
|
+
value: number;
|
|
9
|
+
/** Unit label to print after the value. */
|
|
10
|
+
unit: BloodGlucoseUnit;
|
|
11
|
+
/** Forecast time value to display. */
|
|
12
|
+
forecastTime: string | null;
|
|
13
|
+
/** Latest CGM value to display. */
|
|
14
|
+
latestCGMReading: number | null;
|
|
15
|
+
/** optional object that contains as properties a valueID and a unitID */
|
|
16
|
+
testID?: {
|
|
17
|
+
valueID: Testing.Id.InfoBarTestIds.Value;
|
|
18
|
+
unitID: Testing.Id.InfoBarTestIds.Unit;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** A standard panel to display some data that the user entered. */
|
|
22
|
+
export default class ForecastInfoBar extends React.Component<IProps> {
|
|
23
|
+
/**
|
|
24
|
+
* Determine the unit string to be displayed in the infobar.
|
|
25
|
+
*
|
|
26
|
+
* @returns The string to display as the unit in the infobar
|
|
27
|
+
*/
|
|
28
|
+
displayUnit: () => string;
|
|
29
|
+
/**
|
|
30
|
+
* @returns JSX element to display a card with the entered value.
|
|
31
|
+
*/
|
|
32
|
+
render(): JSX.Element;
|
|
33
|
+
}
|
|
34
|
+
/** @internal */
|
|
35
|
+
export declare const infoStyles: {
|
|
36
|
+
container: {
|
|
37
|
+
flex: number;
|
|
38
|
+
marginVertical: number;
|
|
39
|
+
marginHorizontal: number;
|
|
40
|
+
marginBottom: number;
|
|
41
|
+
};
|
|
42
|
+
gradient: {
|
|
43
|
+
padding: number;
|
|
44
|
+
borderRadius: number;
|
|
45
|
+
};
|
|
46
|
+
border: {
|
|
47
|
+
borderRadius: number;
|
|
48
|
+
backgroundColor: string;
|
|
49
|
+
paddingTop: string;
|
|
50
|
+
paddingBottom: number;
|
|
51
|
+
paddingHorizontal: string;
|
|
52
|
+
};
|
|
53
|
+
row: {
|
|
54
|
+
flexDirection: "row";
|
|
55
|
+
};
|
|
56
|
+
labelContainer: {
|
|
57
|
+
flex: number;
|
|
58
|
+
justifyContent: "center";
|
|
59
|
+
};
|
|
60
|
+
label: {
|
|
61
|
+
color: string;
|
|
62
|
+
fontSize: number;
|
|
63
|
+
};
|
|
64
|
+
valueUnitContainer: {
|
|
65
|
+
flex: number;
|
|
66
|
+
justifyContent: "flex-end";
|
|
67
|
+
flexDirection: "row";
|
|
68
|
+
};
|
|
69
|
+
valueContainer: {
|
|
70
|
+
flex: number;
|
|
71
|
+
justifyContent: "flex-end";
|
|
72
|
+
};
|
|
73
|
+
value: {
|
|
74
|
+
color: string;
|
|
75
|
+
fontSize: number;
|
|
76
|
+
textAlign: "right";
|
|
77
|
+
};
|
|
78
|
+
unitContainer: {
|
|
79
|
+
flex: number;
|
|
80
|
+
justifyContent: "flex-end";
|
|
81
|
+
};
|
|
82
|
+
units: {
|
|
83
|
+
color: string;
|
|
84
|
+
fontSize: number;
|
|
85
|
+
textAlign: "left";
|
|
86
|
+
paddingLeft: string;
|
|
87
|
+
marginBottom: number;
|
|
88
|
+
};
|
|
89
|
+
forecastDot: {
|
|
90
|
+
height: number;
|
|
91
|
+
width: number;
|
|
92
|
+
borderRadius: number;
|
|
93
|
+
borderWidth: number;
|
|
94
|
+
borderColor: string;
|
|
95
|
+
};
|
|
96
|
+
forecastSubtitle: {
|
|
97
|
+
flexDirection: "row";
|
|
98
|
+
alignItems: "center";
|
|
99
|
+
paddingTop: number;
|
|
100
|
+
};
|
|
101
|
+
forecastText: {
|
|
102
|
+
color: string;
|
|
103
|
+
marginLeft: number;
|
|
104
|
+
};
|
|
105
|
+
forecastTime: {
|
|
106
|
+
color: string;
|
|
107
|
+
fontSize: number;
|
|
108
|
+
lineHeight: number;
|
|
109
|
+
marginLeft: number;
|
|
110
|
+
};
|
|
111
|
+
currentCGM: {
|
|
112
|
+
fontSize: number;
|
|
113
|
+
lineHeight: number;
|
|
114
|
+
color: string;
|
|
115
|
+
marginBottom: number;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { t } from "@lingui/macro";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Dimensions, StyleSheet, View } from "react-native";
|
|
4
|
+
import LinearGradient from "react-native-linear-gradient";
|
|
5
|
+
import { i18n } from "../locale/i18nUtils";
|
|
6
|
+
import { colors } from "../utils/Constants";
|
|
7
|
+
import { Utils } from "../utils/Utils";
|
|
8
|
+
import TextBold from "./text/TextBold";
|
|
9
|
+
import TextRegular from "./text/TextRegular";
|
|
10
|
+
/** A standard panel to display some data that the user entered. */
|
|
11
|
+
export default class ForecastInfoBar extends React.Component {
|
|
12
|
+
/**
|
|
13
|
+
* Determine the unit string to be displayed in the infobar.
|
|
14
|
+
*
|
|
15
|
+
* @returns The string to display as the unit in the infobar
|
|
16
|
+
*/
|
|
17
|
+
displayUnit = () => {
|
|
18
|
+
const { unit } = this.props;
|
|
19
|
+
return Utils.formatUnit(unit);
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* @returns JSX element to display a card with the entered value.
|
|
23
|
+
*/
|
|
24
|
+
render() {
|
|
25
|
+
const { testID, label, latestCGMReading } = this.props;
|
|
26
|
+
const { border, container, forecastDot, forecastSubtitle, forecastText, forecastTime, gradient, labelContainer, row, unitContainer, units, value, valueContainer, valueUnitContainer, } = infoStyles;
|
|
27
|
+
return (<View style={container}>
|
|
28
|
+
<LinearGradient style={gradient} colors={[`#8B38F5`, `#D593E5`]} useAngle={true} angle={170}>
|
|
29
|
+
<View style={border}>
|
|
30
|
+
<View style={row}>
|
|
31
|
+
<View style={labelContainer}>
|
|
32
|
+
<TextBold style={infoStyles.label}>{label}</TextBold>
|
|
33
|
+
</View>
|
|
34
|
+
<View style={valueUnitContainer}>
|
|
35
|
+
<View style={valueContainer}>
|
|
36
|
+
<TextBold style={value} testID={testID?.valueID}>
|
|
37
|
+
{this.props.value}
|
|
38
|
+
</TextBold>
|
|
39
|
+
</View>
|
|
40
|
+
<View style={unitContainer}>
|
|
41
|
+
<TextRegular style={units} testID={testID?.unitID}>
|
|
42
|
+
{this.displayUnit()}
|
|
43
|
+
</TextRegular>
|
|
44
|
+
</View>
|
|
45
|
+
</View>
|
|
46
|
+
</View>
|
|
47
|
+
|
|
48
|
+
<View style={row}>
|
|
49
|
+
<View style={forecastSubtitle}>
|
|
50
|
+
<LinearGradient colors={[`#8B38F5`, `#D593E5`]} useAngle={true} angle={135} style={forecastDot}/>
|
|
51
|
+
<TextRegular style={forecastText}>{i18n._(t `Forecast time:`)}</TextRegular>
|
|
52
|
+
<TextRegular style={forecastTime}>{this.props.forecastTime}</TextRegular>
|
|
53
|
+
</View>
|
|
54
|
+
</View>
|
|
55
|
+
|
|
56
|
+
<View style={row}>
|
|
57
|
+
<TextRegular style={infoStyles.currentCGM}>
|
|
58
|
+
{i18n._(t `Current glucose: ${latestCGMReading} ${this.displayUnit()}`)}
|
|
59
|
+
</TextRegular>
|
|
60
|
+
</View>
|
|
61
|
+
</View>
|
|
62
|
+
</LinearGradient>
|
|
63
|
+
</View>);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const { width } = Dimensions.get(`screen`);
|
|
67
|
+
/** @internal */
|
|
68
|
+
export const infoStyles = StyleSheet.create({
|
|
69
|
+
container: {
|
|
70
|
+
flex: 1,
|
|
71
|
+
marginVertical: width / 35,
|
|
72
|
+
marginHorizontal: width / 17,
|
|
73
|
+
marginBottom: 0,
|
|
74
|
+
},
|
|
75
|
+
gradient: {
|
|
76
|
+
padding: 2,
|
|
77
|
+
borderRadius: 5,
|
|
78
|
+
},
|
|
79
|
+
border: {
|
|
80
|
+
borderRadius: 5,
|
|
81
|
+
backgroundColor: colors.darkBlue,
|
|
82
|
+
paddingTop: `2%`,
|
|
83
|
+
paddingBottom: 0,
|
|
84
|
+
paddingHorizontal: `3%`,
|
|
85
|
+
},
|
|
86
|
+
row: {
|
|
87
|
+
flexDirection: `row`,
|
|
88
|
+
},
|
|
89
|
+
labelContainer: {
|
|
90
|
+
flex: 7,
|
|
91
|
+
justifyContent: `center`,
|
|
92
|
+
},
|
|
93
|
+
label: {
|
|
94
|
+
color: `white`,
|
|
95
|
+
fontSize: width / 22,
|
|
96
|
+
},
|
|
97
|
+
valueUnitContainer: {
|
|
98
|
+
flex: 6.5,
|
|
99
|
+
justifyContent: `flex-end`,
|
|
100
|
+
flexDirection: `row`,
|
|
101
|
+
},
|
|
102
|
+
valueContainer: {
|
|
103
|
+
flex: 5,
|
|
104
|
+
justifyContent: `flex-end`,
|
|
105
|
+
},
|
|
106
|
+
value: {
|
|
107
|
+
color: `white`,
|
|
108
|
+
fontSize: width / 15,
|
|
109
|
+
textAlign: `right`,
|
|
110
|
+
},
|
|
111
|
+
unitContainer: {
|
|
112
|
+
flex: 3,
|
|
113
|
+
justifyContent: `flex-end`,
|
|
114
|
+
},
|
|
115
|
+
units: {
|
|
116
|
+
color: `white`,
|
|
117
|
+
fontSize: width / 30,
|
|
118
|
+
textAlign: `left`,
|
|
119
|
+
paddingLeft: `15%`,
|
|
120
|
+
marginBottom: 4,
|
|
121
|
+
},
|
|
122
|
+
forecastDot: {
|
|
123
|
+
height: (width / 100) * 3,
|
|
124
|
+
width: (width / 100) * 3,
|
|
125
|
+
borderRadius: 6,
|
|
126
|
+
borderWidth: 1,
|
|
127
|
+
borderColor: colors.lightBlue,
|
|
128
|
+
},
|
|
129
|
+
forecastSubtitle: {
|
|
130
|
+
flexDirection: `row`,
|
|
131
|
+
alignItems: `center`,
|
|
132
|
+
paddingTop: 5,
|
|
133
|
+
},
|
|
134
|
+
forecastText: {
|
|
135
|
+
color: colors.lightBlue,
|
|
136
|
+
marginLeft: width / 40,
|
|
137
|
+
},
|
|
138
|
+
forecastTime: {
|
|
139
|
+
color: `white`,
|
|
140
|
+
fontSize: width / 32,
|
|
141
|
+
lineHeight: width / 28,
|
|
142
|
+
marginLeft: width / 100,
|
|
143
|
+
},
|
|
144
|
+
currentCGM: {
|
|
145
|
+
fontSize: 13,
|
|
146
|
+
lineHeight: 34,
|
|
147
|
+
color: colors.lightBlue,
|
|
148
|
+
marginBottom: 5,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="react" />
|
|
2
2
|
export interface IProps {
|
|
3
3
|
/** A callback that doesn’t take any arguments and doesn’t return a value to be called when the cancel-button is pressed. */
|
|
4
4
|
exitCallback(): void;
|
|
5
5
|
}
|
|
6
6
|
/** Component to display a header text along with a cancel-button that triggers a callback function. */
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
render(): JSX.Element;
|
|
10
|
-
}
|
|
7
|
+
declare const Header: ({ exitCallback }: IProps) => JSX.Element;
|
|
8
|
+
export default Header;
|
|
@@ -1,75 +1,54 @@
|
|
|
1
1
|
import { t } from "@lingui/macro";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Dimensions, SafeAreaView, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
4
|
+
import XIcon from "../assets/icons/X";
|
|
4
5
|
import { i18n } from "../locale/i18nUtils";
|
|
5
6
|
import { Testing } from "../types/enum";
|
|
6
|
-
import { BACKGROUND_COLOUR_PURPLE, BORDER_COLOUR_GREY } from "../utils/Constants";
|
|
7
|
-
import Icon from "./Icon";
|
|
8
7
|
import TextBold from "./text/TextBold";
|
|
9
8
|
/** Component to display a header text along with a cancel-button that triggers a callback function. */
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</TextBold>
|
|
29
|
-
</View>
|
|
30
|
-
<View style={headerStyles.headerFiller}/>
|
|
31
|
-
</View>
|
|
32
|
-
</View>
|
|
33
|
-
</React.Fragment>);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
9
|
+
const Header = ({ exitCallback }) => {
|
|
10
|
+
const { iconWrapper, container, emptyButton, text } = headerStyles;
|
|
11
|
+
return (<>
|
|
12
|
+
<SafeAreaView />
|
|
13
|
+
<View style={container}>
|
|
14
|
+
<TouchableOpacity testID={Testing.Id.HeaderTestIds.ExitCalculation} style={iconWrapper} onPress={exitCallback}>
|
|
15
|
+
<XIcon />
|
|
16
|
+
</TouchableOpacity>
|
|
17
|
+
|
|
18
|
+
<TextBold style={text} testID={Testing.Id.HeaderTestIds.HeaderText}>
|
|
19
|
+
{i18n._(t `Insulin recommendation`)}
|
|
20
|
+
</TextBold>
|
|
21
|
+
|
|
22
|
+
<View style={emptyButton}/>
|
|
23
|
+
</View>
|
|
24
|
+
</>);
|
|
25
|
+
};
|
|
26
|
+
const { width } = Dimensions.get(`screen`);
|
|
36
27
|
const headerStyles = StyleSheet.create({
|
|
37
|
-
|
|
38
|
-
backgroundColor: BACKGROUND_COLOUR_PURPLE,
|
|
39
|
-
},
|
|
40
|
-
margin: {
|
|
41
|
-
margin: `1%`,
|
|
42
|
-
marginBottom: `3%`,
|
|
43
|
-
flexDirection: `row`,
|
|
44
|
-
flex: 1,
|
|
45
|
-
},
|
|
46
|
-
headerContainer: {
|
|
28
|
+
container: {
|
|
47
29
|
flexDirection: `row`,
|
|
48
30
|
flex: 1,
|
|
49
|
-
|
|
50
|
-
|
|
31
|
+
paddingHorizontal: width / 17,
|
|
32
|
+
paddingVertical: width / 43,
|
|
51
33
|
alignItems: `center`,
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
alignItems: `center`,
|
|
65
|
-
marginTop: `5%`,
|
|
66
|
-
},
|
|
67
|
-
headerText: {
|
|
68
|
-
fontSize: Dimensions.get(`screen`).width / 18,
|
|
34
|
+
justifyContent: `space-between`,
|
|
35
|
+
},
|
|
36
|
+
iconWrapper: {
|
|
37
|
+
padding: width / 60,
|
|
38
|
+
borderWidth: 1,
|
|
39
|
+
borderColor: `#C8CCFA33`,
|
|
40
|
+
borderRadius: 10,
|
|
41
|
+
},
|
|
42
|
+
text: {
|
|
43
|
+
fontFamily: `Poppins-SemiBold`,
|
|
44
|
+
fontSize: width / 22,
|
|
45
|
+
lineHeight: width / 14,
|
|
69
46
|
textAlign: `center`,
|
|
70
47
|
color: `white`,
|
|
71
48
|
},
|
|
72
|
-
|
|
73
|
-
|
|
49
|
+
emptyButton: {
|
|
50
|
+
width: width / 11,
|
|
51
|
+
height: width / 11,
|
|
74
52
|
},
|
|
75
53
|
});
|
|
54
|
+
export default Header;
|
|
@@ -50,7 +50,8 @@ export declare const infoStyles: {
|
|
|
50
50
|
};
|
|
51
51
|
margin: {
|
|
52
52
|
flex: number;
|
|
53
|
-
|
|
53
|
+
marginVertical: number;
|
|
54
|
+
marginHorizontal: number;
|
|
54
55
|
marginBottom: number;
|
|
55
56
|
};
|
|
56
57
|
border: {
|
|
@@ -90,12 +91,12 @@ export declare const infoStyles: {
|
|
|
90
91
|
unitContainer: {
|
|
91
92
|
flex: number;
|
|
92
93
|
justifyContent: "flex-end";
|
|
93
|
-
paddingBottom: string;
|
|
94
94
|
};
|
|
95
95
|
units: {
|
|
96
96
|
color: string;
|
|
97
97
|
fontSize: number;
|
|
98
98
|
textAlign: "left";
|
|
99
99
|
paddingLeft: string;
|
|
100
|
+
marginBottom: number;
|
|
100
101
|
};
|
|
101
102
|
};
|