aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/components/FilesListVirtual.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/components/FilesListVirtual.vue')
-rw-r--r--apps/files/src/components/FilesListVirtual.vue146
1 files changed, 102 insertions, 44 deletions
diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue
index 93f567f25a4..04acbd302f5 100644
--- a/apps/files/src/components/FilesListVirtual.vue
+++ b/apps/files/src/components/FilesListVirtual.vue
@@ -9,6 +9,7 @@
:data-sources="nodes"
:grid-mode="userConfig.grid_view"
:extra-props="{
+ isMimeAvailable,
isMtimeAvailable,
isSizeAvailable,
nodes,
@@ -20,7 +21,9 @@
</template>
<template v-if="!isNoneSelected" #header-overlay>
- <span class="files-list__selected">{{ t('files', '{count} selected', { count: selectedNodes.length }) }}</span>
+ <span class="files-list__selected">
+ {{ n('files', '{count} selected', '{count} selected', selectedNodes.length, { count: selectedNodes.length }) }}
+ </span>
<FilesListTableHeaderActions :current-view="currentView"
:selected-nodes="selectedNodes" />
</template>
@@ -39,15 +42,22 @@
<!-- Table header and sort buttons -->
<FilesListTableHeader ref="thead"
:files-list-width="fileListWidth"
+ :is-mime-available="isMimeAvailable"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes" />
</template>
+ <!-- Body replacement if no files are available -->
+ <template #empty>
+ <slot name="empty" />
+ </template>
+
<!-- Tfoot-->
<template #footer>
<FilesListTableFooter :current-view="currentView"
:files-list-width="fileListWidth"
+ :is-mime-available="isMimeAvailable"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes"
@@ -60,12 +70,11 @@
import type { UserConfig } from '../types'
import type { Node as NcNode } from '@nextcloud/files'
import type { ComponentPublicInstance, PropType } from 'vue'
-import type { Location } from 'vue-router'
import { Folder, Permission, View, getFileActions, FileType } from '@nextcloud/files'
import { showError } from '@nextcloud/dialogs'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
-import { translate as t } from '@nextcloud/l10n'
+import { n, t } from '@nextcloud/l10n'
import { useHotKey } from '@nextcloud/vue/composables/useHotKey'
import { defineComponent } from 'vue'
@@ -76,6 +85,7 @@ import { useFileListWidth } from '../composables/useFileListWidth.ts'
import { useRouteParameters } from '../composables/useRouteParameters.ts'
import { useSelectionStore } from '../store/selection.js'
import { useUserConfigStore } from '../store/userconfig.ts'
+import logger from '../logger.ts'
import FileEntry from './FileEntry.vue'
import FileEntryGrid from './FileEntryGrid.vue'
@@ -85,7 +95,6 @@ import FilesListTableFooter from './FilesListTableFooter.vue'
import FilesListTableHeader from './FilesListTableHeader.vue'
import FilesListTableHeaderActions from './FilesListTableHeaderActions.vue'
import VirtualList from './VirtualList.vue'
-import logger from '../logger.ts'
export default defineComponent({
name: 'FilesListVirtual',
@@ -137,6 +146,7 @@ export default defineComponent({
selectionStore,
userConfigStore,
+ n,
t,
}
},
@@ -146,7 +156,6 @@ export default defineComponent({
FileEntry,
FileEntryGrid,
scrollToIndex: 0,
- openFileId: null as number|null,
}
},
@@ -155,6 +164,16 @@ export default defineComponent({
return this.userConfigStore.userConfig
},
+ isMimeAvailable() {
+ if (!this.userConfig.show_mime_column) {
+ return false
+ }
+ // Hide mime column on narrow screens
+ if (this.fileListWidth < 1024) {
+ return false
+ }
+ return this.nodes.some(node => node.mime !== undefined || node.mime !== 'application/octet-stream')
+ },
isMtimeAvailable() {
// Hide mtime column on narrow screens
if (this.fileListWidth < 768) {
@@ -201,39 +220,26 @@ export default defineComponent({
isNoneSelected() {
return this.selectedNodes.length === 0
},
+
+ isEmpty() {
+ return this.nodes.length === 0
+ },
},
watch: {
- fileId: {
- handler(fileId) {
- this.scrollToFile(fileId, false)
- },
- immediate: true,
+ // If nodes gets populated and we have a fileId,
+ // an openFile or openDetails, we fire the appropriate actions.
+ isEmpty() {
+ this.handleOpenQueries()
},
-
- openFile: {
- handler(openFile) {
- if (!openFile || !this.fileId) {
- return
- }
-
- this.handleOpenFile(this.fileId)
- },
- immediate: true,
+ fileId() {
+ this.handleOpenQueries()
},
-
- openDetails: {
- handler(openDetails) {
- // wait for scrolling and updating the actions to settle
- this.$nextTick(() => {
- if (!openDetails || !this.fileId) {
- return
- }
-
- this.openSidebarForFile(this.fileId)
- })
- },
- immediate: true,
+ openFile() {
+ this.handleOpenQueries()
+ },
+ openDetails() {
+ this.handleOpenQueries()
},
},
@@ -263,6 +269,33 @@ export default defineComponent({
},
methods: {
+ handleOpenQueries() {
+ // If the list is empty, or we don't have a fileId,
+ // there's nothing to be done.
+ if (this.isEmpty || !this.fileId) {
+ return
+ }
+
+ logger.debug('FilesListVirtual: checking for requested fileId, openFile or openDetails', {
+ nodes: this.nodes,
+ fileId: this.fileId,
+ openFile: this.openFile,
+ openDetails: this.openDetails,
+ })
+
+ if (this.openFile) {
+ this.handleOpenFile(this.fileId)
+ }
+
+ if (this.openDetails) {
+ this.openSidebarForFile(this.fileId)
+ }
+
+ if (this.fileId) {
+ this.scrollToFile(this.fileId, false)
+ }
+ },
+
openSidebarForFile(fileId) {
// Open the sidebar for the given URL fileid
// iif we just loaded the app.
@@ -272,7 +305,7 @@ export default defineComponent({
sidebarAction.exec(node, this.currentView, this.currentFolder.path)
return
}
- logger.error(`Failed to open sidebar on file ${fileId}, file isn't cached yet !`, { fileId, node })
+ logger.warn(`Failed to open sidebar on file ${fileId}, file isn't cached yet !`, { fileId, node })
},
scrollToFile(fileId: number|null, warn = true) {
@@ -288,6 +321,7 @@ export default defineComponent({
}
this.scrollToIndex = Math.max(0, index)
+ logger.debug('Scrolling to file ' + fileId, { fileId, index })
}
},
@@ -299,7 +333,7 @@ export default defineComponent({
delete query.openfile
delete query.opendetails
- this.activeStore.clearActiveNode()
+ this.activeStore.activeNode = undefined
window.OCP.Files.Router.goToRoute(
null,
{ ...this.$route.params, fileid: String(this.currentFolder.fileid ?? '') },
@@ -352,15 +386,13 @@ export default defineComponent({
}
// The file is either a folder or has no default action other than downloading
// in this case we need to open the details instead and remove the route from the history
- const query = this.$route.query
- delete query.openfile
- query.opendetails = ''
-
logger.debug('Ignore `openfile` query and replacing with `opendetails` for ' + node.path, { node })
- await this.$router.replace({
- ...(this.$route as Location),
- query,
- })
+ window.OCP.Files.Router.goToRoute(
+ null,
+ this.$route.params,
+ { ...this.$route.query, openfile: undefined, opendetails: '' },
+ true, // silent update of the URL
+ )
},
onDragOver(event: DragEvent) {
@@ -433,7 +465,7 @@ export default defineComponent({
delete query.openfile
delete query.opendetails
- this.activeStore.setActiveNode(node)
+ this.activeStore.activeNode = node
// Silent update of the URL
window.OCP.Files.Router.goToRoute(
@@ -458,6 +490,8 @@ export default defineComponent({
--icon-preview-size: 32px;
--fixed-block-start-position: var(--default-clickable-area);
+ display: flex;
+ flex-direction: column;
overflow: auto;
height: 100%;
will-change: scroll-position;
@@ -505,6 +539,13 @@ export default defineComponent({
// Hide the table header below the overlay
margin-block-start: calc(-1 * var(--row-height));
}
+
+ // Visually hide the table when there are no files
+ &--hidden {
+ visibility: hidden;
+ z-index: -1;
+ opacity: 0;
+ }
}
.files-list__filters {
@@ -536,6 +577,7 @@ export default defineComponent({
background-color: var(--color-main-background);
border-block-end: 1px solid var(--color-border);
height: var(--row-height);
+ flex: 0 0 var(--row-height);
}
.files-list__thead,
@@ -554,6 +596,16 @@ export default defineComponent({
top: var(--fixed-block-start-position);
}
+ // Empty content
+ .files-list__empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ }
+
tr {
position: relative;
display: flex;
@@ -829,10 +881,12 @@ export default defineComponent({
margin-inline-end: 7px;
}
+ .files-list__row-mime,
.files-list__row-mtime,
.files-list__row-size {
color: var(--color-text-maxcontrast);
}
+
.files-list__row-size {
width: calc(var(--row-height) * 1.5);
// Right align content/text
@@ -843,6 +897,10 @@ export default defineComponent({
width: calc(var(--row-height) * 2);
}
+ .files-list__row-mime {
+ width: calc(var(--row-height) * 2.5);
+ }
+
.files-list__row-column-custom {
width: calc(var(--row-height) * 2);
}