Skip to content

Commit 8f598b3

Browse files
committed
feat: Compare files side by side
Signed-off-by: Julius Härtl <[email protected]>
1 parent 328af58 commit 8f598b3

File tree

2 files changed

+107
-4
lines changed

2 files changed

+107
-4
lines changed

src/services/Viewer.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ import Images from '../models/images.js'
2424
import Videos from '../models/videos.js'
2525
import Audios from '../models/audios.js'
2626

27+
/**
28+
* Handler type definition
29+
*
30+
* @typedef {object} Handler
31+
* @property {string} id unique identifier for the handler
32+
* @property {string[]} mimes list of mime types that are supported for opening
33+
* @property {object} component Vue component to render the file
34+
* @property {string} group group identifier to combine for navigating to the next/previous files
35+
* @property {?string} theme viewer modal theme (one of 'dark', 'light', 'default')
36+
* @property {boolean} canCompare Indicate support for comparing two files
37+
*/
38+
2739
/**
2840
* File info type definition
2941
*
@@ -41,12 +53,15 @@ export default class Viewer {
4153

4254
_state
4355
_mimetypes
56+
_mimetypesCompare
4457

4558
constructor() {
4659
this._mimetypes = []
60+
this._mimetypesCompare = []
4761
this._state = {}
4862
this._state.file = ''
4963
this._state.fileInfo = null
64+
this._state.compareFileInfo = null
5065
this._state.files = []
5166
this._state.enableSidebar = true
5267
this._state.el = null
@@ -71,6 +86,7 @@ export default class Viewer {
7186
*
7287
* @readonly
7388
* @memberof Viewer
89+
* @return {Handler[]}
7490
*/
7591
get availableHandlers() {
7692
return this._state.handlers
@@ -80,11 +96,14 @@ export default class Viewer {
8096
* Register a new handler
8197
*
8298
* @memberof Viewer
83-
* @param {object} handler a new unregistered handler
99+
* @param {Handler} handler a new unregistered handler
84100
*/
85101
registerHandler(handler) {
86102
this._state.handlers.push(handler)
87103
this._mimetypes.push.apply(this._mimetypes, handler.mimes)
104+
if (handler?.canCompare === true) {
105+
this._mimetypesCompare.push.apply(this._mimetypesCompare, handler.mimes)
106+
}
88107
}
89108

90109
/**
@@ -107,6 +126,16 @@ export default class Viewer {
107126
return this._state.fileInfo
108127
}
109128

129+
/**
130+
* Get the current comparison view opened file fileInfo
131+
*
132+
* @memberof Viewer
133+
* @return {?Fileinfo} the currently opened file fileInfo
134+
*/
135+
get compareFileInfo() {
136+
return this._state.compareFileInfo
137+
}
138+
110139
/**
111140
* Get the current files list
112141
*
@@ -147,6 +176,16 @@ export default class Viewer {
147176
return this._mimetypes
148177
}
149178

179+
/**
180+
* Get the supported mimetypes that can be opened side by side for comparison
181+
*
182+
* @memberof Viewer
183+
* @return {Array} list of mimetype strings that the viewer can open side by side for comparison
184+
*/
185+
get mimetypesCompare() {
186+
return this._mimetypesCompare
187+
}
188+
150189
/**
151190
* Return the method provided to fetch more results
152191
*
@@ -291,6 +330,20 @@ export default class Viewer {
291330
this.open(options)
292331
}
293332

333+
/**
334+
* Open the viewer with two files side by side
335+
*
336+
* @memberof Viewer
337+
* @param {Fileinfo} fileInfo current file
338+
* @param {Fileinfo} compareFileInfo older file to compare
339+
*/
340+
compare(fileInfo, compareFileInfo) {
341+
this.open({
342+
fileInfo,
343+
})
344+
this._state.compareFileInfo = compareFileInfo
345+
}
346+
294347
/**
295348
* Close the opened file
296349
*

src/views/Viewer.vue

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,22 @@
111111
</NcActionButton>
112112
</template>
113113

114-
<div class="viewer__content" @click.self.exact="close">
114+
<div class="viewer__content" :class="contentClass" @click.self.exact="close">
115+
<component :is="comparisonFile.modal"
116+
v-if="comparisonFile && !comparisonFile.failed && showComparison"
117+
:key="comparisonFile.fileid"
118+
ref="content"
119+
v-bind="comparisonFile"
120+
:active="true"
121+
:can-swipe="false"
122+
:can-zoom="false"
123+
:editing="false"
124+
:is-full-screen="isFullscreen"
125+
:is-sidebar-shown="isSidebarShown"
126+
:loaded.sync="comparisonFile.loaded"
127+
class="viewer__file viewer__file--active"
128+
@error="currentFailed" />
129+
115130
<!-- PREVIOUS -->
116131
<component :is="previousFile.modal"
117132
v-if="previousFile && !previousFile.failed"
@@ -230,6 +245,7 @@ export default {
230245
currentIndex: 0,
231246
previousFile: {},
232247
currentFile: {},
248+
comparisonFile: {},
233249
nextFile: {},
234250
fileList: [],
235251
@@ -274,6 +290,9 @@ export default {
274290
fileInfo() {
275291
return this.Viewer.fileInfo
276292
},
293+
comparisonFileInfo() {
294+
return this.Viewer.compareFileInfo
295+
},
277296
files() {
278297
return this.Viewer.files
279298
},
@@ -365,6 +384,16 @@ export default {
365384
'image--fullscreen': this.isImage && this.isFullscreenMode,
366385
}
367386
},
387+
388+
showComparison() {
389+
return !this.isMobile
390+
},
391+
392+
contentClass() {
393+
return {
394+
'viewer--split': this.comparisonFile,
395+
}
396+
},
368397
},
369398
370399
watch: {
@@ -406,6 +435,16 @@ export default {
406435
}
407436
},
408437
438+
comparisonFileInfo(fileInfo) {
439+
if (fileInfo) {
440+
logger.info('Opening viewer for comparisonFileInfo ', { fileInfo })
441+
this.compareFile(fileInfo)
442+
} else {
443+
// object is undefined, we're closing!
444+
this.cleanup()
445+
}
446+
},
447+
409448
files(fileList) {
410449
// the files list changed, let's update the current opened index
411450
const currentIndex = fileList.findIndex(file => file.basename === this.currentFile.basename)
@@ -516,7 +555,7 @@ export default {
516555
this.cancelRequestFile()
517556
518557
// do not open the same file again
519-
if (path === this.currentFile.path) {
558+
if (path === this.currentFile.path && !this.currentFile.source) { // need to still run when switching from version to current file
520559
return
521560
}
522561
@@ -567,7 +606,7 @@ export default {
567606
this.cancelRequestFolder()
568607
569608
// do not open the same file info again
570-
if (fileInfo.basename === this.currentFile.basename) {
609+
if (fileInfo.basename === this.currentFile.basename && fileInfo.source !== this.currentFile.source) {
571610
return
572611
}
573612
@@ -637,6 +676,7 @@ export default {
637676
638677
// show file
639678
this.currentFile = new File(fileInfo, mime, handler.component)
679+
this.comparisonFile = null
640680
this.updatePreviousNext()
641681
642682
// if sidebar was opened before, let's update the file
@@ -656,6 +696,10 @@ export default {
656696
this.updatePreviousNext()
657697
},
658698
699+
async compareFile(fileInfo) {
700+
this.comparisonFile = new File(fileInfo, fileInfo.mime, this.components[fileInfo.mime])
701+
},
702+
659703
/**
660704
* Show sidebar if available and a file is already opened
661705
*/
@@ -1085,6 +1129,12 @@ export default {
10851129
cursor: pointer;
10861130
}
10871131
1132+
&--split {
1133+
.viewer__file--active {
1134+
width: 50%;
1135+
}
1136+
}
1137+
10881138
:deep(.modal-wrapper) {
10891139
.modal-container {
10901140
// Ensure some space at the bottom

0 commit comments

Comments
 (0)