Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Compare files side by side #1835

Merged
merged 2 commits into from
Aug 8, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions js/viewer-filerobot.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/viewer-filerobot.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/viewer-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/viewer-main.js.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/services/FileList.ts
Expand Up @@ -26,6 +26,8 @@ import type { FileStat, ResponseDataDetailed } from 'webdav'

/**
* Retrieve the files list
* @param path
* @param options
*/
export default async function(path: string, options = {}): Promise<FileInfo[]> {
// getDirectoryContents doesn't accept / for root
Expand Down
55 changes: 54 additions & 1 deletion src/services/Viewer.js
Expand Up @@ -24,6 +24,18 @@ import Images from '../models/images.js'
import Videos from '../models/videos.js'
import Audios from '../models/audios.js'

/**
* Handler type definition
*
* @typedef {object} Handler
* @property {string} id unique identifier for the handler
* @property {string[]} mimes list of mime types that are supported for opening
* @property {object} component Vue component to render the file
* @property {string} group group identifier to combine for navigating to the next/previous files
* @property {?string} theme viewer modal theme (one of 'dark', 'light', 'default')
* @property {boolean} canCompare Indicate support for comparing two files
*/

/**
* File info type definition
*
Expand All @@ -41,12 +53,15 @@ export default class Viewer {

_state
_mimetypes
_mimetypesCompare

constructor() {
this._mimetypes = []
this._mimetypesCompare = []
this._state = {}
this._state.file = ''
this._state.fileInfo = null
this._state.compareFileInfo = null
this._state.files = []
this._state.enableSidebar = true
this._state.el = null
Expand All @@ -71,6 +86,7 @@ export default class Viewer {
*
* @readonly
* @memberof Viewer
* @return {Handler[]}
*/
get availableHandlers() {
return this._state.handlers
Expand All @@ -80,11 +96,14 @@ export default class Viewer {
* Register a new handler
*
* @memberof Viewer
* @param {object} handler a new unregistered handler
* @param {Handler} handler a new unregistered handler
*/
registerHandler(handler) {
this._state.handlers.push(handler)
this._mimetypes.push.apply(this._mimetypes, handler.mimes)
if (handler?.canCompare === true) {
this._mimetypesCompare.push.apply(this._mimetypesCompare, handler.mimes)
}
}

/**
Expand All @@ -107,6 +126,16 @@ export default class Viewer {
return this._state.fileInfo
}

/**
* Get the current comparison view opened file fileInfo
*
* @memberof Viewer
* @return {?Fileinfo} the currently opened file fileInfo
*/
get compareFileInfo() {
return this._state.compareFileInfo
}

/**
* Get the current files list
*
Expand Down Expand Up @@ -147,6 +176,16 @@ export default class Viewer {
return this._mimetypes
}

/**
* Get the supported mimetypes that can be opened side by side for comparison
*
* @memberof Viewer
* @return {Array} list of mimetype strings that the viewer can open side by side for comparison
*/
get mimetypesCompare() {
return this._mimetypesCompare
}

/**
* Return the method provided to fetch more results
*
Expand Down Expand Up @@ -291,6 +330,20 @@ export default class Viewer {
this.open(options)
}

/**
* Open the viewer with two files side by side
*
* @memberof Viewer
* @param {Fileinfo} fileInfo current file
* @param {Fileinfo} compareFileInfo older file to compare
*/
compare(fileInfo, compareFileInfo) {
this.open({
fileInfo,
})
this._state.compareFileInfo = compareFileInfo
}

/**
* Close the opened file
*
Expand Down
1 change: 1 addition & 0 deletions src/utils/fileUtils.ts
Expand Up @@ -82,6 +82,7 @@ export type FileInfo = object
/**
* Generate a fileinfo object based on the full dav properties
* It will flatten everything and put all keys to camelCase
* @param obj
*/
const genFileInfo = function(obj: FileStat): FileInfo {
const fileInfo = {}
Expand Down
65 changes: 61 additions & 4 deletions src/views/Viewer.vue
Expand Up @@ -111,7 +111,23 @@
</NcActionButton>
</template>

<div class="viewer__content" @click.self.exact="close">
<div class="viewer__content" :class="contentClass" @click.self.exact="close">
<!-- COMPARE FILE -->
<component :is="comparisonFile.modal"
v-if="comparisonFile && !comparisonFile.failed && showComparison"
:key="comparisonFile | uniqueKey"
ref="comparison-content"
v-bind="comparisonFile"
:active="true"
:can-swipe="false"
:can-zoom="false"
:editing="false"
:is-full-screen="isFullscreen"
:is-sidebar-shown="isSidebarShown"
:loaded.sync="comparisonFile.loaded"
class="viewer__file viewer__file--active"
@error="comparisonFailed" />

<!-- PREVIOUS -->
<component :is="previousFile.modal"
v-if="previousFile && !previousFile.failed"
Expand Down Expand Up @@ -236,6 +252,7 @@ export default {
currentIndex: 0,
previousFile: {},
currentFile: {},
comparisonFile: null,
nextFile: {},
fileList: [],

Expand Down Expand Up @@ -280,6 +297,9 @@ export default {
fileInfo() {
return this.Viewer.fileInfo
},
comparisonFileInfo() {
return this.Viewer.compareFileInfo
},
files() {
return this.Viewer.files
},
Expand Down Expand Up @@ -345,7 +365,7 @@ export default {
* @return {boolean}
*/
canDownload() {
return canDownload()
return canDownload() && !this.comparisonFile
},

/**
Expand All @@ -359,6 +379,7 @@ export default {
&& canDownload()
&& this.currentFile?.permissions?.includes('W')
&& this.isImage
&& !this.comparisonFile
},

modalClass() {
Expand All @@ -371,6 +392,16 @@ export default {
'image--fullscreen': this.isImage && this.isFullscreenMode,
}
},

showComparison() {
return !this.isMobile
},

contentClass() {
return {
'viewer--split': this.comparisonFile,
}
},
},

watch: {
Expand Down Expand Up @@ -412,6 +443,16 @@ export default {
}
},

comparisonFileInfo(fileInfo) {
if (fileInfo) {
logger.info('Opening viewer for comparisonFileInfo ', { fileInfo })
this.compareFile(fileInfo)
} else {
// object is undefined, we're closing!
this.cleanup()
}
},

files(fileList) {
// the files list changed, let's update the current opened index
const currentIndex = fileList.findIndex(file => file.basename === this.currentFile.basename)
Expand Down Expand Up @@ -522,7 +563,7 @@ export default {
this.cancelRequestFile()

// do not open the same file again
if (path === this.currentFile.path) {
if (path === this.currentFile.path && !this.currentFile.source) {
return
}

Expand Down Expand Up @@ -573,7 +614,7 @@ export default {
this.cancelRequestFolder()

// do not open the same file info again
if (fileInfo.basename === this.currentFile.basename) {
if (fileInfo.basename === this.currentFile.basename && fileInfo.source !== this.currentFile.source) {
return
}

Expand Down Expand Up @@ -643,6 +684,7 @@ export default {

// show file
this.currentFile = new File(fileInfo, mime, handler.component)
this.comparisonFile = null
this.updatePreviousNext()

// if sidebar was opened before, let's update the file
Expand All @@ -662,6 +704,10 @@ export default {
this.updatePreviousNext()
},

async compareFile(fileInfo) {
this.comparisonFile = new File(fileInfo, fileInfo.mime, this.components[fileInfo.mime])
},

/**
* Show sidebar if available and a file is already opened
*/
Expand Down Expand Up @@ -877,6 +923,7 @@ export default {
cleanup() {
// reset all properties
this.currentFile = {}
this.comparisonFile = null
this.currentModal = null
this.fileList = []
this.initiated = false
Expand Down Expand Up @@ -939,6 +986,10 @@ export default {
/**
* Failures handlers
*/
comparisonFailed() {
this.comparisonFile.failed = true
},

previousFailed() {
this.previousFile.failed = true
},
Expand Down Expand Up @@ -1091,6 +1142,12 @@ export default {
cursor: pointer;
}

&--split {
.viewer__file--active {
width: 50%;
}
}

:deep(.modal-wrapper) {
.modal-container {
// Ensure some space at the bottom
Expand Down