diff --git a/controllers/albumsController.js b/controllers/albumsController.js index f5461eb..2f56c8c 100644 --- a/controllers/albumsController.js +++ b/controllers/albumsController.js @@ -73,7 +73,7 @@ self.list = async (req, res, next) => { if (!user) return const all = req.headers.all === '1' - const sidebar = req.headers.sidebar + const simple = req.headers.simple const ismoderator = perms.is(user, 'moderator') if (all && !ismoderator) return res.status(403).end() @@ -97,10 +97,9 @@ self.list = async (req, res, next) => { const fields = ['id', 'name'] let albums - if (sidebar) { + if (simple) { albums = await db.table('albums') .where(filter) - .limit(9) .select(fields) return res.json({ success: true, albums, count }) diff --git a/public/libs/bulma-collapsible/LICENSE b/public/libs/bulma-collapsible/LICENSE new file mode 100644 index 0000000..917b28a --- /dev/null +++ b/public/libs/bulma-collapsible/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Wikiki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/public/libs/bulma-collapsible/bulma-collapsible.min.css b/public/libs/bulma-collapsible/bulma-collapsible.min.css new file mode 100644 index 0000000..e2861ec --- /dev/null +++ b/public/libs/bulma-collapsible/bulma-collapsible.min.css @@ -0,0 +1 @@ +@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.is-collapsible{height:0;overflow-y:hidden;transition:height .2s ease}.is-collapsible.is-active{transition:height .2s ease}.is-collapsible.message-body{padding:0!important}.is-collapsible.message-body .message-body-content{padding:1.25em 1.5em} \ No newline at end of file diff --git a/public/libs/bulma-collapsible/bulma-collapsible.min.js b/public/libs/bulma-collapsible/bulma-collapsible.min.js new file mode 100644 index 0000000..c529bab --- /dev/null +++ b/public/libs/bulma-collapsible/bulma-collapsible.min.js @@ -0,0 +1 @@ +!function webpackUniversalModuleDefinition(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.bulmaCollapsible=t():e.bulmaCollapsible=t()}(window,function(){return function(e){var t={};function __webpack_require__(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,__webpack_require__),r.l=!0,r.exports}return __webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.d=function(e,t,n){__webpack_require__.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.t=function(e,t){if(1&t&&(e=__webpack_require__(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(__webpack_require__.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)__webpack_require__.d(n,r,function(t){return e[t]}.bind(null,r));return n},__webpack_require__.n=function(e){var t=e&&e.__esModule?function getDefault(){return e.default}:function getModuleExports(){return e};return __webpack_require__.d(t,"a",t),t},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=12)}([function(e,t){function _getPrototypeOf(t){return e.exports=_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function _getPrototypeOf(e){return e.__proto__||Object.getPrototypeOf(e)},_getPrototypeOf(t)}e.exports=_getPrototypeOf},function(e,t){e.exports=function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){function _defineProperties(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];o()(this,EventEmitter),this._listeners=new Map(e),this._middlewares=new Map}return s()(EventEmitter,[{key:"listenerCount",value:function listenerCount(e){return this._listeners.has(e)?this._listeners.get(e).length:0}},{key:"removeListeners",value:function removeListeners(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];null!==t?Array.isArray(t)?name.forEach(function(t){return e.removeListeners(t,n)}):(this._listeners.delete(t),n&&this.removeMiddleware(t)):this._listeners=new Map}},{key:"middleware",value:function middleware(e,t){var n=this;Array.isArray(e)?name.forEach(function(e){return n.middleware(e,t)}):(Array.isArray(this._middlewares.get(e))||this._middlewares.set(e,[]),this._middlewares.get(e).push(t))}},{key:"removeMiddleware",value:function removeMiddleware(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;null!==t?Array.isArray(t)?name.forEach(function(t){return e.removeMiddleware(t)}):this._middlewares.delete(t):this._middlewares=new Map}},{key:"on",value:function on(e,t){var n=this,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(Array.isArray(e))e.forEach(function(e){return n.on(e,t)});else{var o=(e=e.toString()).split(/,|, | /);o.length>1?o.forEach(function(e){return n.on(e,t)}):(Array.isArray(this._listeners.get(e))||this._listeners.set(e,[]),this._listeners.get(e).push({once:r,callback:t}))}}},{key:"once",value:function once(e,t){this.on(e,t,!0)}},{key:"emit",value:function emit(e,t){var n=this,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e=e.toString();var o=this._listeners.get(e),i=null,s=0,l=r;if(Array.isArray(o))for(o.forEach(function(a,c){r||(i=n._middlewares.get(e),Array.isArray(i)?(i.forEach(function(n){n(t,function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;null!==e&&(t=e),s++},e)}),s>=i.length&&(l=!0)):l=!0),l&&(a.once&&(o[c]=null),a.callback(t))});-1!==o.indexOf(null);)o.splice(o.indexOf(null),1)}}]),EventEmitter}(),g=n(8),w=n.n(g),k=n(6),O=n.n(k),x=function isFunction(e){return"function"==typeof e},P=function isString(e){return"string"==typeof e||!!e&&"object"===O()(e)&&"[object String]"===Object.prototype.toString.call(e)},E=function isNode(e){try{return Node.prototype.cloneNode.call(e,!1),!0}catch(e){return!1}},S=function isNodeList(e){return NodeList.prototype.isPrototypeOf(e)},j=/^(?:f(?:alse)?|no?|0+)$/i,C=function uuid(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")+([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,function(e){return(e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)})},A=function detectSupportsPassive(){var e=!1;if("undefined"!=typeof window&&"function"==typeof window.addEventListener){var t=Object.defineProperty({},"passive",{get:function get(){return e=!0,!0}}),n=function noop(){};window.addEventListener("testPassive",n,t),window.removeEventListener("testPassive",n,t)}return e},q=function querySelectorAll(e,t){return x(e)?e(t||("undefined"!=typeof document?document:null)):P(e)?t&&E(t)?t.querySelectorAll(e):"undefined"!=typeof document?document.querySelectorAll(e):null:E(e)?[e]:S(e)?e:null},L=function optionsFromDataset(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return E(e)&&e.dataset?Object.keys(e.dataset).filter(function(e){return Object.keys(t).includes(e)}).reduce(function(t,n){return m()({},t,w()({},n,e.dataset[n]))},{}):{}};"undefined"==typeof Node||Node.prototype.on||(Node.prototype.on=function(e,t){var n=this;return Array.isArray(e)||(e=e.split(" ")),e.forEach(function(e){n.addEventListener(e.trim(),t,!!A()&&{passive:!1})}),this}),"undefined"==typeof NodeList||NodeList.prototype.on||(NodeList.prototype.on=function(e,t){return this.forEach(function(n){n.on(e,t)}),this}),"undefined"==typeof Node||Node.prototype.off||(Node.prototype.off=function(e,t){var n=this;return Array.isArray(e)||(e=e.split(" ")),e.forEach(function(e){n.removeEventListener(e.trim(),t,!!A()&&{passive:!1})}),this}),"undefined"==typeof NodeList||NodeList.prototype.off||(NodeList.prototype.off=function(e,t){return this.forEach(function(n){n.off(e,t)}),this});var N=function(e){function Component(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(o()(this,Component),(t=a()(this,p()(Component).call(this))).element=P(e)?t.options.container.querySelector(e):e,!t.element)throw new Error("An invalid selector or non-DOM node has been provided for ".concat(t.constructor.name,"."));return t.element[t.constructor.name]=t.constructor._interface.bind(u()(t)),t.element[t.constructor.name].Constructor=t.constructor.name,t.id=C(t.constructor.name+"-"),t.options=m()({},r,n,L(t.element,r)),t}return y()(Component,e),s()(Component,null,[{key:"attach",value:function attach(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=new Array;return null===t?o:(n=m()({},r,n,L(this.element,r)),(q(t,n.container)||[]).forEach(function(r){void 0===r[e.constructor.name]?o.push(new e(r,m()({selector:t},n))):o.push(r[e.constructor.name])}),o)}},{key:"_interface",value:function _interface(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if("string"==typeof e){if(void 0===this[e])throw new TypeError('No method named "'.concat(e,'"'));return this[e](e)}return this}}]),Component}(b),M={allowMultiple:!1,container:"undefined"!=typeof document?document:null};n.d(t,"default",function(){return T});var T=function(e){function bulmaCollapsible(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return o()(this,bulmaCollapsible),(t=a()(this,p()(bulmaCollapsible).call(this,e,n,M))).onTriggerClick=t.onTriggerClick.bind(u()(t)),t._init(),t}return y()(bulmaCollapsible,e),s()(bulmaCollapsible,[{key:"_init",value:function _init(){if(this._parent=this.element.dataset.parent,this._parent){var e=this.options.container.querySelector("#".concat(this._parent));this._siblings=q(this.options.selector,e)||[]}this._triggers=this.options.container.querySelectorAll('[data-action="collapse"][href="#'.concat(this.element.id,'"], [data-action="collapse"][data-target="').concat(this.element.id,'"]'))||null,this._triggers&&this._triggers.on("click touch",this.onTriggerClick),this.element.classList.contains("is-active")?this.expand():this.collapse()}},{key:"destroy",value:function destroy(){this._triggers&&this._triggers.off("click touch",this.onTriggerClick,!1)}},{key:"collapsed",value:function collapsed(){return this._collapsed}},{key:"expand",value:function expand(){var e=this;(void 0===this._collapsed||this._collapsed)&&(this.emit("before:expand",this),this._parent&&!function BooleanParse(e){return!j.test(e)&&!!e}(this.options.allowMultiple)&&this._siblings.forEach(function(t){t.isSameNode(e.element)||t.bulmaCollapsible&&t.bulmaCollapsible("close")}),this.element.style.height=this.element.scrollHeight+"px",this.element.classList.add("is-active"),this.element.setAttribute("aria-expanded",!0),this._triggers&&this._triggers.forEach(function(e){e.classList.add("is-active")}),this._collapsed=!1,this.emit("after:expand",this))}},{key:"open",value:function open(){this.expand()}},{key:"collapse",value:function collapse(){void 0!==this._collapsed&&this._collapsed||(this.emit("before:collapse",this),this.element.style.height=0,this.element.classList.remove("is-active"),this.element.setAttribute("aria-expanded",!1),this._triggers&&this._triggers.forEach(function(e){e.classList.remove("is-active")}),this._collapsed=!0,this.emit("after:collapse",this))}},{key:"close",value:function close(){this.collapse()}},{key:"onTriggerClick",value:function onTriggerClick(e){e.preventDefault(),this.collapsed()?(e.currentTarget.classList.add("is-active"),this.expand()):(e.currentTarget.classList.remove("is-active"),this.collapse())}}],[{key:"attach",value:function attach(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:".is-collapsible",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return _()(p()(bulmaCollapsible),"attach",this).call(this,e,t,M)}}]),bulmaCollapsible}(N)}]).default}); \ No newline at end of file diff --git a/src/css/dashboard.scss b/src/css/dashboard.scss index 586a2a6..e2ab6fa 100644 --- a/src/css/dashboard.scss +++ b/src/css/dashboard.scss @@ -20,7 +20,6 @@ body { a { color: $link; border: 1px solid transparent; - margin-top: -1px; &.is-active { color: $white; @@ -53,6 +52,16 @@ body { top: calc(50% - (1em / 2)); position: absolute !important } + + &.is-active[data-action="collapse"] { + color: $link !important; + background: none !important; + border-color: transparent !important; + + &:hover { + border-color: $link !important + } + } } li ul { diff --git a/src/js/dashboard.js b/src/js/dashboard.js index d3cc010..841343e 100644 --- a/src/js/dashboard.js +++ b/src/js/dashboard.js @@ -1,4 +1,4 @@ -/* global swal, axios, ClipboardJS, LazyLoad */ +/* global swal, axios, ClipboardJS, LazyLoad, bulmaCollapsible */ const lsKeys = { token: 'token', @@ -90,6 +90,8 @@ const page = { clipboardJS: null, lazyLoad: null, + albumsSidebarCollapse: null, + albumsSidebarCollapsible: null, imageExts: ['.gif', '.jpeg', '.jpg', '.png', '.svg', '.tif', '.tiff', '.webp'], videoExts: ['.3g2', '.3gp', '.asf', '.avchd', '.avi', '.divx', '.evo', '.flv', '.h264', '.h265', '.hevc', '.m2p', '.m2ts', '.m4v', '.mk3d', '.mkv', '.mov', '.mp4', '.mpeg', '.mpg', '.mxf', '.ogg', '.ogv', '.ps', '.qt', '.rmvb', '.ts', '.vob', '.webm', '.wmv'], @@ -1540,7 +1542,7 @@ page.addUploadsToAlbum = (ids, callback) => { }) // Get albums list then update content of swal - axios.get('api/albums').then(list => { + axios.get('api/albums', { headers: { simple: '1' } }).then(list => { if (list.data.success === false) { if (list.data.description === 'No token provided') { page.verifyToken(page.token) @@ -2040,7 +2042,7 @@ page.submitAlbum = element => { } page.getAlbumsSidebar = () => { - axios.get('api/albums', { headers: { sidebar: '1' } }).then(response => { + axios.get('api/albums', { headers: { simple: '1' } }).then(response => { if (!response) return if (response.data.success === false) { @@ -2053,18 +2055,27 @@ page.getAlbumsSidebar = () => { const albums = response.data.albums const count = response.data.count - const albumsContainer = document.querySelector('#albumsContainer') + const albumsSidebar = document.querySelector('#albumsSidebar') // Clear albums sidebar if necessary - const oldAlbums = albumsContainer.querySelectorAll('li > a') + const oldAlbums = albumsSidebar.querySelectorAll('li > a') + const diffCount = oldAlbums.length !== count if (oldAlbums.length) { for (let i = 0; i < oldAlbums.length; i++) { page.menus.splice(page.menus.indexOf(oldAlbums[i]), 1) } - albumsContainer.innerHTML = '' + albumsSidebar.innerHTML = '' } - if (albums === undefined) return + page.albumsSidebarCollapse.innerText = page.albumsSidebarCollapsible.collapsed() + ? page.albumsSidebarCollapse.dataset.textExpand + : page.albumsSidebarCollapse.dataset.textCollapse + + if (!albums || !albums.length) { + page.albumsSidebarCollapsible.collapse() + page.albumsSidebarCollapse.setAttribute('disabled', 'disabled') + return + } for (let i = 0; i < albums.length; i++) { const album = albums[i] @@ -2083,24 +2094,14 @@ page.getAlbumsSidebar = () => { page.menus.push(a) li.appendChild(a) - albumsContainer.appendChild(li) + albumsSidebar.appendChild(li) } - if (count > albums.length) { - const li = document.createElement('li') - const a = document.createElement('a') - a.className = 'is-relative' - a.innerHTML = '...' - a.title = `You have ${count} albums, but the sidebar can only list your first ${albums.length} albums.` - - a.addEventListener('click', event => { - page.getAlbums({ - trigger: document.querySelector('#itemManageYourAlbums') - }) - }) - - li.appendChild(a) - albumsContainer.appendChild(li) + page.albumsSidebarCollapse.removeAttribute('disabled') + if (!page.albumsSidebarCollapsible.collapsed() && diffCount) { + // Since it's not possible to refresh collapsible's height with bulmaCollapsible APIs, + // forcefully collapse albums sidebar if albums count is different with the previous iteration. + page.albumsSidebarCollapsible.collapse() } }).catch(page.onAxiosError) } @@ -3010,4 +3011,15 @@ window.addEventListener('DOMContentLoaded', () => { page.clipboardJS.on('error', page.onError) page.lazyLoad = new LazyLoad() + + page.albumsSidebarCollapse = document.querySelector('#albumsSidebarCollapse') + + /* eslint-disable-next-line new-cap */ + page.albumsSidebarCollapsible = new bulmaCollapsible(document.querySelector('#albumsSidebar')) + page.albumsSidebarCollapsible.on('before:expand', event => { + page.albumsSidebarCollapse.innerText = page.albumsSidebarCollapse.dataset.textCollapse + }) + page.albumsSidebarCollapsible.on('before:collapse', event => { + page.albumsSidebarCollapse.innerText = page.albumsSidebarCollapse.dataset.textExpand + }) }) diff --git a/src/js/home.js b/src/js/home.js index d87ab0e..fad0a5b 100644 --- a/src/js/home.js +++ b/src/js/home.js @@ -314,7 +314,12 @@ page.setActiveTab = index => { } page.fetchAlbums = () => { - return axios.get('api/albums', { headers: { token: page.token } }).then(response => { + return axios.get('api/albums', { + headers: { + simple: '1', + token: page.token + } + }).then(response => { if (response.data.success === false) { return swal('An error occurred!', response.data.description, 'error') } diff --git a/src/js/player.js b/src/js/player.js index b245d58..5a2cc4f 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -66,7 +66,7 @@ page.toggleReloadBtn = enabled => { page.reloadBtn.removeAttribute('disabled') } else { page.reloadBtn.classList.add('is-loading') - page.reloadBtn.setAttribute('disabled', true) + page.reloadBtn.setAttribute('disabled', 'disabled') } } diff --git a/views/dashboard.njk b/views/dashboard.njk index 89faac6..127b1d3 100644 --- a/views/dashboard.njk +++ b/views/dashboard.njk @@ -6,6 +6,7 @@ {% block stylesheets %} + {{ super() }} @@ -21,6 +22,7 @@ {# Polyfill smooth scroll for older browsers #} + {# We assign an ID for this so that the script can find out its own version #} @@ -64,7 +66,10 @@ Manage your albums
  • - + Fetching albums sidebar… +
  • +
  • +