diff --git a/appinfo/routes.php b/appinfo/routes.php
index f71d51f9..04f0b60c 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -51,6 +51,7 @@ return [
['name' => 'Days#days', 'url' => '/api/days', 'verb' => 'GET'],
['name' => 'Days#day', 'url' => '/api/days/{id}', 'verb' => 'GET'],
['name' => 'Days#dayPost', 'url' => '/api/days', 'verb' => 'POST'],
+ ['name' => 'Folders#sub', 'url' => '/api/folders/sub', 'verb' => 'GET'],
['name' => 'Clusters#list', 'url' => '/api/clusters/{backend}', 'verb' => 'GET'],
['name' => 'Clusters#preview', 'url' => '/api/clusters/{backend}/preview/{name}', 'verb' => 'GET'],
diff --git a/lib/Controller/DaysController.php b/lib/Controller/DaysController.php
index 0ae9d1ab..e131a97a 100644
--- a/lib/Controller/DaysController.php
+++ b/lib/Controller/DaysController.php
@@ -31,8 +31,6 @@ use OCP\AppFramework\Http\JSONResponse;
class DaysController extends GenericApiController
{
- use FoldersTrait;
-
/**
* @NoAdminRequired
*
@@ -60,12 +58,6 @@ class DaysController extends GenericApiController
$list = array_reverse($list);
}
- // Add subfolder info if querying non-recursively
- if (!$this->isRecursive()) {
- $root = $this->timelineQuery->root();
- array_unshift($list, $this->getSubfoldersEntry($root->getFolder($root->getOneId())));
- }
-
return new JSONResponse($list, Http::STATUS_OK);
});
}
diff --git a/lib/Controller/FoldersController.php b/lib/Controller/FoldersController.php
new file mode 100644
index 00000000..874e87ad
--- /dev/null
+++ b/lib/Controller/FoldersController.php
@@ -0,0 +1,56 @@
+get($folder);
+ } catch (\OCP\Files\NotFoundException $e) {
+ throw Exceptions::NotFound('Folder not found');
+ }
+
+ if (!$node instanceof Folder) {
+ throw Exceptions::NotFound('Path is not a 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($node);
+
+ // Get the subfolders
+ $folders = $view->getDirectoryContent($node->getPath(), FileInfo::MIMETYPE_FOLDER, $node);
+
+ // Sort by name
+ usort($folders, fn ($a, $b) => strnatcmp($a->getName(), $b->getName()));
+
+ // Process to response type
+ $list = array_map(fn ($node) => [
+ 'fileid' => $node->getId(),
+ 'name' => $node->getName(),
+ 'path' => $node->getPath(),
+ 'previews' => $this->timelineQuery->getFolderPreviews($node),
+ ], $folders);
+
+ return new Http\JSONResponse($list, Http::STATUS_OK);
+ });
+ }
+}
diff --git a/lib/Controller/FoldersTrait.php b/lib/Controller/FoldersTrait.php
deleted file mode 100644
index 0bafbc55..00000000
--- a/lib/Controller/FoldersTrait.php
+++ /dev/null
@@ -1,56 +0,0 @@
-setAccessible(true);
- $view = $rp->getValue($folder);
-
- // Get the subfolders
- $folders = $view->getDirectoryContent($folder->getPath(), FileInfo::MIMETYPE_FOLDER, $folder);
-
- // Sort by name
- usort($folders, fn ($a, $b) => 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, []),
- ];
- }
-
- private function getFolderPreviews(Folder &$parent, FileInfo &$fileInfo)
- {
- $folder = $parent->getById($fileInfo->getId());
- if (0 === \count($folder)) {
- return [];
- }
-
- return $this->timelineQuery->getFolderPreviews($folder[0]);
- }
-}
diff --git a/lib/Db/TimelineQueryFolders.php b/lib/Db/TimelineQueryFolders.php
index c5a5bed8..e2622dd2 100644
--- a/lib/Db/TimelineQueryFolders.php
+++ b/lib/Db/TimelineQueryFolders.php
@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace OCA\Memories\Db;
-use OCP\Files\Folder;
+use OCP\Files\FileInfo;
use OCP\IDBConnection;
trait TimelineQueryFolders
{
protected IDBConnection $connection;
- public function getFolderPreviews(Folder &$folder)
+ public function getFolderPreviews(FileInfo $folder)
{
$query = $this->connection->getQueryBuilder();
diff --git a/lib/Db/TimelineRoot.php b/lib/Db/TimelineRoot.php
index dc5d1ab6..4f3583a8 100644
--- a/lib/Db/TimelineRoot.php
+++ b/lib/Db/TimelineRoot.php
@@ -4,8 +4,7 @@ declare(strict_types=1);
namespace OCA\Memories\Db;
-use OCP\Files\Folder;
-use OCP\Files\Node;
+use OCP\Files\FileInfo;
class TimelineRoot
{
@@ -28,25 +27,23 @@ class TimelineRoot
/**
* Add a folder to the root.
*
- * @param Node $folder Node to add
- *
* @throws \Exception if node is not valid readable folder
*/
- public function addFolder(Node &$folder)
+ public function addFolder(FileInfo $info)
{
- $folderPath = $folder->getPath();
+ $folderPath = $info->getPath();
- if (!$folder instanceof Folder) {
+ if (FileInfo::MIMETYPE_FOLDER !== $info->getMimetype()) {
throw new \Exception("Not a folder: {$folderPath}");
}
- if (!$folder->isReadable()) {
+ if (!$info->isReadable()) {
throw new \Exception("Folder not readable: {$folderPath}");
}
// Add top level folder
- $id = $folder->getId();
- $this->folders[$id] = $folder;
+ $id = $info->getId();
+ $this->folders[$id] = $info;
$this->folderPaths[$id] = $folderPath;
}
diff --git a/lib/Util.php b/lib/Util.php
index 60b0d0fe..d4d81a28 100644
--- a/lib/Util.php
+++ b/lib/Util.php
@@ -18,9 +18,6 @@ class Util
{
use UtilController;
- public static $TAG_DAYID_START = -(1 << 30); // the world surely didn't exist
- public static $TAG_DAYID_FOLDERS = -(1 << 30) + 1;
-
public static $ARCHIVE_FOLDER = '.archive';
/**
diff --git a/src/components/FolderGrid.vue b/src/components/FolderGrid.vue
new file mode 100644
index 00000000..cc30264f
--- /dev/null
+++ b/src/components/FolderGrid.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
diff --git a/src/components/ScrollerManager.vue b/src/components/ScrollerManager.vue
index 5d64d528..35a6d641 100644
--- a/src/components/ScrollerManager.vue
+++ b/src/components/ScrollerManager.vue
@@ -242,24 +242,18 @@ export default defineComponent({
// Iterate over rows
for (const row of this.rows) {
if (row.type === IRowType.HEAD) {
+ // Make date string
+ const dateTaken = utils.dayIdToDate(row.dayId);
+
// Create tick
- if (this.TagDayIDValueSet.has(row.dayId)) {
- // Blank tick
- this.ticks.push(getTick(row.dayId));
- } else {
- // Make date string
- const dateTaken = utils.dayIdToDate(row.dayId);
+ const dtYear = dateTaken.getUTCFullYear();
+ const dtMonth = dateTaken.getUTCMonth();
+ const isMonth = dtMonth !== prevMonth || dtYear !== prevYear;
+ const text = dtYear === prevYear ? undefined : dtYear;
+ this.ticks.push(getTick(row.dayId, isMonth, text));
- // Create tick
- const dtYear = dateTaken.getUTCFullYear();
- const dtMonth = dateTaken.getUTCMonth();
- const isMonth = dtMonth !== prevMonth || dtYear !== prevYear;
- const text = dtYear === prevYear ? undefined : dtYear;
- this.ticks.push(getTick(row.dayId, isMonth, text));
-
- prevMonth = dtMonth;
- prevYear = dtYear;
- }
+ prevMonth = dtMonth;
+ prevYear = dtYear;
}
}
},
@@ -417,7 +411,7 @@ export default defineComponent({
const dayId = this.ticks[idx]?.dayId;
// Special days
- if (dayId === undefined || this.TagDayIDValueSet.has(dayId)) {
+ if (dayId === undefined) {
this.hoverCursorText = "";
return;
}
@@ -538,6 +532,7 @@ export default defineComponent({
width: 36px;
top: 0;
right: 0;
+ z-index: 100;
cursor: ns-resize;
opacity: 0;
transition: opacity 0.2s ease-in-out;
diff --git a/src/components/SelectionManager.vue b/src/components/SelectionManager.vue
index 9dfa0551..94cd1a8d 100644
--- a/src/components/SelectionManager.vue
+++ b/src/components/SelectionManager.vue
@@ -526,10 +526,7 @@ export default defineComponent({
/** Add a photo to selection list */
selectPhoto(photo: IPhoto, val?: boolean, noUpdate?: boolean) {
- if (
- photo.flag & this.c.FLAG_PLACEHOLDER ||
- photo.flag & this.c.FLAG_IS_FOLDER
- ) {
+ if (photo.flag & this.c.FLAG_PLACEHOLDER) {
return; // ignore placeholders
}
diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue
index fe0cd0e4..fd0b8d9c 100644
--- a/src/components/Timeline.vue
+++ b/src/components/Timeline.vue
@@ -36,6 +36,11 @@
@load="scrollerManager.adjust()"
>
+
+
@@ -67,10 +72,7 @@
transform: `translate(${photo.dispX}px, ${photo.dispY}px`,
}"
>
-
-
30)
) {
@@ -939,19 +937,6 @@ export default defineComponent({
// Convert server flags to bitflags
data.forEach(utils.convertFlags);
- // Filter out items we don't want to show at all
- 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 &&
- ((p).name.startsWith(".") ||
- !(p).previews.length)
- )
- );
- }
-
// Set and make reactive
day.count = data.length;
day.detail = data;
@@ -970,7 +955,7 @@ export default defineComponent({
return {
width: p.w || this.rowHeight,
height: p.h || this.rowHeight,
- forceSquare: Boolean(p.flag & this.c.FLAG_IS_FOLDER),
+ forceSquare: false,
};
}),
{
@@ -1167,17 +1152,12 @@ export default defineComponent({
/** Add and get a new blank photos row */
addRow(day: IDay): IRow {
- let rowType = IRowType.PHOTOS;
- if (day.dayid === this.TagDayID.FOLDERS) {
- rowType = IRowType.FOLDERS;
- }
-
// Create new row
const row = {
id: `${day.dayid}-${day.rows.length}`,
num: day.rows.length,
photos: [],
- type: rowType,
+ type: IRowType.PHOTOS,
size: this.rowHeight,
dayId: day.dayid,
day: day,
diff --git a/src/components/viewer/Viewer.vue b/src/components/viewer/Viewer.vue
index f2e36ca0..4250f49f 100644
--- a/src/components/viewer/Viewer.vue
+++ b/src/components/viewer/Viewer.vue
@@ -631,8 +631,6 @@ export default defineComponent({
// Get days list and map
for (const r of rows) {
if (r.type === IRowType.HEAD) {
- if (this.TagDayIDValueSet.has(r.dayId)) continue;
-
if (r.day.dayid == anchorPhoto.d.dayid) {
startIndex = r.day.detail.indexOf(anchorPhoto);
this.globalAnchor = this.globalCount;
diff --git a/src/services/API.ts b/src/services/API.ts
index 1c6e6737..5f234fea 100644
--- a/src/services/API.ts
+++ b/src/services/API.ts
@@ -70,6 +70,10 @@ export class API {
query[filter] = value;
}
+ static FOLDERS_SUB() {
+ return tok(gen(`${BASE}/folders/sub`));
+ }
+
static ALBUM_LIST(t: 1 | 2 | 3 = 3) {
return gen(`${BASE}/clusters/albums?t=${t}`);
}
diff --git a/src/services/Utils.ts b/src/services/Utils.ts
index b8aff66f..973f6cbb 100644
--- a/src/services/Utils.ts
+++ b/src/services/Utils.ts
@@ -208,10 +208,6 @@ export function convertFlags(photo: IPhoto) {
photo.flag |= constants.c.FLAG_IS_FAVORITE;
delete photo.isfavorite;
}
- if (photo.isfolder) {
- photo.flag |= constants.c.FLAG_IS_FOLDER;
- delete photo.isfolder;
- }
}
/**
@@ -287,15 +283,6 @@ export function setRenewingTimeout(
}, delay);
}
-// Outside for set
-const TagDayID = {
- START: -(1 << 30),
- FOLDERS: -(1 << 30) + 1,
- TAGS: -(1 << 30) + 2,
- FACES: -(1 << 30) + 3,
- ALBUMS: -(1 << 30) + 4,
-};
-
/** Global constants */
export const constants = {
c: {
@@ -303,13 +290,9 @@ export const constants = {
FLAG_LOAD_FAIL: 1 << 1,
FLAG_IS_VIDEO: 1 << 2,
FLAG_IS_FAVORITE: 1 << 3,
- FLAG_IS_FOLDER: 1 << 4,
- FLAG_SELECTED: 1 << 5,
- FLAG_LEAVING: 1 << 6,
+ FLAG_SELECTED: 1 << 4,
+ FLAG_LEAVING: 1 << 5,
},
-
- TagDayID: TagDayID,
- TagDayIDValueSet: new Set(Object.values(TagDayID)),
};
/** Cache store */
diff --git a/src/types.ts b/src/types.ts
index b811efaa..7d69b663 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -71,16 +71,6 @@ export type IPhoto = {
video_duration?: number;
/** Favorite flag from server */
isfavorite?: boolean;
- /** Is this a folder */
- isfolder?: boolean;
- /** Is this a tag */
- istag?: boolean;
- /** Is this an album */
- isalbum?: boolean;
- /** Is this a face */
- isface?: "recognize" | "facerecognition";
- /** Is this a place */
- isplace?: boolean;
/** Optional datetaken epoch */
datetaken?: number;
};
@@ -196,7 +186,6 @@ export type IHeadRow = IRow & {
export enum IRowType {
HEAD = 0,
PHOTOS = 1,
- FOLDERS = 2,
}
export type ITick = {
diff --git a/src/vue-globals.d.ts b/src/vue-globals.d.ts
index e173bc20..9483b803 100644
--- a/src/vue-globals.d.ts
+++ b/src/vue-globals.d.ts
@@ -8,8 +8,6 @@ declare module "vue" {
n: typeof n;
c: typeof constants.c;
- TagDayID: typeof constants.TagDayID;
- TagDayIDValueSet: typeof constants.TagDayIDValueSet;
state_noDownload: boolean;