aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/src/components/FileEntry/FileEntryActions.vue52
-rw-r--r--apps/files/src/components/FileEntry/FileEntryName.vue32
-rw-r--r--apps/files/src/composables/useCurrentView.ts28
-rw-r--r--apps/files/src/composables/useFileActions.ts52
4 files changed, 120 insertions, 44 deletions
diff --git a/apps/files/src/components/FileEntry/FileEntryActions.vue b/apps/files/src/components/FileEntry/FileEntryActions.vue
index 21d5cd9e796..1ae22c98878 100644
--- a/apps/files/src/components/FileEntry/FileEntryActions.vue
+++ b/apps/files/src/components/FileEntry/FileEntryActions.vue
@@ -81,6 +81,7 @@ import type { PropType } from 'vue'
import { DefaultType, FileAction, Node, NodeStatus, View, getFileActions } from '@nextcloud/files'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
+import { defineComponent, toRef } from 'vue'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
@@ -88,9 +89,10 @@ import NcActionSeparator from '@nextcloud/vue/dist/Components/NcActionSeparator.
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft.vue'
-import Vue, { defineComponent } from 'vue'
-
import CustomElementRender from '../CustomElementRender.vue'
+
+import { useCurrentView } from '../../composables/useCurrentView.ts'
+import { useFileActions } from '../../composables/useFileActions.ts'
import logger from '../../logger.js'
// The registered actions list
@@ -132,6 +134,20 @@ export default defineComponent({
},
},
+ setup(props) {
+ const { currentView } = useCurrentView()
+ const {
+ defaultAction,
+ enabledActions,
+ } = useFileActions(toRef(props, 'source'), currentView)
+
+ return {
+ currentView,
+ defaultAction,
+ enabledActions,
+ }
+ },
+
data() {
return {
openedSubmenu: null as FileAction | null,
@@ -143,24 +159,11 @@ export default defineComponent({
// Remove any trailing slash but leave root slash
return (this.$route?.query?.dir?.toString() || '/').replace(/^(.+)\/$/, '$1')
},
- currentView(): View {
- return this.$navigation.active as View
- },
+
isLoading() {
return this.source.status === NodeStatus.LOADING
},
- // Sorted actions that are enabled for this node
- enabledActions() {
- if (this.source.attributes.failed) {
- return []
- }
-
- return actions
- .filter(action => !action.enabled || action.enabled([this.source], this.currentView))
- .sort((a, b) => (a.order || 0) - (b.order || 0))
- },
-
// Enabled action that are displayed inline
enabledInlineActions() {
if (this.filesListWidth < 768 || this.gridMode) {
@@ -177,11 +180,6 @@ export default defineComponent({
return this.enabledActions.filter(action => typeof action.renderInline === 'function')
},
- // Default actions
- enabledDefaultActions() {
- return this.enabledActions.filter(action => !!action?.default)
- },
-
// Actions shown in the menu
enabledMenuActions() {
// If we're in a submenu, only render the inline
@@ -269,7 +267,7 @@ export default defineComponent({
try {
// Set the loading marker
this.$emit('update:loading', action.id)
- Vue.set(this.source, 'status', NodeStatus.LOADING)
+ this.$set(this.source, 'status', NodeStatus.LOADING)
const success = await action.exec(this.source, this.currentView, this.currentDir)
@@ -289,7 +287,7 @@ export default defineComponent({
} finally {
// Reset the loading marker
this.$emit('update:loading', '')
- Vue.set(this.source, 'status', undefined)
+ this.$set(this.source, 'status', undefined)
// If that was a submenu, we just go back after the action
if (isSubmenu) {
@@ -298,12 +296,8 @@ export default defineComponent({
}
},
execDefaultAction(event) {
- if (this.enabledDefaultActions.length > 0) {
- event.preventDefault()
- event.stopPropagation()
- // Execute the first default action if any
- this.enabledDefaultActions[0].exec(this.source, this.currentView, this.currentDir)
- }
+ // If there is one execute it
+ this.defaultAction?.exec(this.source, this.currentView, this.currentDir)
},
isMenu(id: string) {
diff --git a/apps/files/src/components/FileEntry/FileEntryName.vue b/apps/files/src/components/FileEntry/FileEntryName.vue
index 3e671c0c141..b32922b16d7 100644
--- a/apps/files/src/components/FileEntry/FileEntryName.vue
+++ b/apps/files/src/components/FileEntry/FileEntryName.vue
@@ -37,7 +37,7 @@
</template>
<script lang="ts">
-import type { Node, View } from '@nextcloud/files'
+import type { Node } from '@nextcloud/files'
import type { PropType } from 'vue'
import { showError, showSuccess } from '@nextcloud/dialogs'
@@ -45,13 +45,15 @@ import { emit } from '@nextcloud/event-bus'
import { FileType, NodeStatus, Permission } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import { translate as t } from '@nextcloud/l10n'
+import { defineComponent, toRef } from 'vue'
import axios from '@nextcloud/axios'
-import { isAxiosError } from 'axios'
-import Vue, { defineComponent } from 'vue'
+import Axios from 'axios'
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import { useRenamingStore } from '../../store/renaming.ts'
+import { useCurrentView } from '../../composables/useCurrentView'
+import { useFileActions } from '../../composables/useFileActions'
import logger from '../../logger.js'
const forbiddenCharacters = loadState<string[]>('files', 'forbiddenCharacters', [])
@@ -90,18 +92,20 @@ export default defineComponent({
},
},
- setup() {
+ setup(props) {
const renamingStore = useRenamingStore()
+ const { currentView } = useCurrentView()
+ const { defaultAction } = useFileActions(toRef(props, 'source'), currentView)
+
return {
+ currentView,
+ defaultAction,
+
renamingStore,
}
},
computed: {
- currentView(): View {
- return this.$navigation.active as View
- },
-
isRenaming() {
return this.renamingStore.renamingNode === this.source
},
@@ -135,10 +139,8 @@ export default defineComponent({
}
}
- const enabledDefaultActions = this.$parent?.$refs?.actions?.enabledDefaultActions
- if (enabledDefaultActions?.length > 0) {
- const action = enabledDefaultActions[0]
- const displayName = action.displayName([this.source], this.currentView)
+ if (this.defaultAction !== null) {
+ const displayName = this.defaultAction.displayName([this.source], this.currentView)
return {
is: 'a',
params: {
@@ -283,7 +285,7 @@ export default defineComponent({
}
// Set loading state
- Vue.set(this.source, 'status', NodeStatus.LOADING)
+ this.$set(this.source, 'status', NodeStatus.LOADING)
// Update node
this.source.rename(newName)
@@ -314,7 +316,7 @@ export default defineComponent({
this.source.rename(oldName)
this.$refs.renameInput?.focus()
- if (isAxiosError(error)) {
+ if (Axios.isAxiosError(error)) {
// TODO: 409 means current folder does not exist, redirect ?
if (error?.response?.status === 404) {
showError(t('files', 'Could not rename "{oldName}", it does not exist any more', { oldName }))
@@ -328,7 +330,7 @@ export default defineComponent({
// Unknown error
showError(t('files', 'Could not rename "{oldName}"', { oldName }))
} finally {
- Vue.set(this.source, 'status', undefined)
+ this.$set(this.source, 'status', undefined)
}
},
diff --git a/apps/files/src/composables/useCurrentView.ts b/apps/files/src/composables/useCurrentView.ts
new file mode 100644
index 00000000000..7421258be44
--- /dev/null
+++ b/apps/files/src/composables/useCurrentView.ts
@@ -0,0 +1,28 @@
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import type { View } from '@nextcloud/files'
+import { getNavigation } from '@nextcloud/files'
+import { onMounted, onUnmounted, readonly, shallowRef } from 'vue'
+
+/**
+ * Composable to get the current active view
+ */
+export function useCurrentView() {
+ const navigation = getNavigation()
+ const currentView = shallowRef<View>()
+
+ const onUpdateView = ({ detail }) => { currentView.value = detail }
+
+ onMounted(() => navigation.addEventListener('updateActive', onUpdateView))
+ onUnmounted(() => navigation.removeEventListener('updateActive', onUpdateView))
+
+ return {
+ /**
+ * The currently active files view
+ */
+ currentView: readonly(currentView),
+ }
+}
diff --git a/apps/files/src/composables/useFileActions.ts b/apps/files/src/composables/useFileActions.ts
new file mode 100644
index 00000000000..53b8eae68ec
--- /dev/null
+++ b/apps/files/src/composables/useFileActions.ts
@@ -0,0 +1,52 @@
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import type { FileAction, Node, View } from '@nextcloud/files'
+import type { MaybeRef } from '@vueuse/core'
+
+import { NodeStatus, getFileActions } from '@nextcloud/files'
+import { computed, unref } from 'vue'
+
+export function useFileActions(node: MaybeRef<Node>, view: MaybeRef<View>) {
+ const actions = getFileActions()
+
+ const enabledActions = computed<FileAction[]>(() => {
+ if (unref(node).status === NodeStatus.FAILED) {
+ return []
+ }
+
+ return actions
+ .filter((action: FileAction) => action.enabled === undefined || action.enabled([unref(node)], unref(view)))
+ .sort((a: FileAction, b: FileAction) => (a.order || 0) - (b.order || 0))
+ })
+
+ const enabledDefaultActions = computed<FileAction[]>(() => {
+ return enabledActions.value
+ .filter((action: FileAction) => !!action.default)
+ })
+
+ const defaultAction = computed<FileAction | null>(() => enabledDefaultActions[0] ?? null)
+
+ return {
+ /**
+ * All registered actions
+ */
+ actions,
+ /**
+ * The default action to use on the current node and view
+ */
+ defaultAction,
+ /**
+ * All currently enabled actions for the current node and view
+ * This list is sorted by the action order
+ */
+ enabledActions,
+ /**
+ * All currently enabled default actions for the current node and view
+ * This list is sorted by the action order
+ */
+ enabledDefaultActions,
+ }
+}