mirror of
https://github.com/jiojciojsioe3/a3cjroijsiojiorj.git
synced 2024-11-15 15:31:51 +08:00
CustomSongs: Restore custom song list after reload
Uses the File System Access API supported in some browsers to keep the custom song list between sessions, restoring it back even when the page was closed.
This commit is contained in:
parent
1fceaadc7d
commit
b42b246a99
@ -4,6 +4,21 @@ function readFile(file, arrayBuffer, encoding){
|
||||
reader[arrayBuffer ? "readAsArrayBuffer" : "readAsText"](file, encoding)
|
||||
return promise
|
||||
}
|
||||
function filePermission(file){
|
||||
return file.queryPermission().then(response => {
|
||||
if(response === "granted"){
|
||||
return file
|
||||
}else{
|
||||
return file.requestPermission().then(response => {
|
||||
if(response === "granted"){
|
||||
return file
|
||||
}else{
|
||||
return Promise.reject(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
class RemoteFile{
|
||||
constructor(url){
|
||||
this.url = url
|
||||
@ -54,6 +69,23 @@ class LocalFile{
|
||||
return Promise.resolve(this.file)
|
||||
}
|
||||
}
|
||||
class FilesystemFile{
|
||||
constructor(file, path){
|
||||
this.file = file
|
||||
this.path = path
|
||||
this.url = this.path
|
||||
this.name = file.name
|
||||
}
|
||||
arrayBuffer(){
|
||||
return this.blob().then(blob => blob.arrayBuffer())
|
||||
}
|
||||
read(encoding){
|
||||
return this.blob().then(blob => readFile(blob, false, encoding))
|
||||
}
|
||||
blob(){
|
||||
return filePermission(this.file).then(file => file.getFile())
|
||||
}
|
||||
}
|
||||
class GdriveFile{
|
||||
constructor(fileObj){
|
||||
this.path = fileObj.path
|
||||
|
@ -35,7 +35,8 @@ var assets = {
|
||||
"account.js",
|
||||
"lyrics.js",
|
||||
"customsongs.js",
|
||||
"abstractfile.js"
|
||||
"abstractfile.js",
|
||||
"idb.js"
|
||||
],
|
||||
"css": [
|
||||
"main.css",
|
||||
|
@ -1,12 +1,23 @@
|
||||
class CustomSongs{
|
||||
constructor(touchEnabled){
|
||||
constructor(touchEnabled, noPage){
|
||||
this.loaderDiv = document.createElement("div")
|
||||
this.loaderDiv.innerHTML = assets.pages["loadsong"]
|
||||
var loadingText = this.loaderDiv.querySelector("#loading-text")
|
||||
this.setAltText(loadingText, strings.loading)
|
||||
|
||||
this.locked = false
|
||||
this.mode = "main"
|
||||
|
||||
if(noPage){
|
||||
this.noPage = true
|
||||
return
|
||||
}
|
||||
|
||||
this.touchEnabled = touchEnabled
|
||||
loader.changePage("customsongs", true)
|
||||
if(touchEnabled){
|
||||
this.getElement("view-outer").classList.add("touch-enabled")
|
||||
}
|
||||
this.locked = false
|
||||
this.mode = "main"
|
||||
|
||||
var tutorialTitle = this.getElement("view-title")
|
||||
this.setAltText(tutorialTitle, strings.customSongs.title)
|
||||
@ -19,7 +30,7 @@ class CustomSongs{
|
||||
|
||||
this.items = []
|
||||
this.linkLocalFolder = document.getElementById("link-localfolder")
|
||||
this.hasLocal = "webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))
|
||||
this.hasLocal = (typeof showDirectoryPicker === "function" || "webkitdirectory" in HTMLInputElement.prototype) && !(/Android|iPhone|iPad/.test(navigator.userAgent))
|
||||
this.selected = -1
|
||||
|
||||
if(this.hasLocal){
|
||||
@ -68,12 +79,7 @@ class CustomSongs{
|
||||
this.selected = this.items.length - 1
|
||||
}
|
||||
|
||||
this.loaderDiv = document.createElement("div")
|
||||
this.loaderDiv.innerHTML = assets.pages["loadsong"]
|
||||
var loadingText = this.loaderDiv.querySelector("#loading-text")
|
||||
this.setAltText(loadingText, strings.loading)
|
||||
|
||||
if(DataTransferItem.prototype.webkitGetAsEntry){
|
||||
if(DataTransferItem.prototype.getAsFileSystemHandle || DataTransferItem.prototype.webkitGetAsEntry){
|
||||
this.dropzone = document.getElementById("dropzone")
|
||||
var dropContent = this.dropzone.getElementsByClassName("view-content")[0]
|
||||
dropContent.innerText = strings.customSongs.dropzone
|
||||
@ -142,7 +148,19 @@ class CustomSongs{
|
||||
return
|
||||
}
|
||||
this.changeSelected(this.linkLocalFolder)
|
||||
this.browse.click()
|
||||
if(typeof showDirectoryPicker === "function"){
|
||||
return showDirectoryPicker().then(file => {
|
||||
this.walkFilesystem(file).then(files => this.importLocal(files)).then(e => {
|
||||
db.setItem("customFolder", file)
|
||||
}).catch(e => {
|
||||
if(e !== "cancel"){
|
||||
return Promise.reject(e)
|
||||
}
|
||||
})
|
||||
}, () => {})
|
||||
}else{
|
||||
this.browse.click()
|
||||
}
|
||||
}
|
||||
browseChange(event){
|
||||
var files = []
|
||||
@ -151,6 +169,24 @@ class CustomSongs{
|
||||
}
|
||||
this.importLocal(files)
|
||||
}
|
||||
walkFilesystem(dir, path=dir.name + "/", output=[]){
|
||||
return filePermission(dir).then(dir => {
|
||||
var values = dir.values()
|
||||
var walkValues = () => values.next().then(generator => {
|
||||
if(generator.done){
|
||||
return output
|
||||
}
|
||||
var file = generator.value
|
||||
if(file.kind === "directory"){
|
||||
return this.walkFilesystem(file, path + file.name + "/", output).then(() => walkValues())
|
||||
}else{
|
||||
output.push(new FilesystemFile(file, path + file.name))
|
||||
return walkValues()
|
||||
}
|
||||
})
|
||||
return walkValues()
|
||||
}, () => Promise.resolve())
|
||||
}
|
||||
filesDropped(event){
|
||||
event.preventDefault()
|
||||
this.dropzone.classList.remove("dragover")
|
||||
@ -158,46 +194,69 @@ class CustomSongs{
|
||||
if(this.locked){
|
||||
return
|
||||
}
|
||||
var files = []
|
||||
var walk = (entry, path="") => {
|
||||
return new Promise(resolve => {
|
||||
if(entry.isFile){
|
||||
entry.file(file => {
|
||||
files.push(new LocalFile(file, path + file.name))
|
||||
return resolve()
|
||||
}, resolve)
|
||||
}else if(entry.isDirectory){
|
||||
var dirReader = entry.createReader()
|
||||
dirReader.readEntries(entries => {
|
||||
var dirPromises = []
|
||||
for(var i = 0; i < entries.length; i++){
|
||||
dirPromises.push(walk(entries[i], path + entry.name + "/"))
|
||||
}
|
||||
return Promise.all(dirPromises).then(resolve)
|
||||
}, resolve)
|
||||
}else{
|
||||
return resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
var allFiles = []
|
||||
var dropPromises = []
|
||||
for(var i = 0; i < event.dataTransfer.items.length; i++){
|
||||
var entry = event.dataTransfer.items[i].webkitGetAsEntry()
|
||||
if(entry){
|
||||
dropPromises.push(walk(entry))
|
||||
var dropLength = event.dataTransfer.items.length
|
||||
for(var i = 0; i < dropLength; i++){
|
||||
var item = event.dataTransfer.items[i]
|
||||
let promise
|
||||
if(item.getAsFileSystemHandle){
|
||||
promise = item.getAsFileSystemHandle().then(file => {
|
||||
if(file.kind === "directory"){
|
||||
return this.walkFilesystem(file).then(files => {
|
||||
if(files.length && dropLength === 1){
|
||||
db.setItem("customFolder", file)
|
||||
}
|
||||
return files
|
||||
})
|
||||
}else{
|
||||
return [new FilesystemFile(file, file.name)]
|
||||
}
|
||||
})
|
||||
}else{
|
||||
var entry = item.webkitGetAsEntry()
|
||||
if(entry){
|
||||
promise = this.walkEntry(entry)
|
||||
}
|
||||
}
|
||||
if(promise){
|
||||
dropPromises.push(promise.then(files => {
|
||||
allFiles = allFiles.concat(files)
|
||||
}))
|
||||
}
|
||||
}
|
||||
Promise.all(dropPromises).then(() => this.importLocal(files))
|
||||
Promise.all(dropPromises).then(() => this.importLocal(allFiles))
|
||||
}
|
||||
walkEntry(entry, path="", output=[]){
|
||||
return new Promise(resolve => {
|
||||
if(entry.isFile){
|
||||
entry.file(file => {
|
||||
output.push(new LocalFile(file, path + file.name))
|
||||
return resolve()
|
||||
}, resolve)
|
||||
}else if(entry.isDirectory){
|
||||
var dirReader = entry.createReader()
|
||||
dirReader.readEntries(entries => {
|
||||
var dirPromises = []
|
||||
for(var i = 0; i < entries.length; i++){
|
||||
dirPromises.push(this.walkEntry(entries[i], path + entry.name + "/", output))
|
||||
}
|
||||
return Promise.all(dirPromises).then(resolve)
|
||||
}, resolve)
|
||||
}else{
|
||||
return resolve()
|
||||
}
|
||||
}).then(() => output)
|
||||
}
|
||||
importLocal(files){
|
||||
if(!files.length){
|
||||
return
|
||||
return Promise.resolve("cancel")
|
||||
}
|
||||
this.locked = true
|
||||
this.loading(true)
|
||||
|
||||
var importSongs = new ImportSongs()
|
||||
importSongs.load(files).then(this.songsLoaded.bind(this), e => {
|
||||
return importSongs.load(files).then(this.songsLoaded.bind(this), e => {
|
||||
this.browse.parentNode.reset()
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
@ -315,7 +374,7 @@ class CustomSongs{
|
||||
var length = songs.length
|
||||
assets.songs = songs
|
||||
assets.customSongs = true
|
||||
assets.customSelected = 0
|
||||
assets.customSelected = this.noPage ? +localStorage.getItem("customSelected") : 0
|
||||
}
|
||||
assets.sounds["se_don"].play()
|
||||
this.clean()
|
||||
@ -393,15 +452,18 @@ class CustomSongs{
|
||||
touched = this.touchEnabled
|
||||
}
|
||||
this.clean()
|
||||
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
|
||||
setTimeout(() => {
|
||||
if(!this.noPage){
|
||||
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
|
||||
}
|
||||
return new Promise(resolve => setTimeout(() => {
|
||||
new SongSelect("customSongs", false, touched)
|
||||
}, 500)
|
||||
resolve()
|
||||
}, 500))
|
||||
}
|
||||
showError(text){
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
if(this.mode === "error"){
|
||||
if(this.noPage || this.mode === "error"){
|
||||
return
|
||||
}
|
||||
this.mode = "error"
|
||||
@ -418,6 +480,10 @@ class CustomSongs{
|
||||
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
|
||||
}
|
||||
clean(){
|
||||
delete this.loaderDiv
|
||||
if(this.noPage){
|
||||
return
|
||||
}
|
||||
this.keyboard.clean()
|
||||
this.gamepad.clean()
|
||||
pageEvents.remove(this.browse, "change")
|
||||
@ -443,7 +509,6 @@ class CustomSongs{
|
||||
delete this.linkPrivacy
|
||||
delete this.endButton
|
||||
delete this.items
|
||||
delete this.loaderDiv
|
||||
delete this.errorDiv
|
||||
delete this.errorContent
|
||||
delete this.errorEnd
|
||||
|
51
public/src/js/idb.js
Normal file
51
public/src/js/idb.js
Normal file
@ -0,0 +1,51 @@
|
||||
class IDB{
|
||||
constructor(name, store){
|
||||
this.name = name
|
||||
this.store = store
|
||||
}
|
||||
init(){
|
||||
if(this.db){
|
||||
return Promise.resolve(this.db)
|
||||
}
|
||||
var request = indexedDB.open(this.name)
|
||||
request.onupgradeneeded = event => {
|
||||
var db = event.target.result
|
||||
db.createObjectStore(this.store)
|
||||
}
|
||||
return this.promise(request).then(result => {
|
||||
this.db = result
|
||||
return this.db
|
||||
}, target =>
|
||||
console.warn("DB error", target)
|
||||
)
|
||||
}
|
||||
promise(request){
|
||||
return new Promise((resolve, reject) => {
|
||||
return pageEvents.race(request, "success", "error").then(response => {
|
||||
if(response.type === "success"){
|
||||
return resolve(event.target.result)
|
||||
}else{
|
||||
return reject(event.target)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
transaction(method, ...args){
|
||||
return this.init().then(db =>
|
||||
db.transaction(this.store, "readwrite").objectStore(this.store)[method](...args)
|
||||
).then(this.promise.bind(this))
|
||||
}
|
||||
getItem(name){
|
||||
return this.transaction("get", name)
|
||||
}
|
||||
setItem(name, value){
|
||||
return this.transaction("put", value, name)
|
||||
}
|
||||
removeItem(name){
|
||||
return this.transaction("delete", name)
|
||||
}
|
||||
removeDB(){
|
||||
delete this.db
|
||||
return indexedDB.deleteDatabase(this.name)
|
||||
}
|
||||
}
|
@ -252,6 +252,7 @@ class Loader{
|
||||
settings = new Settings()
|
||||
pageEvents.setKbd()
|
||||
scoreStorage = new ScoreStorage()
|
||||
db = new IDB("taiko", "store")
|
||||
|
||||
Promise.all(this.promises).then(() => {
|
||||
if(this.error){
|
||||
|
@ -90,6 +90,7 @@ var settings
|
||||
var scoreStorage
|
||||
var account = {}
|
||||
var gpicker
|
||||
var db
|
||||
|
||||
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
|
||||
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){
|
||||
|
@ -210,7 +210,7 @@ class SongSelect{
|
||||
if(!assets.customSongs && !fromTutorial && !("selectedSong" in localStorage) && !songId){
|
||||
fromTutorial = touchEnabled ? "about" : "tutorial"
|
||||
}
|
||||
if(p2.session){
|
||||
if(p2.session || assets.customSongs && "customSelected" in localStorage){
|
||||
fromTutorial = false
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ class SongSelect{
|
||||
if(songIdIndex !== -1){
|
||||
this.selectedSong = songIdIndex
|
||||
}else if(assets.customSongs){
|
||||
this.selectedSong = assets.customSelected
|
||||
this.selectedSong = Math.min(Math.max(0, assets.customSelected), this.songs.length - 1)
|
||||
}else if((!p2.session || fadeIn) && "selectedSong" in localStorage){
|
||||
this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length - 1)
|
||||
}
|
||||
@ -508,7 +508,7 @@ class SongSelect{
|
||||
moveTo = "account"
|
||||
}else if(p2.session && 438 < mouse.x && mouse.x < 834 && mouse.y > 603){
|
||||
moveTo = "session"
|
||||
}else if(!p2.session && mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
}else if(!p2.session && mouse.x > 641 && mouse.y > 603 && p2.socket && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
moveTo = "session"
|
||||
}else{
|
||||
var moveTo = this.songSelMouse(mouse.x, mouse.y)
|
||||
@ -739,6 +739,7 @@ class SongSelect{
|
||||
try{
|
||||
if(assets.customSongs){
|
||||
assets.customSelected = this.selectedSong
|
||||
localStorage["customSelected"] = this.selectedSong
|
||||
}else{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
}
|
||||
@ -832,7 +833,7 @@ class SongSelect{
|
||||
this.state.moveHover = null
|
||||
}else{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
|
||||
|
||||
this.playSound("se_don")
|
||||
this.clean()
|
||||
setTimeout(() => {
|
||||
@ -850,6 +851,8 @@ class SongSelect{
|
||||
setTimeout(() => {
|
||||
new SongSelect("customSongs", false, this.touchEnabled)
|
||||
}, 500)
|
||||
localStorage.removeItem("customSelected")
|
||||
db.removeItem("customFolder")
|
||||
pageEvents.send("import-songs-default")
|
||||
}else{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
@ -1174,6 +1177,7 @@ class SongSelect{
|
||||
this.state.locked = 2
|
||||
if(assets.customSongs){
|
||||
assets.customSelected = this.selectedSong
|
||||
localStorage["customSelected"] = this.selectedSong
|
||||
}else if(!p2.session){
|
||||
try{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
@ -2097,7 +2101,7 @@ class SongSelect{
|
||||
ctx.lineTo(x + 4, y + 4)
|
||||
ctx.lineTo(x + 4, y + h)
|
||||
ctx.fill()
|
||||
if(screen !== "difficulty" && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
if(screen !== "difficulty" && p2.socket && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
var elapsed = (ms - this.state.screenMS) % 3100
|
||||
var fade = 1
|
||||
if(!p2.session && screen === "song"){
|
||||
|
@ -1,6 +1,7 @@
|
||||
class Titlescreen{
|
||||
constructor(songId){
|
||||
this.songId = songId
|
||||
db.getItem("customFolder").then(folder => this.customFolder = folder)
|
||||
|
||||
if(!songId){
|
||||
loader.changePage("titlescreen", false)
|
||||
@ -50,7 +51,7 @@ class Titlescreen{
|
||||
|
||||
onPressed(pressed, name){
|
||||
if(pressed){
|
||||
if(name === "gamepadConfirm" && snd.buffer.context.state === "suspended"){
|
||||
if(name === "gamepadConfirm" && (snd.buffer.context.state === "suspended" || this.customFolder)){
|
||||
return
|
||||
}
|
||||
this.titleScreen.style.cursor = "auto"
|
||||
@ -62,18 +63,28 @@ class Titlescreen{
|
||||
goNext(fromP2){
|
||||
if(p2.session && !fromP2){
|
||||
p2.send("songsel")
|
||||
}else if(fromP2 || localStorage.getItem("tutorial") === "true"){
|
||||
if(this.touched){
|
||||
localStorage.setItem("tutorial", "true")
|
||||
}
|
||||
pageEvents.remove(p2, "message")
|
||||
setTimeout(() => {
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
}, 500)
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
new SettingsView(this.touched, true, this.songId)
|
||||
}, 500)
|
||||
if(fromP2 || this.customFolder || localStorage.getItem("tutorial") === "true"){
|
||||
if(this.touched){
|
||||
localStorage.setItem("tutorial", "true")
|
||||
}
|
||||
pageEvents.remove(p2, "message")
|
||||
if(this.customFolder && !fromP2 && !assets.customSongs){
|
||||
var customSongs = new CustomSongs(this.touched, true)
|
||||
customSongs.walkFilesystem(this.customFolder).then(files => customSongs.importLocal(files)).catch(() => {
|
||||
db.removeItem("customFolder")
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
})
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
}, 500)
|
||||
}
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
new SettingsView(this.touched, true, this.songId)
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
setLang(lang, noEvent){
|
||||
|
Loading…
Reference in New Issue
Block a user