ImportSongs: Implement Google Drive beta testing

Instances that wish to enable Google Drive support should first enable it to only a small subset of users (100 maximum) to allow the OAuth screen to be verified by Google without hitting the user limit. Minimum level in the config can be set to enable beta testing of this feature and then disabled by setting it to None.

- Add user level assignment screen to the administration panel
- Add privacy policy and links to it in various places
- Add switch accounts link near the Google Drive picker
This commit is contained in:
LoveEevee
2020-12-21 16:02:56 +03:00
parent 665fc57929
commit b8e63c650f
14 changed files with 313 additions and 35 deletions

View File

@@ -112,10 +112,15 @@ kbd{
margin-right: 0.4em;
}
.center-buttons{
display: flex;
justify-content: center;
margin: 1.5em 0;
}
.account-view .center-buttons{
margin: 0.3em 0;
}
.center-buttons>div{
text-align: center;
margin: 0.2em 0;
}
.center-buttons .taibtn{
margin: 0 0.2em;
}

View File

@@ -93,6 +93,11 @@ class Account{
this.inputForms.push(this.accountDel.password)
this.accountDelDiv = this.getElement("accountdel-div")
this.linkPrivacy = this.getElement("privacy-btn")
this.setAltText(this.linkPrivacy, strings.account.privacy)
pageEvents.add(this.linkPrivacy, ["mousedown", "touchstart"], this.openPrivacy.bind(this))
this.items.push(this.linkPrivacy)
this.logoutButton = this.getElement("logout-btn")
this.setAltText(this.logoutButton, strings.account.logout)
pageEvents.add(this.logoutButton, ["mousedown", "touchstart"], this.onLogout.bind(this))
@@ -245,6 +250,12 @@ class Account{
pageEvents.add(this.registerButton, ["mousedown", "touchstart"], this.onSwitchMode.bind(this))
this.items.push(this.registerButton)
this.linkPrivacy = this.getElement("privacy-btn")
this.setAltText(this.linkPrivacy, strings.account.privacy)
pageEvents.add(this.linkPrivacy, ["mousedown", "touchstart"], this.openPrivacy.bind(this))
this.items.push(this.linkPrivacy)
if(!register){
this.items.push(this.loginButton)
}
@@ -282,11 +293,17 @@ class Account{
this.onSwitchMode()
}else if(selected === this.loginButton){
this.onLogin()
}else if(selected === this.linkPrivacy){
assets.sounds["se_don"].play()
this.openPrivacy()
}
}else if(name === "previous" || name === "next"){
selected.classList.remove("selected")
this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1))
this.items[this.selected].classList.add("selected")
if(this.items[this.selected] === this.linkPrivacy){
this.items[this.selected].scrollIntoView()
}
assets.sounds["se_ka"].play()
}else if(name === "back"){
this.onEnd()
@@ -380,6 +397,19 @@ class Account{
}
})
}
openPrivacy(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
open("privacy")
}
onLogout(){
if(event){
if(event.type === "touchstart"){
@@ -583,6 +613,7 @@ class Account{
pageEvents.remove(this.customdonResetBtn, ["click", "touchstart"])
pageEvents.remove(this.accounPassButton, ["click", "touchstart"])
pageEvents.remove(this.accountDelButton, ["click", "touchstart"])
pageEvents.remove(this.linkPrivacy, ["mousedown", "touchstart"])
pageEvents.remove(this.logoutButton, ["mousedown", "touchstart"])
pageEvents.remove(this.saveButton, ["mousedown", "touchstart"])
for(var i = 0; i < this.inputForms.length; i++){
@@ -602,6 +633,7 @@ class Account{
delete this.accountDelButton
delete this.accountDel
delete this.accountDelDiv
delete this.linkPrivacy
delete this.logoutButton
delete this.saveButton
delete this.inputForms
@@ -615,12 +647,14 @@ class Account{
for(var i = 0; i < this.form.length; i++){
pageEvents.remove(this.registerButton, ["keydown", "keyup", "keypress"])
}
pageEvents.remove(this.linkPrivacy, ["mousedown", "touchstart"])
delete this.errorDiv
delete this.form
delete this.password2
delete this.remember
delete this.loginButton
delete this.registerButton
delete this.linkPrivacy
}
pageEvents.remove(this.endButton, ["mousedown", "touchstart"])
delete this.endButton

View File

@@ -36,7 +36,10 @@ class CustomSongs{
this.linkLocalFolder.parentNode.removeChild(this.linkLocalFolder)
}
var groupGdrive = document.getElementById("group-gdrive")
this.linkGdriveFolder = document.getElementById("link-gdrivefolder")
this.linkGdriveAccount = document.getElementById("link-gdriveaccount")
this.linkPrivacy = document.getElementById("link-privacy")
if(gameConfig.google_credentials.gdrive_enabled){
this.setAltText(this.linkGdriveFolder, strings.customSongs.gdriveFolder)
pageEvents.add(this.linkGdriveFolder, ["mousedown", "touchstart"], this.gdriveFolder.bind(this))
@@ -45,8 +48,14 @@ class CustomSongs{
this.linkGdriveFolder.classList.add("selected")
this.selected = this.items.length - 1
}
this.setAltText(this.linkGdriveAccount, strings.customSongs.gdriveAccount)
pageEvents.add(this.linkGdriveAccount, ["mousedown", "touchstart"], this.gdriveAccount.bind(this))
this.items.push(this.linkGdriveAccount)
this.setAltText(this.linkPrivacy, strings.account.privacy)
pageEvents.add(this.linkPrivacy, ["mousedown", "touchstart"], this.openPrivacy.bind(this))
this.items.push(this.linkPrivacy)
}else{
this.linkGdriveFolder.parentNode.removeChild(this.linkGdriveFolder)
groupGdrive.style.display = "none"
}
this.endButton = this.getElement("view-end-button")
@@ -237,13 +246,67 @@ class CustomSongs{
}else if(e !== "cancel"){
return Promise.reject(e)
}
}).finally(() => {
var addRemove = !gpicker || !gpicker.oauthToken ? "add" : "remove"
this.linkGdriveAccount.classList[addRemove]("hiddenbtn")
})
}
gdriveAccount(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked || this.mode !== "main"){
return
}
this.changeSelected(this.linkGdriveAccount)
this.locked = true
this.loading(true)
if(!gpicker){
var gpickerPromise = loader.loadScript("/src/js/gpicker.js").then(() => {
gpicker = new Gpicker()
})
}else{
var gpickerPromise = Promise.resolve()
}
gpickerPromise.then(() => {
return gpicker.switchAccounts(locked => {
this.locked = locked
this.loading(locked)
}, error => {
this.showError(error)
})
}).then(() => {
this.locked = false
this.loading(false)
}).catch(error => {
if(error !== "cancel"){
this.showError(error)
}
})
}
openPrivacy(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked || this.mode !== "main"){
return
}
this.changeSelected(this.linkPrivacy)
open("privacy")
}
loading(show){
if(show){
loader.screen.appendChild(this.loaderDiv)
}else{
loader.screen.removeChild(this.loaderDiv)
}else if(this.loaderDiv.parentNode){
this.loaderDiv.parentNode.removeChild(this.loaderDiv)
}
}
songsLoaded(songs){
@@ -276,6 +339,12 @@ class CustomSongs{
}else if(selected === this.linkGdriveFolder){
assets.sounds["se_don"].play()
this.gdriveFolder()
}else if(selected === this.linkGdriveAccount){
assets.sounds["se_don"].play()
this.gdriveAccount()
}else if(selected === this.linkPrivacy){
assets.sounds["se_don"].play()
this.openPrivacy()
}
}
}else if(name === "previous" || name === "next"){
@@ -327,6 +396,8 @@ class CustomSongs{
}, 500)
}
showError(text){
this.locked = false
this.loading(false)
if(this.mode === "error"){
return
}
@@ -352,6 +423,8 @@ class CustomSongs{
}
if(gameConfig.google_credentials.gdrive_enabled){
pageEvents.remove(this.linkGdriveFolder, ["mousedown", "touchstart"])
pageEvents.remove(this.linkGdriveAccount, ["mousedown", "touchstart"])
pageEvents.remove(this.linkPrivacy, ["mousedown", "touchstart"])
}
pageEvents.remove(this.endButton, ["mousedown", "touchstart"])
pageEvents.remove(this.errorDiv, ["mousedown", "touchstart"])
@@ -363,6 +436,8 @@ class CustomSongs{
delete this.browse
delete this.linkLocalFolder
delete this.linkGdriveFolder
delete this.linkGdriveAccount
delete this.linkPrivacy
delete this.endButton
delete this.items
delete this.loaderDiv

View File

@@ -131,37 +131,48 @@ class Gpicker{
gapi.client.load("drive", "v3").then(resolve, reject)
))
}
getToken(lockedCallback=()=>{}, errorCallback=()=>{}){
if(this.oauthToken){
return Promise.resolve()
}
getAuth(errorCallback=()=>{}){
if(!this.auth){
var authPromise = gapi.auth2.init({
clientId: this.oauthClientId,
fetch_basic_profile: false,
scope: this.scope
}).then(() => {
this.auth = gapi.auth2.getAuthInstance()
}, e => {
if(e.details){
errorCallback(strings.gpicker.authError.replace("%s", e.details))
}
return Promise.reject(e)
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)
})
})
}else{
var authPromise = Promise.resolve()
return Promise.resolve(this.auth)
}
return authPromise.then(() => {
var user = this.auth.currentUser.get()
if(!this.checkScope(user)){
}
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)
this.auth.signIn().then(user => {
return auth.signIn(force ? {
prompt: "select_account"
} : undefined).then(user => {
if(this.checkScope(user)){
lockedCallback(true)
}else{
return Promise.reject("cancel")
}
})
}, () => Promise.reject("cancel"))
}
})
}
@@ -173,6 +184,9 @@ class Gpicker{
return false
}
}
switchAccounts(lockedCallback, errorCallback){
return this.loadApi().then(() => this.getToken(lockedCallback, errorCallback, true))
}
displayPicker(callback){
var picker = gapi.picker.api
new picker.PickerBuilder()

View File

@@ -1,6 +1,6 @@
addEventListener("error", function(err){
var stack
if("error" in err){
if("error" in err && err.error){
stack = err.error.stack
}else{
stack = err.message + "\n at " + err.filename + ":" + err.lineno + ":" + err.colno

View File

@@ -918,6 +918,13 @@ var translations = {
tw: "註冊",
ko: "가입하기"
},
privacy: {
ja: "プライバシー",
en: "Privacy",
cn: "隐私权",
tw: "隱私權",
ko: "개인정보처리방침"
},
registerAccount: {
ja: "アカウントを登録",
en: "Register account",
@@ -1135,6 +1142,13 @@ var translations = {
tw: "Google雲端硬碟...",
ko: "구글 드라이브..."
},
gdriveAccount: {
ja: "アカウントの切り替え",
en: "Switch Accounts",
cn: "切换帐户",
tw: "切換帳戶",
ko: "계정 전환"
},
dropzone: {
ja: "ここにファイルをドロップ",
en: "Drop files here",
@@ -1180,6 +1194,9 @@ var translations = {
},
authError: {
en: "Auth error: %s"
},
cookieError: {
en: "This function requires third party cookies."
}
}
}

View File

@@ -37,6 +37,11 @@
<input type="password" name="password">
</div>
</form>
<div class="center-buttons">
<div>
<div class="privacy-btn taibtn stroke-sub link-btn"></div>
</div>
</div>
</div>
<div id="diag-txt"></div>
<div class="left-buttons">

View File

@@ -3,8 +3,14 @@
<div class="view-title stroke-sub"></div>
<div class="view-content"></div>
<div class="center-buttons">
<div id="link-localfolder" class="taibtn stroke-sub link-btn"></div>
<div id="link-gdrivefolder" class="taibtn stroke-sub link-btn"></div>
<div>
<div id="link-localfolder" class="taibtn stroke-sub link-btn"></div>
</div>
<div id="group-gdrive">
<div id="link-gdrivefolder" class="taibtn stroke-sub link-btn"></div>
<div id="link-gdriveaccount" class="taibtn stroke-sub link-btn"></div>
<div id="link-privacy" class="taibtn stroke-sub link-btn"></div>
</div>
</div>
<div class="view-end-button taibtn stroke-sub"></div>
<div class="view-outer shadow-outer" id="dropzone">

View File

@@ -19,6 +19,7 @@
</div>
<div class="left-buttons">
<div class="register-btn taibtn stroke-sub link-btn"></div>
<div class="privacy-btn taibtn stroke-sub link-btn"></div>
</div>
<div class="view-end-button taibtn stroke-sub selected"></div>
</div>