2023-07-27 14:31:52 +09:00
<!--
2024-02-13 15:59:27 +00:00
SPDX - FileCopyrightText : syuilo and misskey - project
2023-07-27 14:31:52 +09:00
SPDX - License - Identifier : AGPL - 3.0 - only
-- >
2018-02-14 15:54:18 +09:00
< template >
2023-05-29 19:56:17 +09:00
< div :class = "$style.root" >
< nav :class = "$style.nav" >
< div :class = "$style.navPath" @ contextmenu.prevent.stop = " ( ) = > { } " >
2022-01-18 23:06:16 +09:00
< XNavFolder
2023-05-29 19:56:17 +09:00
: class = "[$style.navPathItem, { [$style.navCurrent]: folder == null }]"
2023-05-19 13:58:09 +09:00
: parentFolder = "folder"
2022-01-18 23:06:16 +09:00
@ move = "move"
@ upload = "upload"
2023-05-19 13:58:09 +09:00
@ removeFile = "removeFile"
@ removeFolder = "removeFolder"
2022-01-18 23:06:16 +09:00
/ >
2020-04-22 00:34:56 +09:00
< template v-for = "f in hierarchyFolders" >
2023-09-30 21:53:52 +02:00
< span : class = "[$style.navPathItem, $style.navSeparator]" > < i class = "ph-caret-right ph-bold ph-lg" > < / i > < / span >
2022-01-18 23:06:16 +09:00
< XNavFolder
: folder = "f"
2023-05-19 13:58:09 +09:00
: parentFolder = "folder"
2023-05-29 19:56:17 +09:00
: class = "[$style.navPathItem]"
2022-01-18 23:06:16 +09:00
@ move = "move"
@ upload = "upload"
2023-05-19 13:58:09 +09:00
@ removeFile = "removeFile"
@ removeFolder = "removeFolder"
2022-01-18 23:06:16 +09:00
/ >
2018-02-08 14:50:18 +09:00
< / template >
2023-09-30 21:53:52 +02:00
< span v-if = "folder != null" :class="[$style.navPathItem, $style.navSeparator]"><i class="ph-caret-right ph-bold ph-lg" > < / i > < / span >
2023-05-29 19:56:17 +09:00
< span v-if = "folder != null" :class="[$style.navPathItem, $style.navCurrent]" > {{ folder.name }} < / span >
2017-01-12 05:55:38 +09:00
< / div >
2024-06-28 16:27:28 +02:00
< MkInput :class = "$style.navMenu" v-model = "searchQuery" :large="true" :autofocus="true" type="search" placeholder="Search drive via alt text or file names" @enter="search" >
< template # prefix > < i class = "ph-magnifying-glass ph-bold ph-lg" > < / i > < / template >
< / MkInput >
2023-09-30 21:53:52 +02:00
< button class = "_button" :class = "$style.navMenu" @click ="showMenu" > < i class = "ph-dots-three ph-bold ph-lg" > < / i > < / button >
2017-01-12 05:55:38 +09:00
< / nav >
2022-07-15 19:15:23 +09:00
< div
2023-05-29 19:56:17 +09:00
ref = "main"
: class = "[$style.main, { [$style.uploading]: uploadings.length > 0, [$style.fetching]: fetching }]"
2018-02-14 15:54:18 +09:00
@ dragover . prevent . stop = "onDragover"
2018-02-27 06:35:16 +09:00
@ dragenter = "onDragenter"
2018-02-14 15:54:18 +09:00
@ dragleave = "onDragleave"
@ drop . prevent . stop = "onDrop"
2021-01-11 18:49:39 +09:00
@ contextmenu . stop = "onContextmenu"
2018-02-14 15:54:18 +09:00
>
2023-05-29 19:56:17 +09:00
< div ref = "contents" >
< div v-show = "folders.length > 0" ref="foldersContainer" :class="$style.folders" >
2022-01-18 23:06:16 +09:00
< XFolder
v - for = "(f, i) in folders"
: key = "f.id"
v - anim = "i"
2023-05-29 19:56:17 +09:00
: class = "$style.folder"
2022-01-18 23:06:16 +09:00
: folder = "f"
2023-05-19 13:58:09 +09:00
: selectMode = "select === 'folder'"
: isSelected = "selectedFolders.some(x => x.id === f.id)"
2022-01-18 23:06:16 +09:00
@ chosen = "chooseFolder"
@ move = "move"
@ upload = "upload"
2023-05-19 13:58:09 +09:00
@ removeFile = "removeFile"
@ removeFolder = "removeFolder"
2022-01-18 23:06:16 +09:00
@ dragstart = "isDragSource = true"
@ dragend = "isDragSource = false"
/ >
2017-12-19 13:18:17 +09:00
<!-- SEE : https : //stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
2023-05-29 19:56:17 +09:00
< div v-for = "(n, i) in 16" :key="i" :class="$style.padding" > < / div >
2023-07-08 17:48:10 +09:00
< MkButton v-if = "moreFolders" ref="moreFolders" @click="fetchMoreFolders" > {{ i18n.ts.loadMore }} < / MkButton >
2017-01-12 05:55:38 +09:00
< / div >
2023-05-29 19:56:17 +09:00
< div v-show = "files.length > 0" ref="filesContainer" :class="$style.files" >
2022-01-18 23:06:16 +09:00
< XFile
v - for = "(file, i) in files"
: key = "file.id"
v - anim = "i"
2023-05-29 19:56:17 +09:00
: class = "$style.file"
2022-01-18 23:06:16 +09:00
: file = "file"
2023-07-05 06:54:40 +02:00
: folder = "folder"
2023-05-19 13:58:09 +09:00
: selectMode = "select === 'file'"
: isSelected = "selectedFiles.some(x => x.id === file.id)"
2022-01-18 23:06:16 +09:00
@ chosen = "chooseFile"
@ dragstart = "isDragSource = true"
@ dragend = "isDragSource = false"
/ >
2017-12-19 13:18:17 +09:00
<!-- SEE : https : //stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
2023-05-29 19:56:17 +09:00
< div v-for = "(n, i) in 16" :key="i" :class="$style.padding" > < / div >
2022-01-28 11:39:49 +09:00
< MkButton v-show = "moreFiles" ref="loadMoreFiles" @click="fetchMoreFiles" > {{ i18n.ts.loadMore }} < / MkButton >
2017-01-12 05:55:38 +09:00
< / div >
2023-05-29 19:56:17 +09:00
< div v-if = "files.length == 0 && folders.length == 0 && !fetching" :class="$style.empty" >
2024-01-20 08:11:59 +09:00
< div v-if = "draghover" > {{ i18n.ts [ ' empty -draghover ' ] }} < / div >
< div v-if = "!draghover && folder == null" > < strong > { { i18n . ts . emptyDrive } } < / strong > < br / > { { i18n . ts [ 'empty-drive-description' ] } } < / div >
2023-05-29 19:56:17 +09:00
< div v-if = "!draghover && folder != null" > {{ i18n.ts.emptyFolder }} < / div >
2016-12-29 07:49:51 +09:00
< / div >
2017-01-12 05:55:38 +09:00
< / div >
2020-10-17 20:12:00 +09:00
< MkLoading v -if = " fetching " / >
2017-01-12 05:55:38 +09:00
< / div >
2023-05-29 19:56:17 +09:00
< div v-if = "draghover" :class="$style.dropzone" > < / div >
< input ref = "fileInput" style = "display: none;" type = "file" accept = "*/*" multiple tabindex = "-1" @change ="onChangeFileInput" / >
2018-02-14 15:54:18 +09:00
< / div >
< / template >
2022-01-18 23:06:16 +09:00
< script lang = "ts" setup >
2023-02-16 15:09:41 +01:00
import { nextTick , onActivated , onBeforeUnmount , onMounted , ref , shallowRef , watch } from 'vue' ;
2022-01-18 23:06:16 +09:00
import * as Misskey from 'misskey-js' ;
2022-09-06 18:21:49 +09:00
import MkButton from './MkButton.vue' ;
2024-01-30 19:53:53 +09:00
import type { MenuItem } from '@/types/menu.js' ;
2022-08-31 00:24:33 +09:00
import XNavFolder from '@/components/MkDrive.navFolder.vue' ;
import XFolder from '@/components/MkDrive.folder.vue' ;
import XFile from '@/components/MkDrive.file.vue' ;
2024-06-28 16:27:28 +02:00
import MkInput from '@/components/MkInput.vue' ;
2023-09-19 16:37:43 +09:00
import * as os from '@/os.js' ;
2024-01-04 18:32:46 +09:00
import { misskeyApi } from '@/scripts/misskey-api.js' ;
2023-09-19 16:37:43 +09:00
import { useStream } from '@/stream.js' ;
import { defaultStore } from '@/store.js' ;
import { i18n } from '@/i18n.js' ;
import { uploadFile , uploads } from '@/scripts/upload.js' ;
import { claimAchievement } from '@/scripts/achievements.js' ;
2022-01-18 23:06:16 +09:00
2024-06-28 16:27:28 +02:00
const searchQuery = ref ( '' ) ;
2022-01-18 23:06:16 +09:00
const props = withDefaults ( defineProps < {
initialFolder ? : Misskey . entities . DriveFolder ;
type ? : string ;
multiple ? : boolean ;
select ? : 'file' | 'folder' | null ;
} > ( ) , {
multiple : false ,
select : null ,
} ) ;
2018-02-14 15:54:18 +09:00
2022-01-18 23:06:16 +09:00
const emit = defineEmits < {
2022-05-05 13:45:50 +02:00
( ev : 'selected' , v : Misskey . entities . DriveFile | Misskey . entities . DriveFolder ) : void ;
( ev : 'change-selection' , v : Misskey . entities . DriveFile [ ] | Misskey . entities . DriveFolder [ ] ) : void ;
( ev : 'move-root' ) : void ;
( ev : 'cd' , v : Misskey . entities . DriveFolder | null ) : void ;
( ev : 'open-folder' , v : Misskey . entities . DriveFolder ) : void ;
2022-01-18 23:06:16 +09:00
} > ( ) ;
2023-01-03 13:37:32 +09:00
const loadMoreFiles = shallowRef < InstanceType < typeof MkButton > > ( ) ;
2023-01-03 10:12:37 +09:00
const fileInput = shallowRef < HTMLInputElement > ( ) ;
2022-01-18 23:06:16 +09:00
const folder = ref < Misskey .entities.DriveFolder | null > ( null ) ;
const files = ref < Misskey .entities.DriveFile [ ] > ( [ ] ) ;
const folders = ref < Misskey .entities.DriveFolder [ ] > ( [ ] ) ;
const moreFiles = ref ( false ) ;
const moreFolders = ref ( false ) ;
const hierarchyFolders = ref < Misskey .entities.DriveFolder [ ] > ( [ ] ) ;
const selectedFiles = ref < Misskey .entities.DriveFile [ ] > ( [ ] ) ;
const selectedFolders = ref < Misskey .entities.DriveFolder [ ] > ( [ ] ) ;
2022-04-28 11:14:03 +09:00
const uploadings = uploads ;
2023-05-15 19:08:46 +09:00
const connection = useStream ( ) . useChannel ( 'drive' ) ;
2022-04-28 11:14:03 +09:00
const keepOriginal = ref < boolean > ( defaultStore . state . keepOriginalUploading ) ; // 外部渡しが多いので$refは使わないほうがよい
2022-01-18 23:06:16 +09:00
// ドロップされようとしているか
const draghover = ref ( false ) ;
// 自身の所有するアイテムがドラッグをスタートさせたか
// (自分自身の階層にドロップできないようにするためのフラグ)
const isDragSource = ref ( false ) ;
const fetching = ref ( true ) ;
2024-06-28 16:27:28 +02:00
async function search ( ) {
const query = searchQuery . value . toString ( ) . trim ( ) ;
fetch ( ) ;
return ;
}
2022-01-18 23:06:16 +09:00
const ilFilesObserver = new IntersectionObserver (
2022-07-15 19:15:23 +09:00
( entries ) => entries . some ( ( entry ) => entry . isIntersecting ) && ! fetching . value && moreFiles . value && fetchMoreFiles ( ) ,
2022-06-10 07:36:55 +02:00
) ;
2022-01-18 23:06:16 +09:00
watch ( folder , ( ) => emit ( 'cd' , folder . value ) ) ;
function onStreamDriveFileCreated ( file : Misskey . entities . DriveFile ) {
addFile ( file , true ) ;
}
function onStreamDriveFileUpdated ( file : Misskey . entities . DriveFile ) {
const current = folder . value ? folder . value . id : null ;
2022-05-05 13:45:50 +02:00
if ( current !== file . folderId ) {
2022-01-18 23:06:16 +09:00
removeFile ( file ) ;
} else {
addFile ( file , true ) ;
}
}
function onStreamDriveFileDeleted ( fileId : string ) {
removeFile ( fileId ) ;
}
function onStreamDriveFolderCreated ( createdFolder : Misskey . entities . DriveFolder ) {
addFolder ( createdFolder , true ) ;
}
function onStreamDriveFolderUpdated ( updatedFolder : Misskey . entities . DriveFolder ) {
const current = folder . value ? folder . value . id : null ;
2022-05-05 13:45:50 +02:00
if ( current !== updatedFolder . parentId ) {
2022-01-18 23:06:16 +09:00
removeFolder ( updatedFolder ) ;
} else {
addFolder ( updatedFolder , true ) ;
}
}
2020-05-31 12:53:06 +09:00
2022-01-18 23:06:16 +09:00
function onStreamDriveFolderDeleted ( folderId : string ) {
removeFolder ( folderId ) ;
}
2020-01-30 04:37:25 +09:00
2022-05-05 13:45:50 +02:00
function onDragover ( ev : DragEvent ) : any {
if ( ! ev . dataTransfer ) return ;
2020-01-30 04:37:25 +09:00
2022-01-18 23:06:16 +09:00
// ドラッグ元が自分自身の所有するアイテムだったら
if ( isDragSource . value ) {
// 自分自身にはドロップさせない
2022-05-05 13:45:50 +02:00
ev . dataTransfer . dropEffect = 'none' ;
2022-01-18 23:06:16 +09:00
return ;
}
2020-05-31 12:53:06 +09:00
2022-05-05 13:45:50 +02:00
const isFile = ev . dataTransfer . items [ 0 ] . kind === 'file' ;
const isDriveFile = ev . dataTransfer . types [ 0 ] === _DATA _TRANSFER _DRIVE _FILE _ ;
const isDriveFolder = ev . dataTransfer . types [ 0 ] === _DATA _TRANSFER _DRIVE _FOLDER _ ;
2022-01-18 23:06:16 +09:00
if ( isFile || isDriveFile || isDriveFolder ) {
2022-10-13 08:34:23 +09:00
switch ( ev . dataTransfer . effectAllowed ) {
case 'all' :
case 'uninitialized' :
2023-07-08 07:08:16 +09:00
case 'copy' :
case 'copyLink' :
case 'copyMove' :
2022-10-13 08:34:23 +09:00
ev . dataTransfer . dropEffect = 'copy' ;
break ;
case 'linkMove' :
case 'move' :
ev . dataTransfer . dropEffect = 'move' ;
break ;
default :
ev . dataTransfer . dropEffect = 'none' ;
break ;
}
2022-01-18 23:06:16 +09:00
} else {
2022-05-05 13:45:50 +02:00
ev . dataTransfer . dropEffect = 'none' ;
2022-01-18 23:06:16 +09:00
}
2018-02-14 15:54:18 +09:00
2022-01-18 23:06:16 +09:00
return false ;
}
2018-02-14 15:54:18 +09:00
2022-01-18 23:06:16 +09:00
function onDragenter ( ) {
if ( ! isDragSource . value ) draghover . value = true ;
}
function onDragleave ( ) {
draghover . value = false ;
}
2022-05-05 13:45:50 +02:00
function onDrop ( ev : DragEvent ) : any {
2022-01-18 23:06:16 +09:00
draghover . value = false ;
2020-01-30 04:37:25 +09:00
2022-05-05 13:45:50 +02:00
if ( ! ev . dataTransfer ) return ;
2022-01-18 23:06:16 +09:00
// ドロップされてきたものがファイルだったら
2022-05-05 13:45:50 +02:00
if ( ev . dataTransfer . files . length > 0 ) {
for ( const file of Array . from ( ev . dataTransfer . files ) ) {
2022-01-18 23:06:16 +09:00
upload ( file , folder . value ) ;
2020-05-31 12:53:06 +09:00
}
2022-01-18 23:06:16 +09:00
return ;
}
//#region ドライブのファイル
2022-05-05 13:45:50 +02:00
const driveFile = ev . dataTransfer . getData ( _DATA _TRANSFER _DRIVE _FILE _ ) ;
if ( driveFile != null && driveFile !== '' ) {
2022-01-18 23:06:16 +09:00
const file = JSON . parse ( driveFile ) ;
2022-05-05 13:45:50 +02:00
if ( files . value . some ( f => f . id === file . id ) ) return ;
2022-01-18 23:06:16 +09:00
removeFile ( file . id ) ;
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/files/update' , {
2022-01-18 23:06:16 +09:00
fileId : file . id ,
2022-07-15 19:15:23 +09:00
folderId : folder . value ? folder . value . id : null ,
2022-01-18 23:06:16 +09:00
} ) ;
}
//#endregion
//#region ドライブのフォルダ
2022-05-05 13:45:50 +02:00
const driveFolder = ev . dataTransfer . getData ( _DATA _TRANSFER _DRIVE _FOLDER _ ) ;
if ( driveFolder != null && driveFolder !== '' ) {
2022-01-18 23:06:16 +09:00
const droppedFolder = JSON . parse ( driveFolder ) ;
// 移動先が自分自身ならreject
2022-05-05 13:45:50 +02:00
if ( folder . value && droppedFolder . id === folder . value . id ) return false ;
if ( folders . value . some ( f => f . id === droppedFolder . id ) ) return false ;
2022-01-18 23:06:16 +09:00
removeFolder ( droppedFolder . id ) ;
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/folders/update' , {
2022-01-18 23:06:16 +09:00
folderId : droppedFolder . id ,
2022-07-15 19:15:23 +09:00
parentId : folder . value ? folder . value . id : null ,
2022-01-18 23:06:16 +09:00
} ) . then ( ( ) => {
// noop
} ) . catch ( err => {
2023-01-21 13:14:55 +09:00
switch ( err . code ) {
case 'RECURSIVE_NESTING' :
claimAchievement ( 'driveFolderCircularReference' ) ;
2022-01-18 23:06:16 +09:00
os . alert ( {
2023-01-21 13:14:55 +09:00
type : 'error' ,
2022-01-28 11:39:49 +09:00
title : i18n . ts . unableToProcess ,
2022-07-15 19:15:23 +09:00
text : i18n . ts . circularReferenceFolder ,
2022-01-18 23:06:16 +09:00
} ) ;
break ;
default :
os . alert ( {
type : 'error' ,
2022-07-15 19:15:23 +09:00
text : i18n . ts . somethingHappened ,
2022-01-18 23:06:16 +09:00
} ) ;
2017-02-21 09:49:35 +09:00
}
2022-01-18 23:06:16 +09:00
} ) ;
}
//#endregion
}
function selectLocalFile ( ) {
fileInput . value ? . click ( ) ;
}
function urlUpload ( ) {
os . inputText ( {
2022-01-28 11:39:49 +09:00
title : i18n . ts . uploadFromUrl ,
2022-01-18 23:06:16 +09:00
type : 'url' ,
2022-07-15 19:15:23 +09:00
placeholder : i18n . ts . uploadFromUrlDescription ,
2022-01-18 23:06:16 +09:00
} ) . then ( ( { canceled , result : url } ) => {
if ( canceled || ! url ) return ;
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/files/upload-from-url' , {
2022-01-18 23:06:16 +09:00
url : url ,
2022-07-15 19:15:23 +09:00
folderId : folder . value ? folder . value . id : undefined ,
2022-01-18 23:06:16 +09:00
} ) ;
os . alert ( {
2022-01-28 11:39:49 +09:00
title : i18n . ts . uploadFromUrlRequested ,
2022-07-15 19:15:23 +09:00
text : i18n . ts . uploadFromUrlMayTakeTime ,
2022-01-18 23:06:16 +09:00
} ) ;
} ) ;
}
function createFolder ( ) {
os . inputText ( {
2022-01-28 11:39:49 +09:00
title : i18n . ts . createFolder ,
2022-07-15 19:15:23 +09:00
placeholder : i18n . ts . folderName ,
2022-01-18 23:06:16 +09:00
} ) . then ( ( { canceled , result : name } ) => {
if ( canceled ) return ;
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/folders/create' , {
2022-01-18 23:06:16 +09:00
name : name ,
2022-07-15 19:15:23 +09:00
parentId : folder . value ? folder . value . id : undefined ,
2022-01-18 23:06:16 +09:00
} ) . then ( createdFolder => {
addFolder ( createdFolder , true ) ;
} ) ;
} ) ;
}
function renameFolder ( folderToRename : Misskey . entities . DriveFolder ) {
os . inputText ( {
2022-01-28 11:39:49 +09:00
title : i18n . ts . renameFolder ,
placeholder : i18n . ts . inputNewFolderName ,
2022-07-15 19:15:23 +09:00
default : folderToRename . name ,
2022-01-18 23:06:16 +09:00
} ) . then ( ( { canceled , result : name } ) => {
if ( canceled ) return ;
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/folders/update' , {
2022-01-18 23:06:16 +09:00
folderId : folderToRename . id ,
2022-07-15 19:15:23 +09:00
name : name ,
2022-01-18 23:06:16 +09:00
} ) . then ( updatedFolder => {
// FIXME: 画面を更新するために自分自身に移動
move ( updatedFolder ) ;
} ) ;
} ) ;
}
function deleteFolder ( folderToDelete : Misskey . entities . DriveFolder ) {
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/folders/delete' , {
2022-07-15 19:15:23 +09:00
folderId : folderToDelete . id ,
2022-01-18 23:06:16 +09:00
} ) . then ( ( ) => {
// 削除時に親フォルダに移動
move ( folderToDelete . parentId ) ;
} ) . catch ( err => {
2022-06-10 07:36:55 +02:00
switch ( err . id ) {
2022-01-18 23:06:16 +09:00
case 'b0fc8a17-963c-405d-bfbc-859a487295e1' :
os . alert ( {
type : 'error' ,
2022-01-28 11:39:49 +09:00
title : i18n . ts . unableToDelete ,
2022-07-15 19:15:23 +09:00
text : i18n . ts . hasChildFilesOrFolders ,
2022-01-18 23:06:16 +09:00
} ) ;
break ;
default :
os . alert ( {
type : 'error' ,
2022-07-15 19:15:23 +09:00
text : i18n . ts . unableToDelete ,
2022-01-18 23:06:16 +09:00
} ) ;
2022-07-15 19:15:23 +09:00
}
2022-01-18 23:06:16 +09:00
} ) ;
}
2018-02-27 06:25:17 +09:00
2022-01-18 23:06:16 +09:00
function onChangeFileInput ( ) {
if ( ! fileInput . value ? . files ) return ;
for ( const file of Array . from ( fileInput . value . files ) ) {
upload ( file , folder . value ) ;
}
}
function upload ( file : File , folderToUpload ? : Misskey . entities . DriveFolder | null ) {
2022-05-05 13:45:50 +02:00
uploadFile ( file , ( folderToUpload && typeof folderToUpload === 'object' ) ? folderToUpload . id : null , undefined , keepOriginal . value ) . then ( res => {
2022-01-18 23:06:16 +09:00
addFile ( res , true ) ;
} ) ;
}
function chooseFile ( file : Misskey . entities . DriveFile ) {
2022-05-05 13:45:50 +02:00
const isAlreadySelected = selectedFiles . value . some ( f => f . id === file . id ) ;
2022-01-18 23:06:16 +09:00
if ( props . multiple ) {
if ( isAlreadySelected ) {
2022-05-05 13:45:50 +02:00
selectedFiles . value = selectedFiles . value . filter ( f => f . id !== file . id ) ;
2022-01-18 23:06:16 +09:00
} else {
selectedFiles . value . push ( file ) ;
}
emit ( 'change-selection' , selectedFiles . value ) ;
} else {
if ( isAlreadySelected ) {
emit ( 'selected' , file ) ;
} else {
selectedFiles . value = [ file ] ;
emit ( 'change-selection' , [ file ] ) ;
}
}
}
2018-02-27 06:25:17 +09:00
2022-01-18 23:06:16 +09:00
function chooseFolder ( folderToChoose : Misskey . entities . DriveFolder ) {
2022-05-05 13:45:50 +02:00
const isAlreadySelected = selectedFolders . value . some ( f => f . id === folderToChoose . id ) ;
2022-01-18 23:06:16 +09:00
if ( props . multiple ) {
if ( isAlreadySelected ) {
2022-05-05 13:45:50 +02:00
selectedFolders . value = selectedFolders . value . filter ( f => f . id !== folderToChoose . id ) ;
2022-01-18 23:06:16 +09:00
} else {
selectedFolders . value . push ( folderToChoose ) ;
}
emit ( 'change-selection' , selectedFolders . value ) ;
} else {
if ( isAlreadySelected ) {
emit ( 'selected' , folderToChoose ) ;
} else {
selectedFolders . value = [ folderToChoose ] ;
emit ( 'change-selection' , [ folderToChoose ] ) ;
}
}
}
2024-01-30 19:53:53 +09:00
function move ( target ? : Misskey . entities . DriveFolder | Misskey . entities . DriveFolder [ 'id' | 'parentId' ] ) {
2022-01-18 23:06:16 +09:00
if ( ! target ) {
goRoot ( ) ;
return ;
2022-05-05 13:45:50 +02:00
} else if ( typeof target === 'object' ) {
2022-01-18 23:06:16 +09:00
target = target . id ;
}
2018-02-27 06:35:16 +09:00
2022-01-18 23:06:16 +09:00
fetching . value = true ;
2018-02-18 12:35:18 +09:00
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/folders/show' , {
2022-07-15 19:15:23 +09:00
folderId : target ,
2022-01-18 23:06:16 +09:00
} ) . then ( folderToMove => {
folder . value = folderToMove ;
hierarchyFolders . value = [ ] ;
2018-02-18 12:35:18 +09:00
2022-01-18 23:06:16 +09:00
const dive = folderToDive => {
hierarchyFolders . value . unshift ( folderToDive ) ;
if ( folderToDive . parent ) dive ( folderToDive . parent ) ;
} ;
2018-02-18 12:35:18 +09:00
2022-01-18 23:06:16 +09:00
if ( folderToMove . parent ) dive ( folderToMove . parent ) ;
2017-01-12 05:55:38 +09:00
2022-01-18 23:06:16 +09:00
emit ( 'open-folder' , folderToMove ) ;
fetch ( ) ;
} ) ;
}
2017-01-12 05:55:38 +09:00
2022-01-18 23:06:16 +09:00
function addFolder ( folderToAdd : Misskey . entities . DriveFolder , unshift = false ) {
const current = folder . value ? folder . value . id : null ;
2022-05-05 13:45:50 +02:00
if ( current !== folderToAdd . parentId ) return ;
2017-02-17 05:46:14 +09:00
2022-05-05 13:45:50 +02:00
if ( folders . value . some ( f => f . id === folderToAdd . id ) ) {
2022-01-18 23:06:16 +09:00
const exist = folders . value . map ( f => f . id ) . indexOf ( folderToAdd . id ) ;
folders . value [ exist ] = folderToAdd ;
return ;
}
2020-02-08 13:09:38 +09:00
2022-01-18 23:06:16 +09:00
if ( unshift ) {
folders . value . unshift ( folderToAdd ) ;
} else {
folders . value . push ( folderToAdd ) ;
}
}
2017-02-21 09:49:35 +09:00
2022-01-18 23:06:16 +09:00
function addFile ( fileToAdd : Misskey . entities . DriveFile , unshift = false ) {
const current = folder . value ? folder . value . id : null ;
2022-05-05 13:45:50 +02:00
if ( current !== fileToAdd . folderId ) return ;
2016-12-29 07:49:51 +09:00
2022-05-05 13:45:50 +02:00
if ( files . value . some ( f => f . id === fileToAdd . id ) ) {
2022-01-18 23:06:16 +09:00
const exist = files . value . map ( f => f . id ) . indexOf ( fileToAdd . id ) ;
files . value [ exist ] = fileToAdd ;
return ;
}
2016-12-29 07:49:51 +09:00
2022-01-18 23:06:16 +09:00
if ( unshift ) {
files . value . unshift ( fileToAdd ) ;
} else {
files . value . push ( fileToAdd ) ;
}
}
function removeFolder ( folderToRemove : Misskey . entities . DriveFolder | string ) {
const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove . id : folderToRemove ;
2022-05-05 13:45:50 +02:00
folders . value = folders . value . filter ( f => f . id !== folderIdToRemove ) ;
2022-01-18 23:06:16 +09:00
}
function removeFile ( file : Misskey . entities . DriveFile | string ) {
const fileId = typeof file === 'object' ? file . id : file ;
2022-05-05 13:45:50 +02:00
files . value = files . value . filter ( f => f . id !== fileId ) ;
2022-01-18 23:06:16 +09:00
}
function appendFile ( file : Misskey . entities . DriveFile ) {
addFile ( file ) ;
}
function appendFolder ( folderToAppend : Misskey . entities . DriveFolder ) {
addFolder ( folderToAppend ) ;
}
2023-10-29 16:09:20 +09:00
2022-01-18 23:06:16 +09:00
/ *
function prependFile ( file : Misskey . entities . DriveFile ) {
addFile ( file , true ) ;
}
function prependFolder ( folderToPrepend : Misskey . entities . DriveFolder ) {
addFolder ( folderToPrepend , true ) ;
}
* /
function goRoot ( ) {
// 既にrootにいるなら何もしない
if ( folder . value == null ) return ;
folder . value = null ;
hierarchyFolders . value = [ ] ;
emit ( 'move-root' ) ;
fetch ( ) ;
}
async function fetch ( ) {
folders . value = [ ] ;
files . value = [ ] ;
moreFolders . value = false ;
moreFiles . value = false ;
fetching . value = true ;
const foldersMax = 30 ;
const filesMax = 30 ;
2024-01-04 18:32:46 +09:00
const foldersPromise = misskeyApi ( 'drive/folders' , {
2022-01-18 23:06:16 +09:00
folderId : folder . value ? folder . value . id : null ,
2022-07-15 19:15:23 +09:00
limit : foldersMax + 1 ,
2024-06-28 16:27:28 +02:00
searchQuery : searchQuery . value . toString ( ) . trim ( ) ,
2022-01-18 23:06:16 +09:00
} ) . then ( fetchedFolders => {
2022-05-05 13:45:50 +02:00
if ( fetchedFolders . length === foldersMax + 1 ) {
2022-01-18 23:06:16 +09:00
moreFolders . value = true ;
fetchedFolders . pop ( ) ;
}
return fetchedFolders ;
} ) ;
2024-01-04 18:32:46 +09:00
const filesPromise = misskeyApi ( 'drive/files' , {
2022-01-18 23:06:16 +09:00
folderId : folder . value ? folder . value . id : null ,
type : props . type ,
2022-07-15 19:15:23 +09:00
limit : filesMax + 1 ,
2024-06-28 16:27:28 +02:00
searchQuery : searchQuery . value . toString ( ) . trim ( ) ,
2022-01-18 23:06:16 +09:00
} ) . then ( fetchedFiles => {
2022-05-05 13:45:50 +02:00
if ( fetchedFiles . length === filesMax + 1 ) {
2022-01-18 23:06:16 +09:00
moreFiles . value = true ;
fetchedFiles . pop ( ) ;
}
return fetchedFiles ;
} ) ;
2016-12-29 07:49:51 +09:00
2022-01-18 23:06:16 +09:00
const [ fetchedFolders , fetchedFiles ] = await Promise . all ( [ foldersPromise , filesPromise ] ) ;
2017-01-12 05:55:38 +09:00
2022-01-18 23:06:16 +09:00
for ( const x of fetchedFolders ) appendFolder ( x ) ;
for ( const x of fetchedFiles ) appendFile ( x ) ;
2018-02-18 12:35:18 +09:00
2022-01-18 23:06:16 +09:00
fetching . value = false ;
}
2017-01-12 05:55:38 +09:00
2023-07-08 17:48:10 +09:00
function fetchMoreFolders ( ) {
fetching . value = true ;
const max = 30 ;
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/folders' , {
2023-07-08 17:48:10 +09:00
folderId : folder . value ? folder . value . id : null ,
type : props . type ,
2023-07-14 10:45:01 +09:00
untilId : folders . value . at ( - 1 ) ? . id ,
2023-07-08 17:48:10 +09:00
limit : max + 1 ,
} ) . then ( folders => {
if ( folders . length === max + 1 ) {
moreFolders . value = true ;
folders . pop ( ) ;
} else {
moreFolders . value = false ;
}
for ( const x of folders ) appendFolder ( x ) ;
fetching . value = false ;
} ) ;
}
2022-01-18 23:06:16 +09:00
function fetchMoreFiles ( ) {
fetching . value = true ;
2017-01-12 05:55:38 +09:00
2022-01-18 23:06:16 +09:00
const max = 30 ;
2018-02-18 12:35:18 +09:00
2022-01-18 23:06:16 +09:00
// ファイル一覧取得
2024-01-04 18:32:46 +09:00
misskeyApi ( 'drive/files' , {
2022-01-18 23:06:16 +09:00
folderId : folder . value ? folder . value . id : null ,
type : props . type ,
2023-07-14 10:45:01 +09:00
untilId : files . value . at ( - 1 ) ? . id ,
2022-07-15 19:15:23 +09:00
limit : max + 1 ,
2022-01-18 23:06:16 +09:00
} ) . then ( files => {
2022-05-05 13:45:50 +02:00
if ( files . length === max + 1 ) {
2022-01-18 23:06:16 +09:00
moreFiles . value = true ;
files . pop ( ) ;
} else {
moreFiles . value = false ;
}
for ( const x of files ) appendFile ( x ) ;
fetching . value = false ;
} ) ;
}
function getMenu ( ) {
2024-01-30 19:53:53 +09:00
const menu : MenuItem [ ] = [ {
2022-04-28 11:14:03 +09:00
type : 'switch' ,
text : i18n . ts . keepOriginalUploading ,
ref : keepOriginal ,
2023-12-12 10:26:37 +09:00
} , { type : 'divider' } , {
2022-01-28 11:39:49 +09:00
text : i18n . ts . addFile ,
2022-07-15 19:15:23 +09:00
type : 'label' ,
2022-01-18 23:06:16 +09:00
} , {
2022-01-28 11:39:49 +09:00
text : i18n . ts . upload ,
2023-09-30 21:53:52 +02:00
icon : 'ph-upload ph-bold ph-lg' ,
2022-07-15 19:15:23 +09:00
action : ( ) => { selectLocalFile ( ) ; } ,
2022-01-18 23:06:16 +09:00
} , {
2022-01-28 11:39:49 +09:00
text : i18n . ts . fromUrl ,
2023-09-30 21:53:52 +02:00
icon : 'ph-link ph-bold ph-lg' ,
2022-07-15 19:15:23 +09:00
action : ( ) => { urlUpload ( ) ; } ,
2023-12-12 10:26:37 +09:00
} , { type : 'divider' } , {
2022-01-28 11:39:49 +09:00
text : folder . value ? folder . value . name : i18n . ts . drive ,
2022-07-15 19:15:23 +09:00
type : 'label' ,
2022-01-18 23:06:16 +09:00
} , folder . value ? {
2022-01-28 11:39:49 +09:00
text : i18n . ts . renameFolder ,
2023-09-30 21:53:52 +02:00
icon : 'ph-textbox ph-bold ph-lg' ,
2024-01-30 19:53:53 +09:00
action : ( ) => { if ( folder . value ) renameFolder ( folder . value ) ; } ,
2022-01-18 23:06:16 +09:00
} : undefined , folder . value ? {
2022-01-28 11:39:49 +09:00
text : i18n . ts . deleteFolder ,
2023-09-30 21:53:52 +02:00
icon : 'ph-trash ph-bold ph-lg' ,
2022-07-15 19:15:23 +09:00
action : ( ) => { deleteFolder ( folder . value as Misskey . entities . DriveFolder ) ; } ,
2022-01-18 23:06:16 +09:00
} : undefined , {
2022-01-28 11:39:49 +09:00
text : i18n . ts . createFolder ,
2023-09-30 21:53:52 +02:00
icon : 'ph-folder ph-bold ph-lg-plus' ,
2022-07-15 19:15:23 +09:00
action : ( ) => { createFolder ( ) ; } ,
2022-01-18 23:06:16 +09:00
} ] ;
2024-01-30 19:53:53 +09:00
return menu ;
2022-01-18 23:06:16 +09:00
}
function showMenu ( ev : MouseEvent ) {
2022-01-28 12:20:42 +09:00
os . popupMenu ( getMenu ( ) , ( ev . currentTarget ? ? ev . target ? ? undefined ) as HTMLElement | undefined ) ;
2022-01-18 23:06:16 +09:00
}
function onContextmenu ( ev : MouseEvent ) {
os . contextMenu ( getMenu ( ) , ev ) ;
}
onMounted ( ( ) => {
if ( defaultStore . state . enableInfiniteScroll && loadMoreFiles . value ) {
nextTick ( ( ) => {
2022-06-10 07:36:55 +02:00
ilFilesObserver . observe ( loadMoreFiles . value ? . $el ) ;
2022-01-18 23:06:16 +09:00
} ) ;
}
2017-01-12 05:55:38 +09:00
2022-01-18 23:06:16 +09:00
connection . on ( 'fileCreated' , onStreamDriveFileCreated ) ;
connection . on ( 'fileUpdated' , onStreamDriveFileUpdated ) ;
connection . on ( 'fileDeleted' , onStreamDriveFileDeleted ) ;
connection . on ( 'folderCreated' , onStreamDriveFolderCreated ) ;
connection . on ( 'folderUpdated' , onStreamDriveFolderUpdated ) ;
connection . on ( 'folderDeleted' , onStreamDriveFolderDeleted ) ;
if ( props . initialFolder ) {
move ( props . initialFolder ) ;
} else {
fetch ( ) ;
}
} ) ;
2016-12-29 07:49:51 +09:00
2022-01-18 23:06:16 +09:00
onActivated ( ( ) => {
if ( defaultStore . state . enableInfiniteScroll ) {
nextTick ( ( ) => {
2022-06-10 07:36:55 +02:00
ilFilesObserver . observe ( loadMoreFiles . value ? . $el ) ;
2022-01-18 23:06:16 +09:00
} ) ;
2018-02-14 15:54:18 +09:00
}
} ) ;
2022-01-18 23:06:16 +09:00
onBeforeUnmount ( ( ) => {
connection . dispose ( ) ;
ilFilesObserver . disconnect ( ) ;
} ) ;
2018-02-14 15:54:18 +09:00
< / script >
2023-05-29 19:56:17 +09:00
< style lang = "scss" module >
. root {
2020-11-03 10:27:00 +09:00
display : flex ;
flex - direction : column ;
height : 100 % ;
2023-05-29 19:56:17 +09:00
}
2020-11-03 10:27:00 +09:00
2023-05-29 19:56:17 +09:00
. nav {
display : flex ;
z - index : 2 ;
width : 100 % ;
padding : 0 8 px ;
box - sizing : border - box ;
overflow : auto ;
font - size : 0.9 em ;
box - shadow : 0 1 px 0 var ( -- divider ) ;
user - select : none ;
}
. navPath {
display : inline - block ;
vertical - align : bottom ;
line - height : 42 px ;
white - space : nowrap ;
}
. navPathItem {
display : inline - block ;
margin : 0 ;
padding : 0 8 px ;
line - height : 42 px ;
cursor : pointer ;
& : hover {
text - decoration : underline ;
}
2020-01-30 04:37:25 +09:00
2023-05-29 19:56:17 +09:00
& . navCurrent {
font - weight : bold ;
cursor : default ;
2021-07-29 17:10:16 +09:00
2023-05-29 19:56:17 +09:00
& : hover {
text - decoration : none ;
2021-07-29 17:10:16 +09:00
}
2020-01-30 04:37:25 +09:00
}
2023-05-29 19:56:17 +09:00
& . navSeparator {
margin : 0 ;
padding : 0 ;
opacity : 0.5 ;
cursor : default ;
}
}
2018-02-14 15:54:18 +09:00
2023-05-29 19:56:17 +09:00
. navMenu {
margin - left : auto ;
padding : 0 12 px ;
}
2020-01-30 04:37:25 +09:00
2023-05-29 19:56:17 +09:00
. main {
flex : 1 ;
overflow : auto ;
padding : var ( -- margin ) ;
user - select : none ;
2020-01-30 04:37:25 +09:00
2023-05-29 19:56:17 +09:00
& . fetching {
cursor : wait ! important ;
opacity : 0.5 ;
pointer - events : none ;
}
2020-01-30 04:37:25 +09:00
2023-05-29 19:56:17 +09:00
& . uploading {
height : calc ( 100 % - 38 px - 100 px ) ;
}
}
2020-01-30 04:37:25 +09:00
2023-05-29 19:56:17 +09:00
. folders ,
. files {
display : flex ;
flex - wrap : wrap ;
}
2018-02-14 15:54:18 +09:00
2023-05-29 19:56:17 +09:00
. folder ,
. file {
flex - grow : 1 ;
width : 128 px ;
margin : 4 px ;
box - sizing : border - box ;
}
2018-02-14 15:54:18 +09:00
2023-05-29 19:56:17 +09:00
. padding {
flex - grow : 1 ;
pointer - events : none ;
width : 128 px + 8 px ;
}
2018-02-14 15:54:18 +09:00
2023-05-29 19:56:17 +09:00
. empty {
padding : 16 px ;
text - align : center ;
pointer - events : none ;
opacity : 0.5 ;
}
2020-01-30 04:37:25 +09:00
2023-05-29 19:56:17 +09:00
. dropzone {
position : absolute ;
left : 0 ;
top : 38 px ;
width : 100 % ;
height : calc ( 100 % - 38 px ) ;
border : dashed 2 px var ( -- focus ) ;
pointer - events : none ;
2020-01-30 04:37:25 +09:00
}
2018-02-14 15:54:18 +09:00
< / style >