From 80a76a5a4856ecae49f5283b4f13725789832258 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 3 Nov 2022 15:39:48 -0700 Subject: [PATCH] Group months in album --- lib/Controller/ApiBase.php | 15 ++++++++ lib/Controller/DaysController.php | 63 +++++++++++++++++-------------- lib/Db/TimelineQueryAlbums.php | 38 +++++++++++++++++++ lib/Db/TimelineQueryDays.php | 2 +- src/components/Timeline.vue | 23 ++++++++++- src/services/Utils.ts | 9 +++++ 6 files changed, 120 insertions(+), 30 deletions(-) diff --git a/lib/Controller/ApiBase.php b/lib/Controller/ApiBase.php index 043fb398..4f7618fc 100644 --- a/lib/Controller/ApiBase.php +++ b/lib/Controller/ApiBase.php @@ -133,6 +133,21 @@ class ApiBase extends Controller return $folder; } + protected function isRecursive() + { + return null === $this->request->getParam('folder'); + } + + protected function isArchive() + { + return null !== $this->request->getParam('archive'); + } + + protected function isMonthView() + { + return null !== $this->request->getParam('monthView'); + } + protected function getShareToken() { return $this->request->getParam('folder_share'); diff --git a/lib/Controller/DaysController.php b/lib/Controller/DaysController.php index 7f4245a2..2df870ca 100644 --- a/lib/Controller/DaysController.php +++ b/lib/Controller/DaysController.php @@ -49,25 +49,26 @@ class DaysController extends ApiBase return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_NOT_FOUND); } - // Params - $recursive = null === $this->request->getParam('folder'); - $archive = null !== $this->request->getParam('archive'); - // Run actual query try { $list = $this->timelineQuery->getDays( $folder, $uid, - $recursive, - $archive, + $this->isRecursive(), + $this->isArchive(), $this->getTransformations(true), ); - // Preload some day responses - $this->preloadDays($list, $uid, $folder, $recursive, $archive); + if ($this->isMonthView()) { + // Group days together into months + $list = $this->timelineQuery->daysToMonths($list); + } else { + // Preload some day responses + $this->preloadDays($list, $uid, $folder); + } // Add subfolder info if querying non-recursively - if (!$recursive) { + if (!$this->isRecursive()) { array_unshift($list, $this->getSubfoldersEntry($folder)); } @@ -88,18 +89,18 @@ class DaysController extends ApiBase $uid = $this->getUid(); // Check for wildcard - $day_ids = []; + $dayIds = []; if ('*' === $id) { - $day_ids = null; + $dayIds = null; } else { // Split at commas and convert all parts to int - $day_ids = array_map(function ($part) { + $dayIds = array_map(function ($part) { return (int) $part; }, explode(',', $id)); } - // Check if $day_ids is empty - if (null !== $day_ids && 0 === \count($day_ids)) { + // Check if $dayIds is empty + if (null !== $dayIds && 0 === \count($dayIds)) { return new JSONResponse([], Http::STATUS_OK); } @@ -112,21 +113,29 @@ class DaysController extends ApiBase return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_NOT_FOUND); } - // Params - $recursive = null === $this->request->getParam('folder'); - $archive = null !== $this->request->getParam('archive'); + // Convert to actual dayIds if month view + if ($this->isMonthView()) { + $dayIds = $this->timelineQuery->monthIdToDayIds($dayIds[0]); + } // Run actual query try { $list = $this->timelineQuery->getDay( $folder, $uid, - $day_ids, - $recursive, - $archive, + $dayIds, + $this->isRecursive(), + $this->isArchive(), $this->getTransformations(false), ); + // Force month id for dayId for month view + if ($this->isMonthView()) { + foreach ($list as &$photo) { + $photo['dayid'] = (int) $dayIds[0]; + } + } + return new JSONResponse($list, Http::STATUS_OK); } catch (\Exception $e) { return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR); @@ -252,13 +261,11 @@ class DaysController extends ApiBase /** * Preload a few "day" at the start of "days" response. * - * @param array $days the days array - * @param string $uid User ID or blank for public shares - * @param null|Folder $folder the folder to search in - * @param bool $recursive search in subfolders - * @param bool $archive search in archive folder only + * @param array $days the days array + * @param string $uid User ID or blank for public shares + * @param null|Folder $folder the folder to search in */ - private function preloadDays(array &$days, string $uid, &$folder, bool $recursive, bool $archive) + private function preloadDays(array &$days, string $uid, &$folder) { $transforms = $this->getTransformations(false); $preloaded = 0; @@ -283,8 +290,8 @@ class DaysController extends ApiBase $folder, $uid, $preloadDayIds, - $recursive, - $archive, + $this->isRecursive(), + $this->isArchive(), $transforms, ); diff --git a/lib/Db/TimelineQueryAlbums.php b/lib/Db/TimelineQueryAlbums.php index 55bc4c4a..6ac3a84e 100644 --- a/lib/Db/TimelineQueryAlbums.php +++ b/lib/Db/TimelineQueryAlbums.php @@ -78,6 +78,44 @@ trait TimelineQueryAlbums return $albums; } + /** + * Convert days response to months response. + * The dayId is used to group the days into months. + */ + public function daysToMonths(array &$days) + { + $months = []; + foreach ($days as &$day) { + $dayId = $day['dayid']; + $time = $dayId * 86400; + $monthid = strtotime(date('Ym', $time).'01') / 86400; + + if (empty($months) || $months[\count($months) - 1]['dayid'] !== $monthid) { + $months[] = [ + 'dayid' => $monthid, + 'count' => 0, + ]; + } + + $months[\count($months) - 1]['count'] += $day['count']; + } + + return $months; + } + + /** Convert list of month IDs to list of dayIds */ + public function monthIdToDayIds(int $monthId) + { + $dayIds = []; + $firstDay = (int) $monthId; + $lastDay = strtotime(date('Ymt', $firstDay * 86400)) / 86400; + for ($i = $firstDay; $i <= $lastDay; ++$i) { + $dayIds[] = (string) $i; + } + + return $dayIds; + } + /** * Get album if allowed. Also check if album is shared with user. * diff --git a/lib/Db/TimelineQueryDays.php b/lib/Db/TimelineQueryDays.php index 37741f02..8e5c2bb9 100644 --- a/lib/Db/TimelineQueryDays.php +++ b/lib/Db/TimelineQueryDays.php @@ -78,7 +78,7 @@ trait TimelineQueryDays * * @param null|Folder $folder The folder to get the day from * @param string $uid The user id - * @param int[] $dayid The day id + * @param int[] $day_ids The day ids to fetch * @param bool $recursive If the query should be recursive * @param bool $archive If the query should include only the archive folder * @param array $queryTransforms The query transformations to apply diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index a3765004..e0bc5d0b 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -256,6 +256,10 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { return window.innerWidth <= 600; } + isMonthView() { + return this.$route.name === "albums"; + } + allowBreakout() { return this.isMobileLayout() && !this.config_squareThumbs; } @@ -534,6 +538,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { query.set("folder_share", this.$route.params.token); } + // Month view + if (this.isMonthView()) { + query.set("monthView", "1"); + } + // Create query string and append to URL const queryStr = query.toString(); if (queryStr) { @@ -582,7 +591,12 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { // The reason this function is separate from processDays is // because this call is terribly slow even on desktop const dateTaken = utils.dayIdToDate(head.dayId); - const name = utils.getLongDateStr(dateTaken, true); + let name: string; + if (this.isMonthView()) { + name = utils.getMonthDateStr(dateTaken); + } else { + name = utils.getLongDateStr(dateTaken, true); + } // Cache and return head.name = name; @@ -782,6 +796,13 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { // Aggregate fetch requests this.fetchDayQueue.push(dayId); + + // Only single queries allowed for month vie + if (this.isMonthView()) { + return this.fetchDayExpire(); + } + + // Defer for aggregation if (!this.fetchDayTimer) { this.fetchDayTimer = window.setTimeout(() => { this.fetchDayTimer = null; diff --git a/src/services/Utils.ts b/src/services/Utils.ts index 5ecf9140..a0c196bf 100644 --- a/src/services/Utils.ts +++ b/src/services/Utils.ts @@ -51,6 +51,15 @@ export function getLongDateStr(date: Date, skipYear = false, time = false) { }); } +/** Get month and year string */ +export function getMonthDateStr(date: Date) { + return date.toLocaleDateString(getCanonicalLocale(), { + month: "long", + year: "numeric", + timeZone: "UTC", + }); +} + /** Get text like "5 years ago" from a date */ export function getFromNowStr(date: Date) { // Get fromNow in correct locale