Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 4 additions & 1 deletion l10n/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ msgstr ""
msgid "Existing version"
msgstr ""

msgid "If you select both versions, the copied file will have a number added to its name."
msgid "If you select both versions, the incoming file will have a number added to its name."
msgstr ""

msgid "Last modified date unknown"
Expand Down Expand Up @@ -87,6 +87,9 @@ msgstr ""
msgid "When an incoming folder is selected, any conflicting files within it will also be overwritten."
msgstr ""

msgid "When an incoming folder is selected, the content is written into the existing folder and a recursive conflict resolution is performed."
msgstr ""

msgid "Which files do you want to keep?"
msgstr ""

Expand Down
8 changes: 5 additions & 3 deletions lib/components/ConflictPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
<!-- Description -->
<p id="conflict-picker-description" class="conflict-picker__description">
{{ t('Which files do you want to keep?') }}<br>
{{ t('If you select both versions, the copied file will have a number added to its name.') }}
<template v-if="!recursiveUpload">
<br>
{{ t('If you select both versions, the incoming file will have a number added to its name.') }}<br>
<template v-if="recursiveUpload">
{{ t('When an incoming folder is selected, the content is written into the existing folder and a recursive conflict resolution is performed.') }}
</template>
<template v-else>
{{ t('When an incoming folder is selected, any conflicting files within it will also be overwritten.') }}
</template>
</p>
Expand Down
44 changes: 33 additions & 11 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export { Upload, Status as UploadStatus } from './upload'

let _uploader: Uploader | null = null

export type ConflictResolutionResult = {
selected: (File|FileSystemEntry|Node)[],
renamed: (File|FileSystemEntry|Node)[],
export type ConflictResolutionResult<T extends File|FileSystemEntry|Node> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should create a type out of those to ease its use?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes sense, but I need generics here to allow returning the same as the input.
E.g. if you put in an array of File you also get back an array of File and not Array<File|Node|FileSystemEntry>.
So this prevents casting it every time you use it

selected: T[],
renamed: T[],
}
/**
* Get an Uploader instance
Expand Down Expand Up @@ -47,14 +47,28 @@ export function upload(destinationPath: string, file: File): Uploader {
return uploader
}

interface ConflictPickerOptions {
/**
* When this is set to true a hint is shown that conflicts in directories are handles recursivly
* You still need to call this function for each directory separatly.
*/
recursive?: boolean
}

/**
* Open the conflict resolver
* @param {string} dirname the directory name
* @param {(File|Node)[]} conflicts the incoming files
* @param {Node[]} content all the existing files in the directory
* @param {ConflictPickerOptions} options Optional settings for the conflict picker
* @return {Promise<ConflictResolutionResult>} the selected and renamed files
*/
export async function openConflictPicker(dirname: string | undefined, conflicts: (File|FileSystemEntry|Node)[], content: Node[]): Promise<ConflictResolutionResult> {
export async function openConflictPicker<T extends File|FileSystemEntry|Node>(
dirname: string | undefined,
conflicts: T[],
content: Node[],
options?: ConflictPickerOptions,
): Promise<ConflictResolutionResult<T>> {
const ConflictPicker = defineAsyncComponent(() => import('./components/ConflictPicker.vue')) as AsyncComponent
return new Promise((resolve, reject) => {
const picker = new Vue({
Expand All @@ -64,9 +78,10 @@ export async function openConflictPicker(dirname: string | undefined, conflicts:
dirname,
conflicts,
content,
recursiveUpload: options?.recursive === true,
},
on: {
submit(results: ConflictResolutionResult) {
submit(results: ConflictResolutionResult<T>) {
// Return the results
resolve(results)

Expand Down Expand Up @@ -94,21 +109,28 @@ export async function openConflictPicker(dirname: string | undefined, conflicts:

/**
* Check if there is a conflict between two sets of files
* @param {(File|Node)[]} files the incoming files
* @param {Array<File|FileSystemEntry|Node>} files the incoming files
* @param {Node[]} content all the existing files in the directory
* @return {boolean} true if there is a conflict
*/
export function hasConflict(files: (File|FileSystemEntry|Node)[], content: Node[]): boolean {
// If the browser does not support the file system api we do not want a ReferenceError, so fallback to File
const SupportedFileSystemEntry = window.FileSystemEntry ?? File
return getConflicts(files, content).length > 0
}

/**
* Get the conflicts between two sets of files
* @param {Array<File|FileSystemEntry|Node>} files the incoming files
* @param {Node[]} content all the existing files in the directory
* @return {boolean} true if there is a conflict
*/
export function getConflicts<T extends File|FileSystemEntry|Node>(files: T[], content: Node[]): T[] {
const contentNames = content.map((node: Node) => node.basename)
const conflicts = files.filter((node: File|FileSystemEntry|Node) => {
const name = (node instanceof File || node instanceof SupportedFileSystemEntry) ? node.name : node.basename
const name = (node instanceof File || node instanceof FileSystemEntry) ? node.name : node.basename
return contentNames.indexOf(name) !== -1
}) as Node[]
})

return conflicts.length > 0
return conflicts
}

/** UploadPicker vue component */
Expand Down