Skip to content

Commit

Permalink
fix(files): Add more visual move / copy notification
Browse files Browse the repository at this point in the history
* Resolves: #46645

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

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
  • Loading branch information
susnux authored and kesselb committed Sep 30, 2024
1 parent be87dec commit d97e17b
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 13 deletions.
30 changes: 27 additions & 3 deletions apps/files/src/actions/moveOrCopyAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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()
}
})
}
Expand Down Expand Up @@ -313,7 +337,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)
}
Expand Down
3 changes: 2 additions & 1 deletion apps/files/src/components/FileEntryMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,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 !== ''
},

/**
Expand Down
7 changes: 7 additions & 0 deletions apps/files/src/views/FilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,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;
Expand Down
4 changes: 2 additions & 2 deletions dist/core-common.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/core-common.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/files-init.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-init.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/files-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-main.js.map

Large diffs are not rendered by default.

0 comments on commit d97e17b

Please sign in to comment.