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

Added file previews #3187

Merged
merged 6 commits into from
Mar 26, 2020
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
40 changes: 29 additions & 11 deletions apps/files/src/components/AllFilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@
<oc-star class="uk-display-block" @click.native.stop="toggleFileFavorite(item)" :shining="item.starred" />
</div>
<div class="uk-text-truncate uk-width-expand" :ref="index === 0 ? 'firstRowNameColumn' : null">
<oc-file @click.native.stop="item.type === 'folder' ? navigateTo(item.path.substr(1)) : openFileActionBar(item)"
:name="$_ocFileName(item)" :extension="item.extension" class="file-row-name" :icon="fileTypeIcon(item)"
:filename="item.name" :key="item.id"/>
<file-item
@click.native.stop="item.type === 'folder' ? navigateTo(item.path.substr(1)) : openFileActionBar(item)"
:item="item"
:dav-url="davUrl"
:show-path="$_isFavoritesList"
class="file-row-name"
:key="item.viewId"/>
<oc-spinner
v-if="actionInProgress(item)"
size="small"
Expand Down Expand Up @@ -112,6 +116,7 @@
<script>
import FileList from './FileList.vue'
import Indicators from './FilesLists/Indicators.vue'
import FileItem from './FileItem.vue'
import NoContentMessage from './NoContentMessage.vue'
import { mapGetters, mapActions, mapState } from 'vuex'

Expand All @@ -127,6 +132,7 @@ export default {
components: {
FileList,
Indicators,
FileItem,
NoContentMessage,
SortableColumnHeader
},
Expand Down Expand Up @@ -188,6 +194,26 @@ export default {
return Object.keys(shareTypes).map(shareType => parseInt(shareType, 10))
},

davUrl () {
let davUrl
// FIXME: use SDK once it switches to DAV v2
if (this.publicPage()) {
davUrl = [
'..',
'dav',
'public-files'
].join('/')
} else {
davUrl = [
'..',
'dav',
'files',
this.$store.getters.user.id
].join('/')
}
return this.$client.files.getFileUrl(davUrl)
},

$_isFavoritesList () {
return (this.$route.name === 'files-favorites')
},
Expand Down Expand Up @@ -269,14 +295,6 @@ export default {
file: item
})
},
$_ocFileName (item) {
if (this.$_isFavoritesList) {
const pathSplit = item.path.substr(1).split('/')
if (pathSplit.length === 2) return `${pathSplit[pathSplit.length - 2]}/${item.basename}`
if (pathSplit.length > 2) return `…/${pathSplit[pathSplit.length - 2]}/${item.basename}`
}
return item.basename
},

isActionEnabled (item, action) {
return action.isEnabled(item, this.parentFolder)
Expand Down
23 changes: 20 additions & 3 deletions apps/files/src/components/Collaborators/SharedFilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@
</template>
<template #rowColumns="{ item }">
<div class="uk-text-truncate uk-width-expand">
<oc-file @click.native.stop="item.type === 'folder' ? navigateTo(item.path.substr(1)) : openFileActionBar(item)"
:name="item.basename" :extension="item.extension" class="file-row-name" :icon="fileTypeIcon(item)"
:filename="item.name" :key="item.path" />
<file-item
@click.native.stop="item.type === 'folder' ? navigateTo(item.path.substr(1)) : openFileActionBar(item)"
:item="item"
:dav-url="davUrl"
class="file-row-name"
:key="item.path" />
<oc-spinner
v-if="actionInProgress(item)"
size="small"
Expand Down Expand Up @@ -110,6 +113,7 @@ import { mapGetters, mapActions } from 'vuex'
import Mixins from '../../mixins'
import FileActions from '../../fileactions'
import FileList from '../FileList.vue'
import FileItem from '../FileItem.vue'
import NoContentMessage from '../NoContentMessage.vue'
import SortableColumnHeader from '../FilesLists/SortableColumnHeader.vue'
import { shareTypes } from '../../helpers/shareTypes'
Expand All @@ -119,6 +123,7 @@ export default {
name: 'SharedFilesList',
components: {
FileList,
FileItem,
NoContentMessage,
SortableColumnHeader
},
Expand All @@ -145,7 +150,19 @@ export default {

$_isSharedWithMe () {
return (this.$route.name === 'files-shared-with-me')
},

davUrl () {
// FIXME: use SDK once it switches to DAV v2
const davUrl = [
'..',
'dav',
'files',
this.$store.getters.user.id
].join('/')
return this.$client.files.getFileUrl(davUrl)
}

},
watch: {
$route () {
Expand Down
111 changes: 111 additions & 0 deletions apps/files/src/components/FileItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<template>
<oc-file
:name="fileName"
:extension="item.extension"
:icon="previewIcon"
:iconUrl="previewUrl"
:filename="item.name"
:data-preview-loaded="previewLoaded"
/>
</template>
<script>
import queryString from 'query-string'
import Mixins from '../mixins'

export default {
name: 'FileItem',
mixins: [
Mixins
],
props: {
item: {
type: Object
},
// override for file name
name: {
type: String
},
davUrl: {
type: String
},
showPath: {
type: Boolean,
default: false
}
},
data: function () {
return {
previewUrl: this.item.previewUrl,
// 'false' while the preview is loading (needs string for Vue.js to render the attribute)
// true when the preview loading process is done,
// even if we fell back to the mime type icon
previewLoaded: 'false'
}
},
computed: {
fileName () {
if (this.name) {
return this.name
}
if (this.showPath) {
const pathSplit = this.item.path.substr(1).split('/')
if (pathSplit.length === 2) return `${pathSplit[pathSplit.length - 2]}/${this.item.basename}`
if (pathSplit.length > 2) return `…/${pathSplit[pathSplit.length - 2]}/${this.item.basename}`
}
return this.item.basename
},
previewIcon () {
return this.fileTypeIcon(this.item)
}
},
mounted () {
this.loadPreview()
},
methods: {
loadPreview () {
if (this.item.previewUrl) {
this.previewUrl = this.item.previewUrl
this.previewLoaded = true
return
}

// TODO: check if previews are globally enabled (requires capability entry)
// don't load previews for pending or rejected shares (status)
if (!this.davUrl || this.item.type === 'folder' || (typeof this.item.status !== 'undefined' && this.item.status !== 0)) {
this.previewLoaded = true
return
}

const query = {
x: this.thumbDimensions,
y: this.thumbDimensions,
scalingup: 0,
preview: 1,
a: 1
}

if (this.item.etag) {
// add etag for URL based caching
// strip double quotes from etag
query.c = this.item.etag.substr(2, this.item.etag.length - 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

@LukasHirt, oh no here is a bug..
the substr should be substr(1, this.item.etag.length - 2) or else it will cut of the first character. :/

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for catching that! I'll open a PR with fix and ping you for review 😉

}

let itemPath = this.item.path
if (itemPath.charAt(0) === '/') {
itemPath = itemPath.substr(1)
}

const previewUrl = this.davUrl + '/' + this.encodePath(itemPath) + '?' + queryString.stringify(query)

this.mediaSource(previewUrl, 'url', this.requestHeaders).then(dataUrl => {
PVince81 marked this conversation as resolved.
Show resolved Hide resolved
// cache inside item
this.previewUrl = this.item.previewUrl = dataUrl
this.previewLoaded = true
}).catch((e) => {
this.previewUrl = null
this.previewLoaded = true
})
}
}
}
</script>
11 changes: 8 additions & 3 deletions apps/files/src/components/Trashbin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@
</template>
<template #rowColumns="{ item }">
<div class="uk-text-truncate uk-width-expand">
<oc-file
:name="$_ocTrashbin_fileName(item)" :extension="item.extension" class="file-row-name" :icon="fileTypeIcon(item)"
:filename="item.name" :key="item.id"/>
<file-item
:item="item"
:name="$_ocTrashbin_fileName(item)"
class="file-row-name"
:key="item.viewId"
/>
</div>
<div class="uk-text-meta uk-text-nowrap uk-width-small uk-text-right" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-hidden' : _sidebarOpen }">
{{ formDateFromNow(item.deleteTimestamp) }}
Expand All @@ -58,6 +61,7 @@
import { mapGetters, mapActions } from 'vuex'
import Mixins from '../mixins'
import FileList from './FileList.vue'
import FileItem from './FileItem.vue'
import NoContentMessage from './NoContentMessage.vue'
import OcDialogPrompt from './ocDialogPrompt.vue'
import SortableColumnHeader from './FilesLists/SortableColumnHeader.vue'
Expand All @@ -69,6 +73,7 @@ export default {
components: {
OcDialogPrompt,
FileList,
FileItem,
NoContentMessage,
SortableColumnHeader
},
Expand Down
15 changes: 15 additions & 0 deletions apps/files/src/mixins.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ export default {

_sidebarOpen () {
return this.highlightedFile !== null
},

requestHeaders () {
if (!this.publicPage()) {
return null
}

const headers = new Headers()
headers.append('X-Requested-With', 'XMLHttpRequest')

const password = this.publicLinkPassword
if (password) {
headers.append('Authorization', 'Basic ' + Buffer.from('public:' + password).toString('base64'))
}
return headers
}
},
methods: {
Expand Down
6 changes: 6 additions & 0 deletions apps/files/src/store/mutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ export default {

CLEAR_CURRENT_FILES_LIST (state) {
state.currentFolder = null
// release blob urls
state.files.forEach(item => {
if (item.previewUrl && item.previewUrl.startsWith('blob:')) {
window.URL.revokeObjectURL(item.previewUrl)
}
})
state.files = []
}
}
3 changes: 2 additions & 1 deletion apps/media-viewer/src/Mediaviewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ export default {
const query = queryString.stringify({
x: this.thumbDimensions,
y: this.thumbDimensions,
c: this.activeMediaFile.etag,
// strip double quotes from etag
c: this.activeMediaFile.etag.substr(1, this.activeMediaFile.etag.length - 2),
scalingup: 0,
preview: 1,
a: 1
Expand Down
7 changes: 7 additions & 0 deletions changelog/unreleased/276
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Added thumbnails in file list

Thumbnails are now displayed in the file list for known file types.
When no thumbnail was returned, fall back to the file type icon.

https://github.com/owncloud/phoenix/issues/276
https://github.com/owncloud/phoenix/pull/3187
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"owncloud-design-system": "^1.1.1",
"owncloud-sdk": "^1.0.0-523",
"p-limit": "^2.2.1",
"p-queue": "^6.1.1",
"parse-json": "^5.0.0",
"path": "^0.12.7",
"requirejs": "^2.3.6",
Expand Down
12 changes: 11 additions & 1 deletion src/plugins/mediaSource.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import store from '../store'
const { default: PQueue } = require('p-queue')

export default {
install (Vue) {
Expand All @@ -11,6 +12,10 @@ export default {
headers.append('X-Requested-With', 'XMLHttpRequest')

fetch(source, { headers }).then(response => {
if (!response.ok) {
reject(response)
return
}
response.blob().then(blob => {
if (returnAs === 'base64') {
const reader = new FileReader()
Expand Down Expand Up @@ -44,9 +49,14 @@ export default {
})

Vue.mixin({
data: function () {
return {
mediaSourceQueue: new PQueue({ concurrency: 2 })
}
},
methods: {
mediaSource (source, returnAs = 'url', headers = null) {
return _mediaSource(source, returnAs, headers)
return this.mediaSourceQueue.add(() => _mediaSource(source, returnAs, headers))
}
}
})
Expand Down
13 changes: 13 additions & 0 deletions src/plugins/phoenix.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ export default {
*/
isIE11 () {
return !!window.MSInputMethodContext && !!document.documentMode
},
/**
* URI-Encodes a file path but keep the path slashes.
*
* @param path path
* @return encoded path
*/
encodePath: function (path) {
if (!path) {
return path
}
var parts = path.split('/').map(part => encodeURIComponent(part))
return parts.join('/')
}
}
})
Expand Down