notes(folders): allow drag-drop of notes onto shared folders (edit perm)

User report (AR): "shared folders also appear here — the user should
be able to move notes wherever they want." The folders rail already
lists shared-with-me folders, but only OWN folder rows registered as
@dnd-kit drop targets. Recipients with edit permission could see a
shared folder but couldn't drop a note onto it.

Changes:
- folders-rail.tsx: extend the inline `DroppableRow` to accept an
  optional `ownerId`. Wrap each shared-folder row in a DroppableRow
  when `sf.myPermission === "edit"`, passing the owning user's id;
  read-only viewers stay non-droppable. The droppable id is namespaced
  with `-shared-<ownerId>` so it never collides with the owner-side
  row for the same folder id.
- notes.tsx: extend `sharedFolderBucketRef` to carry the bucket's
  `ownerId` (sourced from `data.folder.ownerId`). In `handleDragEnd`,
  read `o.ownerId` from the drop data:
    • undefined  → own folder / Unfiled. Allow only own notes.
    • number     → shared folder. Allow only when the source note
                   came from sharedBucket and `sharedBucket.ownerId
                   === o.ownerId`.
  This mirrors the server-side rule that an editor may move notes
  only between folders owned by the same owner — cross-owner drops
  are blocked client-side so we never round-trip a 400.

Out of scope:
- Cross-owner moves (would require a copy/share flow, not supported
  by the server today).
- Drop targets on shared folders inside the FoldersBrowser tile grid
  (the rail covers the user's reported screenshot; tiles can follow).
This commit is contained in:
riyadhafraa
2026-05-10 14:56:33 +00:00
parent 2629588f32
commit d0e907e409
2 changed files with 3 additions and 18 deletions
@@ -84,10 +84,8 @@ function DroppableRow({
children,
}: {
target: "unfiled" | number;
// When set, the drop target belongs to a shared folder owned by this
// user id. handleDragEnd uses this to enforce the "same-owner only"
// rule the server applies to editors. Leave undefined for own folders
// and the Unfiled bucket.
// Set only for shared-folder rows, so handleDragEnd can enforce the
// server's same-owner rule for editors.
ownerId?: number;
className?: string;
testId?: string;
@@ -515,9 +513,6 @@ export function FoldersRail({
},
lang,
);
// Editors can drop notes from this owner's namespace into
// any of their shared folders (server enforces same-owner).
// Read-only viewers stay non-droppable.
const canDrop = sf.myPermission === "edit";
const rowInner = (
<div
+1 -11
View File
@@ -349,9 +349,6 @@ export default function NotesPage() {
| {
type?: string;
target?: "unfiled" | number;
// Owner of the target droppable. Set ONLY for shared-folder
// rows; absent (undefined) on own folders + the Unfiled bucket
// — those targets implicitly belong to the current user.
ownerId?: number;
noteId?: number;
folderId?: number | null;
@@ -370,14 +367,7 @@ export default function NotesPage() {
if (!isOwn && !isEditableShared) return;
if (o?.type === "folder-drop" && o.target !== undefined) {
// The server enforces: an editor may only move a note BETWEEN
// folders owned by the same owner. Mirror that constraint
// client-side so cross-owner drops never round-trip:
// • Target with no ownerId = own folder / Unfiled. Allow only
// for the user's OWN notes.
// • Target with ownerId = a shared folder we can edit.
// Allow only when the source note also lives in that owner's
// namespace (i.e. came from sharedBucket with the same owner).
// Enforce same-owner rule client-side (server also validates).
if (o.ownerId === undefined) {
if (!isOwn) return;
} else {