Unsubscribing from a subscription in dva - dvajs

I have a model that will subscribe a websocket to several expensive end points when the user is on particular routes. When the user leaves the route, I want to disconnect the websockets.
The dva api docs say
Notice: if we want to unregister a model with app.unmodel(), it's subscriptions must return unsubscribe method.
However the docs do not include how to register a subscription with an unsubscribe method.
How does one create a subscription with an unsubscribe handler?

It's necessary to add return to the end of your function.
subscriptions: {
setup() {
emitter.on('event', () => {
emitterCount += 1;
});
return () => {
emitter.removeAllListeners();
};
},
},

Related

Few questions about Vue SSE(Server Sent Event)

I'm going to use SSE to implement real-time notifications.
Please look at my method and tell me what the problem is and how to solve it.
in vuex login action method
// SSE EvnetSource Connect
let url = process.env.VUE_APP_API_URL + "subscribe";
let eventSource = new EventSource(url, {
withCredentials: true
});
eventSource.addEventListener("notification", function (event) {
console.log(event.data);
commit('setNotification', event.data) // => set event's data to vuex 'notification' state as array
});
and then
in top nav component's watch method
watch: {
notification(val) {
if(val) {
const notiData = JSON.parse(val)
if(notiData.id) {
// show notification alert component
this.$notify("info filled", "notification", notiData.content, null, {
duration: 7000,
permanent: false
});
}
}
}
}
This is my current situation.
And this is my questions.
Currently, when logging in through vuex, I create an EventSource, but how to delete the EventSource when logging out? (EventSource is not defined globally, so I don't know how to approach it when logging out).
How to reconnect EventSource after page refresh? (I think main.js can handle it.)
Is there a way to put in Custom Header when creating EventSource?
As any other event bus, EventSource needs to be unsubscribed when events shouldn't be received. This requires to keep a reference to listener function. If a listener uses Vuex context that is available inside an action, it should be defined inside login action and stored in a state:
const notificationListener = (event) => {...};
eventSource.addEventListener("notification", notificationListener);
// can be moved to a mutation
state._notificationEventSource = eventSource;
state._notificationListener = notificationListener;
Inside logout action:
let { _notificationEventSource: eventSource, _notificationListener: notificationListener } = state;
eventSource.removeEventListener("notification", notificationListener);
It's no different when a page is initially loaded and reloaded.

FCM notification received twice when in the background (service worker)

When im triggering a notification with my website in the background or close, i get two notifications. The first one has missing elements in the payload (icon for example) while the 2nd one has all the info.
The code is running on this site: https://www.maltachamber.org.mt
https://www.maltachamber.org.mt/firebase-messaging-sw.js
The foreground notification also works as intended, the code is at line 2782 for the homepage.
I was able to solve it with a workaround.
Override the push Event with a custom Event class then use the listnerto intercept the incoming messages and use the custom class to re-push the events but without the 'notification' key exposed so firebase won't consider it.
In this way they won't be showed twice.
messaging.onBackgroundMessage(function(payload) {
notificationOptions = { /*...*/ };
self.registration.showNotification("Title",notificationOptions);
});
class CustomPushEvent extends Event {
constructor(data) {
super('push')
Object.assign(this, data)
this.custom = true
}
}
self.addEventListener('push', (e) => {
console.log("PUSH");
// Skip if event is our own custom event
if (e.custom) return;
// Keep old event data to override
let oldNotificationWrapper = e.data
// Create a new event to dispatch, pull values from notification key and put it in data key,
// and then remove notification key
let newEvent = new CustomPushEvent({
data: {
json() {
let newNotificationWrapper= oldNotificationWrapper.json()
newNotificationWrapper.data = {
...newNotificationWrapper.data,
...newNotificationWrapper.notification
}
delete newNotificationWrapper.notification
return newNotificationWrapper
},
},
waitUntil: e.waitUntil.bind(e),
})
// Stop event propagation
e.stopImmediatePropagation()
// Dispatch the new wrapped event
dispatchEvent(newEvent)
});

Set up listeners only after permission is verified

I am using react-native-notifications - Version 2.1.7 library to receive notifications in a react-native mobile app. I don't want to set up the notification-related listeners until the user has provided permission to receive notifications.
Q1. The documentation says that it is highly recommended to keep listeners registration at global scope rather than at screen scope. What problems should I expect if I set up listeners at a screen, at which the user is asked to provide permission?
Q2. The device token listener NotificationsAndroid.setRegistrationTokenUpdateListener() does NOT seem to work if it is inside a promise. What am I missing here? Please, see my code below.
// This function is called when the user clicks on the button "Provide permission to receive notifications."
const _requestPermissionNotification = async () => {
let hasPermission = false;
try {
hasPermission = await NotificationsAndroid.isRegisteredForRemoteNotifications();
}
catch (error) {
Alert.alert(
"Notification",
"To utilise the full functionality of this app, Permission to receive notifications is required.",
[{ text: "Ok." }]
);
} // end of: try/catch
if (hasPermission) {
// A. Register Token
// THIS LISTENER DOES NOT SEEM TO WORK UNLESS IT IS SET UP OUTSIDE THE COMPONENT!
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
console.log("PermissionsScreen - setRegistrationTokenUpdateListener - deviceToken:", deviceToken);
});
// B. Pending Notifications
PendingNotifications.getInitialNotification()
.then((notification) => {
console.log("PermissionsScreen - getInitialNotification - notification:", notification);
})
.catch((err) => console.error("getInitialNotifiation failed", err));
// C. Notification Opened
NotificationsAndroid.setNotificationOpenedListener((notification) => {
console.log("PermissionsScreen - setNotificationOpenedListener - :data", notification.getData());
});
// D.a Notification Received
NotificationsAndroid.setNotificationReceivedListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedListener - data:", notification.getData());
});
// D.b Notification Received "IN FOREGROUND"
NotificationsAndroid.setNotificationReceivedInForegroundListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedInForegroundListener (foreground)", notification.getData());
});
} // end of: if()
}; // end of: _requestPermissionNotification()
It seems that version 3.1.1 of React-Native-Notifications does not have these limitations anymore.
The following code, which uses the new commands, can be used inside a promise and inside a component.
// Step A.1: Register this app to receive notifications.
Notifications.registerRemoteNotifications();
// Step A.2: Get the device token
Notifications.events().registerRemoteNotificationsRegistered( (event) => {
console.log("deviceToken:", event.deviceToken);
});

VueJs auh0 social authentication, how to customize handle redicrect

Using VueJs SDK .
My redirect url is example.com/callback, inside routes I have callback component which suppose to send user data to backend and check if user exists and log user in otherwise create new user and get back jwt from our backend application .
Problem is following, inside created/mounted cycle I can not reach auth0Client and so user properties.
this.$auth.auth0Client
I have to do this hack
const interval = setInterval(async () => {
count++;
if (this.$auth.auth0Client) {
clearInterval(interval);
let loggedUser = await this.$auth.auth0Client.getUser();
if(loggedUser) {
const email = loggedUser.email;
const name = loggedUser.name;
try {
//backend logic
} catch(e) {
console.log(e)
}
}
} else {
if (count > 80) {
clearInterval(interval);
}
}
}, 100)
My problem/question is how to handle this better as it seems not very logical and nice.
I want to send authenticated user data to our backend and do the rest there,
Auth0 seems to lack the details for this type of usecases
Thanks
I solve my own problem by using Vue event system
this.auth0Client = await createAuth0Client({
domain: options.domain,
client_id: options.clientId,
audience: options.audience,
redirect_uri: redirectUri,
responseType: 'token'
});
this.$eventBus.$emit('auth0Client',this.auth0Client) // creating event here after auth0Client
handling it in callback component
this.$eventBus.$on('auth0Client', async (auth0Client) => {
//logic here
})
Do not forget to register your event bus in main js
Vue.prototype.$eventBus = new Vue();
Hope it will help

VueJS check periodically global function

I have a VueJS project where I need to check periodically a function to see if a token has expired once the user login to the app successfully and if the token has expired have to show a modal message to user.
I have my Singin.vue file that contains the following code:
....
methods: {
...mapActions(['authorize']),
submit() {
this.$validator.validateAll().then(result => {
if (result) {
this.error = null;
this.processing = true;
this.authorize(this.credentials).then(() => {
// ***********
// HERE I have to check periodically if the token has expired
// ***********
this.$router.push({name: 'home'});
}).catch(error => {
console.warn('error message', error);
this.error = error.response.data.message;
this.processing = false;
});
}
});
}
When this.authorize happens I route to home, but before that happens I need to start calling a function periodically. Then If user Logoff then I have to clear the interval.
So first, I don't know where is the best place to have this TokenExpiration function code. Does it make sense to have it in a store file?
This is my api.js store file where I have my authorize function and my logout function, does it make sense to have the tokenExpirationCheck function here also?
There are several ways of doing it, but I would probably solve this using a plugin, because timers should not be in the store, and the behavior is global to the application, so I wouldn't put it into any single component.
The pugin would have a vuex.watch on the stoere's logged-in flag. When it goes from false => true, remove the timer (if active) and if it goes from false => true, add the timer. The timer function can then call the vuex dispatch to handle the functionality.