Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 6 additions & 12 deletions apps/comments/src/actions/inlineUnreadCommentsAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,12 @@ describe('Inline unread comments action enabled tests', () => {
describe('Inline unread comments action execute tests', () => {
test('Action opens sidebar', async () => {
const openMock = vi.fn()
const setActiveTabMock = vi.fn()
window.OCA = {
Files: {
// @ts-expect-error Mocking for testing
Sidebar: {
_sidebar: () => ({
open: openMock,
setActiveTab: setActiveTabMock,
},
}),
},
}

Expand All @@ -211,22 +209,19 @@ describe('Inline unread comments action execute tests', () => {
})

expect(result).toBe(null)
expect(setActiveTabMock).toBeCalledWith('comments')
expect(openMock).toBeCalledWith('/foobar.txt')
expect(openMock).toBeCalledWith(file, 'comments')
})

test('Action handles sidebar open failure', async () => {
const openMock = vi.fn(() => {
throw new Error('Mock error')
})
const setActiveTabMock = vi.fn()
window.OCA = {
Files: {
// @ts-expect-error Mocking for testing
Sidebar: {
_sidebar: () => ({
open: openMock,
setActiveTab: setActiveTabMock,
},
}),
},
}
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
Expand All @@ -251,8 +246,7 @@ describe('Inline unread comments action execute tests', () => {
})

expect(result).toBe(false)
expect(setActiveTabMock).toBeCalledWith('comments')
expect(openMock).toBeCalledWith('/foobar.txt')
expect(openMock).toBeCalledWith(file, 'comments')
expect(logger.error).toBeCalledTimes(1)
})
})
9 changes: 5 additions & 4 deletions apps/comments/src/actions/inlineUnreadCommentsAction.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/**
/*!
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import CommentProcessingSvg from '@mdi/svg/svg/comment-processing.svg?raw'
import { FileAction } from '@nextcloud/files'
import { FileAction, getSidebar } from '@nextcloud/files'
import { n, t } from '@nextcloud/l10n'
import logger from '../logger.js'

Expand Down Expand Up @@ -34,8 +35,8 @@ export const action = new FileAction({
}

try {
window.OCA.Files.Sidebar.setActiveTab('comments')
await window.OCA.Files.Sidebar.open(nodes[0].path)
const sidebar = getSidebar()
sidebar.open(nodes[0], 'comments')
return null
} catch (error) {
logger.error('Error while opening sidebar', { error })
Expand Down
59 changes: 0 additions & 59 deletions apps/comments/src/comments-tab.js

This file was deleted.

57 changes: 57 additions & 0 deletions apps/comments/src/files-sidebar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import MessageReplyText from '@mdi/svg/svg/message-reply-text.svg?raw'
import { getCSPNonce } from '@nextcloud/auth'
import { registerSidebarTab } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import wrap from '@vue/web-component-wrapper'
import { createPinia, PiniaVuePlugin } from 'pinia'
import Vue from 'vue'
import FilesSidebarTab from './views/FilesSidebarTab.vue'
import { registerCommentsPlugins } from './comments-activity-tab.ts'

__webpack_nonce__ = getCSPNonce()

const tagName = 'comments_files-sidebar-tab'

if (loadState('comments', 'activityEnabled', false) && OCA?.Activity?.registerSidebarAction !== undefined) {
// Do not mount own tab but mount into activity
window.addEventListener('DOMContentLoaded', function() {
registerCommentsPlugins()
})
} else {
registerSidebarTab({
id: 'comments',
displayName: t('comments', 'Comments'),
iconSvgInline: MessageReplyText,
order: 50,
tagName,
enabled() {
if (!window.customElements.get(tagName)) {
setupSidebarTab()
}
return true
},
})
}

/**
* Setup the sidebar tab as a web component
*/
function setupSidebarTab() {
Vue.use(PiniaVuePlugin)
Vue.mixin({ pinia: createPinia() })
const webComponent = wrap(Vue, FilesSidebarTab)
// In Vue 2, wrap doesn't support disabling 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)
}
7 changes: 4 additions & 3 deletions apps/comments/src/mixins/CommentView.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { getCurrentUser } from '@nextcloud/auth'
/**
/*!
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { getCurrentUser } from '@nextcloud/auth'
import axios from '@nextcloud/axios'
import { loadState } from '@nextcloud/initial-state'
import { generateOcsUrl } from '@nextcloud/router'
Expand Down Expand Up @@ -32,7 +33,7 @@ export default defineComponent({
},
methods: {
/**
* Autocomplete @mentions
* Autocomplete `@mentions`
*
* @param search the query
* @param callback the callback to process the results with
Expand Down
40 changes: 40 additions & 0 deletions apps/comments/src/views/FilesSidebarTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import type { IFolder, INode, IView } from '@nextcloud/files'

import { computed } from 'vue'
import Comments from './Comments.vue'

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
}>()

defineExpose({ setActive })

const resourceId = computed(() => props.node?.fileid)

/**
* Set this tab as active
*
* @param active - The active state
*/
function setActive(active: boolean) {
return active
}
</script>

<template>
<Comments
v-if="resourceId !== undefined"
:key="resourceId"
:resource-id="resourceId"
resource-type="files" />
</template>
26 changes: 5 additions & 21 deletions apps/files/src/FilesApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,19 @@
<NcContent app-name="files">
<FilesNavigation v-if="!isPublic" />
<FilesList :is-public="isPublic" />
<FilesSidebar v-if="!isPublic" />
</NcContent>
</template>

<script lang="ts">
<script setup lang="ts">
import { isPublicShare } from '@nextcloud/sharing/public'
import { defineComponent } from 'vue'
import NcContent from '@nextcloud/vue/components/NcContent'
import FilesList from './views/FilesList.vue'
import FilesNavigation from './views/FilesNavigation.vue'
import FilesSidebar from './views/FilesSidebar.vue'
import { useHotKeys } from './composables/useHotKeys.ts'

export default defineComponent({
name: 'FilesApp',
useHotKeys()

components: {
NcContent,
FilesList,
FilesNavigation,
},

setup() {
// Register global hotkeys
useHotKeys()

const isPublic = isPublicShare()

return {
isPublic,
}
},
})
const isPublic = isPublicShare()
</script>
12 changes: 6 additions & 6 deletions apps/files/src/actions/favoriteAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ describe('Favorite action execute tests', () => {

// Check node change propagation
expect(file.attributes.favorite).toBe(1)
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toHaveBeenCalled()
expect(eventBus.emit).toBeCalledWith('files:favorites:added', file)
})

Expand Down Expand Up @@ -251,7 +251,7 @@ describe('Favorite action execute tests', () => {

// Check node change propagation
expect(file.attributes.favorite).toBe(0)
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toHaveBeenCalled()
expect(eventBus.emit).toBeCalledWith('files:favorites:removed', file)
})

Expand Down Expand Up @@ -285,9 +285,9 @@ describe('Favorite action execute tests', () => {

// Check node change propagation
expect(file.attributes.favorite).toBe(0)
expect(eventBus.emit).toBeCalledTimes(2)
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:node:deleted', file)
expect(eventBus.emit).toHaveBeenNthCalledWith(2, 'files:favorites:removed', file)
expect(eventBus.emit).toHaveBeenCalled()
expect(eventBus.emit).toHaveBeenCalledWith('files:node:deleted', file)
expect(eventBus.emit).toHaveBeenCalledWith('files:favorites:removed', file)
})

test('Favorite does NOT triggers node removal if favorite view but NOT root dir', async () => {
Expand Down Expand Up @@ -320,7 +320,7 @@ describe('Favorite action execute tests', () => {

// Check node change propagation
expect(file.attributes.favorite).toBe(0)
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toHaveBeenCalled()
expect(eventBus.emit).toBeCalledWith('files:favorites:removed', file)
})

Expand Down
17 changes: 10 additions & 7 deletions apps/files/src/actions/favoriteAction.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/**
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Node, View } from '@nextcloud/files'

import type { INode, IView } from '@nextcloud/files'

import StarOutlineSvg from '@mdi/svg/svg/star-outline.svg?raw'
import StarSvg from '@mdi/svg/svg/star.svg?raw'
Expand All @@ -26,17 +27,18 @@ const queue = new PQueue({ concurrency: 5 })
*
* @param nodes - The nodes to check
*/
function shouldFavorite(nodes: Node[]): boolean {
function shouldFavorite(nodes: INode[]): boolean {
return nodes.some((node) => node.attributes.favorite !== 1)
}

/**
* Favorite or unfavorite a node
*
* @param node
* @param view
* @param willFavorite
* @param node - The node to favorite/unfavorite
* @param view - The current view
* @param willFavorite - Whether to favorite or unfavorite the node
*/
export async function favoriteNode(node: Node, view: View, willFavorite: boolean): Promise<boolean> {
export async function favoriteNode(node: INode, view: IView, willFavorite: boolean): Promise<boolean> {
try {
// TODO: migrate to webdav tags plugin
const url = generateUrl('/apps/files/api/v1/files') + encodePath(node.path)
Expand All @@ -55,6 +57,7 @@ export async function favoriteNode(node: Node, view: View, willFavorite: boolean

// Update the node webdav attribute
Vue.set(node.attributes, 'favorite', willFavorite ? 1 : 0)
emit('files:node:updated', node)

// Dispatch event to whoever is interested
if (willFavorite) {
Expand Down
Loading
Loading