Show previews on folders

pull/37/head
Varun Patil 2022-08-20 08:26:52 +00:00
parent 5144c64b19
commit 3999f7b9bd
4 changed files with 171 additions and 10 deletions

View File

@ -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, []),
];

View File

@ -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="name">{{ data.name }}</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>

View File

@ -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;

View File

@ -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, '') }));
}