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

Implemented: logic to update notification icon after viewing and added notification preference card on the settings page (#301) #304

Merged
merged 8 commits into from
Oct 9, 2023
22 changes: 9 additions & 13 deletions src/components/NotificationPreferenceModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ import { translate } from "@/i18n";
import { showToast } from "@/utils";
import emitter from "@/event-bus"
import { generateTopicName } from "@/utils/firebase";
import {
subscribeTopic,
unsubscribeTopic
} from '@/adapter';
import { subscribeTopic, unsubscribeTopic } from '@/adapter'

export default defineComponent({
name: "NotificationPreferenceModal",
Expand All @@ -78,7 +75,7 @@ export default defineComponent({
data() {
return {
notificationPrefState: {} as any,
notificationPrefToUpate: {
notificationPrefToUpdate: {
subscribe: [],
unsubscribe: []
} as any,
Expand Down Expand Up @@ -115,16 +112,16 @@ export default defineComponent({
// running when the ion-toggle hydrates and hence, updated the
// initialNotificationPrefState here
const value = !event.target.checked
// updates the notificationPrefToUpate to check which pref
// updates the notificationPrefToUpdate to check which pref
// values were updated from their initial values
if (value !== this.initialNotificationPrefState[enumId]) {
value
? this.notificationPrefToUpate.subscribe.push(enumId)
: this.notificationPrefToUpate.unsubscribe.push(enumId)
? this.notificationPrefToUpdate.subscribe.push(enumId)
: this.notificationPrefToUpdate.unsubscribe.push(enumId)
} else {
!value
? this.notificationPrefToUpate.subscribe.splice(this.notificationPrefToUpate.subscribe.indexOf(enumId), 1)
: this.notificationPrefToUpate.unsubscribe.splice(this.notificationPrefToUpate.subscribe.indexOf(enumId), 1)
? this.notificationPrefToUpdate.subscribe.splice(this.notificationPrefToUpdate.subscribe.indexOf(enumId), 1)
: this.notificationPrefToUpdate.unsubscribe.splice(this.notificationPrefToUpdate.subscribe.indexOf(enumId), 1)
}

// updating this.notificationPrefState as it is used to
Expand All @@ -133,7 +130,6 @@ export default defineComponent({
this.notificationPrefState[enumId] = value
},
async updateNotificationPref() {
// TODO disbale button if initial and final are same
// added loader as the API call is in pending state for too long, blocking the flow
emitter.emit("presentLoader");
try {
Expand All @@ -148,13 +144,13 @@ export default defineComponent({
const ofbizInstanceName = this.userProfile.ofbizInstanceName
const facilityId = (this.currentFacility as any).facilityId
const subscribeRequests = [] as any
this.notificationPrefToUpate.subscribe.map(async (enumId: string) => {
this.notificationPrefToUpdate.subscribe.map(async (enumId: string) => {
const topicName = generateTopicName(ofbizInstanceName, facilityId, enumId)
await subscribeRequests.push(subscribeTopic(topicName, process.env.VUE_APP_NOTIF_APP_ID))
})

const unsubscribeRequests = [] as any
this.notificationPrefToUpate.unsubscribe.map(async (enumId: string) => {
this.notificationPrefToUpdate.unsubscribe.map(async (enumId: string) => {
const topicName = generateTopicName(ofbizInstanceName, facilityId, enumId)
await unsubscribeRequests.push(unsubscribeTopic(topicName, process.env.VUE_APP_NOTIF_APP_ID))
})
Expand Down
1 change: 1 addition & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"Search time zones": "Search time zones",
"Select facility": "Select facility",
"Select time zone": "Select time zone",
"Select the notifications you want to receive.": "Select the notifications you want to receive.",
"Select your preferred language.": "Select your preferred language.",
"Settings": "Settings",
"Select a picker": "Select a picker",
Expand Down
1 change: 1 addition & 0 deletions src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"Search time zones": "Buscar zonas horarias",
"Select facility": "Seleccionar instalación",
"Select time zone": "Seleccionar zona horaria",
"Select the notifications you want to receive.": "Select the notifications you want to receive.",
"Select your preferred language.": "Selecciona tu idioma preferido.",
"Settings": "Configuraciones",
"Select a picker": "Seleccionar un recolector",
Expand Down
1 change: 1 addition & 0 deletions src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"Search time zones": "タイムゾーンの検索",
"Select facility": "拠点の検索",
"Select time zone": "タイムゾーンを選択",
"Select the notifications you want to receive.": "Select the notifications you want to receive.",
"Select your preferred language.": "お好みの言語の選択",
"Settings": "設定",
"Select a picker": "受取人の選択",
Expand Down
1 change: 1 addition & 0 deletions src/store/modules/user/UserState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export default interface UserState {
notifications: any;
notificationPrefs: any;
firebaseDeviceId: string;
hasUnreadNotifications: boolean
}
6 changes: 6 additions & 0 deletions src/store/modules/user/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ const actions: ActionTree<UserState, RootState> = {
addNotification({ state, commit }, payload) {
const notifications = JSON.parse(JSON.stringify(state.notifications))
notifications.push({ ...payload.notification, time: DateTime.now().toMillis() })
commit(types.USER_UNREAD_NOTIFICATIONS_STATUS_UPDATED, true)
if (payload.isForeground) {
showToast(translate("New notification received."));
}
Expand Down Expand Up @@ -218,6 +219,11 @@ const actions: ActionTree<UserState, RootState> = {
commit(types.USER_NOTIFICATIONS_UPDATED, [])
commit(types.USER_NOTIFICATIONS_PREFERENCES_UPDATED, [])
commit(types.USER_FIREBASE_DEVICEID_UPDATED, '')
commit(types.USER_UNREAD_NOTIFICATIONS_STATUS_UPDATED, true)
},

setUnreadNotificationsStatus({ commit }, payload) {
commit(types.USER_UNREAD_NOTIFICATIONS_STATUS_UPDATED, payload)
}
}
export default actions;
3 changes: 3 additions & 0 deletions src/store/modules/user/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ const getters: GetterTree <UserState, RootState> = {
},
getFirebaseDeviceId(state) {
return state.firebaseDeviceId
},
getUnreadNotificationsStatus(state) {
return state.hasUnreadNotifications
}
}
export default getters;
3 changes: 2 additions & 1 deletion src/store/modules/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const userModule: Module<UserState, RootState> = {
permissions: [],
notifications: [],
notificationPrefs: [],
firebaseDeviceId: ''
firebaseDeviceId: '',
hasUnreadNotifications: true
},
getters,
actions,
Expand Down
3 changes: 2 additions & 1 deletion src/store/modules/user/mutation-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export const USER_CURRENT_ECOM_STORE_UPDATED = SN_USER + '/CURRENT_ECOM_STORE_UP
export const USER_PERMISSIONS_UPDATED = SN_USER + '/PERMISSIONS_UPDATED'
export const USER_NOTIFICATIONS_UPDATED = SN_USER + '/NOTIFICATIONS_UPDATED'
export const USER_NOTIFICATIONS_PREFERENCES_UPDATED = SN_USER + '/NOTIFICATIONS_PREFERENCES_UPDATED'
export const USER_FIREBASE_DEVICEID_UPDATED = SN_USER + '/FIREBASE_DEVICEID_UPDATED'
export const USER_FIREBASE_DEVICEID_UPDATED = SN_USER + '/FIREBASE_DEVICEID_UPDATED'
export const USER_UNREAD_NOTIFICATIONS_STATUS_UPDATED = SN_USER + '/UNREAD_NOTIFICATIONS_STATUS_UPDATED'
3 changes: 3 additions & 0 deletions src/store/modules/user/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const mutations: MutationTree <UserState> = {
},
[types.USER_FIREBASE_DEVICEID_UPDATED] (state, payload) {
state.firebaseDeviceId = payload
},
[types.USER_UNREAD_NOTIFICATIONS_STATUS_UPDATED] (state, payload) {
state.hasUnreadNotifications = payload
}
}
export default mutations;
4 changes: 3 additions & 1 deletion src/views/Orders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ion-title>{{ currentFacility?.facilityName }}</ion-title>
<ion-buttons slot="end">
<ion-button @click="viewNotifications()">
<ion-icon slot="icon-only" :icon="notificationsOutline" :color="notifications.length ? 'primary' : ''" />
<ion-icon slot="icon-only" :icon="notificationsOutline" :color="(unreadNotificationsStatus && notifications.length) ? 'primary' : ''" />
</ion-button>
<ion-button @click="viewShipToStoreOrders()">
<ion-icon slot="icon-only" :icon="trailSignOutline" />
Expand Down Expand Up @@ -251,6 +251,7 @@ export default defineComponent({
isCompletedOrdersScrollable: 'order/isCompletedOrdersScrollable',
showPackingSlip: 'user/showPackingSlip',
notifications: 'user/getNotifications',
unreadNotificationsStatus: 'user/getUnreadNotificationsStatus'
})
},
data() {
Expand Down Expand Up @@ -446,6 +447,7 @@ export default defineComponent({
this.$router.push({ path: '/ship-to-store-orders' })
},
viewNotifications() {
this.store.dispatch('user/setUnreadNotificationsStatus', false)
this.$router.push({ path: '/notifications' })
}
},
Expand Down
106 changes: 102 additions & 4 deletions src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -194,25 +194,72 @@
<ion-toggle :checked="configurePicker" @ionChange="setConfigurePickerPreference($event)" slot="end" />
</ion-item>
</ion-card>

<ion-card v-if="notificationPrefs.length">
<ion-card-header>
<ion-card-title>
{{ $t("Notification Preference") }}
</ion-card-title>
</ion-card-header>
<ion-card-content>
{{ $t('Select the notifications you want to receive.') }}
</ion-card-content>
<ion-list>
<ion-item :key="pref.enumId" v-for="pref in notificationPrefs" lines="none">
<ion-label class="ion-text-wrap">{{ pref.description }}</ion-label>
<ion-toggle @click="confirmNotificationPrefUpdate(pref.enumId, $event)" :checked="pref.isEnabled" slot="end" />
</ion-item>
</ion-list>
</ion-card>
</section>
</ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonAvatar, IonButton, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonContent, IonHeader, IonIcon, IonItem, IonLabel, IonPage, IonSelect, IonSelectOption, IonTitle, IonToggle , IonToolbar, modalController } from '@ionic/vue';
import {
alertController,
IonAvatar,
IonButton,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonContent,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonPage,
IonSelect,
IonSelectOption,
IonTitle,
IonToggle,
IonToolbar,
modalController
} from '@ionic/vue';
import { defineComponent } from 'vue';
import { ellipsisVertical, personCircleOutline, sendOutline , storefrontOutline, codeWorkingOutline, openOutline } from 'ionicons/icons'
import {
codeWorkingOutline,
ellipsisVertical,
alsoK2maan marked this conversation as resolved.
Show resolved Hide resolved
openOutline,
personCircleOutline,
sendOutline,
storefrontOutline
} from 'ionicons/icons'
import { mapGetters, useStore } from 'vuex';
import { useRouter } from 'vue-router';
import TimeZoneModal from './TimezoneModal.vue';
import Image from '@/components/Image.vue';
import { DateTime } from 'luxon';
import { UserService } from '@/services/UserService'
import { showToast } from '@/utils';
import { hasError, removeClientRegistrationToken } from '@/adapter'
import { hasError, removeClientRegistrationToken, subscribeTopic, unsubscribeTopic } from '@/adapter'
import { translate } from "@/i18n";
import { Actions, hasPermission } from '@/authorization'
import { generateTopicName } from "@/utils/firebase";
import emitter from "@/event-bus"

export default defineComponent({
name: 'Settings',
Expand Down Expand Up @@ -266,17 +313,21 @@ export default defineComponent({
showPackingSlip: 'user/showPackingSlip',
locale: 'user/getLocale',
firebaseDeviceId: 'user/getFirebaseDeviceId',
notificationPrefs: 'user/getNotificationPrefs'
})
},
mounted() {
this.appVersion = this.appInfo.branch ? (this.appInfo.branch + "-" + this.appInfo.revision) : this.appInfo.tag;
},
ionViewWillEnter() {
async ionViewWillEnter() {
// Only fetch configuration when environment mapping exists
if (Object.keys(this.rerouteFulfillmentConfigMapping).length > 0) {
this.getAvailableShipmentMethods();
this.getRerouteFulfillmentConfiguration();
}
// as notification prefs can also be updated from the notification pref modal,
// latest state is fetched each time we open the settings page
await this.store.dispatch('user/fetchNotificationPreferences')
},
methods: {
setFacility (event: any) {
Expand Down Expand Up @@ -410,6 +461,53 @@ export default defineComponent({
}
// Fetch the updated configuration
await this.getRerouteFulfillmentConfiguration(config.settingTypeEnumId);
},
async updateNotificationPref(enumId: string, event: any) {
try {
const ofbizInstanceName = this.userProfile.ofbizInstanceName
Copy link
Contributor

@dt2patel dt2patel Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be "omsInstanceName" we can't make this specific to Ofbiz @ravilodhi

Copy link
Collaborator

@ravilodhi ravilodhi Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, got it. Will make changes to the Get Use Profile API and the respective occurrences on the UI side. Currently, this variable does not hold an OMS instance name, it is going to be a unique keyword. Currently "hotwax" is getting set for all the instances during deployment and used internally for some operations.

I had a discussion with Deepak Sir to set this to some unique value (most probably the OMS instance) so that the firebase topic can be created uniquely per OMS instance.
@dt2patel

const facilityId = (this.currentFacility as any).facilityId
const topicName = generateTopicName(ofbizInstanceName, facilityId, enumId)
// event.target.checked returns the initial value (the value that was there before clicking
// and updating the toggle). But it returns the updated value on further references (if passed
// as a parameter in other function, here in our case, passed from confirmNotificationPrefUpdate)
// Hence, event.target.checked here holds the updated value (value after the toggle action)
event.target.checked
? await subscribeTopic(topicName, process.env.VUE_APP_NOTIF_APP_ID)
: await unsubscribeTopic(topicName, process.env.VUE_APP_NOTIF_APP_ID)
showToast(translate('Notification preferences updated.'))
} catch (error) {
// reverting the value of toggle as event.target.checked is
// updated on click event, and revert is needed on API fail
event.target.checked = !event.target.checked;
showToast(translate('Notification preferences not updated. Please try again.'))
} finally {
emitter.emit("dismissLoader")
}
},
async confirmNotificationPrefUpdate(enumId: string, event: any) {
const message = this.$t("Are you sure you want to update the notification preferences?");
const alert = await alertController.create({
header: this.$t("Update notification preferences"),
message,
buttons: [
{
text: this.$t("Cancel"),
handler: () => {
// reverting the value of toggle as event.target.checked is
// updated on click event and revert is needed on "Cancel"
event.target.checked = !event.target.checked
}
},
{
text: this.$t("Confirm"),
handler: async () => {
// passing event reference for updation in case the API fails
await this.updateNotificationPref(enumId, event)
}
}
],
});
return alert.present();
}
},
setup () {
Expand Down
Loading