diff --git a/package.cordovabuild.json b/package.cordovabuild.json index a4a729beb..a2c36e855 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -137,7 +137,7 @@ "cordova-custom-config": "^5.1.1", "cordova-plugin-ibeacon": "git+https://github.com/louisg1337/cordova-plugin-ibeacon.git", "core-js": "^2.5.7", - "e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.1", + "e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.4", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", "fast-xml-parser": "^4.2.2", diff --git a/package.serve.json b/package.serve.json index ff9bf5879..a96c29657 100644 --- a/package.serve.json +++ b/package.serve.json @@ -65,7 +65,7 @@ "chartjs-adapter-luxon": "^1.3.1", "chartjs-plugin-annotation": "^3.0.1", "core-js": "^2.5.7", - "e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.1", + "e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.4", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", "fast-xml-parser": "^4.2.2", diff --git a/www/__tests__/diaryHelper.test.ts b/www/__tests__/diaryHelper.test.ts index 0eb4a8628..58cd20246 100644 --- a/www/__tests__/diaryHelper.test.ts +++ b/www/__tests__/diaryHelper.test.ts @@ -4,10 +4,10 @@ import { getFormattedDateAbbr, getFormattedTimeRange, getDetectedModes, - getBaseModeByKey, - modeColors, } from '../js/diary/diaryHelper'; +import { base_modes } from 'e-mission-common'; + import initializedI18next from '../js/i18nextInit'; window['i18next'] = initializedI18next; @@ -38,20 +38,17 @@ it('returns a human readable time range', () => { }); it('returns a Base Mode for a given key', () => { - expect(getBaseModeByKey('WALKING')).toEqual({ - name: 'WALKING', + expect(base_modes.get_base_mode_by_key('WALKING')).toMatchObject({ icon: 'walk', - color: modeColors.blue, + color: base_modes.mode_colors['blue'], }); - expect(getBaseModeByKey('MotionTypes.WALKING')).toEqual({ - name: 'WALKING', + expect(base_modes.get_base_mode_by_key('MotionTypes.WALKING')).toMatchObject({ icon: 'walk', - color: modeColors.blue, + color: base_modes.mode_colors['blue'], }); - expect(getBaseModeByKey('I made this type up')).toEqual({ - name: 'UNKNOWN', + expect(base_modes.get_base_mode_by_key('I made this type up')).toMatchObject({ icon: 'help', - color: modeColors.grey, + color: base_modes.mode_colors['grey'], }); }); @@ -87,11 +84,13 @@ let myFakeTrip2 = { }; let myFakeDetectedModes = [ - { mode: 'BICYCLING', icon: 'bike', color: modeColors.green, pct: 89 }, - { mode: 'WALKING', icon: 'walk', color: modeColors.blue, pct: 11 }, + { mode: 'BICYCLING', icon: 'bike', color: base_modes.mode_colors['green'], pct: 89 }, + { mode: 'WALKING', icon: 'walk', color: base_modes.mode_colors['blue'], pct: 11 }, ]; -let myFakeDetectedModes2 = [{ mode: 'BICYCLING', icon: 'bike', color: modeColors.green, pct: 100 }]; +let myFakeDetectedModes2 = [ + { mode: 'BICYCLING', icon: 'bike', color: base_modes.mode_colors['green'], pct: 100 }, +]; it('returns the detected modes, with percentages, for a trip', () => { expect(getDetectedModes(myFakeTrip)).toEqual(myFakeDetectedModes); diff --git a/www/js/components/charting.ts b/www/js/components/charting.ts index f536fc04f..11ae43be7 100644 --- a/www/js/components/charting.ts +++ b/www/js/components/charting.ts @@ -1,5 +1,4 @@ import color from 'color'; -import { getBaseModeByKey } from '../diary/diaryHelper'; import { readableLabelToKey } from '../survey/multilabel/confirmHelper'; import { logDebug } from '../plugin/logger'; diff --git a/www/js/diary/cards/ModesIndicator.tsx b/www/js/diary/cards/ModesIndicator.tsx index 2ec5d9dc2..8a4cf1689 100644 --- a/www/js/diary/cards/ModesIndicator.tsx +++ b/www/js/diary/cards/ModesIndicator.tsx @@ -3,9 +3,9 @@ import { View, StyleSheet } from 'react-native'; import color from 'color'; import TimelineContext from '../../TimelineContext'; import { logDebug } from '../../plugin/logger'; -import { getBaseModeByKey, getBaseModeByValue } from '../diaryHelper'; import { Text, Icon, useTheme } from 'react-native-paper'; import { useTranslation } from 'react-i18next'; +import { base_modes } from 'e-mission-common'; const ModesIndicator = ({ trip, detectedModes }) => { const { t } = useTranslation(); @@ -18,7 +18,7 @@ const ModesIndicator = ({ trip, detectedModes }) => { let modeViews; const confirmedModeForTrip = confirmedModeFor(trip); if (labelOptions && confirmedModeForTrip?.value) { - const baseMode = getBaseModeByKey(confirmedModeForTrip.baseMode); + const baseMode = base_modes.get_base_mode_by_key(confirmedModeForTrip.baseMode); indicatorBorderColor = baseMode.color; logDebug(`TripCard: got baseMode = ${JSON.stringify(baseMode)}`); modeViews = ( diff --git a/www/js/diary/details/TripSectionsDescriptives.tsx b/www/js/diary/details/TripSectionsDescriptives.tsx index 13c15019d..4592c838f 100644 --- a/www/js/diary/details/TripSectionsDescriptives.tsx +++ b/www/js/diary/details/TripSectionsDescriptives.tsx @@ -2,8 +2,8 @@ import React, { useContext } from 'react'; import { View, StyleSheet } from 'react-native'; import { Icon, Text, useTheme } from 'react-native-paper'; import useDerivedProperties from '../useDerivedProperties'; -import { getBaseModeByKey, getBaseModeByValue } from '../diaryHelper'; import TimelineContext from '../../TimelineContext'; +import { base_modes } from 'e-mission-common'; const TripSectionsDescriptives = ({ trip, showConfirmedMode = false }) => { const { labelOptions, labelFor, confirmedModeFor } = useContext(TimelineContext); @@ -24,9 +24,9 @@ const TripSectionsDescriptives = ({ trip, showConfirmedMode = false }) => { if ((showConfirmedMode && confirmedModeForTrip) || !trip.sections?.length) { let baseMode; if (showConfirmedMode && labelOptions && confirmedModeForTrip) { - baseMode = getBaseModeByKey(confirmedModeForTrip.baseMode); + baseMode = base_modes.get_base_mode_by_key(confirmedModeForTrip.baseMode); } else { - baseMode = getBaseModeByKey('UNPROCESSED'); + baseMode = base_modes.get_base_mode_by_key('UNPROCESSED'); } sections = [ { diff --git a/www/js/diary/diaryHelper.ts b/www/js/diary/diaryHelper.ts index 12495742d..4af19c2cf 100644 --- a/www/js/diary/diaryHelper.ts +++ b/www/js/diary/diaryHelper.ts @@ -9,24 +9,9 @@ import { LocalDt } from '../types/serverData'; import humanizeDuration from 'humanize-duration'; import { AppConfig } from '../types/appConfigTypes'; import { ImperialConfig } from '../config/useImperialConfig'; +import { base_modes } from 'e-mission-common'; -export const modeColors = { - pink: '#c32e85', // oklch(56% 0.2 350) // e-car - red: '#c21725', // oklch(52% 0.2 25) // car - orange: '#bf5900', // oklch(58% 0.16 50) // air, hsr - green: '#008148', // oklch(53% 0.14 155) // bike, e-bike, moped - blue: '#0074b7', // oklch(54% 0.14 245) // walk - periwinkle: '#6356bf', // oklch(52% 0.16 285) // light rail, train, tram, subway - magenta: '#9240a4', // oklch(52% 0.17 320) // bus - grey: '#555555', // oklch(45% 0 0) // unprocessed / unknown - taupe: '#7d585a', // oklch(50% 0.05 15) // ferry, trolleybus, user-defined modes -}; - -type BaseMode = { - name: string; - icon: string; - color: string; -}; +export type BaseModeKey = string; // TODO figure out how to get keyof typeof base_modes.BASE_MODES // parallels the server-side MotionTypes enum: https://github.com/e-mission/e-mission-server/blob/94e7478e627fa8c171323662f951c611c0993031/emission/core/wrapper/motionactivity.py#L12 export type MotionTypeKey = @@ -42,54 +27,9 @@ export type MotionTypeKey = | 'STOPPED_WHILE_IN_VEHICLE' | 'AIR_OR_HSR'; -const BaseModes: { [k: string]: BaseMode } = { - // BEGIN MotionTypes - IN_VEHICLE: { name: 'IN_VEHICLE', icon: 'speedometer', color: modeColors.red }, - BICYCLING: { name: 'BICYCLING', icon: 'bike', color: modeColors.green }, - ON_FOOT: { name: 'ON_FOOT', icon: 'walk', color: modeColors.blue }, - UNKNOWN: { name: 'UNKNOWN', icon: 'help', color: modeColors.grey }, - WALKING: { name: 'WALKING', icon: 'walk', color: modeColors.blue }, - AIR_OR_HSR: { name: 'AIR_OR_HSR', icon: 'airplane', color: modeColors.orange }, - // END MotionTypes - CAR: { name: 'CAR', icon: 'car', color: modeColors.red }, - E_CAR: { name: 'E_CAR', icon: 'car-electric', color: modeColors.pink }, - E_BIKE: { name: 'E_BIKE', icon: 'bicycle-electric', color: modeColors.green }, - E_SCOOTER: { name: 'E_SCOOTER', icon: 'scooter-electric', color: modeColors.periwinkle }, - MOPED: { name: 'MOPED', icon: 'moped', color: modeColors.green }, - TAXI: { name: 'TAXI', icon: 'taxi', color: modeColors.red }, - BUS: { name: 'BUS', icon: 'bus-side', color: modeColors.magenta }, - AIR: { name: 'AIR', icon: 'airplane', color: modeColors.orange }, - LIGHT_RAIL: { name: 'LIGHT_RAIL', icon: 'train-car-passenger', color: modeColors.periwinkle }, - TRAIN: { name: 'TRAIN', icon: 'train-car-passenger', color: modeColors.periwinkle }, - TRAM: { name: 'TRAM', icon: 'fas fa-tram', color: modeColors.periwinkle }, - SUBWAY: { name: 'SUBWAY', icon: 'subway-variant', color: modeColors.periwinkle }, - FERRY: { name: 'FERRY', icon: 'ferry', color: modeColors.taupe }, - TROLLEYBUS: { name: 'TROLLEYBUS', icon: 'bus-side', color: modeColors.taupe }, - UNPROCESSED: { name: 'UNPROCESSED', icon: 'help', color: modeColors.grey }, - OTHER: { name: 'OTHER', icon: 'pencil-circle', color: modeColors.taupe }, -}; - -export type BaseModeKey = keyof typeof BaseModes; -/** - * @param motionName A string like "WALKING" or "MotionTypes.WALKING" - * @returns A BaseMode object containing the name, icon, and color of the motion type - */ -export function getBaseModeByKey( - motionName: BaseModeKey | MotionTypeKey | `MotionTypes.${MotionTypeKey}`, -) { - const key = ('' + motionName).toUpperCase(); - const pop = key.split('.').pop(); // if "MotionTypes.WALKING", then just take "WALKING" - return (pop && BaseModes[pop]) || BaseModes.UNKNOWN; -} - -export function getBaseModeByValue(value: string, labelOptions: LabelOptions) { - const modeOption = labelOptions?.MODE?.find((opt) => opt.value == value); - return getBaseModeByKey(modeOption?.baseMode || 'OTHER'); -} - export function getBaseModeByText(text: string, labelOptions: LabelOptions) { const modeOption = labelOptions?.MODE?.find((opt) => opt.text == text); - return getBaseModeByKey(modeOption?.baseMode || 'OTHER'); + return base_modes.get_base_mode_by_key(modeOption?.baseMode || 'OTHER'); } /** @@ -172,11 +112,11 @@ export function getDetectedModes(trip: CompositeTrip) { if (!sectionSummary?.distance) return []; return Object.entries(sectionSummary.distance) - .sort(([modeA, distA], [modeB, distB]) => distB - distA) // sort by distance (highest first) + .sort(([modeA, distA]: [string, number], [modeB, distB]: [string, number]) => distB - distA) // sort by distance (highest first) .map(([mode, dist]: [MotionTypeKey, number]) => ({ mode, - icon: getBaseModeByKey(mode)?.icon, - color: getBaseModeByKey(mode)?.color || 'black', + icon: base_modes.get_base_mode_by_key(mode)?.icon, + color: base_modes.get_base_mode_by_key(mode)?.color || 'black', pct: Math.round((dist / trip.distance) * 100) || '<1', // if rounds to 0%, show <1% })); } @@ -187,8 +127,8 @@ export function getFormattedSectionProperties(trip: CompositeTrip, imperialConfi duration: getFormattedTimeRange(s.start_fmt_time, s.end_fmt_time), distance: imperialConfig.getFormattedDistance(s.distance), distanceSuffix: imperialConfig.distanceSuffix, - icon: getBaseModeByKey(s.sensed_mode_str)?.icon, - color: getBaseModeByKey(s.sensed_mode_str)?.color || '#333', + icon: base_modes.get_base_mode_by_key(s.sensed_mode_str)?.icon, + color: base_modes.get_base_mode_by_key(s.sensed_mode_str)?.color || '#333', })); } diff --git a/www/js/diary/timelineHelper.ts b/www/js/diary/timelineHelper.ts index d5c1e507b..d4dded91b 100644 --- a/www/js/diary/timelineHelper.ts +++ b/www/js/diary/timelineHelper.ts @@ -1,5 +1,4 @@ import { displayError, displayErrorMsg, logDebug } from '../plugin/logger'; -import { getBaseModeByKey, getBaseModeByValue } from './diaryHelper'; import { getUnifiedDataForInterval } from '../services/unifiedDataLoader'; import { getRawEntries } from '../services/commHelper'; import { ServerResponse, BEMData } from '../types/serverData'; @@ -28,7 +27,7 @@ import { } from '../survey/enketo/enketoHelper'; import { AppConfig } from '../types/appConfigTypes'; import { Point, Feature } from 'geojson'; -import { ble_matching } from 'e-mission-common'; +import { ble_matching, base_modes } from 'e-mission-common'; const cachedGeojsons: Map = new Map(); @@ -42,7 +41,8 @@ export function useGeojsonForTrip(trip: CompositeTrip, baseMode?: string) { return cachedGeojsons.get(gjKey); } - const trajectoryColor = (baseMode && getBaseModeByKey(baseMode)?.color) || undefined; + const trajectoryColor = + (baseMode && base_modes.get_base_mode_by_key(baseMode)?.color) || undefined; logDebug("Reading trip's " + trip.locations.length + ' location points at ' + new Date()); const features = [ @@ -266,7 +266,10 @@ function locations2GeojsonTrajectory( style: { /* If a color was passed as arg, use it for the whole trajectory. Otherwise, use the color for the sensed mode of this section, and fall back to dark grey */ - color: trajectoryColor || getBaseModeByKey(section?.sensed_mode_str)?.color || '#333', + color: + trajectoryColor || + base_modes.get_base_mode_by_key(section?.sensed_mode_str)?.color || + '#333', }, properties: { feature_type: 'section_trajectory', diff --git a/www/js/metrics/MetricsCard.tsx b/www/js/metrics/MetricsCard.tsx index 287193711..241ab8208 100644 --- a/www/js/metrics/MetricsCard.tsx +++ b/www/js/metrics/MetricsCard.tsx @@ -15,10 +15,11 @@ import { import ToggleSwitch from '../components/ToggleSwitch'; import { cardStyles } from './MetricsTab'; import { labelKeyToRichMode, labelOptions } from '../survey/multilabel/confirmHelper'; -import { getBaseModeByKey, getBaseModeByText, modeColors } from '../diary/diaryHelper'; +import { getBaseModeByText } from '../diary/diaryHelper'; import { useTranslation } from 'react-i18next'; import { GroupingField, MetricName } from '../types/appConfigTypes'; import { useImperialConfig } from '../config/useImperialConfig'; +import { base_modes } from 'e-mission-common'; type Props = { metricName: MetricName; @@ -115,7 +116,7 @@ const MetricsCard = ({ // All other modes are colored according to their base mode const getColorForLabel = (label: string) => { if (label == 'Unlabeled') { - const unknownModeColor = getBaseModeByKey('UNKNOWN').color; + const unknownModeColor = base_modes.get_base_mode_by_key('UNKNOWN').color; return colorLib(unknownModeColor).alpha(0.15).rgb().string(); } return getBaseModeByText(label, labelOptions).color;