Commit Graph

17 Commits

Author SHA1 Message Date
Edouard Vanbelle 5eb4d9117c chore(front): apply biome formatter 2026-06-15 16:30:38 +02:00
DioCrafts 81a93a489b 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>
2026-06-15 00:24:27 +02:00
Claude 8dab135090 Add embedded Tantivy full-text content search
/api/search now finds files by CONTENT as well as by name: BM25-ranked
matches over extracted text (PDF, Office OOXML/ODF, plain text/code)
with typo-tolerant fuzzy terms and search-as-you-type prefix matching,
served from an embedded Tantivy index at {storage}/.search-index.

Pipeline (all off the request path, mirroring tree-etag + thumbnails):
- statement triggers on storage.files append to a durable dirty queue
  (storage.search_index_dirty) - every write surface (REST, WebDAV,
  NextCloud, WOPI, trash) is covered, crash-safe by construction
- ContentIndexWorker drains the queue on the maintenance pool, extracts
  text once per unique BLAKE3 blob (storage.blob_extracted_text cache:
  N copies = 1 extraction, renames/moves = 0 re-extraction) and applies
  batched single-writer Tantivy commits; queue rows are deleted only
  after the commit succeeds (at-least-once, idempotent upserts)
- the index is a derived artifact: a version-marker mismatch wipes and
  reseeds it from Postgres, which remains the single source of truth

SearchService merges content hits into the existing name search: hits
are hydrated through ONE SQL round-trip that re-applies user scope,
trash state and every active filter (a stale index id can never leak),
scored below name matches, and returned with a plain-text snippet and
a match_source field. Index failure or
OXICLOUD_ENABLE_CONTENT_SEARCH=false degrades to name-only search; a
discard-only janitor keeps the trigger-fed queue bounded while disabled.

The frontend renders the snippet under the file name in list view.

New dependencies: tantivy 0.26, zip 8.6 (deflate only), pdf-extract 0.10.

https://claude.ai/code/session_01Sc7F4xbo83YbFAQ4xEeDrX
2026-06-11 15:16:03 +00:00
Claude ecbdaee19e Frontend perf: pdf.js smart preload, admin poll cleanup, list render hoisting
Three targeted frontend fixes:

1. pdf.js smart preload (thumbnail.js, resourceIcon.js). The first PDF
   thumbnail of a session stalled 1-2s on the lazy import of the ~1.3 MB
   pdf.js stack. buildResourceIcon() now fires thumbnail.preloadPdf()
   the moment a PDF row enters the DOM, warming both the module
   (~300 KB, via the now promise-memoized getPdfjsLib, shared with real
   users) and the worker script (~1 MB, via a cache-priming fetch —
   pdf.js only requests it on first getDocument). Only folders that
   actually contain PDFs pay the download; idempotent after first call,
   resets on failure so transient offline retries.

2. Admin migration poll cleanup (admin.js). The 2s setInterval kept
   hitting the API and updating hidden DOM after leaving the Storage
   tab, and polled a failing endpoint forever after session expiry
   (!resp.ok returned without clearing). New stopMigrationPolling()
   helper, invoked on tab switch away from Storage, on non-running
   status, and on failed polls; tab re-entry re-arms via loadStorage().

3. resourceList.js render hoisting. Per-row i18n.t() type-cell lookups
   and the fully item-invariant _renderCustomActions() HTML were
   recomputed for every row; they now resolve once per batch via
   _buildItemLabels() (per-category labels memoized, rebuilt each
   batch so locale switches keep working). _findLaneByKey() swaps the
   container-wide attribute querySelector for an O(1) _lanes Map kept
   in sync at the only lane create/wipe sites.

https://claude.ai/code/session_0193Hff42gaA962wThxMGSd1
2026-06-11 09:58:46 +00:00
Claude 64c1e4c8e5 perf(resource-list): fold per-row listeners into the delegated handler
_bindItemEvents attached direct click listeners to the favorite star,
the shared badge and every custom action button of every row — ~3+
listeners per row on top of the container delegation the component
already wires, i.e. ~10-15k listeners with a few thousand rows loaded
via load-more, plus the per-row binding cost on every render.

The container's delegated click handler already dispatches by
closest() checks; move the three button behaviours there (they return
before the card-open branch, preserving the old stopPropagation
semantics) and drop _bindItemEvents entirely. Row creation is now
pure innerHTML — no listener allocation per row.

https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
2026-06-10 09:01:55 +00:00
Edouard Vanbelle 540c947e61 fix(ui): better handling of new folder creation
issue: on folder creation view was wiped and displaying only the new folder
    fix: add a "new" swimlane if in group mode and scroll up to the new created folder
2026-06-05 11:25:09 +02:00
Edouard Vanbelle ea83891a61 feat(trash): move trash API to normalized version (with cursor, orderBy) + normalize Trash section to existing components
normalize also component to format badges (expiry, role, etc)
2026-05-30 00:49:32 +02:00
Edouard Vanbelle 615657a7be fix(shares): fix outgoing shares when browsing in files, fix shareBadge action to open shareDialog 2026-05-29 14:08:41 +02:00
Edouard Vanbelle 63722c868e feat(myshares): new version of myshares sections, supporting grants Users + Tokens 2026-05-29 13:12:59 +02:00
Edouard Vanbelle 99da124388 fix(ui/thumbnail): fix thumbnail generation in release
-  reference to /vendors/ must be absolute path (the bundle isn't the same path as the current library doing the lazy loading)
2026-05-28 14:22:57 +02:00
Edouard Vanbelle 29d8aab9c0 feat(resourceList): keep consistence in groupby options order accross sectons + add userVignette on owner's swimlane 2026-05-28 12:12:22 +02:00
Edouard Vanbelle 7f4b329718 feat(itemToolltip): fusion pathTooltip + ownerTooltip into itemToolltip, move it in the header for more visibility 2026-05-28 12:12:22 +02:00
Edouard Vanbelle 859f9b5f86 refactor: separate roles between ui, fileView, resourceList 2026-05-28 00:15:05 +02:00
Edouard Vanbelle 5afb30ebfd feat(swimlane): add swimlane engine with first version on SharedWithMe section
added group by:

        - None (= ordered by folders/file name)
        - Type (Folder first, then Image, Vidao, Audio, Document, etc...)
        - Owner
        - Size (With logarithmic groups))
        - Shared date (with groups: today, last 7 days, last 30 days, then year)
2026-05-28 00:15:05 +02:00
Edouard Vanbelle 720bf11168 use of resourceList 2026-05-28 00:15:05 +02:00
Edouard Vanbelle 6ac4e6177c refactor(js): move multiSelect into batchToolbar + move residual method into relevant components 2026-05-28 00:15:05 +02:00
Edouard Vanbelle 1cd934d594 refactor(resourceList): move grid/list view into a resourceList component, purpose normalize on all sections views 2026-05-28 00:15:04 +02:00