mirror of
https://github.com/nextcloud/server.git
synced 2026-02-26 03:11:28 -05:00
fix(files): Add more visual move / copy notification
* 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>
This commit is contained in:
parent
f2bdd06c4f
commit
64cc7afb12
3 changed files with 36 additions and 4 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 !== ''
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue