Show previews on folders
parent
5144c64b19
commit
3999f7b9bd
|
@ -149,11 +149,12 @@ class ApiController extends Controller {
|
|||
// Map sub to JSON array
|
||||
$subdirArray = [
|
||||
"day_id" => -0.1,
|
||||
"detail" => array_map(function ($item) {
|
||||
"detail" => array_map(function ($node) {
|
||||
return [
|
||||
"file_id" => $item->getId(),
|
||||
"name" => $item->getName(),
|
||||
"file_id" => $node->getId(),
|
||||
"name" => $node->getName(),
|
||||
"is_folder" => 1,
|
||||
"path" => $node->getPath(),
|
||||
];
|
||||
}, $sub, []),
|
||||
];
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
<template>
|
||||
<div class="folder"
|
||||
<div class="folder" v-bind:class="{
|
||||
hasPreview: previewFileInfos.length > 0,
|
||||
onePreview: previewFileInfos.length === 1,
|
||||
}"
|
||||
@click="openFolder(data.file_id)"
|
||||
v-bind:style="{
|
||||
width: rowHeight + 'px',
|
||||
height: rowHeight + 'px',
|
||||
}">
|
||||
<div class="icon-folder icon-dark"></div>
|
||||
<div class="big-icon">
|
||||
<div class="icon-folder"></div>
|
||||
<div class="name">{{ data.name }}</div>
|
||||
</div>
|
||||
|
||||
<div class="previews">
|
||||
<img v-for="info of previewFileInfos"
|
||||
:src="`/core/preview?fileId=${info.fileid}&c=${info.etag}&x=250&y=250&forceIcon=0&a=0`" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dav from "../services/DavRequests";
|
||||
|
||||
export default {
|
||||
name: 'Folder',
|
||||
|
@ -24,6 +35,29 @@ export default {
|
|||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
previewFileInfos: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.data.previewFileInfos) {
|
||||
const folderPath = this.data.path.split('/').slice(3).join('/');
|
||||
dav.getFolderPreviewFileIds(folderPath, 4).then(fileInfos => {
|
||||
fileInfos = fileInfos.filter(f => f.hasPreview);
|
||||
if (fileInfos.length > 0 && fileInfos.length < 4) {
|
||||
fileInfos = [fileInfos[0]];
|
||||
}
|
||||
this.data.previewFileInfos = fileInfos;
|
||||
this.previewFileInfos = fileInfos;
|
||||
}).catch(() => {
|
||||
this.data.previewFileInfos = [];
|
||||
});
|
||||
} else {
|
||||
this.previewFileInfos = this.data.previewFileInfos;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Open album folder */
|
||||
openFolder(id) {
|
||||
|
@ -40,13 +74,67 @@ export default {
|
|||
.folder .name {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding: 0 5%;
|
||||
text-align: center;
|
||||
font-size: 1.08em;
|
||||
word-wrap: break-word;
|
||||
text-overflow: ellipsis;
|
||||
max-height: 35%;
|
||||
}
|
||||
.icon-folder {
|
||||
cursor: pointer;
|
||||
background-size: 40%;
|
||||
height: 60%; width: 100%;
|
||||
background-position: bottom;
|
||||
height: 65%; width: 100%;
|
||||
opacity: 0.3;
|
||||
background-size: 40%;
|
||||
background-position: bottom;
|
||||
background-image: var(--icon-folder-000);
|
||||
}
|
||||
.big-icon {
|
||||
cursor: pointer;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
.folder.hasPreview .big-icon .icon-folder {
|
||||
opacity: 1;
|
||||
background-image: var(--icon-folder-fff);
|
||||
}
|
||||
.folder.hasPreview .big-icon .name {
|
||||
color: white;
|
||||
}
|
||||
.folder.hasPreview:hover .big-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
.folder:hover .previews img {
|
||||
filter: brightness(100%);
|
||||
}
|
||||
.previews {
|
||||
z-index: 3;
|
||||
line-height: 0;
|
||||
position: absolute;
|
||||
height: calc(100% - 4px);
|
||||
width: calc(100% - 4px);
|
||||
top: 2px; left: 2px;
|
||||
}
|
||||
.previews img {
|
||||
padding: 0;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
filter: brightness(50%);
|
||||
transition: filter 0.2s ease-in-out;
|
||||
}
|
||||
.previews img:nth-of-type(1) { border-top-left-radius: 3px; }
|
||||
.previews img:nth-of-type(2) { border-top-right-radius: 3px; }
|
||||
.previews img:nth-of-type(3) { border-bottom-left-radius: 3px; }
|
||||
.previews img:nth-of-type(4) { border-bottom-right-radius: 3px; }
|
||||
|
||||
.folder.onePreview .previews img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
|
@ -573,7 +573,7 @@ export default {
|
|||
top: 2px; left: 2px;
|
||||
background: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.3) 95%);
|
||||
opacity: 0;
|
||||
border-radius: 3%;
|
||||
border-radius: 3px;
|
||||
transition: opacity .1s ease-in-out;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
|
|
@ -12,6 +12,13 @@ const props = `
|
|||
<oc:favorite />
|
||||
<d:resourcetype />`;
|
||||
|
||||
const IMAGE_MIME_TYPES = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/tiff',
|
||||
'image/heic',
|
||||
];
|
||||
|
||||
export async function getFiles(fileIds) {
|
||||
const prefixPath = `/files/${getCurrentUser().uid}`;
|
||||
|
||||
|
@ -64,3 +71,68 @@ export async function getFiles(fileIds) {
|
|||
.map(data => genFileInfo(data))
|
||||
.map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }));
|
||||
}
|
||||
|
||||
export async function getFolderPreviewFileIds(folderPath, limit) {
|
||||
const prefixPath = `/files/${getCurrentUser().uid}`;
|
||||
|
||||
const filter = IMAGE_MIME_TYPES.map(mime => `
|
||||
<d:like>
|
||||
<d:prop>
|
||||
<d:getcontenttype/>
|
||||
</d:prop>
|
||||
<d:literal>${mime}</d:literal>
|
||||
</d:like>
|
||||
`).join('');
|
||||
|
||||
const options = {
|
||||
method: 'SEARCH',
|
||||
headers: {
|
||||
'content-Type': 'text/xml',
|
||||
},
|
||||
data: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:searchrequest xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns"
|
||||
xmlns:nc="http://nextcloud.org/ns"
|
||||
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns"
|
||||
xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:basicsearch>
|
||||
<d:select>
|
||||
<d:prop>
|
||||
${props}
|
||||
</d:prop>
|
||||
</d:select>
|
||||
<d:from>
|
||||
<d:scope>
|
||||
<d:href>${prefixPath}/${folderPath}</d:href>
|
||||
<d:depth>0</d:depth>
|
||||
</d:scope>
|
||||
</d:from>
|
||||
<d:where>
|
||||
<d:or>
|
||||
${filter}
|
||||
</d:or>
|
||||
</d:where>
|
||||
|
||||
<d:orderby>
|
||||
<d:order>
|
||||
<d:prop>
|
||||
<d:getlastmodified/>
|
||||
</d:prop>
|
||||
<d:ascending/>
|
||||
</d:order>
|
||||
</d:orderby>
|
||||
<d:limit>
|
||||
<d:nresults>${limit}</d:nresults>
|
||||
</d:limit>
|
||||
</d:basicsearch>
|
||||
</d:searchrequest>`,
|
||||
deep: true,
|
||||
details: true,
|
||||
responseType: 'text',
|
||||
};
|
||||
|
||||
let response = await client.getDirectoryContents('', options);
|
||||
return response.data
|
||||
.map(data => genFileInfo(data))
|
||||
.map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue