refactor: separate folder logic
Signed-off-by: Varun Patil <varunpatil@ucla.edu>pull/563/head
parent
37a108c2fc
commit
eb3c834241
|
@ -51,6 +51,7 @@ return [
|
||||||
['name' => 'Days#days', 'url' => '/api/days', 'verb' => 'GET'],
|
['name' => 'Days#days', 'url' => '/api/days', 'verb' => 'GET'],
|
||||||
['name' => 'Days#day', 'url' => '/api/days/{id}', 'verb' => 'GET'],
|
['name' => 'Days#day', 'url' => '/api/days/{id}', 'verb' => 'GET'],
|
||||||
['name' => 'Days#dayPost', 'url' => '/api/days', 'verb' => 'POST'],
|
['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#list', 'url' => '/api/clusters/{backend}', 'verb' => 'GET'],
|
||||||
['name' => 'Clusters#preview', 'url' => '/api/clusters/{backend}/preview/{name}', 'verb' => 'GET'],
|
['name' => 'Clusters#preview', 'url' => '/api/clusters/{backend}/preview/{name}', 'verb' => 'GET'],
|
||||||
|
|
|
@ -31,8 +31,6 @@ use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
|
||||||
class DaysController extends GenericApiController
|
class DaysController extends GenericApiController
|
||||||
{
|
{
|
||||||
use FoldersTrait;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
*
|
*
|
||||||
|
@ -60,12 +58,6 @@ class DaysController extends GenericApiController
|
||||||
$list = array_reverse($list);
|
$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);
|
return new JSONResponse($list, Http::STATUS_OK);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\Memories\Controller;
|
||||||
|
|
||||||
|
use OCA\Memories\Db\TimelineQuery;
|
||||||
|
use OCA\Memories\Exceptions;
|
||||||
|
use OCA\Memories\Util;
|
||||||
|
use OCP\AppFramework\Http;
|
||||||
|
use OCP\Files\FileInfo;
|
||||||
|
use OCP\Files\Folder;
|
||||||
|
|
||||||
|
class FoldersController extends GenericApiController
|
||||||
|
{
|
||||||
|
protected TimelineQuery $timelineQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*/
|
||||||
|
public function sub(string $folder): Http\Response
|
||||||
|
{
|
||||||
|
return Util::guardEx(function () use ($folder) {
|
||||||
|
try {
|
||||||
|
$node = Util::getUserFolder()->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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace OCA\Memories\Controller;
|
|
||||||
|
|
||||||
use OCA\Memories\Db\TimelineQuery;
|
|
||||||
use OCP\Files\FileInfo;
|
|
||||||
use OCP\Files\Folder;
|
|
||||||
|
|
||||||
trait FoldersTrait
|
|
||||||
{
|
|
||||||
protected TimelineQuery $timelineQuery;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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, 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]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,14 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\Memories\Db;
|
namespace OCA\Memories\Db;
|
||||||
|
|
||||||
use OCP\Files\Folder;
|
use OCP\Files\FileInfo;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
trait TimelineQueryFolders
|
trait TimelineQueryFolders
|
||||||
{
|
{
|
||||||
protected IDBConnection $connection;
|
protected IDBConnection $connection;
|
||||||
|
|
||||||
public function getFolderPreviews(Folder &$folder)
|
public function getFolderPreviews(FileInfo $folder)
|
||||||
{
|
{
|
||||||
$query = $this->connection->getQueryBuilder();
|
$query = $this->connection->getQueryBuilder();
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\Memories\Db;
|
namespace OCA\Memories\Db;
|
||||||
|
|
||||||
use OCP\Files\Folder;
|
use OCP\Files\FileInfo;
|
||||||
use OCP\Files\Node;
|
|
||||||
|
|
||||||
class TimelineRoot
|
class TimelineRoot
|
||||||
{
|
{
|
||||||
|
@ -28,25 +27,23 @@ class TimelineRoot
|
||||||
/**
|
/**
|
||||||
* Add a folder to the root.
|
* Add a folder to the root.
|
||||||
*
|
*
|
||||||
* @param Node $folder Node to add
|
|
||||||
*
|
|
||||||
* @throws \Exception if node is not valid readable folder
|
* @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}");
|
throw new \Exception("Not a folder: {$folderPath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$folder->isReadable()) {
|
if (!$info->isReadable()) {
|
||||||
throw new \Exception("Folder not readable: {$folderPath}");
|
throw new \Exception("Folder not readable: {$folderPath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add top level folder
|
// Add top level folder
|
||||||
$id = $folder->getId();
|
$id = $info->getId();
|
||||||
$this->folders[$id] = $folder;
|
$this->folders[$id] = $info;
|
||||||
$this->folderPaths[$id] = $folderPath;
|
$this->folderPaths[$id] = $folderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,6 @@ class Util
|
||||||
{
|
{
|
||||||
use UtilController;
|
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';
|
public static $ARCHIVE_FOLDER = '.archive';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
<template>
|
||||||
|
<div class="grid" v-if="items.length">
|
||||||
|
<div class="grid-item fill-block" v-for="item of items" :key="item.fileid">
|
||||||
|
<Folder :data="item" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import axios from "@nextcloud/axios";
|
||||||
|
|
||||||
|
import UserConfig from "../mixins/UserConfig";
|
||||||
|
import Folder from "./frame/Folder.vue";
|
||||||
|
|
||||||
|
import * as utils from "../services/Utils";
|
||||||
|
import { IFolder } from "../types";
|
||||||
|
import { API } from "../services/API";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "ClusterGrid",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Folder,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [UserConfig],
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
items: [] as IFolder[],
|
||||||
|
path: "",
|
||||||
|
}),
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.refresh();
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.items = [];
|
||||||
|
this.refresh();
|
||||||
|
},
|
||||||
|
config_showHidden() {
|
||||||
|
this.refresh();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async refresh() {
|
||||||
|
// Get folder path
|
||||||
|
const folder = (this.path = utils.getFolderRoutePath(
|
||||||
|
this.config_foldersPath
|
||||||
|
));
|
||||||
|
|
||||||
|
// Get subfolders for this folder
|
||||||
|
const res = await axios.get<IFolder[]>(
|
||||||
|
API.Q(API.FOLDERS_SUB(), { folder })
|
||||||
|
);
|
||||||
|
if (folder !== this.path) return;
|
||||||
|
this.items = res.data;
|
||||||
|
this.$emit("load", this.items);
|
||||||
|
|
||||||
|
// Filter out hidden folders
|
||||||
|
if (!this.config_showHidden) {
|
||||||
|
this.items = this.items.filter(
|
||||||
|
(f) => !f.name.startsWith(".") && f.previews.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(min(100% / 3, 165px), 1fr));
|
||||||
|
|
||||||
|
width: calc(100% - 40px); // leave space for scroller
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
width: calc(100% - 2px); // compensation for negative margin
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item {
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -242,24 +242,18 @@ export default defineComponent({
|
||||||
// Iterate over rows
|
// Iterate over rows
|
||||||
for (const row of this.rows) {
|
for (const row of this.rows) {
|
||||||
if (row.type === IRowType.HEAD) {
|
if (row.type === IRowType.HEAD) {
|
||||||
|
// Make date string
|
||||||
|
const dateTaken = utils.dayIdToDate(row.dayId);
|
||||||
|
|
||||||
// Create tick
|
// Create tick
|
||||||
if (this.TagDayIDValueSet.has(row.dayId)) {
|
const dtYear = dateTaken.getUTCFullYear();
|
||||||
// Blank tick
|
const dtMonth = dateTaken.getUTCMonth();
|
||||||
this.ticks.push(getTick(row.dayId));
|
const isMonth = dtMonth !== prevMonth || dtYear !== prevYear;
|
||||||
} else {
|
const text = dtYear === prevYear ? undefined : dtYear;
|
||||||
// Make date string
|
this.ticks.push(getTick(row.dayId, isMonth, text));
|
||||||
const dateTaken = utils.dayIdToDate(row.dayId);
|
|
||||||
|
|
||||||
// Create tick
|
prevMonth = dtMonth;
|
||||||
const dtYear = dateTaken.getUTCFullYear();
|
prevYear = dtYear;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -417,7 +411,7 @@ export default defineComponent({
|
||||||
const dayId = this.ticks[idx]?.dayId;
|
const dayId = this.ticks[idx]?.dayId;
|
||||||
|
|
||||||
// Special days
|
// Special days
|
||||||
if (dayId === undefined || this.TagDayIDValueSet.has(dayId)) {
|
if (dayId === undefined) {
|
||||||
this.hoverCursorText = "";
|
this.hoverCursorText = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -538,6 +532,7 @@ export default defineComponent({
|
||||||
width: 36px;
|
width: 36px;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
cursor: ns-resize;
|
cursor: ns-resize;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
|
|
@ -526,10 +526,7 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Add a photo to selection list */
|
/** Add a photo to selection list */
|
||||||
selectPhoto(photo: IPhoto, val?: boolean, noUpdate?: boolean) {
|
selectPhoto(photo: IPhoto, val?: boolean, noUpdate?: boolean) {
|
||||||
if (
|
if (photo.flag & this.c.FLAG_PLACEHOLDER) {
|
||||||
photo.flag & this.c.FLAG_PLACEHOLDER ||
|
|
||||||
photo.flag & this.c.FLAG_IS_FOLDER
|
|
||||||
) {
|
|
||||||
return; // ignore placeholders
|
return; // ignore placeholders
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
@load="scrollerManager.adjust()"
|
@load="scrollerManager.adjust()"
|
||||||
>
|
>
|
||||||
</OnThisDay>
|
</OnThisDay>
|
||||||
|
|
||||||
|
<FolderGrid
|
||||||
|
v-if="routeIsFolders && !$route.query.recursive"
|
||||||
|
@load="scrollerManager.adjust()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -67,10 +72,7 @@
|
||||||
transform: `translate(${photo.dispX}px, ${photo.dispY}px`,
|
transform: `translate(${photo.dispX}px, ${photo.dispY}px`,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Folder v-if="photo.flag & c.FLAG_IS_FOLDER" :data="photo" />
|
|
||||||
|
|
||||||
<Photo
|
<Photo
|
||||||
v-else
|
|
||||||
:data="photo"
|
:data="photo"
|
||||||
:day="item.day"
|
:day="item.day"
|
||||||
@select="selectionManager.selectPhoto"
|
@select="selectionManager.selectPhoto"
|
||||||
|
@ -123,10 +125,10 @@ import { showError } from "@nextcloud/dialogs";
|
||||||
import { subscribe, unsubscribe } from "@nextcloud/event-bus";
|
import { subscribe, unsubscribe } from "@nextcloud/event-bus";
|
||||||
|
|
||||||
import { getLayout } from "../services/Layout";
|
import { getLayout } from "../services/Layout";
|
||||||
import { IDay, IFolder, IHeadRow, IPhoto, IRow, IRowType } from "../types";
|
import { IDay, IHeadRow, IPhoto, IRow, IRowType } from "../types";
|
||||||
|
|
||||||
import UserConfig from "../mixins/UserConfig";
|
import UserConfig from "../mixins/UserConfig";
|
||||||
import Folder from "./frame/Folder.vue";
|
import FolderGrid from "./FolderGrid.vue";
|
||||||
import Photo from "./frame/Photo.vue";
|
import Photo from "./frame/Photo.vue";
|
||||||
import ScrollerManager from "./ScrollerManager.vue";
|
import ScrollerManager from "./ScrollerManager.vue";
|
||||||
import SelectionManager from "./SelectionManager.vue";
|
import SelectionManager from "./SelectionManager.vue";
|
||||||
|
@ -151,7 +153,7 @@ export default defineComponent({
|
||||||
name: "Timeline",
|
name: "Timeline",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Folder,
|
FolderGrid,
|
||||||
Photo,
|
Photo,
|
||||||
EmptyContent,
|
EmptyContent,
|
||||||
OnThisDay,
|
OnThisDay,
|
||||||
|
@ -244,6 +246,9 @@ export default defineComponent({
|
||||||
routeIsArchive(): boolean {
|
routeIsArchive(): boolean {
|
||||||
return this.$route.name === "archive";
|
return this.$route.name === "archive";
|
||||||
},
|
},
|
||||||
|
routeIsFolders(): boolean {
|
||||||
|
return this.$route.name === "folders";
|
||||||
|
},
|
||||||
isMonthView(): boolean {
|
isMonthView(): boolean {
|
||||||
if (this.$route.query.sort === "timeline") return false;
|
if (this.$route.query.sort === "timeline") return false;
|
||||||
|
|
||||||
|
@ -635,11 +640,6 @@ export default defineComponent({
|
||||||
return head.name;
|
return head.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special headers
|
|
||||||
if (this.TagDayIDValueSet.has(head.dayId)) {
|
|
||||||
return (head.name = "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make date string
|
// Make date string
|
||||||
// The reason this function is separate from processDays is
|
// The reason this function is separate from processDays is
|
||||||
// because this call is terribly slow even on desktop
|
// because this call is terribly slow even on desktop
|
||||||
|
@ -756,9 +756,7 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
// Special headers
|
// Special headers
|
||||||
if (this.TagDayIDValueSet.has(day.dayid)) {
|
if (
|
||||||
head.size = 10;
|
|
||||||
} else if (
|
|
||||||
this.$route.name === "thisday" &&
|
this.$route.name === "thisday" &&
|
||||||
(!prevDay || Math.abs(prevDay.dayid - day.dayid) > 30)
|
(!prevDay || Math.abs(prevDay.dayid - day.dayid) > 30)
|
||||||
) {
|
) {
|
||||||
|
@ -939,19 +937,6 @@ export default defineComponent({
|
||||||
// Convert server flags to bitflags
|
// Convert server flags to bitflags
|
||||||
data.forEach(utils.convertFlags);
|
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 &&
|
|
||||||
((<IFolder>p).name.startsWith(".") ||
|
|
||||||
!(<IFolder>p).previews.length)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set and make reactive
|
// Set and make reactive
|
||||||
day.count = data.length;
|
day.count = data.length;
|
||||||
day.detail = data;
|
day.detail = data;
|
||||||
|
@ -970,7 +955,7 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
width: p.w || this.rowHeight,
|
width: p.w || this.rowHeight,
|
||||||
height: p.h || 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 */
|
/** Add and get a new blank photos row */
|
||||||
addRow(day: IDay): IRow {
|
addRow(day: IDay): IRow {
|
||||||
let rowType = IRowType.PHOTOS;
|
|
||||||
if (day.dayid === this.TagDayID.FOLDERS) {
|
|
||||||
rowType = IRowType.FOLDERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new row
|
// Create new row
|
||||||
const row = {
|
const row = {
|
||||||
id: `${day.dayid}-${day.rows.length}`,
|
id: `${day.dayid}-${day.rows.length}`,
|
||||||
num: day.rows.length,
|
num: day.rows.length,
|
||||||
photos: [],
|
photos: [],
|
||||||
type: rowType,
|
type: IRowType.PHOTOS,
|
||||||
size: this.rowHeight,
|
size: this.rowHeight,
|
||||||
dayId: day.dayid,
|
dayId: day.dayid,
|
||||||
day: day,
|
day: day,
|
||||||
|
|
|
@ -631,8 +631,6 @@ export default defineComponent({
|
||||||
// Get days list and map
|
// Get days list and map
|
||||||
for (const r of rows) {
|
for (const r of rows) {
|
||||||
if (r.type === IRowType.HEAD) {
|
if (r.type === IRowType.HEAD) {
|
||||||
if (this.TagDayIDValueSet.has(r.dayId)) continue;
|
|
||||||
|
|
||||||
if (r.day.dayid == anchorPhoto.d.dayid) {
|
if (r.day.dayid == anchorPhoto.d.dayid) {
|
||||||
startIndex = r.day.detail.indexOf(anchorPhoto);
|
startIndex = r.day.detail.indexOf(anchorPhoto);
|
||||||
this.globalAnchor = this.globalCount;
|
this.globalAnchor = this.globalCount;
|
||||||
|
|
|
@ -70,6 +70,10 @@ export class API {
|
||||||
query[filter] = value;
|
query[filter] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FOLDERS_SUB() {
|
||||||
|
return tok(gen(`${BASE}/folders/sub`));
|
||||||
|
}
|
||||||
|
|
||||||
static ALBUM_LIST(t: 1 | 2 | 3 = 3) {
|
static ALBUM_LIST(t: 1 | 2 | 3 = 3) {
|
||||||
return gen(`${BASE}/clusters/albums?t=${t}`);
|
return gen(`${BASE}/clusters/albums?t=${t}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,10 +208,6 @@ export function convertFlags(photo: IPhoto) {
|
||||||
photo.flag |= constants.c.FLAG_IS_FAVORITE;
|
photo.flag |= constants.c.FLAG_IS_FAVORITE;
|
||||||
delete photo.isfavorite;
|
delete photo.isfavorite;
|
||||||
}
|
}
|
||||||
if (photo.isfolder) {
|
|
||||||
photo.flag |= constants.c.FLAG_IS_FOLDER;
|
|
||||||
delete photo.isfolder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,15 +283,6 @@ export function setRenewingTimeout(
|
||||||
}, delay);
|
}, 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 */
|
/** Global constants */
|
||||||
export const constants = {
|
export const constants = {
|
||||||
c: {
|
c: {
|
||||||
|
@ -303,13 +290,9 @@ export const constants = {
|
||||||
FLAG_LOAD_FAIL: 1 << 1,
|
FLAG_LOAD_FAIL: 1 << 1,
|
||||||
FLAG_IS_VIDEO: 1 << 2,
|
FLAG_IS_VIDEO: 1 << 2,
|
||||||
FLAG_IS_FAVORITE: 1 << 3,
|
FLAG_IS_FAVORITE: 1 << 3,
|
||||||
FLAG_IS_FOLDER: 1 << 4,
|
FLAG_SELECTED: 1 << 4,
|
||||||
FLAG_SELECTED: 1 << 5,
|
FLAG_LEAVING: 1 << 5,
|
||||||
FLAG_LEAVING: 1 << 6,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
TagDayID: TagDayID,
|
|
||||||
TagDayIDValueSet: new Set(Object.values(TagDayID)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Cache store */
|
/** Cache store */
|
||||||
|
|
11
src/types.ts
11
src/types.ts
|
@ -71,16 +71,6 @@ export type IPhoto = {
|
||||||
video_duration?: number;
|
video_duration?: number;
|
||||||
/** Favorite flag from server */
|
/** Favorite flag from server */
|
||||||
isfavorite?: boolean;
|
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 */
|
/** Optional datetaken epoch */
|
||||||
datetaken?: number;
|
datetaken?: number;
|
||||||
};
|
};
|
||||||
|
@ -196,7 +186,6 @@ export type IHeadRow = IRow & {
|
||||||
export enum IRowType {
|
export enum IRowType {
|
||||||
HEAD = 0,
|
HEAD = 0,
|
||||||
PHOTOS = 1,
|
PHOTOS = 1,
|
||||||
FOLDERS = 2,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ITick = {
|
export type ITick = {
|
||||||
|
|
|
@ -8,8 +8,6 @@ declare module "vue" {
|
||||||
n: typeof n;
|
n: typeof n;
|
||||||
|
|
||||||
c: typeof constants.c;
|
c: typeof constants.c;
|
||||||
TagDayID: typeof constants.TagDayID;
|
|
||||||
TagDayIDValueSet: typeof constants.TagDayIDValueSet;
|
|
||||||
|
|
||||||
state_noDownload: boolean;
|
state_noDownload: boolean;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue