Skip to content

Commit

Permalink
Merge pull request #1026 from JGreenlee/label_fixes_sept2023
Browse files Browse the repository at this point in the history
🐛 Label Screen Fixes, Sept 2023
  • Loading branch information
shankari authored Sep 9, 2023
2 parents 6abaae5 + 951f9df commit afa2d79
Show file tree
Hide file tree
Showing 16 changed files with 160 additions and 106 deletions.
5 changes: 5 additions & 0 deletions www/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@
"error-loading-config-app-start": "Error loading config on app start",
"survey-missing-formpath": "Error while fetching resources in config: survey_info.surveys has a survey without a formPath"
},
"errors": {
"while-populating-composite": "Error while populating composite trips",
"while-loading-another-week": "Error while loading travel of {{when}} week",
"while-loading-specific-week": "Error while loading travel for the week of {{day}}"
},
"consent-text": {
"title":"NREL OPENPATH PRIVACY POLICY/TERMS OF USE",
"introduction":{
Expand Down
7 changes: 5 additions & 2 deletions www/js/appTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ const flavorOverrides = {
},
}
},
draft: { // for draft TripCards; a greyish color scheme
draft: { // for TripCards and LabelDetailsScreen of draft trips; a greyish color scheme
colors: {
primary: '#616971', // lch(44 6 250)
primaryContainer: '#b6bcc2', // lch(76 4 250)
background: '#eef1f4', // lch(95 2 250)
surface: '#eef1f4', // lch(95 2 250)
surfaceDisabled: '#c7cacd', // lch(81 2 250)
elevation: {
level1: '#dbddde', // lch(88 1 250)
level1: '#e1e3e4', // lch(90 1 250)
level2: '#d2d5d8', // lch(85 2 250)
},
}
Expand Down
17 changes: 10 additions & 7 deletions www/js/components/DiaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,13 @@ type Props = ButtonProps & { fillColor?: string, borderColor?: string };
const DiaryButton = ({ children, fillColor, borderColor, icon, ...rest } : Props) => {

const { colors } = useTheme();

const blackAlpha15 = color('black').alpha(0.15).rgb().string();
const style = {
borderColor: borderColor || (fillColor ? blackAlpha15 : colors.primary),
borderWidth: 1.5,
};
const textColor = rest.textColor || (fillColor ? colors.onPrimary : colors.primary);

return (
<Button mode="elevated"
textColor={textColor}
buttonColor={fillColor || colors.onPrimary} style={style}
buttonColor={fillColor || colors.onPrimary}
style={s.button(borderColor, fillColor, colors)}
labelStyle={[s.label, {color: textColor}]}
contentStyle={s.buttonContent}
{...rest}>
Expand All @@ -34,7 +29,15 @@ const DiaryButton = ({ children, fillColor, borderColor, icon, ...rest } : Props
);
};

const blackAlpha15 = color('black').alpha(0.15).rgb().string();
const s = StyleSheet.create({
button: ((borderColor, fillColor, colors) => ({
width: '100%',
maxWidth: 200,
margin: 'auto',
borderColor: borderColor || (fillColor ? blackAlpha15 : colors.primary),
borderWidth: 1.5,
})) as any,
buttonContent: {
height: 25,
},
Expand Down
2 changes: 1 addition & 1 deletion www/js/components/ToggleSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ToggleSwitch = ({ value, buttons, ...rest}: SegmentedButtonsProps) => {
minWidth: 0,
borderTopWidth: rest.density == 'high' ? 0 : 1,
borderBottomWidth: rest.density == 'high' ? 0 : 1,
backgroundColor: value == o.value ? colors.elevation.level2 : colors.surfaceDisabled,
backgroundColor: value == o.value ? colors.elevation.level1 : colors.surfaceDisabled,
},
...o
}))} {...rest} />
Expand Down
67 changes: 39 additions & 28 deletions www/js/diary/LabelTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { compositeTrips2TimelineMap, getAllUnprocessedInputs, getLocalUnprocesse
import { fillLocationNamesOfTrip, resetNominatimLimiter } from "./addressNamesHelper";
import { SurveyOptions } from "../survey/survey";
import { getLabelOptions } from "../survey/multilabel/confirmHelper";
import { displayError } from "../plugin/logger";
import AppStatusModal from "../control/AppStatusModal";
import { useTheme } from "react-native-paper";

Expand Down Expand Up @@ -136,39 +137,49 @@ const LabelTab = () => {
}

async function loadAnotherWeek(when: 'past'|'future') {
const reachedPipelineStart = queriedRange?.start_ts && queriedRange.start_ts <= pipelineRange.start_ts;
const reachedPipelineEnd = queriedRange?.end_ts && queriedRange.end_ts >= pipelineRange.end_ts;
try {
const reachedPipelineStart = queriedRange?.start_ts && queriedRange.start_ts <= pipelineRange.start_ts;
const reachedPipelineEnd = queriedRange?.end_ts && queriedRange.end_ts >= pipelineRange.end_ts;

if (!queriedRange) {
// first time loading
if(!isLoading) setIsLoading('replace');
const nowTs = new Date().getTime() / 1000;
const [ctList, utList] = await fetchTripsInRange(pipelineRange.end_ts - ONE_WEEK, nowTs);
handleFetchedTrips(ctList, utList, 'replace');
setQueriedRange({start_ts: pipelineRange.end_ts - ONE_WEEK, end_ts: nowTs});
} else if (when == 'past' && !reachedPipelineStart) {
if(!isLoading) setIsLoading('prepend');
const fetchStartTs = Math.max(queriedRange.start_ts - ONE_WEEK, pipelineRange.start_ts);
const [ctList, utList] = await fetchTripsInRange(queriedRange.start_ts - ONE_WEEK, queriedRange.start_ts - 1);
handleFetchedTrips(ctList, utList, 'prepend');
setQueriedRange({start_ts: fetchStartTs, end_ts: queriedRange.end_ts})
} else if (when == 'future' && !reachedPipelineEnd) {
if(!isLoading) setIsLoading('append');
const fetchEndTs = Math.min(queriedRange.end_ts + ONE_WEEK, pipelineRange.end_ts);
const [ctList, utList] = await fetchTripsInRange(queriedRange.end_ts + 1, fetchEndTs);
handleFetchedTrips(ctList, utList, 'append');
setQueriedRange({start_ts: queriedRange.start_ts, end_ts: fetchEndTs})
if (!queriedRange) {
// first time loading
if(!isLoading) setIsLoading('replace');
const nowTs = new Date().getTime() / 1000;
const [ctList, utList] = await fetchTripsInRange(pipelineRange.end_ts - ONE_WEEK, nowTs);
handleFetchedTrips(ctList, utList, 'replace');
setQueriedRange({start_ts: pipelineRange.end_ts - ONE_WEEK, end_ts: nowTs});
} else if (when == 'past' && !reachedPipelineStart) {
if(!isLoading) setIsLoading('prepend');
const fetchStartTs = Math.max(queriedRange.start_ts - ONE_WEEK, pipelineRange.start_ts);
const [ctList, utList] = await fetchTripsInRange(queriedRange.start_ts - ONE_WEEK, queriedRange.start_ts - 1);
handleFetchedTrips(ctList, utList, 'prepend');
setQueriedRange({start_ts: fetchStartTs, end_ts: queriedRange.end_ts})
} else if (when == 'future' && !reachedPipelineEnd) {
if(!isLoading) setIsLoading('append');
const fetchEndTs = Math.min(queriedRange.end_ts + ONE_WEEK, pipelineRange.end_ts);
const [ctList, utList] = await fetchTripsInRange(queriedRange.end_ts + 1, fetchEndTs);
handleFetchedTrips(ctList, utList, 'append');
setQueriedRange({start_ts: queriedRange.start_ts, end_ts: fetchEndTs})
}
} catch (e) {
setIsLoading(false);
displayError(e, t('errors.while-loading-another-week', {when: when}));
}
}

async function loadSpecificWeek(day: string) {
if (!isLoading) setIsLoading('replace');
resetNominatimLimiter();
const threeDaysBefore = moment(day).subtract(3, 'days').unix();
const threeDaysAfter = moment(day).add(3, 'days').unix();
const [ctList, utList] = await fetchTripsInRange(threeDaysBefore, threeDaysAfter);
handleFetchedTrips(ctList, utList, 'replace');
setQueriedRange({start_ts: threeDaysBefore, end_ts: threeDaysAfter});
try {
if (!isLoading) setIsLoading('replace');
resetNominatimLimiter();
const threeDaysBefore = moment(day).subtract(3, 'days').unix();
const threeDaysAfter = moment(day).add(3, 'days').unix();
const [ctList, utList] = await fetchTripsInRange(threeDaysBefore, threeDaysAfter);
handleFetchedTrips(ctList, utList, 'replace');
setQueriedRange({start_ts: threeDaysBefore, end_ts: threeDaysAfter});
} catch (e) {
setIsLoading(false);
displayError(e, t('errors.while-loading-specific-week', {day: day}));
}
}

function handleFetchedTrips(ctList, utList, mode: 'prepend' | 'append' | 'replace') {
Expand Down
2 changes: 1 addition & 1 deletion www/js/diary/cards/ModesIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const ModesIndicator = ({ trip, detectedModes, }) => {
</Text>
</View>
);
} else if (detectedModes?.length > 1 || detectedModes[0]?.mode != 'UNKNOWN') {
} else if (detectedModes?.length > 1 || detectedModes?.length == 1 && detectedModes[0].mode != 'UNKNOWN') {
// show detected modes if there are more than one, or if there is only one and it's not UNKNOWN
modeViews = (<>
<Text style={{fontSize: 12, fontWeight: '500'}}>{t('diary.detected')}</Text>
Expand Down
3 changes: 2 additions & 1 deletion www/js/diary/cards/TripCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const TripCard = ({ trip }: Props) => {
const flavoredTheme = getTheme(isDraft ? 'draft' : undefined);

function showDetail() {
navigation.navigate("label.details", { tripId: trip._id.$oid });
const tripId = trip._id.$oid;
navigation.navigate("label.details", { tripId, flavoredTheme });
}

const mapOpts = { zoomControl: false, dragging: false };
Expand Down
26 changes: 18 additions & 8 deletions www/js/diary/details/LabelDetailsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import React, { useContext, useState } from "react";
import { View, Modal, ScrollView, useWindowDimensions } from "react-native";
import { Appbar, SegmentedButtons, Button, Surface, Text, useTheme } from "react-native-paper";
import { PaperProvider, Appbar, SegmentedButtons, Button, Surface, Text, useTheme } from "react-native-paper";
import { LabelTabContext } from "../LabelTab";
import LeafletView from "../../components/LeafletView";
import { useTranslation } from "react-i18next";
Expand All @@ -24,19 +24,20 @@ const LabelScreenDetails = ({ route, navigation }) => {
const { surveyOpt, timelineMap, labelOptions } = useContext(LabelTabContext);
const { t } = useTranslation();
const { height: windowHeight } = useWindowDimensions();
const { colors } = useTheme();
const trip = timelineMap.get(route.params.tripId);
const { tripId, flavoredTheme } = route.params;
const trip = timelineMap.get(tripId);
const { colors } = flavoredTheme || useTheme();
const { displayDate, displayStartTime, displayEndTime } = useDerivedProperties(trip);
const [ tripStartDisplayName, tripEndDisplayName ] = useAddressNames(trip);

const [ modesShown, setModesShown ] = useState<'labeled'|'detected'>('labeled');
const tripGeojson = useGeojsonForTrip(trip, labelOptions, modesShown=='labeled' && trip?.userInput?.MODE?.value);
const mapOpts = {minZoom: 3, maxZoom: 17};

return (
const modal = (
<Modal visible={true}>
<SafeAreaView style={{flex: 1}}>
<Appbar.Header statusBarHeight={0} elevated={true} style={{ height: 46, backgroundColor: 'white', elevation: 3 }}>
<Appbar.Header statusBarHeight={0} elevated={true} style={{ height: 46, backgroundColor: colors.surface, elevation: 3 }}>
<Appbar.BackAction onPress={() => { navigation.goBack() }} />
<Appbar.Content title={displayDate} titleStyle={{fontSize: 17}} />
</Appbar.Header>
Expand All @@ -45,8 +46,9 @@ const LabelScreenDetails = ({ route, navigation }) => {
displayStartTime={displayStartTime} displayEndTime={displayEndTime}
displayStartName={tripStartDisplayName} displayEndName={tripEndDisplayName} />
</Surface>
<ScrollView style={{ paddingBottom: 30}}>
<Surface mode='flat' style={{padding: 10, marginHorizontal: 10, rowGap: 12 }}>
<ScrollView style={{ paddingBottom: 30, backgroundColor: colors.background }}>
<Surface mode='flat'
style={{margin: 10, paddingHorizontal: 10, rowGap: 12, borderRadius: 15 }}>
{/* MultiLabel or UserInput button, inline on one row */}
<View style={{ paddingVertical: 10 }}>
{surveyOpt?.elementTag == 'multilabel' &&
Expand Down Expand Up @@ -83,7 +85,15 @@ const LabelScreenDetails = ({ route, navigation }) => {
</ScrollView>
</SafeAreaView>
</Modal>
)
);
if (route.params.flavoredTheme) {
return (
<PaperProvider theme={route.params.flavoredTheme}>
{modal}
</PaperProvider>
);
}
return modal;
}

export default LabelScreenDetails;
15 changes: 11 additions & 4 deletions www/js/diary/details/TripSectionsDescriptives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { View } from 'react-native';
import { Text, useTheme } from 'react-native-paper'
import { Icon } from '../../components/Icon';
import useDerivedProperties from '../useDerivedProperties';
import { getBaseModeOfLabeledTrip } from '../diaryHelper';
import { getBaseModeByKey, getBaseModeOfLabeledTrip } from '../diaryHelper';
import { LabelTabContext } from '../LabelTab';

const TripSectionsDescriptives = ({ trip, showLabeledMode=false }) => {
Expand All @@ -15,15 +15,22 @@ const TripSectionsDescriptives = ({ trip, showLabeledMode=false }) => {
const { colors } = useTheme();

let sections = formattedSectionProperties;
if (showLabeledMode && trip?.userInput?.MODE) {
const baseMode = getBaseModeOfLabeledTrip(trip, labelOptions);
/* if we're only showing the labeled mode, or there are no sections (i.e. unprocessed trip),
we treat this as unimodal and use trip-level attributes to construct a single section */
if (showLabeledMode && trip?.userInput?.MODE || !trip.sections?.length) {
let baseMode;
if (showLabeledMode && trip?.userInput?.MODE) {
baseMode = getBaseModeOfLabeledTrip(trip, labelOptions);
} else {
baseMode = getBaseModeByKey('UNPROCESSED');
}
sections = [{
startTime: displayStartTime,
duration: displayTime,
distance: formattedDistance,
color: baseMode.color,
icon: baseMode.icon,
text: trip.userInput.MODE.text,
text: showLabeledMode && trip.userInput?.MODE?.text, // label text only shown for labeled trips
}];
}
return (
Expand Down
18 changes: 14 additions & 4 deletions www/js/diary/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,14 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',

return {
...tripProps,
start_loc: tripStartPoint.data.loc,
end_loc: tripEndPoint.data.loc,
start_loc: {
type: "Point",
coordinates: [tripStartPoint.data.longitude, tripStartPoint.data.latitude]
},
end_loc: {
type: "Point",
coordinates: [tripEndPoint.data.longitude, tripEndPoint.data.latitude],
},
}
});
}
Expand Down Expand Up @@ -327,8 +333,12 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',
// anyway.

Logger.log("mapped trips to trip_gj_list of size "+raw_trip_gj_list.length);
var trip_gj_list = raw_trip_gj_list.filter(angular.isDefined);
Logger.log("after filtering undefined, trip_gj_list size = "+raw_trip_gj_list.length);
/* Filtering: we will keep trips that are 1) defined and 2) have a distance >= 100m or duration >= 5 minutes
https://github.com/e-mission/e-mission-docs/issues/966#issuecomment-1709112578 */
const trip_gj_list = raw_trip_gj_list.filter((trip) =>
trip && (trip.distance >= 100 || trip.duration >= 300)
);
Logger.log("after filtering undefined and distance < 100, trip_gj_list size = "+raw_trip_gj_list.length);
// Link 0th trip to first, first to second, ...
for (var i = 0; i < trip_gj_list.length-1; i++) {
linkTrips(trip_gj_list[i], trip_gj_list[i+1]);
Expand Down
45 changes: 25 additions & 20 deletions www/js/diary/timelineHelper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import moment from "moment";
import { getAngularService } from "../angular-react-helper";
import { logDebug } from "../plugin/logger";
import { displayError, logDebug } from "../plugin/logger";
import { getBaseModeByKey, getBaseModeOfLabeledTrip } from "./diaryHelper";
import i18next from "i18next";

const cachedGeojsons = new Map();
/**
Expand Down Expand Up @@ -70,25 +71,29 @@ export function compositeTrips2TimelineMap(ctList: any[], unpackPlaces?: boolean
}

export function populateCompositeTrips(ctList, showPlaces, labelsFactory, labelsResultMap, notesFactory, notesResultMap) {
ctList.forEach((ct, i) => {
if (showPlaces && ct.start_confirmed_place) {
const cp = ct.start_confirmed_place;
cp.getNextEntry = () => ctList[i];
labelsFactory.populateInputsAndInferences(cp, labelsResultMap);
notesFactory.populateInputsAndInferences(cp, notesResultMap);
}
if (showPlaces && ct.end_confirmed_place) {
const cp = ct.end_confirmed_place;
cp.getNextEntry = () => ctList[i + 1];
labelsFactory.populateInputsAndInferences(cp, labelsResultMap);
notesFactory.populateInputsAndInferences(cp, notesResultMap);
ct.getNextEntry = () => cp;
} else {
ct.getNextEntry = () => ctList[i + 1];
}
labelsFactory.populateInputsAndInferences(ct, labelsResultMap);
notesFactory.populateInputsAndInferences(ct, notesResultMap);
});
try {
ctList.forEach((ct, i) => {
if (showPlaces && ct.start_confirmed_place) {
const cp = ct.start_confirmed_place;
cp.getNextEntry = () => ctList[i];
labelsFactory.populateInputsAndInferences(cp, labelsResultMap);
notesFactory.populateInputsAndInferences(cp, notesResultMap);
}
if (showPlaces && ct.end_confirmed_place) {
const cp = ct.end_confirmed_place;
cp.getNextEntry = () => ctList[i + 1];
labelsFactory.populateInputsAndInferences(cp, labelsResultMap);
notesFactory.populateInputsAndInferences(cp, notesResultMap);
ct.getNextEntry = () => cp;
} else {
ct.getNextEntry = () => ctList[i + 1];
}
labelsFactory.populateInputsAndInferences(ct, labelsResultMap);
notesFactory.populateInputsAndInferences(ct, notesResultMap);
});
} catch (e) {
displayError(e, i18next.t('errors.while-populating-composite'));
}
}

const getUnprocessedInputQuery = (pipelineRange) => ({
Expand Down
2 changes: 1 addition & 1 deletion www/js/plugin/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function displayErrorMsg(errorMsg: string, title?: string) {
if (errorMsg.includes?.("403")) {
title = "Invalid OPcode: " + (title || '');
}
const displayMsg = title ? title + '\n' + errorMsg : errorMsg;
const displayMsg = `━━━━\n${title}\n━━━━\n` + errorMsg;
window.alert(displayMsg);
console.error(displayMsg);
window['Logger'].log(window['Logger'].LEVEL_ERROR, displayMsg);
Expand Down
5 changes: 3 additions & 2 deletions www/js/survey/enketo/AddNoteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next";
import moment from "moment";
import { LabelTabContext } from "../../diary/LabelTab";
import EnketoModal from "./EnketoModal";
import { displayErrorMsg, logDebug } from "../../plugin/logger";

type Props = {
timelineEntry: any,
Expand Down Expand Up @@ -83,10 +84,10 @@ const AddNoteButton = ({ timelineEntry, notesConfig, storeKey }: Props) => {

function onResponseSaved(result) {
if (result) {
console.log('AddNoteButton: response was saved, about to repopulateTimelineEntry; result=', result);
logDebug('AddNoteButton: response was saved, about to repopulateTimelineEntry; result=' + JSON.stringify(result));
repopulateTimelineEntry(timelineEntry._id.$oid);
} else {
console.error('AddNoteButton: response was not saved, result=', result);
displayErrorMsg('AddNoteButton: response was not saved, result=', result);
}
}

Expand Down
Loading

0 comments on commit afa2d79

Please sign in to comment.