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 ( ) ) {
2018-03-07 14:57:03 +00:00
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 ) {
2019-06-04 04:37:59 +00:00
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
2019-06-04 04:37:59 +00:00
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
2019-06-04 04:37:59 +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 > ) {
2019-06-04 04:37:59 +00:00
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-22 13:24:17 +00:00
}
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
}