From 679acd604e163ea1c5ab04f71496c18c6d0714f1 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Fri, 29 Apr 2022 15:59:25 -0700 Subject: [PATCH 01/11] Fix the weird issue where the map popup breaks maps in the diary list By converting the popover into a separate view, similar to the detail view. Presumably, this does something magical with the scope, which prevents the unfortunate spillover of some kind of invalidation into the list view. Issue reproduction: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1111796872 https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1113485619 Potential fixes (none of which worked): - Removing invalidateSize: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1112912290 - Trying to make the map id depend on the index: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1113543611 - Changing from `collection-repeat` to `ng-repeat`: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1113592634 - pass in the trip object directly: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1113728041 Final solution: - Create a map of confirmed trip objects in the timeline - Reset it every time we modify the list of trips - Create a new simplified detail screen without sections - Look up the confirmed trip, convert it to geojson and display it - Copy over the id for the retrieved confirmed trips to ensure that we have a unique key Bonus fix: Handle an undefined input for the trip -> geojson function correctly Testing done: - Loaded data for day in diary view - Clicked on detail from infinite scroll view - Went back to diary view - Everything was fine --- www/index.html | 1 + www/js/diary.js | 12 ++++- www/js/diary/infinite_scroll_list.js | 20 ++------ www/js/diary/services.js | 23 ++++++++- .../diary/infinite_scroll_detail.html | 47 +++++++++++++++++++ www/templates/diary/trip-detail-popover.html | 41 ---------------- 6 files changed, 85 insertions(+), 59 deletions(-) create mode 100644 www/templates/diary/infinite_scroll_detail.html delete mode 100644 www/templates/diary/trip-detail-popover.html diff --git a/www/index.html b/www/index.html index 7e7298d3c..b186aa323 100644 --- a/www/index.html +++ b/www/index.html @@ -91,6 +91,7 @@ + diff --git a/www/js/diary.js b/www/js/diary.js index 30da4e9f5..71a8e903b 100644 --- a/www/js/diary.js +++ b/www/js/diary.js @@ -1,5 +1,6 @@ angular.module('emission.main.diary',['emission.main.diary.list', 'emission.main.diary.infscrolllist', + 'emission.main.diary.infscrolldetail', 'emission.main.diary.detail', 'emission.main.diary.services', 'emission.main.diary.current',]) @@ -34,7 +35,16 @@ angular.module('emission.main.diary',['emission.main.diary.list', controller: 'InfiniteDiaryListCtrl' }, } - + }) + + .state('root.main.inf_scroll-detail', { + url: "/inf_scroll/:tripId", + views: { + 'main-inf-scroll': { + templateUrl: "templates/diary/infinite_scroll_detail.html", + controller: 'InfiniteDiaryDetailCtrl' + }, + } }) .state('root.main.current', { diff --git a/www/js/diary/infinite_scroll_list.js b/www/js/diary/infinite_scroll_list.js index 469802306..4c4257fb4 100644 --- a/www/js/diary/infinite_scroll_list.js +++ b/www/js/diary/infinite_scroll_list.js @@ -26,7 +26,7 @@ angular.module('emission.main.diary.infscrolllist',['ui-leaflet', $timeout, leafletData, Timeline, CommonGraph, DiaryHelper, SurveyOptions, - Config, ImperialConfig, PostTripManualMarker, nzTour, KVStore, Logger, UnifiedDataLoader, $ionicPopover, $ionicModal, $translate) { + Config, ImperialConfig, PostTripManualMarker, nzTour, KVStore, Logger, UnifiedDataLoader, $ionicModal, $translate) { // TODO: load only a subset of entries instead of everything @@ -114,6 +114,7 @@ angular.module('emission.main.diary.infscrolllist',['ui-leaflet', }); $scope.data.allTrips = ctList.concat($scope.data.allTrips); Logger.log("After adding batch of size "+ctList.length+" cumulative size = "+$scope.data.allTrips.length); + Timeline.setInfScrollConfirmedTripList($scope.data.allTrips); const oldestTrip = ctList[0]; if (oldestTrip) { if (oldestTrip.start_ts <= $scope.infScrollControl.pipelineRange.start_ts) { @@ -186,22 +187,9 @@ angular.module('emission.main.diary.infscrolllist',['ui-leaflet', } }); - $ionicModal.fromTemplateUrl("templates/diary/trip-detail-popover.html", { - scope: $scope, - animation: 'slide-in-up' - }).then((popover) => { - $scope.tripDetailPopover = popover; - }); - $scope.showDetail = function($event, trip) { - Timeline.confirmedTrip2Geojson(trip).then((tripgj) => { - $scope.currgj = trip; - $scope.currgj.data = tripgj; - $scope.currgj.pointToLayer = DiaryHelper.pointFormat; - $scope.tripDetailPopover.show(); - leafletData.getMap("detailPopoverMap").then(function(map) { - map.invalidateSize(); - }); + $state.go("root.main.inf_scroll-detail", { + tripId: trip.id }); } diff --git a/www/js/diary/services.js b/www/js/diary/services.js index b86330491..9f4d8033b 100644 --- a/www/js/diary/services.js +++ b/www/js/diary/services.js @@ -537,7 +537,11 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', return Promise.all(readPromises) .then(([ctList]) => { $ionicLoading.hide(); - return ctList.phone_data.map((ct) => ct.data); + return ctList.phone_data.map((ct) => { + const retVal = ct.data; + retVal.id = ct._id["$oid"]; + return retVal; + }); }) .catch((err) => { Logger.displayError("while reading confirmed trips", err); @@ -860,6 +864,9 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', } timeline.confirmedTrip2Geojson = function(trip) { + if (trip == undefined) { + return Promise.resolve(undefined); + } Logger.log("About to pull location data for range " + moment.unix(trip.start_ts).toString() + " -> " + moment.unix(trip.end_ts).toString()); @@ -1181,6 +1188,10 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', return angular.isDefined(timeline.data.tripWrapperMap)? timeline.data.tripWrapperMap[tripId] : undefined; }; + timeline.getConfirmedTrip = function(tripId) { + return angular.isDefined(timeline.data.infScrollConfirmedTripMap)? timeline.data.infScrollConfirmedTripMap[tripId] : undefined; + }; + /* Let us assume that we have recieved a list of trips for that date from somewhere (either local usercache or the internet). Now, what do we need to process them? @@ -1262,6 +1273,16 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', }); } + timeline.setInfScrollConfirmedTripList = function(confirmedTripList) { + timeline.data.infScrollConfirmedTripList = confirmedTripList; + + timeline.data.infScrollConfirmedTripMap = {}; + + timeline.data.infScrollConfirmedTripList.forEach(function(trip, index, array) { + timeline.data.infScrollConfirmedTripMap[trip.id] = trip; + }); + } + // TODO: Should this be in the factory or in the scope? var generateDaySummary = function() { var dayMovingTime = 0; diff --git a/www/templates/diary/infinite_scroll_detail.html b/www/templates/diary/infinite_scroll_detail.html new file mode 100644 index 000000000..ccec070bb --- /dev/null +++ b/www/templates/diary/infinite_scroll_detail.html @@ -0,0 +1,47 @@ + + {{ tripgj.display_start_tim }} + + + + +
+ +
+
{{tripgj.display_start_time}}
+
{{tripgj.common.displayEarlierLater}}
+
{{tripgj.display_end_time}} +
+
+ +
+ +
+ + {{tripgj.start_display_name}} + +
+
+ + {{tripgj.end_display_name}} +
+
+
+
+ + +
+
+
+

{{'.distance'}}

{{tripgj.display_distance}} {{tripgj.display_distance_suffix}}
+

{{'.time'}}

{{tripgj.display_time}}
+
+
+
+ + +
+
+
diff --git a/www/templates/diary/trip-detail-popover.html b/www/templates/diary/trip-detail-popover.html deleted file mode 100644 index 7ed6e0189..000000000 --- a/www/templates/diary/trip-detail-popover.html +++ /dev/null @@ -1,41 +0,0 @@ - - -

Trip Details

- -
- -
-

{{currgj.display_date}}

-
-
-
-
-
{{currgj.display_start_time}}
-
{{currgj.display_end_time}} -
-
- -
-
- - {{currgj.start_display_name}} - -
-
- - {{currgj.end_display_name}} -
-
-
-
-
-
-

{{'.distance'}}

{{currgj.display_distance}} mi
-

{{'.time'}}

{{currgj.display_time}}
-
-
- -
-
From 7d98d5dcbaafce0ae449694f5aa7421d8d8a9d9e Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Fri, 29 Apr 2022 22:23:06 -0700 Subject: [PATCH 02/11] Add the labels to the detail screen So you don't have to see the trip and then go back to the list to verify Fortunately, this was already a directive, so the change largely involved adding the directive to the detail screen. Couple of modifications: - we allow the specification of a linkedid to make the DOM traversal simpler - we choose the survey opt in the detail screen as well - on `recomputeDisplayTrips`, we close the detail screen TODO: Might want to configure the wait to recompute since 30 secs is too large for the detail screen use case. https://github.com/e-mission/e-mission-docs/issues/111 --- www/js/diary/infinite_scroll_detail.js | 167 ++++++++++++++++++ www/js/survey/one-click-button.js | 29 ++- .../diary/infinite_scroll_detail.html | 10 ++ 3 files changed, 197 insertions(+), 9 deletions(-) create mode 100644 www/js/diary/infinite_scroll_detail.js diff --git a/www/js/diary/infinite_scroll_detail.js b/www/js/diary/infinite_scroll_detail.js new file mode 100644 index 000000000..554a94d2e --- /dev/null +++ b/www/js/diary/infinite_scroll_detail.js @@ -0,0 +1,167 @@ +'use strict'; +angular.module('emission.main.diary.infscrolldetail',['ui-leaflet', 'ng-walkthrough', + 'nvd3', 'emission.plugin.kvstore', + 'emission.services', + 'emission.config.imperial', + 'emission.plugin.logger', + 'emission.stats.clientstats', + 'emission.incident.posttrip.manual']) + +.controller("InfiniteDiaryDetailCtrl", function($scope, $rootScope, $window, $ionicPlatform, + $state, $stateParams, ClientStats, $ionicActionSheet, + leafletData, leafletMapEvents, nzTour, KVStore, + Logger, Timeline, DiaryHelper, SurveyOptions, Config, ImperialConfig, + CommHelper, PostTripManualMarker, $translate) { + console.log("controller InfiniteDiaryDetailCtrl called with params = "+ + JSON.stringify($stateParams)); + $scope.surveyOpt = SurveyOptions.MULTILABEL; + + $scope.mapCtrl = {}; + angular.extend($scope.mapCtrl, { + defaults : { + } + }); + + angular.extend($scope.mapCtrl.defaults, Config.getMapTiles()) + + var mapEvents = leafletMapEvents.getAvailableMapEvents(); + for (var k in mapEvents) { + var eventName = 'leafletDirectiveMap.infscroll-detail.' + mapEvents[k]; + $scope.$on(eventName, function(event, data){ + try { + console.log("in mapEvents, event = "+JSON.stringify(event.name)+ + " leafletEvent = "+JSON.stringify(data.leafletEvent.type)+ + " leafletObject = "+JSON.stringify(data.leafletObject.getBounds())); + } catch (e) { + if (e instanceof TypeError) { + console.log("in mapEvents, event = "+JSON.stringify(event.name)+ + " leafletEvent = "+JSON.stringify(data.leafletEvent.type)+ + " leafletObject is undefined"); + } else { + console.log(e); + } + } + $scope.eventDetected = event.name; + }); + } + + /* + leafletData.getMap('detail').then(function(map) { + map.on('touch', function(ev) { + alert("touch" + ev.latlng); // ev is an event object (MouseEvent in this case) + }); + }); + */ + + $scope.$on('leafletDirectiveMap.infscroll-detail.resize', function(event, data) { + console.log("diary/detail received resize event, invalidating map size"); + data.leafletObject.invalidateSize(); + }); + + $scope.refreshTiles = function() { + $scope.$broadcast('invalidateSize'); + }; + + $scope.trip = Timeline.getConfirmedTrip($stateParams.tripId); + Timeline.confirmedTrip2Geojson($scope.trip).then((tripgj) => { + $scope.$apply(() => { + $scope.tripgj = $scope.trip; + $scope.tripgj.data = tripgj; + $scope.tripgj.common = {}; + $scope.tripgj.common.earlierOrLater = ''; + $scope.tripgj.pointToLayer = DiaryHelper.pointFormat; + + if (!angular.isDefined($scope.trip) || !angular.isDefined($scope.tripgj)) { + console.log("Detail trip = "+$scope.trip+" tripgj = "+$scope.tripgj+" not defined, going back to the list view") + $state.go("root.main.inf_scroll"); + } + }); + }); + + $scope.recomputeDisplayTrips = function() { + console.log("Called inf scroll details.recomputeDisplayTrips"); + $state.go("root.main.inf_scroll"); + }; + + /* START: ng-walkthrough code */ + // Tour steps + var tour = { + config: { + mask: { + visibleOnNoTarget: true, + clickExit: true + }, + previousText: $translate.instant('tour-previous'), + nextText: $translate.instant('tour-next'), + finishText: $translate.instant('tour-finish') + }, + steps: [{ + target: '#detail', + content: $translate.instant('details.tour-detail-content') + }, { + target: '#sectionList', + content: $translate.instant('details.tour-sectionList-content') + }, { + target: '#sectionPct', + content: $translate.instant('details.tour-sectionPct-content') + }] + }; + + var startWalkthrough = function () { + nzTour.start(tour).then(function(result) { + Logger.log("detail walkthrough start completed, no error"); + }).catch(function(err) { + Logger.displayError("detail walkthrough start errored", err); + }); + }; + + + var checkDetailTutorialDone = function () { + var DETAIL_DONE_KEY = 'detail_tutorial_done'; + var detailTutorialDone = KVStore.getDirect(DETAIL_DONE_KEY); + if (!detailTutorialDone) { + startWalkthrough(); + KVStore.set(DETAIL_DONE_KEY, true); + } + }; + + $scope.startWalkthrough = function () { + startWalkthrough(); + } + + $scope.$on('$ionicView.afterEnter', function(ev) { + // Workaround from + // https://github.com/driftyco/ionic/issues/3433#issuecomment-195775629 + if(ev.targetScope !== $scope) + return; + checkDetailTutorialDone(); + }); + + $scope.$on('$ionicView.enter',function(){ + $scope.startTime = moment().utc() + ClientStats.addEvent(ClientStats.getStatKeys().EXPANDED_TRIP).then( + function() { + console.log("Added "+ClientStats.getStatKeys().EXPANDED_TRIP+" event"); + } + ); + }); + + $scope.$on('$ionicView.leave',function() { + var timeOnPage = moment().utc() - $scope.startTime; + ClientStats.addReading(ClientStats.getStatKeys().DIARY_TIME, timeOnPage); + }); + + $ionicPlatform.on("pause", function() { + if ($state.$current == "root.main.diary.detail") { + var timeOnPage = moment().utc() - $scope.startTime; + ClientStats.addReading(ClientStats.getStatKeys().DIARY_TIME, timeOnPage); + } + }) + + $ionicPlatform.on("resume", function() { + if ($state.$current == "root.main.diary.detail") { + $scope.startTime = moment().utc() + } + }) + /* END: ng-walkthrough code */ +}) diff --git a/www/js/survey/one-click-button.js b/www/js/survey/one-click-button.js index 79dbbc771..6cc68c2ef 100644 --- a/www/js/survey/one-click-button.js +++ b/www/js/survey/one-click-button.js @@ -3,6 +3,7 @@ angular.module('emission.survey.verifycheck', []) return { scope: { linkedtag: "@", + linkedid: "@", // specific ID, if present, simplifies the dom traversal logic }, controller: "OneClickButtonCtrl", templateUrl: 'templates/survey/one-click-button.html' @@ -10,15 +11,25 @@ angular.module('emission.survey.verifycheck', []) }) .controller("OneClickButtonCtrl", function($scope, $element, $attrs) { var findLinkedLabelScope = function() { - console.log("$element is ", $element, "linkedtag is ",$scope.linkedtag); - console.log("parent row is", $element.parents("ion-item")); - let rowElement = $element.parents("ion-item") - console.log("row Element is", rowElement); - let linkedlabel = rowElement.find($scope.linkedtag); - console.log("child linkedlabel is", linkedlabel); - let linkedlabelScope = angular.element(linkedlabel).isolateScope(); - console.log("linkedlabel scope is", linkedlabelScope); - return linkedlabelScope; + if ($scope.linkedid) { + let linkedsurveytag = angular.element(document.getElementById($scope.linkedid)); + console.log("matching linkedsurvey with id "+$scope.linkedid+" is ",linkedsurveytag); + let linkedlabel = linkedsurveytag.find($scope.linkedtag); + console.log("child linkedlabel is", linkedlabel); + let linkedlabelScope = angular.element(linkedlabel).isolateScope(); + console.log("linkedlabel scope is", linkedlabelScope); + return linkedlabelScope; + } else { + console.log("$element is ", $element, "linkedtag is ",$scope.linkedtag); + console.log("parent row is", $element.parents("ion-item")); + let rowElement = $element.parents("ion-item") + console.log("row Element is", rowElement); + let linkedlabel = rowElement.find($scope.linkedtag); + console.log("child linkedlabel is", linkedlabel); + let linkedlabelScope = angular.element(linkedlabel).isolateScope(); + console.log("linkedlabel scope is", linkedlabelScope); + return linkedlabelScope; + } }; $scope.verifyTrip = function() { diff --git a/www/templates/diary/infinite_scroll_detail.html b/www/templates/diary/infinite_scroll_detail.html index ccec070bb..0ddb66f0a 100644 --- a/www/templates/diary/infinite_scroll_detail.html +++ b/www/templates/diary/infinite_scroll_detail.html @@ -37,6 +37,16 @@

{{'.time'}}

{{tripgj.display_time}}
+
+ + + +
+
From 0eb72b1a597b7cac9e5083798641757bd27f326b Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Fri, 29 Apr 2022 22:54:49 -0700 Subject: [PATCH 03/11] Add the labels to the diary detail screen as well This is a pretty straightforward extension to 7d98d5dcbaafce0ae449694f5aa7421d8d8a9d9e --- www/js/diary/detail.js | 8 +++++++- www/templates/diary/detail.html | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/www/js/diary/detail.js b/www/js/diary/detail.js index 2da16e74e..cc1d94abf 100644 --- a/www/js/diary/detail.js +++ b/www/js/diary/detail.js @@ -10,10 +10,11 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', .controller("DiaryDetailCtrl", function($scope, $rootScope, $window, $ionicPlatform, $state, $stateParams, ClientStats, $ionicActionSheet, leafletData, leafletMapEvents, nzTour, KVStore, - Logger, Timeline, DiaryHelper, Config, ImperialConfig, + Logger, Timeline, DiaryHelper, SurveyOptions, Config, ImperialConfig, CommHelper, PostTripManualMarker, $translate) { console.log("controller DiaryDetailCtrl called with params = "+ JSON.stringify($stateParams)); + $scope.surveyOpt = SurveyOptions.MULTILABEL; $scope.mapCtrl = {}; angular.extend($scope.mapCtrl, { @@ -74,6 +75,11 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', }; }); + $scope.recomputeDisplayTrips = function() { + console.log("Called diary details.recomputeDisplayTrips"); + $state.go("root.main.diary"); + }; + if (!angular.isDefined($scope.trip) || !angular.isDefined($scope.tripgj)) { console.log("Detail trip not defined, going back to the list view") $state.go("root.main.diary"); diff --git a/www/templates/diary/detail.html b/www/templates/diary/detail.html index c64d582ae..38c44eef2 100644 --- a/www/templates/diary/detail.html +++ b/www/templates/diary/detail.html @@ -31,6 +31,16 @@ height="50%" width="100%" data-tap-disabled="true">
+
+ + + +
+

{{'.distance'}}

{{tripgj.display_distance}} {{tripgj.display_distance_suffix}}
From a2384211020a52befe290671121eea2f0da47e71 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Fri, 29 Apr 2022 23:07:08 -0700 Subject: [PATCH 04/11] Ensure that we show the detail date on the detail screen header Because it doesn't appear anywhere else --- www/js/diary/list.js | 1 + www/templates/diary/detail.html | 2 +- www/templates/diary/infinite_scroll_detail.html | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/www/js/diary/list.js b/www/js/diary/list.js index 474da7cb5..4fff5084d 100644 --- a/www/js/diary/list.js +++ b/www/js/diary/list.js @@ -167,6 +167,7 @@ angular.module('emission.main.diary.list',['ui-leaflet', tripgj.display_end_time = DiaryHelper.getLocalTimeString(tripgj.data.properties.end_local_dt); tripgj.display_distance = ImperialConfig.getFormattedDistance(tripgj.data.properties.distance); tripgj.display_distance_suffix = ImperialConfig.getDistanceSuffix; + tripgj.display_date = moment(tripgj.data.properties.start_ts * 1000).format('ddd DD MMM YY'); tripgj.display_time = DiaryHelper.getFormattedTimeRange(tripgj.data.properties.start_ts, tripgj.data.properties.end_ts); tripgj.isDraft = DiaryHelper.isDraft(tripgj); diff --git a/www/templates/diary/detail.html b/www/templates/diary/detail.html index 38c44eef2..63192c86e 100644 --- a/www/templates/diary/detail.html +++ b/www/templates/diary/detail.html @@ -1,5 +1,5 @@ - {{ tripgj.display_start_tim }} + {{ tripgj.display_date }} diff --git a/www/templates/diary/infinite_scroll_detail.html b/www/templates/diary/infinite_scroll_detail.html index 0ddb66f0a..ed56070fa 100644 --- a/www/templates/diary/infinite_scroll_detail.html +++ b/www/templates/diary/infinite_scroll_detail.html @@ -1,5 +1,5 @@ - {{ tripgj.display_start_tim }} + {{ tripgj.display_date }} From bddce886f2f1d3443fab064167a1c139d18438cb Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 30 Apr 2022 09:41:18 -0700 Subject: [PATCH 05/11] Support a reconfigurable recompute delay instead of the hardcoded 30 secs Summary of changes: - create a new attribute called `recomputedelay` - plumb it through `linkedsurvey` -> `multi-label-ui` directives - set it to a default of thirty secs if it doesn't exist, and convert secs -> ms - pass it through from the controller to the service - use it in the service in place of the hardcoded value --- www/js/survey/multilabel/multi-label-ui.js | 20 +++++++++++++++++--- www/js/survey/survey.js | 1 + www/templates/survey/wrapper.html | 4 ++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/www/js/survey/multilabel/multi-label-ui.js b/www/js/survey/multilabel/multi-label-ui.js index 59dfabfb7..bbf513b07 100644 --- a/www/js/survey/multilabel/multi-label-ui.js +++ b/www/js/survey/multilabel/multi-label-ui.js @@ -21,6 +21,7 @@ angular.module('emission.survey.multilabel.buttons', scope: { trip: "=", unifiedConfirmsResults: "=", + recomputedelay: "@", }, controller: "MultiLabelCtrl", templateUrl: 'templates/survey/multilabel/multi-label-ui.html' @@ -98,6 +99,15 @@ angular.module('emission.survey.multilabel.buttons', * END: Required external interface for all label directives */ + if (angular.isDefined($scope.recomputedelay)) { + $scope.recomputedelay = $scope.recomputedelay * 1000; + } else { + let THIRTY_SECS = 30 * 1000; + $scope.recomputedelay = THIRTY_SECS; + } + + MultiLabelService.setRecomputeDelay($scope.recomputedelay); + $scope.fillUserInputsGeojson = function() { console.log("Checking to fill user inputs for " +$scope.trip.display_start_time+" -> "+$scope.trip.display_end_time); @@ -374,6 +384,11 @@ angular.module('emission.survey.multilabel.buttons', * If the trip has all green labels, the button should be disabled because everything has already been verified. * If the trip has all red labels or a mix of red and green, the button should be disabled because we need more detailed user input. */ + + mls.setRecomputeDelay = function(rd) { + mls.recomputedelay = rd; + } + mls.updateVerifiability = function(trip) { var allGreen = true; var someYellow = false; @@ -400,15 +415,14 @@ angular.module('emission.survey.multilabel.buttons', // is going to modify it further trip.waitingForMod = true; let currTimeoutPromise = trip.timeoutPromise; - let THIRTY_SECS = 30 * 1000; - Logger.log("trip starting at "+trip.start_fmt_time+": creating new timeout"); + Logger.log("trip starting at "+trip.start_fmt_time+": creating new timeout of "+mls.recomputedelay); trip.timeoutPromise = $timeout(function() { Logger.log("trip starting at "+trip.start_fmt_time+": executing recompute"); trip.waitingForMod = false; trip.timeoutPromise = undefined; console.log("Recomputing display trips on ", viewScope); viewScope.recomputeDisplayTrips(); - }, THIRTY_SECS); + }, mls.recomputedelay); Logger.log("trip starting at "+trip.start_fmt_time+": cancelling existing timeout "+currTimeoutPromise); $timeout.cancel(currTimeoutPromise); } diff --git a/www/js/survey/survey.js b/www/js/survey/survey.js index 4ee6a604f..3c9b40291 100644 --- a/www/js/survey/survey.js +++ b/www/js/survey/survey.js @@ -23,6 +23,7 @@ angular.module('emission.survey', [ scope: { elementTag:"@", trip: "=", + recomputedelay: "@", }, templateUrl: "templates/survey/wrapper.html", }; diff --git a/www/templates/survey/wrapper.html b/www/templates/survey/wrapper.html index eb3a522cb..948ec9a36 100644 --- a/www/templates/survey/wrapper.html +++ b/www/templates/survey/wrapper.html @@ -1,2 +1,2 @@ - - + + From ba1fa7a62742477da6e466dc9b961825f3af9ac8 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 30 Apr 2022 09:45:26 -0700 Subject: [PATCH 06/11] Automatically close the detail screen once we have the labels Summary of changes: - read in the filter checks for the survey option - run all of them and go back to the list view if they are false (e.g. filter is false; would not be displayed) - change the recompute delay to 5 secs for the detail screens so the screen disappears fairly quickly Note that the check in the diary is fairly hacky since the trip doesn't have all the information required for the toLabelCheck and it always succeeds --- www/js/diary/detail.js | 18 ++++++++++++++++-- www/js/diary/infinite_scroll_detail.js | 12 ++++++++++-- www/templates/diary/detail.html | 4 +++- .../diary/infinite_scroll_detail.html | 3 ++- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/www/js/diary/detail.js b/www/js/diary/detail.js index cc1d94abf..0db872e28 100644 --- a/www/js/diary/detail.js +++ b/www/js/diary/detail.js @@ -7,7 +7,7 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', 'emission.stats.clientstats', 'emission.incident.posttrip.manual']) -.controller("DiaryDetailCtrl", function($scope, $rootScope, $window, $ionicPlatform, +.controller("DiaryDetailCtrl", function($scope, $rootScope, $window, $injector, $ionicPlatform, $state, $stateParams, ClientStats, $ionicActionSheet, leafletData, leafletMapEvents, nzTour, KVStore, Logger, Timeline, DiaryHelper, SurveyOptions, Config, ImperialConfig, @@ -15,6 +15,8 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', console.log("controller DiaryDetailCtrl called with params = "+ JSON.stringify($stateParams)); $scope.surveyOpt = SurveyOptions.MULTILABEL; + $scope.tripFilterFactory = $injector.get($scope.surveyOpt.filter); + $scope.filterInputs = $scope.tripFilterFactory.configuredFilters; $scope.mapCtrl = {}; angular.extend($scope.mapCtrl, { @@ -77,7 +79,19 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', $scope.recomputeDisplayTrips = function() { console.log("Called diary details.recomputeDisplayTrips"); - $state.go("root.main.diary"); + // Let's copy over the userInput to the field expected by the checks (user_input) + // We definitely need to unify this ASAP + $scope.tripgj.user_input = $scope.tripgj.userInput; + const filterMap = $scope.filterInputs.map((f) => f.filter($scope.tripgj)); + // again, we cannot use both filters in the detail screen because the trip + // version of in the list view doesn't have the expectation value filled + // out. We really need to unify ASAP! + console.log("filterMap = "+filterMap+" we will only use the second (unlabeled check)"); + // if the trip was going to stay (not be filtered), we should not go back to the scroll list + // TODO: Unify with infinite scroll and remove this hack + if (!filterMap[1]) { + $state.go("root.main.diary"); + } }; if (!angular.isDefined($scope.trip) || !angular.isDefined($scope.tripgj)) { diff --git a/www/js/diary/infinite_scroll_detail.js b/www/js/diary/infinite_scroll_detail.js index 554a94d2e..317534cd6 100644 --- a/www/js/diary/infinite_scroll_detail.js +++ b/www/js/diary/infinite_scroll_detail.js @@ -7,7 +7,7 @@ angular.module('emission.main.diary.infscrolldetail',['ui-leaflet', 'ng-walkthro 'emission.stats.clientstats', 'emission.incident.posttrip.manual']) -.controller("InfiniteDiaryDetailCtrl", function($scope, $rootScope, $window, $ionicPlatform, +.controller("InfiniteDiaryDetailCtrl", function($scope, $rootScope, $injector, $window, $ionicPlatform, $state, $stateParams, ClientStats, $ionicActionSheet, leafletData, leafletMapEvents, nzTour, KVStore, Logger, Timeline, DiaryHelper, SurveyOptions, Config, ImperialConfig, @@ -15,6 +15,8 @@ angular.module('emission.main.diary.infscrolldetail',['ui-leaflet', 'ng-walkthro console.log("controller InfiniteDiaryDetailCtrl called with params = "+ JSON.stringify($stateParams)); $scope.surveyOpt = SurveyOptions.MULTILABEL; + $scope.tripFilterFactory = $injector.get($scope.surveyOpt.filter); + $scope.filterInputs = $scope.tripFilterFactory.configuredFilters; $scope.mapCtrl = {}; angular.extend($scope.mapCtrl, { @@ -80,7 +82,13 @@ angular.module('emission.main.diary.infscrolldetail',['ui-leaflet', 'ng-walkthro $scope.recomputeDisplayTrips = function() { console.log("Called inf scroll details.recomputeDisplayTrips"); - $state.go("root.main.inf_scroll"); + const filterMap = $scope.filterInputs.map((f) => f.filter($scope.trip)); + const filterValue = filterMap.reduce((a, b) => a || b, false); + console.log("filterMap = "+filterMap+" value = "+filterValue); + // if the trip was going to stay (not be filtered), we should not go back to the scroll list + if (!filterValue) { + $state.go("root.main.inf_scroll"); + } }; /* START: ng-walkthrough code */ diff --git a/www/templates/diary/detail.html b/www/templates/diary/detail.html index 63192c86e..043120fe8 100644 --- a/www/templates/diary/detail.html +++ b/www/templates/diary/detail.html @@ -36,7 +36,9 @@ filled in outside the directive to filter properly e.g. https://github.com/e-mission/e-mission-docs/issues/674#issuecomment-933083710 --> - +
diff --git a/www/templates/diary/infinite_scroll_detail.html b/www/templates/diary/infinite_scroll_detail.html index ed56070fa..604392799 100644 --- a/www/templates/diary/infinite_scroll_detail.html +++ b/www/templates/diary/infinite_scroll_detail.html @@ -42,7 +42,8 @@ filled in outside the directive to filter properly e.g. https://github.com/e-mission/e-mission-docs/issues/674#issuecomment-933083710 --> - +
From 6b85be332b53ea998ccc06a99c04233d8c65d202 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 30 Apr 2022 09:54:22 -0700 Subject: [PATCH 07/11] Comment out the labels in the detail screen Since setting them causes the maps in the list view to break: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1114013338 --- www/templates/diary/detail.html | 2 +- www/templates/diary/infinite_scroll_detail.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/templates/diary/detail.html b/www/templates/diary/detail.html index 043120fe8..1a5b02aba 100644 --- a/www/templates/diary/detail.html +++ b/www/templates/diary/detail.html @@ -35,12 +35,12 @@ + -->
diff --git a/www/templates/diary/infinite_scroll_detail.html b/www/templates/diary/infinite_scroll_detail.html index 604392799..99f675988 100644 --- a/www/templates/diary/infinite_scroll_detail.html +++ b/www/templates/diary/infinite_scroll_detail.html @@ -41,11 +41,11 @@ + -->
From 2b724685b9de4b98d139eefa85c0fcc1bc3bf4b2 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 1 May 2022 09:14:00 -0700 Subject: [PATCH 08/11] Hack to fix the grayed out map issues for once and for all https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1114271894 Principled fix is being tracked in: https://github.com/e-mission/e-mission-docs/issues/726 Change: - If we receive an invalidateSize when we are hidden (size == 0), then we ignore it instead of moving the view to match --- www/manual_lib/leaflet/leaflet-src.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/www/manual_lib/leaflet/leaflet-src.js b/www/manual_lib/leaflet/leaflet-src.js index 550ff950f..9a061ac9d 100644 --- a/www/manual_lib/leaflet/leaflet-src.js +++ b/www/manual_lib/leaflet/leaflet-src.js @@ -3523,6 +3523,11 @@ // pan by default. invalidateSize: function (options) { if (!this._loaded) { return this; } + // Hack added by shankari to fix + // https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1114128934 + // console.log("LEAFLET: clientWidth = "+this._container.clientWidth+" clientHeight = "+this._container.clientHeight + " combined: "+ (this._container.clientWidth && this._container._clientHeight)); + if ((this._container.clientWidth || 0) == 0 && (this._container.clientHeight || 0) == 0) { return this; } + options = extend({ animate: false, @@ -3537,7 +3542,7 @@ oldCenter = oldSize.divideBy(2).round(), newCenter = newSize.divideBy(2).round(), offset = oldCenter.subtract(newCenter); - + if (!offset.x && !offset.y) { return this; } if (options.animate && options.pan) { From 317e297617661e158fbb6446d26ba35d2af9fe38 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 1 May 2022 09:15:53 -0700 Subject: [PATCH 09/11] Fix the "recompute delay" check Since an unspecified value returns an empty string ("") and is not undefined. --- www/js/survey/multilabel/multi-label-ui.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/js/survey/multilabel/multi-label-ui.js b/www/js/survey/multilabel/multi-label-ui.js index bbf513b07..530a3d066 100644 --- a/www/js/survey/multilabel/multi-label-ui.js +++ b/www/js/survey/multilabel/multi-label-ui.js @@ -99,11 +99,11 @@ angular.module('emission.survey.multilabel.buttons', * END: Required external interface for all label directives */ - if (angular.isDefined($scope.recomputedelay)) { - $scope.recomputedelay = $scope.recomputedelay * 1000; - } else { + if ($scope.recomputedelay == "") { let THIRTY_SECS = 30 * 1000; $scope.recomputedelay = THIRTY_SECS; + } else { + $scope.recomputedelay = $scope.recomputedelay * 1000; } MultiLabelService.setRecomputeDelay($scope.recomputedelay); From 85a94ff933eae8c2c3399dc8e04bc48def22260b Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 1 May 2022 09:20:46 -0700 Subject: [PATCH 10/11] Re-enable the in-detail labels How that we have finally figured out and fixed the broken maps issue Originally commented out due to: https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1114013338 That issue was fixed in 2b724685b9de4b98d139eefa85c0fcc1bc3bf4b2 (https://github.com/e-mission/e-mission-docs/issues/111#issuecomment-1114271894) So we can now restore this useful fix + reduce the delay to help with impatient people :) --- www/templates/diary/detail.html | 4 ++-- www/templates/diary/infinite_scroll_detail.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/templates/diary/detail.html b/www/templates/diary/detail.html index 1a5b02aba..80db0bc99 100644 --- a/www/templates/diary/detail.html +++ b/www/templates/diary/detail.html @@ -35,12 +35,12 @@ - -->
diff --git a/www/templates/diary/infinite_scroll_detail.html b/www/templates/diary/infinite_scroll_detail.html index 99f675988..93e7b17c1 100644 --- a/www/templates/diary/infinite_scroll_detail.html +++ b/www/templates/diary/infinite_scroll_detail.html @@ -41,11 +41,11 @@ + recomputedelay=2 class="col-90" trip="trip"> - -->
From c302d65904662253214dce9c5ea966060b7e9a65 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 1 May 2022 17:36:12 -0700 Subject: [PATCH 11/11] Ensure that labels set in the diary screen are reflected in the label screen and vice versa Since the diary and label screens are not currently unified, values set in one screen are not reflected in the other. This is confusing if people switch between the two. Hacking around this by copying the user inputs from the diary to the list and vice versa after enter. Changes: - new function to copy input if newer - add a write_ts to modified user inputs to track which is newer - call the new function for all trips in the diary and for trips that are loaded in the label (since the label has more trips than the diary). --- www/js/diary/infinite_scroll_list.js | 15 +++++++++++++ www/js/diary/list.js | 21 ++++++++++++++++++ www/js/survey/multilabel/multi-label-ui.js | 25 ++++++++++++++++++---- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/www/js/diary/infinite_scroll_list.js b/www/js/diary/infinite_scroll_list.js index 4c4257fb4..c4c3e22e9 100644 --- a/www/js/diary/infinite_scroll_list.js +++ b/www/js/diary/infinite_scroll_list.js @@ -522,6 +522,21 @@ angular.module('emission.main.diary.infscrolllist',['ui-leaflet', ClientStats.addEvent(ClientStats.getStatKeys().CHECKED_INF_SCROLL).then(function() { console.log("Added "+ClientStats.getStatKeys().CHECKED_INF_SCROLL+" event"); }); + $scope.$apply(() => { + if ($scope.data && $scope.data.allTrips) { + $scope.data.allTrips.forEach(function(tripgj, tripIndex, array) { + let tripFromDiary = Timeline.getTripWrapper(tripgj.id); + // Since the label screen has trips from multiple days, we + // may not always find a matching trip in the diary + if (tripFromDiary) { + $scope.labelPopulateFactory.copyInputIfNewer(tripFromDiary, tripgj); + } + }); + $scope.recomputeDisplayTrips(); + } else { + console.log("No trips loaded yet, no inputs to copy over"); + } + }); if($rootScope.barDetail){ readAndUpdateForDay($rootScope.barDetailDate); $rootScope.barDetail = false; diff --git a/www/js/diary/list.js b/www/js/diary/list.js index 4fff5084d..c0f6fc86b 100644 --- a/www/js/diary/list.js +++ b/www/js/diary/list.js @@ -474,6 +474,27 @@ angular.module('emission.main.diary.list',['ui-leaflet', ClientStats.addEvent(ClientStats.getStatKeys().CHECKED_DIARY).then(function() { console.log("Added "+ClientStats.getStatKeys().CHECKED_DIARY+" event"); }); + /* + In case we have set the labels in the label screen, we want them to + show up when we come to this screen. It is really hard to do this + using the original code, because the unification is not complete, and + the code to read the manual inputs is completely different. + Instead, let's find the corresponding trip from the label view and + copy over the `userInput` (and potentially the `user_input`) values over + */ + $scope.$apply(() => { + if ($scope.data && $scope.data.currDayTripWrappers) { + $scope.data.currDayTripWrappers.forEach(function(tripgj, tripIndex, array) { + let tripFromLabel = Timeline.getConfirmedTrip(tripgj.data.id); + // Should we just copy over the entry from the label screen + // NO, what if the user changed the labels here, then went to + // the profile and came back. Don't want to lose the upgraded entries + $scope.labelPopulateFactory.copyInputIfNewer(tripFromLabel, tripgj); + }); + } else { + console.log("No trips loaded yet, no inputs to copy over"); + } + }); if($rootScope.barDetail){ readAndUpdateForDay($rootScope.barDetailDate); $rootScope.barDetail = false; diff --git a/www/js/survey/multilabel/multi-label-ui.js b/www/js/survey/multilabel/multi-label-ui.js index 530a3d066..68f5e2385 100644 --- a/www/js/survey/multilabel/multi-label-ui.js +++ b/www/js/survey/multilabel/multi-label-ui.js @@ -216,11 +216,14 @@ angular.module('emission.survey.multilabel.buttons', $window.cordova.plugins.BEMUserCache.putMessage(ConfirmHelper.inputDetails[inputType].key, $scope.draftInput).then(function () { $scope.$apply(function() { if (isOther) { - tripToUpdate.userInput[inputType] = ConfirmHelper.getFakeEntry(input.value); - $scope.inputParams[inputType].options.push(tripToUpdate.userInput[inputType]); - $scope.inputParams[inputType].value2entry[input.value] = tripToUpdate.userInput[inputType]; + let fakeEntry = ConfirmHelper.getFakeEntry(input.value); + $scope.inputParams[inputType].options.push(fakeEntry); + $scope.inputParams[inputType].value2entry[input.value] = fakeEntry; + tripToUpdate.userInput[inputType] = angular.copy(fakeEntry); + tripToUpdate.userInput[inputType].write_ts = Date.now(); } else { - tripToUpdate.userInput[inputType] = $scope.inputParams[inputType].value2entry[input.value]; + tripToUpdate.userInput[inputType] = angular.copy($scope.inputParams[inputType].value2entry[input.value]); + tripToUpdate.userInput[inputType].write_ts = Date.now(); } let viewScope = findViewScope(); MultiLabelService.updateTripProperties(tripToUpdate, viewScope); // Redo our inferences, filters, etc. based on this new information @@ -305,6 +308,20 @@ angular.module('emission.survey.multilabel.buttons', } } + /* + * This is a HACK to work around the issue that the label screen and diary + * screen are not unified. We should remove this, and the timestamp in the + * userInput field when we do. + */ + mls.copyInputIfNewer = function(potentiallyModifiedTrip, originalTrip) { + ConfirmHelper.INPUTS.forEach(function(item, index) { + let pmInput = potentiallyModifiedTrip.userInput; + let origInput = originalTrip.userInput; + if (((pmInput[item] || {}).write_ts || 0) > ((origInput[item] || {}).write_ts || 0)) { + origInput[item] = pmInput[item]; + } + }); + } mls.updateTripProperties = function(trip, viewScope) { mls.inferFinalLabels(trip);