Skip to content

Commit

Permalink
Merge pull request #1166 from nextcloud-libraries/feat/allow-filesyst…
Browse files Browse the repository at this point in the history
…ementry-conflictpicker

feat(ConflictPicker): Allow to use `FileSystemEntry`
  • Loading branch information
susnux authored Apr 24, 2024
2 parents 1c7e227 + 04e61df commit b23360b
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 29 deletions.
63 changes: 40 additions & 23 deletions lib/components/ConflictPicker.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<NcDialog class="conflict-picker"
<NcDialog can-close
class="conflict-picker"
data-cy-conflict-picker
:close-on-click-outside="false"
:can-close="true"
:show="opened"
:name="name"
size="large"
Expand All @@ -12,8 +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.') }}<br>
{{ t('When an incoming folder is selected, any conflicting files within it will also be overwritten.') }}
{{ t('If you select both versions, the copied file will have a number added to its name.') }}
<template v-if="!recursiveUpload">
<br>
{{ t('When an incoming folder is selected, any conflicting files within it will also be overwritten.') }}
</template>
</p>
</div>

Expand Down Expand Up @@ -92,12 +95,12 @@
</template>

<script lang="ts">
import type { ConflictResolutionResult } from '../index.ts'
import type { Node } from '@nextcloud/files'
import type { PropType } from 'vue'
import type { ConflictResolutionResult } from '../index.ts'
import { basename, extname } from 'path'
import { defineComponent } from 'vue'
import { Node } from '@nextcloud/files'
import { showError } from '@nextcloud/dialogs'
import ArrowRight from 'vue-material-design-icons/ArrowRight.vue'
Expand Down Expand Up @@ -139,21 +142,35 @@ export default defineComponent({
/** New files being moved/uploaded */
conflicts: {
type: Array as PropType<(Node|File)[]>,
type: Array as PropType<(Node|File|FileSystemEntry)[]>,
required: true,
},
/**
* If set to true no hint about overwriting directory content will be shown
*/
recursiveUpload: {
type: Boolean,
default: false,
},
},
emits: ['cancel', 'submit'],
setup() {
// Non reactive props
return {
blockedTitle: t('You need to select at least one version of each file to continue.'),
}
},
data() {
return {
// computed list of conflicting files already present in the directory
files: [] as Node[],
opened: true,
blockedTitle: t('You need to select at least one version of each file to continue.'),
newSelected: [] as (Node|File)[],
newSelected: [] as (Node|File|FileSystemEntry)[],
oldSelected: [] as Node[],
}
},
Expand Down Expand Up @@ -232,8 +249,8 @@ export default defineComponent({
mounted() {
// Using map keep the same order
this.files = this.conflicts.map((conflict: File|Node) => {
const name = (conflict instanceof File) ? conflict.name : conflict.basename
this.files = this.conflicts.map((conflict: File|FileSystemEntry|Node) => {
const name = (conflict instanceof File || conflict instanceof FileSystemEntry) ? conflict.name : conflict.basename
return this.content.find((node: Node) => node.basename === name)
}).filter(Boolean) as Node[]
Expand Down Expand Up @@ -273,29 +290,29 @@ export default defineComponent({
// the user can still click and trigger the
// form validation check.
if (!this.isEnoughSelected) {
this.scrollValidityInputIntoView()
this.$refs.form.reportValidity()
this.scrollValidityInputIntoView();
(this.$refs.form as HTMLFormElement).reportValidity()
showError(this.blockedTitle)
return
}
const selectedOldNames = this.oldSelected.map((node: Node) => node.basename) as string[]
const selectedOldNames = (this.oldSelected as Node[]).map((node: Node) => node.basename) as string[]
const directoryContent = this.content.map((node: Node) => node.basename) as string[]
// Files that got selected twice (new and old) gets renamed
const renamed = [] as (File|Node)[]
const toRename = this.newSelected.filter((node: File|Node) => {
const name = (node instanceof File) ? node.name : node.basename
const renamed = [] as (File|FileSystemEntry|Node)[]
const toRename = (this.newSelected as (File|FileSystemEntry|Node)[]).filter((node) => {
const name = (node instanceof File || node instanceof FileSystemEntry) ? node.name : node.basename
return selectedOldNames.includes(name)
}) as (File|Node)[]
})
// Rename files
if (toRename.length > 0) {
toRename.forEach(file => {
const name = (file instanceof File) ? file.name : file.basename
const name = (file instanceof File || file instanceof FileSystemEntry) ? file.name : file.basename
const newName = this.getUniqueName(name, directoryContent)
// If File, create a new one with the new name
if (file instanceof File) {
if (file instanceof File || file instanceof FileSystemEntry) {
// Keep the original file object and force rename
Object.defineProperty(file, 'name', { value: newName })
renamed.push(file)
Expand All @@ -309,8 +326,8 @@ export default defineComponent({
}
// Remove files that got renamed from the new selection
const selected = this.newSelected.filter((node: File|Node) => {
const name = (node instanceof File) ? node.name : node.basename
const selected = (this.newSelected as (File|FileSystemEntry|Node)[]).filter((node) => {
const name = (node instanceof File || node instanceof FileSystemEntry) ? node.name : node.basename
// files that are not in the old selection
return !selectedOldNames.includes(name) && !toRename.includes(node)
}) as (File|Node)[]
Expand Down Expand Up @@ -349,7 +366,7 @@ export default defineComponent({
const selector = '.checkbox-radio-switch input[type="checkbox"]'
// Reset the custom validity of all checkboxes
const checkboxes = [...this.$el.querySelectorAll(selector)] as HTMLInputElement[]
const checkboxes: HTMLInputElement[] = Array.from(this.$el.querySelectorAll(selector))
checkboxes.forEach(input => input?.setCustomValidity?.(''))
// Scroll the first invalid input into view if any
Expand Down
15 changes: 9 additions & 6 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export { Upload, Status as UploadStatus } from './upload'
let _uploader: Uploader | null = null

export type ConflictResolutionResult = {
selected: (File|Node)[],
renamed: (File|Node)[],
selected: (File|FileSystemEntry|Node)[],
renamed: (File|FileSystemEntry|Node)[],
}
/**
* Get an Uploader instance
Expand Down Expand Up @@ -54,7 +54,7 @@ export function upload(destinationPath: string, file: File): Uploader {
* @param {Node[]} content all the existing files in the directory
* @return {Promise<ConflictResolutionResult>} the selected and renamed files
*/
export async function openConflictPicker(dirname: string, conflicts: (File|Node)[], content: Node[]): Promise<ConflictResolutionResult> {
export async function openConflictPicker(dirname: string | undefined, conflicts: (File|FileSystemEntry|Node)[], content: Node[]): Promise<ConflictResolutionResult> {
const ConflictPicker = defineAsyncComponent(() => import('./components/ConflictPicker.vue')) as AsyncComponent
return new Promise((resolve, reject) => {
const picker = new Vue({
Expand Down Expand Up @@ -98,10 +98,13 @@ export async function openConflictPicker(dirname: string, conflicts: (File|Node)
* @param {Node[]} content all the existing files in the directory
* @return {boolean} true if there is a conflict
*/
export function hasConflict(files: (File|Node)[], content: Node[]): boolean {
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

const contentNames = content.map((node: Node) => node.basename)
const conflicts = files.filter((node: File|Node) => {
const name = (node instanceof File) ? node.name : node.basename
const conflicts = files.filter((node: File|FileSystemEntry|Node) => {
const name = (node instanceof File || node instanceof SupportedFileSystemEntry) ? node.name : node.basename
return contentNames.indexOf(name) !== -1
}) as Node[]

Expand Down

0 comments on commit b23360b

Please sign in to comment.