From e0c43393651fc9bf3fcbb2a2d8e8b190b9fa013e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=93=E3=81=B4=E3=81=AA=E3=81=9F=E3=81=BF=E3=81=BD?= <syuilotan@yahoo.co.jp> Date: Sat, 17 Feb 2018 00:36:28 +0900 Subject: [PATCH] wip --- .../drive.tag => views/components/drive.vue} | 581 +++++++++--------- 1 file changed, 287 insertions(+), 294 deletions(-) rename src/web/app/mobile/{tags/drive.tag => views/components/drive.vue} (54%) diff --git a/src/web/app/mobile/tags/drive.tag b/src/web/app/mobile/views/components/drive.vue similarity index 54% rename from src/web/app/mobile/tags/drive.tag rename to src/web/app/mobile/views/components/drive.vue index e0a5872d8..a3dd95973 100644 --- a/src/web/app/mobile/tags/drive.tag +++ b/src/web/app/mobile/views/components/drive.vue @@ -1,41 +1,38 @@ -<mk-drive> +<template> +<div class="mk-drive"> <nav ref="nav"> - <a @click="goRoot" href="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-drive.drive%</a> - <template each={ folder in hierarchyFolders }> - <span>%fa:angle-right%</span> - <a @click="move" href="/i/drive/folder/{ folder.id }">{ folder.name }</a> + <a @click.prevent="goRoot" href="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-drive.drive%</a> + <template v-for="folder in hierarchyFolders"> + <span :key="folder.id + '>'">%fa:angle-right%</span> + <a :key="folder.id" @click.prevent="cd(folder)" :href="`/i/drive/folder/${folder.id}`">{{ folder.name }}</a> </template> <template v-if="folder != null"> <span>%fa:angle-right%</span> - <p>{ folder.name }</p> + <p>{{ folder.name }}</p> </template> <template v-if="file != null"> <span>%fa:angle-right%</span> - <p>{ file.name }</p> + <p>{{ file.name }}</p> </template> </nav> <mk-uploader ref="uploader"/> - <div class="browser { fetching: fetching }" v-if="file == null"> + <div class="browser" :class="{ fetching }" v-if="file == null"> <div class="info" v-if="info"> - <p v-if="folder == null">{ (info.usage / info.capacity * 100).toFixed(1) }% %i18n:mobile.tags.mk-drive.used%</p> + <p v-if="folder == null">{{ (info.usage / info.capacity * 100).toFixed(1) }}% %i18n:mobile.tags.mk-drive.used%</p> <p v-if="folder != null && (folder.folders_count > 0 || folder.files_count > 0)"> - <template v-if="folder.folders_count > 0">{ folder.folders_count } %i18n:mobile.tags.mk-drive.folder-count%</template> + <template v-if="folder.folders_count > 0">{{ folder.folders_count }} %i18n:mobile.tags.mk-drive.folder-count%</template> <template v-if="folder.folders_count > 0 && folder.files_count > 0">%i18n:mobile.tags.mk-drive.count-separator%</template> - <template v-if="folder.files_count > 0">{ folder.files_count } %i18n:mobile.tags.mk-drive.file-count%</template> + <template v-if="folder.files_count > 0">{{ folder.files_count }} %i18n:mobile.tags.mk-drive.file-count%</template> </p> </div> <div class="folders" v-if="folders.length > 0"> - <template each={ folder in folders }> - <mk-drive-folder folder={ folder }/> - </template> + <mk-drive-folder v-for="folder in folders" :key="folder.id" :folder="folder"/> <p v-if="moreFolders">%i18n:mobile.tags.mk-drive.load-more%</p> </div> <div class="files" v-if="files.length > 0"> - <template each={ file in files }> - <mk-drive-file file={ file }/> - </template> + <mk-drive-file v-for="file in files" :key="file.id" :file="file"/> <button class="more" v-if="moreFiles" @click="fetchMoreFiles"> - { fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-drive.load-more%' } + {{ fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-drive.load-more%' }} </button> </div> <div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching"> @@ -49,221 +46,117 @@ <div class="dot2"></div> </div> </div> - <input ref="file" type="file" multiple="multiple" onchange={ changeLocalFile }/> - <mk-drive-file-viewer v-if="file != null" file={ file }/> - <style lang="stylus" scoped> - :scope - display block - background #fff + <input ref="file" type="file" multiple="multiple" @change="onChangeLocalFile"/> + <mk-drive-file-viewer v-if="file != null" :file="file"/> +</div> +</template> - > nav - display block - position sticky - position -webkit-sticky - top 0 - z-index 1 - width 100% - padding 10px 12px - overflow auto - white-space nowrap - font-size 0.9em - color rgba(0, 0, 0, 0.67) - -webkit-backdrop-filter blur(12px) - backdrop-filter blur(12px) - background-color rgba(#fff, 0.75) - border-bottom solid 1px rgba(0, 0, 0, 0.13) +<script lang="ts"> +import Vue from 'vue'; - > p - > a - display inline - margin 0 - padding 0 - text-decoration none !important - color inherit +export default Vue.extend({ + props: ['initFolder', 'initFile', 'selectFile', 'multiple', 'isNaked', 'top'], + data() { + return { + /** + * 現在の階層(フォルダ) + * * null でルートを表す + */ + folder: null, - &:last-child - font-weight bold + file: null, - > [data-fa] - margin-right 4px + files: [], + folders: [], + moreFiles: false, + moreFolders: false, + hierarchyFolders: [], + selectedFiles: [], + info: null, + connection: null, + connectionId: null, - > span - margin 0 8px - opacity 0.5 - - > .browser - &.fetching - opacity 0.5 - - > .info - border-bottom solid 1px #eee - - &:empty - display none - - > p - display block - max-width 500px - margin 0 auto - padding 4px 16px - font-size 10px - color #777 - - > .folders - > mk-drive-folder - border-bottom solid 1px #eee - - > .files - > mk-drive-file - border-bottom solid 1px #eee - - > .more - display block - width 100% - padding 16px - font-size 16px - color #555 - - > .empty - padding 16px - text-align center - color #999 - pointer-events none - - > p - margin 0 - - > .fetching - .spinner - margin 100px auto - width 40px - height 40px - text-align center - - animation sk-rotate 2.0s infinite linear - - .dot1, .dot2 - width 60% - height 60% - display inline-block - position absolute - top 0 - background rgba(0, 0, 0, 0.2) - border-radius 100% - - animation sk-bounce 2.0s infinite ease-in-out - - .dot2 - top auto - bottom 0 - animation-delay -1.0s - - @keyframes sk-rotate { 100% { transform: rotate(360deg); }} - - @keyframes sk-bounce { - 0%, 100% { - transform: scale(0.0); - } 50% { - transform: scale(1.0); - } - } - - > [ref='file'] - display none - - </style> - <script lang="typescript"> - this.mixin('i'); - this.mixin('api'); - - this.mixin('drive-stream'); - this.connection = this.driveStream.getConnection(); - this.connectionId = this.driveStream.use(); - - this.files = []; - this.folders = []; - this.hierarchyFolders = []; - this.selectedFiles = []; - - // 現在の階層(フォルダ) - // * null でルートを表す - this.folder = null; - - this.file = null; - - this.isFileSelectMode = this.opts.selectFile; - this.multiple = this.opts.multiple; - - this.on('mount', () => { - this.connection.on('file_created', this.onStreamDriveFileCreated); - this.connection.on('file_updated', this.onStreamDriveFileUpdated); - this.connection.on('folder_created', this.onStreamDriveFolderCreated); - this.connection.on('folder_updated', this.onStreamDriveFolderUpdated); - - if (this.opts.folder) { - this.cd(this.opts.folder, true); - } else if (this.opts.file) { - this.cf(this.opts.file, true); - } else { - this.fetch(); - } - - if (this.opts.isNaked) { - this.$refs.nav.style.top = `${this.opts.top}px`; - } - }); - - this.on('unmount', () => { - this.connection.off('file_created', this.onStreamDriveFileCreated); - this.connection.off('file_updated', this.onStreamDriveFileUpdated); - this.connection.off('folder_created', this.onStreamDriveFolderCreated); - this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); - this.driveStream.dispose(this.connectionId); - }); - - this.onStreamDriveFileCreated = file => { - this.addFile(file, true); + fetching: true, + fetchingMoreFiles: false, + fetchingMoreFolders: false }; + }, + computed: { + isFileSelectMode(): boolean { + return this.selectFile; + } + }, + mounted() { + this.connection = this.$root.$data.os.streams.driveStream.getConnection(); + this.connectionId = this.$root.$data.os.streams.driveStream.use(); - this.onStreamDriveFileUpdated = file => { + this.connection.on('file_created', this.onStreamDriveFileCreated); + this.connection.on('file_updated', this.onStreamDriveFileUpdated); + this.connection.on('folder_created', this.onStreamDriveFolderCreated); + this.connection.on('folder_updated', this.onStreamDriveFolderUpdated); + + if (this.initFolder) { + this.cd(this.initFolder, true); + } else if (this.initFile) { + this.cf(this.initFile, true); + } else { + this.fetch(); + } + + if (this.isNaked) { + (this.$refs.nav as any).style.top = `${this.top}px`; + } + }, + beforeDestroy() { + this.connection.off('file_created', this.onStreamDriveFileCreated); + this.connection.off('file_updated', this.onStreamDriveFileUpdated); + this.connection.off('folder_created', this.onStreamDriveFolderCreated); + this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); + this.$root.$data.os.streams.driveStream.dispose(this.connectionId); + }, + methods: { + onStreamDriveFileCreated(file) { + this.addFile(file, true); + }, + + onStreamDriveFileUpdated(file) { const current = this.folder ? this.folder.id : null; if (current != file.folder_id) { this.removeFile(file); } else { this.addFile(file, true); } - }; + }, - this.onStreamDriveFolderCreated = folder => { + onStreamDriveFolderCreated(folder) { this.addFolder(folder, true); - }; + }, - this.onStreamDriveFolderUpdated = folder => { + onStreamDriveFolderUpdated(folder) { const current = this.folder ? this.folder.id : null; if (current != folder.parent_id) { this.removeFolder(folder); } else { this.addFolder(folder, true); } - }; + }, - this.move = ev => { - ev.preventDefault(); - this.cd(ev.item.folder); - return false; - }; + dive(folder) { + this.hierarchyFolders.unshift(folder); + if (folder.parent) this.dive(folder.parent); + }, - this.cd = (target, silent = false) => { + cd(target, silent = false) { this.file = null; if (target == null) { this.goRoot(); return; - } else if (typeof target == 'object') target = target.id; + } else if (typeof target == 'object') { + target = target.id; + } - this.update({ - fetching: true - }); + this.fetching = true; this.$root.$data.os.api('drive/folders/show', { folder_id: target @@ -271,15 +164,14 @@ this.folder = folder; this.hierarchyFolders = []; - if (folder.parent) dive(folder.parent); + if (folder.parent) this.dive(folder.parent); - this.update(); this.$emit('open-folder', this.folder, silent); this.fetch(); }); - }; + }, - this.addFolder = (folder, unshift = false) => { + addFolder(folder, unshift = false) { const current = this.folder ? this.folder.id : null; // 追加しようとしているフォルダが、今居る階層とは違う階層のものだったら中断 if (current != folder.parent_id) return; @@ -292,19 +184,16 @@ } else { this.folders.push(folder); } + }, - this.update(); - }; - - this.addFile = (file, unshift = false) => { + addFile(file, unshift = false) { const current = this.folder ? this.folder.id : null; // 追加しようとしているファイルが、今居る階層とは違う階層のものだったら中断 if (current != file.folder_id) return; if (this.files.some(f => f.id == file.id)) { const exist = this.files.map(f => f.id).indexOf(file.id); - this.files[exist] = file; - this.update(); + this.files[exist] = file; // TODO return; } @@ -313,51 +202,47 @@ } else { this.files.push(file); } + }, - this.update(); - }; - - this.removeFolder = folder => { + removeFolder(folder) { if (typeof folder == 'object') folder = folder.id; this.folders = this.folders.filter(f => f.id != folder); - this.update(); - }; + }, - this.removeFile = file => { + removeFile(file) { if (typeof file == 'object') file = file.id; this.files = this.files.filter(f => f.id != file); - this.update(); - }; + }, - this.appendFile = file => this.addFile(file); - this.appendFolder = file => this.addFolder(file); - this.prependFile = file => this.addFile(file, true); - this.prependFolder = file => this.addFolder(file, true); - - this.goRoot = ev => { - ev.preventDefault(); + appendFile(file) { + this.addFile(file); + }, + appendFolder(folder) { + this.addFolder(folder); + }, + prependFile(file) { + this.addFile(file, true); + }, + prependFolder(folder) { + this.addFolder(folder, true); + }, + goRoot() { if (this.folder || this.file) { - this.update({ - file: null, - folder: null, - hierarchyFolders: [] - }); + this.file = null; + this.folder = null; + this.hierarchyFolders = []; this.$emit('move-root'); this.fetch(); } + }, - return false; - }; - - this.fetch = () => { - this.update({ - folders: [], - files: [], - moreFolders: false, - moreFiles: false, - fetching: true - }); + fetch() { + this.folders = []; + this.files = []; + this.moreFolders = false; + this.moreFiles = false; + this.fetching = true; this.$emit('begin-fetch'); @@ -398,9 +283,8 @@ if (flag) { fetchedFolders.forEach(this.appendFolder); fetchedFiles.forEach(this.appendFile); - this.update({ - fetching: false - }); + this.fetching = false; + // 一連の読み込みが完了したイベントを発行 this.$emit('fetched'); } else { @@ -413,16 +297,14 @@ if (this.folder == null) { // Fetch addtional drive info this.$root.$data.os.api('drive').then(info => { - this.update({ info }); + this.info = info; }); } - }; + }, - this.fetchMoreFiles = () => { - this.update({ - fetching: true, - fetchingMoreFiles: true - }); + fetchMoreFiles() { + this.fetching = true; + this.fetchingMoreFiles = true; const max = 30; @@ -439,14 +321,12 @@ this.moreFiles = false; } files.forEach(this.appendFile); - this.update({ - fetching: false, - fetchingMoreFiles: false - }); + this.fetching = false; + this.fetchingMoreFiles = false; }); - }; + }, - this.chooseFile = file => { + chooseFile(file) { if (this.isFileSelectMode) { if (this.multiple) { if (this.selectedFiles.some(f => f.id == file.id)) { @@ -454,7 +334,6 @@ } else { this.selectedFiles.push(file); } - this.update(); this.$emit('change-selection', this.selectedFiles); } else { this.$emit('selected', file); @@ -462,14 +341,12 @@ } else { this.cf(file); } - }; + }, - this.cf = (file, silent = false) => { + cf(file, silent = false) { if (typeof file == 'object') file = file.id; - this.update({ - fetching: true - }); + this.fetching = true; this.$root.$data.os.api('drive/files/show', { file_id: file @@ -479,19 +356,13 @@ this.folder = null; this.hierarchyFolders = []; - if (file.folder) dive(file.folder); + if (file.folder) this.dive(file.folder); - this.update(); this.$emit('open-file', this.file, silent); }); - }; + }, - const dive = folder => { - this.hierarchyFolders.unshift(folder); - if (folder.parent) dive(folder.parent); - }; - - this.openContextMenu = () => { + openContextMenu() { const fn = window.prompt('何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>'); if (fn == null || fn == '') return; switch (fn) { @@ -514,13 +385,13 @@ alert('ごめんなさい!フォルダの削除は未実装です...。'); break; } - }; + }, - this.selectLocalFile = () => { - this.$refs.file.click(); - }; + selectLocalFile() { + (this.$refs.file as any).click(); + }, - this.createFolder = () => { + createFolder() { const name = window.prompt('フォルダー名'); if (name == null || name == '') return; this.$root.$data.os.api('drive/folders/create', { @@ -528,11 +399,10 @@ parent_id: this.folder ? this.folder.id : undefined }).then(folder => { this.addFolder(folder, true); - this.update(); }); - }; + }, - this.renameFolder = () => { + renameFolder() { if (this.folder == null) { alert('現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。'); return; @@ -545,9 +415,9 @@ }).then(folder => { this.cd(folder); }); - }; + }, - this.moveFolder = () => { + moveFolder() { if (this.folder == null) { alert('現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。'); return; @@ -561,9 +431,9 @@ this.cd(folder); }); }); - }; + }, - this.urlUpload = () => { + urlUpload() { const url = window.prompt('アップロードしたいファイルのURL'); if (url == null || url == '') return; this.$root.$data.os.api('drive/files/upload_from_url', { @@ -571,10 +441,133 @@ folder_id: this.folder ? this.folder.id : undefined }); alert('アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。'); - }; + }, - this.changeLocalFile = () => { - Array.from(this.$refs.file.files).forEach(f => this.$refs.uploader.upload(f, this.folder)); - }; - </script> -</mk-drive> + onChangeLocalFile() { + Array.from((this.$refs.file as any).files) + .forEach(f => (this.$refs.uploader as any).upload(f, this.folder)); + } + } +}); +</script> + +<style lang="stylus" scoped> +.mk-drive + background #fff + + > nav + display block + position sticky + position -webkit-sticky + top 0 + z-index 1 + width 100% + padding 10px 12px + overflow auto + white-space nowrap + font-size 0.9em + color rgba(0, 0, 0, 0.67) + -webkit-backdrop-filter blur(12px) + backdrop-filter blur(12px) + background-color rgba(#fff, 0.75) + border-bottom solid 1px rgba(0, 0, 0, 0.13) + + > p + > a + display inline + margin 0 + padding 0 + text-decoration none !important + color inherit + + &:last-child + font-weight bold + + > [data-fa] + margin-right 4px + + > span + margin 0 8px + opacity 0.5 + + > .browser + &.fetching + opacity 0.5 + + > .info + border-bottom solid 1px #eee + + &:empty + display none + + > p + display block + max-width 500px + margin 0 auto + padding 4px 16px + font-size 10px + color #777 + + > .folders + > mk-drive-folder + border-bottom solid 1px #eee + + > .files + > mk-drive-file + border-bottom solid 1px #eee + + > .more + display block + width 100% + padding 16px + font-size 16px + color #555 + + > .empty + padding 16px + text-align center + color #999 + pointer-events none + + > p + margin 0 + + > .fetching + .spinner + margin 100px auto + width 40px + height 40px + text-align center + + animation sk-rotate 2.0s infinite linear + + .dot1, .dot2 + width 60% + height 60% + display inline-block + position absolute + top 0 + background rgba(0, 0, 0, 0.2) + border-radius 100% + + animation sk-bounce 2.0s infinite ease-in-out + + .dot2 + top auto + bottom 0 + animation-delay -1.0s + + @keyframes sk-rotate { 100% { transform: rotate(360deg); }} + + @keyframes sk-bounce { + 0%, 100% { + transform: scale(0.0); + } 50% { + transform: scale(1.0); + } + } + + > [ref='file'] + display none + +</style>