feat: photo/video capture-date pipeline + premium UI/UX overhaul

Backend — Photos timeline now groups by real capture date instead of upload time. New MediaMetadataService (FileLifecycleHook) extracts EXIF DateTimeOriginal from images and container creation_time from videos (mov/mp4/mkv) via nom-exif, timezone-correct (OffsetTimeOriginal), persisting captured_at so the existing media_sort_date trigger takes over. Adds POST /admin/photos/metadata/reextract to backfill existing media. Falls back to upload date when no embedded date exists.

Frontend — premium grid cards: combined metadata line (relative date · size, owner avatar when shared), custom selection checkbox with a clear checked state, uniform full-width 4:3 thumbnail tiles independent of filename length, centered file-type icons, and a hit-test fix so checkbox/star/kebab clicks reach the controls (the decorative thumbnail no longer captures pointer events). Notification messages internationalised across all 16 locales. Broader polish: design tokens, a11y/focus-visible states, brand + PWA assets.

Chore — bump semver-compatible dependencies (cargo upgrade); add nom-exif 3.6.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DioCrafts
2026-06-15 00:17:17 +02:00
parent dac299fea6
commit 81a93a489b
129 changed files with 8194 additions and 2473 deletions
+102 -7
View File
@@ -157,7 +157,11 @@
"close": "Sluiten",
"delete_permanently": "Permanent verwijderen",
"empty_trash": "Prullenbak legen",
"open_parent_folder": "Naar bovenliggende map"
"open_parent_folder": "Naar bovenliggende map",
"add": "Add",
"apply": "Apply",
"clear": "Clear",
"remove": "Remove"
},
"user_menu": {
"appearance": "Weergave",
@@ -197,7 +201,22 @@
"shareCreated": "Deellink succesvol aangemaakt",
"shareUpdated": "Deelinstellingen bijgewerkt",
"shareRemoved": "Delen verwijderd",
"inviteByEmail": "Uitnodigen via e-mail — uitnodiging wordt verzonden"
"inviteByEmail": "Uitnodigen via e-mail — uitnodiging wordt verzonden",
"directoryUnavailable": "User directory unavailable",
"linkNamePlaceholder": "Link name (optional)",
"newLink": "New link",
"noExpiry": "No expiry",
"pending": "Pending",
"people": "People",
"publicLinks": "Public links",
"role": {
"canEdit": "Can edit",
"canManage": "Can manage",
"canView": "Can view"
},
"searchPlaceholder": "Search people…",
"shareOf": "Share of:",
"sharedLink": "Shared link"
},
"share_dialogTitle": "Deellink",
"share_linkLabel": "Deellink:",
@@ -460,12 +479,15 @@
"magicLinkSubmit": "Aanmeldlink versturen",
"magicLinkSent": "Als er een account bestaat voor dat e-mailadres, is een aanmeldlink verzonden. Controleer uw inbox.",
"magicLinkUnavailable": "Aanmelden per e-mail is niet beschikbaar op deze server.",
"magicLinkNetworkError": "Kan de server niet bereiken: {{message}}"
"magicLinkNetworkError": "Kan de server niet bereiken: {{message}}",
"magicLinkToggle": "No password? Email me a sign-in link",
"passwordsMatch": "Passwords match",
"capsLock": "Caps Lock is on"
},
"storage": {
"title": "Opslag",
"calculating": "Bezig met berekenen...",
"used": "{percentage}% gebruikt ({used} / {total})"
"used": "{{percentage}}% gebruikt ({{used}} / {{total}})"
},
"viewer": {
"unsupported_file": "Dit bestandstype kan niet bekeken worden.",
@@ -532,9 +554,9 @@
},
"batch": {
"one_selected": "1 item geselecteerd",
"n_selected": "{count} items geselecteerd",
"confirm_delete": "Weet je zeker dat je {count} items naar de prullenbak wilt verplaatsen?",
"move_title": "Verplaats {count} item(s)",
"n_selected": "{{count}} items geselecteerd",
"confirm_delete": "Weet je zeker dat je {{count}} items naar de prullenbak wilt verplaatsen?",
"move_title": "Verplaats {{count}} item(s)",
"add_favorites": "Toevoegen aan favorieten",
"move_copy": "Verplaatsen of kopiëren"
},
@@ -881,5 +903,78 @@
"notifyRateLimited": "Te veel notificaties voor deze ontvanger — probeer het later opnieuw.",
"removeAccess": "Toegang verwijderen",
"resendInvitation": "Uitnodigingsmail opnieuw verzenden"
},
"sort": {
"asc": "ascending",
"desc": "descending"
},
"notif": {
"errorTitle": "Error",
"searchError": "Error performing search",
"cleanupCompleted": "Cleanup completed",
"cleanupCompletedBody": "Recent files history has been cleared",
"batchCopy": "Batch copy",
"batchCopyBody": "{{success}} copied, {{errors}} failed",
"itemsCopied": "Items copied",
"itemsCopiedBody": "{{count}} items copied successfully",
"batchMove": "Batch move",
"batchMoveBody": "{{success}} moved, {{errors}} failed",
"itemsMoved": "Items moved",
"itemsMovedBody": "{{count}} items moved successfully",
"batchDelete": "Batch delete",
"batchDeleteBody": "{{success}} moved to trash, {{errors}} failed",
"movedToTrash": "Moved to trash",
"movedToTrashBody": "{{count}} items moved to trash",
"trashItemsError": "Could not move items to trash",
"preparingDownload": "Preparing download",
"preparingDownloadBody": "Preparing your download…",
"downloadItemsError": "Could not download selected items",
"favoritesAddError": "Could not add items to favorites",
"invalidEmail": "Please enter a valid email address",
"notificationSendError": "Could not send notification",
"folderCreated": "Folder created",
"folderCreatedBody": "\"{{name}}\" created successfully",
"fileMoved": "File moved",
"fileMovedBody": "File moved successfully",
"fileMoveError": "Error moving the file: {{error}}",
"fileMoveErrorGeneric": "Error moving the file",
"folderMoved": "Folder moved",
"folderMovedBody": "Folder moved successfully",
"folderMoveError": "Error moving the folder: {{error}}",
"folderMoveErrorGeneric": "Error moving the folder",
"fileCopied": "File copied",
"fileCopiedBody": "File copied successfully",
"fileCopyError": "Error copying the file: {{error}}",
"fileCopyErrorGeneric": "Error copying the file",
"folderRenamed": "Folder renamed",
"folderRenamedBody": "Folder renamed to \"{{name}}\"",
"fileTrashed": "File moved to trash",
"fileTrashedBody": "\"{{name}}\" moved to trash",
"fileDeleted": "File deleted",
"fileDeletedBody": "\"{{name}}\" deleted successfully",
"fileDeleteError": "Error deleting the file",
"folderTrashed": "Folder moved to trash",
"folderTrashedBody": "\"{{name}}\" moved to trash",
"folderDeleted": "Folder deleted",
"folderDeletedBody": "\"{{name}}\" deleted successfully",
"folderDeleteError": "Error deleting the folder",
"itemRestored": "Item restored",
"itemRestoredBody": "Item restored successfully",
"itemRestoreError": "Error restoring the item",
"itemDeleted": "Item deleted",
"itemDeletedBody": "Item permanently deleted",
"itemDeleteError": "Error deleting the item",
"trashEmptied": "Trash emptied",
"trashEmptiedBody": "The trash has been emptied successfully",
"trashEmptyError": "Error emptying the trash",
"cacheCleared": "Cache cleared",
"cacheClearedBody": "Search cache cleared successfully",
"cacheClearError": "Error clearing search cache",
"wopiOpenError": "Could not open the document editor.",
"linkCopied": "Link copied",
"linkCopiedBody": "Link copied to clipboard",
"linkCopyError": "Could not copy link",
"notificationSent": "Notification sent",
"notificationSentBody": "Notification sent to {{email}}"
}
}