Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>tags/v29.0.0beta1
@@ -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('/') |