mirror of
https://github.com/yuukiwww/taiko-web.git
synced 2024-10-22 17:05:49 +02:00
Improve the song loading screen error message
This commit is contained in:
parent
a25e108a4b
commit
4b37150ee0
@ -119,6 +119,7 @@ kbd{
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #a9a9a9;
|
border: 1px solid #a9a9a9;
|
||||||
user-select: all;
|
user-select: all;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.text-warn{
|
.text-warn{
|
||||||
color: #d00;
|
color: #d00;
|
||||||
|
@ -207,14 +207,14 @@ class Controller{
|
|||||||
displayScore(score, notPlayed, bigNote){
|
displayScore(score, notPlayed, bigNote){
|
||||||
this.view.displayScore(score, notPlayed, bigNote)
|
this.view.displayScore(score, notPlayed, bigNote)
|
||||||
}
|
}
|
||||||
songSelection(fadeIn, scoreSaveFailed){
|
songSelection(fadeIn, showWarning){
|
||||||
if(!fadeIn){
|
if(!fadeIn){
|
||||||
this.clean()
|
this.clean()
|
||||||
}
|
}
|
||||||
if(this.calibrationMode){
|
if(this.calibrationMode){
|
||||||
new SettingsView(this.touchEnabled, false, null, "latency")
|
new SettingsView(this.touchEnabled, false, null, "latency")
|
||||||
}else{
|
}else{
|
||||||
new SongSelect(false, fadeIn, this.touchEnabled, null, scoreSaveFailed)
|
new SongSelect(false, fadeIn, this.touchEnabled, null, showWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
restartSong(){
|
restartSong(){
|
||||||
|
@ -34,7 +34,7 @@ class LoadSong{
|
|||||||
run(){
|
run(){
|
||||||
var song = this.selectedSong
|
var song = this.selectedSong
|
||||||
var id = song.folder
|
var id = song.folder
|
||||||
var promises = []
|
this.promises = []
|
||||||
if(song.folder !== "calibration"){
|
if(song.folder !== "calibration"){
|
||||||
assets.sounds["v_start"].play()
|
assets.sounds["v_start"].play()
|
||||||
var songObj = assets.songs.find(song => song.id === id)
|
var songObj = assets.songs.find(song => song.id === id)
|
||||||
@ -92,9 +92,9 @@ class LoadSong{
|
|||||||
img.crossOrigin = "Anonymous"
|
img.crossOrigin = "Anonymous"
|
||||||
}
|
}
|
||||||
let promise = pageEvents.load(img)
|
let promise = pageEvents.load(img)
|
||||||
promises.push(promise.then(() => {
|
this.addPromise(promise.then(() => {
|
||||||
return this.scaleImg(img, filename, prefix, force)
|
return this.scaleImg(img, filename, prefix, force)
|
||||||
}))
|
}), songObj.music ? filename + ".png" : skinBase + filename + ".png")
|
||||||
if(songObj.music){
|
if(songObj.music){
|
||||||
img.src = URL.createObjectURL(song.songSkin[filename + ".png"])
|
img.src = URL.createObjectURL(song.songSkin[filename + ".png"])
|
||||||
}else{
|
}else{
|
||||||
@ -102,14 +102,15 @@ class LoadSong{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
promises.push(this.loadSongBg(id))
|
this.loadSongBg(id)
|
||||||
|
|
||||||
promises.push(new Promise((resolve, reject) => {
|
var url = gameConfig.songs_baseurl + id + "/main.mp3"
|
||||||
|
this.addPromise(new Promise((resolve, reject) => {
|
||||||
if(songObj.sound){
|
if(songObj.sound){
|
||||||
songObj.sound.gain = snd.musicGain
|
songObj.sound.gain = snd.musicGain
|
||||||
resolve()
|
resolve()
|
||||||
}else if(!songObj.music){
|
}else if(!songObj.music){
|
||||||
snd.musicGain.load(gameConfig.songs_baseurl + id + "/main.mp3").then(sound => {
|
snd.musicGain.load(url).then(sound => {
|
||||||
songObj.sound = sound
|
songObj.sound = sound
|
||||||
resolve()
|
resolve()
|
||||||
}, reject)
|
}, reject)
|
||||||
@ -121,15 +122,15 @@ class LoadSong{
|
|||||||
}else{
|
}else{
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
}))
|
}), songObj.music ? songObj.music.webkitRelativePath : url)
|
||||||
if(songObj.chart){
|
if(songObj.chart){
|
||||||
if(songObj.chart === "blank"){
|
if(songObj.chart === "blank"){
|
||||||
this.songData = ""
|
this.songData = ""
|
||||||
}else{
|
}else{
|
||||||
var reader = new FileReader()
|
var reader = new FileReader()
|
||||||
promises.push(pageEvents.load(reader).then(event => {
|
this.addPromise(pageEvents.load(reader).then(event => {
|
||||||
this.songData = event.target.result.replace(/\0/g, "").split("\n")
|
this.songData = event.target.result.replace(/\0/g, "").split("\n")
|
||||||
}))
|
}), songObj.chart.webkitRelativePath)
|
||||||
if(song.type === "tja"){
|
if(song.type === "tja"){
|
||||||
reader.readAsText(songObj.chart, "sjis")
|
reader.readAsText(songObj.chart, "sjis")
|
||||||
}else{
|
}else{
|
||||||
@ -137,68 +138,88 @@ class LoadSong{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
promises.push(loader.ajax(this.getSongPath(song)).then(data => {
|
var url = this.getSongPath(song)
|
||||||
|
this.addPromise(loader.ajax(url).then(data => {
|
||||||
this.songData = data.replace(/\0/g, "").split("\n")
|
this.songData = data.replace(/\0/g, "").split("\n")
|
||||||
}))
|
}), url)
|
||||||
}
|
}
|
||||||
if(this.touchEnabled && !assets.image["touch_drum"]){
|
if(this.touchEnabled && !assets.image["touch_drum"]){
|
||||||
let img = document.createElement("img")
|
let img = document.createElement("img")
|
||||||
if(this.imgScale !== 1){
|
if(this.imgScale !== 1){
|
||||||
img.crossOrigin = "Anonymous"
|
img.crossOrigin = "Anonymous"
|
||||||
}
|
}
|
||||||
promises.push(pageEvents.load(img).then(() => {
|
var url = gameConfig.assets_baseurl + "img/touch_drum.png"
|
||||||
|
this.addPromise(pageEvents.load(img).then(() => {
|
||||||
return this.scaleImg(img, "touch_drum", "")
|
return this.scaleImg(img, "touch_drum", "")
|
||||||
}))
|
}), url)
|
||||||
img.src = gameConfig.assets_baseurl + "img/touch_drum.png"
|
img.src = url
|
||||||
}
|
}
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(this.promises).then(() => {
|
||||||
this.setupMultiplayer()
|
if(!this.error){
|
||||||
}, error => {
|
this.setupMultiplayer()
|
||||||
if(Array.isArray(error) && error[1] instanceof HTMLElement){
|
|
||||||
error = error[0] + ": " + error[1].outerHTML
|
|
||||||
}
|
}
|
||||||
console.error(error)
|
|
||||||
pageEvents.send("load-song-error", error)
|
|
||||||
errorMessage(new Error(error).stack)
|
|
||||||
alert(strings.errorOccured)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
addPromise(promise, url){
|
||||||
|
this.promises.push(promise.catch(response => {
|
||||||
|
this.errorMsg(response, url)
|
||||||
|
return Promise.resolve()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
errorMsg(error, url){
|
||||||
|
if(!this.error){
|
||||||
|
if(url){
|
||||||
|
error = (Array.isArray(error) ? error[0] + ": " : (error ? error + ": " : "")) + url
|
||||||
|
}
|
||||||
|
pageEvents.send("load-song-error", error)
|
||||||
|
errorMessage(new Error(error).stack)
|
||||||
|
var title = this.selectedSong.title
|
||||||
|
if(title !== this.selectedSong.originalTitle){
|
||||||
|
title += " (" + this.selectedSong.originalTitle + ")"
|
||||||
|
}
|
||||||
|
this.clean()
|
||||||
|
new SongSelect(false, false, this.touchEnabled, null, {
|
||||||
|
name: "loadSongError",
|
||||||
|
title: title,
|
||||||
|
id: this.selectedSong.folder,
|
||||||
|
error: error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.error = true
|
||||||
|
}
|
||||||
loadSongBg(){
|
loadSongBg(){
|
||||||
return new Promise((resolve, reject) => {
|
var filenames = []
|
||||||
var promises = []
|
if(this.selectedSong.songBg !== null){
|
||||||
var filenames = []
|
filenames.push("bg_song_" + this.selectedSong.songBg)
|
||||||
if(this.selectedSong.songBg !== null){
|
}
|
||||||
filenames.push("bg_song_" + this.selectedSong.songBg)
|
if(this.selectedSong.donBg !== null){
|
||||||
|
filenames.push("bg_don_" + this.selectedSong.donBg)
|
||||||
|
if(this.multiplayer){
|
||||||
|
filenames.push("bg_don2_" + this.selectedSong.donBg)
|
||||||
}
|
}
|
||||||
if(this.selectedSong.donBg !== null){
|
}
|
||||||
filenames.push("bg_don_" + this.selectedSong.donBg)
|
if(this.selectedSong.songStage !== null){
|
||||||
if(this.multiplayer){
|
filenames.push("bg_stage_" + this.selectedSong.songStage)
|
||||||
filenames.push("bg_don2_" + this.selectedSong.donBg)
|
}
|
||||||
}
|
for(var i = 0; i < filenames.length; i++){
|
||||||
}
|
var filename = filenames[i]
|
||||||
if(this.selectedSong.songStage !== null){
|
var stage = filename.startsWith("bg_stage_")
|
||||||
filenames.push("bg_stage_" + this.selectedSong.songStage)
|
for(var letter = 0; letter < (stage ? 1 : 2); letter++){
|
||||||
}
|
let filenameAb = filenames[i] + (stage ? "" : (letter === 0 ? "a" : "b"))
|
||||||
for(var i = 0; i < filenames.length; i++){
|
if(!(filenameAb in assets.image)){
|
||||||
var filename = filenames[i]
|
let img = document.createElement("img")
|
||||||
var stage = filename.startsWith("bg_stage_")
|
let force = filenameAb.startsWith("bg_song_") && this.touchEnabled
|
||||||
for(var letter = 0; letter < (stage ? 1 : 2); letter++){
|
if(this.imgScale !== 1 || force){
|
||||||
let filenameAb = filenames[i] + (stage ? "" : (letter === 0 ? "a" : "b"))
|
img.crossOrigin = "Anonymous"
|
||||||
if(!(filenameAb in assets.image)){
|
|
||||||
let img = document.createElement("img")
|
|
||||||
let force = filenameAb.startsWith("bg_song_") && this.touchEnabled
|
|
||||||
if(this.imgScale !== 1 || force){
|
|
||||||
img.crossOrigin = "Anonymous"
|
|
||||||
}
|
|
||||||
promises.push(pageEvents.load(img).then(() => {
|
|
||||||
return this.scaleImg(img, filenameAb, "", force)
|
|
||||||
}))
|
|
||||||
img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png"
|
|
||||||
}
|
}
|
||||||
|
var url = gameConfig.assets_baseurl + "img/" + filenameAb + ".png"
|
||||||
|
this.addPromise(pageEvents.load(img).then(() => {
|
||||||
|
return this.scaleImg(img, filenameAb, "", force)
|
||||||
|
}), url)
|
||||||
|
img.src = url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Promise.all(promises).then(resolve, reject)
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
scaleImg(img, filename, prefix, force){
|
scaleImg(img, filename, prefix, force){
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -333,6 +354,7 @@ class LoadSong{
|
|||||||
pageEvents.send("load-song-cancel")
|
pageEvents.send("load-song-cancel")
|
||||||
}
|
}
|
||||||
clean(){
|
clean(){
|
||||||
|
delete this.promises
|
||||||
pageEvents.remove(p2, "message")
|
pageEvents.remove(p2, "message")
|
||||||
if(this.cancelButton){
|
if(this.cancelButton){
|
||||||
pageEvents.remove(this.cancelButton, ["mousedown", "touchstart"])
|
pageEvents.remove(this.cancelButton, ["mousedown", "touchstart"])
|
||||||
|
@ -864,7 +864,7 @@ class Scoresheet{
|
|||||||
|
|
||||||
if(elapsed >= 1000){
|
if(elapsed >= 1000){
|
||||||
this.clean()
|
this.clean()
|
||||||
this.controller.songSelection(true, this.scoreSaveFailed)
|
this.controller.songSelection(true, this.showWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -932,12 +932,12 @@ class Scoresheet{
|
|||||||
delete this.resultsObj.difficulty
|
delete this.resultsObj.difficulty
|
||||||
delete this.resultsObj.gauge
|
delete this.resultsObj.gauge
|
||||||
scoreStorage.add(hash, difficulty, this.resultsObj, true, title).catch(() => {
|
scoreStorage.add(hash, difficulty, this.resultsObj, true, title).catch(() => {
|
||||||
this.scoreSaveFailed = true
|
this.showWarning = {name: "scoreSaveFailed"}
|
||||||
})
|
})
|
||||||
}else if(oldScore && (crown === "gold" && oldScore.crown !== "gold" || crown && !oldScore.crown)){
|
}else if(oldScore && (crown === "gold" && oldScore.crown !== "gold" || crown && !oldScore.crown)){
|
||||||
oldScore.crown = crown
|
oldScore.crown = crown
|
||||||
scoreStorage.add(hash, difficulty, oldScore, true, title).catch(() => {
|
scoreStorage.add(hash, difficulty, oldScore, true, title).catch(() => {
|
||||||
this.scoreSaveFailed = true
|
this.showWarning = {name: "scoreSaveFailed"}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
class SongSelect{
|
class SongSelect{
|
||||||
constructor(fromTutorial, fadeIn, touchEnabled, songId, scoreSaveFailed){
|
constructor(fromTutorial, fadeIn, touchEnabled, songId, showWarning){
|
||||||
this.touchEnabled = touchEnabled
|
this.touchEnabled = touchEnabled
|
||||||
|
|
||||||
loader.changePage("songselect", false)
|
loader.changePage("songselect", false)
|
||||||
@ -167,7 +167,8 @@ class SongSelect{
|
|||||||
category: strings.random
|
category: strings.random
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if(scoreSaveFailed){
|
this.showWarning = showWarning
|
||||||
|
if(showWarning && showWarning.name === "scoreSaveFailed"){
|
||||||
scoreStorage.scoreSaveFailed = true
|
scoreStorage.scoreSaveFailed = true
|
||||||
}
|
}
|
||||||
this.songs.push({
|
this.songs.push({
|
||||||
@ -383,10 +384,11 @@ class SongSelect{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var shift = event ? event.shiftKey : this.pressedKeys["shift"]
|
var shift = event ? event.shiftKey : this.pressedKeys["shift"]
|
||||||
if(this.state.scoreSaveFailed){
|
if(this.state.showWarning){
|
||||||
if(name === "confirm"){
|
if(name === "confirm"){
|
||||||
this.playSound("se_don")
|
this.playSound("se_don")
|
||||||
this.state.scoreSaveFailed = false
|
this.state.showWarning = false
|
||||||
|
this.showWarning = false
|
||||||
}
|
}
|
||||||
}else if(this.state.screen === "song"){
|
}else if(this.state.screen === "song"){
|
||||||
if(name === "confirm"){
|
if(name === "confirm"){
|
||||||
@ -462,10 +464,11 @@ class SongSelect{
|
|||||||
var ctrl = false
|
var ctrl = false
|
||||||
var touch = true
|
var touch = true
|
||||||
}
|
}
|
||||||
if(this.state.scoreSaveFailed){
|
if(this.state.showWarning){
|
||||||
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
|
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
|
||||||
this.playSound("se_don")
|
this.playSound("se_don")
|
||||||
this.state.scoreSaveFailed = false
|
this.state.showWarning = false
|
||||||
|
this.showWarning = false
|
||||||
}
|
}
|
||||||
}else if(this.state.screen === "song"){
|
}else if(this.state.screen === "song"){
|
||||||
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
|
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
|
||||||
@ -522,9 +525,9 @@ class SongSelect{
|
|||||||
mouseMove(event){
|
mouseMove(event){
|
||||||
var mouse = this.mouseOffset(event.offsetX, event.offsetY)
|
var mouse = this.mouseOffset(event.offsetX, event.offsetY)
|
||||||
var moveTo = null
|
var moveTo = null
|
||||||
if(this.state.scoreSaveFailed){
|
if(this.state.showWarning){
|
||||||
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
|
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
|
||||||
moveTo = "scoreSaveFailed"
|
moveTo = "showWarning"
|
||||||
}
|
}
|
||||||
}else if(this.state.screen === "song"){
|
}else if(this.state.screen === "song"){
|
||||||
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
|
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
|
||||||
@ -1019,12 +1022,17 @@ class SongSelect{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(screen === "song" && scoreStorage.scoreSaveFailed && !p2.session){
|
if(screen === "song" && (this.showWarning || scoreStorage.scoreSaveFailed) && !p2.session){
|
||||||
|
if(!this.showWarning){
|
||||||
|
this.showWarning = {name: "scoreSaveFailed"}
|
||||||
|
}
|
||||||
if(this.bgmEnabled){
|
if(this.bgmEnabled){
|
||||||
this.playBgm(false)
|
this.playBgm(false)
|
||||||
}
|
}
|
||||||
scoreStorage.scoreSaveFailed = false
|
if(this.showWarning.name === "scoreSaveFailed"){
|
||||||
this.state.scoreSaveFailed = true
|
scoreStorage.scoreSaveFailed = false
|
||||||
|
}
|
||||||
|
this.state.showWarning = true
|
||||||
this.state.locked = true
|
this.state.locked = true
|
||||||
this.playSound("se_pause")
|
this.playSound("se_pause")
|
||||||
}
|
}
|
||||||
@ -2130,7 +2138,7 @@ class SongSelect{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.state.scoreSaveFailed){
|
if(this.state.showWarning){
|
||||||
if(this.preview){
|
if(this.preview){
|
||||||
this.endPreview()
|
this.endPreview()
|
||||||
}
|
}
|
||||||
@ -2164,9 +2172,24 @@ class SongSelect{
|
|||||||
dx: 68,
|
dx: 68,
|
||||||
dy: 11
|
dy: 11
|
||||||
})
|
})
|
||||||
|
if(this.showWarning.name === "scoreSaveFailed"){
|
||||||
|
var text = strings.scoreSaveFailed
|
||||||
|
}else if(this.showWarning.name === "loadSongError"){
|
||||||
|
var text = []
|
||||||
|
var textIndex = 0
|
||||||
|
var subText = [this.showWarning.title, this.showWarning.id, this.showWarning.error]
|
||||||
|
var textParts = strings.loadSongError.split("%s")
|
||||||
|
textParts.forEach((textPart, i) => {
|
||||||
|
if(i !== 0){
|
||||||
|
text.push(subText[textIndex++])
|
||||||
|
}
|
||||||
|
text.push(textPart)
|
||||||
|
})
|
||||||
|
text = text.join("")
|
||||||
|
}
|
||||||
this.draw.wrappingText({
|
this.draw.wrappingText({
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
text: strings.scoreSaveFailed,
|
text: text,
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
fontFamily: this.font,
|
fontFamily: this.font,
|
||||||
x: 300,
|
x: 300,
|
||||||
@ -2176,7 +2199,7 @@ class SongSelect{
|
|||||||
lineHeight: 35,
|
lineHeight: 35,
|
||||||
fill: "#000",
|
fill: "#000",
|
||||||
verticalAlign: "middle",
|
verticalAlign: "middle",
|
||||||
textAlign: "center",
|
textAlign: "center"
|
||||||
})
|
})
|
||||||
|
|
||||||
var _x = 640
|
var _x = 640
|
||||||
@ -2211,7 +2234,7 @@ class SongSelect{
|
|||||||
}, layers)
|
}, layers)
|
||||||
|
|
||||||
var highlight = 1
|
var highlight = 1
|
||||||
if(this.state.moveHover === "scoreSaveFailed"){
|
if(this.state.moveHover === "showWarning"){
|
||||||
highlight = 2
|
highlight = 2
|
||||||
}
|
}
|
||||||
if(highlight){
|
if(highlight){
|
||||||
@ -2330,7 +2353,7 @@ class SongSelect{
|
|||||||
}
|
}
|
||||||
|
|
||||||
startPreview(loadOnly){
|
startPreview(loadOnly){
|
||||||
if(!loadOnly && this.state && this.state.scoreSaveFailed){
|
if(!loadOnly && this.state && this.state.showWarning){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var currentSong = this.songs[this.selectedSong]
|
var currentSong = this.songs[this.selectedSong]
|
||||||
@ -2408,7 +2431,7 @@ class SongSelect{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
playBgm(enabled){
|
playBgm(enabled){
|
||||||
if(enabled && this.state && this.state.scoreSaveFailed){
|
if(enabled && this.state && this.state.showWarning){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(enabled && !this.bgmEnabled){
|
if(enabled && !this.bgmEnabled){
|
||||||
|
@ -266,6 +266,10 @@ var translations = {
|
|||||||
ja: null,
|
ja: null,
|
||||||
en: "Could not connect to the server, your score has not been saved.\n\nPlease log in or refresh the page to try saving the score again."
|
en: "Could not connect to the server, your score has not been saved.\n\nPlease log in or refresh the page to try saving the score again."
|
||||||
},
|
},
|
||||||
|
loadSongError: {
|
||||||
|
ja: null,
|
||||||
|
en: "Could not load song %s with id %s.\n\n%s"
|
||||||
|
},
|
||||||
loading: {
|
loading: {
|
||||||
ja: "ロード中...",
|
ja: "ロード中...",
|
||||||
en: "Loading...",
|
en: "Loading...",
|
||||||
|
Loading…
Reference in New Issue
Block a user