From 7c53248936f28b8a4302b5f79e9d564d675525d2 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Sat, 1 Apr 2023 18:14:37 -0700 Subject: [PATCH] map: fix init position Signed-off-by: Varun Patil --- appinfo/routes.php | 1 + lib/Controller/MapController.php | 12 ++++++ lib/Db/TimelineQueryDays.php | 2 +- lib/Db/TimelineQueryMap.php | 34 ++++++++++++++++ src/components/Timeline.vue | 26 +++++++++---- src/components/top-matter/MapSplitMatter.vue | 41 ++++++++++++++++---- src/services/API.ts | 4 ++ 7 files changed, 104 insertions(+), 16 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index b0b2fdf3..b70dfd14 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -60,6 +60,7 @@ return [ ['name' => 'Tags#set', 'url' => '/api/tags/set/{id}', 'verb' => 'PATCH'], ['name' => 'Map#clusters', 'url' => '/api/map/clusters', 'verb' => 'GET'], + ['name' => 'Map#init', 'url' => '/api/map/init', 'verb' => 'GET'], ['name' => 'Archive#archive', 'url' => '/api/archive/{id}', 'verb' => 'PATCH'], diff --git a/lib/Controller/MapController.php b/lib/Controller/MapController.php index 7660b942..2c0ac165 100644 --- a/lib/Controller/MapController.php +++ b/lib/Controller/MapController.php @@ -65,4 +65,16 @@ class MapController extends GenericApiController return new JSONResponse($clusters); }); } + + /** + * @NoAdminRequired + */ + public function init(): Http\Response + { + return Util::guardEx(function () { + return new JSONResponse([ + 'pos' => $this->timelineQuery->getMapInitialPosition(), + ]); + }); + } } diff --git a/lib/Db/TimelineQueryDays.php b/lib/Db/TimelineQueryDays.php index 000a8f6d..fc562709 100644 --- a/lib/Db/TimelineQueryDays.php +++ b/lib/Db/TimelineQueryDays.php @@ -211,7 +211,7 @@ trait TimelineQueryDays ?TimelineRoot $root = null, bool $recursive = true, bool $archive = false - ) { + ): IQueryBuilder { if (null === $root) { $root = $this->root(); } diff --git a/lib/Db/TimelineQueryMap.php b/lib/Db/TimelineQueryMap.php index 25e2567a..870d9687 100644 --- a/lib/Db/TimelineQueryMap.php +++ b/lib/Db/TimelineQueryMap.php @@ -9,6 +9,8 @@ use OCP\IDBConnection; trait TimelineQueryMap { + use TimelineQueryDays; + protected IDBConnection $connection; public function transformMapBoundsFilter(IQueryBuilder &$query, bool $aggregate, string $bounds, string $table = 'm') @@ -121,4 +123,36 @@ trait TimelineQueryMap return $files; } + + /** + * Gets the suggested initial coordinates for the map. + * Uses the coordinates of the newest photo (by date). + */ + public function getMapInitialPosition(): ?array + { + $query = $this->connection->getQueryBuilder(); + + // SELECT coordinates + $query->select('m.lat', 'm.lon')->from('memories', 'm'); + + // WHERE this photo is in the user's requested folder recursively + $query = $this->joinFilecache($query); + + // WHERE this photo has coordinates + $query->where($query->expr()->andX( + $query->expr()->isNotNull('m.lat'), + $query->expr()->isNotNull('m.lon') + )); + + // ORDER BY datetaken DESC + $query->orderBy('m.datetaken', 'DESC'); + + // LIMIT 1 + $query->setMaxResults(1); + + // FETCH coordinates + $coords = $this->executeQueryWithCTEs($query)->fetch(); + + return $coords ?: null; + } } diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index f912768c..7b8c6ebf 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -627,12 +627,13 @@ export default defineComponent({ } // Map Bounds - if (this.$route.name === "map" && this.$route.query.b) { - API.DAYS_FILTER( - query, - DaysFilterType.MAP_BOUNDS, - this.$route.query.b - ); + if (this.$route.name === "map") { + const bounds = this.$route.query.b; + if (!bounds) { + throw new Error("Missing map bounds"); + } + + API.DAYS_FILTER(query, DaysFilterType.MAP_BOUNDS, bounds); } // Month view @@ -699,11 +700,20 @@ export default defineComponent({ /** Fetch timeline main call */ async fetchDays(noCache = false) { - // Get top folders if route + // Awaiting this is important because the folders must render + // before the timeline to prevent glitches await this.fetchFolders(); // Get URL an cache identifier - const url = API.Q(API.DAYS(), this.getQuery()); + let url: string; + try { + url = API.Q(API.DAYS(), this.getQuery()); + } catch (err) { + // Likely invalid route; just quit doing anything + return; + } + + // URL for cached data const cacheUrl = this.$route.name + url; // Try cache first diff --git a/src/components/top-matter/MapSplitMatter.vue b/src/components/top-matter/MapSplitMatter.vue index 4da1d29c..71013a0f 100644 --- a/src/components/top-matter/MapSplitMatter.vue +++ b/src/components/top-matter/MapSplitMatter.vue @@ -110,12 +110,7 @@ export default defineComponent({ map.mapObject.zoomControl.setPosition("topright"); // Initialize - if (this.$route.query.b && this.$route.query.z) { - this.setBoundsFromQuery(); - this.fetchClusters(); - } else { - this.refresh(); - } + this.initialize(); // If currently dark mode, set isDark const pane = document.querySelector(".leaflet-tile-pane"); @@ -143,11 +138,43 @@ export default defineComponent({ watch: { $route() { - this.fetchClusters(); + this.initialize(true); }, }, methods: { + /** + * Get initial coordinates for display and set them. + * Then fetch clusters. + */ + async initialize(reinit: boolean = false) { + // Check if we have bounds and zoom in query + if (this.$route.query.b && this.$route.query.z) { + if (!reinit) { + this.setBoundsFromQuery(); + } + return await this.fetchClusters(); + } + + // Otherwise, get location from server + try { + const init = await axios.get(API.MAP_INIT()); + + // Init data contains position information + const map = this.$refs.map as LMap; + const pos = init?.data?.pos; + if (!pos?.lat || !pos?.lon) { + throw new Error("No position data"); + } + + // This will trigger route change -> fetchClusters + map.mapObject.setView([pos.lat, pos.lon], 11); + } catch (e) { + // Make sure we initialize clusters anyway + this.refresh(); + } + }, + async refreshDebounced() { utils.setRenewingTimeout(this, "refreshTimer", this.refresh, 250); }, diff --git a/src/services/API.ts b/src/services/API.ts index e93b5557..b572e2e5 100644 --- a/src/services/API.ts +++ b/src/services/API.ts @@ -188,4 +188,8 @@ export class API { static MAP_CLUSTER_PREVIEW(id: number) { return tok(gen(`${BASE}/map/clusters/preview/{id}`, { id })); } + + static MAP_INIT() { + return tok(gen(`${BASE}/map/init`)); + } }