fix(files): Adjust `getUniqueName` for custom suffix and reuse for copy-move-action

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
pull/42889/head
Ferdinand Thiessen 4 months ago
parent eaad30c346
commit b29c0cca24
No known key found for this signature in database
GPG Key ID: 45FAE7268762B400

@ -39,6 +39,7 @@ import FolderMoveSvg from '@mdi/svg/svg/folder-move.svg?raw'
import { MoveCopyAction, canCopy, canMove, getQueue } from './moveOrCopyActionUtils'
import logger from '../logger'
import { getUniqueName } from '../utils/fileUtils'
/**
* Return the action that is possible for the given nodes
@ -67,30 +68,6 @@ const getActionForNodes = (nodes: Node[]): MoveCopyAction => {
* @return {Promise<void>} A promise that resolves when the copy/move is done
*/
export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, method: MoveCopyAction.COPY | MoveCopyAction.MOVE, overwrite = false) => {
/**
* Create an unique name for a node
* @param node Node that is copied
* @param otherNodes Other nodes in the target directory to check for unique name
* @return Either the node basename, if unique, or the name with a `(copy N)` suffix that is unique
*/
const makeUniqueName = (node: Node, otherNodes: Node[]|FileStat[]) => {
const basename = node.basename.slice(0, node.basename.lastIndexOf('.'))
let index = 0
const currentName = () => {
switch (index) {
case 0: return node.basename
case 1: return `${basename} (copy)${node.extension ?? ''}`
default: return `${basename} ${t('files', '(copy %n)', undefined, index)}${node.extension ?? ''}` // TRANSLATORS: Meaning it is the n'th copy of a file
}
}
while (otherNodes.some((other: Node|FileStat) => currentName() === other.basename)) {
index += 1
}
return currentName()
}
if (!destination) {
return
}
@ -122,6 +99,13 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth
const queue = getQueue()
return await queue.add(async () => {
const copySuffix = (index: number) => {
if (index === 1) {
return t('files', '(copy)') // TRANSLATORS: Mark a file as a copy of another file
}
return t('files', '(copy %n)', undefined, index) // TRANSLATORS: Meaning it is the n'th copy of a file
}
try {
const client = davGetClient()
const currentPath = join(davRootPath, node.path)
@ -132,7 +116,7 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth
// If we do not allow overwriting then find an unique name
if (!overwrite) {
const otherNodes = await client.getDirectoryContents(destinationPath) as FileStat[]
target = makeUniqueName(node, otherNodes)
target = getUniqueName(node.basename, otherNodes.map((n) => n.basename), copySuffix)
}
await client.copyFile(currentPath, join(destinationPath, target))
// If the node is copied into current directory the view needs to be updated

@ -21,6 +21,7 @@
*
*/
import type { Entry } from '@nextcloud/files'
import type { TemplateFile } from './types'
import { Folder, Node, Permission, addNewFileMenuEntry, removeNewFileMenuEntry } from '@nextcloud/files'
import { generateOcsUrl } from '@nextcloud/router'
@ -35,7 +36,7 @@ import Vue from 'vue'
import PlusSvg from '@mdi/svg/svg/plus.svg?raw'
import TemplatePickerView from './views/TemplatePicker.vue'
import { getUniqueName } from './newMenu/newFolder'
import { getUniqueName } from './utils/fileUtils.ts'
import { getCurrentUser } from '@nextcloud/auth'
// Set up logger
@ -58,7 +59,7 @@ TemplatePickerRoot.id = 'template-picker'
document.body.appendChild(TemplatePickerRoot)
// Retrieve and init templates
let templates = loadState('files', 'templates', [])
let templates = loadState<TemplateFile[]>('files', 'templates', [])
let templatesPath = loadState('files', 'templates_path', false)
logger.debug('Templates providers', { templates })
logger.debug('Templates folder', { templatesPath })

@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import MenuIcon from '@mdi/svg/svg/sun-compass.svg?raw'
import { FileAction, addNewFileMenuEntry, registerDavProperty, registerFileAction } from '@nextcloud/files'
import { addNewFileMenuEntry, registerDavProperty, registerFileAction } from '@nextcloud/files'
import { action as deleteAction } from './actions/deleteAction'
import { action as downloadAction } from './actions/downloadAction'

@ -21,7 +21,7 @@
*/
import type { Entry, Node } from '@nextcloud/files'
import { basename, extname } from 'path'
import { basename } from 'path'
import { emit } from '@nextcloud/event-bus'
import { getCurrentUser } from '@nextcloud/auth'
import { Permission, Folder } from '@nextcloud/files'
@ -31,6 +31,7 @@ import axios from '@nextcloud/axios'
import FolderPlusSvg from '@mdi/svg/svg/folder-plus.svg?raw'
import { getUniqueName } from '../utils/fileUtils.ts'
import logger from '../logger'
type createFolderResponse = {
@ -55,17 +56,6 @@ const createNewFolder = async (root: Folder, name: string): Promise<createFolder
}
}
// TODO: move to @nextcloud/files
export const getUniqueName = (name: string, names: string[]): string => {
let newName = name
let i = 1
while (names.includes(newName)) {
const ext = extname(name)
newName = `${basename(name, ext)} (${i++})${ext}`
}
return newName
}
export const entry = {
id: 'newFolder',
displayName: t('files', 'New folder'),

@ -111,3 +111,12 @@ export interface UploaderStore {
export interface DragAndDropStore {
dragging: FileId[]
}
export interface TemplateFile {
app: string
label: string
extension: string
iconClass?: string
mimetypes: string[]
ratio?: number
}

@ -21,6 +21,25 @@
*/
import { FileType, type Node } from '@nextcloud/files'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import { basename, extname } from 'path'
// TODO: move to @nextcloud/files
/**
* Create an unique file name
* @param name The initial name to use
* @param otherNames Other names that are already used
* @param suffix A function that takes an index an returns a suffix to add, defaults to '(index)'
* @return Either the initial name, if unique, or the name with the suffix so that the name is unique
*/
export const getUniqueName = (name: string, otherNames: string[], suffix = (n: number) => `(${n})`): string => {
let newName = name
let i = 1
while (otherNames.includes(newName)) {
const ext = extname(name)
newName = `${basename(name, ext)} ${suffix(i++)}${ext}`
}
return newName
}
export const encodeFilePath = function(path) {
const pathSections = (path.startsWith('/') ? path : `/${path}`).split('/')

Loading…
Cancel
Save