Skip to content

Commit

Permalink
Hide folders without photos (fix #163)
Browse files Browse the repository at this point in the history
  • Loading branch information
pulsejet committed Nov 7, 2022
1 parent 38475f0 commit 6f3cb99
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 78 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ This file is manually updated. Please file an issue if something is missing.
## v4.6.1, v3.6.1

- **Feature**: Native sharing from the viewer (images only)
- **Feature**: Deep linking to photos
- **Feature**: Deep linking to photos on opening viewer
- **Feature**: Password protected folder shares ([#165](https://github.com/pulsejet/memories/issues/165))
- **Feature**: Folders view will now show only folders with photos ([#163](https://github.com/pulsejet/memories/issues/163))
- Improvements to viewer UX

## v4.6.0, v3.6.0 (2022-11-06)
Expand Down
40 changes: 4 additions & 36 deletions lib/Controller/DaysController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@

use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\FileInfo;
use OCP\Files\Folder;

class DaysController extends ApiBase
{
use FoldersTrait;

/**
* @NoAdminRequired
* @NoCSRFRequired
*
*
* @PublicPage
*/
Expand Down Expand Up @@ -167,41 +170,6 @@ public function dayPost(): JSONResponse
return $this->day($id);
}

/**
* Get subfolders entry for days response.
*/
public function getSubfoldersEntry(Folder &$folder)
{
// Ugly: get the view of the folder with reflection
// This is unfortunately the only way to get the contents of a folder
// matching a MIME type without using SEARCH, which is deep
$rp = new \ReflectionProperty('\OC\Files\Node\Node', 'view');
$rp->setAccessible(true);
$view = $rp->getValue($folder);

// Get the subfolders
$folders = $view->getDirectoryContent($folder->getPath(), FileInfo::MIMETYPE_FOLDER, $folder);

// Sort by name
usort($folders, function ($a, $b) {
return strnatcmp($a->getName(), $b->getName());
});

// Process to response type
return [
'dayid' => \OCA\Memories\Util::$TAG_DAYID_FOLDERS,
'count' => \count($folders),
'detail' => array_map(function ($node) {
return [
'fileid' => $node->getId(),
'name' => $node->getName(),
'isfolder' => 1,
'path' => $node->getPath(),
];
}, $folders, []),
];
}

/**
* Get transformations depending on the request.
*
Expand Down
55 changes: 55 additions & 0 deletions lib/Controller/FoldersTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace OCA\Memories\Controller;

use OCP\Files\FileInfo;
use OCP\Files\Folder;
use OCA\Memories\Db\TimelineQuery;

trait FoldersTrait {
protected TimelineQuery $timelineQuery;

private function getFolderPreviews(Folder &$parent, FileInfo &$fileInfo) {
$folder = $parent->getById($fileInfo->getId());
if (count($folder) === 0) {
return [];
}
return $this->timelineQuery->getFolderPreviews($folder[0]);
}

/**
* Get subfolders entry for days response.
*/
public function getSubfoldersEntry(Folder &$folder)
{
// Ugly: get the view of the folder with reflection
// This is unfortunately the only way to get the contents of a folder
// matching a MIME type without using SEARCH, which is deep
$rp = new \ReflectionProperty('\OC\Files\Node\Node', 'view');
$rp->setAccessible(true);
$view = $rp->getValue($folder);

// Get the subfolders
$folders = $view->getDirectoryContent($folder->getPath(), FileInfo::MIMETYPE_FOLDER, $folder);

// Sort by name
usort($folders, function ($a, $b) {
return strnatcmp($a->getName(), $b->getName());
});

// Process to response type
return [
'dayid' => \OCA\Memories\Util::$TAG_DAYID_FOLDERS,
'count' => \count($folders),
'detail' => array_map(function (&$node) use (&$folder) {
return [
'fileid' => $node->getId(),
'name' => $node->getName(),
'isfolder' => 1,
'path' => $node->getPath(),
'previews' => $this->getFolderPreviews($folder, $node),
];
}, $folders, []),
];
}
}
1 change: 1 addition & 0 deletions lib/Db/TimelineQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class TimelineQuery
use TimelineQueryFaces;
use TimelineQueryFilters;
use TimelineQueryTags;
use TimelineQueryFolders;

protected IDBConnection $connection;

Expand Down
42 changes: 42 additions & 0 deletions lib/Db/TimelineQueryFolders.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace OCA\Memories\Db;

use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Folder;
use OCP\IDBConnection;

trait TimelineQueryFolders
{
protected IDBConnection $connection;

public function getFolderPreviews(Folder &$folder)
{
$query = $this->connection->getQueryBuilder();

// SELECT all photos
$query->select('f.fileid', 'f.etag')->from('memories', 'm');

// WHERE these photos are in the user's requested folder recursively
$query = $this->joinFilecache($query, $folder, true, false);

// ORDER descending by fileid
$query->orderBy('f.fileid', 'DESC');

// MAX 4
$query->setMaxResults(4);

// FETCH tag previews
$cursor = $this->executeQueryWithCTEs($query);
$ans = $cursor->fetchAll();

// Post-process
foreach ($ans as &$row) {
$row['fileid'] = (int) $row['fileid'];
}

return $ans;
}
}
9 changes: 6 additions & 3 deletions src/components/Timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -938,11 +938,14 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
data.forEach(utils.convertFlags);
// Filter out items we don't want to show at all
if (!this.config_showHidden) {
// Hidden folders
if (!this.config_showHidden && dayId === this.TagDayID.FOLDERS) {
// Hidden folders and folders without previews
data = data.filter(
(p) =>
!(p.flag & this.c.FLAG_IS_FOLDER && (<IFolder>p).name.startsWith("."))
!(
p.flag & this.c.FLAG_IS_FOLDER &&
((<IFolder>p).name.startsWith(".") || !(<IFolder>p).previews.length)
)
);
}
Expand Down
49 changes: 13 additions & 36 deletions src/components/frame/Folder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
draggable="false"
class="folder fill-block"
:class="{
hasPreview: previewFileInfos.length > 0,
onePreview: previewFileInfos.length === 1,
hasPreview: previews.length > 0,
onePreview: previews.length === 1,
hasError: error,
}"
:to="target"
Expand All @@ -15,17 +15,11 @@
</div>

<div class="previews fill-block">
<div
class="img-outer"
v-for="info of previewFileInfos"
:key="info.fileid"
>
<div class="img-outer" v-for="info of previews" :key="info.fileid">
<img
class="fill-block"
:class="{ error: info.flag & c.FLAG_LOAD_FAIL }"
:key="'fpreview-' + info.fileid"
:src="getPreviewUrl(info, true, 256)"
@error="info.flag |= c.FLAG_LOAD_FAIL"
@error="$event.target.classList.add('error')"
/>
</div>
</div>
Expand All @@ -34,11 +28,10 @@

<script lang="ts">
import { Component, Prop, Watch, Mixins } from "vue-property-decorator";
import { IFileInfo, IFolder } from "../../types";
import { IFolder, IPhoto } from "../../types";
import GlobalMixin from "../../mixins/GlobalMixin";
import UserConfig from "../../mixins/UserConfig";
import * as dav from "../../services/DavRequests";
import { getPreviewUrl } from "../../services/FileUtils";
import FolderIcon from "vue-material-design-icons/Folder.vue";
Expand All @@ -52,7 +45,7 @@ export default class Folder extends Mixins(GlobalMixin, UserConfig) {
@Prop() data: IFolder;
// Separate property because the one on data isn't reactive
private previewFileInfos: IFileInfo[] = [];
private previews: IPhoto[] = [];
// Error occured fetching thumbs
private error = false;
Expand Down Expand Up @@ -81,29 +74,13 @@ export default class Folder extends Mixins(GlobalMixin, UserConfig) {
}
// Get preview infos
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);
fileInfos.forEach((f) => (f.flag = 0));
if (fileInfos.length > 0 && fileInfos.length < 4) {
fileInfos = [fileInfos[0]];
}
this.data.previewFileInfos = fileInfos;
this.previewFileInfos = fileInfos;
})
.catch(() => {
this.data.previewFileInfos = [];
this.previewFileInfos = [];
// Something is wrong with the folder
// e.g. external storage not available
this.error = true;
});
} else {
this.previewFileInfos = this.data.previewFileInfos;
const previews = this.data.previews;
if (previews) {
if (previews.length > 0 && previews.length < 4) {
this.previews = [previews[0]];
} else {
this.previews = previews.slice(0, 4);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ export type IPhoto = {
export interface IFolder extends IPhoto {
/** Path to folder */
path: string;
/** FileInfos for preview images */
previewFileInfos?: IFileInfo[];
/** Photos for preview images */
previews?: IPhoto[];
/** Name of folder */
name: string;
}
Expand Down

0 comments on commit 6f3cb99

Please sign in to comment.