feat(fileDto, folderDto): add is_favorite + is_shared

- provide is_shared and is_favorite information in DTO, information propagated as badge/buttons per items
      regarding performances I try to be minimalis on SQL to prevent any perf regression

    - remove old set of sharedids and favoriteids (was not functionnal anymore)
    - fix date picker (no past selection) in grant
    - fix contextmenu close on /files section
This commit is contained in:
Edouard Vanbelle
2026-07-22 00:36:14 +02:00
parent b7d5d41c90
commit d9987782c4
50 changed files with 1140 additions and 287 deletions
+8
View File
@@ -132,6 +132,14 @@ pub struct FavoriteResourceRow {
pub created_by: Option<Uuid>,
/// §14 provenance — who last touched the row.
pub updated_by: Option<Uuid>,
/// Caller-scoped favorite state — `TRUE` by construction on this
/// listing (every row IS a favorite). The listing SQL hardcodes
/// `TRUE AS is_favorite`; this field stays here so the DTO builder
/// signature stays symmetric with the other listings.
pub is_favorite: bool,
/// Resource-scoped: `true` when the row has any `storage.role_grants`
/// entry. Computed by a per-row `EXISTS` in the listing SQL.
pub is_shared: bool,
/// `true` when `owner_id == requesting user_id`.
pub is_owner: bool,
pub favorited_at: DateTime<Utc>,
+26
View File
@@ -83,6 +83,25 @@ pub struct FileDto {
/// stub/legacy files.
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_by: Option<Uuid>,
/// Caller-scoped: `true` when the requesting user has favorited
/// this file. **Wire contract: always present**, never null and
/// never absent — the SPA reads it as a required `boolean` with no
/// nullish branch. Every emission path (listing endpoints inline
/// via a per-row `EXISTS` in the listing SQL; single-item endpoints
/// via the shared `caller_flags` helper on the favorites port) is
/// responsible for populating this before the DTO reaches the
/// wire. WebDAV/CalDAV/CardDAV DTOs default to `false` — the XML
/// property serializer drops the field entirely, so a stale
/// default is never observable on those surfaces.
pub is_favorite: bool,
/// Resource-scoped: `true` when the file has ANY explicit
/// role-grant on it (link share via `subject_type = 'token'`,
/// user/group grant, any role). "Someone was given access to
/// this beyond drive membership." Same wire contract as
/// `is_favorite` — always present.
pub is_shared: bool,
}
impl From<File> for FileDto {
@@ -132,6 +151,11 @@ impl From<File> for FileDto {
etag,
created_by: parts.created_by,
updated_by: parts.updated_by,
// `From<File>` has no caller context. Callers that will
// emit the DTO to the SPA MUST override these before
// Json emission via the `caller_flags` helper.
is_favorite: false,
is_shared: false,
}
}
}
@@ -189,6 +213,8 @@ impl FileDto {
sort_date: None,
created_by: None,
updated_by: None,
is_favorite: false,
is_shared: false,
}
}
}
+28
View File
@@ -96,6 +96,17 @@ pub struct FolderDto {
/// stub/legacy folders.
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_by: Option<Uuid>,
/// Caller-scoped: `true` when the requesting user has favorited
/// this folder. See `FileDto::is_favorite` for the full wire
/// contract note (always present, never null; enrichment path
/// covers listing rows via inline `EXISTS` and single-item
/// endpoints via the `caller_flags` helper).
pub is_favorite: bool,
/// Resource-scoped: `true` when the folder has ANY explicit
/// role-grant on it. Same wire contract as `is_favorite`.
pub is_shared: bool,
}
impl From<Folder> for FolderDto {
@@ -127,6 +138,11 @@ impl From<Folder> for FolderDto {
etag,
created_by: parts.created_by,
updated_by: parts.updated_by,
// `From<Folder>` has no caller context. Handlers that
// emit to the SPA MUST override via `caller_flags` before
// Json response.
is_favorite: false,
is_shared: false,
}
}
}
@@ -179,6 +195,8 @@ impl FolderDto {
etag: String::new(),
created_by: None,
updated_by: None,
is_favorite: false,
is_shared: false,
}
}
}
@@ -226,6 +244,16 @@ pub struct FolderResourceRow {
pub created_by: Option<Uuid>,
/// §14 provenance — who last touched the row.
pub updated_by: Option<Uuid>,
/// Caller-scoped: `true` when the requesting user has favorited
/// this row. Populates `FileDto::is_favorite` / `FolderDto::is_favorite`
/// on the listing without a follow-up query. Computed by the
/// per-row `EXISTS` in `list_resources_paged`.
pub is_favorite: bool,
/// Resource-scoped: `true` when the row has any `storage.role_grants`
/// entry — link share (`subject_type = 'token'`), user grant, group
/// grant, or any role. Populates `FileDto::is_shared` /
/// `FolderDto::is_shared`.
pub is_shared: bool,
// Pre-computed sort fields — returned by the SQL for cursor construction.
/// `LOWER(name)` used by `name`/`type` sorts.
pub sort_str: String,
+8
View File
@@ -122,6 +122,14 @@ pub struct RecentResourceRow {
/// consumed by the UI but surfaced for API parity with the other
/// listing endpoints.
pub updated_by: Option<Uuid>,
/// Caller-scoped: `true` when the requesting user has favorited
/// this row. Populates `FileDto::is_favorite` /
/// `FolderDto::is_favorite` on this listing via a per-row
/// `EXISTS` in the SQL.
pub is_favorite: bool,
/// Resource-scoped: `true` when the row has any
/// `storage.role_grants` entry.
pub is_shared: bool,
/// `true` when `owner_id == requesting user_id`.
pub is_owner: bool,
pub accessed_at: DateTime<Utc>,
+7
View File
@@ -77,6 +77,13 @@ pub struct TrashResourceRow {
/// §14 provenance — who last touched the row (includes the trash
/// action itself, which stamps `updated_by = caller_id`).
pub updated_by: Option<Uuid>,
/// Caller-scoped: `true` when the caller has favorited this
/// trashed item.
pub is_favorite: bool,
/// Resource-scoped: `true` when the row has any
/// `storage.role_grants` entry (surviving trash — grants are
/// GC'd by `purge_expired_grants` on the 15-day grace).
pub is_shared: bool,
pub trashed_at: DateTime<Utc>,
pub deletion_date: DateTime<Utc>,
/// Original location path (for folders: `path`; for files: `parent.path || '/' || name`).