Open mesqueeb opened 6 years ago
@mesqueeb we observed this issue when injecting the api.js
script asynchronously, in some browsers. We're not sure yet what the root cause of the bug is, but in the meantime you can use a setTimeout
before the gapi.client.init
call or not injecting dynamically the api.js
script.
@TMSCH
I found out what the problem is. Right before window.gapi.client.init(gapiConfig)
I logged window.gapi.client
and it seems it's still undefined
at that point.
Is there a better way that you know of to load the script in a Single Page application besides:
window.gapi.load('client:auth2', _ => {
window.gapi.client.init(gapiConfig)
})
I need to be sure the script is loaded before I do window.gapi.client.init(gapiConfig)
@mesqueeb that's the proper way to do it, as I mentioned, there's a bug in the loader currently that can create this issue sometimes. A setTimeout(init, 1)
seems to workaround the bug.
@TMSCH Thank you for the advice. However this did not resolve the issue for me. I'm not sure how I can use the GAPI js client with this bug. Also adding the script just to the html at the header didn't work for me.
I saw in the one-tap signin documentation they have something to check if the script properly loaded:
window.onGoogleYoloLoad = (googleyolo) => {
// The 'googleyolo' object is ready for use.
}
Maybe the GAPI js script could get something similar?
This is my updated code with setTimeout()
function loadAndInitGAPI() {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'https://apis.google.com/js/api.js'
script.onload = e => {
window.gapi.load('client:auth2', _ => {
console.log('loaded GAPI')
function initGAPI(){
if (!window.gapi || window.gapi.client){ return reject('no window.gapi.client') }
window.gapi.client.init(gapiConfig)
.then(_ => {
console.log('initialised GAPI')
window.GAPIiniOK = true
// Global auth listener
gapi.auth2.getAuthInstance().isSignedIn
.listen(store.dispatch('gapiAuthListener'))
// Not required anymore if global auth listener does its job!
store.dispatch('user/updateAuthStatus')
.then(_ => {
resolve()
})
}).catch(error => {
dispatch('authenticationError', {error, note: 'error during gapi initialisation'})
return reject(error)
})
}
setTimeout(initGAPI, 10)
})
}
document.getElementsByTagName('head')[0].appendChild(script)
})
}
There seems to be a typo:
if (!window.gapi || !window.gapi.client)
it lacks the !
in the test for window.gapi.client
.
@TMSCH Thanks for now this solved my problem. I leave it up to you to decide if you want to close this issue or not.
Let's keep it open. We have an internal bug opened for this too.
Did you make any progress with fixing the bug?
This is still happening, is there any way to detect library is loaded? Something like @mesqueeb suggested?
window.onGoogleScriptLoad = () => { // Script is ready for use. }
Since I got the update e-mail from @milenkovic, this is how I fixed it (maybe this will help you too @Neseke): First include the script (I'm not sure this is the right script, but this will work for the one you use):
<script type="text/javascript" src="https://apis.google.com/js/api:client.js?onLoad=onGoogleScriptLoaded"></script>
Then you can use the snippet @mesqueeb and @milenkovic shared:
window.onGoogleScriptLoad = () => {
console.log('The google script has really loaded, cool!');
}
When you see "The google script has really loaded, cool!" in the console, you know the script has loaded and can safely use gapi or window.gapi.
Thanks @roelofjan-elsinga, that's exactly what I needed.
I'm still facing this issue, sometimes it does not load... When gapi is loaded correctly we have this behavior:
When gapi does not load correctly we have this behavior: The call to cb=gapi.loaded_1 is missing.
I'm loading the platform.js using this function:
public async loadGoogleApi(): Promise<void> {
try {
if (!this.$window.gapi) {
const libLoaded = this.$q.defer();
const node = document.createElement('script');
node.src = 'https://apis.google.com/js/platform.js';
node.type = 'text/javascript';
node.onload = libLoaded.resolve;
node.onerror = libLoaded.reject;
node.async = true;
node.charset = 'utf-8';
document.getElementsByTagName('head')[0].appendChild(node);
await libLoaded.promise;
}
if (!this.$window.gapi) throw new Error('Failed to load google api');
if (!this.$window.gapi.signin2) {
const signin2Loaded = this.$q.defer();
this.$window.gapi.load('signin2', {
callback: function () { signin2Loaded.resolve(true) },
onerror: function () { signin2Loaded.reject('gapi.signin2 failed to load') },
timeout: 5000, // 5 seconds.
ontimeout: function () { signin2Loaded.reject('gapi.signin2 load timeout reached') }
});
await signin2Loaded.promise;
}
if (!this.$window.gapi.auth2) {
const auth2Loaded = this.$q.defer();
this.$window.gapi.load('auth2', {
callback: function () { auth2Loaded.resolve(true) },
onerror: function () { auth2Loaded.reject('gapi.auth2 failed to load') },
timeout: 5000, // 5 seconds.
ontimeout: function () { auth2Loaded.reject('gapi.auth2 load timeout reached') }
});
await auth2Loaded.promise;
}
} catch (ex) {
HandleError.exception(ex);
}
}
Even using the onLoad=onGoogleScriptLoaded like the example above, I face the same issue. I've tried loading the script sync/async, same issue for both.
After some debugging, the gapi is always loaded inside the window object, the problem is the google button that sometimes is not rendered.
The work around is to manually render the button like that:
public renderGoogleLoginButton(buttonID: string): void {
try {
if (!buttonID) throw new Error('Google buttonID is NULL');
this.loadGoogleApi().then(() => {
this.$window.gapi.signin2.render(buttonID,
{
'scope': 'email profile',
//width': 200,
//'height': 50,
//'longtitle': false,
'theme': 'dark',
'onsuccess': this.handleGoogleLogin.bind(this),
'onfailure': this.handleGoogleError.bind(this)
});
}).catch((ex) => {
HandleError.exception(ex);
});
} catch (ex) {
HandleError.exception(ex);
}
}
As @gustavomassa I experience fluctuating behaviour - sometimes the onLoad callback is invoked and sometimes it's not.
As a workaround I've done this which is a complete hack and not acceptable in an app where performance is somewhat a priority but for the project I'm working on here it should be alright for now.
window.gapiWasLoaded = () => {
if (!gapiWasLoaded) {
gapiWasLoaded = true;
ReactDOM.render(<App />, document.getElementById('root'));
}
};
setTimeout(() => {
// @ts-ignore
window.gapiWasLoaded();
}, 1000);
I am experiencing the same. I am trying to use gapi from React. Just out of curiosity, is any work going into fixing this?
I am experiencing the same. I am trying to use gapi from React. Just out of curiosity, is any work going into fixing this?
Have you tried my solution in this post yet? This solved the problem for me. It's not a GAPI problem, but an implementation problem.
I've tried many variations on that theme. Here's a link to the current code try and a website preview. Slowing it down with my browser dev tools makes the problem more likely to occur.
The problem appears to be that the gapi.client.init doesn't always result in running either the fulfilled or the rejected callbacks provide to the then method. Is there any way to detect that situation? FWIW, this only seems to happen when I increase he latency for my network connection with the dev tools.
So, I moved the <script>
tags for gapi to the bottom of the <body>
, and it seems to be working. I am not sure why that seems to be making a difference, but it does.
Here's a link to the commit: https://github.com/ymatyt/ymatyt_site/commit/f031a628e613494baba30a80e625e84e7f6ebf0f
I came up with a solution that makes use of both @mesqueeb and @roelofjan-elsinga solutions. It grantees that gapiLoaded
is initialized before loading Google Api and it's safe to call gapi inside it.
window.gapiLoaded= function() {
//you can safely call gapi here
};
(function(d, s, id) {
var js,
p = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src =
'https://apis.google.com/js/platform.js?onload=gapiLoaded';
p.parentNode.insertBefore(js, p);
})(document, 'script', 'google-api-js');
@mesqueeb we observed this issue when injecting the
api.js
script asynchronously, in some browsers. We're not sure yet what the root cause of the bug is, but in the meantime you can use asetTimeout
before thegapi.client.init
call or not injecting dynamically theapi.js
script.
Works. Thanks @TMSCH
If you are like me and need to dispatch an action after gapi is loaded, the snippet below did the trick:
async function loadGAPI() {
let loaded = false;
let errorMessage = null;
const gapiClientLoad = new Promise((resolve, reject) => {
setTimeout(() => {
window.gapi.load('client:auth2', async () => {
try {
await window.gapi.client.init(GAPI_CONFIG);
resolve({ errorMessage, loaded: true });
} catch (gapiErrorResponse) {
errorMessage = gapiErrorResponse.error.message;
reject({ error: errorMessage, loaded });
}
})
}, 1000);
});
return gapiClientLoad;
}
Calling loadGAPI
resolves or rejects a value.
Hello everybody, I'am facing issue with google calendar. The issue is "when user1 is connecting his google calendar with our website account and then logout his account from our website only and then user2 connect his google calendar with his account of our website and then logout. So what happening is, now if user1 is login to his account then he is not able to access his google calendar and the error image link is shown below". (https://user-images.githubusercontent.com/38031527/88327203-730e2b00-cd44-11ea-86d2-23bea41cf789.jpeg) .
Please help me out, what should I need to do to make this process work. I'm stuck with this issue from long time. I must be missing something.
Facing same issue in nextjs . Is there any solutions yet ?
Since I got the update e-mail from @milenkovic, this is how I fixed it (maybe this will help you too @Neseke): First include the script (I'm not sure this is the right script, but this will work for the one you use):
<script type="text/javascript" src="https://apis.google.com/js/api:client.js?onLoad=onGoogleScriptLoaded"></script>
Then you can use the snippet @mesqueeb and @milenkovic shared:
window.onGoogleScriptLoad = () => { console.log('The google script has really loaded, cool!'); }
When you see "The google script has really loaded, cool!" in the console, you know the script has loaded and can safely use gapi or window.gapi.
This didn't work in nextjs
. Any ideas ?
Had the same problem. Solved it by calling gapi.load
after loading the script.
My solution (Typescript):
function loadScript(url: string) {
console.log(`Loading script from ${url} ...`)
return new Promise<Event>((resolve, reject) => {
const script = document.createElement("script");
script.src = url;
script.async = true;
script.onload = (ev) => { resolve(ev); };
script.onerror = reject;
document.body.appendChild(script);
}
);
}
function loadGapiClient() {
new Promise<void>((resolve, reject) => {
gapi.load('client', {
callback: function() {
// Handle gapi.client initialization.
console.log('gapi.client loaded successfully!')
console.log(`gapi.client : ${gapi.client}`);
resolve();
},
onerror: function() {
// Handle loading error.
console.log('gapi.client failed to load!');
reject();
},
timeout: 5000, // 5 seconds.
ontimeout: function() {
// Handle timeout.
console.log('gapi.client could not load in a timely manner!');
reject();
}
});
});
}
await loadScript("https://apis.google.com/js/api.js");
await loadGapiClient();
Had the same problem. Solved it by calling
gapi.load
after loading the script.My solution (Typescript):
function loadScript(url: string) { console.log(`Loading script from ${url} ...`) return new Promise<Event>((resolve, reject) => { const script = document.createElement("script"); script.src = url; script.async = true; script.onload = (ev) => { resolve(ev); }; script.onerror = reject; document.body.appendChild(script); } ); } function loadGapiClient() { new Promise<void>((resolve, reject) => { gapi.load('client', { callback: function() { // Handle gapi.client initialization. console.log('gapi.client loaded successfully!') console.log(`gapi.client : ${gapi.client}`); resolve(); }, onerror: function() { // Handle loading error. console.log('gapi.client failed to load!'); reject(); }, timeout: 5000, // 5 seconds. ontimeout: function() { // Handle timeout. console.log('gapi.client could not load in a timely manner!'); reject(); } }); }); } await loadScript("https://apis.google.com/js/api.js"); await loadGapiClient();
I have no idea why this works. I've tried doing this awaiting the new Promises
directly... but for some reason it doesn't work and has to wrapped in functions??! I must be making some typos somewhere... what's special about this? The key things I'm looking at is that async
is true
, and it's being appended to body
instead of head
...
Excellent gaidunce
I have (finally) noticed one of the biggest problems I have. The GAPI client is often not initialised, but hangs just after loading. This is my setup, I'll mark where it hangs:
It hangs right where I wrote the comment. But the load worked, but the client.init is not launched. I even added a catch statement, but it doesn't get launched.
Does anyone have any idea why?