From 39c0ac49fcb7bb5a8a37589568c3859f65e531ea Mon Sep 17 00:00:00 2001 From: Muah Date: Sat, 12 May 2018 07:09:01 +0200 Subject: [PATCH] v3.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Fixed - refetching content after cache clearing - bad html in card modal causing tippy error - bulk selection with "shift+click" doesn't work correctly when **sortBy** is used - a couple of gestures “recheck shortcuts” - better scroll to item in long list on manager init “hopefully its fixed for good now” - folders with locked items getting fake remove ## New - move modal have the selected items info as well “same as the delete modal” - removed “vue-scroll-stop” and replaced it with something simpler - the url will be changed according to the opened folder - so you can use the back/forward button like normal. - open a path directly with a url - using local storage to remember opened folders now works only for the modal - locked list moved to ls instead of saving it with each cached key --- README.md | 71 +- logs/v3.0.1.txt | 12 - logs/v3.0.2.txt | 18 + src/Controllers/MediaController.php | 82 ++- .../js/components/imageEditor/cropper.vue | 140 ++-- src/resources/assets/js/components/media.vue | 50 +- src/resources/assets/js/manager.js | 3 +- src/resources/assets/js/modules/cache.js | 75 +- src/resources/assets/js/modules/filtration.js | 10 +- src/resources/assets/js/modules/form.js | 139 ++-- src/resources/assets/js/modules/image.js | 4 +- src/resources/assets/js/modules/selected.js | 110 +-- src/resources/assets/js/modules/url.js | 35 + src/resources/assets/js/modules/utils.js | 87 +-- src/resources/assets/js/modules/watch.js | 3 + src/resources/assets/sass/media.scss | 40 +- src/resources/views/_manager.blade.php | 674 ++++++++---------- src/resources/views/cards/vertical.blade.php | 8 +- .../_breadcrumb.blade.php | 0 .../partials/_modal-files-info.blade.php | 74 ++ 20 files changed, 878 insertions(+), 757 deletions(-) delete mode 100644 logs/v3.0.1.txt create mode 100644 logs/v3.0.2.txt create mode 100644 src/resources/assets/js/modules/url.js rename src/resources/views/{extras => partials}/_breadcrumb.blade.php (100%) create mode 100644 src/resources/views/partials/_modal-files-info.blade.php diff --git a/README.md b/README.md index a79e26a..6f0b306 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ - [install dependencies](https://github.com/ctf0/Laravel-Media-Manager/wiki/Packages-In-Use) ```bash - yarn add vue vue-ls vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome@v2 vue-touch@next vue-scroll-stop idb-keyval axios dropzone cropperjs keycode date-fns babel-preset-es2015-node6 babel-preset-stage-2 + yarn add vue vue-ls vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome@v2 vue-touch@next idb-keyval axios dropzone cropperjs keycode date-fns # or - npm install vue vue-ls vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome@v2 vue-touch@next vue-scroll-stop idb-keyval axios dropzone cropperjs keycode date-fns babel-preset-es2015-node6 babel-preset-stage-2 --save + npm install vue vue-ls vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome@v2 vue-touch@next idb-keyval axios dropzone cropperjs keycode date-fns --save ``` - add this one liner to your main js file and run `npm run watch` to compile your `js/css` files. @@ -98,41 +98,38 @@ + rename + new folder - disable/enable buttons depend on the usage to avoid noise & keep the user focused -- shortcuts - - | navigation | button | keyboard | click / tap | touch | - |----------------|--------------------------------------------|---------------|--------------------------|-------------------------| - | | upload *(toolbar)* | u | * | | - | | refresh *(toolbar)* | r | * / hold *(clear cache)* | | - | | move *(toolbar)* | m | * | swipe up | - | | image editor *(toolbar)* | e | * | | - | | delete *(toolbar)* | d/del | * | swipe down | - | | lock/unlock *(toolbar)* | l | * | | - | | change visibility *(toolbar)* | v | * | | - | | (reset) bulk select *(toolbar)* | b | * | | - | | (reset) bulk select all *(toolbar)* | a | * | | - | | cancel bulk selection | esc | | | - | | cancel search *(toolbar)* | esc | * | | - | |   | | | | - | | toggle *(sidebar)* | t | * | | - | | play/pause media *(sidebar)* | space | * | | - | | preview image/pdf/text *(sidebar)* | space | * | | - | |   | | | | - | | confirm rename *(modal)* | enter | * | | - | | confirm delete *(modal)* | enter | * | | - | | confirm move *(modal)* | enter | * | | - | | create new folder *(modal)* | enter | * | | - | |   | | | | - | | limit bulk select *(files container)* | shift + click | | | - | | preview image/pdf/text *(files container)* | space | ** | | - | | image editor *(files container)* | | hold | | - | | hide *(preview)* | space/esc | * | | - | select next | | right / down | * | swipe left *(preview)* | - | select prev | | left / up | * | swipe right *(preview)* | - | select first | | home | | | - | select last | | end | | | - | open folder | | enter | ** | | - | go to prev dir | folderName *(breadcrumb)* | backspace | * | swipe right *(files container)* | +- shortcuts / gestures + + | navigation | button | keyboard | click / tap | touch | + |----------------|-------------------------------------|---------------|--------------------------|---------------------------------| + | | upload *(toolbar)* | u | * | | + | | refresh *(toolbar)* | r | * / hold *(clear cache)* | | + | | move *(toolbar)* | m | * | | + | | image editor *(toolbar)* | e | * | | + | | delete *(toolbar)* | d/del | * | | + | | lock/unlock *(toolbar)* | l | * | | + | | change visibility *(toolbar)* | v | * | | + | | (reset) bulk select *(toolbar)* | b / esc | * | | + | | (reset) bulk select all *(toolbar)* | a | * | | + | | reset search *(toolbar)* | esc *(input)* | * | | + | |   | | | | + | | toggle sidebar *(breadcrumb bar)* | t | * | | + | | confirm *(modal)* | enter | * | | + | | hide *(preview)* | space/esc | * | | + | |   | | | | + | | play/pause media *(item)* | space | ** | | + | | preview image/pdf/text *(item)* | space | ** | | + | | move *(item)* | | | swipe up | + | | delete *(item)* | | | swipe down | + | | image editor *(item)* | | | hold | + | | limit bulk select *(item)* | shift + click | | | + | |   | | | | + | select next | | right / down | * | swipe left *(preview)* | + | select prev | | left / up | * | swipe right *(preview)* | + | select first | | home | | | + | select last | | end | | | + | open folder | | enter | ** | | + | go to prev dir | folderName *(breadcrumb bar)* | backspace | * | swipe right *(items container)* | - events diff --git a/logs/v3.0.1.txt b/logs/v3.0.1.txt deleted file mode 100644 index 65ec3dc..0000000 --- a/logs/v3.0.1.txt +++ /dev/null @@ -1,12 +0,0 @@ -- separate lock&link icon click events from the file box -- fix btns being disabled after cache deletion and refetching -- fix dbl selection happens when going up a dir -- select first item when lazy loading is active & first item is a folder -- fix sidebar info not having correct bg color when empty -- allow zipping files with duplicate names -- standardize animation -- preserve search across navigation “a temp solution until global search is implemented” -- re-worked how the cache gets cleared through the different operations, so you get accurate info no matter how deep the current dir is -- cache is now also checked for invalidation when going up a dir -- fix issue of missing “$randId” when using the modal view -- cleanup \ No newline at end of file diff --git a/logs/v3.0.2.txt b/logs/v3.0.2.txt new file mode 100644 index 0000000..ffc69bd --- /dev/null +++ b/logs/v3.0.2.txt @@ -0,0 +1,18 @@ +## Fixed + +- refetching content after cache clearing +- bad html in card modal causing tippy error +- bulk selection with "shift+click" doesn't work correctly when **sortBy** is used +- a couple of gestures “recheck shortcuts” +- better scroll to item in long list on manager init “hopefully its fixed for good now” +- folders with locked items getting fake remove + +## New + +- move modal have the selected items info as well “same as the delete modal” +- removed “vue-scroll-stop” and replaced it with something simpler +- the url will be changed according to the opened folder + - so you can use the back/forward button like normal. + - open a path directly with a url + - using local storage to remember opened folders now works only for the modal +- locked list moved to ls instead of saving it with each cached key \ No newline at end of file diff --git a/src/Controllers/MediaController.php b/src/Controllers/MediaController.php index 14dee6c..00281ab 100755 --- a/src/Controllers/MediaController.php +++ b/src/Controllers/MediaController.php @@ -441,36 +441,44 @@ public function delete_file(Request $request) { $folderLocation = $request->folder_location; $result = []; - $fullCacheClear = false; + $lockedList = $this->db->pluck('path')->toArray(); foreach ($request->deleted_files as $one) { - $file_name = $one['name']; - $type = $one['type']; - $result[] = [ - 'success' => true, - 'name' => $file_name, - 'type' => $type, - ]; - - $file_name = "$folderLocation/$file_name"; + $file_name = $one['name']; + $file_type = $one['type']; + $file_name = $folderLocation == '' ? $file_name : "$folderLocation/$file_name"; // folder - if ($type == 'folder') { + if ($file_type == 'folder') { // check for files in lock list - foreach ($this->storageDisk->allFiles($file_name) as $file) { - if (in_array($this->resolveUrl($file), $this->db->pluck('path')->toArray())) { - $fullCacheClear = true; - $result[] = [ + foreach ($this->getFolderListByType($this->getFolderContent($file_name, true), 'file') as $file) { + $item_name = $file['basename']; + $item_type = $file['mimetype']; + $item_path = $file['path']; + + if (in_array($this->resolveUrl($item_path), $lockedList)) { + $result[] = [ 'success' => false, - 'message' => trans('MediaManager::messages.error_in_locked_list', ['attr' => pathinfo($file, PATHINFO_BASENAME)]), + 'name' => $item_name, + 'type' => $item_type, + 'message' => trans('MediaManager::messages.error_in_locked_list', ['attr' => $item_name]), ]; } // remove file else { - if (!$this->storageDisk->delete($file)) { + if ($this->storageDisk->delete($item_path)) { + $result[] = [ + 'success' => true, + 'name' => $item_name, + 'type' => $item_type, + 'parent_path' => dirname($item_path), + ]; + } else { $result[] = [ 'success' => false, + 'name' => $item_name, + 'type' => $item_type, 'message' => trans('MediaManager::messages.error_deleting_file'), ]; } @@ -485,17 +493,25 @@ public function delete_file(Request $request) // remove folder if its size is == 0 // even if it have locked folders without items if ($this->getFolderInfo($file_name)['files_size'] == 0) { - if (!$this->storageDisk->deleteDirectory($file_name)) { - $result[] = [ - 'success' => false, - 'message' => trans('MediaManager::messages.error_deleting_file'), + if ($this->storageDisk->deleteDirectory($file_name)) { + $result[] = [ + 'success' => true, + 'name' => $file_name, + 'type' => $file_type, ]; - } else { + // fire event event('MMFileDeleted', [ 'file_path' => $this->getFilePath($file_name), 'is_folder' => true, ]); + } else { + $result[] = [ + 'success' => false, + 'name' => $file_name, + 'type' => $file_type, + 'message' => trans('MediaManager::messages.error_deleting_file'), + ]; } } @@ -503,6 +519,8 @@ public function delete_file(Request $request) else { $result[] = [ 'success' => false, + 'name' => $file_name, + 'type' => $file_type, 'message' => trans('MediaManager::messages.error_delete_fwli', ['attr'=>$file_name]), ]; } @@ -510,22 +528,30 @@ public function delete_file(Request $request) // file else { - if (!$this->storageDisk->delete($file_name)) { - $result[] = [ - 'success' => false, - 'message' => trans('MediaManager::messages.error_deleting_file'), + if ($this->storageDisk->delete($file_name)) { + $result[] = [ + 'success' => true, + 'name' => $file_name, + 'type' => $file_type, ]; - } else { + // fire event event('MMFileDeleted', [ 'file_path' => $this->getFilePath($file_name), 'is_folder' => false, ]); + } else { + $result[] = [ + 'success' => false, + 'name' => $file_name, + 'type' => $file_type, + 'message' => trans('MediaManager::messages.error_deleting_file'), + ]; } } } - return response()->json(['res' => $result, 'fullCacheClear' => $fullCacheClear]); + return response()->json(['res' => $result]); } /** diff --git a/src/resources/assets/js/components/imageEditor/cropper.vue b/src/resources/assets/js/components/imageEditor/cropper.vue index 0a7cd9b..e0aa140 100644 --- a/src/resources/assets/js/components/imageEditor/cropper.vue +++ b/src/resources/assets/js/components/imageEditor/cropper.vue @@ -3,101 +3,101 @@
-
+
+ :reset="reset" :processing="processing" + icon="sun-o" filter-name="brightness"/> + :reset="reset" :processing="processing" + icon="adjust" filter-name="contrast"/> + :reset="reset" :processing="processing" + icon="eye-slash" filter-name="saturation"/> + :reset="reset" :processing="processing" + icon="flash" filter-name="vibrance"/> + :reset="reset" :processing="processing" + icon="thermometer-half" filter-name="exposure"/> + :reset="reset" :processing="processing" + icon="eyedropper" filter-name="hue"/> + :reset="reset" :processing="processing" + icon="lemon-o" filter-name="sepia"/> + :reset="reset" :processing="processing" + icon="flask" filter-name="gamma"/> + :reset="reset" :processing="processing" + icon="dot-circle-o" filter-name="noise"/> + :reset="reset" :processing="processing" + icon="scissors" filter-name="clip"/> + :reset="reset" :processing="processing" + icon="diamond" filter-name="sharpen"/> - - + :reset="reset" :processing="processing" + icon="filter" filter-name="stackBlur"/> + +
-
- - - - - - - -
@@ -105,32 +105,32 @@
- +
-
+
- - -
@@ -203,6 +203,10 @@ export default { } }, + mounted() { + this.camanStart() + }, + // make cropperjs rotation follow the new value computed: { rotation() { @@ -224,10 +228,6 @@ export default { return final } }, - - mounted() { - this.camanStart() - }, methods: { camanStart() { this.imageCaman = Caman('#cropper', () => { @@ -420,4 +420,4 @@ export default { } } } - + \ No newline at end of file diff --git a/src/resources/assets/js/components/media.vue b/src/resources/assets/js/components/media.vue index 3b4d4bb..9eb13e6 100644 --- a/src/resources/assets/js/components/media.vue +++ b/src/resources/assets/js/components/media.vue @@ -11,6 +11,7 @@ import Restriction from '../modules/restriction' import Watchers from '../modules/watch' import Computed from '../modules/computed' import Image from '../modules/image' +import Url from '../modules/url' import Bounty from 'vue-bounty' import Cropper from './imageEditor/cropper.vue' @@ -30,7 +31,8 @@ export default { Restriction, Computed, Watchers, - Image + Image, + Url ], props: [ 'config', @@ -98,16 +100,12 @@ export default { } }, created() { + window.addEventListener('popstate', this.urlNavigation) document.addEventListener('keydown', this.shortCuts) - this.invalidateCache().then(() => { - this.preSaved() - this.getFiles(this.folders, null, this.selectedFile) - }) + this.init() }, mounted() { - this.fileUpload() - // check if image was edited EventHub.listen('image-edited', () => { this.imageWasEdited = true @@ -122,9 +120,19 @@ export default { }) }, beforeDestroy() { + window.removeEventListener('popstate', this.urlNavigation) document.removeEventListener('keydown', this.shortCuts) }, methods: { + init() { + this.getPathFromUrl().then(() => { + return this.preSaved() + }).then(() => { + return this.getFiles(this.folders, null, this.selectedFile).then(() => { + this.fileUpload() + }) + }) + }, shortCuts(e) { let key = keycode(e) @@ -162,11 +170,7 @@ export default { // play-pause media if (this.selectedFileIs('video') || this.selectedFileIs('audio')) { - let player = this.$refs.player - - return player.paused - ? player.play() - : player.pause() + this.playMedia() } // "show" image/pdf/text quick view @@ -320,18 +324,16 @@ export default { autoPlay() { if (this.filterNameIs('audio') || this.filterNameIs('video')) { let player = this.$refs.player - if (player) { - player.onended = () => { - // stop at the end of list - if (this.currentFileIndex < this.allItemsCount - 1) { - // nav to next - this.goToNext() - - // play navigated to - this.$nextTick(() => { - this.$refs.player.play() - }) - } + player.onended = () => { + // stop at the end of list + if (this.currentFileIndex < this.allItemsCount - 1) { + // nav to next + this.goToNext() + + // play navigated to + this.$nextTick(() => { + this.$refs.player.play() + }) } } } diff --git a/src/resources/assets/js/manager.js b/src/resources/assets/js/manager.js index 7366e58..437abc7 100644 --- a/src/resources/assets/js/manager.js +++ b/src/resources/assets/js/manager.js @@ -7,7 +7,6 @@ window.Dropzone = require('dropzone') Vue.use(require('vue2-filters')) Vue.use(require('vue-clipboard2')) Vue.use(require('vue-ls')) -Vue.use(require('vue-scroll-stop')) // vue-tippy Vue.use(require('vue-tippy'), { @@ -50,4 +49,4 @@ Vue.component('MediaManager', require('./components/media.vue')) Vue.component('MyNotification', require('vue-notif')) /* Events */ -require('./modules/events') +require('./modules/events') \ No newline at end of file diff --git a/src/resources/assets/js/modules/cache.js b/src/resources/assets/js/modules/cache.js index 39e5f1f..6373644 100644 --- a/src/resources/assets/js/modules/cache.js +++ b/src/resources/assets/js/modules/cache.js @@ -28,6 +28,7 @@ export default { if (ls) { this.randomNames = ls.randomNames || false this.folders = ls.folders || [] + this.lockedList = ls.lockedList || [] this.toolBar = ls.toolBar || true this.selectedFile = ls.selectedFileName || null } @@ -37,17 +38,18 @@ export default { getCachedResponse(key = this.cacheName) { return get(key, cacheStore) }, - cacheResponse(val) { + cacheResponse(val, key = this.cacheName) { let date = getTime(addMinutes(new Date(), this.config.cacheExp)) val = Object.assign(val, {expire: date}) - return set(this.cacheName, val, cacheStore).catch((err) => { + return set(key, val, cacheStore).catch((err) => { console.warn('cacheStore.setItem', err) }) }, - removeCachedResponse(destination = null) { + removeCachedResponse(destination = null, extra = []) { let cacheName = this.cacheName - let items = [] + let items = ['root_'] + let promises = [] // current path only if (!destination) { @@ -55,27 +57,22 @@ export default { } // up & down - destination == '../' - ? false - : cacheName = cacheName == 'root_' - ? `/${destination}` - : `${cacheName}${destination}` + destination == '../' ? false : cacheName = this.getCacheName(destination) - let arr = cacheName.split('/').filter((e) => e) - let i = arr.length - 1 - for (i; i >= 0; i--) { - items.push(cacheName) // add current - arr.pop() // remove last - cacheName = `/${arr.join('/')}` // regroup remaining - } - - if (!items.includes('root_')) { - items.push('root_') + // clear nested folders cache too + items.push(...this.getRecursivePathParent(cacheName)) + if (extra.length) { + extra.forEach((e) => { + items.push(...this.getRecursivePathParent(e)) + }) } - items.forEach((one) => { - return this.deleteCache(one) + // clear dups and delete items + Array.from(new Set(items)).forEach((one) => { + promises.push(this.deleteCache(one)) }) + + return Promise.all(promises) }, deleteCache(item) { return del(item, cacheStore).then(() => { @@ -101,16 +98,40 @@ export default { let now = getTime(new Date()) return keys(cacheStore).then((keys) => { - keys.map((key) => { - this.getCachedResponse(key).then((item) => { - if (item.expire < now) { - this.deleteCache(key) - } + return Promise.all( + keys.map((key) => { + return this.getCachedResponse(key).then((item) => { + if (item.expire <= now) { + return this.deleteCache(key) + } + }) }) - }) + ) }).catch((err) => { console.error(err) }) + }, + + /* helpers */ + getCacheName(path) { + let str = this.cacheName == 'root_' + ? `/${path}` + : `${this.cacheName}/${path}` + + return str.replace('//', '/') + }, + getRecursivePathParent(path) { + let list = [] + let arr = path.split('/').filter((e) => e) + let i = arr.length - 1 + + for (i; i >= 0; i--) { + list.push(path) // add current + arr.pop() // remove last + path = `/${arr.join('/')}` // regroup remaining + } + + return list } } } \ No newline at end of file diff --git a/src/resources/assets/js/modules/filtration.js b/src/resources/assets/js/modules/filtration.js index 0abdac9..90b940f 100644 --- a/src/resources/assets/js/modules/filtration.js +++ b/src/resources/assets/js/modules/filtration.js @@ -19,11 +19,13 @@ export default { return this.currentFilterName == val }, fileTypeIs(item, val) { - if (val == 'image' && this.config.imageTypes.includes(item.type)) { - return true - } + if (item.type) { + if (val == 'image' && this.config.imageTypes.includes(item.type)) { + return true + } - return item.type.includes(val) + return item.type.includes(val) + } }, showFilesOfType(val) { if (this.currentFilterName == val) { diff --git a/src/resources/assets/js/modules/form.js b/src/resources/assets/js/modules/form.js index 81498c4..b0bb36a 100644 --- a/src/resources/assets/js/modules/form.js +++ b/src/resources/assets/js/modules/form.js @@ -47,11 +47,11 @@ export default { manager.showProgress = false manager.$refs['success-audio'].play() - manager.removeCachedResponse('../') - - last - ? manager.getFiles(manager.folders, null, last) - : manager.getFiles(manager.folders) + manager.removeCachedResponse('../').then(() => { + last + ? manager.getFiles(manager.folders, null, last) + : manager.getFiles(manager.folders) + }) } }, errormultiple(files, res) { @@ -90,8 +90,9 @@ export default { }) this.showNotif(`${this.trans('save_success')} "${data.message}"`) - this.removeCachedResponse('../') - this.getFiles(this.folders, null, data.message) + this.removeCachedResponse('../').then(() => { + this.getFiles(this.folders, null, data.message) + }) }).catch((err) => { console.error(err) @@ -106,9 +107,7 @@ export default { /* Main */ getFiles(folders = '/', prev_folder = null, prev_file = null) { - this.resetInput(['sortBy', 'currentFilterName', 'selectedFile', 'currentFileIndex']) - this.noFiles('hide') if (!this.loading_files) { this.toggleInfo = false @@ -120,43 +119,45 @@ export default { folders = '/' + folders.join('/') } - this.getCachedResponse() - // get cache - .then((res) => { - this.files = res.files - this.lockedList = res.lockedList - this.filesListCheck(prev_folder, prev_file, folders, res.dirs) - }) + // clear expired cache + return this.invalidateCache().then(() => { + // get data + return this.getCachedResponse() + .then((res) => { + // return cache + if (res) { + this.files = res.files + return this.filesListCheck(prev_folder, prev_file, folders, res.dirs) + } - // or make the call if nothing found - .catch((err) => { - axios.post(this.filesRoute, { - folder: folders, - dirs: this.folders - }).then(({data}) => { + // or make new call + return axios.post(this.filesRoute, { + folder: folders, + dirs: this.folders + }).then(({data}) => { - // folder doesnt exist - if (data.error) { - return this.showNotif(data.error, 'danger') - } + // folder doesnt exist + if (data.error) { + return this.showNotif(data.error, 'danger') + } - // normal - this.files = data.files - this.lockedList = data.locked - this.filesListCheck(prev_folder, prev_file, folders, data.dirs) + // normal + this.files = data.files + this.lockedList = data.locked + this.filesListCheck(prev_folder, prev_file, folders, data.dirs) - // cache response - this.cacheResponse({ - files: data.files, - lockedList: data.locked, - dirs: data.dirs - }) + // cache response + this.cacheResponse({ + files: data.files, + dirs: data.dirs + }) - }).catch((err) => { - console.error(err) - this.ajaxError() + }).catch((err) => { + console.error(err) + this.ajaxError() + }) }) - }) + }) }, updateDirsList() { axios.post(this.dirsRoute, { @@ -225,16 +226,7 @@ export default { this.$nextTick(() => { let curIndex = this.currentFileIndex if (curIndex) { - if (document.readyState === 'complete') { - this.scrollToFile(this.$refs[`file_${curIndex}`]) - } else { - let t = setInterval(() => { - if (document.readyState === 'complete') { - this.scrollToFile(this.$refs[`file_${curIndex}`]) - clearInterval(t) - } - }, 500) - } + this.scrollToFile(this.getElementByIndex(curIndex)) } }) @@ -306,8 +298,9 @@ export default { } this.showNotif(`${this.trans('create_success')} "${data.new_folder_name}" at "${path}"`) - this.removeCachedResponse('../') - this.getFiles(this.folders, data.new_folder_name) + this.removeCachedResponse('../').then(() => { + this.getFiles(this.folders, data.new_folder_name) + }) }).catch((err) => { console.error(err) @@ -318,6 +311,9 @@ export default { // rename RenameFileForm(event) { let changed = this.newFilename + let filename = this.selectedFile.name + let ext = this.getExtension(filename) + let newFilename = ext == null ? changed : `${changed}.${ext}` if (!changed) { return this.showNotif(this.trans('no_val'), 'warning') @@ -329,10 +325,6 @@ export default { this.toggleLoading() - let filename = this.selectedFile.name - let ext = this.getExtension(filename) - let newFilename = ext == null ? changed : `${changed}.${ext}` - axios.post(event.target.action, { folder_location: this.files.path, filename: filename, @@ -350,7 +342,7 @@ export default { if (this.selectedFileIs('folder')) { this.updateDirsList() - this.removeCachedResponse('../') + this.removeCachedResponse('../', [this.getCacheName(filename)]) } else { this.removeCachedResponse() } @@ -433,6 +425,8 @@ export default { // delete DeleteFileForm(event) { + let clearCache = false + let foldersList = [] let files = this.bulkItemsCount ? this.bulkListFilter : [this.selectedFile] @@ -451,26 +445,27 @@ export default { return this.showNotif(item.message, 'danger') } - if (!data.fullCacheClear) { - this.showNotif(`${this.trans('delete_success')} "${item.name}"`, 'warning') - this.removeFromLists(item.name, item.type) + // clear nested folders cache + if (item.type == 'folder' || item.parent_path) { + foldersList.push(this.getCacheName(item.parent_path || item.name)) } + + clearCache = true + this.showNotif(`${this.trans('delete_success')} "${item.name}"`, 'warning') + this.removeFromLists(item.name, item.type) }) - this.$refs['success-audio'].play() - this.isBulkSelecting() - ? this.blkSlct() - : !this.config.lazyLoad - ? this.selectFirst() - : this.lazySelectFirst() + if (clearCache) { + this.$refs['success-audio'].play() + this.removeCachedResponse('../', foldersList) + this.isBulkSelecting() + ? this.blkSlct() + : !this.config.lazyLoad + ? this.selectFirst() + : this.lazySelectFirst() + } this.$nextTick(() => { - if (data.fullCacheClear) { - this.clearCache(false) - } else { - this.removeCachedResponse('../') - } - if (this.searchFor) { this.searchItemsCount = this.filesList.length } diff --git a/src/resources/assets/js/modules/image.js b/src/resources/assets/js/modules/image.js index 7ba25f5..4b92a25 100644 --- a/src/resources/assets/js/modules/image.js +++ b/src/resources/assets/js/modules/image.js @@ -16,9 +16,9 @@ export default { }, // lazy - lazyImageActivate(item) { + lazyImageActivate(index) { this.$nextTick(() => { - let img = this.$refs[item] + let img = this.$refs[`img_${index}`] if (img && img.length && img[0].dataset.src) { img = img[0] diff --git a/src/resources/assets/js/modules/selected.js b/src/resources/assets/js/modules/selected.js index 2765649..e24bfc3 100644 --- a/src/resources/assets/js/modules/selected.js +++ b/src/resources/assets/js/modules/selected.js @@ -1,19 +1,16 @@ export default { methods: { - /* Selected */ + /* Item */ selectFirst() { this.$nextTick(() => { - let file = this.$refs.file_0[0] - - if (file) { - file.$el.click() - } + this.scrollToFile(this.getElementByIndex(0)) }) }, setSelected(file, index, e = null) { // select with shift if (e && e.shiftKey) { this.bulkSelect = true + this.bulkList = [] // forward let begin = this.currentFileIndex @@ -27,44 +24,34 @@ export default { dir = 'backward' } - // search - if (this.searchFor) { - this.bulkList = [] - let indexList = this.getRange(begin, end) + let indexList = this.getIndexRange(begin, end) - indexList.map((i) => { - this.$refs[`file_${i}`][0].$el.click() - }) - - // to have the same expected pattern as normal shift + click - if (dir == 'forward') { - this.selectedFile = this.bulkList[0] - this.currentFileIndex = indexList[0] - } else { - this.selectedFile = this.bulkList[this.bulkItemsCount - 1] - this.currentFileIndex = indexList[indexList.length - 1] - } + indexList.map((i) => { + this.getElementByIndex(i).click() + }) - return + // to have the same expected pattern as normal shift + click + if (dir == 'forward') { + this.selectedFile = this.bulkList[0] + this.currentFileIndex = indexList[0] + } else { + this.selectedFile = this.bulkList[this.bulkItemsCount - 1] + this.currentFileIndex = indexList[indexList.length - 1] } - // default - return this.bulkList = this.allFiles.slice(begin, end + 1) + return } // normal selection this.selectedFile = file this.currentFileIndex = index - this.lazyImageActivate(file.path) + this.lazyImageActivate(index) // bulk selection if (this.isBulkSelecting()) { this.pushtoBulkList(file) } }, - getRange(start, end) { - return Array(end - start + 1).fill().map((_, idx) => start + idx) - }, selectedFileIs(val) { if (this.selectedFile !== null) { return this.fileTypeIs(this.selectedFile, val) @@ -72,9 +59,24 @@ export default { }, dbltap() { if (!this.isBulkSelecting()) { - return this.selectedFileIs('image') || this.selectedFileIs('pdf') || this.selectedFileIs('text') - ? this.toggleModal('preview_modal') - : this.openFolder(this.selectedFile) + if (this.selectedFileIs('image') || this.selectedFileIs('pdf') || this.selectedFileIs('text')) { + return this.toggleModal('preview_modal') + } + + if (this.selectedFileIs('audio') || this.selectedFileIs('video')) { + return this.playMedia() + } + + this.openFolder(this.selectedFile) + } + }, + playMedia() { + let player = this.$refs.player + + if (player) { + return player.paused + ? player.play() + : player.pause() } }, @@ -82,10 +84,8 @@ export default { openFolder(file) { if (!this.isBulkSelecting() && this.fileTypeIs(file, 'folder')) { this.folders.push(file.name) - - this.invalidateCache().then(() => { - this.getFiles(this.folders) - }) + this.getFiles(this.folders) + this.updatePageUrl() } this.resetInput('currentFilterName') @@ -98,9 +98,8 @@ export default { let prev_folder_name = this.folders[index] this.folders = this.folders.splice(0, index) - this.invalidateCache().then(() => { - this.getFiles(this.folders, prev_folder_name) - }) + this.getFiles(this.folders, prev_folder_name) + this.updatePageUrl() } }, goToPrevFolder() { @@ -133,14 +132,14 @@ export default { this.imageSlideDirection = 'next' let last = this.filesList.length - 1 - this.scrollToFile(this.$refs[`file_${last}`]) + this.scrollToFile(this.getElementByIndex(last)) } // go to first item if (key == 'home') { e.preventDefault() this.imageSlideDirection = 'prev' - this.scrollToFile(this.$refs.file_0) + this.scrollToFile(this.getElementByIndex(0)) } // toggle modal off @@ -160,7 +159,7 @@ export default { if (curSelectedIndex !== 0) { this.imageSlideDirection = 'prev' - this.scrollToFile(this.$refs[`file_${curSelectedIndex - 1}`]) + this.scrollToFile(this.getElementByIndex(curSelectedIndex - 1)) } }, goToNext() { @@ -168,20 +167,25 @@ export default { if (curSelectedIndex < this.allItemsCount - 1) { this.imageSlideDirection = 'next' - this.scrollToFile(this.$refs[`file_${curSelectedIndex + 1}`]) + this.scrollToFile(this.getElementByIndex(curSelectedIndex + 1)) } }, scrollToFile(file) { - file = file[0].$el - file.click() - - let container = this.$refs['__stack-files'].$el - let count = file.offsetTop - container.scrollTop - 20 - container.scrollBy({top: count, left: 0, behavior: 'smooth'}) - - // when scrollBy() doesnt work - if (!(container.scrollHeight > container.clientHeight)) { - file.scrollIntoView({behavior: 'smooth', block: 'end', inline: 'end'}) + if (file) { + file.click() + + this.$nextTick(() => { + this.$nextTick(() => { + let container = this.$refs['__stack-files'].$el + let count = file.offsetTop - container.scrollTop - 20 + container.scrollBy({top: count, left: 0, behavior: 'smooth'}) + + // when scrollBy() doesnt work + if (!(container.scrollHeight > container.clientHeight)) { + file.scrollIntoView({behavior: 'smooth', block: 'end', inline: 'end'}) + } + }) + }) } } } diff --git a/src/resources/assets/js/modules/url.js b/src/resources/assets/js/modules/url.js new file mode 100644 index 0000000..bcf7011 --- /dev/null +++ b/src/resources/assets/js/modules/url.js @@ -0,0 +1,35 @@ +export default { + methods: { + checkForUrlQuery() { + return window.location.href.split('?')[0] + }, + getPathFromUrl() { + return new Promise((resolve, reject) => { + if (!this.inModal) { + let path = window.location.search + this.folders = path.includes('path') ? path.replace('?path=', '').split('/') : [] + } + + return resolve() + }) + }, + updatePageUrl() { + if (!this.inModal) { + let url = this.checkForUrlQuery() + + history.pushState(null, null, + this.folders.length + ? `${url}?path=${this.folders.join('/')}` + : url + ) + } + }, + urlNavigation(e) { + if (!this.inModal) { + this.getPathFromUrl().then(() => { + this.getFiles(this.folders) + }) + } + } + } +} \ No newline at end of file diff --git a/src/resources/assets/js/modules/utils.js b/src/resources/assets/js/modules/utils.js index 8968c8d..8f76798 100644 --- a/src/resources/assets/js/modules/utils.js +++ b/src/resources/assets/js/modules/utils.js @@ -1,50 +1,12 @@ export default { methods: { + /* Check */ isLastItem(item, list) { return item == list[list.length - 1] }, isActiveModal(el) { return this.activeModal == el }, - showNotif(msg, s = 'success') { - if (msg) { - if (s == 'danger') { - this.$refs['alert-audio'].play() - } - - let title - let duration = 3 - - switch (s) { - case 'black': - case 'danger': - title = 'Error' - duration = null - break - case 'warning': - title = 'Warning' - break - default: - title = 'Success' - } - - EventHub.fire('showNotif', { - title: title, - body: msg, - type: s, - duration: duration - }) - } - }, - resetInput(input, val = null) { - if (Array.isArray(input)) { - return input.forEach((e) => { - this[e] = val - }) - } - - this[input] = val - }, moveUpCheck() { return this.allItemsCount && this.folders.length }, @@ -99,6 +61,12 @@ export default { return index > 0 ? name.substring(index + 1) : null }, + getIndexRange(start, end) { + return Array(end - start + 1).fill().map((_, idx) => start + idx) + }, + getElementByIndex(i) { + return document.querySelector(`[data-file-index='${i}']`) + }, trans(key) { return this.translations[key] }, @@ -180,11 +148,50 @@ export default { EventHub.fire('ajax-error-show') }, - /* Ops */ + /* Helpers */ // copy to clipboard copyLink(path) { this.linkCopied = true this.$copyText(path) + }, + resetInput(input, val = null) { + if (Array.isArray(input)) { + return input.forEach((e) => { + this[e] = val + }) + } + + this[input] = val + }, + showNotif(msg, s = 'success') { + if (msg) { + if (s == 'danger') { + this.$refs['alert-audio'].play() + } + + let title + let duration = 3 + + switch (s) { + case 'black': + case 'danger': + title = 'Error' + duration = null + break + case 'warning': + title = 'Warning' + break + default: + title = 'Success' + } + + EventHub.fire('showNotif', { + title: title, + body: msg, + type: s, + duration: duration + }) + } } } } \ No newline at end of file diff --git a/src/resources/assets/js/modules/watch.js b/src/resources/assets/js/modules/watch.js index c12ea6c..20f2d22 100644 --- a/src/resources/assets/js/modules/watch.js +++ b/src/resources/assets/js/modules/watch.js @@ -100,6 +100,9 @@ export default { toolBar(val) { this.updateLs({'toolBar': val}) }, + lockedList(val) { + this.updateLs({'lockedList': val}) + }, // filter sortBy(val) { diff --git a/src/resources/assets/sass/media.scss b/src/resources/assets/sass/media.scss index 0c64d3e..4359141 100644 --- a/src/resources/assets/sass/media.scss +++ b/src/resources/assets/sass/media.scss @@ -1,7 +1,3 @@ -* { - backface-visibility: hidden; -} - [v-cloak] { display: none; } @@ -161,7 +157,7 @@ overflow: scroll; align-items: center; width: 100%; - padding: 1rem 0 0; + padding: 1rem 0; list-style: none; transition: all 0.25s ease-out; @@ -300,12 +296,14 @@ display: flex; overflow: hidden; min-height: 50vh; + transition: all 0.25s; background-color: $white; } -.media-manager__stack-container, -.media-manager__stack-sidebar { - transition: all 0.25s ease-out; +@include media('max', 1023) { + .media-manager__stack-container { + max-height: 100vh; + } } // left "files list" @@ -317,7 +315,7 @@ left: 20%; overflow: scroll; padding: 0.7rem; - transition: left 0.25s ease-out; + transition: all 0.25s; } .__stack-sidebar-hidden { @@ -417,9 +415,21 @@ } .__box-img { - background-repeat: no-repeat; - background-position: top; - background-size: cover; + position: relative; + + &::before { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + content: ''; + background-image: var(--imageSrc); + background-repeat: no-repeat; + background-position: top; + background-size: cover; + will-change: transform; + } } .__box-img-lazy { @@ -570,7 +580,7 @@ display: flex; flex: 1 0 auto; flex-direction: column; - transition: background 0.25s ease-out; + transition: all 0.25s ease-out; word-break: break-all; background-color: $active_theme; @@ -919,6 +929,10 @@ $editorBtns: rgba(darken($active_theme, 70%), 0.8); margin-top: 5px !important; } +.m-b-10 { + margin-bottom: 10px !important; +} + .m-l-50 { margin-left: 50px !important; } diff --git a/src/resources/views/_manager.blade.php b/src/resources/views/_manager.blade.php index 437474a..adb0934 100644 --- a/src/resources/views/_manager.blade.php +++ b/src/resources/views/_manager.blade.php @@ -43,308 +43,310 @@ {{-- top toolbar --}} -