Skip to content

feat(4336): sharing hierarchy conflict UX (ADR-0005)#256

Open
Gerry-Cern wants to merge 6 commits into
cernboxfrom
feat-4336
Open

feat(4336): sharing hierarchy conflict UX (ADR-0005)#256
Gerry-Cern wants to merge 6 commits into
cernboxfrom
feat-4336

Conversation

@Gerry-Cern

Copy link
Copy Markdown

Summary

Implements the ownCloud Web side of ADR-0005 hierarchical sharing conflicts for collaborator share mutations via Graph.

When Reva rejects a create/update share with HTTP 409 and a structured JSON body, the UI:

  • Shows an error for hard conflicts (parent_conflict, can_force: false)
  • Shows a confirmation dialog for warnings (child_conflict, can_force: true)
  • Retries the same mutation with the Force: true header after the user confirms

Backend contract: targets Jesse Geens' Reva branch feat/share-consistency. Deploy web and Reva together.

Behaviour

Mutation Entry point Conflict handling
Create (invite) InviteCollaboratorForm Deferred batch: collect all child_conflict 409s during parallel invites, show one combined modal, then re-issue failed invites with Force: true
Update ListItem (role/expiry) Per-action confirm modal → retry with Force: true
Delete FileShares, SpaceMembers Same retry wrapper (latent until Reva exposes hierarchy 409 on DELETE)

Reva JSON contract (409)

{
  "error_type": "parent_conflict | child_conflict",
  "message": "...",
  "can_force": true,
  "conflicting_shares": [{ "id", "resource_id", "path", "permission_type" }]
}

- Added `useSharingHierarchyConflictConfirm` to manage user confirmation for sharing conflicts.
- Introduced `SharingHierarchyConflictCancelledError` for error handling.
- Updated share-related components to utilize the new conflict confirmation logic.
- Enhanced `useSharesStore` to retry share operations with a force header upon user confirmation of conflicts.
- Added tests for conflict handling in share operations.
Match Jesse Geens hierarchy conflict contract (error_type, can_force,
conflicting_shares, Force header) and batch deferred 409 confirmations
when inviting multiple collaborators.
@Gerry-Cern Gerry-Cern requested a review from diocas June 18, 2026 10:55
Gerry-Cern and others added 4 commits June 25, 2026 09:02
Show an inform modal when the server returns can_force false, suppress
error toasts when the user cancels a force confirmation, filter indirect
shares from invite autocomplete, and parse richer conflicting_shares fields.
Replace plain-text hierarchy conflict dialogs with a grouped scrollable modal
aligned to the ticket mockup. Revert stale role dropdown state on failed
updates and offer to remove redundant direct shares on parent_conflict.
collaboratorShares filtering in the invite autocomplete was matching
direct and indirect shares alike, so any user with an inherited
(parent) share vanished from search entirely. Since roles only ever
increase going down the tree (read < write < deny), a user with just
an indirect share must still be searchable to receive a stronger
direct share on the current resource. Only an existing direct share
should hide the user, since that case is edited via the share list
instead.
- Drop the raw server "message" shown below the intro text; the
  curated, translatable intro already explains the conflict, so
  showing the untranslated API error text underneath was redundant.
- Stop forcing a trailing slash onto every conflicting-share path.
  It broke file shares (e.g. "myfile.txt/") since the fallback applied
  unconditionally regardless of whether the entry was a file or folder.
- Restore bullet points on the conflicting-share list (was disabled
  via list-style: none) and render each entry as a bare <li> instead
  of wrapping it in a <span>, since OcModal's warning-variation styles
  color every descendant <span>, which is what turned the entries
  yellow.
- Simplify the permission-label fallback: drop the heuristic that
  matched a Reva permission_type against role display-name substrings
  (e.g. "view"/"edit"/"deny"), which could mislabel custom role names.
  The role-id lookup path already mirrors how ListItem.vue resolves
  a share's role name (graphRoles[id].displayName via $gettext); the
  permission_type-only fallback now goes straight to the generic
  translated label instead of guessing at a matching role.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants