diff --git a/package.cordovabuild.json b/package.cordovabuild.json index b3b85fb6a..5018b3b05 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -98,8 +98,7 @@ }, "cordova-plugin-bluetooth-classic-serial-port": {}, "cordova-custom-config": {}, - "com.unarin.cordova.beacon": {}, - "cordova-plugin-statusbar": {} + "com.unarin.cordova.beacon": {} } }, "dependencies": { @@ -137,7 +136,6 @@ "cordova-plugin-bluetooth-classic-serial-port": "git+https://github.com/e-mission/cordova-plugin-bluetooth-classic-serial-port.git", "cordova-custom-config": "^5.1.1", "com.unarin.cordova.beacon": "github:e-mission/cordova-plugin-ibeacon", - "cordova-plugin-statusbar": "^4.0.0", "core-js": "^2.5.7", "e-mission-common": "github:JGreenlee/e-mission-common#semver:0.6.2", "enketo-core": "^6.1.7", diff --git a/setup/setup_native.sh b/setup/setup_native.sh index a7c396ab2..05624a693 100644 --- a/setup/setup_native.sh +++ b/setup/setup_native.sh @@ -121,7 +121,7 @@ sed -i -e "s|/usr/bin/env node|/usr/bin/env node --unhandled-rejections=strict|" npx cordova prepare$PLATFORMS -EXPECTED_COUNT=26 +EXPECTED_COUNT=25 INSTALLED_COUNT=`npx cordova plugin list | wc -l` echo "Found $INSTALLED_COUNT plugins, expected $EXPECTED_COUNT" if [ $INSTALLED_COUNT -lt $EXPECTED_COUNT ]; diff --git a/www/__tests__/util.ts b/www/__tests__/datetimeUtil.test.ts similarity index 94% rename from www/__tests__/util.ts rename to www/__tests__/datetimeUtil.test.ts index 0a8678a34..6d3fab6f2 100644 --- a/www/__tests__/util.ts +++ b/www/__tests__/datetimeUtil.test.ts @@ -1,4 +1,4 @@ -import { formatForDisplay } from '../js/util'; +import { formatForDisplay } from '../js/datetimeUtil'; describe('util.ts', () => { describe('formatForDisplay', () => { diff --git a/www/index.js b/www/index.js index 4ea9e93ac..3b7c86a0b 100644 --- a/www/index.js +++ b/www/index.js @@ -24,7 +24,7 @@ window.skipLocalNotificationReady = true; deviceReady.then(() => { logDebug('deviceReady'); // On init, use 'default' status bar (black text) - window['StatusBar']?.styleDefault(); + // window['StatusBar']?.styleDefault(); cordova.plugin.http.setDataSerializer('json'); const rootEl = document.getElementById('appRoot'); const reactRoot = createRoot(rootEl); diff --git a/www/js/TimelineContext.ts b/www/js/TimelineContext.ts index 2a94d2b0b..9b53d2a27 100644 --- a/www/js/TimelineContext.ts +++ b/www/js/TimelineContext.ts @@ -22,7 +22,7 @@ import { getNotDeletedCandidates, mapInputsToTimelineEntries } from './survey/in import { EnketoUserInputEntry } from './survey/enketo/enketoHelper'; import { primarySectionForTrip } from './diary/diaryHelper'; import useAppStateChange from './useAppStateChange'; -import { isoDateRangeToTsRange, isoDateWithOffset } from './util'; +import { isoDateRangeToTsRange, isoDateWithOffset } from './datetimeUtil'; import { base_modes } from 'e-mission-common'; const TODAY_DATE = DateTime.now().toISODate(); diff --git a/www/js/components/Chart.tsx b/www/js/components/Chart.tsx index 8d7406b6c..97844f946 100644 --- a/www/js/components/Chart.tsx +++ b/www/js/components/Chart.tsx @@ -4,7 +4,8 @@ import { useTheme } from 'react-native-paper'; import { ChartData, Chart as ChartJS, ScriptableContext, registerables } from 'chart.js'; import { Chart as ChartJSChart } from 'react-chartjs-2'; import Annotation, { AnnotationOptions, LabelPosition } from 'chartjs-plugin-annotation'; -import { dedupColors, getChartHeight, darkenOrLighten } from './charting'; +import { getChartHeight } from './charting'; +import { base_modes } from 'e-mission-common'; import { logDebug } from '../plugin/logger'; ChartJS.register(...registerables, Annotation); @@ -64,7 +65,7 @@ const Chart = ({ let labelColorMap; // object mapping labels to colors if (getColorForLabel) { const colorEntries = chartDatasets.map((d) => [d.label, getColorForLabel(d.label)]); - labelColorMap = dedupColors(colorEntries); + labelColorMap = base_modes.dedupe_colors(colorEntries, [0.4, 1.6]); } return { datasets: chartDatasets.map((e, i) => ({ @@ -73,7 +74,7 @@ const Chart = ({ labelColorMap?.[e.label] || getColorForChartEl?.(chartRef.current, e, barCtx, 'background'), borderColor: (barCtx) => - darkenOrLighten(labelColorMap?.[e.label], -0.5) || + base_modes.scale_lightness(labelColorMap?.[e.label], 0.5) || getColorForChartEl?.(chartRef.current, e, barCtx, 'border'), borderWidth: borderWidth || 2, borderRadius: 3, diff --git a/www/js/components/charting.ts b/www/js/components/charting.ts index 657a3b8ab..91379f23b 100644 --- a/www/js/components/charting.ts +++ b/www/js/components/charting.ts @@ -59,44 +59,44 @@ function getBarHeight(stacks) { return totalHeight; } -//fill pattern creation -//https://stackoverflow.com/questions/28569667/fill-chart-js-bar-chart-with-diagonal-stripes-or-other-patterns -function createDiagonalPattern(color = 'black') { - let shape = document.createElement('canvas'); - shape.width = 10; - shape.height = 10; - let c = shape.getContext('2d') as CanvasRenderingContext2D; - c.strokeStyle = color; - c.lineWidth = 2; - c.beginPath(); - c.moveTo(2, 0); - c.lineTo(10, 8); - c.stroke(); - c.beginPath(); - c.moveTo(0, 8); - c.lineTo(2, 10); - c.stroke(); - return c.createPattern(shape, 'repeat'); -} +// //fill pattern creation +// //https://stackoverflow.com/questions/28569667/fill-chart-js-bar-chart-with-diagonal-stripes-or-other-patterns +// function createDiagonalPattern(color = 'black') { +// let shape = document.createElement('canvas'); +// shape.width = 10; +// shape.height = 10; +// let c = shape.getContext('2d') as CanvasRenderingContext2D; +// c.strokeStyle = color; +// c.lineWidth = 2; +// c.beginPath(); +// c.moveTo(2, 0); +// c.lineTo(10, 8); +// c.stroke(); +// c.beginPath(); +// c.moveTo(0, 8); +// c.lineTo(2, 10); +// c.stroke(); +// return c.createPattern(shape, 'repeat'); +// } -export function getMeteredBackgroundColor(meter, currDataset, barCtx, colors, darken = 0) { - if (!barCtx || !currDataset) return; - let bar_height = getBarHeight(barCtx.parsed._stacks); - logDebug(`bar height for ${barCtx.raw.y} is ${bar_height} which in chart is ${currDataset}`); - let meteredColor; - if (bar_height > meter.high) meteredColor = colors.danger; - else if (bar_height > meter.middle) meteredColor = colors.warn; - else meteredColor = colors.success; - if (darken) { - return color(meteredColor).darken(darken).hex(); - } - //if "unlabeled", etc -> stripes - if (currDataset.label == meter.uncertainty_prefix) { - return createDiagonalPattern(meteredColor); - } - //if :labeled", etc -> solid - return meteredColor; -} +// export function getMeteredBackgroundColor(meter, currDataset, barCtx, colors, darken = 0) { +// if (!barCtx || !currDataset) return; +// let bar_height = getBarHeight(barCtx.parsed._stacks); +// logDebug(`bar height for ${barCtx.raw.y} is ${bar_height} which in chart is ${currDataset}`); +// let meteredColor; +// if (bar_height > meter.high) meteredColor = colors.danger; +// else if (bar_height > meter.middle) meteredColor = colors.warn; +// else meteredColor = colors.success; +// if (darken) { +// return color(meteredColor).darken(darken).hex(); +// } +// //if "unlabeled", etc -> stripes +// if (currDataset.label == meter.uncertainty_prefix) { +// return createDiagonalPattern(meteredColor); +// } +// //if :labeled", etc -> solid +// return meteredColor; +// } const meterColors = { below: '#00cc95', // green oklch(75% 0.3 165) @@ -142,42 +142,3 @@ export function getGradient( } return gradient; } - -/** - * @param baseColor a color string - * @param change a number between -1 and 1, indicating the amount to darken or lighten the color - * @returns an adjusted color, either darkened or lightened, depending on the sign of change - */ -export function darkenOrLighten(baseColor: string, change: number) { - if (!baseColor) return baseColor; - let colorObj = color(baseColor); - if (change < 0) { - // darkening appears more drastic than lightening, so we will be less aggressive (scale change by .5) - return colorObj.darken(Math.abs(change * 0.5)).hex(); - } else { - return colorObj.lighten(Math.abs(change)).hex(); - } -} - -/** - * @param colors an array of colors, each of which is an array of [key, color string] - * @returns an object mapping keys to colors, with duplicates darkened/lightened to be distinguishable - */ -export function dedupColors(colors: string[][]) { - const dedupedColors = {}; - const maxAdjustment = 0.7; // more than this is too drastic and the colors approach black/white - for (const [key, clr] of colors) { - if (!clr) continue; // skip empty colors - const duplicates = colors.filter(([k, c]) => c == clr); - if (duplicates.length > 1) { - // there are duplicates; calculate an evenly-spaced adjustment for each one - duplicates.forEach(([k, c], i) => { - const change = -maxAdjustment + ((maxAdjustment * 2) / (duplicates.length - 1)) * i; - dedupedColors[k] = darkenOrLighten(clr, change); - }); - } else if (!dedupedColors[key]) { - dedupedColors[key] = clr; // not a dupe, & not already deduped, so use the color as-is - } - } - return dedupedColors; -} diff --git a/www/js/config/useImperialConfig.ts b/www/js/config/useImperialConfig.ts index 900c9854e..ba32ccec2 100644 --- a/www/js/config/useImperialConfig.ts +++ b/www/js/config/useImperialConfig.ts @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import useAppConfig from '../useAppConfig'; -import { formatForDisplay } from '../util'; +import { formatForDisplay } from '../datetimeUtil'; export type ImperialConfig = { distanceSuffix: string; diff --git a/www/js/util.ts b/www/js/datetimeUtil.ts similarity index 100% rename from www/js/util.ts rename to www/js/datetimeUtil.ts diff --git a/www/js/diary/diaryHelper.ts b/www/js/diary/diaryHelper.ts index e182835c4..b6510f288 100644 --- a/www/js/diary/diaryHelper.ts +++ b/www/js/diary/diaryHelper.ts @@ -4,7 +4,7 @@ import { LabelOptions } from '../types/labelTypes'; import { LocalDt } from '../types/serverData'; import { ImperialConfig } from '../config/useImperialConfig'; import { base_modes } from 'e-mission-common'; -import { humanizeIsoRange } from '../util'; +import { humanizeIsoRange } from '../datetimeUtil'; export type BaseModeKey = string; // TODO figure out how to get keyof typeof base_modes.BASE_MODES diff --git a/www/js/diary/list/DateSelect.tsx b/www/js/diary/list/DateSelect.tsx index 2f629b3d1..52cebe63e 100644 --- a/www/js/diary/list/DateSelect.tsx +++ b/www/js/diary/list/DateSelect.tsx @@ -19,7 +19,7 @@ import { Text, useTheme } from 'react-native-paper'; import i18next from 'i18next'; import { useTranslation } from 'react-i18next'; import { NavBarButton } from '../../components/NavBar'; -import { formatIsoNoYear, isoDateRangeToTsRange } from '../../util'; +import { formatIsoNoYear, isoDateRangeToTsRange } from '../../datetimeUtil'; // formats as e.g. 'Aug 1' const MONTH_DAY_SHORT: Intl.DateTimeFormatOptions = { diff --git a/www/js/diary/list/TimelineScrollList.tsx b/www/js/diary/list/TimelineScrollList.tsx index 98f12d139..e640b2b11 100644 --- a/www/js/diary/list/TimelineScrollList.tsx +++ b/www/js/diary/list/TimelineScrollList.tsx @@ -7,7 +7,7 @@ import { ActivityIndicator, Banner, Button, Icon, Text } from 'react-native-pape import LoadMoreButton from './LoadMoreButton'; import { useTranslation } from 'react-i18next'; import TimelineContext from '../../TimelineContext'; -import { isoDateRangeToTsRange, isoDateWithOffset } from '../../util'; +import { isoDateRangeToTsRange, isoDateWithOffset } from '../../datetimeUtil'; import { DateTime } from 'luxon'; function renderCard({ item: listEntry, index }) { diff --git a/www/js/diary/useDerivedProperties.tsx b/www/js/diary/useDerivedProperties.tsx index 4071000f7..3a46f7cca 100644 --- a/www/js/diary/useDerivedProperties.tsx +++ b/www/js/diary/useDerivedProperties.tsx @@ -7,7 +7,7 @@ import { primarySectionForTrip, } from './diaryHelper'; import TimelineContext from '../TimelineContext'; -import { formatIsoNoYear, formatIsoWeekday, humanizeIsoRange, isoDatesDifference } from '../util'; +import { formatIsoNoYear, formatIsoWeekday, humanizeIsoRange } from '../datetimeUtil'; const useDerivedProperties = (tlEntry) => { const imperialConfig = useImperialConfig(); @@ -18,7 +18,8 @@ const useDerivedProperties = (tlEntry) => { const endFmt = tlEntry.end_fmt_time || tlEntry.exit_fmt_time; const beginDt = tlEntry.start_local_dt || tlEntry.enter_local_dt; const endDt = tlEntry.end_local_dt || tlEntry.exit_local_dt; - const tlEntryIsMultiDay = isoDatesDifference(beginFmt, endFmt); + // given YYYY-MM-DDTHH:MM:SSZ strings: if YYYY-MM-DD differs, is multi-day + const tlEntryIsMultiDay = beginFmt.substring(0, 10) != endFmt.substring(0, 10); return { confirmedMode: confirmedModeFor(tlEntry), diff --git a/www/js/metrics/MetricsTab.tsx b/www/js/metrics/MetricsTab.tsx index d4fdddc35..65a0bb847 100644 --- a/www/js/metrics/MetricsTab.tsx +++ b/www/js/metrics/MetricsTab.tsx @@ -14,7 +14,7 @@ import { metrics_summaries } from 'e-mission-common'; import MetricsScreen from './MetricsScreen'; import { LabelOptions } from '../types/labelTypes'; import { useAppTheme } from '../appTheme'; -import { isoDatesDifference } from '../util'; +import { isoDatesDifference } from '../datetimeUtil'; const N_DAYS_TO_LOAD = 14; // 2 weeks export const DEFAULT_METRIC_LIST: MetricList = { diff --git a/www/js/metrics/SumaryCard.tsx b/www/js/metrics/SummaryCard.tsx similarity index 89% rename from www/js/metrics/SumaryCard.tsx rename to www/js/metrics/SummaryCard.tsx index 7f3170c5d..577f2ee97 100644 --- a/www/js/metrics/SumaryCard.tsx +++ b/www/js/metrics/SummaryCard.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { View, StyleSheet } from 'react-native'; import { Card, Text } from 'react-native-paper'; -import { formatForDisplay } from '../util'; +import { formatForDisplay } from '../datetimeUtil'; import { colors } from '../appTheme'; import { t } from 'i18next'; import { FootprintGoal } from '../types/appConfigTypes'; @@ -20,9 +20,8 @@ const SummaryCard = ({ title, unit, value, nDays, goals }: Props) => { const perDayValue = value.map((v) => v / nDays) as Value; const formatVal = (v: Value) => { - const opts = { maximumFractionDigits: 1 }; - if (valueIsRange) return `${formatForDisplay(v[0], opts)} - ${formatForDisplay(v[1], opts)}`; - return `${formatForDisplay(v[0], opts)}`; + if (valueIsRange) return `${formatForDisplay(v[0])} - ${formatForDisplay(v[1])}`; + return `${formatForDisplay(v[0])}`; }; const colorFn = (v: Value) => { diff --git a/www/js/metrics/footprint/FootprintComparisonCard.tsx b/www/js/metrics/footprint/FootprintComparisonCard.tsx index 344a94164..0dfd4bc42 100644 --- a/www/js/metrics/footprint/FootprintComparisonCard.tsx +++ b/www/js/metrics/footprint/FootprintComparisonCard.tsx @@ -5,7 +5,7 @@ import BarChart from '../../components/BarChart'; import { useTranslation } from 'react-i18next'; import { ChartRecord } from '../../components/Chart'; import TimelineContext from '../../TimelineContext'; -import { formatIsoNoYear } from '../../util'; +import { formatIsoNoYear } from '../../datetimeUtil'; const FootprintComparisonCard = ({ type, diff --git a/www/js/metrics/footprint/FootprintSection.tsx b/www/js/metrics/footprint/FootprintSection.tsx index c871f9b07..ce97c99e5 100644 --- a/www/js/metrics/footprint/FootprintSection.tsx +++ b/www/js/metrics/footprint/FootprintSection.tsx @@ -2,11 +2,11 @@ import React, { useContext, useMemo, useState } from 'react'; import { View } from 'react-native'; import { Text } from 'react-native-paper'; import color from 'color'; -import SummaryCard from '../SumaryCard'; +import SummaryCard from '../SummaryCard'; import { useTranslation } from 'react-i18next'; import { sumMetricEntries } from '../metricsHelper'; import TimelineContext from '../../TimelineContext'; -import { formatIso, isoDatesDifference } from '../../util'; +import { formatIso, isoDatesDifference } from '../../datetimeUtil'; import WeeklyFootprintCard from './WeeklyFootprintCard'; import useAppConfig from '../../useAppConfig'; import { getFootprintGoals } from './footprintHelper'; diff --git a/www/js/metrics/footprint/WeeklyFootprintCard.tsx b/www/js/metrics/footprint/WeeklyFootprintCard.tsx index 5f4968f1a..d3005362d 100644 --- a/www/js/metrics/footprint/WeeklyFootprintCard.tsx +++ b/www/js/metrics/footprint/WeeklyFootprintCard.tsx @@ -10,7 +10,7 @@ import { sumMetricEntries, trimGroupingPrefix, } from '../metricsHelper'; -import { formatIsoNoYear, isoDateWithOffset } from '../../util'; +import { formatIsoNoYear, isoDateWithOffset } from '../../datetimeUtil'; import { useTranslation } from 'react-i18next'; import BarChart from '../../components/BarChart'; import { ChartRecord } from '../../components/Chart'; diff --git a/www/js/metrics/metricsHelper.ts b/www/js/metrics/metricsHelper.ts index f0cc458c5..fc759f320 100644 --- a/www/js/metrics/metricsHelper.ts +++ b/www/js/metrics/metricsHelper.ts @@ -6,7 +6,12 @@ import { MetricName, groupingFields } from '../types/appConfigTypes'; import { ImperialConfig } from '../config/useImperialConfig'; import i18next from 'i18next'; import { base_modes, metrics_summaries } from 'e-mission-common'; -import { formatForDisplay, formatIsoNoYear, isoDatesDifference, isoDateWithOffset } from '../util'; +import { + formatForDisplay, + formatIsoNoYear, + isoDatesDifference, + isoDateWithOffset, +} from '../datetimeUtil'; import { LabelOptions, RichMode } from '../types/labelTypes'; import { labelOptions, textToLabelKey } from '../survey/multilabel/confirmHelper'; import { UNCERTAIN_OPACITY } from '../components/charting'; diff --git a/www/js/metrics/movement/WeeklyActiveMinutesCard.tsx b/www/js/metrics/movement/WeeklyActiveMinutesCard.tsx index 3fcdade74..bce6db844 100644 --- a/www/js/metrics/movement/WeeklyActiveMinutesCard.tsx +++ b/www/js/metrics/movement/WeeklyActiveMinutesCard.tsx @@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next'; import BarChart from '../../components/BarChart'; import { labelKeyToText } from '../../survey/multilabel/confirmHelper'; import TimelineContext from '../../TimelineContext'; -import { formatIsoNoYear, isoDateWithOffset } from '../../util'; +import { formatIsoNoYear, isoDateWithOffset } from '../../datetimeUtil'; type Props = { userMetrics?: MetricsData; activeModes: string[] }; const WeeklyActiveMinutesCard = ({ userMetrics, activeModes }: Props) => { diff --git a/www/js/onboarding/OnboardingStack.tsx b/www/js/onboarding/OnboardingStack.tsx index 38def9cc3..c84f484ac 100644 --- a/www/js/onboarding/OnboardingStack.tsx +++ b/www/js/onboarding/OnboardingStack.tsx @@ -14,11 +14,11 @@ const OnboardingStack = () => { if (onboardingState.route == OnboardingRoute.WELCOME) { // This page needs 'light content' status bar (white text) due to blue header at the top - window['StatusBar']?.styleLightContent(); + // window['StatusBar']?.styleLightContent(); return ; } // All other pages go back to 'default' (black text) - window['StatusBar']?.styleDefault(); + // window['StatusBar']?.styleDefault(); if (onboardingState.route == OnboardingRoute.SUMMARY) { return ; } else if (onboardingState.route == OnboardingRoute.PROTOCOL) { diff --git a/www/js/survey/enketo/AddedNotesList.tsx b/www/js/survey/enketo/AddedNotesList.tsx index caf53e89c..49e883cf5 100644 --- a/www/js/survey/enketo/AddedNotesList.tsx +++ b/www/js/survey/enketo/AddedNotesList.tsx @@ -11,7 +11,7 @@ import EnketoModal from './EnketoModal'; import { useTranslation } from 'react-i18next'; import { EnketoUserInputEntry } from './enketoHelper'; import { displayErrorMsg, logDebug } from '../../plugin/logger'; -import { formatIsoNoYear, isoDatesDifference } from '../../util'; +import { formatIsoNoYear, isoDatesDifference } from '../../datetimeUtil'; type Props = { timelineEntry: any;