]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix(files): Add more visual move / copy notification backport/47910/stable30
authorFerdinand Thiessen <opensource@fthiessen.de>
Wed, 11 Sep 2024 20:29:31 +0000 (22:29 +0200)
committerFerdinand Thiessen <opensource@fthiessen.de>
Tue, 15 Oct 2024 14:59:27 +0000 (16:59 +0200)
* Resolves: https://github.com/nextcloud/server/issues/46645

This adds loading toast notification while the move operation is running.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
apps/files/src/actions/moveOrCopyAction.ts
apps/files/src/components/FileEntryMixin.ts
apps/files/src/views/FilesList.vue

index 4d70995a0e4a0c1c2674e82874bbefb6a1fa192c..fa7b5d4345ae9439c2f449efd1cf4e377269c668 100644 (file)
@@ -8,7 +8,7 @@ import type { FileStat, ResponseDataDetailed } from 'webdav'
 import type { MoveCopyResult } from './moveOrCopyActionUtils'
 
 import { isAxiosError } from '@nextcloud/axios'
-import { FilePickerClosed, getFilePickerBuilder, showError, showInfo } from '@nextcloud/dialogs'
+import { FilePickerClosed, getFilePickerBuilder, showError, showInfo, TOAST_PERMANENT_TIMEOUT } from '@nextcloud/dialogs'
 import { emit } from '@nextcloud/event-bus'
 import { FileAction, FileType, NodeStatus, davGetClient, davRootPath, davResultToNode, davGetDefaultPropfind, getUniqueName, Permission } from '@nextcloud/files'
 import { translate as t } from '@nextcloud/l10n'
@@ -40,6 +40,28 @@ const getActionForNodes = (nodes: Node[]): MoveCopyAction => {
        return MoveCopyAction.COPY
 }
 
+/**
+ * Create a loading notification toast
+ * @param mode The move or copy mode
+ * @param source Name of the node that is copied / moved
+ * @param destination Destination path
+ * @return {() => void} Function to hide the notification
+ */
+function createLoadingNotification(mode: MoveCopyAction, source: string, destination: string): () => void {
+       const text = mode === MoveCopyAction.MOVE ? t('files', 'Moving "{source}" to "{destination}" …', { source, destination }) : t('files', 'Copying "{source}" to "{destination}" …', { source, destination })
+
+       let toast: ReturnType<typeof showInfo>|undefined
+       toast = showInfo(
+               `<span class="icon icon-loading-small toast-loading-icon"></span> ${text}`,
+               {
+                       isHTML: true,
+                       timeout: TOAST_PERMANENT_TIMEOUT,
+                       onRemove: () => { toast?.hideToast(); toast = undefined },
+               },
+       )
+       return () => toast && toast.hideToast()
+}
+
 /**
  * Handle the copy/move of a node to a destination
  * This can be imported and used by other scripts/components on server
@@ -80,6 +102,7 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth
 
        // Set loading state
        Vue.set(node, 'status', NodeStatus.LOADING)
+       const actionFinished = createLoadingNotification(method, node.basename, destination.path)
 
        const queue = getQueue()
        return await queue.add(async () => {
@@ -162,7 +185,8 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth
                        logger.debug(error as Error)
                        throw new Error()
                } finally {
-                       Vue.set(node, 'status', undefined)
+                       Vue.set(node, 'status', '')
+                       actionFinished()
                }
        })
 }
@@ -309,7 +333,7 @@ export const action = new FileAction({
                if (result === false) {
                        showInfo(nodes.length === 1
                                ? t('files', 'Cancelled move or copy of "{filename}".', { filename: nodes[0].displayname })
-                               : t('files', 'Cancelled move or copy operation')
+                               : t('files', 'Cancelled move or copy operation'),
                        )
                        return nodes.map(() => null)
                }
index f2a6f89cf22e314117fa220a71a30b963790a0a0..33c077c61598673ceef8ca317faa7c4bebb83567 100644 (file)
@@ -71,8 +71,9 @@ export default defineComponent({
                uniqueId() {
                        return hashCode(this.source.source)
                },
+
                isLoading() {
-                       return this.source.status === NodeStatus.LOADING
+                       return this.source.status === NodeStatus.LOADING || this.loading !== ''
                },
 
                /**
index 70c9a38607e36006bfc0be144ec669448e614e6f..c7574c8a7a7da18e9b9bcbb1d253cd48cb03d26f 100644 (file)
@@ -662,6 +662,13 @@ export default defineComponent({
 </script>
 
 <style scoped lang="scss">
+:global(.toast-loading-icon) {
+       // Reduce start margin (it was made for text but this is an icon)
+       margin-inline-start: -4px;
+       // 16px icon + 5px on both sides
+       min-width: 26px;
+}
+
 .app-content {
        // Virtual list needs to be full height and is scrollable
        display: flex;