client refactor: use pagination in drive component

Squashed commit of the following:

commit 8636adab6455bea29659a6799a7f3aad9e7cc10d
Author: Johann150 <johann.galle@protonmail.com>
Date:   Mon Oct 17 22:53:24 2022 +0200

    fix: remove comment

commit 7ff8d45bfa2ed5c07c9a053e817604ef2eb115ad
Author: Johann150 <johann.galle@protonmail.com>
Date:   Mon Oct 17 21:55:48 2022 +0200

    fix paginations reloading

    The Pagination type actually specifies that just the params property
    should be a Ref.

commit 55fe9210c15785611603e3a7a2535ebf8008ea64
Author: Johann150 <johann.galle@protonmail.com>
Date:   Mon Oct 17 18:55:54 2022 +0200

    fix variable name

commit a464d1363bc8c62606a4d2acc148ce269973bede
Author: Johann150 <johann.galle@protonmail.com>
Date:   Sun Oct 16 22:36:11 2022 +0200

    fix: don't display empty drive message while loading

commit 52905b398f683ff3c71c2d5592851b2d2a428550
Author: Johann150 <johann.galle@protonmail.com>
Date:   Fri Oct 14 22:19:13 2022 +0200

    remove unavailable i18n strings

commit d491a71cbec05f991864a06b8e0001d40da006a3
Author: Johann150 <johann.galle@protonmail.com>
Date:   Fri Oct 14 22:18:42 2022 +0200

    client refactor: use pagination in drive component

    This majorly refactors the drive component to use the proper pagination
    component instead of reimplementing pagination.

    The drive component is also refactored to use ref sugar (i.e. $ref).
This commit is contained in:
Johann150 2022-10-17 22:58:12 +02:00
parent 04d4dd323f
commit f4ee8b321e
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1
2 changed files with 184 additions and 250 deletions

View file

@ -28,7 +28,7 @@
</nav> </nav>
<div <div
ref="main" class="main" ref="main" class="main"
:class="{ uploading: uploadings.length > 0, fetching }" :class="{ uploading: uploadings.length > 0 }"
@dragover.prevent.stop="onDragover" @dragover.prevent.stop="onDragover"
@dragenter="onDragenter" @dragenter="onDragenter"
@dragleave="onDragleave" @dragleave="onDragleave"
@ -36,51 +36,77 @@
@contextmenu.stop="onContextmenu" @contextmenu.stop="onContextmenu"
> >
<div ref="contents" class="contents"> <div ref="contents" class="contents">
<div v-show="folders.length > 0" ref="foldersContainer" class="folders"> <MkPagination
<XFolder ref="foldersPaginationElem"
v-for="(f, i) in folders" :pagination="foldersPagination"
:key="f.id" class="folders"
v-anim="i" @loaded="foldersLoading = false"
class="folder" >
:folder="f" <template #empty>
:select-mode="select === 'folder'" <!--
:is-selected="selectedFolders.some(x => x.id === f.id)" Don't display anything here if there are no folders,
@chosen="chooseFolder" there is a separate check if both paginations are empty.
@move="move" -->
@upload="upload" {{ null }}
@removeFile="removeFile" </template>
@removeFolder="removeFolder"
@dragstart="isDragSource = true" <template #default="{ items: folders }">
@dragend="isDragSource = false" <XFolder
/> v-for="(f, i) in folders"
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> :key="f.id"
<div v-for="(n, i) in 16" :key="i" class="padding"></div> v-anim="i"
<MkButton v-if="moreFolders" ref="moreFolders">{{ i18n.ts.loadMore }}</MkButton> class="folder"
</div> :folder="f"
<div v-show="files.length > 0" ref="filesContainer" class="files"> :select-mode="select === 'folder'"
<XFile :is-selected="selectedFolders.some(x => x.id === f.id)"
v-for="(file, i) in files" @chosen="chooseFolder"
:key="file.id" @move="move"
v-anim="i" @upload="upload"
class="file" @removeFile="removeFile"
:file="file" @removeFolder="removeFolder"
:select-mode="select === 'file'" @dragstart="isDragSource = true"
:is-selected="selectedFiles.some(x => x.id === file.id)" @dragend="isDragSource = false"
@chosen="chooseFile" />
@dragstart="isDragSource = true" <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
@dragend="isDragSource = false" <div v-for="(n, i) in 16" :key="i" class="padding"></div>
/> </template>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> </MkPagination>
<div v-for="(n, i) in 16" :key="i" class="padding"></div> <MkPagination
<MkButton v-show="moreFiles" ref="loadMoreFiles" @click="fetchMoreFiles">{{ i18n.ts.loadMore }}</MkButton> ref="filesPaginationElem"
</div> :pagination="filesPagination"
<div v-if="files.length == 0 && folders.length == 0 && !fetching" class="empty"> class="files"
<p v-if="draghover">{{ i18n.t('empty-draghover') }}</p> @loaded="filesLoading = false"
<p v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong><br/>{{ i18n.t('empty-drive-description') }}</p> >
<p v-if="!draghover && folder != null">{{ i18n.ts.emptyFolder }}</p> <template #empty>
<!--
Don't display anything here if there are no files,
there is a separate check if both paginations are empty.
-->
{{ null }}
</template>
<template #default="{ items: files }">
<XFile
v-for="(file, i) in files"
:key="file.id"
v-anim="i"
class="file"
:file="file"
:select-mode="select === 'file'"
:is-selected="selectedFiles.some(x => x.id === file.id)"
@chosen="chooseFile"
@dragstart="isDragSource = true"
@dragend="isDragSource = false"
/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div v-for="(n, i) in 16" :key="i" class="padding"></div>
</template>
</MkPagination>
<div v-if="empty" class="empty">
<p v-if="folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong></p>
<p v-else>{{ i18n.ts.emptyFolder }}</p>
</div> </div>
</div> </div>
<MkLoading v-if="fetching"/>
</div> </div>
<div v-if="draghover" class="dropzone"></div> <div v-if="draghover" class="dropzone"></div>
<input ref="fileInput" type="file" accept="*/*" multiple tabindex="-1" @change="onChangeFileInput"/> <input ref="fileInput" type="file" accept="*/*" multiple tabindex="-1" @change="onChangeFileInput"/>
@ -88,12 +114,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import { computed, onBeforeUnmount, onMounted, watch } from 'vue';
import * as foundkey from 'foundkey-js'; import * as foundkey from 'foundkey-js';
import XNavFolder from './drive.nav-folder.vue'; import XNavFolder from './drive.nav-folder.vue';
import XFolder from './drive.folder.vue'; import XFolder from './drive.folder.vue';
import XFile from './drive.file.vue'; import XFile from './drive.file.vue';
import MkButton from './ui/button.vue'; import MkButton from './ui/button.vue';
import MkPagination from './ui/pagination.vue';
import * as os from '@/os'; import * as os from '@/os';
import { stream } from '@/stream'; import { stream } from '@/stream';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
@ -118,42 +145,40 @@ const emit = defineEmits<{
(ev: 'open-folder', v: foundkey.entities.DriveFolder): void; (ev: 'open-folder', v: foundkey.entities.DriveFolder): void;
}>(); }>();
const loadMoreFiles = ref<InstanceType<typeof MkButton>>(); let foldersPaginationElem = $ref<InstanceType<typeof MkPagination>>();
const fileInput = ref<HTMLInputElement>(); let filesPaginationElem = $ref<InstanceType<typeof MkPagination>>();
let foldersLoading = $ref<boolean>(true);
let filesLoading = $ref<boolean>(true);
const empty = $computed(() => !foldersLoading && !filesLoading
&& foldersPaginationElem?.items.length === 0 && filesPaginationElem?.items.length === 0);
let fileInput = $ref<HTMLInputElement>();
const folder = ref<foundkey.entities.DriveFolder | null>(null);
const files = ref<foundkey.entities.DriveFile[]>([]);
const folders = ref<foundkey.entities.DriveFolder[]>([]);
const moreFiles = ref(false);
const moreFolders = ref(false);
const hierarchyFolders = ref<foundkey.entities.DriveFolder[]>([]);
const selectedFiles = ref<foundkey.entities.DriveFile[]>([]);
const selectedFolders = ref<foundkey.entities.DriveFolder[]>([]);
const uploadings = uploads; const uploadings = uploads;
const connection = stream.useChannel('drive'); const connection = stream.useChannel('drive');
const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // $ref使
let folder = $ref<foundkey.entities.DriveFolder | null>(null);
let hierarchyFolders = $ref<foundkey.entities.DriveFolder[]>([]);
let selectedFiles = $ref<foundkey.entities.DriveFile[]>([]);
let selectedFolders = $ref<foundkey.entities.DriveFolder[]>([]);
let keepOriginal = $ref<boolean>(defaultStore.state.keepOriginalUploading);
// //
const draghover = ref(false); let draghover = $ref(false);
// //
// () // ()
const isDragSource = ref(false); let isDragSource = $ref(false);
const fetching = ref(true); watch($$(folder), () => emit('cd', folder));
const ilFilesObserver = new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(),
);
watch(folder, () => emit('cd', folder.value));
function onStreamDriveFileCreated(file: foundkey.entities.DriveFile) { function onStreamDriveFileCreated(file: foundkey.entities.DriveFile) {
addFile(file, true); addFile(file, true);
} }
function onStreamDriveFileUpdated(file: foundkey.entities.DriveFile) { function onStreamDriveFileUpdated(file: foundkey.entities.DriveFile) {
const current = folder.value ? folder.value.id : null; const current = folder?.id ?? null;
if (current !== file.folderId) { if (current !== file.folderId) {
removeFile(file); removeFile(file);
} else { } else {
@ -170,7 +195,7 @@ function onStreamDriveFolderCreated(createdFolder: foundkey.entities.DriveFolder
} }
function onStreamDriveFolderUpdated(updatedFolder: foundkey.entities.DriveFolder) { function onStreamDriveFolderUpdated(updatedFolder: foundkey.entities.DriveFolder) {
const current = folder.value ? folder.value.id : null; const current = folder?.id ?? null;
if (current !== updatedFolder.parentId) { if (current !== updatedFolder.parentId) {
removeFolder(updatedFolder); removeFolder(updatedFolder);
} else { } else {
@ -186,7 +211,7 @@ function onDragover(ev: DragEvent): any {
if (!ev.dataTransfer) return; if (!ev.dataTransfer) return;
// //
if (isDragSource.value) { if (isDragSource) {
// //
ev.dataTransfer.dropEffect = 'none'; ev.dataTransfer.dropEffect = 'none';
return; return;
@ -205,22 +230,22 @@ function onDragover(ev: DragEvent): any {
} }
function onDragenter() { function onDragenter() {
if (!isDragSource.value) draghover.value = true; if (!isDragSource) draghover = true;
} }
function onDragleave() { function onDragleave() {
draghover.value = false; draghover = false;
} }
function onDrop(ev: DragEvent): any { function onDrop(ev: DragEvent): any {
draghover.value = false; draghover = false;
if (!ev.dataTransfer) return; if (!ev.dataTransfer) return;
// //
if (ev.dataTransfer.files.length > 0) { if (ev.dataTransfer.files.length > 0) {
for (const file of Array.from(ev.dataTransfer.files)) { for (const file of Array.from(ev.dataTransfer.files)) {
upload(file, folder.value); upload(file, folder);
} }
return; return;
} }
@ -229,11 +254,14 @@ function onDrop(ev: DragEvent): any {
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
if (driveFile != null && driveFile !== '') { if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile); const file = JSON.parse(driveFile);
if (files.value.some(f => f.id === file.id)) return;
// cannot move file within parent folder
if (folder.id === file.folderId) return;
removeFile(file.id); removeFile(file.id);
os.api('drive/files/update', { os.api('drive/files/update', {
fileId: file.id, fileId: file.id,
folderId: folder.value ? folder.value.id : null, folderId: folder?.id ?? null,
}); });
} }
//#endregion //#endregion
@ -243,15 +271,15 @@ function onDrop(ev: DragEvent): any {
if (driveFolder != null && driveFolder !== '') { if (driveFolder != null && driveFolder !== '') {
const droppedFolder = JSON.parse(driveFolder); const droppedFolder = JSON.parse(driveFolder);
// reject // cannot move folder into itself
if (folder.value && droppedFolder.id === folder.value.id) return false; if (droppedFolder.id === folder?.id) return false;
if (folders.value.some(f => f.id === droppedFolder.id)) return false; // cannot move folder within parent folder
if (foldersPaginationElem.items.some(f => f.id === droppedFolder.id)) return false;
removeFolder(droppedFolder.id); removeFolder(droppedFolder.id);
os.api('drive/folders/update', { os.api('drive/folders/update', {
folderId: droppedFolder.id, folderId: droppedFolder.id,
parentId: folder.value ? folder.value.id : null, parentId: folder?.id ?? null,
}).then(() => {
// noop
}).catch(err => { }).catch(err => {
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
@ -272,7 +300,7 @@ function onDrop(ev: DragEvent): any {
} }
function selectLocalFile() { function selectLocalFile() {
fileInput.value?.click(); fileInput?.click();
} }
function urlUpload() { function urlUpload() {
@ -284,7 +312,7 @@ function urlUpload() {
if (canceled || !url) return; if (canceled || !url) return;
os.api('drive/files/upload-from-url', { os.api('drive/files/upload-from-url', {
url, url,
folderId: folder.value ? folder.value.id : undefined, folderId: folder?.id ?? undefined,
}); });
os.alert({ os.alert({
@ -302,7 +330,7 @@ function createFolder() {
if (canceled) return; if (canceled) return;
os.api('drive/folders/create', { os.api('drive/folders/create', {
name, name,
parentId: folder.value ? folder.value.id : undefined, parentId: folder?.id ?? undefined,
}).then(createdFolder => { }).then(createdFolder => {
addFolder(createdFolder, true); addFolder(createdFolder, true);
}); });
@ -351,57 +379,61 @@ function deleteFolder(folderToDelete: foundkey.entities.DriveFolder) {
} }
function onChangeFileInput() { function onChangeFileInput() {
if (!fileInput.value?.files) return; if (!fileInput?.files) return;
for (const file of Array.from(fileInput.value.files)) { for (const file of Array.from(fileInput.files)) {
upload(file, folder.value); upload(file, folder);
} }
} }
function upload(file: File, folderToUpload?: foundkey.entities.DriveFolder | null) { function upload(file: File, folderToUpload?: foundkey.entities.DriveFolder | null) {
uploadFile(file, (folderToUpload && typeof folderToUpload === 'object') ? folderToUpload.id : null, undefined, keepOriginal.value).then(res => { uploadFile(file, (typeof folderToUpload === 'object') ? folderToUpload.id : null, undefined, keepOriginal).then(res => {
addFile(res, true); addFile(res, true);
}); });
} }
function chooseFile(file: foundkey.entities.DriveFile) { function chooseFile(file: foundkey.entities.DriveFile) {
const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id); const isAlreadySelected = selectedFiles.some(f => f.id === file.id);
if (props.multiple) { if (props.multiple) {
if (isAlreadySelected) { if (isAlreadySelected) {
selectedFiles.value = selectedFiles.value.filter(f => f.id !== file.id); selectedFiles = selectedFiles.filter(f => f.id !== file.id);
} else { } else {
selectedFiles.value.push(file); selectedFiles.push(file);
} }
emit('change-selection', selectedFiles.value); emit('change-selection', selectedFiles);
} else { } else {
if (isAlreadySelected) { if (isAlreadySelected) {
emit('selected', file); emit('selected', file);
} else { } else {
selectedFiles.value = [file]; selectedFiles = [file];
emit('change-selection', [file]); emit('change-selection', [file]);
} }
} }
} }
function chooseFolder(folderToChoose: foundkey.entities.DriveFolder) { function chooseFolder(folderToChoose: foundkey.entities.DriveFolder) {
const isAlreadySelected = selectedFolders.value.some(f => f.id === folderToChoose.id); const isAlreadySelected = selectedFolders.some(f => f.id === folderToChoose.id);
if (props.multiple) { if (props.multiple) {
if (isAlreadySelected) { if (isAlreadySelected) {
selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToChoose.id); selectedFolders = selectedFolders.filter(f => f.id !== folderToChoose.id);
} else { } else {
selectedFolders.value.push(folderToChoose); selectedFolders.push(folderToChoose);
} }
emit('change-selection', selectedFolders.value); emit('change-selection', selectedFolders);
} else { } else {
if (isAlreadySelected) { if (isAlreadySelected) {
emit('selected', folderToChoose); emit('selected', folderToChoose);
} else { } else {
selectedFolders.value = [folderToChoose]; selectedFolders = [folderToChoose];
emit('change-selection', [folderToChoose]); emit('change-selection', [folderToChoose]);
} }
} }
} }
function move(target?: string | foundkey.entities.DriveFolder) { function move(target?: string | foundkey.entities.DriveFolder) {
// reset loading state
foldersLoading = true;
filesLoading = true;
if (!target) { if (!target) {
goRoot(); goRoot();
return; return;
@ -409,165 +441,92 @@ function move(target?: string | foundkey.entities.DriveFolder) {
const targetId = typeof target === 'string' ? target : target.id; const targetId = typeof target === 'string' ? target : target.id;
fetching.value = true;
os.api('drive/folders/show', { os.api('drive/folders/show', {
folderId: targetId, folderId: targetId,
}).then(folderToMove => { }).then(folderToMove => {
folder.value = folderToMove; folder = folderToMove;
hierarchyFolders.value = [];
const dive = folderToDive => { // display new folder hierarchy appropriately
hierarchyFolders.value.unshift(folderToDive); hierarchyFolders = [];
if (folderToDive.parent) dive(folderToDive.parent); let parent = folderToMove.parent;
}; while (parent) {
hierarchyFolders.unshift(parent);
if (folderToMove.parent) dive(folderToMove.parent); parent = parent.parent;
}
emit('open-folder', folderToMove); emit('open-folder', folderToMove);
fetch();
}); });
} }
function addFolder(folderToAdd: foundkey.entities.DriveFolder, unshift = false) { function addFolder(folderToAdd: foundkey.entities.DriveFolder, unshift = false) {
const current = folder.value ? folder.value.id : null; const current = folder?.id ?? null;
if (current !== folderToAdd.parentId) return; if (current !== folderToAdd.parentId) return;
if (folders.value.some(f => f.id === folderToAdd.id)) { const exist = foldersPaginationElem.items.some(f => f.id === folderToAdd.id);
const exist = folders.value.map(f => f.id).indexOf(folderToAdd.id); if (exist) {
folders.value[exist] = folderToAdd; foldersPaginationElem.updateItem(folderToAdd.id, () => folderToAdd);
return; } else if (unshift) {
} foldersPaginationElem.prepend(folderToAdd);
if (unshift) {
folders.value.unshift(folderToAdd);
} else { } else {
folders.value.push(folderToAdd); foldersPaginationElem.append(folderToAdd);
} }
} }
function addFile(fileToAdd: foundkey.entities.DriveFile, unshift = false) { function addFile(fileToAdd: foundkey.entities.DriveFile, unshift = false) {
const current = folder.value ? folder.value.id : null; const current = folder?.id ?? null;
if (current !== fileToAdd.folderId) return; if (current !== fileToAdd.folderId) return;
if (files.value.some(f => f.id === fileToAdd.id)) { const exist = filesPaginationElem.items.some(f => f.id === fileToAdd.id);
const exist = files.value.map(f => f.id).indexOf(fileToAdd.id); if (exist) {
files.value[exist] = fileToAdd; filesPaginationElem.updateItem(fileToAdd.id, () => fileToAdd);
return; } else if (unshift) {
} filesPaginationElem.prepend(fileToAdd);
if (unshift) {
files.value.unshift(fileToAdd);
} else { } else {
files.value.push(fileToAdd); filesPaginationElem.append(fileToAdd);
} }
} }
function removeFolder(folderToRemove: foundkey.entities.DriveFolder | string) { function removeFolder(folderToRemove: foundkey.entities.DriveFolder | string): void {
const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove.id : folderToRemove; const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove.id : folderToRemove;
folders.value = folders.value.filter(f => f.id !== folderIdToRemove); foldersPaginationElem.removeItem(item => item.id === folderIdToRemove);
} }
function removeFile(file: foundkey.entities.DriveFile | string) { function removeFile(fileToRemove: foundkey.entities.DriveFile | string): void {
const fileId = typeof file === 'object' ? file.id : file; const fileIdToRemove = typeof fileToRemove === 'object' ? fileToRemove.id : fileToRemove;
files.value = files.value.filter(f => f.id !== fileId); filesPaginationElem.removeItem(item => item.id === fileIdToRemove);
} }
function appendFile(file: foundkey.entities.DriveFile) {
addFile(file);
}
function appendFolder(folderToAppend: foundkey.entities.DriveFolder) {
addFolder(folderToAppend);
}
/*
function prependFile(file: foundkey.entities.DriveFile) {
addFile(file, true);
}
function prependFolder(folderToPrepend: foundkey.entities.DriveFolder) {
addFolder(folderToPrepend, true);
}
*/
function goRoot() { function goRoot() {
// root // do nothing if already at root
if (folder.value == null) return; if (folder == null) return;
folder.value = null; folder = null;
hierarchyFolders.value = []; hierarchyFolders = [];
emit('move-root'); emit('move-root');
fetch();
} }
async function fetch() { const foldersPagination = {
folders.value = []; endpoint: 'drive/folders' as const,
files.value = []; limit: 30,
moreFolders.value = false; params: computed(() => ({
moreFiles.value = false; folderId: folder?.id ?? null,
fetching.value = true; })),
};
const foldersMax = 30; const filesPagination = {
const filesMax = 30; endpoint: 'drive/files' as const,
limit: 30,
const foldersPromise = os.api('drive/folders', { params: computed(() => ({
folderId: folder.value ? folder.value.id : null, folderId: folder?.id ?? null,
limit: foldersMax + 1,
}).then(fetchedFolders => {
if (fetchedFolders.length === foldersMax + 1) {
moreFolders.value = true;
fetchedFolders.pop();
}
return fetchedFolders;
});
const filesPromise = os.api('drive/files', {
folderId: folder.value ? folder.value.id : null,
type: props.type, type: props.type,
limit: filesMax + 1, })),
}).then(fetchedFiles => { };
if (fetchedFiles.length === filesMax + 1) {
moreFiles.value = true;
fetchedFiles.pop();
}
return fetchedFiles;
});
const [fetchedFolders, fetchedFiles] = await Promise.all([foldersPromise, filesPromise]);
for (const x of fetchedFolders) appendFolder(x);
for (const x of fetchedFiles) appendFile(x);
fetching.value = false;
}
function fetchMoreFiles() {
fetching.value = true;
const max = 30;
//
os.api('drive/files', {
folderId: folder.value ? folder.value.id : null,
type: props.type,
untilId: files.value[files.value.length - 1].id,
limit: max + 1,
}).then(files => {
if (files.length === max + 1) {
moreFiles.value = true;
files.pop();
} else {
moreFiles.value = false;
}
for (const x of files) appendFile(x);
fetching.value = false;
});
}
function getMenu() { function getMenu() {
return [{ return [{
type: 'switch', type: 'switch',
text: i18n.ts.keepOriginalUploading, text: i18n.ts.keepOriginalUploading,
ref: keepOriginal, ref: $$(keepOriginal),
}, null, { }, null, {
text: i18n.ts.addFile, text: i18n.ts.addFile,
type: 'label', type: 'label',
@ -580,16 +539,16 @@ function getMenu() {
icon: 'fas fa-link', icon: 'fas fa-link',
action: () => { urlUpload(); }, action: () => { urlUpload(); },
}, null, { }, null, {
text: folder.value ? folder.value.name : i18n.ts.drive, text: folder?.name ?? i18n.ts.drive,
type: 'label', type: 'label',
}, folder.value ? { }, folder != null ? {
text: i18n.ts.renameFolder, text: i18n.ts.renameFolder,
icon: 'fas fa-i-cursor', icon: 'fas fa-i-cursor',
action: () => { renameFolder(folder.value); }, action: () => { renameFolder(folder); },
} : undefined, folder.value ? { } : undefined, folder != null ? {
text: i18n.ts.deleteFolder, text: i18n.ts.deleteFolder,
icon: 'fas fa-trash-alt', icon: 'fas fa-trash-alt',
action: () => { deleteFolder(folder.value as foundkey.entities.DriveFolder); }, action: () => { deleteFolder(folder as foundkey.entities.DriveFolder); },
} : undefined, { } : undefined, {
text: i18n.ts.createFolder, text: i18n.ts.createFolder,
icon: 'fas fa-folder-plus', icon: 'fas fa-folder-plus',
@ -606,12 +565,6 @@ function onContextmenu(ev: MouseEvent) {
} }
onMounted(() => { onMounted(() => {
if (defaultStore.state.enableInfiniteScroll && loadMoreFiles.value) {
nextTick(() => {
ilFilesObserver.observe(loadMoreFiles.value?.$el);
});
}
connection.on('fileCreated', onStreamDriveFileCreated); connection.on('fileCreated', onStreamDriveFileCreated);
connection.on('fileUpdated', onStreamDriveFileUpdated); connection.on('fileUpdated', onStreamDriveFileUpdated);
connection.on('fileDeleted', onStreamDriveFileDeleted); connection.on('fileDeleted', onStreamDriveFileDeleted);
@ -621,22 +574,11 @@ onMounted(() => {
if (props.initialFolder) { if (props.initialFolder) {
move(props.initialFolder); move(props.initialFolder);
} else {
fetch();
}
});
onActivated(() => {
if (defaultStore.state.enableInfiniteScroll) {
nextTick(() => {
ilFilesObserver.observe(loadMoreFiles.value?.$el);
});
} }
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
connection.dispose(); connection.dispose();
ilFilesObserver.disconnect();
}); });
</script> </script>
@ -718,18 +660,6 @@ onBeforeUnmount(() => {
user-select: none; user-select: none;
} }
&.fetching {
cursor: wait !important;
* {
pointer-events: none;
}
> .contents {
opacity: 0.5;
}
}
&.uploading { &.uploading {
height: calc(100% - 38px - 100px); height: calc(100% - 38px - 100px);
} }

View file

@ -70,6 +70,8 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'queue', count: number): void; (ev: 'queue', count: number): void;
(ev: 'loaded'): void;
(ev: 'error'): void;
}>(); }>();
type Item = { id: string; [another: string]: unknown; }; type Item = { id: string; [another: string]: unknown; };
@ -105,9 +107,11 @@ const init = async (): Promise<void> => {
offset.value = res.length; offset.value = res.length;
error.value = false; error.value = false;
fetching.value = false; fetching.value = false;
emit('loaded');
}, () => { }, () => {
error.value = true; error.value = true;
fetching.value = false; fetching.value = false;
emit('error');
}); });
}; };