191 lines
5.0 KiB
TypeScript
Raw Normal View History

2019-11-18 02:04:13 +00:00
import bytesHumanReadable from "scripts/Utils/bytesHumanReadable"
import uploadWithProgress from "scripts/Utils/uploadWithProgress"
2018-04-02 05:34:16 +00:00
import AnimeNotifier from "../AnimeNotifier"
2018-03-02 16:18:29 +00:00
// Select file
export function selectFile(arn: AnimeNotifier, button: HTMLButtonElement) {
2019-11-17 09:25:14 +00:00
const fileType = button.dataset.type
const endpoint = button.dataset.endpoint
2019-04-22 06:59:08 +00:00
2019-11-18 02:04:13 +00:00
if(endpoint === "/api/upload/user/cover" && arn.user && !arn.user.IsPro()) {
alert("Please buy a PRO account to use this feature.")
return
}
2018-03-07 13:00:14 +00:00
// Click on virtual file input element
2019-11-17 09:25:14 +00:00
const input = document.createElement("input")
2018-03-02 16:18:29 +00:00
input.setAttribute("type", "file")
2019-04-22 06:59:08 +00:00
input.value = ""
2018-03-02 16:18:29 +00:00
2018-10-31 09:57:28 +00:00
input.onchange = async () => {
2019-04-22 06:59:08 +00:00
if(!fileType || !endpoint) {
console.error("Missing data-type or data-endpoint:", button)
return
}
2018-03-02 20:42:34 +00:00
2019-04-22 06:59:08 +00:00
if(!input.files || input.files.length === 0) {
2018-03-03 17:38:21 +00:00
return
}
2019-11-17 09:25:14 +00:00
const file = input.files[0]
2019-04-22 06:59:08 +00:00
2018-04-14 22:20:53 +00:00
// Check mime type for images
if(fileType === "image" && !file.type.startsWith("image/")) {
2018-03-03 18:58:18 +00:00
arn.statusMessage.showError(file.name + " is not an image file!")
return
}
2018-04-14 22:20:53 +00:00
// Check mime type for videos
if(fileType === "video" && !file.type.startsWith("video/webm")) {
arn.statusMessage.showError(file.name + " is not a WebM video file!")
return
}
2018-03-02 16:18:29 +00:00
2018-04-14 22:20:53 +00:00
// Preview image
if(fileType === "image") {
2019-11-17 09:25:14 +00:00
const previews = document.getElementsByClassName(button.id + "-preview")
const dataURL = await readImageAsDataURL(file)
const img = await loadImage(dataURL)
2018-10-31 09:57:28 +00:00
switch(endpoint) {
case "/api/upload/user/image":
2018-10-31 09:57:28 +00:00
if(img.naturalWidth <= 280 || img.naturalHeight < 280) {
arn.statusMessage.showError(`Your image has a resolution of ${img.naturalWidth} x ${img.naturalHeight} pixels which is too small. Recommended: 560 x 560. Minimum: 280 x 280.`, 8000)
return
}
break
case "/api/upload/user/cover":
2018-10-31 09:57:28 +00:00
if(img.naturalWidth <= 960 || img.naturalHeight < 225) {
arn.statusMessage.showError(`Your image has a resolution of ${img.naturalWidth} x ${img.naturalHeight} pixels which is too small. Recommended: 1920 x 450. Minimum: 960 x 225.`, 8000)
return
}
break
}
previewImage(dataURL, endpoint, previews)
2018-03-03 17:29:39 +00:00
}
2018-04-14 22:20:53 +00:00
uploadFile(file, fileType, endpoint, arn)
2018-03-02 16:18:29 +00:00
}
2018-04-14 22:20:53 +00:00
input.click()
2018-03-02 16:18:29 +00:00
}
2018-03-02 23:20:10 +00:00
// Upload file
2018-04-14 22:20:53 +00:00
function uploadFile(file: File, fileType: string, endpoint: string, arn: AnimeNotifier) {
2019-11-17 09:25:14 +00:00
const reader = new FileReader()
2018-03-02 20:42:34 +00:00
reader.onloadend = async () => {
2019-11-17 09:25:14 +00:00
const result = reader.result as ArrayBuffer
const fileSize = result.byteLength
2018-04-16 21:23:14 +00:00
2018-04-17 11:01:51 +00:00
if(fileSize === 0) {
arn.statusMessage.showError("File is empty")
return
2018-04-16 21:23:14 +00:00
}
2018-04-17 11:01:51 +00:00
arn.statusMessage.showInfo(`Preparing to upload ${fileType} (${bytesHumanReadable(fileSize)})`, -1)
2018-04-16 19:41:05 +00:00
try {
2019-11-17 09:25:14 +00:00
const responseText = await uploadWithProgress(endpoint, {
2018-04-16 19:41:05 +00:00
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/octet-stream"
},
body: reader.result
}, e => {
2019-11-17 09:25:14 +00:00
const progress = e.loaded / (e.lengthComputable ? e.total : fileSize) * 100
2018-04-16 19:41:05 +00:00
arn.statusMessage.showInfo(`Uploading ${fileType}...${progress.toFixed(1)}%`, -1)
})
2018-03-03 15:03:18 +00:00
2018-04-14 22:20:53 +00:00
arn.statusMessage.showInfo(`Successfully uploaded your new ${fileType}.`)
2018-04-16 19:41:05 +00:00
if(endpoint === "/api/upload/user/image") {
2018-04-16 19:41:05 +00:00
// We received the new avatar URL
updateSideBarAvatar(responseText)
}
} catch(err) {
2018-04-14 22:20:53 +00:00
arn.statusMessage.showError(`Failed uploading your new ${fileType}.`)
2018-04-16 19:41:05 +00:00
console.error(err)
2018-03-03 15:03:18 +00:00
}
2018-03-02 20:42:34 +00:00
}
2018-03-02 16:18:29 +00:00
2018-04-16 19:41:05 +00:00
arn.statusMessage.showInfo(`Reading ${fileType} from disk...`, -1)
2018-03-02 23:04:54 +00:00
reader.readAsArrayBuffer(file)
2018-03-03 17:29:39 +00:00
}
2018-10-31 09:57:28 +00:00
// Read image as data URL
function readImageAsDataURL(file: File): Promise<string> {
return new Promise((resolve, reject) => {
2019-11-17 09:25:14 +00:00
const reader = new FileReader()
2018-04-14 22:20:53 +00:00
2018-10-31 09:57:28 +00:00
reader.onloadend = () => {
2019-11-17 09:25:14 +00:00
const dataURL = reader.result as string
2018-10-31 09:57:28 +00:00
resolve(dataURL)
}
2018-09-27 04:54:02 +00:00
2018-10-31 09:57:28 +00:00
reader.onerror = event => {
reader.abort()
reject(event)
}
2018-04-14 22:20:53 +00:00
2018-10-31 09:57:28 +00:00
reader.readAsDataURL(file)
})
}
// Load image and resolve when loading has finished
function loadImage(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
2019-11-17 09:25:14 +00:00
const img = new Image()
2018-10-31 09:57:28 +00:00
img.src = url
img.onload = () => {
resolve(img)
2018-04-14 22:20:53 +00:00
}
2018-10-31 09:57:28 +00:00
img.onerror = error => {
reject(error)
}
})
}
2018-04-24 00:51:00 +00:00
2018-10-31 09:57:28 +00:00
// Preview image
function previewImage(dataURL: string, endpoint: string, previews: HTMLCollectionOf<Element>) {
if(endpoint === "/api/upload/user/image") {
2019-11-17 09:25:14 +00:00
const svgPreview = document.getElementById("avatar-input-preview-svg") as HTMLImageElement
2018-04-24 00:51:00 +00:00
2018-10-31 09:57:28 +00:00
if(svgPreview) {
svgPreview.classList.add("hidden")
}
2018-04-14 22:20:53 +00:00
}
2019-11-17 09:25:14 +00:00
for(const preview of previews) {
const img = preview as HTMLImageElement
2018-10-31 09:57:28 +00:00
img.classList.remove("hidden")
// Make not found images visible again
if(img.classList.contains("lazy")) {
img.classList.remove("element-not-found")
img.classList.add("element-found")
}
img.src = dataURL
}
2018-04-14 22:20:53 +00:00
}
2018-03-03 17:29:39 +00:00
// Update sidebar avatar
function updateSideBarAvatar(url: string) {
2019-11-17 09:25:14 +00:00
const sidebar = document.getElementById("sidebar") as HTMLElement
const userImage = sidebar.getElementsByClassName("user-image")[0] as HTMLImageElement
const lazyLoad = userImage["became visible"]
2018-03-03 17:29:39 +00:00
if(lazyLoad) {
userImage.dataset.src = url
lazyLoad()
} else {
location.reload()
}
2019-11-17 09:44:30 +00:00
}