Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize Labels Step 1 #1110

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1b57f5e
show customized modes (used dummy data for now)
jiji14 Dec 1, 2023
cad2d7c
fix prettier issu
jiji14 Dec 1, 2023
74e2dfe
fetch post/mode
jiji14 Dec 4, 2023
0402a34
use displayErrorMsg
jiji14 Dec 4, 2023
09b56ee
Done with fetching data to server
jiji14 Dec 7, 2023
a3cb1f2
update mode - old mode and new mode
jiji14 Dec 8, 2023
abc7f8a
Change modes type to string array and call it when modal opens
jiji14 Dec 11, 2023
cb1c491
add comments
jiji14 Dec 11, 2023
0a7a4ba
change mode text in a desired format (ex) my_car => My Car
jiji14 Dec 12, 2023
8ba3fb6
add default mode & custom mode label
jiji14 Dec 13, 2023
aa18e98
noted that getModes & updateMode need to be tested after e2e testing …
jiji14 Dec 14, 2023
7548de7
reuse existing common function and delete the original function
jiji14 Dec 18, 2023
01c1afe
use dynamic color
jiji14 Dec 18, 2023
b5a63be
change logic for showing custom modes before 'other' option
jiji14 Dec 18, 2023
f64d719
change function names more descriptive
jiji14 Dec 18, 2023
14c4438
delete unnecessary variable and change variable names more intuitive
jiji14 Dec 18, 2023
447e7e2
add new translation entry
jiji14 Dec 18, 2023
0509d5e
Fetch custom modes on initial rendering to prevent unnecessary api calls
jiji14 Dec 27, 2023
189f79d
check if inputType is 'MODE' for custom mode label
jiji14 Jan 3, 2024
643ae90
Modify the mode functions to encompass all types of labels (purpose, …
jiji14 Jan 5, 2024
01a3729
fix radio button issue when 'other' option is selected
jiji14 Jan 9, 2024
8cec495
change variable names for better understanding
jiji14 Jan 9, 2024
606816b
update radio button comment
jiji14 Jan 9, 2024
2d7352b
combine replaced_mode and mode
jiji14 Jan 11, 2024
4e484a9
Merge branch 'service_rewrite_2023' of https://github.com/e-mission/e…
JGreenlee Feb 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions www/__tests__/commHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ it('fetches text from a URL and caches it so the next call is faster', async ()
* - updateUser
* - getUser
* - putOne
* - getModes
* - updateMode
jiji14 marked this conversation as resolved.
Show resolved Hide resolved
*/
5 changes: 4 additions & 1 deletion www/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
"trip-confirm": {
"services-please-fill-in": "Please fill in the {{text}} not listed.",
"services-cancel": "Cancel",
"services-save": "Save"
"services-save": "Save",
"default-mode": "Default Mode",
"custom-mode": "Custom Mode",
"default-purpose": "Default Purpose"
},

"control": {
Expand Down
7 changes: 5 additions & 2 deletions www/js/diary/LabelTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { fillLocationNamesOfTrip, resetNominatimLimiter } from './addressNamesHe
import { getLabelOptions } from '../survey/multilabel/confirmHelper';
import { displayError, displayErrorMsg, logDebug, logWarn } from '../plugin/logger';
import { useTheme } from 'react-native-paper';
import { getPipelineRangeTs } from '../services/commHelper';
import { getPipelineRangeTs, getUserCustomModes } from '../services/commHelper';
import { mapInputsToTimelineEntries } from '../survey/inputMatcher';
import { configuredFilters as multilabelConfiguredFilters } from '../survey/multilabel/infinite_scroll_filters';
import { configuredFilters as enketoConfiguredFilters } from '../survey/enketo/infinite_scroll_filters';
Expand Down Expand Up @@ -56,6 +56,7 @@ const LabelTab = () => {
const [displayedEntries, setDisplayedEntries] = useState(null);
const [refreshTime, setRefreshTime] = useState(null);
const [isLoading, setIsLoading] = useState<string | false>('replace');
const [customModes, setCustomModes] = useState<string[]>([]);

const Timeline = getAngularService('Timeline');

Expand All @@ -65,7 +66,7 @@ const LabelTab = () => {
if (!appConfig) return;
showPlaces = appConfig.survey_info?.buttons?.['place-notes'];
getLabelOptions(appConfig).then((labelOptions) => setLabelOptions(labelOptions));

getUserCustomModes().then((res) => setCustomModes(res['modes'] as string[]));
// we will show filters if 'additions' are not configured
// https://github.com/e-mission/e-mission-docs/issues/894
if (appConfig.survey_info?.buttons == undefined) {
Expand Down Expand Up @@ -322,6 +323,8 @@ const LabelTab = () => {
loadSpecificWeek,
refresh,
repopulateTimelineEntry,
customModes,
setCustomModes,
};

const Tab = createStackNavigator();
Expand Down
4 changes: 3 additions & 1 deletion www/js/diary/LabelTabContext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext } from 'react';
import { Dispatch, SetStateAction, createContext } from 'react';
import { TimelineEntry, UserInputEntry } from '../types/diaryTypes';
import { LabelOption } from '../survey/multilabel/confirmHelper';

Expand Down Expand Up @@ -34,6 +34,8 @@ type ContextProps = {
loadSpecificWeek: any; // TODO
refresh: any; // TODO
repopulateTimelineEntry: any; // TODO
customModes: string[];
setCustomModes: Dispatch<SetStateAction<string[]>>;
};

export default createContext<ContextProps>(null);
29 changes: 29 additions & 0 deletions www/js/services/commHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,32 @@ export function putOne(key, data) {
throw error;
});
}

export function getUserCustomModes() {
return new Promise((rs, rj) => {
window['cordova'].plugins.BEMServerComm.getUserPersonalData('/mode/get', rs, rj);
}).catch((error) => {
error = 'While getting modes, ' + error;
throw error;
});
}

export function updateUserCustomMode(oldMode, newMode, isNewModeMustAdded) {
const updatedMode = {
old_mode: oldMode,
new_mode: newMode,
is_new_mode_must_added: isNewModeMustAdded,
};
return new Promise((rs, rj) => {
window['cordova'].plugins.BEMServerComm.postUserPersonalData(
'/mode/update',
'updated_mode',
updatedMode,
rs,
rj,
);
}).catch((error) => {
error = 'While putting one mode, ' + error;
throw error;
});
}
90 changes: 69 additions & 21 deletions www/js/survey/multilabel/MultiLabelButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
RadioButton,
Button,
TextInput,
Divider,
} from 'react-native-paper';
import DiaryButton from '../../components/DiaryButton';
import { useTranslation } from 'react-i18next';
Expand All @@ -23,29 +24,29 @@ import {
getLabelInputs,
inferFinalLabels,
labelInputDetailsForTrip,
labelKeyToReadable,
labelKeyToRichMode,
readableLabelToKey,
verifiabilityForTrip,
} from './confirmHelper';
import useAppConfig from '../../useAppConfig';
import { updateUserCustomMode } from '../../services/commHelper';

const MultilabelButtonGroup = ({ trip, buttonsInline = false }) => {
const { colors } = useTheme();
const { t } = useTranslation();
const appConfig = useAppConfig();
const { repopulateTimelineEntry, labelOptions, timelineLabelMap } = useContext(LabelTabContext);
const { repopulateTimelineEntry, labelOptions, timelineLabelMap, customModes, setCustomModes } =
useContext(LabelTabContext);
const { height: windowHeight } = useWindowDimensions();

// modal visible for which input type? (mode or purpose or replaced_mode, null if not visible)
const [modalVisibleFor, setModalVisibleFor] = useState<
'MODE' | 'PURPOSE' | 'REPLACED_MODE' | null
>(null);
const [otherLabel, setOtherLabel] = useState<string | null>(null);
const chosenLabel = useMemo<string>(() => {
if (otherLabel != null) return 'other';
const initialLabel = useMemo<string>(() => {
return timelineLabelMap[trip._id.$oid]?.[modalVisibleFor]?.value;
}, [modalVisibleFor, otherLabel]);

}, [modalVisibleFor]);
// to mark 'inferred' labels as 'confirmed'; turn yellow labels blue
function verifyTrip() {
const inferredLabelsForTrip = inferFinalLabels(trip, timelineLabelMap[trip._id.$oid]);
Expand All @@ -72,19 +73,34 @@ const MultilabelButtonGroup = ({ trip, buttonsInline = false }) => {
setOtherLabel(null);
}

function store(inputType, chosenLabel, isOther) {
if (!chosenLabel) return displayErrorMsg('Label is empty');
function store(inputType, newLabel, isOther) {
if (!newLabel) return displayErrorMsg('Label is empty');
if (isOther) {
/* Let's make the value for user entered inputs look consistent with our other values
(i.e. lowercase, and with underscores instead of spaces) */
chosenLabel = readableLabelToKey(chosenLabel);
newLabel = readableLabelToKey(newLabel);
}
// If a user saves a new customized mode or makes changes to/from customized modes, the modes need to be updated.
if (isOther || customModes.indexOf(initialLabel) > -1 || customModes.indexOf(newLabel) > -1) {
if (inputType === 'MODE') {
updateUserCustomMode(initialLabel ?? '', newLabel, isOther)
.then((res) => {
setCustomModes(res['modes'] as string[]);
logDebug('Successfuly stored custom mode ' + JSON.stringify(res));
})
.catch((e) => {
displayErrorMsg(e, 'Create Mode Error');
});
}
if (inputType === 'PURPOSE') {
// TODO : update custom purpose
}
}
const inputDataToStore = {
start_ts: trip.start_ts,
end_ts: trip.end_ts,
label: chosenLabel,
label: newLabel,
};

const storageKey = getLabelInputDetails()[inputType].key;
window['cordova'].plugins.BEMUserCache.putMessage(storageKey, inputDataToStore).then(() => {
dismiss();
Expand All @@ -94,6 +110,7 @@ const MultilabelButtonGroup = ({ trip, buttonsInline = false }) => {
}

const tripInputDetails = labelInputDetailsForTrip(timelineLabelMap[trip._id.$oid], appConfig);

return (
<>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
Expand Down Expand Up @@ -151,16 +168,46 @@ const MultilabelButtonGroup = ({ trip, buttonsInline = false }) => {
</Dialog.Title>
<Dialog.Content style={{ maxHeight: windowHeight / 2, paddingBottom: 0 }}>
<ScrollView style={{ paddingBottom: 24 }}>
<RadioButton.Group onValueChange={(val) => onChooseLabel(val)} value={chosenLabel}>
{labelOptions?.[modalVisibleFor]?.map((o, i) => (
// @ts-ignore
<RadioButton.Item
key={i}
label={t(o.text)}
value={o.value}
style={{ paddingVertical: 2 }}
/>
))}
<Text style={{ fontSize: 12, color: colors.onSurface, paddingVertical: 4 }}>
{modalVisibleFor === 'MODE' && t('trip-confirm.default-mode')}
{modalVisibleFor === 'PURPOSE' && t('trip-confirm.default-purpose')}
</Text>
<RadioButton.Group onValueChange={(val) => onChooseLabel(val)} value={initialLabel}>
{labelOptions?.[modalVisibleFor]?.map((o, i) => {
const radioItemForOption = (
<RadioButton.Item
key={i}
label={t(o.text)}
value={o.value}
style={{ paddingVertical: 2 }}
/>
);
/* if this is the 'other' option and there are some custom modes,
show the custom modes section before 'other' */
if (modalVisibleFor == 'MODE' && o.value == 'other' && customModes?.length) {
return (
<>
<Divider style={{ marginVertical: 10 }} />
<Text
style={{ fontSize: 12, color: colors.onSurface, paddingVertical: 4 }}>
{t('trip-confirm.custom-mode')}
</Text>
{customModes.map((key, i) => (
<RadioButton.Item
key={i}
label={labelKeyToReadable(key)}
value={key}
style={{ paddingVertical: 2 }}
/>
))}
<Divider style={{ marginVertical: 10 }} />
{radioItemForOption}
</>
);
}
// otherwise, just show the radio item as normal
return radioItemForOption;
})}
</RadioButton.Group>
</ScrollView>
</Dialog.Content>
Expand All @@ -172,6 +219,7 @@ const MultilabelButtonGroup = ({ trip, buttonsInline = false }) => {
})}
value={otherLabel || ''}
onChangeText={(t) => setOtherLabel(t)}
maxLength={25}
jiji14 marked this conversation as resolved.
Show resolved Hide resolved
/>
<Dialog.Actions>
<Button onPress={() => store(modalVisibleFor, otherLabel, true)}>
Expand Down
Loading