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)
});
Related
please help for using this typing handler.this is handling for react native typing handler for but not getting any message during user typing please help.here using quick typing handler to catch the user typing but not working properly...please help,My code is here...
const emitter = new NativeEventEmitter(QB.chat);
function userTypingHandler(event) {
const {
type, // name of the event (the one you've subscribed for)
payload, // event data
} = event;
const {
dialogId, // in dialog with id specified
userId, // user with id specified is typing
} = payload;
// handle as
console.log("user is typing", userId);
}
emitter.addListener(QB.chat.EVENT_TYPE.USER_IS_TYPING, userTypingHandler);
return () =>{
emitter.removeListeners(
QB.chat.EVENT_TYPE.USER_IS_TYPING, userTypingHandler
);
}
}, [QB.chat.EVENT_TYPE.USER_IS_TYPING]);
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.
Imagine I'm using a bloc to handle a network request. If the request fails, the way to handle the failure would be different depending on the platform. On my web app, I would like to redirect the user to an error page while on my IOS app I would like to show a dialog.
As bloc should only be used and shared to handle the business logic, and the error handling part has nothing to do with the business logic, we should ask the UI part to take care of the error handling.
The UI can send error callback to the bloc and the bloc will run it when an error happens. We can also handle the error in a platform-specific way by sending different callbacks in different platforms.
Then there come my two questions:
Is there a more appropriate way to do this?
How to send the callback to the bloc?
In flutter, we only have access to bloc after the initState life cycle method(for we get bloc from builder context, which only comes after initState). Then we can only send callback in the build method.
In this way, we will repetitively send callback to bloc every time rebuilding happens(these repetitions make no sense).
With react, such one-time initialization could be done in life cycles such as componentDidMount.
In flutter how do we reach the goal of running these initialization only once?
This is how we handle it in my team:
First we build our main page (The navigation root) like this:
#override
Widget build(BuildContext context) {
return BlocBuilder<SuspectEvent, SuspectState>(
bloc: _bloc,
builder: (context, state) {
if (state.cameras.isEmpty) _bloc.dispatch(GetCamerasEvent());
if (!_isExceptionHandled) {
_shouldHandleException(
hasException: state.hasException,
handleException: state.handleException);
}
return Scaffold(
...
We declare the _shouldHandleException like this (still on the main page):
_shouldHandleException(
{#required bool hasException, #required Exception handleException}) {
if (hasException) {
if (handleException is AuthenticationException) {
_isExceptionHandled = true;
SchedulerBinding.instance.addPostFrameCallback((_) async {
InfoDialog.showMessage(
context: context,
infoDialogType: DialogType.error,
text: 'Please, do your login again.',
title: 'Session expired')
.then((val) {
Navigator.popUntil(context, ModalRoute.withName('/'));
this._showLogin();
});
});
} else if (handleException is BusinessException) {
_isExceptionHandled = true;
SchedulerBinding.instance.addPostFrameCallback((_) async {
InfoDialog.showMessage(
context: context,
infoDialogType: DialogType.alert,
text: handleException.toString(),
title: 'Verify your fields')
.then((val) {
_bloc.dispatch(CleanExceptionEvent());
_isExceptionHandled = false;
});
});
} else {
_isExceptionHandled = true;
SchedulerBinding.instance.addPostFrameCallback((_) async {
InfoDialog.showMessage(
context: context,
infoDialogType: DialogType.error,
text: handleException.toString(),
title: 'Error on request')
.then((val) {
_bloc.dispatch(CleanExceptionEvent());
_isExceptionHandled = false;
});
});
}
}
}
On our block we have:
#override
Stream<SuspectState> mapEventToState(SuspectEvent event) async* {
try {
if (event is GetCamerasEvent) {
... //(our logic)
yield (SuspectState.newValue(state: currentState)
..cameras = _cameras
..suspects = _suspects);
}
... //(other events)
} catch (error) {
yield (SuspectState.newValue(state: currentState)
..hasException = true
..handleException = error);
}
}
In our error handling (on main page) the InfoDialog is just a showDialog (from Flutter) and it gets on top of any route. So the alert just needed to be called on the root route.
You can access the BLoC in the initState method if you wrap it in a scheduleMicrotask method, so that it runs after the initState method completed:
#override
void initState() {
super.initState();
// Do initialization here.
scheduleMicrotask(() {
// Do stuff that uses the BLoC here.
});
}
You can also check out this answer to a different question outlining the Simple BLoC pattern, which just calls asynchronous methods directly on the BLoC instead of putting events into sinks.
That would allow code like this:
Future<void> login() {
try {
// Do the network stuff, like logging the user in or whatever.
Bloc.of(context).login(userController.text, emailController.text);
} on ServerNotReachableException {
// Redirect the user, display a prompt or change this
// widget's state to display an error. It's up to you.
}
}
You can use superEnum package to create states and events for a Bloc.(and here you will declare a state for the Error by doing this :
#Data(fields: [DataField<Error>('error')])
OrderLoadingFailedState,
(If anyone need an example of how to use it, please tell me i will show you an example)
I'm using Aurelia's EventAggregator to publish and subscribe to events in my app. Some of my custom elements take a while to load, so I've used a loading event to tell my main app.js to add a spinner to the page during loading.
This works fine once the app has loaded and I start switching between routes, however, on first page load the event doesn't seem to fire - or at least, it isn't picked up by the subscribe method.
Here's basically what my app.js does:
attached () {
this.mainLoadingSubscription = this.eventAggregator.subscribe('main:loading', isLoading => {
// If main is loading
if (isLoading) {
document.documentElement.classList.add('main-loading');
}
// Stopped loading
else {
document.documentElement.classList.remove('main-loading');
}
});
}
And here's what my custom elements do:
constructor () {
this.eventAggregator.publish('main:loading', true);
}
attached () {
this.doSomeAsyncAction.then(() => {
this.eventAggregator.publish('main:loading', false);
});
}
This causes the first page load to not show a spinner and instead the page looks kind of broken.
Btw, I am aware of the fact that you can return a Promise from the element's attached method but I can't do this because of this other problem
Set up your subscriptions in your viewModel's constructor or activate callback
In the above example, you set up subscriptions in the viewModel's attached() callback. Unfortunately, this will not be called until all child custom element's attached() callbacks are called, which is long after any one custom element's constructor() function is called.
Try this:
app.js
#inject(EventAggregator)
export class AppViewModel {
constructor(eventAggregator) {
this.mainLoadingSubscription = eventAggregator.subscribe('main:loading', isLoading => {
// do your thing
}
}
}
If the viewModel is a route that can be navigated to, then handle this in the activate() callback with appropriate teardown in the deactivate() callback.
#inject(EventAggregator)
export class AppViewModel {
constructor(eventAggregator) {
this.eventAggregator = eventAggregator;
}
activate() {
this.mainLoadingSubscription = this.eventAggregator.subscribe('main:loading', isLoading => {
// do your thing
}
}
deactivate() {
this.mainLoadingSubscription.dispose();
}
}
I'm reviewing the Durandal documentation, and I can't find a concrete implementation of listening for Durandal events, e.g, router events.
Can someone point me to the docs, or (if there is no documentation on this) an example?
In your view model you should listen to activator events. Link. check this example from Durandal starter template. It is listening to activate and canDeactivate events:
define(['plugins/http', 'durandal/app', 'knockout'], function (http, app, ko) {
//Note: This module exports an object.
//That means that every module that "requires" it will get the same object instance.
//If you wish to be able to create multiple instances, instead export a function.
//See the "welcome" module for an example of function export.
return {
displayName: 'Flickr',
images: ko.observableArray([]),
activate: function () {
//the router's activator calls this function and waits for it to complete before proceding
if (this.images().length > 0) {
return;
}
var that = this;
return http.jsonp('http://api.flickr.com/services/feeds/photos_public.gne', { tags: 'mount ranier', tagmode: 'any', format: 'json' }, 'jsoncallback').then(function(response) {
that.images(response.items);
});
},
select: function(item) {
//the app model allows easy display of modal dialogs by passing a view model
//views are usually located by convention, but you an specify it as well with viewUrl
item.viewUrl = 'views/detail';
app.showDialog(item);
},
canDeactivate: function () {
//the router's activator calls this function to see if it can leave the screen
return app.showMessage('Are you sure you want to leave this page?', 'Navigate', ['Yes', 'No']);
}
};
});
Here's some example code from the project I have worked in:
//authentication.js
define(['durandal/events'], function(events){
var authentication = {};
events.includeIn(authentication);
//perform login then trigger events to whoever is listening...
authentication.trigger('logged:on',user);
//perfom logoff then trigger events to whoever is listening...
authentication.trigger('logged:off');
return {
authentication: authentication
}
});
//logon.js
//pull in authenticaion
define(['authentication'], function(authentication){
authentication.on('logged:on',loggedOn);
//callback that gets called when the logged:on event is fired on authentication
function loggedOn(user){
console.log(user);
}
});