Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(files): Adjust getUniqueName for custom suffix and reuse for co…
…py-move-action

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Jan 20, 2024
commit b29c0cca24188fdd01679045cfdd18c91fad9a15
34 changes: 9 additions & 25 deletions apps/files/src/actions/moveOrCopyAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions apps/files/src/init-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand All @@ -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 })
Expand Down
3 changes: 1 addition & 2 deletions apps/files/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
14 changes: 2 additions & 12 deletions apps/files/src/newMenu/newFolder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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 = {
Expand All @@ -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'),
Expand Down
9 changes: 9 additions & 0 deletions apps/files/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
19 changes: 19 additions & 0 deletions apps/files/src/utils/fileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('/')
Expand Down