diff --git a/public/src/js/customsongs.js b/public/src/js/customsongs.js index 50ddd5e..e9f2d86 100644 --- a/public/src/js/customsongs.js +++ b/public/src/js/customsongs.js @@ -313,8 +313,10 @@ class CustomSongs{ return Promise.reject(e) } }).finally(() => { - var addRemove = !gpicker || !gpicker.oauthToken ? "add" : "remove" - this.linkGdriveAccount.classList[addRemove]("hiddenbtn") + if(this.linkGdriveAccount){ + var addRemove = !gpicker || !gpicker.oauthToken ? "add" : "remove" + this.linkGdriveAccount.classList[addRemove]("hiddenbtn") + } }) } gdriveAccount(event){ @@ -514,6 +516,9 @@ class CustomSongs{ pageEvents.remove(document, ["dragover", "dragleave", "drop"]) delete this.dropzone } + if(gpicker){ + gpicker.tokenResolve = null + } delete this.browse delete this.linkLocalFolder delete this.linkGdriveFolder diff --git a/public/src/js/gpicker.js b/public/src/js/gpicker.js index f5fb1ab..5f6eaa0 100644 --- a/public/src/js/gpicker.js +++ b/public/src/js/gpicker.js @@ -11,6 +11,7 @@ class Gpicker{ this.filesUrl = "https://www.googleapis.com/drive/v3/files/" this.resolveQueue = [] this.queueActive = false + this.clientCallbackBind = this.clientCallback.bind(this) } browse(lockedCallback, errorCallback){ return this.loadApi() @@ -123,9 +124,12 @@ class Gpicker{ if(window.gapi && gapi.client && gapi.client.drive){ return Promise.resolve() } - return loader.loadScript("https://apis.google.com/js/api.js") - .then(() => new Promise((resolve, reject) => - gapi.load("auth2:picker:client", { + var promises = [ + loader.loadScript("https://apis.google.com/js/api.js"), + loader.loadScript("https://accounts.google.com/gsi/client") + ] + return Promise.all(promises).then(() => new Promise((resolve, reject) => + gapi.load("picker:client", { callback: resolve, onerror: reject }) @@ -134,58 +138,53 @@ class Gpicker{ gapi.client.load("drive", "v3").then(resolve, reject) )) } - getAuth(errorCallback=()=>{}){ - if(!this.auth){ - return new Promise((resolve, reject) => { - gapi.auth2.init({ - clientId: this.oauthClientId, - fetch_basic_profile: false, - scope: this.scope - }).then(() => { - this.auth = gapi.auth2.getAuthInstance() - resolve(this.auth) - }, e => { - if(e.details){ - var errorStr = strings.gpicker.authError.replace("%s", e.details) - if(/cookie/i.test(e.details)){ - errorStr += "\n\n" + strings.gpicker.cookieError - } - errorCallback(errorStr) - } - reject(e) - }) - }) + getClient(errorCallback=()=>{}, force){ + var obj = { + client_id: this.oauthClientId, + scope: this.scope, + callback: this.clientCallbackBind + } + if(force){ + if(!this.clientForce){ + obj.select_account = true + this.clientForce = google.accounts.oauth2.initTokenClient(obj) + } + return this.clientForce }else{ - return Promise.resolve(this.auth) + if(!this.client){ + this.client = google.accounts.oauth2.initTokenClient(obj) + } + return this.client + } + } + clientCallback(tokenResponse){ + this.tokenResponse = tokenResponse + this.oauthToken = tokenResponse.access_token + if(this.oauthToken && this.tokenResolve){ + this.tokenResolve() } } getToken(lockedCallback=()=>{}, errorCallback=()=>{}, force){ if(this.oauthToken && !force){ return Promise.resolve() } - return this.getAuth(errorCallback).then(auth => { - var user = force || auth.currentUser.get() - if(force || !this.checkScope(user)){ - lockedCallback(false) - return auth.signIn(force ? { - prompt: "select_account" - } : undefined).then(user => { - if(this.checkScope(user)){ - lockedCallback(true) - }else{ - return Promise.reject("cancel") - } - }, () => Promise.reject("cancel")) + var client = this.getClient(errorCallback, force) + var promise = new Promise(resolve => { + this.tokenResolve = resolve + }) + lockedCallback(false) + client.requestAccessToken() + return promise.then(() => { + this.tokenResolve = null + if(this.checkScope()){ + lockedCallback(true) + }else{ + return Promise.reject("cancel") } }) } - checkScope(user){ - if(user.hasGrantedScopes(this.scope)){ - this.oauthToken = user.getAuthResponse(true).access_token - return this.oauthToken - }else{ - return false - } + checkScope(){ + return google.accounts.oauth2.hasGrantedAnyScope(this.tokenResponse, this.scope) } switchAccounts(lockedCallback, errorCallback){ return this.loadApi().then(() => this.getToken(lockedCallback, errorCallback, true)) diff --git a/templates/privacy.txt b/templates/privacy.txt index 0496f2f..9816989 100644 --- a/templates/privacy.txt +++ b/templates/privacy.txt @@ -23,10 +23,7 @@ You can use the Google Drive integration to let Taiko Web make your Taiko chart Applications that integrate with a Google account must declare their intent by requesting permissions. These permissions to your account must be granted in order for Taiko Web to integrate with Google accounts. Below is a list of these permissions and why they are required. At no time will Taiko Web request or have access to your Google account password. -3.1 "Associate you with your personal info on Google" Permission -Required for Google Sign-In to provide Taiko Web with a non-identifiable user authentication token. No other information provided by this permission is used. - -3.2 "See and download all your Google Drive files" Permission +3.1 "See and download all your Google Drive files" Permission When selecting a folder with the Google Drive file picker, Taiko Web instructs your Browser to recursively download all the files of that folder directly into your computer's memory. Limitation of Google Drive's permission model requires us to request access to all your Google Drive files, however, Taiko Web will only access the selected folder and its children, and only when requested. File parsing is handled locally; none of your Google Drive files is ever sent to our servers or third parties. {% endif %}{% if config.email %} {% if integration %}4{% else %}3{% endif %}. Contact Info