I'm struggling with the following issue. In my Ionic (3) application. I got the oneSignal (push notifications) handling script in my app.component.ts file. I'm using the handleNotificationOpened().subcribe function to be able to open a page or run a function when a user presses the push notification.
Now my question is, how can I change tab or page from app.component.ts and run a page specific function while opening that tab/page.
For instance:
User get notification that he has a new friendship invite.
User presses the push notification
App will open "friendlist" tab and opens the specific invite.
For people having the same question, I found the solution using events. It may not be the best solution but it works.
First you need to add the following components to your page.ts
import { Events } from 'ionic-angular';
import { App } from 'ionic-angular';
The following function fires when the user press the pushnotification using OneSignal.
this.oneSignal.handleNotificationOpened().subscribe((data) => {
// do something when a notification is opened
// the following two lines pass data I send with the push notification so the app knows what to open
let pushaction = data.notification.payload.additionalData.action;
let pushactionvalue = data.notification.payload.additionalData.actionvalue;
// this fires up the tab-switching
this.runNotificationAction(pushaction, pushactionvalue);
});
The following function directs user to the right tab
runNotificationAction(pushaction, pushactionvalue){
// this is the data passed the the other page
let data = {"action": pushaction, "value:": pushactionvalue};
// this opens the right tab. Make sure to change select '0' to the required tab (0 is the first one).
this.app.getRootNav().getActiveChildNav().select(0);
// fires the function that passed the data. Using second parameter to filter event listeners per page.
this.sendData(data, 'homepage');
}
And the function that submits the data to the other pages:
sendData(data, command){
//We set a timeout because I had problems with sending it imediatly. Like this it works fine for me.
setTimeout(() => {
let pushcommand = "pushData:" + command ;
this.events.publish(pushcommand, data);
}, 500);
}
And at last we have to add an event listener on the other tabs/pages you are going to redirect to.
// making an event listerner command for each page like pushData:homepage makes sure the action is only fired from the specific page
this.events.subscribe('pushData:homepage', (data) => {
console.log('Yes, data passed!');
console.log(data);
// Then you can fire your function and use the data
});
If anyone has any questions feel free to ask!
Related
So lately I have been learning JS and trying to interact with webpages, scraping at first but now also doing interactions on a specific webpage.
For instance, I have a webpage that contains a button, I want to press this button roughly every 30 seconds and then it refreshes (and the countdown starts again). I wrote to following script to do this:
var klikCount = 0;
function getPlayElement() {
var playElement = document.querySelector('.button_red');
return playElement;
}
function doKlik() {
var playElement = getPlayElement();
klikCount++;
console.log('Watched ' + klikCount);
playElement.click();
setTimeout(doKlik, 30000);
}
doKlik()
But now I need to step up my game, and every time I click the button a new window pops up and I need to perform an action in there too, then close it and go back to the 'main' script.
Is this possible through JS? Please keep in mind I am a total javascript noob and not aware of a lot of basic functionality.
Thank you,
Alex
DOM events have an isTrusted property that is true only when the event has been generated by the user, instead of synthetically, as it is for the el.click() case.
The popup is one of the numerous Web mechanism that works only if the click, or similar action, has been performed by the user, not the code itself.
Giving a page the ability to open infinite amount of popups has never been a great idea so that very long time ago they killed the feature in many ways.
You could, in your own tab/window, create iframes and perform actions within these frames through postMessage, but I'm not sure that's good enough for you.
Regardless, the code that would work if the click was generated from the user, is something like the following:
document.body.addEventListener(
'click',
event => {
const outer = open(
'about:blank',
'blanka',
'menubar=no,location=yes,resizable=no,scrollbars=no,status=yes'
);
outer.document.open();
outer.document.write('This is a pretty big popup!');
// post a message to the opener (aka current window)
outer.document.write(
'<script>opener.postMessage("O hi Mark!", "*");</script>'
);
// set a timer to close the popup
outer.document.write(
'<script>setTimeout(close, 1000)</script>'
);
outer.document.close();
// you could also outer.close()
// instead of waiting the timeout
}
);
// will receive the message and log
// "O hi Mark!"
addEventListener('message', event => {
console.log(event.data);
});
Every popup has an opener, and every different window can communicate via postMessage.
You can read more about window.open in MDN.
Currently when opening a push notification while the app is running, it will navigate to the correct content scene. When the app is closed however, and a user opens a push notification it will just open the app and not navigate to the related content scene. I think the problem has to do with the onNotification function firing around the same time as the checkLogin function. I would like to somehow wait until the checkLogin is finished and continue running onNotification. I appreciate anyones help.
// When receiving an incoming notification (iOS/Android)
onNotification: async function (notification) {
//for iOS the data is nested, for Android the data is attached directly to the object
let data = (Platform.OS === 'ios') ? notification.data : notification;
let id = data._id;
let channelId = data.channel;
let message = data.message;
Logger.info('Notification Received', message);
if (isLoggedIn()) {
// Check if the logged in user is the one who received the message
let content = ModelCache.getContent(id);
Navigation.goContent(content);
}
},
This block of code is on the index file when the app boots up.
componentWillMount() {
this.checkLogin();
}
async checkLogin() {
let user = await Data.getStoredUser();
this.setState({
isUserChecked: true,
initialRoute: user ? Navigation.MAIN_SCENE : Navigation.LANDING_SCENE
});
}
Since it's javascript, it is unlikely that one thing is occurring at the same time as another thing, so therefore it likely means that your onNotification is being called before your checkLogin function. Perhaps a workaround would be to set a value in the state from within onNotification which could be referenced and acted upon by checkLogin.
I want to know whether we can retrieve count of history messages in pubnub channel prior to calling ngHistory. I am using Angularjs.
I need to show a loader until message history is loaded. Now , since pubnub is routing all history and chat typed to a single event, I need to hide the loader inside the event. But this makes a problem when there are no messages in history. The loader will not be hidden since the event has not been called at least once. If I put the code for hiding the spinner outside the event, it will immediately hide the loader long before the history is loaded. I have tried using callback for ngHistory but it was not triggered.
If I could retrieve count of messages, I can hide the loader if history message count is 0.
I use the below code:
$scope.limit = 50;
ActivityIndicator.showSpinner(); // to show a spinner
PubNub.ngHistory({
channel : $scope.channel,
limit : $scope.limit
});
$rootScope.$on(PubNub.ngMsgEv($scope.channel), function(ngEvent, payload) {
$scope.messages.push(payload.message);
ActivityIndicator.hideSpinner(); // to hide the spinner
});
The problem is when there are no messages, the event will not fire, if the event does not fire, hide spinner will never be called.
By default in pubnub, they are implemented like this. If they can ensure the event will be called at least once, then the issue will be solved. I looked into the pubnub Angularjs library, I could find out the following code routes messages to event:
c.ngHistory = function(args) {
args.callback = c._ngFireMessages(args.channel); // to route messages to event
return c.jsapi.history(args);
};
If I comment the second line and implement a callback for ngHistory call like this:
PubNub.ngHistory({
channel : $scope.channel,
limit : $scope.limit,
callback : function(payload){
$scope.messages.push(payload.message);
ActivityIndicator.hideSpinner(); // to hide the spinner
}
});
Then the code works fine. But since editing a library file is not recommended. I should not do that. Do you have any other idea to rectify this problem?
AngularJS PubNub SDK Determine Number of Messages to be Returned by ngHistory() Method
Today as of now, there is not a way to determine the number of messages in a channel. However you can limit the max returned messages by using the limit : 10 parameter in your PubNub.ngHistory({ ... }) call.
I am working on a function which allows users to sign in on my website with their Google account.
My code is based on the Google documentation (others signIn() options are in meta tags).
function login() {
gapi.auth.signIn({'callback':
function (authResult) {
if (authResult['status']['signed_in']) {
console.log('Okay');
}else {
console.log('Error');
}
}
});
}
When I call login(), a Google pop up appears, I approve the terms of my application and everything works fine.
But the callback is called twice :
1st case: If I never approved apps permissions then the callback will be call at the opening of the pop up AND when I will approve the permissions. So it will write "Error" and "Okay".
2nd case: If I already approved the permissions, it will write "Okay" two times.
I added the option 'approvalprompt': 'force' to the signIn() function. The callback function is no longer called twice but it forces the user to approve the app's permissions, even if previously approved. So it's not user friendly.
Is there a friendly user way to approve the app's permissions one time without having two callback ?
Thank you.
I'm facing this same issue here, but I'm calling gapi.auth.signIn() via a button click handler. The callback is still called twice. One thing I noticed between the two authResult objects was that authResult.status.method is 'AUTO' in the first call (before the popup window appears) and is 'PROMPT' in the second call after the window is auto-dismissed due to previous authorisation.
The solution I'm exploring now is to ignore the AUTO instance and only process the PROMPT instance of the callback. Not sure how this will work once I revoke the permissions within Google due to the lack of details in the docs on the 'status' object.
I am facing the same issue: signin callback called twice in case of user that already granted permission; the local variable approach (initializedGoogleCallback) isn't working for me because it call the callback one time only when the user already granted access, but didn't call it if the user is the new one.
After a bit of research (i especially dig in site using the g+ auth) i noticed that all of them use the 'approvalprompt': 'force' and they have the already granted user to reapprove a "Offline Access" policy everytime.
Even the google example i followed to setup my app (https://developers.google.com/+/web/signin/javascript-flow) even if it did not mention it, it uses the "force" parameter.
For the moment it seems the only solution if you want to use the javascript flow (that mean if you need a personal style signin button)
Try to register first call in some local variable and then process it
This quick solution helps me:
function login() {
var initializedGoogleCallback = false
gapi.auth.signIn({
'callback': function (authResult) {
if (!initializedGoogleCallback) {
// after first call other will be ignored
initializedGoogleCallback = true;
if (authResult['status']['signed_in']) {
console.log('Okay');
} else {
console.log('Error');
}
}
}
});
}
also you can add following code before call gapi.auth.signIn
window.removeEventListener('load')
Like the Drew Taylor's answer, to avoid the double callback with the pure javascript sign in solution, you can check the user's session state:
if (authResult["status"]["method"] == "PROMPT") {...}
I think that the callback with the AUTO method is fired by the bottom welcome bar that appears on first login.
That is the intentional plan for page level config! It being present in the page causes the callback to fire when the Javascript is finished loading. What you should do is prepare for that in your code.
Don't show the sign in button until you have received a callback - if authResult['status']['signed_in'] == true, then treat the user as signed in (setup a session etc, whatever you would normally do). If it is false, then display the button.
function signinCallback(authResult) {
if (authResult['status']['signed_in']) {
document.getElementById('signinButton').setAttribute('style', 'display: none');
// Do sign in stuff here!
} else {
document.getElementById('signinButton').setAttribute('style', 'display: block');
}
}
I would avoid using approval prompt force if you can!
finally i solved with a workaround; i don't know if this is the correct way to approach or i am just cheating but i do this way:
first of all some script in the page (i am using bootstrap + jquery)
function render() {
//I am not using it but kept anyway
}
var i;
// Function called from a onClick on a link or button (the 'sign in with g+' button)
function gp_login() {
i=0;
$('#alertbox').remove();
var additionalParams = {
'callback': signinCallback,
/*'approvalprompt': 'force' finally removed*/
};
$('#gp-login').button('loading');
gapi.auth.signIn(additionalParams);
}
function signinCallback(authResult) { //my callback function
var email='';
var given_name='';
if (authResult['status']['signed_in']) { //get some user info
gapi.client.load('oauth2', 'v2', function() {
gapi.client.oauth2.userinfo.get().execute(function(resp){
email = resp.email; //get user email
given_name = resp.given_name; //get user email
family_name=resp.family_name;
id=resp.id;
if (i<2) { //execute the doLogin just one time (my cheat)
doLogin(email,given_name,family_name,id); //dologin does my logic with an ajax call to signup/register user to my site
}
i=2;
});
});
} else {
// Update the app to reflect a signed out user
}
}
this approch have the doLogin part called just one time, but the callback is called twice (gapi.client.oauth2.userinfo.get() this function is called twice); with a bit more tweaking with the if / var check i think is possible to call everything once.
This way if the user already granted the auth, it will be automatically signed.
I notice that sometimes google have a popup layer on the bottom of layer showing a "welcome back message", but i didn't understand when it appears or if i have to call it manually
As in the title,
how can i close a MessageDialog from code in HTML5 Windows Store app?
my code so far:
var msg = new Windows.UI.Popups.MessageDialog("Please wait");
msg.commands.append(new Windows.UI.Popups.UICommand("OK",
function (command) {
...
}));
msg.showAsync();
and then i would like to close this popup FROM THE CODE, i have not found any methods
in the specification like
msg.close();
is there a way?
thanks
The fact that your message is "Please wait" suggests to me that you might want to use a different tool for this job.
If what you're trying to do is inform the user that you're doing something in the background that they need to wait for, consider using a Progress control instead, as documented here:
http://msdn.microsoft.com/en-us/library/windows/apps/hh465487.aspx
If you use a Progress control, you can both include a text label with your desired text, and dismiss the progress control when you've finished whatever task it is you're asking the user to wait for.
And to answer your original question, I don't believe there's any API for dismissing a MessageDialog programmatically, as that would break the interaction pattern of this control, which is for the app to display a message, and then allow the user to dismiss it when they're ready to.
Hope that helps.
For more information on Windows Store app development, register for App Builder.
I think you want to use a flyout, similarly to this answer. The link solves a slightly different problem in that it closes the flyout after a timeout.
However, you should be able to define a flyout that you, as well as the user, can close. In both cases, you end up calling something like:
flyout.winControl.hide(); // Dismiss the flyout
Take a look at this...
(function(){
"use strict";
var page = WinJS.UI.Pages.define("/html/cancelcommand.html", {
ready: function (element, options) {
document.getElementById("cancelCommand").addEventListener("click", cancelCommand_Click, false);
}
});
// Click handler for the 'cancelCommand' button.
// Demonstrates setting the command to be invoked when the 'escape' key is pressed.
// Also demonstrates retrieval of the label of the chosen command and setting a callback to a function.
// A message will be displayed indicating which command was invoked.
// In this scenario, 'Try again' is selected as the default choice, and the 'escape' key will invoke the command named 'Close'
function cancelCommand_Click() {
// Create the message dialog and set its content
var msg = new Windows.UI.Popups.MessageDialog("No internet connection has been found.");
// Add commands and set their command handlers
msg.commands.append(new Windows.UI.Popups.UICommand("Try again", commandInvokedHandler));
msg.commands.append(new Windows.UI.Popups.UICommand("Close", commandInvokedHandler));
// Set the command that will be invoked by default
msg.defaultCommandIndex = 0;
// Set the command to be invoked when escape is pressed
msg.cancelCommandIndex = 1;
// Show the message dialog
msg.showAsync();
}
function commandInvokedHandler(command) {
// Display message
WinJS.log && WinJS.log("The '" + command.label + "' command has been selected.", "sample", "status");
}
}());
http://code.msdn.microsoft.com/windowsapps/Message-dialog-sample-00c928f5/sourcecode?fileId=50972&pathId=1064922824