diff options
Diffstat (limited to 'apps/files')
-rw-r--r-- | apps/files/src/actions/sidebarAction.ts | 6 | ||||
-rw-r--r-- | apps/files/src/components/FileEntry.vue | 22 | ||||
-rw-r--r-- | apps/files/src/components/FilesListVirtual.vue | 15 | ||||
-rw-r--r-- | apps/files/src/newMenu/newFolder.ts | 2 | ||||
-rw-r--r-- | apps/files/src/views/FilesList.vue | 84 |
5 files changed, 105 insertions, 24 deletions
diff --git a/apps/files/src/actions/sidebarAction.ts b/apps/files/src/actions/sidebarAction.ts index e9d18dbf719..aa265d6709a 100644 --- a/apps/files/src/actions/sidebarAction.ts +++ b/apps/files/src/actions/sidebarAction.ts @@ -19,7 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ -import { Permission, type Node, View, registerFileAction, FileAction } from '@nextcloud/files' +import { Permission, type Node, View, registerFileAction, FileAction, FileType } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import InformationSvg from '@mdi/svg/svg/information-variant.svg?raw' @@ -51,7 +51,7 @@ export const action = new FileAction({ return (nodes[0].root?.startsWith('/files/') && nodes[0].permissions !== Permission.NONE) ?? false }, - async exec(node: Node, view: View) { + async exec(node: Node, view: View, dir: string) { try { // TODO: migrate Sidebar to use a Node instead await window.OCA.Files.Sidebar.open(node.path) @@ -60,7 +60,7 @@ export const action = new FileAction({ window.OCP.Files.Router.goToRoute( null, { view: view.id, fileid: node.fileid }, - { dir: node.dirname }, + { dir }, true, ) diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue index fce8b7ed263..3d30ba1055c 100644 --- a/apps/files/src/components/FileEntry.vue +++ b/apps/files/src/components/FileEntry.vue @@ -166,12 +166,15 @@ </template> <script lang='ts'> +import type { PropType } from 'vue' +import type { Node } from '@nextcloud/files' + import { CancelablePromise } from 'cancelable-promise' import { debounce } from 'debounce' import { emit } from '@nextcloud/event-bus' import { extname } from 'path' import { generateUrl } from '@nextcloud/router' -import { getFileActions, DefaultType, FileType, formatFileSize, Permission, NodeStatus } from '@nextcloud/files' +import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File } from '@nextcloud/files' import { showError, showSuccess } from '@nextcloud/dialogs' import { translate } from '@nextcloud/l10n' import { vOnClickOutside } from '@vueuse/components' @@ -186,7 +189,7 @@ import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js' import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' import Vue from 'vue' -import { ACTION_DETAILS } from '../actions/sidebarAction.ts' +import { action as sidebarAction } from '../actions/sidebarAction.ts' import { hashCode } from '../utils/hashUtils.ts' import { isCachedPreview } from '../services/PreviewService.ts' import { useActionsMenuStore } from '../store/actionsmenu.ts' @@ -235,7 +238,7 @@ export default Vue.extend({ default: false, }, source: { - type: Object, + type: [Folder, File, Node] as PropType<Node>, required: true, }, index: { @@ -243,7 +246,7 @@ export default Vue.extend({ required: true, }, nodes: { - type: Array, + type: Array as PropType<Node[]>, required: true, }, filesListWidth: { @@ -295,7 +298,7 @@ export default Vue.extend({ currentDir() { // Remove any trailing slash but leave root slash - return (this.$route?.query?.dir || '/').replace(/^(.+)\/$/, '$1') + return (this.$route?.query?.dir?.toString() || '/').replace(/^(.+)\/$/, '$1') }, currentFileId() { return this.$route.params.fileid || this.$route.query.fileid || null @@ -660,11 +663,10 @@ export default Vue.extend({ }, openDetailsIfAvailable(event) { - const detailsAction = this.enabledActions.find(action => action.id === ACTION_DETAILS) - if (detailsAction) { - event.preventDefault() - event.stopPropagation() - detailsAction.exec(this.source, this.currentView) + event.preventDefault() + event.stopPropagation() + if (sidebarAction?.enabled?.([this.source], this.currentView)) { + sidebarAction.exec(this.source, this.currentView, this.currentDir) } }, diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index 8c7242561e9..9938969437d 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -67,11 +67,13 @@ </template> <script lang="ts"> +import type { PropType } from 'vue' +import type { Node } from '@nextcloud/files' + import { translate, translatePlural } from '@nextcloud/l10n' -import { getFileListHeaders, type Node } from '@nextcloud/files' +import { getFileListHeaders, Folder, View } from '@nextcloud/files' import { showError } from '@nextcloud/dialogs' import Vue from 'vue' -import VirtualList from './VirtualList.vue' import { action as sidebarAction } from '../actions/sidebarAction.ts' import FileEntry from './FileEntry.vue' @@ -80,6 +82,7 @@ import FilesListTableFooter from './FilesListTableFooter.vue' import FilesListTableHeader from './FilesListTableHeader.vue' import filesListWidthMixin from '../mixins/filesListWidth.ts' import logger from '../logger.js' +import VirtualList from './VirtualList.vue' export default Vue.extend({ name: 'FilesListVirtual', @@ -97,15 +100,15 @@ export default Vue.extend({ props: { currentView: { - type: Object, + type: View, required: true, }, currentFolder: { - type: Object, + type: Folder, required: true, }, nodes: { - type: Array, + type: Array as PropType<Node[]>, required: true, }, }, @@ -179,7 +182,7 @@ export default Vue.extend({ const node = this.nodes.find(n => n.fileid === this.fileId) as Node if (node && sidebarAction?.enabled?.([node], this.currentView)) { logger.debug('Opening sidebar on file ' + node.path, { node }) - sidebarAction.exec(node, this.currentView, this.currentFolder) + sidebarAction.exec(node, this.currentView, this.currentFolder.path) } } }, diff --git a/apps/files/src/newMenu/newFolder.ts b/apps/files/src/newMenu/newFolder.ts index 399e6c1649a..1bf178de183 100644 --- a/apps/files/src/newMenu/newFolder.ts +++ b/apps/files/src/newMenu/newFolder.ts @@ -69,7 +69,7 @@ const entry = { iconSvgInline: FolderPlusSvg, async handler(context: Folder, content: Node[]) { const contentNames = content.map((node: Node) => node.basename) - const name = getUniqueName(t('files', 'New Folder'), contentNames) + const name = getUniqueName(t('files', 'New folder'), contentNames) const { fileid, source } = await createNewFolder(context.source, name) // Create the folder in the store diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue index fbd483fd23f..0b858a15e4d 100644 --- a/apps/files/src/views/FilesList.vue +++ b/apps/files/src/views/FilesList.vue @@ -25,8 +25,20 @@ <!-- Current folder breadcrumbs --> <BreadCrumbs :path="dir" @reload="fetchContent"> <template #actions> + <NcButton v-if="canShare" + :aria-label="shareButtonLabel" + :class="{ 'files-list__header-share-button--shared': shareButtonType }" + :title="shareButtonLabel" + class="files-list__header-share-button" + type="tertiary" + @click="openSharingSidebar"> + <template #icon> + <LinkIcon v-if="shareButtonType === Type.SHARE_TYPE_LINK" /> + <ShareVariantIcon v-else :size="20" /> + </template> + </NcButton> <!-- Uploader --> - <UploadPicker v-if="currentFolder" + <UploadPicker v-if="currentFolder && canUpload" :content="dirContents" :destination="currentFolder" :multiple="true" @@ -77,18 +89,24 @@ import type { Upload } from '@nextcloud/upload' import type { UserConfig } from '../types.ts' import type { View, ContentsWithRoot } from '@nextcloud/files' -import { Folder, Node } from '@nextcloud/files' +import { Folder, Node, Permission } from '@nextcloud/files' +import { getCapabilities } from '@nextcloud/capabilities' import { join, dirname } from 'path' import { orderBy } from 'natural-orderby' import { translate } from '@nextcloud/l10n' import { UploadPicker } from '@nextcloud/upload' +import { Type } from '@nextcloud/sharing' +import Vue from 'vue' + import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js' import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js' import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js' -import Vue from 'vue' +import LinkIcon from 'vue-material-design-icons/Link.vue' +import ShareVariantIcon from 'vue-material-design-icons/ShareVariant.vue' +import { action as sidebarAction } from '../actions/sidebarAction.ts' import { useFilesStore } from '../store/files.ts' import { usePathsStore } from '../store/paths.ts' import { useSelectionStore } from '../store/selection.ts' @@ -100,17 +118,21 @@ import FilesListVirtual from '../components/FilesListVirtual.vue' import filesSortingMixin from '../mixins/filesSorting.ts' import logger from '../logger.js' +const isSharingEnabled = getCapabilities()?.files_sharing !== undefined + export default Vue.extend({ name: 'FilesList', components: { BreadCrumbs, FilesListVirtual, + LinkIcon, NcAppContent, NcButton, NcEmptyContent, NcIconSvgWrapper, NcLoadingIcon, + ShareVariantIcon, UploadPicker, }, @@ -139,6 +161,7 @@ export default Vue.extend({ return { loading: true, promise: null, + Type, } }, @@ -157,7 +180,7 @@ export default Vue.extend({ */ dir(): string { // Remove any trailing slash but leave root slash - return (this.$route?.query?.dir || '/').replace(/^(.+)\/$/, '$1') + return (this.$route?.query?.dir?.toString() || '/').replace(/^(.+)\/$/, '$1') }, /** @@ -242,6 +265,43 @@ export default Vue.extend({ const dir = this.dir.split('/').slice(0, -1).join('/') || '/' return { ...this.$route, query: { dir } } }, + + shareAttributes(): number[]|undefined { + if (!this.currentFolder?.attributes?.['share-types']) { + return undefined + } + return Object.values(this.currentFolder?.attributes?.['share-types'] || {}).flat() as number[] + }, + shareButtonLabel() { + if (!this.shareAttributes) { + return this.t('files', 'Share') + } + + if (this.shareButtonType === Type.SHARE_TYPE_LINK) { + return this.t('files', 'Shared by link') + } + return this.t('files', 'Shared') + }, + shareButtonType(): Type|null { + if (!this.shareAttributes) { + return null + } + + // If all types are links, show the link icon + if (this.shareAttributes.some(type => type === Type.SHARE_TYPE_LINK)) { + return Type.SHARE_TYPE_LINK + } + + return Type.SHARE_TYPE_USER + }, + + canUpload() { + return this.currentFolder && (this.currentFolder.permissions & Permission.CREATE) !== 0 + }, + canShare() { + return isSharingEnabled + && this.currentFolder && (this.currentFolder.permissions & Permission.SHARE) !== 0 + }, }, watch: { @@ -348,6 +408,13 @@ export default Vue.extend({ } }, + openSharingSidebar() { + if (window?.OCA?.Files?.Sidebar?.setActiveTab) { + window.OCA.Files.Sidebar.setActiveTab('sharing') + } + sidebarAction.exec(this.currentFolder, this.currentView, this.currentFolder.path) + }, + t: translate, }, }) @@ -378,12 +445,21 @@ $navigationToggleSize: 50px; // Only the breadcrumbs shrinks flex: 0 0; } + + &-share-button { + opacity: .3; + &--shared { + opacity: 1; + } + } } + &__refresh-icon { flex: 0 0 44px; width: 44px; height: 44px; } + &__loading-icon { margin: auto; } |