22 <NcDialog class =" conflict-picker"
33 data-cy-conflict-picker
44 :close-on-click-outside =" false"
5- :can-close =" false "
5+ :can-close =" true "
66 :show =" opened"
77 :name =" name"
88 size =" large"
9- @close =" onCancel" >
9+ @closing =" onCancel" >
1010 <!-- Header -->
1111 <div class =" conflict-picker__header" >
1212 <!-- Description -->
1313 <p id =" conflict-picker-description" class =" conflict-picker__description" >
1414 {{ t('Which files do you want to keep?') }}<br >
15- {{ t('If you select both versions, the copied file will have a number added to its name.') }}
15+ {{ t('If you select both versions, the copied file will have a number added to its name.') }}<br >
16+ {{ t('When an incoming folder is selected, any conflicting files within it will also be overwritten.') }}
1617 </p >
1718 </div >
1819
4950
5051 <!-- Controls -->
5152 <template #actions >
52- <NcButton data-cy-conflict-picker-skip @click =" onSkip" >
53+ <!-- Cancel the entire operation -->
54+ <NcButton :aria-label =" t('Cancel')"
55+ :title =" t('Cancel the entire operation')"
56+ data-cy-conflict-picker-cancel
57+ type =" tertiary"
58+ @click =" onCancel" >
59+ <template #icon >
60+ <Close :size =" 20" />
61+ </template >
62+ {{ t('Cancel') }}
63+ </NcButton >
64+
65+ <!-- Align right -->
66+ <span class =" dialog__actions-separator" />
67+
68+ <NcButton :aria-label =" skipButtonLabel"
69+ data-cy-conflict-picker-skip
70+ @click =" onSkip" >
5371 <template #icon >
5472 <Close :size =" 20" />
5573 </template >
5674 {{ skipButtonLabel }}
5775 </NcButton >
58- <NcButton type = " primary "
76+ <NcButton :aria-label = " t('Continue') "
5977 :class =" { 'button-vue--disabled': !isEnoughSelected}"
6078 :title =" isEnoughSelected ? '' : blockedTitle"
61- native-type =" submit"
6279 data-cy-conflict-picker-submit
80+ native-type =" submit"
81+ type =" primary"
6382 @click.stop.prevent =" onSubmit" >
6483 <template #icon >
6584 <ArrowRight :size =" 20" />
@@ -75,23 +94,23 @@ import type { ConflictResolutionResult } from '../index.ts'
7594import type { PropType } from ' vue'
7695
7796import { basename , extname } from ' path'
97+ import { defineComponent } from ' vue'
7898import { Node } from ' @nextcloud/files'
7999import { showError } from ' @nextcloud/dialogs'
80- import Vue from ' vue'
81100
82- import NcButton from ' @nextcloud/vue/dist/Components/NcButton.js'
83- import NcCheckboxRadioSwitch from ' @nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
84- import NcDialog from ' @nextcloud/vue/dist/Components/NcDialog.js'
85101import ArrowRight from ' vue-material-design-icons/ArrowRight.vue'
86102import Close from ' vue-material-design-icons/Close.vue'
103+ import NcCheckboxRadioSwitch from ' @nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
104+ import NcDialog from ' @nextcloud/vue/dist/Components/NcDialog.js'
105+ import NcButton from ' @nextcloud/vue/dist/Components/NcButton.js'
87106
88107import { n , t } from ' ../utils/l10n.ts'
89108import logger from ' ../utils/logger.ts'
90109import NodesPicker from ' ./NodesPicker.vue'
91110
92111export type NodesPickerRef = InstanceType <typeof NodesPicker >
93112
94- export default Vue . extend ({
113+ export default defineComponent ({
95114 name: ' ConflictPicker' ,
96115
97116 components: {
@@ -123,6 +142,8 @@ export default Vue.extend({
123142 },
124143 },
125144
145+ emits: [' cancel' , ' submit' ],
146+
126147 data() {
127148 return {
128149 // computed list of conflicting files already present in the directory
@@ -207,13 +228,12 @@ export default Vue.extend({
207228 },
208229 },
209230
210- beforeMount () {
231+ mounted () {
211232 // Using map keep the same order
212233 this .files = this .conflicts .map ((conflict : File | Node ) => {
213234 const name = (conflict instanceof File ) ? conflict .name : conflict .basename
214235 return this .content .find ((node : Node ) => node .basename === name )
215236 }).filter (Boolean ) as Node []
216- logger .debug (' ConflictPicker initialised' , { files: this .files , conflicts: this .conflicts , content: this .content })
217237
218238 if (this .conflicts .length === 0 || this .files .length === 0 ) {
219239 const error = new Error (' ConflictPicker: files and conflicts must not be empty' )
@@ -222,10 +242,13 @@ export default Vue.extend({
222242 }
223243
224244 if (this .conflicts .length !== this .files .length ) {
225- const error = new Error (' ConflictPicker: files and conflicts must have the same length' )
245+ const error = new Error (' ConflictPicker: files and conflicts must have the same length. Make sure you filter out non conflicting files from the conflicts array. ' )
226246 this .onCancel (error )
227247 throw error
228248 }
249+
250+ // Successful initialisation
251+ logger .debug (' ConflictPicker initialised' , { files: this .files , conflicts: this .conflicts , content: this .content })
229252 },
230253
231254 methods: {
@@ -269,11 +292,15 @@ export default Vue.extend({
269292 toRename .forEach (file => {
270293 const name = (file instanceof File ) ? file .name : file .basename
271294 const newName = this .getUniqueName (name , directoryContent )
295+ // If File, create a new one with the new name
272296 if (file instanceof File ) {
273- file = new File ([file ], newName , { type: file .type , lastModified: file .lastModified })
297+ // Keep the original file object and force rename
298+ Object .defineProperty (file , ' name' , { value: newName })
274299 renamed .push (file )
275300 return
276301 }
302+
303+ // Rename the node
277304 file .rename (newName )
278305 renamed .push (file )
279306 })
@@ -283,7 +310,7 @@ export default Vue.extend({
283310 const selected = this .newSelected .filter ((node : File | Node ) => {
284311 const name = (node instanceof File ) ? node .name : node .basename
285312 // files that are not in the old selection
286- return ! selectedOldNames .includes (name )
313+ return ! selectedOldNames .includes (name ) && ! toRename . includes ( node )
287314 }) as (File | Node )[]
288315
289316 logger .debug (' Conflict resolved' , { selected , renamed })
@@ -373,17 +400,15 @@ export default Vue.extend({
373400 z-index : 10 ;
374401 top : 0 ;
375402 padding : 0 var (--margin );
376- padding-bottom : 0 ;
403+ padding-bottom : var ( --secondary-margin ) ;
377404 }
378405
379406 & __form {
380407 position : relative ;
381408 overflow : auto ;
382409 padding : 0 var (--margin );
383- // 12 px to underlap the header and controls
384- // and have the gradient background visible
385- padding-bottom : 12px ;
386- margin-bottom : -12px ;
410+ // overlap header bottom padding
411+ margin-top : calc (-1 * var (--secondary-margin ));
387412 }
388413
389414 fieldset {
@@ -425,6 +450,14 @@ export default Vue.extend({
425450 opacity : .5 ;
426451 filter : saturate (.7 );
427452 }
453+
454+ :deep(.dialog__actions ) {
455+ width : auto ;
456+ margin-inline : 12px ;
457+ span .dialog__actions-separator {
458+ margin-left : auto ;
459+ }
460+ }
428461}
429462
430463// Responsive layout
@@ -445,4 +478,16 @@ export default Vue.extend({
445478 }
446479}
447480
481+ // Responsive layout
482+ @media screen and (max-width : 512px ) {
483+ .conflict-picker {
484+ :deep (.dialog__actions ) {
485+ flex-wrap : wrap ;
486+ span .dialog__actions-separator {
487+ // Make the second row wrap
488+ width : 100% ;
489+ }
490+ }
491+ }
492+ }
448493 </style >
0 commit comments