All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- AlbumDetailView: 'Add photos' button now correctly hidden for album viewers (was visible regardless of role)
- AlbumsView: suppress 'Shared (Owner)' badge on own albums when using Immich v3 (owner now appears in
albumUserswithrole: 'owner') - AlbumDetailView:
existingAssetIdsfalls back to already-loaded bucket assets on Immich v3 (wherealbum.assetswas removed), so the photo picker can still grey-out duplicates ImmichService::getAlbums(): send both v2 (shared) and v3 (isOwned/isShared) filter params so owned and shared albums are fetched correctly on both Immich versionsImmichService::searchByLocation(): explicitly setvisibility: 'timeline'to exclude archived/locked assets on Immich v3 (v3 changed the default from'timeline'to'all')ImmichService::uploadAsset(): addhttp_errors: false+ explicit HTTP status check with response body logging; transport errors are now caught and logged with full context
webpack5.107.2 → 5.108.1webpack-cli7.0.3 → 7.1.0@nextcloud/eslint-config8.4.2 → 9.0.0
- AlbumsView: role badge for shared albums — albums shared with you show a "Shared (Editor)" or "Shared (Viewer)" badge next to the photo count
- AlbumDetailView: role-based buttons — Rename and Add photos buttons are hidden for viewers; Delete album button is hidden for non-owners
- App: "Remove from album" action hidden for viewers; "Add to album" action hidden inside album detail view for viewers
- Translations (de, fr, nl, es-ES, pt): added "Shared with me", "Shared ({role})", "Editor", "Viewer" and plural forms for photo-count strings
- Albums shared with the current user (editor / viewer role) were not shown — only owned albums were displayed (fixes #20)
ImmichService::getAlbums()now fetches owned and shared albums in two separate API calls and merges/deduplicates by ID
- TimelineView: masonry grid layout toggle — photos are displayed in a balanced column-based mosaic, preserving aspect ratios
- TimelineView, AlbumDetailView, PersonDetailView, PlaceDetailView: layout toggle button with
aria-labeland:focus-visibleoutline for full keyboard/screen-reader accessibility - AlbumDetailView: timeline bucket lazy-loading — album photos are now fetched in time-based batches (same virtual-scroll pattern as the main timeline)
- AlbumDetailView: desktop "Add photos" button now shown as long as the album has photos (uses total count from buckets, not the eagerly loaded
assetsarray) - PlaceDetailView: place/location asset search results view
- Store: race condition in
fetchAlbumBuckets/fetchAlbumBucketAssets— stale responses from a previous album can no longer overwrite the cache of the currently viewed album - AlbumDetailView: scroll state (
scrollTop,pendingQueue,activeRequests) is now fully reset when navigating to a different album, preventing wrong virtual-scroll window indices - TimelineView, AlbumDetailView, PersonDetailView:
estimateBucketHeightMasonry— replaced O(n²)indexOf(Math.min(...spread))with a simple O(n)for-loop for minimum-column lookup
vue3.5.37 → 3.5.38sass1.100.0 → 1.101.0form-data4.0.5 → 4.0.6 (fixes field-name/filename injection via CR/LF)launch-editor2.13.2 → 2.14.1dompurify3.4.9 → 3.4.11 (fixes leaky config for hooks viasetConfig)ts-loader9.6.0 → 9.6.2@babel/core7.29.7 → 8.0.1 (major — removed unuseduseBuiltIns, no breaking impact on this project)actions/checkoutGitHub Action v6 → v7
AssetsController: added 7 PHPUnit tests covering all code paths of the newsearchLocation()endpoint (field validation, value validation, allowed-field whitelist, service exception propagation)
- App: removed undefined
mobileMenuOpenref in route-change watcher — preventedReferenceErroron navigation - App: restored responsive CSS for
selection-actions-desktop— secondary action buttons now hidden on mobile (≤ 680 px) so the overflow menu is correctly prioritised - PhotoGrid: fixed selection double-toggle on checkbox click —
handleClicknow ignores clicks originating from the checkbox element to prevent simultaneous firing of both handlers - PhotoGrid: replaced
box-shadow: insetselection border with::afterpseudo-element for reliable rendering across all browsers and virtual-scroll contexts - Store:
toggleAssetSelectionnow mutates theSetin-place (add/delete) instead of replacing it — ensures Vue 3 reactive tracking fires correctly for all consumers including virtualised grids - LightboxView: replaced
NcIconSvgWrapperwith inline SVGs in all toolbar buttons and nav arrows — fixes icon colour (fill: #fff) and centering (NcIconSvgWrapperimposed 44 px min-size overriding button layout) - LightboxView: toolbar buttons, nav arrows and panel close button now have
background: transparent— removes unintended hover/focus box drawn by Nextcloud button styles - DetailViews (Album, Explore, People): removed redundant
<h2>title duplicating the breadcrumb label; photo count is now placed directly below the breadcrumb with consistent spacing - PersonDetailView: face avatar repositioned to the right end of the breadcrumb row
axios1.15.2 → 1.17.0 viaoverrides— fixes GHSA-pjwm-pj3p-43mv, GHSA-898c-q2cr-xwhg, GHSA-654m-c8p4-x5fp and othersshell-quote1.8.3 → 1.8.4 viaoverrides— fixes GHSA-w7jw-789q-3m8p (critical: newline injection)wsupdated — fixes GHSA-58qx-3vcg-4xpx (uninitialized memory disclosure)
@nextcloud/vue9.7.0 → 9.8.1vue3.5.33 → 3.5.35vue-router5.0.7 → 5.1.0@babel/core7.22.9 → 7.29.7 (dev)node-polyfill-webpack-plugin4.0.0 → 4.1.0 (dev)sass1.64.2 → 1.100.0 (dev)sass-loader16.0.2 → 17.0.0 (dev)ts-loader9.4.4 → 9.6.0 (dev)webpack5.88.2 → 5.107.2 (dev)webpack-cli6.0.1 → 7.0.3 (dev)
webpack.config.js: addfullySpecified: falserule for.mjs/.jsto resolve Node polyfills (buffer,process) in ESM context introduced bywebpack 5.107+axios 1.17
- Timeline & People view: normalize
timeBucketto ISO-8601 (YYYY-MM-DDTHH:MM:SS.000Z) for Immich v2 API compatibility — fixes empty All Media and Faces views (#53, closes #46)
@nextcloud/dialogs7.3.0 → 7.4.0vue-router5.0.6 → 5.0.7webpack-dev-server5.2.3 → 5.2.4 (dev)qs6.15.1 → 6.15.2express4.22.1 → 4.22.2
- Lightbox: panel close button was hidden behind toolbar due to incorrect z-index — reverted z-index hack, panels now naturally overlay the toolbar while the close button (✕) handles dismissal
- Permission warnings — Admin settings now detect and display missing API-key permissions with a remediation hint
- Timeout error feedback — Timeline/bucket fetches now show a user-friendly message instead of a raw Axios error on timeout
- Lightbox: info/album panel overlapped the toolbar, making all buttons (Close, Download, Info, …) inaccessible (#49)
- Lightbox: added close button (✕) to info panel and album panel so they can be dismissed without pressing Escape
- HTTP: added 60 s timeout to all Guzzle requests to prevent PHP workers from hanging on slow/unresponsive Immich instances
- HTTP: 403 responses from Immich now return
[]with a warning log instead of throwing HTTP 500
- Debug logging: bucket fetches now log asset count per bucket (enable with
occ log:manage --level debug)
@nextcloud/axios2.5.2 → 2.6.0@nextcloud/vue9.6.0 → 9.7.0vue3.5.32 → 3.5.33vue-router5.0.4 → 5.0.6eslint-webpack-plugin5.0.3 → 6.0.0 (dev)fast-xml-builder1.1.5 → 1.2.0fast-uri3.1.0 → 3.1.2 (dev)
- Restored corrupted app icon
<img>tag in README header
postcss8.5.8 → 8.5.12axios1.13.5 → 1.15.2dompurify3.3.3 → 3.4.1follow-redirects1.15.11 → 1.16.0fast-xml-parser(transitive viawebdav) updated to patched version
- Added PHP 8.4 to test matrix; excluded unsupported PHP 8.4 + NC stable30 combination (refs #39)
- Delete assets — Delete files from Immich via lightbox or selection toolbar (moved to trash if enabled in Immich) (#18)
asset.deleteAPI permission required for delete functionality
- Optimized selection toolbar on desktop: Download button + overflow menu (moved Album/Favorites/Delete to kebab menu to reduce clutter)
vue3.5.31 → 3.5.32lodash4.17.23 → 4.18.1 (dev dependency, security update)brace-expansion1.1.12 → 1.1.13 (dev dependency)eslint-webpack-plugin5.0.3 → 6.0.0 (dev dependency)yaml2.8.2 → 2.8.3
- Map view: OpenStreetMap tiles were blocked because Nextcloud sets
Referrer-Policy: no-referrerpage-wide, stripping theRefererheader OSM requires — addedreferrerPolicy: no-referrer-when-downgradedirectly on the tile layer so the browser sends the origin as referer for tile requests (fixes #26)
vue3.5.30 → 3.5.31vue-router5.0.3 → 5.0.4@nextcloud/vue9.5.0 → 9.6.0flatted3.3.3 → 3.4.2 (security: prototype pollution fix)picomatch4.0.3 → 4.0.4 (security: ReDoS fix)yaml2.8.2 → 2.8.3 (security: stack overflow fix)
- Map view: OpenStreetMap tile images were blocked by Nextcloud's Content Security Policy — added a
CspListenerthat extendsimg-srcto allowhttps://*.tile.openstreetmap.org(fixes #18) - Photo hover date tooltip was displayed in German (
de-DE) regardless of user language — now uses browser locale
- French (
fr), Spanish (es-ES), Dutch (nl) and Portuguese (pt) translations via l10n.dev (closes #19) build-l10n.mjs— automatically generatesl10n/*.jsfroml10n/*.jsonat build time; adding a new language only requires a single JSON file
- When the Immich server URL points to a private/local IP and Nextcloud's SSRF protection blocks the request, the settings page now shows the exact cause and the
occcommand to resolve it, instead of a generic "Connection failed" message (refs #12) - Connection test errors now show the actual error detail from the server instead of a generic message
- All hardcoded German UI strings in
LightboxView.vueandTimelineView.vuereplaced witht()i18n calls — tooltips and labels now follow the Nextcloud user language setting instead of always displaying in German (fixes #15) - Date formatting in lightbox now uses browser locale instead of hardcoded
de-DE - Added missing German (
de) translations for all newly i18n-wrapped strings
- Missing
use OCP\AppFramework\Http\Attribute\NoAdminRequired;import inConfigController— the attribute was present but PHP silently ignored it without the import, causing Nextcloud to treat both config endpoints as admin-only and returning403 Forbiddenfor regular users - Added
#[NoAdminRequired]togetConfig()so regular users can also read their own saved settings
- Added required Immich API key permissions table to README
vue3.5.29 → 3.5.30 (bug fixes: reactivity, SSR, custom elements)
- File upload to Immich now streams the file content instead of loading it fully into PHP memory — prevents OOM crashes for large files
userIdnull-guard added in upload and save-to-Nextcloud flows to return401instead of crashingsetConfig()now correctly ignoresvalidate=falsestrings (PHP truthy-check bug)#[NoAdminRequired]added tosetConfig()so regular users can save their own settings
- UUID validation regex extracted to
ImmichService::UUID_PATTERN— eliminates 18 duplicated inline patterns across all controllers getUniqueFileName()loop replaced with boundedfor-loop anduniqid()fallback to prevent infinite loopsgetPersonAssets()capped at 24 monthly buckets (~2 years) to prevent unbounded sequential HTTP requestsuploadAsset()response null-guard: invalid JSON from Immich no longer returnsnullto the frontend- API key decrypt failures now logged as
warningwith hint to re-save the key
dompurifyupdated to 3.3.2 — fixes XSS bypass and prototype pollutionimmutableupdated to 5.1.5 — fixes prototype pollution inmergeDeep/toJSminimatchupdated to 3.1.5 — fixes ReDoS vulnerability
pinia2 → 3vue→ 3.5.29,vue-router4 → 5eslint-webpack-plugin4 → 5actions/checkoutv4 → v6,actions/setup-nodev4 → v6terser-webpack-plugin→ 5.3.17,fast-xml-parser→ 4.5.4
- NC26–32 compatibility: "Add to Immich" file action was not visible on Nextcloud 26–32
due to a registry scoping change in
@nextcloud/filesv4 (only compatible with NC33+). A separate webpack bundle using@nextcloud/filesv3 is now built and loaded automatically for Nextcloud versions below 33. Minimum supported version is now NC27.
- API key encryption: The Immich API key is now encrypted at rest using Nextcloud's
OCP\Security\ICryptobefore being written tooc_preferences. On read, the value is transparently decrypted; a plaintext fallback handles keys stored by older versions. - Input validation: All controllers now validate and sanitize incoming parameters
(album IDs, asset IDs, paths, URLs) and return proper HTTP error codes (
400,500) instead of leaking raw error messages to the client. - Error handling:
ImmichServicecatches exceptions internally and surfaces structured error responses, preventing stack traces from reaching the frontend.
- Bootstrap: Migrated from
boot()+addListener()toregister()+registerEventListener()for proper lazy-loading of the file-action event listener (recommended pattern since NC26).
- Release ZIP now sets correct Unix permissions (755 for directories, 644 for files)
so that
lib/is traversable after unzip on Linux without a manualchmod - Fixed autoloader not being executed due to a UTF-8 BOM in
composer/autoload.phpthat caused a PHP fatal error (strict_typesmust be the first statement) - Timeline: fast scrollbar jumps no longer leave the view blank — in-flight HTTP
requests for buckets that are no longer visible are now cancelled via
AbortControllerand stale entries are purged from the load queue immediately
- Timeline view with lazy-loaded photos and videos grouped by date
- Albums view — browse, create, rename and delete Immich albums
- People view — explore your library by recognized person (face recognition)
- Map view — interactive map of all geotagged photos with cluster markers
- Explore view — browse by city, country, state, object or tag
- Lightbox — full-screen viewer with keyboard navigation, pinch-to-zoom and EXIF metadata panel
- Favorites — mark and unmark assets as favorites from any view
- Save to Nextcloud — save Immich originals directly to your Nextcloud Files via folder picker
- Upload to Immich — send photos and videos from Nextcloud Files to Immich via file action menu
- Multi-select mode — select multiple assets for batch save, download, favorite and album operations
- Album management in Lightbox — add current photo to existing or new album
- German (de) translation
- Personal settings — configure Immich server URL and API key per user
- Admin settings — set instance-wide defaults for server URL and API key