mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
refactor(files_sharing): migrate to new Files Sidebar API
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
34511e9036
commit
7077685bf8
7 changed files with 98 additions and 69 deletions
|
|
@ -3,28 +3,28 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { Node } from '@nextcloud/files'
|
||||
import type { INode } from '@nextcloud/files'
|
||||
|
||||
import AccountGroupSvg from '@mdi/svg/svg/account-group-outline.svg?raw'
|
||||
import AccountPlusSvg from '@mdi/svg/svg/account-plus-outline.svg?raw'
|
||||
import LinkSvg from '@mdi/svg/svg/link.svg?raw'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { FileAction, Permission, registerFileAction } from '@nextcloud/files'
|
||||
import { FileAction, getSidebar, Permission, registerFileAction } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { ShareType } from '@nextcloud/sharing'
|
||||
import { isPublicShare } from '@nextcloud/sharing/public'
|
||||
import CircleSvg from '../../../../core/img/apps/circles.svg?raw'
|
||||
import { action as sidebarAction } from '../../../files/src/actions/sidebarAction.ts'
|
||||
import { generateAvatarSvg } from '../utils/AccountIcon.ts'
|
||||
|
||||
import './sharingStatusAction.scss'
|
||||
|
||||
/**
|
||||
* Check if the node is external (federated)
|
||||
*
|
||||
* @param node
|
||||
* @param node - The node to check
|
||||
*/
|
||||
function isExternal(node: Node) {
|
||||
function isExternal(node: INode) {
|
||||
return node.attributes?.['is-federated'] ?? false
|
||||
}
|
||||
|
||||
|
|
@ -136,12 +136,12 @@ export const action = new FileAction({
|
|||
&& (node.permissions & Permission.READ) !== 0
|
||||
},
|
||||
|
||||
async exec({ nodes, view, folder, contents }) {
|
||||
async exec({ nodes }) {
|
||||
// You need read permissions to see the sidebar
|
||||
const node = nodes[0]
|
||||
if ((node.permissions & Permission.READ) !== 0) {
|
||||
window.OCA?.Files?.Sidebar?.setActiveTab?.('sharing')
|
||||
sidebarAction.exec({ nodes, view, folder, contents })
|
||||
const sidebar = getSidebar()
|
||||
sidebar.open(node, 'sharing')
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@
|
|||
|
||||
import ShareVariant from '@mdi/svg/svg/share-variant.svg?raw'
|
||||
import { getCSPNonce } from '@nextcloud/auth'
|
||||
import { registerSidebarTab } from '@nextcloud/files'
|
||||
import { n, t } from '@nextcloud/l10n'
|
||||
import wrap from '@vue/web-component-wrapper'
|
||||
import Vue from 'vue'
|
||||
import FilesSidebarTab from './views/FilesSidebarTab.vue'
|
||||
import ExternalShareActions from './services/ExternalShareActions.js'
|
||||
import ShareSearch from './services/ShareSearch.js'
|
||||
import TabSections from './services/TabSections.js'
|
||||
|
|
@ -14,9 +17,7 @@ import TabSections from './services/TabSections.js'
|
|||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
// Init Sharing Tab Service
|
||||
if (!window.OCA.Sharing) {
|
||||
window.OCA.Sharing = {}
|
||||
}
|
||||
window.OCA.Sharing ??= {}
|
||||
Object.assign(window.OCA.Sharing, { ShareSearch: new ShareSearch() })
|
||||
Object.assign(window.OCA.Sharing, { ExternalShareActions: new ExternalShareActions() })
|
||||
Object.assign(window.OCA.Sharing, { ShareTabSections: new TabSections() })
|
||||
|
|
@ -24,42 +25,34 @@ Object.assign(window.OCA.Sharing, { ShareTabSections: new TabSections() })
|
|||
Vue.prototype.t = t
|
||||
Vue.prototype.n = n
|
||||
|
||||
// Init Sharing tab component
|
||||
let TabInstance = null
|
||||
const tagName = 'files_sharing-sidebar-tab'
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
if (OCA.Files && OCA.Files.Sidebar) {
|
||||
OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
|
||||
id: 'sharing',
|
||||
name: t('files_sharing', 'Sharing'),
|
||||
iconSvg: ShareVariant,
|
||||
|
||||
async mount(el, fileInfo, context) {
|
||||
const SharingTab = (await import('./views/SharingTab.vue')).default
|
||||
const View = Vue.extend(SharingTab)
|
||||
|
||||
if (TabInstance) {
|
||||
TabInstance.$destroy()
|
||||
}
|
||||
TabInstance = new View({
|
||||
// Better integration with vue parent component
|
||||
parent: context,
|
||||
})
|
||||
// Only mount after we have all the info we need
|
||||
await TabInstance.update(fileInfo)
|
||||
TabInstance.$mount(el)
|
||||
},
|
||||
|
||||
update(fileInfo) {
|
||||
TabInstance.update(fileInfo)
|
||||
},
|
||||
|
||||
destroy() {
|
||||
if (TabInstance) {
|
||||
TabInstance.$destroy()
|
||||
TabInstance = null
|
||||
}
|
||||
},
|
||||
}))
|
||||
}
|
||||
registerSidebarTab({
|
||||
id: 'sharing',
|
||||
displayName: t('files_sharing', 'Sharing'),
|
||||
iconSvgInline: ShareVariant,
|
||||
order: 10,
|
||||
tagName,
|
||||
enabled() {
|
||||
if (!window.customElements.get(tagName)) {
|
||||
setupSidebarTab()
|
||||
}
|
||||
return true
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Setup the sidebar tab as a web component
|
||||
*/
|
||||
function setupSidebarTab() {
|
||||
const webComponent = wrap(Vue, FilesSidebarTab)
|
||||
// In Vue 2, wrap doesn't support diseabling shadow. Disable with a hack
|
||||
Object.defineProperty(webComponent.prototype, 'attachShadow', {
|
||||
value() { return this },
|
||||
})
|
||||
Object.defineProperty(webComponent.prototype, 'shadowRoot', {
|
||||
get() { return this },
|
||||
})
|
||||
|
||||
window.customElements.define(tagName, webComponent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
|
||||
import type { Attribute, Node } from '@nextcloud/files'
|
||||
import type { Attribute, INode } from '@nextcloud/files'
|
||||
|
||||
interface RawLegacyFileInfo {
|
||||
id: number
|
||||
|
|
@ -30,11 +28,16 @@ export type LegacyFileInfo = RawLegacyFileInfo & {
|
|||
get: (key: keyof RawLegacyFileInfo) => unknown
|
||||
isDirectory: () => boolean
|
||||
canEdit: () => boolean
|
||||
node: Node
|
||||
node: INode
|
||||
canDownload: () => boolean
|
||||
}
|
||||
|
||||
export default function(node: Node): LegacyFileInfo {
|
||||
/**
|
||||
* Convert Node to legacy file info
|
||||
*
|
||||
* @param node - The Node to convert
|
||||
*/
|
||||
export default function(node: INode): LegacyFileInfo {
|
||||
const rawFileInfo: RawLegacyFileInfo = {
|
||||
id: node.fileid!,
|
||||
path: node.dirname,
|
||||
21
apps/files_sharing/src/views/FilesSidebarTab.vue
Normal file
21
apps/files_sharing/src/views/FilesSidebarTab.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import type { IFolder, INode, IView } from '@nextcloud/files'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import SharingTab from './SharingTab.vue'
|
||||
import FileInfo from '../services/FileInfo.ts'
|
||||
|
||||
const props = defineProps<{
|
||||
node?: INode
|
||||
// eslint-disable-next-line vue/no-unused-properties -- Required on the web component interface
|
||||
folder?: IFolder
|
||||
// eslint-disable-next-line vue/no-unused-properties -- Required on the web component interface
|
||||
view?: IView
|
||||
}>()
|
||||
|
||||
const fileInfo = computed(() => props.node && FileInfo(props.node))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SharingTab v-if="fileInfo" :file-info="fileInfo" />
|
||||
</template>
|
||||
|
|
@ -230,6 +230,13 @@ export default {
|
|||
|
||||
mixins: [ShareDetails],
|
||||
|
||||
props: {
|
||||
fileInfo: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
config: new Config(),
|
||||
|
|
@ -238,8 +245,6 @@ export default {
|
|||
expirationInterval: null,
|
||||
loading: true,
|
||||
|
||||
fileInfo: null,
|
||||
|
||||
// reshare Share object
|
||||
reshare: null,
|
||||
sharedWithMe: {},
|
||||
|
|
@ -328,18 +333,19 @@ export default {
|
|||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Update current fileInfo and fetch new data
|
||||
*
|
||||
* @param {object} fileInfo the current file FileInfo
|
||||
*/
|
||||
async update(fileInfo) {
|
||||
this.fileInfo = fileInfo
|
||||
this.resetState()
|
||||
this.getShares()
|
||||
watch: {
|
||||
fileInfo: {
|
||||
immediate: true,
|
||||
handler(newValue, oldValue) {
|
||||
if (oldValue?.id === undefined || oldValue?.id !== newValue?.id) {
|
||||
this.resetState()
|
||||
this.getShares()
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Get the existing shares infos
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -35,8 +35,12 @@ export function createShare(fileName: string, username: string, shareSettings: P
|
|||
|
||||
export function openSharingDetails(index: number) {
|
||||
cy.get('#app-sidebar-vue').within(() => {
|
||||
cy.get('[data-cy-files-sharing-share-actions]').eq(index).click({ force: true })
|
||||
cy.get('[data-cy-files-sharing-share-permissions-bundle="custom"]').click()
|
||||
cy.findAllByRole('button', { name: /open sharing details/i })
|
||||
.should('have.length.at.least', index + 1)
|
||||
.eq(index)
|
||||
.click({ force: true })
|
||||
cy.get('[data-cy-files-sharing-share-permissions-bundle="custom"]')
|
||||
.click()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,11 +90,13 @@ describe('files_sharing: Expiry date', () => {
|
|||
prepareDirectory(dir)
|
||||
updateShare(dir, 0, { expiryDate: fortnight })
|
||||
validateExpiryDate(dir, fortnightString)
|
||||
|
||||
closeSidebar()
|
||||
|
||||
cy.log('Upadate share and validate expiry date is kept')
|
||||
updateShare(dir, 0, { note: 'Only note changed' })
|
||||
validateExpiryDate(dir, fortnightString)
|
||||
|
||||
cy.log('Reload page and validate expiry date is kept')
|
||||
cy.visit('/apps/files')
|
||||
validateExpiryDate(dir, fortnightString)
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue