From 80c4c89b06897171d768d2e63091e85658128e50 Mon Sep 17 00:00:00 2001 From: Muah Date: Mon, 23 Apr 2018 09:19:36 +0200 Subject: [PATCH] v3.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - use a unique id for the zip progress tracking to avoid issues with multiple download of the same zip at the same time - add auto invalidate cache option, note that to keep things as performance friendly while making sure the cache invalidation updates as regularly as possible, every time the user navigate into a folder the invalidation check will fire. - add new enhancement to stop scrolling the main page when the manager scroll has reached the end “i focken hated that” - updated resources - update readme --- README.md | 11 +++- logs/v2.6.0.txt | 5 -- logs/v3.0.0.txt | 8 +++ src/Controllers/MediaController.php | 29 +++++---- src/Controllers/OpsTrait.php | 15 +++-- src/MediaRoutes.php | 2 +- src/config/mediaManager.php | 6 ++ src/resources/assets/js/components/media.vue | 22 +++---- src/resources/assets/js/manager.js | 5 +- src/resources/assets/js/modules/cache.js | 65 +++++++++++++++----- src/resources/assets/js/modules/download.js | 4 +- src/resources/assets/js/modules/selected.js | 5 +- src/resources/views/_manager.blade.php | 10 ++- 13 files changed, 121 insertions(+), 66 deletions(-) delete mode 100644 logs/v2.6.0.txt create mode 100644 logs/v3.0.0.txt diff --git a/README.md b/README.md index 87c89be..37f2135 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ - install dependencies ```bash -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 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 vue-scroll-stop idb-keyval axios dropzone cropperjs keycode babel-preset-es2015-node6 babel-preset-stage-2 # or -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 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 vue-scroll-stop idb-keyval axios dropzone cropperjs keycode babel-preset-es2015-node6 babel-preset-stage-2 --save ``` - add this one liner to your main js file and run `npm run watch` to compile your `js/css` files. @@ -221,6 +221,12 @@ return [ * load image preview when item is clicked */ 'lazy_load_image_on_click' => false, + + /* + * automatically invalidate cache after ? + * in "Minutes" + */ + 'cacheExpiresAfter'=> 60, ]; ``` @@ -230,4 +236,3 @@ return [ - visit `localhost:8000/media` - [Wiki](https://github.com/ctf0/Laravel-Media-Manager/wiki) -- [Cacheing Strategy](https://github.com/ctf0/Laravel-Media-Manager/issues/29) diff --git a/logs/v2.6.0.txt b/logs/v2.6.0.txt deleted file mode 100644 index e96e18c..0000000 --- a/logs/v2.6.0.txt +++ /dev/null @@ -1,5 +0,0 @@ -- fix the fucken extra space at bottom -- replace localforage with idb-keyval (removes 53kb “547 > 494”) -- fix ff backspace btn -- finalize the new cache lib -- optimization \ No newline at end of file diff --git a/logs/v3.0.0.txt b/logs/v3.0.0.txt new file mode 100644 index 0000000..194edbd --- /dev/null +++ b/logs/v3.0.0.txt @@ -0,0 +1,8 @@ +- use a unique id for the zip progress tracking to avoid issues with multiple download of the same zip at the same time + +- add auto invalidate cache option, note that to keep things as performance friendly while making sure the cache invalidation updates as regularly as possible, every time the user navigate into a folder the invalidation check will fire. + +- add new enhancement to stop scrolling the main page when the manager scroll has reached the end “i focken hated that” + +- updated resources +- update readme \ No newline at end of file diff --git a/src/Controllers/MediaController.php b/src/Controllers/MediaController.php index 97310d5..0d469ff 100755 --- a/src/Controllers/MediaController.php +++ b/src/Controllers/MediaController.php @@ -12,6 +12,7 @@ class MediaController extends Controller { use OpsTrait; + protected $config; protected $baseUrl; protected $carbon; protected $db; @@ -28,21 +29,20 @@ class MediaController extends Controller public function __construct(Carbon $carbon) { - $config = config('mediaManager'); - $this->carbon = $carbon; - $this->fileSystem = array_get($config, 'storage_disk'); + $this->config = config('mediaManager'); + $this->fileSystem = array_get($this->config, 'storage_disk'); $this->storageDisk = app('filesystem')->disk($this->fileSystem); $this->baseUrl = $this->storageDisk->url('/'); - $this->ignoreFiles = array_get($config, 'ignore_files'); - $this->fileChars = array_get($config, 'allowed_fileNames_chars'); - $this->folderChars = array_get($config, 'allowed_folderNames_chars'); - $this->sanitizedText = array_get($config, 'sanitized_text'); - $this->unallowedMimes = array_get($config, 'unallowed_mimes'); - $this->LMF = array_get($config, 'last_modified_format'); + $this->ignoreFiles = array_get($this->config, 'ignore_files'); + $this->fileChars = array_get($this->config, 'allowed_fileNames_chars'); + $this->folderChars = array_get($this->config, 'allowed_folderNames_chars'); + $this->sanitizedText = array_get($this->config, 'sanitized_text'); + $this->unallowedMimes = array_get($this->config, 'unallowed_mimes'); + $this->LMF = array_get($this->config, 'last_modified_format'); $this->db = app('db')->connection('mediamanager')->table('locked'); - $this->storageDiskInfo = config("filesystems.disks.{$this->fileSystem}"); $this->zipCacheStore = app('cache')->store('mediamanager'); + $this->storageDiskInfo = config("filesystems.disks.{$this->fileSystem}"); $this->storageDisk->addPlugin(new ListWith()); } @@ -54,7 +54,10 @@ public function __construct(Carbon $carbon) */ public function index() { - return view('MediaManager::media'); + return view('MediaManager::media')->with([ + 'randId' => uniqid(), + 'cacheExp' => array_get($this->config, 'cacheExpiresAfter'), + ]); } /** @@ -600,6 +603,7 @@ public function folder_download(Request $request) { return $this->download( $request->name, + $request->id, $this->storageDisk->allFiles("{$request->folders}/$request->name"), 'folder' ); @@ -616,6 +620,7 @@ public function files_download(Request $request) { return $this->download( $request->name . '-files', + $request->id, json_decode($request->list, true), 'files' ); @@ -634,7 +639,7 @@ public function zip_progress(Request $request) // params $id = $request->header('last-event-id'); - $name = $request->name; + $name = "{$request->name}-{$request->id}"; // get changes $store = $this->zipCacheStore; diff --git a/src/Controllers/OpsTrait.php b/src/Controllers/OpsTrait.php index daa6cf5..16f5010 100644 --- a/src/Controllers/OpsTrait.php +++ b/src/Controllers/OpsTrait.php @@ -196,15 +196,18 @@ protected function storeFile($item, $upload_path, $file_name) * @param mixed $name * @param mixed $list * @param mixed $type + * @param mixed $id */ - protected function download($name, $list, $type) + protected function download($name, $id, $list, $type) { + $cacheName = "$name-$id"; + // track changes $counter = 100 / count($list); $store = $this->zipCacheStore; - $store->forever("$name.progress", 0); + $store->forever("$cacheName.progress", 0); - return response()->stream(function () use ($name, $list, $type, $counter, $store) { + return response()->stream(function () use ($name, $list, $type, $counter, $store, $cacheName) { $zip = new ZipStream("$name.zip", [ 'content_type' => 'application/octet-stream', ]); @@ -219,14 +222,14 @@ protected function download($name, $list, $type) } if ($streamRead) { - $store->increment("$name.progress", round($counter, 2)); + $store->increment("$cacheName.progress", round($counter, 2)); $zip->addFileFromStream($file_name, $streamRead); } else { - $store->forever("$name.warn", $file_name); + $store->forever("$cacheName.warn", $file_name); } } - $store->forever("$name.done", true); + $store->forever("$cacheName.done", true); $zip->finish(); }); } diff --git a/src/MediaRoutes.php b/src/MediaRoutes.php index 102aece..d838a9d 100644 --- a/src/MediaRoutes.php +++ b/src/MediaRoutes.php @@ -26,7 +26,7 @@ public static function routes() app('router')->post('change_vis', ['uses' => "$controller@change_vis", 'as' => 'change_vis']); app('router')->post('lock_file', ['uses' => "$controller@lock_file", 'as' => 'lock_file']); - app('router')->get('zip_progress/{name?}', ['uses' => "$controller@zip_progress", 'as' => 'zip_progress']); + app('router')->get('zip_progress/{name?}/{id?}', ['uses' => "$controller@zip_progress", 'as' => 'zip_progress']); app('router')->post('folder_download', ['uses' => "$controller@folder_download", 'as' => 'folder_download']); app('router')->post('files_download', ['uses' => "$controller@files_download", 'as' => 'files_download']); }); diff --git a/src/config/mediaManager.php b/src/config/mediaManager.php index f93d148..d47c5f4 100644 --- a/src/config/mediaManager.php +++ b/src/config/mediaManager.php @@ -64,4 +64,10 @@ * load image preview when item is clicked */ 'lazy_load_image_on_click' => false, + + /* + * automatically invalidate cache after ? + * in "Minutes" + */ + 'cacheExpiresAfter'=> 60, ]; diff --git a/src/resources/assets/js/components/media.vue b/src/resources/assets/js/components/media.vue index 8c4cd12..85bd279 100644 --- a/src/resources/assets/js/components/media.vue +++ b/src/resources/assets/js/components/media.vue @@ -42,7 +42,8 @@ export default { 'zipProgressRoute', 'uploadPanelImgList', 'hideExt', - 'hidePath' + 'hidePath', + 'cacheExp' ], data() { return { @@ -99,8 +100,12 @@ export default { }, created() { document.addEventListener('keydown', this.shortCuts) - this.preSaved() - this.getFiles(this.folders, null, this.selectedFile) + + this.invalidateCache().then(() => { + this.preSaved() + this.getFiles(this.folders, null, this.selectedFile) + }) + }, mounted() { this.fileUpload() @@ -122,17 +127,6 @@ export default { document.removeEventListener('keydown', this.shortCuts) }, methods: { - preSaved() { - let ls = this.getLs() - - if (ls) { - this.randomNames = ls.randomNames === undefined ? false : ls.randomNames - this.folders = ls.folders === undefined ? [] : ls.folders - this.toolBar = ls.toolBar === undefined ? true : ls.toolBar - this.selectedFile = ls.selectedFileName === undefined ? null : ls.selectedFileName - } - }, - shortCuts(e) { let key = keycode(e) diff --git a/src/resources/assets/js/manager.js b/src/resources/assets/js/manager.js index 82dbdee..7366e58 100644 --- a/src/resources/assets/js/manager.js +++ b/src/resources/assets/js/manager.js @@ -7,6 +7,7 @@ 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'), { @@ -36,8 +37,8 @@ axios.defaults.headers.common = { 'X-Requested-With': 'XMLHttpRequest' } axios.interceptors.response.use( - (response) => {return response}, - (error) => {return Promise.reject(error.response)} + (response) => response, + (error) => Promise.reject(error.response) ) // vue-awesome diff --git a/src/resources/assets/js/modules/cache.js b/src/resources/assets/js/modules/cache.js index fbf6ed4..b193e39 100644 --- a/src/resources/assets/js/modules/cache.js +++ b/src/resources/assets/js/modules/cache.js @@ -1,5 +1,8 @@ -import { Store, set, get, del, clear } from 'idb-keyval' -const idbKeyVal = new Store( +import addMinutes from 'date-fns/add_minutes' +import getTime from 'date-fns/get_time' + +import { Store, get, set, del, clear, keys } from 'idb-keyval' +const cacheStore = new Store( 'ctf0-Media_Manager', // db 'laravel-media-manager' // store ) @@ -11,26 +14,36 @@ export default { return this.$ls.get('ctf0-Media_Manager', {}) }, updateLs(obj) { - let storage = this.getLs() - - Object.assign(storage, obj) + let storage = Object.assign(this.getLs(), obj) this.$ls.set('ctf0-Media_Manager', storage) }, removeLs() { this.folders = [] this.$ls.remove('ctf0-Media_Manager') - // location.reload() + }, + preSaved() { + let ls = this.getLs() + + if (ls) { + this.randomNames = ls.randomNames || false + this.folders = ls.folders || [] + this.toolBar = ls.toolBar || true + this.selectedFile = ls.selectedFileName || null + } }, // cache - cacheResponse(value) { - return set(this.cacheName, value, idbKeyVal).catch((err) => { + getCachedResponse(key = this.cacheName) { + return get(key, cacheStore) + }, + cacheResponse(val) { + let date = getTime(addMinutes(new Date(), this.cacheExp)) + val = Object.assign(val, {expire: date}) + + return set(this.cacheName, val, cacheStore).catch((err) => { console.warn('cacheStore.setItem', err) }) }, - getCachedResponse() { - return get(this.cacheName, idbKeyVal) - }, removeCachedResponse(destination = null) { let cacheName = this.cacheName let extra @@ -49,15 +62,18 @@ export default { : [cacheName] items.forEach((one) => { - return del(one, idbKeyVal).then(() => { - console.log(`${one} ${this.trans('clear_cache')}`) - }).catch((err) => { - console.warn('cacheStore.removeItem', err) - }) + return this.deleteCache(one) + }) + }, + deleteCache(item) { + return del(item, cacheStore).then(() => { + console.log(`${item} ${this.trans('clear_cache')}`) + }).catch((err) => { + console.error('cacheStore.removeItem', err) }) }, clearCache(showNotif = true) { - clear(idbKeyVal).then(() => { + clear(cacheStore).then(() => { if (showNotif) { this.showNotif(this.trans('clear_cache')) } @@ -68,6 +84,21 @@ export default { }).catch((err) => { console.error(err) }) + }, + invalidateCache() { + let now = getTime(new Date()) + + return keys(cacheStore).then((keys) => { + keys.map((key) => { + this.getCachedResponse(key).then((item) => { + if (item.expire < now) { + return this.deleteCache(key) + } + }) + }) + }).catch((err) => { + console.error(err) + }) } } } diff --git a/src/resources/assets/js/modules/download.js b/src/resources/assets/js/modules/download.js index b3f0364..f668a28 100644 --- a/src/resources/assets/js/modules/download.js +++ b/src/resources/assets/js/modules/download.js @@ -25,7 +25,7 @@ export default { }) }, - ZipDownload(type) { + ZipDownload(type, id) { this.showProgress = true // de-select download btn @@ -39,7 +39,7 @@ export default { : folders.length ? `${folders[folders.length - 1]}-files` : 'media_manager-files' let es = new EventSource( - `${this.zipProgressRoute}/${name}`, + `${this.zipProgressRoute}/${name}/${id}`, {withCredentials: true} ) diff --git a/src/resources/assets/js/modules/selected.js b/src/resources/assets/js/modules/selected.js index aab114f..131fa34 100644 --- a/src/resources/assets/js/modules/selected.js +++ b/src/resources/assets/js/modules/selected.js @@ -82,7 +82,10 @@ export default { openFolder(file) { if (!this.isBulkSelecting() && this.fileTypeIs(file, 'folder')) { this.folders.push(file.name) - this.getFiles(this.folders) + + this.invalidateCache().then(() => { + this.getFiles(this.folders) + }) } this.resetInput('currentFilterName') diff --git a/src/resources/views/_manager.blade.php b/src/resources/views/_manager.blade.php index 4be3ff4..6bdbb06 100644 --- a/src/resources/views/_manager.blade.php +++ b/src/resources/views/_manager.blade.php @@ -11,6 +11,7 @@ 'lazyLoad' => config('mediaManager.lazy_load_image_on_click') ? true : false, 'imageTypes' => config('mediaManager.image_extended_mimes'), ]) }}" + :cache-exp="{{ $cacheExp }}" :in-modal="{{ isset($modal) ? 'true' : 'false' }}" :hide-ext="{{ isset($hideExt) ? json_encode($hideExt) : '[]' }}" :hide-path="{{ isset($hidePath) ? json_encode($hidePath) : '[]' }}" @@ -410,7 +411,8 @@ class="button" ref="__stack-files" :class="{'__stack-sidebar-hidden': !toggleInfo}" @dbltap="dbltap()" - @swiperight="goToPrevFolder()"> + @swiperight="goToPrevFolder()" + v-scroll-stop> {{-- loadings --}}
@@ -578,8 +580,9 @@ class="link image"

{{ trans('MediaManager::messages.download_folder') }}:
-
+ {{ csrf_field() }} +