Turbolinks 5 - tearing down 3rd party widget (async) - google-tag-manager

I am using FreshChat widget and I'm losing it when performing cached visit. The thing is that the destroy() function of the widget is async, so while I execute it on any event such as 'click' or 'before-visit', the widget is still persisted when the caching operation executes. So what I get is a cached page with an initialised widget, this breaks the widget and I can't re-initiate it.
If I manually destroy it in console(before every visit), then everything is fine.
The question is - how can I make sure that once I make a visit, the widget is destroyed BEFORE the current page is cached?
Notes:
The widget is executed from google tag manager, using dataLayer custom event
'before-cache' event would not help since again, it's an async operation.

I checked if the widget is still initialized, then I would destroy it using the below flush_freshchat method and reload the script to initialize window.fcWidget again
function initFreshChat() {
window.fcWidget.init({
token: "<%= Settings::FRESHCHAT_TOKEN %>",
host: "<%= Settings::FRESHCHAT_HOST %>"
});
function flush_freshchat(){
delete window.fcWidget;
delete window.history.pushState_fc_observer;
delete window.history.replaceState_fc_observer;
delete window.history.pushState;
delete window.history.replaceState;
}
(function(d, id) {
var fcJS;
freshchat_sdk = d.getElementById(id);
if (freshchat_sdk) {
freshchat_sdk.remove();
if(window.fcWidget.isInitialized()){
flush_freshchat();
}
}
fcJS = d.createElement('script');
fcJS.id = id;
fcJS.async = true;
fcJS.src = 'https://wchat.freshchat.com/js/widget.js';
fcJS.onload = initFreshChat;
d.head.appendChild(fcJS);
}(document, 'freshchat-js-sdk'));

Found a solution!
function beforeVisit( event ) {
if (!window.turbolinksVisitFlag) {
event.preventDefault();
if (window.fcWidget ) {
window.fcWidget.destroy();
}
window.turbolinksVisitFlag = true;
setTimeout(function(){Turbolinks.visit(event.data.url);}, 500);
}
So now I intercept every visit, do my stuff, and manually initiate next visit with a timeout.

Related

Displaying form on first login

I'm trying to work out how I can display a form to a user upon their first login to my app ( to fill in profile information) after which they can proceed to the regular site.
Could anyone point me in the right direction?
Thanks
You can make the trick using app startup script:
https://devsite.googleplex.com/appmaker/settings#app_start
Assuming that you have Profile model/datasource, code in your startup script will look similar to this:
loader.suspendLoad();
var profileDs = app.datasources.Profile;
// It would be more secure to move this filtering to the server side
profileDs.query.filters.UserEmail._equals = app.user.email;
profileDs.load({
success: function() {
if (profileDs.item === null) {
app.showPage(app.pages.CreateProfile);
} else {
app.showPage(app.pages.HomePage);
}
loader.resumeLoad();
},
failure: function() {
loader.resumeLoad();
// your fallback code goes here
}
});
If profile is absolute must, I would also recommend to enforce the check in onAttach event for every page but CreateProfile (to prevent navigation by direct link):
// Profile datasource should be already loaded by startup script
// when onAttach event is fired
if (app.datasources.Profile.item === null) {
throw new Error('Invalid operation!');
}
I suggest checking the user profile upon login. If the profile is not present, display the profile form, otherwise, proceed to the regular site.

Can’t call View#subscribe from inside the destroyed callback

I got an error and strange behavior inside template.onDestoyed;
I have code for infinite scroll subscribtion (it stored in special subscribtion-template) It work fine, until i switch to another route, and create a new instance of subscriber-template.
Code:
Template.subscriber.onCreated(function() {
var template = this;
var skipCount = 0;
template.autorun(function(c) {
template.subscribe(template.data.name, skipCount, template.data.user);
var block = true;
$(window).scroll(function() {
if (($(window).scrollTop() + $(window).height()) >= ($(document).height()) && block) {
block = false;
skipCount = skipCount + template.data.count;
console.log(template.data);
console.log("skip_count is "+skipCount);
template.subscribe(template.data.name, skipCount, template.data.user, {
onReady: function() {
block = true;
},
onStop: function() {
console.log('route switched, subscribtion stopped');
}
});
}
});
})
});
When i "scroll down" on a page, subscriber work fine, when i go in another page and "scroll down" first i get a data from old subscriber template (what must be destroyed in theory) in first time. In second time (scroll down again) new instance of subscriber starts works normally.
PIRNT SCREEN CONSOLE
What i doing wrong?
Owch!
The good guy from meteor forums helped me.
Actually the problem is in jquery.scroll event. It not cleaned up when template is destroyed. (Is it a bug? Or it is normal behavior?). I just needed to unbind the scroll event in onDestroyed section.

Gitkit - popupMode and signInSuccess callback do not work well together

I'm using popupMode: true on my Sign In page, and have a signInSuccess callback function on my Widget page:
var config = {
...
callbacks: {
signInSuccess: function(tokenString, accountInfo,
opt_signInSuccessUrl) {
console.log(JSON.stringify(accountInfo));
return true;
}
},
...
}
My function gets called, and the user gets signed-in in the original window, but the widget popup window does not close.
Is this a defect or am I missing something?
Yeah this behavior has been changed for popups when signInSuccess is provided. There were problems with the old behavior. The idea here is that when callback is provided, the developer wants to handle that on their own. The page is still closed automatically when no callback is provided. In your case you will need to close manually.
You can add this snippet before you return true:
if (window.opener) {
window.close();
}

Why do Google's JS Client SDK function callbacks fail?

I'm currently in the learning phase for how the Google JS Client SDK works, since my boss needs me to learn how to integrate a Sign In button to his site to enable people to Authenticate via Google. I am testing the code for the custom Sign In button, with a touch of added functionality (like a Sign Out button), and in the process I've practically copy/pasted the code from their website. Let me show you the code first and then explain the issue, so that you can understand where the code is failing:
<script src="https://apis.google.com/js/client.js?onload=init"></script>
<script type="text/javascript">
var clientId = '{my client id here}'; // for web
var apiKey = '{my api key here}';
var scopes = 'profile email';
function SignOut() {
// I know, sloppy, but the signOut method from Google doesn't work.
window.location = 'https://accounts.google.com/logout';
// Additional code if necessary.
};
function makeApiCall() {
gapi.client.load('plus', 'v1', function () {
var request = gapi.client.plus.people.get({ 'userId': 'me' });
request.execute(function (response) {
var heading = document.createElement('h4');
var image = document.createElement('img');
image.src = response.image.url;
heading.appendChild(image);
heading.appendChild(document.createTextNode(response.displayName));
document.getElementById('name').appendChild(heading);
alert('User logged in. makeApiCall() has executed.');
})
})
};
function init() {
gapi.client.setApiKey(this.apiKey);
window.setTimeout(checkAuth, 1);
console.log('Up and ready to go.');
};
function checkAuth() {
// Triggers when the page and the SDK loads.
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: true }, handleAuthResult);
};
function handleAuthClick(event) {
// Triggers after a user click event to ensure no popup blockers interfere.
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false }, handleAuthResult);
return false;
};
function handleAuthResult(authResult) {
var authorizeButton = document.getElementById('SignInBtn');
var signoutButton = document.getElementById('SignOutBtn');
if (authResult && !authResult.error) {
var V = JSON.stringify(authResult);
localStorage.setItem('GoogleAuthResult', V);
console.log(V); // Just for testing...
var authTimeout = (authResult.expires_in - 5 * 60) * 1000; setTimeout(checkAuth, authTimeout); // As recommended by a Google employee in a video, so that the token refreshes.
authorizeButton.style.display = 'none'; // Switching between Sign In and Out buttons.
signoutButton.style.display = 'inline-block';
makeApiCall();
} else {
// Immediate:true failed so user is NOT signed in.
// Make the Sign In button the one visible and prep it
// so that it executes the Immediate:false after user click:
authorizeButton.style.visibility = 'inline-block';
authorizeButton.onclick = handleAuthClick;
signoutButton.style.visibility = 'none';
}
};
</script>
The handleAuthClick function does run on the button click, but after taking the user to the Google Sign In page, when that page brings me back, the browser kinda flickers and the handleAuthResult function does not execute. Therefore, nothing changes in the page after the successful sign in; the button displayed is the Sign In button (Sign Out button not visible) and no information is displayed on the 'name' textNode. This happens on Internet Explorer (11), Firefox (39) and Chrome (44). Also, it happens at home on my laptop (straight connection to the web via Cable broadband) and at work (on Windows 8.1 behind an Active Directory).
I began wondering so I started refreshing the browser page and after a couple of refreshes, since the script runs from the beginning, the immediate:true fires again and voilá: user is connected and API call triggers.
So, on my laptop, I changed the function being called back, in the immediate:false line's callback parameter, to the init() function and that fixed the problem: everything runs smoothly from beginning to end. Yet, this is not the way it is supposed to work. I still don't know what is going on with that line.
This morning, on my computer at work (behind Active Directory), that fix didn't work. I have to refresh the page a couple of times so that the script runs from the beginning and the immediate:true triggers recognizing the user's Signed In state and displaying the proper button on screen.
Any ideas on why does this callback fail?
You need to define your apiKey in the first section of your code
var clientId = '{my client id here}'; // for web
var apiKey = '{my api key here}'
Maybe thats the problem.
Google ApiKeys

Tracker.autorun() not executing jQuery functions

I am updating some Session variables with Router.onAfterAction whenever my route changes. Then I have a Tracker.autorun() waiting for a the change so it can run some logic and update some DOM classes. I checked the Sessions are set correctly and that the element to-be appended is targeted correctly. But my addClass() does not trigger. No console errors either. What am I missing?
Tracker.autorun(function (c) {
var colActive = Session.equals('navColumnActive', true);
var colVisible = Session.equals('navColumnVisible', true);
var $col = $("#nav-column");
console.log($col);
if (colActive) {
console.log("if triggered");
$col.addClass( "active" );
} else {
console.log("else triggered");
$col.removeClass("active");
}
if (colVisible) {
$col.addClass("visible");
} else {
$col.removeClass("visible");
}
});
I had the same problem just recently. In my case the reactive values triggering the computation where being executed before the DOM and the elements where properly rendered.
I would check if the session vars are being set after the DOM has been rendered. Therefore selectors like $col = $("#nav-column") came back empty, though perfectly worked when executed in the browser console.
You could try the following:
Template.<template_name>.onRendered(function() {
Session.set('navColumnActive', true);
Session.set('navColumnVisible', true);
})

Resources