Implemented descriptive upload progress

Say goodbye to upload progress bar 👋

Bumped v1 version string
This commit is contained in:
Bobby Wibowo 2019-11-26 21:58:10 +07:00
parent 603b6b4b83
commit a8c702065f
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
9 changed files with 88 additions and 38 deletions

View File

@ -31,7 +31,7 @@ Due to the fact that it needs to have `page` variable defined first, it can't ev
This may prevent proper async load of JS assets, which I'd like to look into, in pursuit of even more speed. This may prevent proper async load of JS assets, which I'd like to look into, in pursuit of even more speed.
* [ ] Remember last pages of uploads & users lists. * [ ] Remember last pages of uploads & users lists.
Consider remembering last pages of each individual albums as well. When deleting an album, properly delete its remembered last page, if any. When listing albums sidebar and/or listing albums in Manage your albums, also delete remembered last pages of any missing albums (assume the albums were deleted from another device). Consider remembering last pages of each individual albums as well. When deleting an album, properly delete its remembered last page, if any. When listing albums sidebar and/or listing albums in Manage your albums, also delete remembered last pages of any missing albums (assume the albums were deleted from another device).
* [ ] Descriptive upload progress, such as upload speed. Also tell user which chunk is currently being uploaded, to avoid confusion when progress "stops" when shifting to the next chunk. * [x] Descriptive upload progress, such as upload speed. Also tell user which chunk is currently being uploaded, to avoid confusion when progress "stops" when shifting to the next chunk.
Low priority: Low priority:

2
dist/css/home.css vendored
View File

@ -1,2 +1,2 @@
#b{width:200px;height:200px;border-radius:100%;display:inline-block;margin-bottom:40px;vertical-align:top;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:floatUp;animation-name:floatUp;-webkit-animation-timing-function:cubic-bezier(0,.71,.29,1);animation-timing-function:cubic-bezier(0,.71,.29,1)}#b img.logo{max-height:200px}#dropzone *{pointer-events:none}#panel,#tokenContainer{display:none}#maxSize{font-size:1rem}.dz-preview .dz-details{display:-webkit-box;display:flex}.dz-preview .dz-details .dz-filename,.dz-preview .dz-details .dz-size{-webkit-box-flex:1;flex:1}.dz-preview .dz-error-mark,.dz-preview .dz-success-mark,.dz-preview img{display:none}@-webkit-keyframes floatUp{0%{opacity:0;-webkit-transform:scale(.86);transform:scale(.86)}25%{opacity:1}67%{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes floatUp{0%{opacity:0;-webkit-transform:scale(.86);transform:scale(.86)}25%{opacity:1}67%{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(1);transform:scale(1)}}.uploads.is-reversed{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:reverse;flex-direction:column-reverse}.uploads>div{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s;margin:1rem}.uploads.is-reversed>div{-webkit-box-flex:0;flex:0 0 auto}.uploads>div:first-child{margin-top:1.5rem}.uploads.nojs{margin-bottom:0}.uploads>div>.icon:not(.icon-block){color:#209cee}.uploads>div>.icon.icon-block{color:#da4453}.uploads progress{margin-top:.5rem;margin-bottom:1rem}.uploads img{max-width:200px}.name{font-size:1rem;color:#eff0f1}.link>a,.name{word-break:break-all}.clipboard-mobile{margin-top:5px}#albumDiv{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#albumDiv .control{text-align:inherit}#linksColumn{margin-top:-.25rem;margin-left:-.25rem;margin-right:-.25rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#linksColumn .column{padding:.25rem}#linksColumn>span{padding:0 .3rem;color:#7f8c8d}.git-commit a{display:inline-block;word-break:break-all}#tabs{margin-bottom:1rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tabs ul{border-bottom:1px solid #585858}#tabs li a{color:#bdc3c7;border-bottom-color:#585858}#tabs.is-boxed li.is-active a{color:#209cee;background:#000;border-color:#585858 #585858 #000}#tabs.is-boxed li:not(.is-active) a:hover{background:#585858}.tab-content{margin-bottom:-.75rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tab-config.tab-content form{margin-bottom:.75rem}#urlMaxSize{font-weight:700}.render{position:fixed;right:0;bottom:0;font-size:1rem;color:#bdc3c7;cursor:pointer}.render.button{border-bottom-left-radius:0;border-bottom-right-radius:0;right:1%;opacity:.25;-webkit-transition:opacity .25s;transition:opacity .25s}.render.button:hover{opacity:1}input[type=file].is-fullwidth{width:100%} #b{width:200px;height:200px;border-radius:100%;display:inline-block;margin-bottom:40px;vertical-align:top;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:floatUp;animation-name:floatUp;-webkit-animation-timing-function:cubic-bezier(0,.71,.29,1);animation-timing-function:cubic-bezier(0,.71,.29,1)}#b img.logo{max-height:200px}#dropzone *{pointer-events:none}#panel,#tokenContainer{display:none}#maxSize{font-size:1rem}.dz-preview .dz-details{display:-webkit-box;display:flex}.dz-preview .dz-details .dz-filename,.dz-preview .dz-details .dz-size{-webkit-box-flex:1;flex:1}.dz-preview .dz-error-mark,.dz-preview .dz-success-mark,.dz-preview img{display:none}@-webkit-keyframes floatUp{0%{opacity:0;-webkit-transform:scale(.86);transform:scale(.86)}25%{opacity:1}67%{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes floatUp{0%{opacity:0;-webkit-transform:scale(.86);transform:scale(.86)}25%{opacity:1}67%{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(1);transform:scale(1)}}.uploads.is-reversed{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:reverse;flex-direction:column-reverse}.uploads>div{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s;margin:1rem}.uploads.is-reversed>div{-webkit-box-flex:0;flex:0 0 auto}.uploads>div:first-child{margin-top:1.5rem}.uploads.nojs{margin-bottom:0}.uploads>div>.icon:not(.icon-block){color:#209cee}.uploads>div>.icon.icon-block{color:#da4453}.uploads .descriptive-progress{color:#bdc3c7}.uploads img{max-width:200px}.name{font-size:1rem;color:#eff0f1}.link>a,.name{word-break:break-all}.clipboard-mobile{margin-top:5px}#albumDiv{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#albumDiv .control{text-align:inherit}#linksColumn{margin-top:-.25rem;margin-left:-.25rem;margin-right:-.25rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#linksColumn .column{padding:.25rem}#linksColumn>span{padding:0 .3rem;color:#7f8c8d}.git-commit a{display:inline-block;word-break:break-all}#tabs{margin-bottom:1rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tabs ul{border-bottom:1px solid #585858}#tabs li a{color:#bdc3c7;border-bottom-color:#585858}#tabs.is-boxed li.is-active a{color:#209cee;background:#000;border-color:#585858 #585858 #000}#tabs.is-boxed li:not(.is-active) a:hover{background:#585858}.tab-content{margin-bottom:-.75rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tab-config.tab-content form{margin-bottom:.75rem}#urlMaxSize{font-weight:700}.render{position:fixed;right:0;bottom:0;font-size:1rem;color:#bdc3c7;cursor:pointer}.render.button{border-bottom-left-radius:0;border-bottom-right-radius:0;right:1%;opacity:.25;-webkit-transition:opacity .25s;transition:opacity .25s}.render.button:hover{opacity:1}input[type=file].is-fullwidth{width:100%}
/*# sourceMappingURL=home.css.map */ /*# sourceMappingURL=home.css.map */

View File

@ -1 +1 @@
{"version":3,"sources":["css/home.css"],"names":[],"mappings":"AAAA,GACE,WAAY,CACZ,YAAa,CACb,kBAAmB,CACnB,oBAAqB,CACrB,kBAAmB,CACnB,kBAAmB,CACnB,2BAAqB,CAArB,mBAAqB,CACrB,+BAAwB,CAAxB,uBAAwB,CACxB,gCAAyB,CAAzB,wBAAyB,CACzB,8BAAuB,CAAvB,sBAAuB,CACvB,2DAAwD,CAAxD,mDACF,CAEA,YACE,gBACF,CAEA,YACE,mBACF,CAEA,uBAEE,YACF,CAEA,SACE,cACF,CAEA,wBACE,mBAAY,CAAZ,YACF,CAEA,sEAEE,kBAAM,CAAN,MACF,CAEA,wEAGE,YACF,CAEA,2BACE,GACE,SAAU,CACV,4BAAqB,CAArB,oBACF,CAEA,IACE,SACF,CAEA,IACE,0BAAkB,CAAlB,kBACF,CAEA,GACE,0BAAkB,CAAlB,kBACF,CACF,CAEA,mBACE,GACE,SAAU,CACV,4BAAqB,CAArB,oBACF,CAEA,IACE,SACF,CAEA,IACE,0BAAkB,CAAlB,kBACF,CAEA,GACE,0BAAkB,CAAlB,kBACF,CACF,CAEA,qBACE,mBAAa,CAAb,YAAa,CACb,2BAA6B,CAA7B,6BAA6B,CAA7B,6BACF,CAEA,aACE,mCAA6B,CAA7B,2BAA6B,CAC7B,WACF,CAEA,yBACE,kBAAa,CAAb,aACF,CAEA,yBACE,iBACF,CAEA,cACE,eACF,CAEA,oCACE,aACF,CAEA,8BACE,aACF,CAEA,kBACE,gBAAkB,CAClB,kBACF,CAEA,aACE,eACF,CAEA,MACE,cAAe,CACf,aAEF,CAEA,cAHE,oBAKF,CAEA,kBACE,cACF,CAEA,UACE,mCAA4B,CAA5B,2BACF,CAEA,mBACE,kBACF,CAEA,aACE,kBAAoB,CACpB,mBAAqB,CACrB,oBAAsB,CACtB,mCAA4B,CAA5B,2BACF,CAEA,qBACE,cACF,CAEA,kBACE,eAAiB,CACjB,aACF,CAEA,cACE,oBAAqB,CACrB,oBACF,CAEA,MACE,kBAAmB,CACnB,mCAA4B,CAA5B,2BACF,CAEA,SACE,+BACF,CAEA,WACE,aAAc,CACd,2BACF,CAEA,8BACE,aAAc,CACd,eAAgB,CAEhB,iCACF,CAEA,0CACE,kBACF,CAEA,aACE,qBAAuB,CACvB,mCAA4B,CAA5B,2BACF,CAEA,6BACE,oBACF,CAEA,YACE,eACF,CAEA,QACE,cAAe,CACf,OAAQ,CACR,QAAS,CACT,cAAe,CACf,aAAc,CACd,cACF,CAEA,eACE,2BAA4B,CAC5B,4BAA6B,CAC7B,QAAS,CACT,WAAa,CACb,+BAAwB,CAAxB,uBACF,CAEA,qBACE,SACF,CAEA,8BACE,UACF","file":"home.css","sourcesContent":["#b {\n width: 200px;\n height: 200px;\n border-radius: 100%;\n display: inline-block;\n margin-bottom: 40px;\n vertical-align: top;\n animation-delay: 0.5s;\n animation-duration: 1.5s;\n animation-fill-mode: both;\n animation-name: floatUp;\n animation-timing-function: cubic-bezier(0, 0.71, 0.29, 1)\n}\n\n#b img.logo {\n max-height: 200px\n}\n\n#dropzone * {\n pointer-events: none\n}\n\n#tokenContainer,\n#panel {\n display: none\n}\n\n#maxSize {\n font-size: 1rem\n}\n\n.dz-preview .dz-details {\n display: flex\n}\n\n.dz-preview .dz-details .dz-size,\n.dz-preview .dz-details .dz-filename {\n flex: 1\n}\n\n.dz-preview img,\n.dz-preview .dz-success-mark,\n.dz-preview .dz-error-mark {\n display: none\n}\n\n@-webkit-keyframes floatUp {\n 0% {\n opacity: 0;\n transform: scale(0.86)\n }\n\n 25% {\n opacity: 100\n }\n\n 67% {\n transform: scale(1)\n }\n\n 100% {\n transform: scale(1)\n }\n}\n\n@keyframes floatUp {\n 0% {\n opacity: 0;\n transform: scale(0.86)\n }\n\n 25% {\n opacity: 100\n }\n\n 67% {\n transform: scale(1)\n }\n\n 100% {\n transform: scale(1)\n }\n}\n\n.uploads.is-reversed {\n display: flex;\n flex-direction: column-reverse\n}\n\n.uploads > div {\n animation: fadeInOpacity 0.5s;\n margin: 1rem\n}\n\n.uploads.is-reversed > div {\n flex: 0 0 auto\n}\n\n.uploads > div:first-child {\n margin-top: 1.5rem\n}\n\n.uploads.nojs {\n margin-bottom: 0\n}\n\n.uploads > div > .icon:not(.icon-block) {\n color: #209cee\n}\n\n.uploads > div > .icon.icon-block {\n color: #da4453\n}\n\n.uploads progress {\n margin-top: 0.5rem;\n margin-bottom: 1rem\n}\n\n.uploads img {\n max-width: 200px\n}\n\n.name {\n font-size: 1rem;\n color: #eff0f1;\n word-break: break-all\n}\n\n.link > a {\n word-break: break-all\n}\n\n.clipboard-mobile {\n margin-top: 5px\n}\n\n#albumDiv {\n animation: fadeInOpacity 0.5s\n}\n\n#albumDiv .control {\n text-align: inherit\n}\n\n#linksColumn {\n margin-top: -0.25rem;\n margin-left: -0.25rem;\n margin-right: -0.25rem;\n animation: fadeInOpacity 0.5s\n}\n\n#linksColumn .column {\n padding: 0.25rem\n}\n\n#linksColumn > span {\n padding: 0 0.3rem;\n color: #7f8c8d\n}\n\n.git-commit a {\n display: inline-block;\n word-break: break-all\n}\n\n#tabs {\n margin-bottom: 1rem;\n animation: fadeInOpacity 0.5s\n}\n\n#tabs ul {\n border-bottom: 1px solid #585858\n}\n\n#tabs li a {\n color: #bdc3c7;\n border-bottom-color: #585858\n}\n\n#tabs.is-boxed li.is-active a {\n color: #209cee;\n background: #000;\n border-color: #585858;\n border-bottom-color: #000\n}\n\n#tabs.is-boxed li:not(.is-active) a:hover {\n background: #585858\n}\n\n.tab-content {\n margin-bottom: -0.75rem;\n animation: fadeInOpacity 0.5s\n}\n\n#tab-config.tab-content form {\n margin-bottom: 0.75rem\n}\n\n#urlMaxSize {\n font-weight: bold\n}\n\n.render {\n position: fixed;\n right: 0;\n bottom: 0;\n font-size: 1rem;\n color: #bdc3c7;\n cursor: pointer\n}\n\n.render.button {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n right: 1%;\n opacity: 0.25;\n transition: opacity 0.25s\n}\n\n.render.button:hover {\n opacity: 1\n}\n\ninput[type=\"file\"].is-fullwidth {\n width: 100%\n}\n"]} {"version":3,"sources":["css/home.css"],"names":[],"mappings":"AAAA,GACE,WAAY,CACZ,YAAa,CACb,kBAAmB,CACnB,oBAAqB,CACrB,kBAAmB,CACnB,kBAAmB,CACnB,2BAAqB,CAArB,mBAAqB,CACrB,+BAAwB,CAAxB,uBAAwB,CACxB,gCAAyB,CAAzB,wBAAyB,CACzB,8BAAuB,CAAvB,sBAAuB,CACvB,2DAAwD,CAAxD,mDACF,CAEA,YACE,gBACF,CAEA,YACE,mBACF,CAEA,uBAEE,YACF,CAEA,SACE,cACF,CAEA,wBACE,mBAAY,CAAZ,YACF,CAEA,sEAEE,kBAAM,CAAN,MACF,CAEA,wEAGE,YACF,CAEA,2BACE,GACE,SAAU,CACV,4BAAqB,CAArB,oBACF,CAEA,IACE,SACF,CAEA,IACE,0BAAkB,CAAlB,kBACF,CAEA,GACE,0BAAkB,CAAlB,kBACF,CACF,CAEA,mBACE,GACE,SAAU,CACV,4BAAqB,CAArB,oBACF,CAEA,IACE,SACF,CAEA,IACE,0BAAkB,CAAlB,kBACF,CAEA,GACE,0BAAkB,CAAlB,kBACF,CACF,CAEA,qBACE,mBAAa,CAAb,YAAa,CACb,2BAA6B,CAA7B,6BAA6B,CAA7B,6BACF,CAEA,aACE,mCAA6B,CAA7B,2BAA6B,CAC7B,WACF,CAEA,yBACE,kBAAa,CAAb,aACF,CAEA,yBACE,iBACF,CAEA,cACE,eACF,CAEA,oCACE,aACF,CAEA,8BACE,aACF,CAEA,+BACE,aACF,CAEA,aACE,eACF,CAEA,MACE,cAAe,CACf,aAEF,CAEA,cAHE,oBAKF,CAEA,kBACE,cACF,CAEA,UACE,mCAA4B,CAA5B,2BACF,CAEA,mBACE,kBACF,CAEA,aACE,kBAAoB,CACpB,mBAAqB,CACrB,oBAAsB,CACtB,mCAA4B,CAA5B,2BACF,CAEA,qBACE,cACF,CAEA,kBACE,eAAiB,CACjB,aACF,CAEA,cACE,oBAAqB,CACrB,oBACF,CAEA,MACE,kBAAmB,CACnB,mCAA4B,CAA5B,2BACF,CAEA,SACE,+BACF,CAEA,WACE,aAAc,CACd,2BACF,CAEA,8BACE,aAAc,CACd,eAAgB,CAEhB,iCACF,CAEA,0CACE,kBACF,CAEA,aACE,qBAAuB,CACvB,mCAA4B,CAA5B,2BACF,CAEA,6BACE,oBACF,CAEA,YACE,eACF,CAEA,QACE,cAAe,CACf,OAAQ,CACR,QAAS,CACT,cAAe,CACf,aAAc,CACd,cACF,CAEA,eACE,2BAA4B,CAC5B,4BAA6B,CAC7B,QAAS,CACT,WAAa,CACb,+BAAwB,CAAxB,uBACF,CAEA,qBACE,SACF,CAEA,8BACE,UACF","file":"home.css","sourcesContent":["#b {\n width: 200px;\n height: 200px;\n border-radius: 100%;\n display: inline-block;\n margin-bottom: 40px;\n vertical-align: top;\n animation-delay: 0.5s;\n animation-duration: 1.5s;\n animation-fill-mode: both;\n animation-name: floatUp;\n animation-timing-function: cubic-bezier(0, 0.71, 0.29, 1)\n}\n\n#b img.logo {\n max-height: 200px\n}\n\n#dropzone * {\n pointer-events: none\n}\n\n#tokenContainer,\n#panel {\n display: none\n}\n\n#maxSize {\n font-size: 1rem\n}\n\n.dz-preview .dz-details {\n display: flex\n}\n\n.dz-preview .dz-details .dz-size,\n.dz-preview .dz-details .dz-filename {\n flex: 1\n}\n\n.dz-preview img,\n.dz-preview .dz-success-mark,\n.dz-preview .dz-error-mark {\n display: none\n}\n\n@-webkit-keyframes floatUp {\n 0% {\n opacity: 0;\n transform: scale(0.86)\n }\n\n 25% {\n opacity: 100\n }\n\n 67% {\n transform: scale(1)\n }\n\n 100% {\n transform: scale(1)\n }\n}\n\n@keyframes floatUp {\n 0% {\n opacity: 0;\n transform: scale(0.86)\n }\n\n 25% {\n opacity: 100\n }\n\n 67% {\n transform: scale(1)\n }\n\n 100% {\n transform: scale(1)\n }\n}\n\n.uploads.is-reversed {\n display: flex;\n flex-direction: column-reverse\n}\n\n.uploads > div {\n animation: fadeInOpacity 0.5s;\n margin: 1rem\n}\n\n.uploads.is-reversed > div {\n flex: 0 0 auto\n}\n\n.uploads > div:first-child {\n margin-top: 1.5rem\n}\n\n.uploads.nojs {\n margin-bottom: 0\n}\n\n.uploads > div > .icon:not(.icon-block) {\n color: #209cee\n}\n\n.uploads > div > .icon.icon-block {\n color: #da4453\n}\n\n.uploads .descriptive-progress {\n color: #bdc3c7\n}\n\n.uploads img {\n max-width: 200px\n}\n\n.name {\n font-size: 1rem;\n color: #eff0f1;\n word-break: break-all\n}\n\n.link > a {\n word-break: break-all\n}\n\n.clipboard-mobile {\n margin-top: 5px\n}\n\n#albumDiv {\n animation: fadeInOpacity 0.5s\n}\n\n#albumDiv .control {\n text-align: inherit\n}\n\n#linksColumn {\n margin-top: -0.25rem;\n margin-left: -0.25rem;\n margin-right: -0.25rem;\n animation: fadeInOpacity 0.5s\n}\n\n#linksColumn .column {\n padding: 0.25rem\n}\n\n#linksColumn > span {\n padding: 0 0.3rem;\n color: #7f8c8d\n}\n\n.git-commit a {\n display: inline-block;\n word-break: break-all\n}\n\n#tabs {\n margin-bottom: 1rem;\n animation: fadeInOpacity 0.5s\n}\n\n#tabs ul {\n border-bottom: 1px solid #585858\n}\n\n#tabs li a {\n color: #bdc3c7;\n border-bottom-color: #585858\n}\n\n#tabs.is-boxed li.is-active a {\n color: #209cee;\n background: #000;\n border-color: #585858;\n border-bottom-color: #000\n}\n\n#tabs.is-boxed li:not(.is-active) a:hover {\n background: #585858\n}\n\n.tab-content {\n margin-bottom: -0.75rem;\n animation: fadeInOpacity 0.5s\n}\n\n#tab-config.tab-content form {\n margin-bottom: 0.75rem\n}\n\n#urlMaxSize {\n font-weight: bold\n}\n\n.render {\n position: fixed;\n right: 0;\n bottom: 0;\n font-size: 1rem;\n color: #bdc3c7;\n cursor: pointer\n}\n\n.render.button {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n right: 1%;\n opacity: 0.25;\n transition: opacity 0.25s\n}\n\n.render.button:hover {\n opacity: 1\n}\n\ninput[type=\"file\"].is-fullwidth {\n width: 100%\n}\n"]}

2
dist/js/home.js vendored

File diff suppressed because one or more lines are too long

2
dist/js/home.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -112,9 +112,8 @@
color: #da4453 color: #da4453
} }
.uploads progress { .uploads .descriptive-progress {
margin-top: 0.5rem; color: #bdc3c7
margin-bottom: 1rem
} }
.uploads img { .uploads img {

View File

@ -304,17 +304,21 @@ page.prepareDropzone = () => {
autoProcessQueue: true, autoProcessQueue: true,
headers: { token: page.token }, headers: { token: page.token },
chunking: Boolean(page.chunkSize), chunking: Boolean(page.chunkSize),
chunkSize: page.chunkSize * 1e6, // the option below expects Bytes chunkSize: page.chunkSize * 1e6, // this option expects Bytes
parallelChunkUploads: false, // when set to true, it often hangs with hundreds of parallel uploads parallelChunkUploads: false, // for now, enabling this breaks descriptive upload progress
timeout: 0, timeout: 0,
init () { init () {
this.on('addedfile', file => { this.on('addedfile', file => {
// Set active tab to file uploads, if necessary // Set active tab to file uploads, if necessary
if (page.activeTab !== 0) if (page.activeTab !== 0)
page.setActiveTab(0) page.setActiveTab(0)
// Add file entry // Add file entry
tabDiv.querySelector('.uploads').classList.remove('is-hidden') tabDiv.querySelector('.uploads').classList.remove('is-hidden')
file.previewElement.querySelector('.name').innerHTML = file.name file.previewElement.querySelector('.name').innerHTML = file.name
file.previewElement.querySelector('.descriptive-progress').innerHTML = 'Waiting in queue\u2026'
}) })
this.on('sending', (file, xhr) => { this.on('sending', (file, xhr) => {
@ -326,30 +330,55 @@ page.prepareDropzone = () => {
page.dropzone._handleUploadError(instances, xhr, 'Connection timed out. Try to reduce upload chunk size.') page.dropzone._handleUploadError(instances, xhr, 'Connection timed out. Try to reduce upload chunk size.')
} }
// Skip adding additional headers if chunked uploads // Add start timestamp of upload attempt
if (file.upload.chunked) return if (xhr._start === undefined)
xhr._start = Date.now()
// Continue otherwise // If not chunked uploads, add extra headers
if (!file.upload.chunked) {
if (page.album !== null) xhr.setRequestHeader('albumid', page.album) if (page.album !== null) xhr.setRequestHeader('albumid', page.album)
if (page.fileLength !== null) xhr.setRequestHeader('filelength', page.fileLength) if (page.fileLength !== null) xhr.setRequestHeader('filelength', page.fileLength)
if (page.uploadAge !== null) xhr.setRequestHeader('age', page.uploadAge) if (page.uploadAge !== null) xhr.setRequestHeader('age', page.uploadAge)
}
// If not chunked uploads OR first chunk
if (!file.upload.chunked || file.upload.chunks.length === 1)
file.previewElement.querySelector('.descriptive-progress').innerHTML = 'Uploading\u2026'
}) })
// Update the total progress bar // Update descriptive progress
this.on('uploadprogress', (file, progress) => { this.on('uploadprogress', (file, progress) => {
// For some reason, chunked uploads fire 100% progress event // Total bytes will eventually be bigger than file size when chunked
// for each chunk's successful uploads const total = Math.max(file.size, file.upload.total)
if (file.upload.chunked && progress === 100) return const percentage = (file.upload.bytesSent / total * 100).toFixed(0)
file.previewElement.querySelector('.progress').setAttribute('value', progress)
file.previewElement.querySelector('.progress').innerHTML = `${progress}%` const prefix = file.upload.chunked
? `Uploading chunk ${file.upload.chunks.length}/${file.upload.totalChunkCount}\u2026`
: 'Uploading\u2026'
const upl = file.upload.chunked
? file.upload.chunks[file.upload.chunks.length - 1]
: file.upload
const xhr = upl.xhr || file.xhr
const bytesSent = upl.bytesSent
const elapsed = (Date.now() - xhr._start) / 1000
const bytesPerSec = elapsed ? (bytesSent / elapsed) : 0
const prettyBytesPerSec = page.getPrettyBytes(bytesPerSec)
file.previewElement.querySelector('.descriptive-progress').innerHTML =
`${prefix} ${percentage}%${prettyBytesPerSec ? ` at ~${prettyBytesPerSec}/s` : ''}`
}) })
this.on('success', (file, response) => { this.on('success', (file, response) => {
if (!response) return if (!response) return
file.previewElement.querySelector('.progress').classList.add('is-hidden') file.previewElement.querySelector('.descriptive-progress').classList.add('is-hidden')
if (response.success === false) if (response.success === false) {
file.previewElement.querySelector('.error').innerHTML = response.description file.previewElement.querySelector('.error').innerHTML = response.description
file.previewElement.querySelector('.error').classList.remove('is-hidden')
}
if (response.files && response.files[0]) if (response.files && response.files[0])
page.updateTemplate(file, response.files[0]) page.updateTemplate(file, response.files[0])
@ -362,14 +391,18 @@ page.prepareDropzone = () => {
error = `File too large (${page.getPrettyBytes(file.size)}).` error = `File too large (${page.getPrettyBytes(file.size)}).`
page.updateTemplateIcon(file.previewElement, 'icon-block') page.updateTemplateIcon(file.previewElement, 'icon-block')
file.previewElement.querySelector('.progress').classList.add('is-hidden')
file.previewElement.querySelector('.descriptive-progress').classList.add('is-hidden')
file.previewElement.querySelector('.name').innerHTML = file.name file.previewElement.querySelector('.name').innerHTML = file.name
file.previewElement.querySelector('.error').innerHTML = error.description || error file.previewElement.querySelector('.error').innerHTML = error.description || error
file.previewElement.querySelector('.error').classList.remove('is-hidden')
}) })
}, },
chunksUploaded (file, done) { chunksUploaded (file, done) {
file.previewElement.querySelector('.progress').setAttribute('value', 100) file.previewElement.querySelector('.descriptive-progress').innerHTML =
file.previewElement.querySelector('.progress').innerHTML = '100%' `Rebuilding ${file.upload.totalChunkCount} chunks\u2026`
return axios.post('api/upload/finishchunks', { return axios.post('api/upload/finishchunks', {
// This API supports an array of multiple files // This API supports an array of multiple files
@ -381,7 +414,11 @@ page.prepareDropzone = () => {
filelength: page.fileLength, filelength: page.fileLength,
age: page.uploadAge age: page.uploadAge
}] }]
}, { headers: { token: page.token } }).catch(error => { }, {
headers: {
token: page.token
}
}).catch(error => {
// Format error for display purpose // Format error for display purpose
return error.response.data ? error.response : { return error.response.data ? error.response : {
data: { data: {
@ -390,10 +427,12 @@ page.prepareDropzone = () => {
} }
} }
}).then(response => { }).then(response => {
file.previewElement.querySelector('.progress').classList.add('is-hidden') file.previewElement.querySelector('.descriptive-progress').classList.add('is-hidden')
if (response.data.success === false) if (response.data.success === false) {
file.previewElement.querySelector('.error').innerHTML = response.data.description file.previewElement.querySelector('.error').innerHTML = response.data.description
file.previewElement.querySelector('.error').classList.remove('is-hidden')
}
if (response.data.files && response.data.files[0]) if (response.data.files && response.data.files[0])
page.updateTemplate(file, response.data.files[0]) page.updateTemplate(file, response.data.files[0])
@ -425,6 +464,7 @@ page.uploadUrls = button => {
} }
const previewsContainer = tabDiv.querySelector('.uploads') const previewsContainer = tabDiv.querySelector('.uploads')
const urls = document.querySelector('#urls').value const urls = document.querySelector('#urls').value
.split(/\r?\n/) .split(/\r?\n/)
.filter(url => { .filter(url => {
@ -436,11 +476,15 @@ page.uploadUrls = button => {
return done('You have not entered any URLs.') return done('You have not entered any URLs.')
tabDiv.querySelector('.uploads').classList.remove('is-hidden') tabDiv.querySelector('.uploads').classList.remove('is-hidden')
const files = urls.map(url => { const files = urls.map(url => {
const previewTemplate = document.createElement('template') const previewTemplate = document.createElement('template')
previewTemplate.innerHTML = page.previewTemplate.trim() previewTemplate.innerHTML = page.previewTemplate.trim()
const previewElement = previewTemplate.content.firstChild const previewElement = previewTemplate.content.firstChild
previewElement.querySelector('.name').innerHTML = url previewElement.querySelector('.name').innerHTML = url
previewElement.querySelector('.descriptive-progress').innerHTML = 'Waiting in queue\u2026'
previewsContainer.appendChild(previewElement) previewsContainer.appendChild(previewElement)
return { url, previewElement } return { url, previewElement }
}) })
@ -450,18 +494,21 @@ page.uploadUrls = button => {
return done() return done()
function posted (result) { function posted (result) {
files[i].previewElement.querySelector('.progress').classList.add('is-hidden') files[i].previewElement.querySelector('.descriptive-progress').classList.add('is-hidden')
if (result.success) { if (result.success) {
page.updateTemplate(files[i], result.files[0]) page.updateTemplate(files[i], result.files[0])
} else { } else {
page.updateTemplateIcon(files[i].previewElement, 'icon-block') page.updateTemplateIcon(files[i].previewElement, 'icon-block')
files[i].previewElement.querySelector('.error').innerHTML = result.description files[i].previewElement.querySelector('.error').innerHTML = result.description
files[i].previewElement.querySelector('.error').classList.remove('is-hidden')
} }
return post(i + 1) return post(i + 1)
} }
// Animate progress bar files[i].previewElement.querySelector('.descriptive-progress').innerHTML =
files[i].previewElement.querySelector('.progress').removeAttribute('value') 'Waiting for server to fetch URL\u2026'
return axios.post('api/upload', { urls: [files[i].url] }, { headers }).then(response => { return axios.post('api/upload', { urls: [files[i].url] }, { headers }).then(response => {
return posted(response.data) return posted(response.data)
@ -480,6 +527,7 @@ page.uploadUrls = button => {
page.updateTemplateIcon = (templateElement, iconClass) => { page.updateTemplateIcon = (templateElement, iconClass) => {
const iconElement = templateElement.querySelector('.icon') const iconElement = templateElement.querySelector('.icon')
if (!iconElement) return if (!iconElement) return
iconElement.classList.add(iconClass) iconElement.classList.add(iconClass)
iconElement.classList.remove('is-hidden') iconElement.classList.remove('is-hidden')
} }
@ -487,9 +535,12 @@ page.updateTemplateIcon = (templateElement, iconClass) => {
page.updateTemplate = (file, response) => { page.updateTemplate = (file, response) => {
if (!response.url) return if (!response.url) return
const a = file.previewElement.querySelector('.link > a') const link = file.previewElement.querySelector('.link')
const a = link.querySelector('a')
const clipboard = file.previewElement.querySelector('.clipboard-mobile > .clipboard-js') const clipboard = file.previewElement.querySelector('.clipboard-mobile > .clipboard-js')
a.href = a.innerHTML = clipboard.dataset.clipboardText = response.url a.href = a.innerHTML = clipboard.dataset.clipboardText = response.url
link.classList.remove('is-hidden')
clipboard.parentElement.classList.remove('is-hidden') clipboard.parentElement.classList.remove('is-hidden')
const exec = /.[\w]+(\?|$)/.exec(response.url) const exec = /.[\w]+(\?|$)/.exec(response.url)

View File

@ -1,5 +1,5 @@
{ {
"1": "1574772425", "1": "1574780211",
"2": "1568894058", "2": "1568894058",
"3": "1568894058", "3": "1568894058",
"4": "1568894058", "4": "1568894058",

View File

@ -140,9 +140,9 @@
<i class="icon is-hidden"></i> <i class="icon is-hidden"></i>
<img class="is-unselectable is-hidden"> <img class="is-unselectable is-hidden">
<p class="name"></p> <p class="name"></p>
<progress class="progress is-small is-danger" max="100" value="0"></progress> <p class="descriptive-progress"></p>
<p class="error"></p> <p class="error is-hidden"></p>
<p class="link"> <p class="link is-hidden">
<a target="_blank" rel="noopener"></a> <a target="_blank" rel="noopener"></a>
</p> </p>
<p class="help expiry-date is-hidden"></p> <p class="help expiry-date is-hidden"></p>