From c240f7926b4705ae6d0ac0d6d9f77fce46f78283 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 11 Sep 2023 16:53:42 -0600 Subject: [PATCH 01/30] convert the log page to React for react routing, we need to convert this page and the sync settings page maintain access for now by routing to a React page with angular routing using AlertBars for the error messages first draft: scrolling the FlashList is broken and the data doesn't load unless you manually tap refresh --- www/js/control/LogPage.tsx | 155 ++++++++++++++++++++++++++++++++++ www/js/main.js | 2 +- www/js/recent.js | 70 +-------------- www/templates/recent/log.html | 21 ----- 4 files changed, 159 insertions(+), 89 deletions(-) create mode 100644 www/js/control/LogPage.tsx delete mode 100644 www/templates/recent/log.html diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx new file mode 100644 index 000000000..4daec09ed --- /dev/null +++ b/www/js/control/LogPage.tsx @@ -0,0 +1,155 @@ +import React, { useState, useMemo, useEffect } from "react"; +import { View, StyleSheet, ScrollView } from "react-native"; +import { useTheme, Text, Appbar, IconButton } from "react-native-paper"; +import { angularize, getAngularService } from "../angular-react-helper"; +import { useTranslation } from "react-i18next"; +import { FlashList } from '@shopify/flash-list'; +import useAppConfig from "../useAppConfig"; +import moment from "moment"; +import AlertBar from "./AlertBar"; + +type loadStats = { currentStart: number, gotMaxIndex: boolean, reachedEnd: boolean }; + +//any pure functions can go outside +const LogPage = () => { + // anything that mutates must go in --- depend on props or state... + const { t } = useTranslation(); + const { colors } = useTheme(); + const EmailHelper = getAngularService('EmailHelper'); + const $state = getAngularService('$state'); + const { appConfig, loading } = useAppConfig(); + + const [ loadStats, setLoadStats ] = useState(); + const [ entries, setEntries ] = useState([]); + const [ maxErrorVis, setMaxErrorVis ] = useState(false); + const [ logErrorVis, setLogErrorVis ] = useState(false); + + const [ maxMessage, setMaxMessage ] = useState(""); + const [ logMessage, setLogMessage ] = useState(""); + + var RETRIEVE_COUNT = 100; + + useEffect(() => { + if(!loading) { + refreshEntries(); + } + }, [appConfig]); + + const refreshEntries = function() { + window?.Logger.getMaxIndex().then(function(maxIndex) { + console.log("maxIndex = "+maxIndex); + let tempStats = {} as loadStats; + tempStats.currentStart = maxIndex; + tempStats.gotMaxIndex = true; + tempStats.reachedEnd = false; + setLoadStats(tempStats); + setEntries([]); + addEntries(); + }, function(error) { + let errorString = "While getting max index "+JSON.stringify(error, null, 2); + console.log(errorString); + setMaxMessage(errorString); + setMaxErrorVis(true); + }) + } + + const moreDataCanBeLoaded = useMemo(() => { + return loadStats?.gotMaxIndex && loadStats?.reachedEnd; + }, [loadStats]) + + const clear = function() { + window?.Logger.clearAll(); + window?.Logger.log(window.Logger.LEVEL_INFO, "Finished clearing entries from unified log"); + refreshEntries(); + } + + async function addEntries() { + console.log("calling addEntries"); + window.Logger.getMessagesFromIndex(loadStats?.currentStart, RETRIEVE_COUNT) + .then(function(entryList) { + processEntries(entryList); + console.log("entry list size = "+ entries.length); + //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? + }, function(error) { + let errStr = "While getting messages from the log "+JSON.stringify(error, null, 2); + console.log(errStr); + setLogMessage(errStr); + setLogErrorVis(true); + //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? + }) + } + + const processEntries = function(entryList) { + let tempEntries = []; + let tempLoadStats = {...loadStats}; + entryList.forEach(e => { + e.fmt_time = moment.unix(e.ts).format("llll"); + tempEntries.push(e); + }); + if (entryList.length == 0) { + console.log("Reached the end of the scrolling"); + tempLoadStats.reachedEnd = true; + } else { + tempLoadStats.currentStart = entryList[entryList.length-1].ID; + console.log("new start index = "+loadStats.currentStart); + } + setEntries([...entries].concat(tempEntries)); //push the new entries onto the list + setLoadStats(tempLoadStats); + } + + const emailLog = function () { + EmailHelper.sendEmail("loggerDB"); + } + + const separator = () => + const logItem = ({item: logItem}) => + {logItem.fmt_time} + {logItem.ID + "|" + logItem.level + "|" + logItem.message} + + + return ( + <> + {/* //appbar across the top with back to profile and "log" page title */} + + {$state.go("root.main.control");}}/> + + + + {/* //row of buttons to refresh, delete, or email */} + + refreshEntries()}/> + clear()}/> + emailLog()}/> + + + {/* //list of dates and times, each with some data associated */} + + item.ID} + ItemSeparatorComponent={separator} + onScroll={e => {if(moreDataCanBeLoaded){addEntries()}}}/> + + + + + + ); +}; +const styles = StyleSheet.create({ + date: (surfaceColor) => ({ + backgroundColor: surfaceColor, + }), + details: { + fontFamily: "monospace", + }, + entry: (surfaceColor) => ({ + backgroundColor: surfaceColor, + marginLeft: 5, + }), + }); + +angularize(LogPage, 'LogPage', 'emission.main.log.logPage'); +export default LogPage; \ No newline at end of file diff --git a/www/js/main.js b/www/js/main.js index 79bfdee0d..93b1c4abc 100644 --- a/www/js/main.js +++ b/www/js/main.js @@ -47,7 +47,7 @@ angular.module('emission.main', ['emission.main.diary', url: '/log', views: { 'main-control': { - templateUrl: 'templates/recent/log.html', + template: ``, controller: 'logCtrl' } } diff --git a/www/js/recent.js b/www/js/recent.js index 5fbfbf66c..0656d3463 100644 --- a/www/js/recent.js +++ b/www/js/recent.js @@ -1,72 +1,8 @@ -angular.module('emission.main.recent', ['emission.services']) +import LogPage from './control/LogPage'; +angular.module('emission.main.recent', ['emission.services', LogPage.module]) .controller('logCtrl', function(ControlHelper, $scope, EmailHelper) { - console.log("Launching logCtr"); - var RETRIEVE_COUNT = 100; - $scope.logCtrl = {}; - - $scope.refreshEntries = function() { - window.Logger.getMaxIndex().then(function(maxIndex) { - console.log("maxIndex = "+maxIndex); - $scope.logCtrl.currentStart = maxIndex; - $scope.logCtrl.gotMaxIndex = true; - $scope.logCtrl.reachedEnd = false; - $scope.entries = []; - $scope.addEntries(); - }, function (e) { - var errStr = "While getting max index "+JSON.stringify(e, null, 2); - console.log(errStr); - alert(errStr); - }); - } - - $scope.moreDataCanBeLoaded = function() { - return $scope.logCtrl.gotMaxIndex && !($scope.logCtrl.reachedEnd); - } - - $scope.clear = function() { - window.Logger.clearAll(); - window.Logger.log(window.Logger.LEVEL_INFO, "Finished clearing entries from unified log"); - $scope.refreshEntries(); - } - - $scope.addEntries = function() { - console.log("calling addEntries"); - window.Logger.getMessagesFromIndex($scope.logCtrl.currentStart, RETRIEVE_COUNT) - .then(function(entryList) { - $scope.$apply($scope.processEntries(entryList)); - console.log("entry list size = "+$scope.entries.length); - console.log("Broadcasting infinite scroll complete"); - $scope.$broadcast('scroll.infiniteScrollComplete') - }, function(e) { - var errStr = "While getting messages from the log "+JSON.stringify(e, null, 2); - console.log(errStr); - alert(errStr); - $scope.$broadcast('scroll.infiniteScrollComplete') - } - ) - } - - $scope.processEntries = function(entryList) { - for (let i = 0; i < entryList.length; i++) { - var currEntry = entryList[i]; - currEntry.fmt_time = moment.unix(currEntry.ts).format("llll"); - $scope.entries.push(currEntry); - } - if (entryList.length == 0) { - console.log("Reached the end of the scrolling"); - $scope.logCtrl.reachedEnd = true; - } else { - $scope.logCtrl.currentStart = entryList[entryList.length-1].ID - console.log("new start index = "+$scope.logCtrl.currentStart); - } - } - - $scope.emailLog = function () { - EmailHelper.sendEmail("loggerDB"); - } - - $scope.refreshEntries(); + //can remove when we have react routing! }) .controller('sensedDataCtrl', function($scope, $ionicActionSheet, EmailHelper) { diff --git a/www/templates/recent/log.html b/www/templates/recent/log.html deleted file mode 100644 index 455294705..000000000 --- a/www/templates/recent/log.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - -
- - - -
- - -
{{entry.fmt_time}}
-
{{entry.ID}} | {{entry.level}} | {{entry.message}}
-
- -
-
-
From c8ceafee6327b9edc75a52b279281a2fb663bd90 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 10:22:58 -0600 Subject: [PATCH 02/30] refactor dialogStyle and create Action Menu instead of making dialogStyle a parameter, export and import the stylesheet where it is used, to maintain consistency and limit parameters Needed something similar to some of the dialogs in ProfileSettings for the SyncLogs -- so create a component `ActionMenu` to show the list of options, replacing the two hard-coded modals (carbon and force state) with an instance of ActionMenu --- www/js/components/ActionMenu.tsx | 41 +++++++++++++ www/js/control/AppStatusModal.tsx | 5 +- www/js/control/PopOpCode.jsx | 3 +- www/js/control/PrivacyPolicyModal.tsx | 5 +- www/js/control/ProfileSettings.jsx | 87 ++++++++------------------- www/js/diary/LabelTab.tsx | 1 - 6 files changed, 74 insertions(+), 68 deletions(-) create mode 100644 www/js/components/ActionMenu.tsx diff --git a/www/js/components/ActionMenu.tsx b/www/js/components/ActionMenu.tsx new file mode 100644 index 000000000..32546f3a7 --- /dev/null +++ b/www/js/components/ActionMenu.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { Modal } from "react-native"; +import { Dialog, Button, useTheme } from "react-native-paper"; +import { useTranslation } from "react-i18next"; +import { settingStyles } from "../control/ProfileSettings"; + +const ActionMenu = ({vis, setVis, actionSet, onAction, onExit}) => { + + const { t } = useTranslation(); + const { colors } = useTheme(); + + return ( + setVis(false)} + transparent={true}> + setVis(false)} + style={settingStyles.dialog(colors.elevation.level3)}> + {"Force State"} + + {actionSet.map((e) => + + )} + + + + + + + ) +} + +export default ActionMenu; \ No newline at end of file diff --git a/www/js/control/AppStatusModal.tsx b/www/js/control/AppStatusModal.tsx index 64c63f720..3f696adcc 100644 --- a/www/js/control/AppStatusModal.tsx +++ b/www/js/control/AppStatusModal.tsx @@ -8,8 +8,9 @@ import useAppConfig from "../useAppConfig"; import useAppStateChange from "../useAppStateChange"; import ExplainPermissions from "../appstatus/ExplainPermissions"; import AlertBar from "./AlertBar"; +import { settingStyles } from "./ProfileSettings"; -const AppStatusModal = ({permitVis, setPermitVis, dialogStyle, settingsScope}) => { +const AppStatusModal = ({permitVis, setPermitVis, settingsScope}) => { const { t } = useTranslation(); const { colors } = useTheme(); const { appConfig, loading } = useAppConfig(); @@ -412,7 +413,7 @@ const AppStatusModal = ({permitVis, setPermitVis, dialogStyle, settingsScope}) = setPermitVis(false)} transparent={true}> setPermitVis(false)} - style={dialogStyle}> + style={settingStyles.dialog(colors.elevation.level3)}> {t('consent.permissions')} diff --git a/www/js/control/PopOpCode.jsx b/www/js/control/PopOpCode.jsx index 8325b98c7..721b3d511 100644 --- a/www/js/control/PopOpCode.jsx +++ b/www/js/control/PopOpCode.jsx @@ -4,6 +4,7 @@ import { Button, Text, IconButton, Dialog, useTheme } from 'react-native-paper'; import { useTranslation } from "react-i18next"; import QrCode from "../components/QrCode"; import AlertBar from "./AlertBar"; +import { settingStyles } from "./ProfileSettings"; const PopOpCode = ({visibilityValue, tokenURL, action, setVis, dialogStyle}) => { const { t } = useTranslation(); @@ -31,7 +32,7 @@ const PopOpCode = ({visibilityValue, tokenURL, action, setVis, dialogStyle}) => transparent={true}> setVis(false)} - style={dialogStyle}> + style={settingStyles.dialog(colors.elevation.level3)}> {t("general-settings.qrcode")} {t("general-settings.qrcode-share-title")} diff --git a/www/js/control/PrivacyPolicyModal.tsx b/www/js/control/PrivacyPolicyModal.tsx index 7451581a6..9a0b06d07 100644 --- a/www/js/control/PrivacyPolicyModal.tsx +++ b/www/js/control/PrivacyPolicyModal.tsx @@ -4,8 +4,9 @@ import { Dialog, Button, useTheme } from 'react-native-paper'; import { useTranslation } from "react-i18next"; import useAppConfig from "../useAppConfig"; import i18next from "i18next"; +import { settingStyles } from "./ProfileSettings"; -const PrivacyPolicyModal = ({ privacyVis, setPrivacyVis, dialogStyle }) => { +const PrivacyPolicyModal = ({ privacyVis, setPrivacyVis }) => { const { t } = useTranslation(); const { height: windowHeight } = useWindowDimensions(); const { colors } = useTheme(); @@ -40,7 +41,7 @@ const PrivacyPolicyModal = ({ privacyVis, setPrivacyVis, dialogStyle }) => { setPrivacyVis(false)} transparent={true}> setPrivacyVis(false)} - style={dialogStyle}> + style={settingStyles.dialog(colors.elevation.level3)}> {t('consent-text.title')} diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index e3dab82e3..c59c1d4d7 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -14,6 +14,7 @@ import AlertBar from "./AlertBar"; import DataDatePicker from "./DataDatePicker"; import AppStatusModal from "./AppStatusModal"; import PrivacyPolicyModal from "./PrivacyPolicyModal"; +import ActionMenu from "../components/ActionMenu"; let controlUpdateCompleteListenerRegistered = false; @@ -458,6 +459,17 @@ const ProfileSettings = () => { }); } + const onSelectState = function(stateObject) { + ControlCollectionHelper.forceTransition(stateObject.transition); + } + + const onSelectCarbon = function(carbonObject) { + console.log("changeCarbonDataset(): chose locale " + carbonObject.value); + CarbonDatasetHelper.saveCurrentCarbonDatasetLocale(carbonObject.value); //there's some sort of error here + //Unhandled Promise Rejection: While logging, error -[NSNull UTF8String]: unrecognized selector sent to instance 0x7fff8a625fb0 + carbonDatasetString = i18next.t('general-settings.carbon-dataset') + ": " + CarbonDatasetHelper.getCurrentCarbonDatasetCode(); + } + //conditional creation of setting sections let logUploadSection; @@ -484,7 +496,7 @@ const ProfileSettings = () => { - + setPrivacyVis(true)}> {timePicker} @@ -521,7 +533,7 @@ const ProfileSettings = () => { transparent={true}> setNukeVis(false)} - style={styles.dialog(colors.elevation.level3)}> + style={settingStyles.dialog(colors.elevation.level3)}> {t('general-settings.clear-data')} - )} - - - - - - + clearNotifications()}> {/* force state sheet */} - setForceStateVis(false)} - transparent={true}> - setForceStateVis(false)} - style={styles.dialog(colors.elevation.level3)}> - {"Force State"} - - {stateActions.map((e) => - - )} - - - - - - + {/* opcode viewing popup */} - + {/* {view permissions} */} - + {/* {view privacy} */} - - + + {/* logout menu */} setLogoutVis(false)} transparent={true}> setLogoutVis(false)} - style={styles.dialog(colors.elevation.level3)}> + style={settingStyles.dialog(colors.elevation.level3)}> {t('general-settings.are-you-sure')} {t('general-settings.log-out-warning')} @@ -633,7 +596,7 @@ const ProfileSettings = () => { setDataPendingVis(false)} transparent={true}> setDataPendingVis(false)} - style={styles.dialog(colors.elevation.level3)}> + style={settingStyles.dialog(colors.elevation.level3)}> {t('data pending for push')} - - - - -
{{entry.metadata.write_fmt_time}}
-
{{entry.data}}
-
-
- - From f1f9b57533b0dfa6f7f202f30049ae53b87abb27 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 14:20:05 -0600 Subject: [PATCH 06/30] update log page to load more previously, was not loading more when you hit the end of the list, now the list loads more when you reach the end --- www/js/control/LogPage.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index b0e438472..90a363bcc 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -14,7 +14,6 @@ const LogPage = ({pageVis, setPageVis}) => { const { t } = useTranslation(); const { colors } = useTheme(); const EmailHelper = getAngularService('EmailHelper'); - const $state = getAngularService('$state'); const { appConfig, loading } = useAppConfig(); const [ loadStats, setLoadStats ] = useState(); @@ -25,13 +24,15 @@ const LogPage = ({pageVis, setPageVis}) => { const [ maxMessage, setMaxMessage ] = useState(""); const [ logMessage, setLogMessage ] = useState(""); + const [ isFetching, setIsFetching ] = useState(false); + var RETRIEVE_COUNT = 100; useEffect(() => { refreshEntries(); }, [appConfig]); - const refreshEntries = function() { + async function refreshEntries() { window?.Logger.getMaxIndex().then(function(maxIndex) { console.log("maxIndex = "+maxIndex); let tempStats = {} as loadStats; @@ -40,17 +41,17 @@ const LogPage = ({pageVis, setPageVis}) => { tempStats.reachedEnd = false; setLoadStats(tempStats); setEntries([]); - addEntries(); }, function(error) { let errorString = "While getting max index "+JSON.stringify(error, null, 2); console.log(errorString); setMaxMessage(errorString); setMaxErrorVis(true); }) + addEntries(); } const moreDataCanBeLoaded = useMemo(() => { - return loadStats?.gotMaxIndex && loadStats?.reachedEnd; + return loadStats?.gotMaxIndex && !loadStats?.reachedEnd; }, [loadStats]) const clear = function() { @@ -61,16 +62,19 @@ const LogPage = ({pageVis, setPageVis}) => { async function addEntries() { console.log("calling addEntries"); + setIsFetching(true); window.Logger.getMessagesFromIndex(loadStats?.currentStart, RETRIEVE_COUNT) .then(function(entryList) { processEntries(entryList); console.log("entry list size = "+ entries.length); + setIsFetching(false); //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? }, function(error) { let errStr = "While getting messages from the log "+JSON.stringify(error, null, 2); console.log(errStr); setLogMessage(errStr); setLogErrorVis(true); + setIsFetching(false); //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? }) } @@ -123,7 +127,10 @@ const LogPage = ({pageVis, setPageVis}) => { estimatedItemSize={75} keyExtractor={(item) => item.ID} ItemSeparatorComponent={separator} - onScroll={e => {if(moreDataCanBeLoaded){addEntries()}}} + onEndReachedThreshold={0.5} + refreshing={isFetching} + onRefresh={() => {if(moreDataCanBeLoaded){addEntries()}}} + onEndReached={() => {if(moreDataCanBeLoaded){addEntries()}}} /> From e21cb6e1151b6154eb8e8907bbad16e2a957d69d Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 14:45:13 -0600 Subject: [PATCH 07/30] load on enter load the log pages when their visibility changes, this seems cleaner than tying it to the config, since this is unrelated to the config --- www/js/control/LogPage.tsx | 5 ++--- www/js/control/SensedPage.tsx | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index 90a363bcc..9e1628a10 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -4,7 +4,6 @@ import { useTheme, Text, Appbar, IconButton } from "react-native-paper"; import { getAngularService } from "../angular-react-helper"; import { useTranslation } from "react-i18next"; import { FlashList } from '@shopify/flash-list'; -import useAppConfig from "../useAppConfig"; import moment from "moment"; import AlertBar from "./AlertBar"; @@ -14,7 +13,6 @@ const LogPage = ({pageVis, setPageVis}) => { const { t } = useTranslation(); const { colors } = useTheme(); const EmailHelper = getAngularService('EmailHelper'); - const { appConfig, loading } = useAppConfig(); const [ loadStats, setLoadStats ] = useState(); const [ entries, setEntries ] = useState([]); @@ -28,9 +26,10 @@ const LogPage = ({pageVis, setPageVis}) => { var RETRIEVE_COUNT = 100; + //when opening the modal, load the entries useEffect(() => { refreshEntries(); - }, [appConfig]); + }, [pageVis]); async function refreshEntries() { window?.Logger.getMaxIndex().then(function(maxIndex) { diff --git a/www/js/control/SensedPage.tsx b/www/js/control/SensedPage.tsx index 86cbee3bc..a25422b98 100644 --- a/www/js/control/SensedPage.tsx +++ b/www/js/control/SensedPage.tsx @@ -4,7 +4,6 @@ import { useTheme, Appbar, IconButton } from "react-native-paper"; import { getAngularService } from "../angular-react-helper"; import { useTranslation } from "react-i18next"; import { FlashList } from '@shopify/flash-list'; -import useAppConfig from "../useAppConfig"; import moment from "moment"; import ActionMenu from "../components/ActionMenu"; @@ -14,7 +13,6 @@ const SensedPage = ({pageVis, setPageVis}) => { const { t } = useTranslation(); const { colors } = useTheme(); const EmailHelper = getAngularService('EmailHelper'); - const { appConfig, loading } = useAppConfig(); /* Let's keep a reference to the database for convenience */ const [ DB, setDB ]= useState(); @@ -25,8 +23,6 @@ const SensedPage = ({pageVis, setPageVis}) => { const [ entries, setEntries ] = useState([]); const setup = function() { - setDB(window?.cordova.plugins.BEMUserCache); - if(DB) { let tempConfig = {} as configObject; tempConfig.key_data_mapping = { @@ -108,8 +104,9 @@ const SensedPage = ({pageVis, setPageVis}) => { } useEffect(() => { + setDB(window.cordova.plugins.BEMUserCache); setup(); - }, [appConfig]); + }, [pageVis]); const separator = () => const cacheItem = ({item: cacheItem}) => ( From 496b353dc7c61e598bc1e05261b1f5acfc359901 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 15:04:23 -0600 Subject: [PATCH 08/30] refactor .then() to try/catch --- www/js/control/LogPage.tsx | 40 +++++++++++++++++------------------ www/js/control/SensedPage.tsx | 12 +++++------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index 9e1628a10..8b8c572be 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -18,10 +18,8 @@ const LogPage = ({pageVis, setPageVis}) => { const [ entries, setEntries ] = useState([]); const [ maxErrorVis, setMaxErrorVis ] = useState(false); const [ logErrorVis, setLogErrorVis ] = useState(false); - const [ maxMessage, setMaxMessage ] = useState(""); const [ logMessage, setLogMessage ] = useState(""); - const [ isFetching, setIsFetching ] = useState(false); var RETRIEVE_COUNT = 100; @@ -32,7 +30,8 @@ const LogPage = ({pageVis, setPageVis}) => { }, [pageVis]); async function refreshEntries() { - window?.Logger.getMaxIndex().then(function(maxIndex) { + try { + let maxIndex = await window.Logger.getMaxIndex(); console.log("maxIndex = "+maxIndex); let tempStats = {} as loadStats; tempStats.currentStart = maxIndex; @@ -40,13 +39,14 @@ const LogPage = ({pageVis, setPageVis}) => { tempStats.reachedEnd = false; setLoadStats(tempStats); setEntries([]); - }, function(error) { + } catch(error) { let errorString = "While getting max index "+JSON.stringify(error, null, 2); console.log(errorString); setMaxMessage(errorString); setMaxErrorVis(true); - }) - addEntries(); + } finally { + addEntries(); + } } const moreDataCanBeLoaded = useMemo(() => { @@ -62,20 +62,20 @@ const LogPage = ({pageVis, setPageVis}) => { async function addEntries() { console.log("calling addEntries"); setIsFetching(true); - window.Logger.getMessagesFromIndex(loadStats?.currentStart, RETRIEVE_COUNT) - .then(function(entryList) { - processEntries(entryList); - console.log("entry list size = "+ entries.length); - setIsFetching(false); - //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? - }, function(error) { - let errStr = "While getting messages from the log "+JSON.stringify(error, null, 2); - console.log(errStr); - setLogMessage(errStr); - setLogErrorVis(true); - setIsFetching(false); - //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? - }) + try { + let entryList = await window.Logger.getMessagesFromIndex(loadStats?.currentStart, RETRIEVE_COUNT); + processEntries(entryList); + console.log("entry list size = "+ entries.length); + setIsFetching(false); + //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? + } catch(error) { + let errStr = "While getting messages from the log "+JSON.stringify(error, null, 2); + console.log(errStr); + setLogMessage(errStr); + setLogErrorVis(true); + setIsFetching(false); + //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? + } } const processEntries = function(entryList) { diff --git a/www/js/control/SensedPage.tsx b/www/js/control/SensedPage.tsx index a25422b98..d78b935d5 100644 --- a/www/js/control/SensedPage.tsx +++ b/www/js/control/SensedPage.tsx @@ -61,7 +61,7 @@ const SensedPage = ({pageVis, setPageVis}) => { setSelectedKey(newVal); } - const updateEntries = function() { + async function updateEntries() { let userCacheFn, userCacheKey; if(selectedKey == "") { userCacheFn = DB.getAllMessages; @@ -71,7 +71,8 @@ const SensedPage = ({pageVis, setPageVis}) => { userCacheKey = config.key_data_mapping[selectedKey]["key"]; } - userCacheFn(userCacheKey, true).then(function(entryList) { + try { + let entryList = await userCacheFn(userCacheKey, true); let tempEntries = []; entryList.forEach(entry => { entry.metadata.write_fmt_time = moment.unix(entry.metadata.write_ts) @@ -81,11 +82,10 @@ const SensedPage = ({pageVis, setPageVis}) => { tempEntries.push(entry); }); setEntries(tempEntries); - // This should really be within a try/catch/finally block - //$scope.$broadcast('scroll.refreshComplete'); //---> what to do instead? - }, function(error) { + } + catch(error) { window.Logger.log(window.Logger.LEVEL_ERROR, "Error updating entries"+ error); - }) + } } //update entries anytime the selected key changes From fe44ae6cac50a2a9e6b3f81fe06f83e86f3096a6 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 15:27:54 -0600 Subject: [PATCH 09/30] resolve error I was getting a mysterious "unrecognized selector" error and came to the conclusion that it was because loadStats.currentStart was null on first load, by setting a fallback, the error is resolved --- www/js/control/LogPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index 8b8c572be..08b0c86fb 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -62,8 +62,9 @@ const LogPage = ({pageVis, setPageVis}) => { async function addEntries() { console.log("calling addEntries"); setIsFetching(true); + let start = loadStats.currentStart ? loadStats.currentStart : 0; //set a default start to prevent initial fetch error try { - let entryList = await window.Logger.getMessagesFromIndex(loadStats?.currentStart, RETRIEVE_COUNT); + let entryList = await window.Logger.getMessagesFromIndex(start, RETRIEVE_COUNT); processEntries(entryList); console.log("entry list size = "+ entries.length); setIsFetching(false); From e50f0a1d1d41892e8249972440bad989de45d59c Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 15:30:57 -0600 Subject: [PATCH 10/30] remove infiniteScrollComplete broadcasts these had been commented out, and by searching the codebase I came to the conclusion that they were for communicating with angular/ionic, which we no longer use unlike other broadcasts, this is not received by other parts of our code, to trigger some action, so we don't need it --- www/js/control/LogPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index 08b0c86fb..d722127b3 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -68,14 +68,12 @@ const LogPage = ({pageVis, setPageVis}) => { processEntries(entryList); console.log("entry list size = "+ entries.length); setIsFetching(false); - //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? } catch(error) { let errStr = "While getting messages from the log "+JSON.stringify(error, null, 2); console.log(errStr); setLogMessage(errStr); setLogErrorVis(true); setIsFetching(false); - //$scope.$broadcast('scroll.infiniteScrollComplete') //do I still need this? } } From fd32aee97f2be31c6f3e3265d900fc9275d2d158 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 12 Sep 2023 15:45:03 -0600 Subject: [PATCH 11/30] fix sensed formatting I was using react-native Text when I wanted react-native-paper Text --- www/js/control/SensedPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/js/control/SensedPage.tsx b/www/js/control/SensedPage.tsx index d78b935d5..f46086ad7 100644 --- a/www/js/control/SensedPage.tsx +++ b/www/js/control/SensedPage.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; -import { View, StyleSheet, SafeAreaView, Text, Modal } from "react-native"; -import { useTheme, Appbar, IconButton } from "react-native-paper"; +import { View, StyleSheet, SafeAreaView, Modal } from "react-native"; +import { useTheme, Appbar, IconButton, Text } from "react-native-paper"; import { getAngularService } from "../angular-react-helper"; import { useTranslation } from "react-i18next"; import { FlashList } from '@shopify/flash-list'; From d20bf2e89333b6ac807abb04b49993f31653a65f Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Wed, 13 Sep 2023 09:14:12 -0600 Subject: [PATCH 12/30] i18n for ui strings Adding translations for the user-visible strings to ensure users can access the dev zone, the logs will be in English, but with headers and error messages in selected language participating in debugging will be easier --- www/i18n/en.json | 7 +++++++ www/js/control/LogPage.tsx | 10 +++++----- www/js/control/SensedPage.tsx | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index 758960ade..b7c14d0e6 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -36,7 +36,9 @@ "nuke-all": "Nuke all buffers and cache", "test-notification": "Test local notification", "check-log": "Check log", + "log-title" : "Log", "check-sensed-data": "Check sensed data", + "sensed-title": "Sensed Data", "collection": "Collection", "sync": "Sync", "button-accept": "I accept", @@ -48,6 +50,11 @@ "log-out": "Log Out" }, + "dev-errors" : { + "while-messages": "While getting messages from the log ", + "while-max" : "While getting max index " + }, + "general-settings":{ "choose-date" : "Choose date to download data", "choose-dataset" : "Choose a dataset for carbon footprint calculations", diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index d722127b3..6a417e879 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -40,7 +40,7 @@ const LogPage = ({pageVis, setPageVis}) => { setLoadStats(tempStats); setEntries([]); } catch(error) { - let errorString = "While getting max index "+JSON.stringify(error, null, 2); + let errorString = t('dev-errors.while-max')+JSON.stringify(error, null, 2); console.log(errorString); setMaxMessage(errorString); setMaxErrorVis(true); @@ -69,7 +69,7 @@ const LogPage = ({pageVis, setPageVis}) => { console.log("entry list size = "+ entries.length); setIsFetching(false); } catch(error) { - let errStr = "While getting messages from the log "+JSON.stringify(error, null, 2); + let errStr = t('dev-errors.while-messages')+JSON.stringify(error, null, 2); console.log(errStr); setLogMessage(errStr); setLogErrorVis(true); @@ -110,7 +110,7 @@ const LogPage = ({pageVis, setPageVis}) => { {setPageVis(false)}}/> - + @@ -132,8 +132,8 @@ const LogPage = ({pageVis, setPageVis}) => { /> - - + +
); }; diff --git a/www/js/control/SensedPage.tsx b/www/js/control/SensedPage.tsx index f46086ad7..cc667a467 100644 --- a/www/js/control/SensedPage.tsx +++ b/www/js/control/SensedPage.tsx @@ -119,7 +119,7 @@ const SensedPage = ({pageVis, setPageVis}) => { setPageVis(false)}/> - + From f132056736404a7f79825c8337ac1e226e806686 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Wed, 13 Sep 2023 09:14:38 -0600 Subject: [PATCH 13/30] remove old functions we don't need these show sensed or show logs anymore! --- www/js/control/general-settings.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/www/js/control/general-settings.js b/www/js/control/general-settings.js index 5225af1c6..e29702d6e 100644 --- a/www/js/control/general-settings.js +++ b/www/js/control/general-settings.js @@ -33,12 +33,4 @@ angular.module('emission.main.control',['emission.services', //to change reminder time if accessing profile by specific android notification flow //would open the date picker - //TODO create React pages and use React routing - $scope.showLog = function() { - $state.go("root.main.log"); - } - $scope.showSensed = function() { - $state.go("root.main.sensed"); - } - }); From 723c5e77385cd6450710286b42e94978ca2fb51c Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Wed, 13 Sep 2023 09:27:13 -0600 Subject: [PATCH 14/30] factor out "Locations" and "Motion Type" these two options are not supported, so we should not present them to the user -- removing them leaves us with one option, so no need for an options menu This reduces the complexity of the sensed data page significantly note about removing options: https://github.com/e-mission/e-mission-phone/pull/1029#issuecomment-1716907821 --- www/i18n/en.json | 2 +- www/js/control/SensedPage.tsx | 76 +++-------------------------------- 2 files changed, 6 insertions(+), 72 deletions(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index b7c14d0e6..f3c203b3d 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -38,7 +38,7 @@ "check-log": "Check log", "log-title" : "Log", "check-sensed-data": "Check sensed data", - "sensed-title": "Sensed Data", + "sensed-title": "Sensed Data: Transitions", "collection": "Collection", "sync": "Sync", "button-accept": "I accept", diff --git a/www/js/control/SensedPage.tsx b/www/js/control/SensedPage.tsx index cc667a467..8de1e7e5f 100644 --- a/www/js/control/SensedPage.tsx +++ b/www/js/control/SensedPage.tsx @@ -5,9 +5,6 @@ import { getAngularService } from "../angular-react-helper"; import { useTranslation } from "react-i18next"; import { FlashList } from '@shopify/flash-list'; import moment from "moment"; -import ActionMenu from "../components/ActionMenu"; - -type configObject = { key_data_mapping: object, keys: string[], keyMap: {}[] }; const SensedPage = ({pageVis, setPageVis}) => { const { t } = useTranslation(); @@ -16,61 +13,16 @@ const SensedPage = ({pageVis, setPageVis}) => { /* Let's keep a reference to the database for convenience */ const [ DB, setDB ]= useState(); - - const [ config, setConfig ] = useState(); - const [ selectedKey, setSelectedKey ] = useState(""); - const [ keysVisible, setKeysVisible ] = useState(false); const [ entries, setEntries ] = useState([]); - const setup = function() { - if(DB) { - let tempConfig = {} as configObject; - tempConfig.key_data_mapping = { - "Transitions": { - fn: DB.getAllMessages, - key: "statemachine/transition" - }, - "Locations": { - fn: DB.getAllSensorData, - key: "background/location" - }, - "Motion Type": { - fn: DB.getAllSensorData, - key: "background/motion_activity" - }, - } - - tempConfig.keys = []; - for (let key in tempConfig.key_data_mapping) { - tempConfig.keys.push(key); - } - - tempConfig.keyMap = mapForActionMenu(tempConfig.keys); - - setSelectedKey(tempConfig.keys[0]); - setConfig(tempConfig); - updateEntries(); - } - } - const emailCache = function() { EmailHelper.sendEmail("userCacheDB"); } - const setSelected = function(newVal) { - setSelectedKey(newVal); - } - async function updateEntries() { - let userCacheFn, userCacheKey; - if(selectedKey == "") { - userCacheFn = DB.getAllMessages; - userCacheKey = "statemachine/transition"; - } else { - userCacheFn = config.key_data_mapping[selectedKey]["fn"]; - userCacheKey = config.key_data_mapping[selectedKey]["key"]; - } - + //hardcoded function and keys after eliminating bit-rotted options + let userCacheFn = DB.getAllMessages; + let userCacheKey = "statemachine/transition"; try { let entryList = await userCacheFn(userCacheKey, true); let tempEntries = []; @@ -88,24 +40,9 @@ const SensedPage = ({pageVis, setPageVis}) => { } } - //update entries anytime the selected key changes - useEffect(() => { - if(DB){ - updateEntries(); - } - }, [selectedKey]) - - const mapForActionMenu = function(keys) { - let map = []; - keys.forEach(key => { - map.push({text: key}); - }); - return map; - } - useEffect(() => { setDB(window.cordova.plugins.BEMUserCache); - setup(); + updateEntries(); }, [pageVis]); const separator = () => @@ -119,13 +56,12 @@ const SensedPage = ({pageVis, setPageVis}) => { setPageVis(false)}/> - + updateEntries()}/> emailCache()}/> - setKeysVisible(true)}/> { ItemSeparatorComponent={separator} /> - - setSelected(key.text)} onExit={() => {}}>
); }; From d04d5ee8ed372ed142f979be6782b8a1f9d755cd Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Wed, 13 Sep 2023 09:47:56 -0600 Subject: [PATCH 15/30] combine dev-errors into errors --- www/i18n/en.json | 9 +++------ www/js/control/LogPage.tsx | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index f3c203b3d..2e164eacb 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -50,11 +50,6 @@ "log-out": "Log Out" }, - "dev-errors" : { - "while-messages": "While getting messages from the log ", - "while-max" : "While getting max index " - }, - "general-settings":{ "choose-date" : "Choose date to download data", "choose-dataset" : "Choose a dataset for carbon footprint calculations", @@ -367,7 +362,9 @@ "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}}" + "while-loading-specific-week": "Error while loading travel for the week of {{day}}", + "while-log-messages": "While getting messages from the log ", + "while-max-index" : "While getting max index " }, "consent-text": { "title":"NREL OPENPATH PRIVACY POLICY/TERMS OF USE", diff --git a/www/js/control/LogPage.tsx b/www/js/control/LogPage.tsx index 6a417e879..7d6d279ee 100644 --- a/www/js/control/LogPage.tsx +++ b/www/js/control/LogPage.tsx @@ -40,7 +40,7 @@ const LogPage = ({pageVis, setPageVis}) => { setLoadStats(tempStats); setEntries([]); } catch(error) { - let errorString = t('dev-errors.while-max')+JSON.stringify(error, null, 2); + let errorString = t('errors.while-max-index')+JSON.stringify(error, null, 2); console.log(errorString); setMaxMessage(errorString); setMaxErrorVis(true); @@ -69,7 +69,7 @@ const LogPage = ({pageVis, setPageVis}) => { console.log("entry list size = "+ entries.length); setIsFetching(false); } catch(error) { - let errStr = t('dev-errors.while-messages')+JSON.stringify(error, null, 2); + let errStr = t('errors.while-log-messages')+JSON.stringify(error, null, 2); console.log(errStr); setLogMessage(errStr); setLogErrorVis(true); From 5e7a50c08bb2e084f06a7c7d7cd1686db705546a Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 09:09:56 -0600 Subject: [PATCH 16/30] React Control Helpers create a React version of ControlCollectionHelper and ControlSync Helper. These hold the logic for interfacing with the plugins, as well as UI elements for editing menus and various alerts / messages The place this is being stored has also changed, we no longer need to import from the other repos These helpers will eliminate the last parts of React routing, and allow us to no longer rely on the broadcast of "control update complete" to ensure the UI stays up to date --- www/js/control/ControlCollectionHelper.tsx | 273 +++++++++++++++++++++ www/js/control/ControlSyncHelper.tsx | 143 +++++++++++ www/js/control/ProfileSettings.jsx | 65 +++-- 3 files changed, 463 insertions(+), 18 deletions(-) create mode 100644 www/js/control/ControlCollectionHelper.tsx create mode 100644 www/js/control/ControlSyncHelper.tsx diff --git a/www/js/control/ControlCollectionHelper.tsx b/www/js/control/ControlCollectionHelper.tsx new file mode 100644 index 000000000..8d9b73878 --- /dev/null +++ b/www/js/control/ControlCollectionHelper.tsx @@ -0,0 +1,273 @@ +import React, { useEffect, useState } from "react"; +import { Modal, View } from "react-native"; +import { Dialog, Button, Switch, Text, useTheme, TextInput } from 'react-native-paper'; +import { useTranslation } from "react-i18next"; +import ActionMenu from "../components/ActionMenu"; +import { settingStyles } from "./ProfileSettings"; +import { getAngularService } from "../angular-react-helper"; + +type collectionConfig = { + is_duty_cycling: boolean, + simulate_user_interaction: boolean, + accuracy: number, + accuracy_threshold: number, + filter_distance: number, + filter_time: number, + geofence_radius: number, + ios_use_visit_notifications_for_detection: boolean, + ios_use_remote_push_for_sync: boolean, + android_geofence_responsiveness: number +}; + +// const Logger = getAngularService("Logger"); + +async function accuracy2String(config) { + var accuracy = config.accuracy; + let accuracyOptions = await getAccuracyOptions(); + for (var k in accuracyOptions) { + if (accuracyOptions[k] == accuracy) { + return k; + } + } + return accuracy; +} + +export async function isMediumAccuracy(config) { + if (config == null) { + return undefined; // config not loaded when loading ui, set default as false + } else { + var v = await accuracy2String(config); + if (window.cordova.platformId == 'ios') { + return v != "kCLLocationAccuracyBestForNavigation" && v != "kCLLocationAccuracyBest" && v != "kCLLocationAccuracyTenMeters"; + } else if (window.cordova.platformId == 'android') { + return v != "PRIORITY_HIGH_ACCURACY"; + } else { + // $ionicPopup.alert("Emission does not support this platform"); + } + } +} + +export async function helperToggleLowAccuracy(config) { + let tempConfig = {...config}; + let accuracyOptions = await getAccuracyOptions(); + if (isMediumAccuracy(config)) { + if (window.cordova.platformId == 'ios') { + tempConfig.accuracy = accuracyOptions["kCLLocationAccuracyBest"]; + } else if (window.cordova.platformId == 'android') { + tempConfig.accuracy = cch.accuracyOptions["PRIORITY_HIGH_ACCURACY"]; + } + } else { + if (window.cordova.platformId == 'ios') { + tempConfig.accuracy = accuracyOptions["kCLLocationAccuracyHundredMeters"]; + } else if (window.cordova.platformId == 'android') { + tempConfig.accuracy = accuracyOptions["PRIORITY_BALANCED_POWER_ACCURACY"]; + } + } + try{ + let set = setConfig(tempConfig); + console.log("setConfig Sucess"); + } catch (err) { + // Logger.displayError("Error while setting collection config", err); + } +} + +/* +* Simple read/write wrappers +*/ + +export const getState = function() { + return window.cordova.plugins.BEMDataCollection.getState(); +}; + +export async function getHelperCollectionSettings() { + let promiseList = []; + promiseList.push(getConfig()); + promiseList.push(getAccuracyOptions()); + let resultList = await Promise.all(promiseList); + let tempConfig = resultList[0]; + let tempAccuracyOptions = resultList[1]; + return formatConfigForDisplay(tempConfig, tempAccuracyOptions); +} + +const setConfig = function(config) { + return window.cordova.plugins.BEMDataCollection.setConfig(config); +}; + +const getConfig = function() { + return window.cordova.plugins.BEMDataCollection.getConfig(); +}; +const getAccuracyOptions = function() { + return window.cordova.plugins.BEMDataCollection.getAccuracyOptions(); +}; + +export const forceTransitionWrapper = function(transition) { + return window.cordova.plugins.BEMDataCollection.forceTransition(transition); +}; + +const formatConfigForDisplay = function(config, accuracyOptions) { + var retVal = []; + for (var prop in config) { + if (prop == "accuracy") { + for (var name in accuracyOptions) { + if (accuracyOptions[name] == config[prop]) { + retVal.push({'key': prop, 'val': name}); + } + } + } else { + retVal.push({'key': prop, 'val': config[prop]}); + } + } + return retVal; +} + +const ControlSyncHelper = ({ editVis, setEditVis, localConfig, setLocalConfig }) => { + const {colors} = useTheme(); + const Logger = getAngularService("Logger"); + + // const [ localConfig, setLocalConfig ] = useState(); + const [ accuracyActions, setAccuracyActions ] = useState([]); + const [ accuracyVis, setAccuracyVis ] = useState(false); + + async function getCollectionSettings() { + let promiseList = []; + promiseList.push(getConfig()); + promiseList.push(getAccuracyOptions()); + let resultList = await Promise.all(promiseList); + let tempConfig = resultList[0]; + setLocalConfig(tempConfig); + let tempAccuracyOptions = resultList[1]; + setAccuracyActions(formatAccuracyForActions(tempAccuracyOptions)); + return formatConfigForDisplay(tempConfig, tempAccuracyOptions); + } + + useEffect(() => { + getCollectionSettings(); + }, [editVis]) + + const formatAccuracyForActions = function(accuracyOptions) { + let tempAccuracyActions = []; + for (var name in accuracyOptions) { + tempAccuracyActions.push({text: name, value: accuracyOptions[name]}); + } + return tempAccuracyActions; + } + + /* + * Functions to edit and save values + */ + + async function saveAndReload() { + console.log("new config = ", localConfig); + try{ + let set = await setConfig(localConfig); + //TODO find way to not need control.update.complete event broadcast + } catch(err) { + Logger.displayError("Error while setting collection config", err); + } + } + + const onToggle = function(config_key) { + let tempConfig = {...localConfig}; + tempConfig[config_key] = !localConfig[config_key]; + setLocalConfig(tempConfig); + } + + const onChooseAccuracy = function(accuracyOption) { + let tempConfig = {...localConfig}; + tempConfig.accuracy = accuracyOption.value; + setLocalConfig(tempConfig); + } + + const onChangeText = function(newText, config_key) { + let tempConfig = {...localConfig}; + tempConfig[config_key] = parseInt(newText); + setLocalConfig(tempConfig); + } + + /*ios vs android*/ + let filterComponent; + if(window.cordova.platformId == 'ios') { + filterComponent = + Filter Distance + onChangeText(text, "filter_distance")}/> + + } else { + filterComponent = + Filter Interval + onChangeText(text, "filter_time")}/> + + } + let iosToggles; + if(window.cordova.platformId == 'ios') { + iosToggles = <> + {/* use visit notifications toggle NO ANDROID */} + + Use Visit Notifications + onToggle("ios_use_visit_notifications_for_detection")}> + + {/* sync on remote push toggle NO ANDROID */} + + Sync on remote push + onToggle("ios_use_remote_push_for_sync}")}> + + + } + let geofenceComponent; + if(window.cordova.platformId == 'android') { + geofenceComponent = + Geofence Responsiveness + onChangeText(text, "android_geofence_responsiveness")}/> + + } + + return ( + <> + setEditVis(false)} transparent={true}> + setEditVis(false)} style={settingStyles.dialog(colors.elevation.level3)}> + Edit Collection Settings + + {/* duty cycling toggle */} + + Duty Cycling + onToggle("is_duty_cycling")}> + + {/* simulate user toggle */} + + Simulate User + onToggle("simulate_user_interaction")}> + + {/* accuracy */} + + Accuracy + + + {/* accuracy threshold not editable*/} + + Accuracy Threshold + {localConfig?.accuracy_threshold} + + {filterComponent} + {/* geofence radius */} + + Geofence Radius + onChangeText(text, "geofence_radius")}/> + + {iosToggles} + {geofenceComponent} + + + + + + + + + {}}> + + ); + }; + +export default ControlSyncHelper; diff --git a/www/js/control/ControlSyncHelper.tsx b/www/js/control/ControlSyncHelper.tsx new file mode 100644 index 000000000..a5e3182ca --- /dev/null +++ b/www/js/control/ControlSyncHelper.tsx @@ -0,0 +1,143 @@ +import React, { useEffect, useState } from "react"; +import { Modal, View } from "react-native"; +import { Dialog, Button, Switch, Text, useTheme } from 'react-native-paper'; +import { useTranslation } from "react-i18next"; +import ActionMenu from "../components/ActionMenu"; +import { settingStyles } from "./ProfileSettings"; +import { getAngularService } from "../angular-react-helper"; + +/* +* BEGIN: Simple read/write wrappers +*/ +export function forcePluginSync() { + return window.cordova.plugins.BEMServerSync.forceSync(); +}; + +const formatConfigForDisplay = (configToFormat) => { + var formatted = []; + for (let prop in configToFormat) { + formatted.push({'key': prop, 'val': configToFormat[prop]}); + } + return formatted; +} + +const setConfig = function(config) { + return window.cordova.plugins.BEMServerSync.setConfig(config); + }; + +const getConfig = function() { + return window.cordova.plugins.BEMServerSync.getConfig(); +}; + +export async function getHelperSyncSettings() { + let tempConfig = await getConfig(); + return formatConfigForDisplay(tempConfig); +} + +type syncConfig = { sync_interval: number, + ios_use_remote_push: boolean }; + +const ControlSyncHelper = ({ editVis, setEditVis }) => { + const { t } = useTranslation(); + const { colors } = useTheme(); + const CommHelper = getAngularService("CommHelper"); + const Logger = getAngularService("Logger"); + + const [ localConfig, setLocalConfig ] = useState(); + const [ intervalVis, setIntervalVis ] = useState(false); + + /* + * Functions to read and format values for display + */ + async function getSyncSettings() { + let tempConfig = await getConfig(); + setLocalConfig(tempConfig); + } + + useEffect(() => { + getSyncSettings(); + }, [editVis]) + + const syncIntervalActions = [ + {text: "1 min", value: 60}, + {text: "10 min", value: 10 * 60}, + {text: "30 min", value: 30 * 60}, + {text: "1 hr", value: 60 * 60} + ] + + /* + * Functions to edit and save values + */ + async function saveAndReload() { + console.log("new config = "+localConfig); + try{ + let set = setConfig(localConfig); + //NOTE -- we need to make sure we update these settings in ProfileSettings :) -- getting rid of broadcast handling for migration!! + CommHelper.updateUser({ + // TODO: worth thinking about where best to set this + // Currently happens in native code. Now that we are switching + // away from parse, we can store this from javascript here. + // or continue to store from native + // this is easier for people to see, but means that calls to + // native, even through the javascript interface are not complete + curr_sync_interval: localConfig.sync_interval + }); + } catch (err) + { + console.log("error with setting sync config", err); + Logger.displayError("Error while setting sync config", err); + } + } + + const onChooseInterval = function(interval) { + let tempConfig = {...localConfig}; + tempConfig.sync_interval = interval.value; + setLocalConfig(tempConfig); + } + + const onTogglePush = function() { + let tempConfig = {...localConfig}; + tempConfig.ios_use_remote_push = !localConfig.ios_use_remote_push; + setLocalConfig(tempConfig); + } + + /* + * configure the UI + */ + let toggle; + if(window.cordova.platformId == 'ios'){ + toggle = + Use Remote Push + + + } + + return ( + <> + {/* popup to show when we want to edit */} + setEditVis(false)} transparent={true}> + setEditVis(false)} style={settingStyles.dialog(colors.elevation.level3)}> + Edit Sync Settings + + + Sync Interval + + + {toggle} + + + + + + + + + {}}> + + ); + }; + +export default ControlSyncHelper; \ No newline at end of file diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 88ef53ce9..3856c77f2 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -17,6 +17,8 @@ import PrivacyPolicyModal from "./PrivacyPolicyModal"; import ActionMenu from "../components/ActionMenu"; import SensedPage from "./SensedPage" import LogPage from "./LogPage"; +import ControlSyncHelper, {forcePluginSync, getHelperSyncSettings} from "./ControlSyncHelper"; +import ControlCollectionHelper, {getHelperCollectionSettings, getState, isMediumAccuracy, helperToggleLowAccuracy, forceTransitionWrapper} from "./ControlCollectionHelper"; let controlUpdateCompleteListenerRegistered = false; @@ -36,8 +38,6 @@ const ProfileSettings = () => { const CarbonDatasetHelper = getAngularService('CarbonDatasetHelper'); const UploadHelper = getAngularService('UploadHelper'); const EmailHelper = getAngularService('EmailHelper'); - const ControlCollectionHelper = getAngularService('ControlCollectionHelper'); - const ControlSyncHelper = getAngularService('ControlSyncHelper'); const KVStore = getAngularService('KVStore'); const NotificationScheduler = getAngularService('NotificationScheduler'); const ControlHelper = getAngularService('ControlHelper'); @@ -55,8 +55,8 @@ const ProfileSettings = () => { } //functions that come directly from an Angular service - const editCollectionConfig = ControlCollectionHelper.editConfig; - const editSyncConfig = ControlSyncHelper.editConfig; + const editCollectionConfig = () => setEditCollection(true); + const editSyncConfig = () => setEditSync(true); //states and variables used to control/create the settings const [opCodeVis, setOpCodeVis] = useState(false); @@ -75,7 +75,12 @@ const ProfileSettings = () => { const [privacyVis, setPrivacyVis] = useState(false); const [showingSensed, setShowingSensed] = useState(false); const [showingLog, setShowingLog] = useState(false); + const [editSync, setEditSync] = useState(false); + const [editCollection, setEditCollection] = useState(false); + const [forceResultVis, setForceResultVis] = useState(false); + const [forceResult, setForceResult] = useState(""); + const [collectConfig, setCollectConfig] = useState({}); const [collectSettings, setCollectSettings] = useState({}); const [notificationSettings, setNotificationSettings] = useState({}); const [authSettings, setAuthSettings] = useState({}); @@ -151,16 +156,16 @@ const ProfileSettings = () => { console.debug('about to refreshCollectSettings, collectSettings = ', collectSettings); const newCollectSettings = {}; - // refresh collect plugin configuration - const collectionPluginConfig = await ControlCollectionHelper.getCollectionSettings(); + // // refresh collect plugin configuration + const collectionPluginConfig = await getHelperCollectionSettings(); newCollectSettings.config = collectionPluginConfig; - const collectionPluginState = await ControlCollectionHelper.getState(); + const collectionPluginState = await getState(); newCollectSettings.state = collectionPluginState; newCollectSettings.trackingOn = collectionPluginState != "local.state.tracking_stopped" && collectionPluginState != "STATE_TRACKING_STOPPED"; - const isLowAccuracy = await ControlCollectionHelper.isMediumAccuracy(); + const isLowAccuracy = await isMediumAccuracy(); if (typeof isLowAccuracy != 'undefined') { newCollectSettings.lowAccuracy = isLowAccuracy; } @@ -168,6 +173,11 @@ const ProfileSettings = () => { setCollectSettings(newCollectSettings); } + //ensure ui table updated when editor closes + useEffect(() => { + refreshCollectSettings(); + }, [editCollection]) + async function refreshNotificationSettings() { console.debug('about to refreshNotificationSettings, notificationSettings = ', notificationSettings); const newNotificationSettings ={}; @@ -190,13 +200,18 @@ const ProfileSettings = () => { async function getSyncSettings() { console.log("getting sync settings"); var newSyncSettings = {}; - ControlSyncHelper.getSyncSettings().then(function(showConfig) { + getHelperSyncSettings().then(function(showConfig) { newSyncSettings.show_config = showConfig; setSyncSettings(newSyncSettings); console.log("sync settings are ", syncSettings); }); }; + //update sync settings in the table when close editor + useEffect(() => { + getSyncSettings(); + }, [editSync]); + async function getConnectURL() { ControlHelper.getSettings().then(function(response) { var newConnectSettings ={} @@ -256,10 +271,20 @@ const ProfileSettings = () => { async function userStartStopTracking() { const transitionToForce = collectSettings.trackingOn ? 'STOP_TRACKING' : 'START_TRACKING'; - ControlCollectionHelper.forceTransition(transitionToForce); - /* the ControlCollectionHelper.forceTransition call above will trigger a - 'control.update.complete' event when it's done, which will trigger refreshCollectSettings. - So we don't need to call refreshCollectSettings here. */ + forceTransition(transitionToForce); + refreshCollectSettings(); + } + + async function forceTransition(transition) { + try { + let result = forceTransitionWrapper(transition); + setForceResultVis(true); + setForceResult('success -> '+result) + } catch (err) { + console.log("error forcing state", err); + setForceResultVis(true); + setForceResult('error -> '+err) + } } @@ -279,7 +304,7 @@ const ProfileSettings = () => { } async function toggleLowAccuracy() { - ControlCollectionHelper.toggleLowAccuracy(); + helperToggleLowAccuracy(); refreshCollectSettings(); } @@ -353,7 +378,7 @@ const ProfileSettings = () => { async function getTransition(transKey) { var entry_data = {}; - const curr_state = await ControlCollectionHelper.getState(); + const curr_state = await getState(); entry_data.curr_state = curr_state; if (transKey == getEndTransitionKey()) { entry_data.curr_state = getOngoingTransitionState(); @@ -397,7 +422,7 @@ const ProfileSettings = () => { function() { console.log("Added "+ClientStats.getStatKeys().BUTTON_FORCE_SYNC+" event"); }); - ControlSyncHelper.forceSync().then(function() { + forcePluginSync().then(function() { /* * Change to sensorKey to "background/location" after fixing issues * with getLastSensorData and getLastMessages in the usercache @@ -461,7 +486,7 @@ const ProfileSettings = () => { } const onSelectState = function(stateObject) { - ControlCollectionHelper.forceTransition(stateObject.transition); + forceTransition(stateObject.transition); } const onSelectCarbon = function(carbonObject) { @@ -560,7 +585,7 @@ const ProfileSettings = () => { clearNotifications()}> {/* force state sheet */} - + {}}> {/* opcode viewing popup */} @@ -659,9 +684,13 @@ const ProfileSettings = () => { + + + + ); From 9255675b46ada642e254616d3ddd6cfcdf777bde Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 11:29:20 -0600 Subject: [PATCH 17/30] fix medium accuracy medium accuracy switch was broken, now can be toggled smoothly --- www/js/control/ControlCollectionHelper.tsx | 14 ++++++++------ www/js/control/ProfileSettings.jsx | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/www/js/control/ControlCollectionHelper.tsx b/www/js/control/ControlCollectionHelper.tsx index 8d9b73878..cf8f95053 100644 --- a/www/js/control/ControlCollectionHelper.tsx +++ b/www/js/control/ControlCollectionHelper.tsx @@ -32,8 +32,9 @@ async function accuracy2String(config) { return accuracy; } -export async function isMediumAccuracy(config) { - if (config == null) { +export async function isMediumAccuracy() { + let config = await getConfig(); + if (!config || config == null) { return undefined; // config not loaded when loading ui, set default as false } else { var v = await accuracy2String(config); @@ -47,10 +48,11 @@ export async function isMediumAccuracy(config) { } } -export async function helperToggleLowAccuracy(config) { - let tempConfig = {...config}; +export async function helperToggleLowAccuracy() { + let tempConfig = await getConfig(); let accuracyOptions = await getAccuracyOptions(); - if (isMediumAccuracy(config)) { + let medium = await isMediumAccuracy(); + if (medium) { if (window.cordova.platformId == 'ios') { tempConfig.accuracy = accuracyOptions["kCLLocationAccuracyBest"]; } else if (window.cordova.platformId == 'android') { @@ -64,7 +66,7 @@ export async function helperToggleLowAccuracy(config) { } } try{ - let set = setConfig(tempConfig); + let set = await setConfig(tempConfig); console.log("setConfig Sucess"); } catch (err) { // Logger.displayError("Error while setting collection config", err); diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 3856c77f2..36185e13e 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -304,7 +304,7 @@ const ProfileSettings = () => { } async function toggleLowAccuracy() { - helperToggleLowAccuracy(); + let toggle = await helperToggleLowAccuracy(); refreshCollectSettings(); } From 1b8a43cad913e5ad54e061024a0767df92f16805 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 11:31:04 -0600 Subject: [PATCH 18/30] remove update complete listeners no longer needed, as refreshCollect settings is called after toggling accuracy, toggling tracking, and when the settings editor is closed --- www/js/control/ProfileSettings.jsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 36185e13e..9819164ab 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -20,8 +20,6 @@ import LogPage from "./LogPage"; import ControlSyncHelper, {forcePluginSync, getHelperSyncSettings} from "./ControlSyncHelper"; import ControlCollectionHelper, {getHelperCollectionSettings, getState, isMediumAccuracy, helperToggleLowAccuracy, forceTransitionWrapper} from "./ControlCollectionHelper"; -let controlUpdateCompleteListenerRegistered = false; - //any pure functions can go outside const ProfileSettings = () => { // anything that mutates must go in --- depend on props or state... @@ -45,15 +43,6 @@ const ProfileSettings = () => { const StartPrefs = getAngularService('StartPrefs'); const DynamicConfig = getAngularService('DynamicConfig'); - if (!controlUpdateCompleteListenerRegistered) { - settingsScope.$on('control.update.complete', function() { - console.debug("Received control.update.complete event, refreshing screen"); - refreshScreen(); - refreshCollectSettings(); - }); - controlUpdateCompleteListenerRegistered = true; - } - //functions that come directly from an Angular service const editCollectionConfig = () => setEditCollection(true); const editSyncConfig = () => setEditSync(true); From 9078488b2b95b867ef960abd8831e8eb980d0b0c Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 11:39:26 -0600 Subject: [PATCH 19/30] eliminate settingsScope no longer rely on settingsScope to catch recomputeAppStatus, because we check on resume, this is not needed --- www/js/control/AppStatusModal.tsx | 10 +--------- www/js/control/ProfileSettings.jsx | 7 +------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/www/js/control/AppStatusModal.tsx b/www/js/control/AppStatusModal.tsx index 3f696adcc..ff5d18ca1 100644 --- a/www/js/control/AppStatusModal.tsx +++ b/www/js/control/AppStatusModal.tsx @@ -10,13 +10,11 @@ import ExplainPermissions from "../appstatus/ExplainPermissions"; import AlertBar from "./AlertBar"; import { settingStyles } from "./ProfileSettings"; -const AppStatusModal = ({permitVis, setPermitVis, settingsScope}) => { +const AppStatusModal = ({permitVis, setPermitVis}) => { const { t } = useTranslation(); const { colors } = useTheme(); const { appConfig, loading } = useAppConfig(); - console.log("settings scope in app status modal", settingsScope); - const { height: windowHeight } = useWindowDimensions(); const [osver, setOsver] = useState(0); const [platform, setPlatform] = useState(""); @@ -370,12 +368,6 @@ const AppStatusModal = ({permitVis, setPermitVis, settingsScope}) => { refreshAllChecks(); }); - //refresh when recompute message is broadcast - settingsScope.$on("recomputeAppStatus", function() { - console.log("PERMISSION CHECK: recomputing state"); - refreshAllChecks(); - }); - //load when ready useEffect(() => { if (appConfig && window['device']?.platform) { diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 9819164ab..8580d1ea1 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -27,11 +27,6 @@ const ProfileSettings = () => { const { appConfig, loading } = useAppConfig(); const { colors } = useTheme(); - // get the scope of the general-settings.js file - const mainControlEl = document.getElementById('main-control'); - const settingsScope = angular.element(mainControlEl.querySelector('profile-settings')).scope(); - console.log("settings scope", settingsScope); - //angular services needed const CarbonDatasetHelper = getAngularService('CarbonDatasetHelper'); const UploadHelper = getAngularService('UploadHelper'); @@ -580,7 +575,7 @@ const ProfileSettings = () => { {/* {view permissions} */} - + {/* {view privacy} */} From e02db4a25a131d0132e266c5fb743778cd09270a Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 14:05:35 -0600 Subject: [PATCH 20/30] remove "safeToggle" the debouncing patch for toggleLowAccuracy is no longer needed! --- www/js/control/ProfileSettings.jsx | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 8580d1ea1..a714b2e13 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -75,7 +75,7 @@ const ProfileSettings = () => { const [uiConfig, setUiConfig] = useState({}); const [consentDoc, setConsentDoc] = useState({}); const [dumpDate, setDumpDate] = useState(new Date()); - const [toggleTime, setToggleTime] = useState(new Date()); + // const [toggleTime, setToggleTime] = useState(new Date()); let carbonDatasetString = t('general-settings.carbon-dataset') + ": " + CarbonDatasetHelper.getCurrentCarbonDatasetCode(); const carbonOptions = CarbonDatasetHelper.getCarbonDatasetOptions(); @@ -272,20 +272,20 @@ const ProfileSettings = () => { } - const safeToggle = function() { - if(toggleTime){ - const prevTime = toggleTime.getTime(); - const currTime = new Date().getTime(); - if(prevTime + 2000 < currTime ){ - toggleLowAccuracy(); - setToggleTime(new Date()); - } - } - else { - toggleLowAccuracy(); - setToggleTime(new Date()); - } - } + // const safeToggle = function() { + // if(toggleTime){ + // const prevTime = toggleTime.getTime(); + // const currTime = new Date().getTime(); + // if(prevTime + 2000 < currTime ){ + // toggleLowAccuracy(); + // setToggleTime(new Date()); + // } + // } + // else { + // toggleLowAccuracy(); + // setToggleTime(new Date()); + // } + // } async function toggleLowAccuracy() { let toggle = await helperToggleLowAccuracy(); @@ -512,7 +512,7 @@ const ProfileSettings = () => { {timePicker} setPermitVis(true)}> - + setCarbonDataVis(true)}> setDateDumpVis(true)}> From d55760f3e4c68d8f120fa1788091f0b47e3c20ce Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 14:24:53 -0600 Subject: [PATCH 21/30] prevent "missing argument errors" since these arguments are optional, and we render based on their existence, set the default to undefined --- www/js/control/AlertBar.jsx | 2 +- www/js/control/SettingRow.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/js/control/AlertBar.jsx b/www/js/control/AlertBar.jsx index c56db7ad2..fbac80056 100644 --- a/www/js/control/AlertBar.jsx +++ b/www/js/control/AlertBar.jsx @@ -4,7 +4,7 @@ import { Snackbar } from 'react-native-paper'; import { useTranslation } from "react-i18next"; import { SafeAreaView } from "react-native-safe-area-context"; -const AlertBar = ({visible, setVisible, messageKey, messageAddition}) => { +const AlertBar = ({visible, setVisible, messageKey, messageAddition=undefined}) => { const { t } = useTranslation(); const onDismissSnackBar = () => setVisible(false); diff --git a/www/js/control/SettingRow.jsx b/www/js/control/SettingRow.jsx index 3caf36d81..473a45d7f 100644 --- a/www/js/control/SettingRow.jsx +++ b/www/js/control/SettingRow.jsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { List, Switch, useTheme } from 'react-native-paper'; import { useTranslation } from "react-i18next"; -const SettingRow = ({textKey, iconName, action, desc, switchValue, descStyle=undefined}) => { +const SettingRow = ({textKey, iconName=undefined, action, desc=undefined, switchValue=undefined, descStyle=undefined}) => { const { t } = useTranslation(); //this accesses the translations const { colors } = useTheme(); // use this to get the theme colors instead of hardcoded #hex colors From 2cb11455f52f6c2af1e5002807fe4af4418c551a Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 14:25:47 -0600 Subject: [PATCH 22/30] remove safe toggle code no longer need to debounce, so no need to keep code around --- www/js/control/ProfileSettings.jsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index a714b2e13..46c6b05ae 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -75,7 +75,6 @@ const ProfileSettings = () => { const [uiConfig, setUiConfig] = useState({}); const [consentDoc, setConsentDoc] = useState({}); const [dumpDate, setDumpDate] = useState(new Date()); - // const [toggleTime, setToggleTime] = useState(new Date()); let carbonDatasetString = t('general-settings.carbon-dataset') + ": " + CarbonDatasetHelper.getCurrentCarbonDatasetCode(); const carbonOptions = CarbonDatasetHelper.getCarbonDatasetOptions(); @@ -271,22 +270,6 @@ const ProfileSettings = () => { } } - - // const safeToggle = function() { - // if(toggleTime){ - // const prevTime = toggleTime.getTime(); - // const currTime = new Date().getTime(); - // if(prevTime + 2000 < currTime ){ - // toggleLowAccuracy(); - // setToggleTime(new Date()); - // } - // } - // else { - // toggleLowAccuracy(); - // setToggleTime(new Date()); - // } - // } - async function toggleLowAccuracy() { let toggle = await helperToggleLowAccuracy(); refreshCollectSettings(); From 1189700c340c5a23f19dd12cfd5f269ab40e1ddc Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 14:27:21 -0600 Subject: [PATCH 23/30] isolate forceSync code to ControlSyncHelper to start decluttering `ProfileSettings` move the component to the ControlSyncHelper then import it into `Profile Settings` by exporting a component, we can wrap the popups and their states into one place --- www/js/control/ControlSyncHelper.tsx | 95 ++++++++++++++++++++++++++++ www/js/control/ProfileSettings.jsx | 73 +-------------------- 2 files changed, 97 insertions(+), 71 deletions(-) diff --git a/www/js/control/ControlSyncHelper.tsx b/www/js/control/ControlSyncHelper.tsx index a5e3182ca..bb032f398 100644 --- a/www/js/control/ControlSyncHelper.tsx +++ b/www/js/control/ControlSyncHelper.tsx @@ -5,6 +5,8 @@ import { useTranslation } from "react-i18next"; import ActionMenu from "../components/ActionMenu"; import { settingStyles } from "./ProfileSettings"; import { getAngularService } from "../angular-react-helper"; +import SettingRow from "./SettingRow"; +import AlertBar from "./AlertBar"; /* * BEGIN: Simple read/write wrappers @@ -34,9 +36,102 @@ export async function getHelperSyncSettings() { return formatConfigForDisplay(tempConfig); } +const getEndTransitionKey = function() { + if(window.cordova.platformId == 'android') { + return "local.transition.stopped_moving"; + } + else if(window.cordova.platformId == 'ios') { + return "T_TRIP_ENDED"; + } +} + type syncConfig = { sync_interval: number, ios_use_remote_push: boolean }; +export const ForceSyncRow = () => { + const { t } = useTranslation(); + const { colors } = useTheme(); + const ClientStats = getAngularService('ClientStats'); + const Logger = getAngularService('Logger'); + + const [dataPendingVis, setDataPendingVis] = useState(false); + const [dataPushedVis, setDataPushedVis] = useState(false); + + async function forceSync() { + ClientStats.addEvent(ClientStats.getStatKeys().BUTTON_FORCE_SYNC).then( + function() { + console.log("Added "+ClientStats.getStatKeys().BUTTON_FORCE_SYNC+" event"); + }); + forcePluginSync().then(function() { + /* + * Change to sensorKey to "background/location" after fixing issues + * with getLastSensorData and getLastMessages in the usercache + * See https://github.com/e-mission/e-mission-phone/issues/279 for details + */ + var sensorKey = "statemachine/transition"; + return window.cordova.plugins.BEMUserCache.getAllMessages(sensorKey, true); + }).then(function(sensorDataList) { + Logger.log("sensorDataList = "+JSON.stringify(sensorDataList)); + // If everything has been pushed, we should + // only have one entry for the battery, which is the one that was + // inserted on the last successful push. + var isTripEnd = function(entry) { + if (entry.metadata.key == getEndTransitionKey()) { + return true; + } else { + return false; + } + }; + var syncLaunchedCalls = sensorDataList.filter(isTripEnd); + var syncPending = (syncLaunchedCalls.length > 0); + Logger.log("sensorDataList.length = "+sensorDataList.length+ + ", syncLaunchedCalls.length = "+syncLaunchedCalls.length+ + ", syncPending? = "+syncPending); + return syncPending; + }).then(function(syncPending) { + Logger.log("sync launched = "+syncPending); + if (syncPending) { + Logger.log("data is pending, showing confirm dialog"); + setDataPendingVis(true); //consent handling in modal + } else { + setDataPushedVis(true); + } + }).catch(function(error) { + Logger.displayError("Error while forcing sync", error); + }); + }; + + return ( + <> + + + {/* dataPending */} + setDataPendingVis(false)} transparent={true}> + setDataPendingVis(false)} + style={settingStyles.dialog(colors.elevation.level3)}> + {t('data pending for push')} + + + + + + + + + + ) +} + +//UI for editing the sync config const ControlSyncHelper = ({ editVis, setEditVis }) => { const { t } = useTranslation(); const { colors } = useTheme(); diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 46c6b05ae..8e6b98876 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -17,7 +17,7 @@ import PrivacyPolicyModal from "./PrivacyPolicyModal"; import ActionMenu from "../components/ActionMenu"; import SensedPage from "./SensedPage" import LogPage from "./LogPage"; -import ControlSyncHelper, {forcePluginSync, getHelperSyncSettings} from "./ControlSyncHelper"; +import ControlSyncHelper, {ForceSyncRow, getHelperSyncSettings} from "./ControlSyncHelper"; import ControlCollectionHelper, {getHelperCollectionSettings, getState, isMediumAccuracy, helperToggleLowAccuracy, forceTransitionWrapper} from "./ControlCollectionHelper"; //any pure functions can go outside @@ -49,8 +49,6 @@ const ProfileSettings = () => { const [forceStateVis, setForceStateVis] = useState(false); const [permitVis, setPermitVis] = useState(false); const [logoutVis, setLogoutVis] = useState(false); - const [dataPendingVis, setDataPendingVis] = useState(false); - const [dataPushedVis, setDataPushedVis] = useState(false); const [invalidateSuccessVis, setInvalidateSuccessVis] = useState(false); const [noConsentVis, setNoConsentVis] = useState(false); const [noConsentMessageVis, setNoConsentMessageVis] = useState(false); @@ -383,51 +381,6 @@ const ProfileSettings = () => { }).then(forceSync); } - //showing up in an odd space on the screen!! - async function forceSync() { - ClientStats.addEvent(ClientStats.getStatKeys().BUTTON_FORCE_SYNC).then( - function() { - console.log("Added "+ClientStats.getStatKeys().BUTTON_FORCE_SYNC+" event"); - }); - forcePluginSync().then(function() { - /* - * Change to sensorKey to "background/location" after fixing issues - * with getLastSensorData and getLastMessages in the usercache - * See https://github.com/e-mission/e-mission-phone/issues/279 for details - */ - var sensorKey = "statemachine/transition"; - return window.cordova.plugins.BEMUserCache.getAllMessages(sensorKey, true); - }).then(function(sensorDataList) { - Logger.log("sensorDataList = "+JSON.stringify(sensorDataList)); - // If everything has been pushed, we should - // only have one entry for the battery, which is the one that was - // inserted on the last successful push. - var isTripEnd = function(entry) { - if (entry.metadata.key == getEndTransitionKey()) { - return true; - } else { - return false; - } - }; - var syncLaunchedCalls = sensorDataList.filter(isTripEnd); - var syncPending = (syncLaunchedCalls.length > 0); - Logger.log("sensorDataList.length = "+sensorDataList.length+ - ", syncLaunchedCalls.length = "+syncLaunchedCalls.length+ - ", syncPending? = "+syncPending); - return syncPending; - }).then(function(syncPending) { - Logger.log("sync launched = "+syncPending); - if (syncPending) { - Logger.log("data is pending, showing confirm dialog"); - setDataPendingVis(true); //consent handling in modal - } else { - setDataPushedVis(true); - } - }).catch(function(error) { - Logger.displayError("Error while forcing sync", error); - }); - }; - async function invalidateCache() { window.cordova.plugins.BEMUserCache.invalidateAllCache().then(function(result) { console.log("invalidate result", result); @@ -497,7 +450,7 @@ const ProfileSettings = () => { setPermitVis(true)}> setCarbonDataVis(true)}> - + setDateDumpVis(true)}> {logUploadSection} @@ -585,27 +538,6 @@ const ProfileSettings = () => {
- {/* dataPending */} - setDataPendingVis(false)} transparent={true}> - setDataPendingVis(false)} - style={settingStyles.dialog(colors.elevation.level3)}> - {t('data pending for push')} - - - - - - - {/* handle no consent */} setNoConsentVis(false)} transparent={true}> { minDate={new Date(appConfig?.intro?.start_year, appConfig?.intro?.start_month - 1, 1)}> - From df787582310684006fa62f3fd020f805e1dab586 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Thu, 14 Sep 2023 14:29:05 -0600 Subject: [PATCH 24/30] don't store a duplicate copy of the collectConfig this was one way I attempted to control the ControlSettings, but moved to set/get from a central location (plugin) for one-off changes (toggles) and using the local config, and saving once, when editing with the settings editor modal --- www/js/control/ControlCollectionHelper.tsx | 4 ++-- www/js/control/ProfileSettings.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/js/control/ControlCollectionHelper.tsx b/www/js/control/ControlCollectionHelper.tsx index cf8f95053..6d829a3a6 100644 --- a/www/js/control/ControlCollectionHelper.tsx +++ b/www/js/control/ControlCollectionHelper.tsx @@ -122,11 +122,11 @@ const formatConfigForDisplay = function(config, accuracyOptions) { return retVal; } -const ControlSyncHelper = ({ editVis, setEditVis, localConfig, setLocalConfig }) => { +const ControlSyncHelper = ({ editVis, setEditVis }) => { const {colors} = useTheme(); const Logger = getAngularService("Logger"); - // const [ localConfig, setLocalConfig ] = useState(); + const [ localConfig, setLocalConfig ] = useState(); const [ accuracyActions, setAccuracyActions ] = useState([]); const [ accuracyVis, setAccuracyVis ] = useState(false); diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 8e6b98876..05366d480 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -62,7 +62,7 @@ const ProfileSettings = () => { const [forceResultVis, setForceResultVis] = useState(false); const [forceResult, setForceResult] = useState(""); - const [collectConfig, setCollectConfig] = useState({}); + // const [collectConfig, setCollectConfig] = useState({}); const [collectSettings, setCollectSettings] = useState({}); const [notificationSettings, setNotificationSettings] = useState({}); const [authSettings, setAuthSettings] = useState({}); @@ -588,7 +588,7 @@ const ProfileSettings = () => { - + ); From 1551eee6da264283aea38487b37ba11998db4fe9 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Fri, 15 Sep 2023 16:19:48 -0600 Subject: [PATCH 25/30] move functions into helpers moving endForceSync and forceTransition into their respective helpers --- www/js/control/ControlCollectionHelper.tsx | 12 +++- www/js/control/ControlSyncHelper.tsx | 56 ++++++++++++++++- www/js/control/ProfileSettings.jsx | 73 +--------------------- 3 files changed, 68 insertions(+), 73 deletions(-) diff --git a/www/js/control/ControlCollectionHelper.tsx b/www/js/control/ControlCollectionHelper.tsx index 6d829a3a6..7764f526b 100644 --- a/www/js/control/ControlCollectionHelper.tsx +++ b/www/js/control/ControlCollectionHelper.tsx @@ -21,6 +21,16 @@ type collectionConfig = { // const Logger = getAngularService("Logger"); +export async function forceTransition(transition) { + try { + let result = forceTransitionWrapper(transition); + window.alert('success -> '+result); + } catch (err) { + window.alert('error -> '+err); + console.log("error forcing state", err); + } +} + async function accuracy2String(config) { var accuracy = config.accuracy; let accuracyOptions = await getAccuracyOptions(); @@ -43,7 +53,7 @@ export async function isMediumAccuracy() { } else if (window.cordova.platformId == 'android') { return v != "PRIORITY_HIGH_ACCURACY"; } else { - // $ionicPopup.alert("Emission does not support this platform"); + window.alert("Emission does not support this platform"); } } } diff --git a/www/js/control/ControlSyncHelper.tsx b/www/js/control/ControlSyncHelper.tsx index bb032f398..15ba6b4c2 100644 --- a/www/js/control/ControlSyncHelper.tsx +++ b/www/js/control/ControlSyncHelper.tsx @@ -7,6 +7,7 @@ import { settingStyles } from "./ProfileSettings"; import { getAngularService } from "../angular-react-helper"; import SettingRow from "./SettingRow"; import AlertBar from "./AlertBar"; +import moment from "moment"; /* * BEGIN: Simple read/write wrappers @@ -48,7 +49,7 @@ const getEndTransitionKey = function() { type syncConfig = { sync_interval: number, ios_use_remote_push: boolean }; -export const ForceSyncRow = () => { +export const ForceSyncRow = ({getState}) => { const { t } = useTranslation(); const { colors } = useTheme(); const ClientStats = getAngularService('ClientStats'); @@ -101,9 +102,62 @@ export const ForceSyncRow = () => { }); }; + const getStartTransitionKey = function() { + if(window.cordova.platformId == 'android') { + return "local.transition.exited_geofence"; + } + else if(window.cordova.platformId == 'ios') { + return "T_EXITED_GEOFENCE"; + } + } + + const getEndTransitionKey = function() { + if(window.cordova.platformId == 'android') { + return "local.transition.stopped_moving"; + } + else if(window.cordova.platformId == 'ios') { + return "T_TRIP_ENDED"; + } + } + + const getOngoingTransitionState = function() { + if(window.cordova.platformId == 'android') { + return "local.state.ongoing_trip"; + } + else if(window.cordova.platformId == 'ios') { + return "STATE_ONGOING_TRIP"; + } + } + + async function getTransition(transKey) { + var entry_data = {}; + const curr_state = await getState(); + entry_data.curr_state = curr_state; + if (transKey == getEndTransitionKey()) { + entry_data.curr_state = getOngoingTransitionState(); + } + entry_data.transition = transKey; + entry_data.ts = moment().unix(); + return entry_data; + } + + async function endForceSync() { + /* First, quickly start and end the trip. Let's listen to the promise + * result for start so that we ensure ordering */ + var sensorKey = "statemachine/transition"; + return getTransition(getStartTransitionKey()).then(function(entry_data) { + return window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); + }).then(function() { + return getTransition(getEndTransitionKey()).then(function(entry_data) { + return window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); + }) + }).then(forceSync); + }; + return ( <> + {/* dataPending */} setDataPendingVis(false)} transparent={true}> diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 05366d480..82a282da2 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -18,7 +18,7 @@ import ActionMenu from "../components/ActionMenu"; import SensedPage from "./SensedPage" import LogPage from "./LogPage"; import ControlSyncHelper, {ForceSyncRow, getHelperSyncSettings} from "./ControlSyncHelper"; -import ControlCollectionHelper, {getHelperCollectionSettings, getState, isMediumAccuracy, helperToggleLowAccuracy, forceTransitionWrapper} from "./ControlCollectionHelper"; +import ControlCollectionHelper, {getHelperCollectionSettings, getState, isMediumAccuracy, helperToggleLowAccuracy, forceTransition} from "./ControlCollectionHelper"; //any pure functions can go outside const ProfileSettings = () => { @@ -59,8 +59,6 @@ const ProfileSettings = () => { const [showingLog, setShowingLog] = useState(false); const [editSync, setEditSync] = useState(false); const [editCollection, setEditCollection] = useState(false); - const [forceResultVis, setForceResultVis] = useState(false); - const [forceResult, setForceResult] = useState(""); // const [collectConfig, setCollectConfig] = useState({}); const [collectSettings, setCollectSettings] = useState({}); @@ -256,18 +254,6 @@ const ProfileSettings = () => { refreshCollectSettings(); } - async function forceTransition(transition) { - try { - let result = forceTransitionWrapper(transition); - setForceResultVis(true); - setForceResult('success -> '+result) - } catch (err) { - console.log("error forcing state", err); - setForceResultVis(true); - setForceResult('error -> '+err) - } - } - async function toggleLowAccuracy() { let toggle = await helperToggleLowAccuracy(); refreshCollectSettings(); @@ -313,46 +299,6 @@ const ProfileSettings = () => { //Platform.OS returns "web" now, but could be used once it's fully a Native app //for now, use window.cordova.platformId - // helper functions for endForceSync - const getStartTransitionKey = function() { - if(window.cordova.platformId == 'android') { - return "local.transition.exited_geofence"; - } - else if(window.cordova.platformId == 'ios') { - return "T_EXITED_GEOFENCE"; - } - } - - const getEndTransitionKey = function() { - if(window.cordova.platformId == 'android') { - return "local.transition.stopped_moving"; - } - else if(window.cordova.platformId == 'ios') { - return "T_TRIP_ENDED"; - } - } - - const getOngoingTransitionState = function() { - if(window.cordova.platformId == 'android') { - return "local.state.ongoing_trip"; - } - else if(window.cordova.platformId == 'ios') { - return "STATE_ONGOING_TRIP"; - } - } - - async function getTransition(transKey) { - var entry_data = {}; - const curr_state = await getState(); - entry_data.curr_state = curr_state; - if (transKey == getEndTransitionKey()) { - entry_data.curr_state = getOngoingTransitionState(); - } - entry_data.transition = transKey; - entry_data.ts = moment().unix(); - return entry_data; - } - const parseState = function(state) { console.log("state in parse state is", state); if (state) { @@ -368,19 +314,6 @@ const ProfileSettings = () => { } } - async function endForceSync() { - /* First, quickly start and end the trip. Let's listen to the promise - * result for start so that we ensure ordering */ - var sensorKey = "statemachine/transition"; - return getTransition(getStartTransitionKey()).then(function(entry_data) { - return window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); - }).then(function() { - return getTransition(getEndTransitionKey()).then(function(entry_data) { - return window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); - }) - }).then(forceSync); - } - async function invalidateCache() { window.cordova.plugins.BEMUserCache.invalidateAllCache().then(function(result) { console.log("invalidate result", result); @@ -450,14 +383,13 @@ const ProfileSettings = () => { setPermitVis(true)}> setCarbonDataVis(true)}> - + setDateDumpVis(true)}> {logUploadSection} - {notifSchedule} @@ -582,7 +514,6 @@ const ProfileSettings = () => { - From 1cfb190d65b1a7a7ab6636e5a86f671206c41c02 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Fri, 15 Sep 2023 16:41:38 -0600 Subject: [PATCH 26/30] restore log statement --- www/js/control/ControlCollectionHelper.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/www/js/control/ControlCollectionHelper.tsx b/www/js/control/ControlCollectionHelper.tsx index 7764f526b..b0855a733 100644 --- a/www/js/control/ControlCollectionHelper.tsx +++ b/www/js/control/ControlCollectionHelper.tsx @@ -19,8 +19,6 @@ type collectionConfig = { android_geofence_responsiveness: number }; -// const Logger = getAngularService("Logger"); - export async function forceTransition(transition) { try { let result = forceTransitionWrapper(transition); @@ -59,6 +57,7 @@ export async function isMediumAccuracy() { } export async function helperToggleLowAccuracy() { + const Logger = getAngularService("Logger"); let tempConfig = await getConfig(); let accuracyOptions = await getAccuracyOptions(); let medium = await isMediumAccuracy(); @@ -79,7 +78,7 @@ export async function helperToggleLowAccuracy() { let set = await setConfig(tempConfig); console.log("setConfig Sucess"); } catch (err) { - // Logger.displayError("Error while setting collection config", err); + Logger.displayError("Error while setting collection config", err); } } From f0129735229f40f2f9de72e9fae0705792800783 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Fri, 15 Sep 2023 16:42:07 -0600 Subject: [PATCH 27/30] fetch DB before updating --- www/js/control/SensedPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/js/control/SensedPage.tsx b/www/js/control/SensedPage.tsx index 8de1e7e5f..2f62d5c6c 100644 --- a/www/js/control/SensedPage.tsx +++ b/www/js/control/SensedPage.tsx @@ -21,6 +21,7 @@ const SensedPage = ({pageVis, setPageVis}) => { async function updateEntries() { //hardcoded function and keys after eliminating bit-rotted options + setDB(window.cordova.plugins.BEMUserCache); let userCacheFn = DB.getAllMessages; let userCacheKey = "statemachine/transition"; try { @@ -41,7 +42,6 @@ const SensedPage = ({pageVis, setPageVis}) => { } useEffect(() => { - setDB(window.cordova.plugins.BEMUserCache); updateEntries(); }, [pageVis]); From c46e2dcf2d3aa62662e6c5f9c13ab0b095ef1dd4 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 18 Sep 2023 10:01:51 -0600 Subject: [PATCH 28/30] move sync rows to devZone --- www/js/control/ProfileSettings.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/js/control/ProfileSettings.jsx b/www/js/control/ProfileSettings.jsx index 82a282da2..dcbedbd78 100644 --- a/www/js/control/ProfileSettings.jsx +++ b/www/js/control/ProfileSettings.jsx @@ -383,7 +383,6 @@ const ProfileSettings = () => { setPermitVis(true)}> setCarbonDataVis(true)}> - setDateDumpVis(true)}> {logUploadSection} @@ -391,6 +390,7 @@ const ProfileSettings = () => { + {notifSchedule} From 7f8ff42a67d72b1eb67fb72f720d23e5581dccaf Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 18 Sep 2023 10:33:02 -0600 Subject: [PATCH 29/30] move from .then to await for consistency and readability, re-writing the forceSync to rely on await rather than chained .then statements --- www/js/control/ControlSyncHelper.tsx | 60 +++++++++++++--------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/www/js/control/ControlSyncHelper.tsx b/www/js/control/ControlSyncHelper.tsx index 15ba6b4c2..341d9bd86 100644 --- a/www/js/control/ControlSyncHelper.tsx +++ b/www/js/control/ControlSyncHelper.tsx @@ -2,9 +2,9 @@ import React, { useEffect, useState } from "react"; import { Modal, View } from "react-native"; import { Dialog, Button, Switch, Text, useTheme } from 'react-native-paper'; import { useTranslation } from "react-i18next"; -import ActionMenu from "../components/ActionMenu"; import { settingStyles } from "./ProfileSettings"; import { getAngularService } from "../angular-react-helper"; +import ActionMenu from "../components/ActionMenu"; import SettingRow from "./SettingRow"; import AlertBar from "./AlertBar"; import moment from "moment"; @@ -49,6 +49,7 @@ const getEndTransitionKey = function() { type syncConfig = { sync_interval: number, ios_use_remote_push: boolean }; +//forceSync and endForceSync SettingRows & their actions export const ForceSyncRow = ({getState}) => { const { t } = useTranslation(); const { colors } = useTheme(); @@ -59,47 +60,40 @@ export const ForceSyncRow = ({getState}) => { const [dataPushedVis, setDataPushedVis] = useState(false); async function forceSync() { - ClientStats.addEvent(ClientStats.getStatKeys().BUTTON_FORCE_SYNC).then( - function() { - console.log("Added "+ClientStats.getStatKeys().BUTTON_FORCE_SYNC+" event"); - }); - forcePluginSync().then(function() { + try { + let addedEvent = ClientStats.addEvent(ClientStats.getStatKeys().BUTTON_FORCE_SYNC); + console.log("Added "+ClientStats.getStatKeys().BUTTON_FORCE_SYNC+" event"); + + let sync = await forcePluginSync(); /* - * Change to sensorKey to "background/location" after fixing issues - * with getLastSensorData and getLastMessages in the usercache - * See https://github.com/e-mission/e-mission-phone/issues/279 for details - */ + * Change to sensorKey to "background/location" after fixing issues + * with getLastSensorData and getLastMessages in the usercache + * See https://github.com/e-mission/e-mission-phone/issues/279 for details + */ var sensorKey = "statemachine/transition"; - return window.cordova.plugins.BEMUserCache.getAllMessages(sensorKey, true); - }).then(function(sensorDataList) { - Logger.log("sensorDataList = "+JSON.stringify(sensorDataList)); + let sensorDataList = await window.cordova.plugins.BEMUserCache.getAllMessages(sensorKey, true); + // If everything has been pushed, we should - // only have one entry for the battery, which is the one that was - // inserted on the last successful push. - var isTripEnd = function(entry) { - if (entry.metadata.key == getEndTransitionKey()) { - return true; - } else { - return false; - } - }; - var syncLaunchedCalls = sensorDataList.filter(isTripEnd); - var syncPending = (syncLaunchedCalls.length > 0); + // have no more trip end transitions left + let isTripEnd = function(entry) { + return entry.metadata == getEndTransitionKey(); + } + let syncLaunchedCalls = sensorDataList.filter(isTripEnd); + let syncPending = syncLaunchedCalls.length > 0; Logger.log("sensorDataList.length = "+sensorDataList.length+ - ", syncLaunchedCalls.length = "+syncLaunchedCalls.length+ - ", syncPending? = "+syncPending); - return syncPending; - }).then(function(syncPending) { + ", syncLaunchedCalls.length = "+syncLaunchedCalls.length+ + ", syncPending? = "+syncPending); Logger.log("sync launched = "+syncPending); - if (syncPending) { - Logger.log("data is pending, showing confirm dialog"); - setDataPendingVis(true); //consent handling in modal + + if(syncPending) { + Logger.log(Logger.log("data is pending, showing confirm dialog")); + setDataPendingVis(true); //consent handling in modal } else { setDataPushedVis(true); } - }).catch(function(error) { + } catch (error) { Logger.displayError("Error while forcing sync", error); - }); + } }; const getStartTransitionKey = function() { From 287687291e6e2931959c85502c6a7a96aa370934 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 18 Sep 2023 10:38:55 -0600 Subject: [PATCH 30/30] endTripForceSync using await for readability, switch from chained .then statements to using await calls --- www/js/control/ControlSyncHelper.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/www/js/control/ControlSyncHelper.tsx b/www/js/control/ControlSyncHelper.tsx index 341d9bd86..490672c4d 100644 --- a/www/js/control/ControlSyncHelper.tsx +++ b/www/js/control/ControlSyncHelper.tsx @@ -139,13 +139,11 @@ export const ForceSyncRow = ({getState}) => { /* First, quickly start and end the trip. Let's listen to the promise * result for start so that we ensure ordering */ var sensorKey = "statemachine/transition"; - return getTransition(getStartTransitionKey()).then(function(entry_data) { - return window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); - }).then(function() { - return getTransition(getEndTransitionKey()).then(function(entry_data) { - return window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); - }) - }).then(forceSync); + let entry_data = await getTransition(getStartTransitionKey()); + let messagePut = await window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); + entry_data = await getTransition(getEndTransitionKey()); + messagePut = await window.cordova.plugins.BEMUserCache.putMessage(sensorKey, entry_data); + forceSync(); }; return (