Skip to content

Commit

Permalink
Merge pull request #1181 from JGreenlee/energy-dashboard-fixes
Browse files Browse the repository at this point in the history
Sep 2024 Fixes
  • Loading branch information
shankari authored Sep 27, 2024
2 parents b08a669 + 85b7b83 commit 0c26330
Show file tree
Hide file tree
Showing 22 changed files with 71 additions and 106 deletions.
4 changes: 1 addition & 3 deletions package.cordovabuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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.1",
"enketo-core": "^6.1.7",
Expand Down
2 changes: 1 addition & 1 deletion setup/setup_native.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 ];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { formatForDisplay } from '../js/util';
import { formatForDisplay } from '../js/datetimeUtil';

describe('util.ts', () => {
describe('formatForDisplay', () => {
Expand Down
2 changes: 1 addition & 1 deletion www/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion www/js/TimelineContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
7 changes: 4 additions & 3 deletions www/js/components/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) => ({
Expand All @@ -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,
Expand Down
113 changes: 37 additions & 76 deletions www/js/components/charting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion www/js/config/useImperialConfig.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion www/js/diary/diaryHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion www/js/diary/list/DateSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
2 changes: 1 addition & 1 deletion www/js/diary/list/TimelineScrollList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand Down
5 changes: 3 additions & 2 deletions www/js/diary/useDerivedProperties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion www/js/metrics/MetricsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion www/js/metrics/footprint/FootprintComparisonCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions www/js/metrics/footprint/FootprintSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion www/js/metrics/footprint/WeeklyFootprintCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
7 changes: 6 additions & 1 deletion www/js/metrics/metricsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion www/js/metrics/movement/WeeklyActiveMinutesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions www/js/onboarding/OnboardingStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <WelcomePage />;
}
// All other pages go back to 'default' (black text)
window['StatusBar']?.styleDefault();
// window['StatusBar']?.styleDefault();
if (onboardingState.route == OnboardingRoute.SUMMARY) {
return <SummaryPage />;
} else if (onboardingState.route == OnboardingRoute.PROTOCOL) {
Expand Down
2 changes: 1 addition & 1 deletion www/js/survey/enketo/AddedNotesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 0c26330

Please sign in to comment.