From eb7fa255b8fb6eb9a774d1b700ff1f5b05929841 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Sat, 4 Jan 2025 08:52:05 -0800 Subject: [PATCH] timeline: show shared name Signed-off-by: Varun Patil Co-Authored-By: Jo Van Bulck --- lib/Controller/ImageController.php | 6 ++++ lib/Db/TimelineQuery.php | 2 ++ lib/Db/TimelineQueryDays.php | 47 ++++++++++++++++++++++++++++++ src/components/Metadata.vue | 24 +++++++++++++++ src/components/frame/Photo.vue | 29 +++++++++++++++++- src/typings/data.d.ts | 5 ++++ 6 files changed, 112 insertions(+), 1 deletion(-) diff --git a/lib/Controller/ImageController.php b/lib/Controller/ImageController.php index ed23abc6f..2bfc6c21d 100644 --- a/lib/Controller/ImageController.php +++ b/lib/Controller/ImageController.php @@ -215,6 +215,12 @@ public function info( $info['basename'] = $file->getName(); $info['uploadtime'] = $file->getUploadTime() ?: $file->getMTime(); + // Get file owner name for shared files + if ($owner = $file->getOwner()) { + $info['owneruid'] = $owner->getUID(); + $info['ownername'] = $owner->getDisplayName(); + } + // Allow these ony for logged in users if ($user = $this->userSession->getUser()) { // Get the path of the file relative to current user diff --git a/lib/Db/TimelineQuery.php b/lib/Db/TimelineQuery.php index c3e719679..77e659b00 100644 --- a/lib/Db/TimelineQuery.php +++ b/lib/Db/TimelineQuery.php @@ -7,6 +7,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IRequest; +use OCP\IUserManager; class TimelineQuery { @@ -33,6 +34,7 @@ class TimelineQuery public function __construct( protected IDBConnection $connection, protected IRequest $request, + protected IUserManager $userManager, ) {} public function allowEmptyRoot(bool $value = true): void diff --git a/lib/Db/TimelineQueryDays.php b/lib/Db/TimelineQueryDays.php index 285558a38..0e9301e6f 100644 --- a/lib/Db/TimelineQueryDays.php +++ b/lib/Db/TimelineQueryDays.php @@ -7,6 +7,7 @@ use OCA\Memories\ClustersBackend; use OCA\Memories\Exif; use OCA\Memories\Settings\SystemConfig; +use OCA\Memories\Util; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -148,6 +149,10 @@ public function getDay( // Filter for files in the timeline path $query = $this->filterFilecache($query, null, $recursive, $archive, $hidden); + // SELECT storage ID to check if this photo is shared + $query->leftJoin('f', 'storages', 's', $query->expr()->eq('f.storage', 's.numeric_id')); + $query->selectAlias('s.id', 'storage_id'); + // FETCH all photos in this day $day = $this->executeQueryWithCTEs($query)->fetchAll(); @@ -345,6 +350,23 @@ private function postProcessDayPhoto(array &$row, bool $monthView = false): void if ($monthView) { $row['dayid'] = $this->dayIdToMonthId($row['dayid']); } + + // Convert storage_id to user display name + if ($storage = $row['storage_id'] ?? null) { + unset($row['storage_id']); + + /** @var array */ + static $userMemo = []; + if ($user = $userMemo[$storage] ?? null) { + $row['shared_by'] = $user; + } elseif ($user = $this->storageIdToUserName($storage)) { + $row['shared_by'] = $userMemo[$storage] = $user; + } + + if ('[self]' === $row['shared_by']) { + unset($row['shared_by']); + } + } } /** @@ -382,4 +404,29 @@ private function dayIdToMonthId(int $dayId): int return $memoize[$dayId] = strtotime(date('Ym', $dayId * 86400).'01') / 86400; } + + private function storageIdToUserName(string $storage): string + { + // Storage ID looks like "home::{uid}" or "local::{/path}" etc + $pos = strpos($storage, '::'); + if (false === $pos) { + return '[unknown]'; + } + $uid = substr($storage, $pos + 2); + + // Check if self + if (Util::isLoggedIn() && $uid === Util::getUID()) { + return '[self]'; + } + + // If there are slashes, it's not a user (could be group folder) + if (str_contains($uid, '/')) { + return '[unknown]'; + } + + // Otherwise it *may* be a user + $user = $this->userManager->get($uid); + + return $user ? $user->getDisplayName() : '[unknown]'; + } } diff --git a/src/components/Metadata.vue b/src/components/Metadata.vue index 63be50354..6a8b16bf9 100644 --- a/src/components/Metadata.vue +++ b/src/components/Metadata.vue @@ -5,6 +5,14 @@
{{ description }}
+
+
{{ t('memories', 'Shared By') }}
+
+ + {{ baseInfo.ownername }} +
+
+
{{ t('memories', 'People') }}
@@ -71,6 +79,7 @@ import type { Component } from 'vue'; import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'; import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'; +const NcAvatar = () => import('@nextcloud/vue/dist/Components/NcAvatar.js'); import axios from '@nextcloud/axios'; import { getCanonicalLocale } from '@nextcloud/l10n'; @@ -107,6 +116,7 @@ export default defineComponent({ components: { NcActions, NcActionButton, + NcAvatar, AlbumsList, Cluster, EditIcon, @@ -388,6 +398,10 @@ export default defineComponent({ return clusters?.recognize ?? []; }, + + isShared(): boolean { + return !!this.baseInfo.owneruid && this.baseInfo.owneruid !== utils.uid; + }, }, methods: { @@ -507,6 +521,16 @@ export default defineComponent({ } } +.shared-by { + > .top-field { + margin-top: 6px; + margin-bottom: 10px; + } + .name { + margin-left: 8px; + } +} + .people { margin-bottom: 6px; > .section-title { diff --git a/src/components/frame/Photo.vue b/src/components/frame/Photo.vue index 0e5a7c4fb..57f5dfe45 100644 --- a/src/components/frame/Photo.vue +++ b/src/components/frame/Photo.vue @@ -35,11 +35,15 @@
-
+
+
+ {{ sharedBy }} +
+
& { + transform: translate(-$icon-size, -$icon-size); + } + } + + > .shared-by { + font-size: 0.75em; + line-height: 0.75em; + font-weight: 400; + } + > .video { display: flex; line-height: 22px; // force text height to match diff --git a/src/typings/data.d.ts b/src/typings/data.d.ts index d95dc2676..179fc4ced 100644 --- a/src/typings/data.d.ts +++ b/src/typings/data.d.ts @@ -51,6 +51,8 @@ declare module '@typings' { h?: number; /** Live Photo identifier */ liveid?: string; + /** File owner display name */ + shared_by?: string; /** Grid display width px */ dispW?: number; @@ -114,6 +116,9 @@ declare module '@typings' { size: number; uploadtime: number; + owneruid: string; + ownername: string; + filename?: string; address?: string; tags?: { [id: string]: string };