Page MenuHomePhabricator

Manual photo groups in table view (persistent, stacked-tables layout) (!20)
Closed, ResolvedPublic

Description

When uploading a batch, users often have natural sub-batches (e.g. 5 photos of the same church inside a 20-photo upload). Editing them together as a sub-batch — and visually keeping them apart from the rest — is currently impossible. Add manual groups: a user selects rows, clicks a "Group selection" button (alongside Publish / Discard in the bulk-action area), and those rows become a group. The table can switch between "All" view and "Groups" view.

Group rules:

  • Groups are nameless (no naming UI).
  • A photo can be in at most one group.
  • Photos not in any group sit in an implicit "Ungrouped" section.
  • Groups are stored on the wiki user-store (cross-device, persistent).

Group-view layout:

  • Multiple mini-tables stacked vertically, one per group, with a thick separator between them.
  • Column headers repeat at the top of each group's mini-table.
  • Row order / row sorting is per-group — clicking a column header to sort sorts only within that group.
  • Column order and column visibility are global — set once, applied across all groups.
  • Group order itself: drag-and-drop the group blocks to reorder them.

Group management:

  • Create: select rows in either view → "Group selection" button (next to Publish / Discard).
  • Remove a row from a group: row goes back to "Ungrouped".
  • Delete a group: a control on the group header (e.g. context menu / × button); members go back to "Ungrouped".

Out of scope:

  • Group names / labels.
  • A photo being in multiple groups simultaneously.
  • Visual piling creation mode (separate task).
  • Per-column filtering (deferred).

Acceptance:

  • Toggle in table toolbar switches between "All" and "Groups" views.
  • Selecting rows + "Group selection" creates a group; rows render as a stacked mini-table in Groups view.
  • Sorting a column in one group does not affect other groups.
  • Reordering or hiding columns affects all groups identically.
  • Drag-reorder of groups persists.
  • Reload (and reload on a different device) preserves groups.

Source: User feedback session, 2026-05-08.

Event Timeline

Daanvr triaged this task as Medium priority.
Daanvr lowered the priority of this task from Medium to Low.May 9 2026, 6:57 AM

Delete group creates a anxiety for the user because it's unclear if it create it deletes also the content of the group, should but only uh Um it should be clear to the user that only the group is removed and files will be remained. Don't add this in text to this in the user interface. Design to explain this. Um or on a tooltip on hover. Maybe by renaming the text this can be clarified but don't add like a sentence to explain in the user interface default without hover or something.

Just to be sure and to check every file can only be in one group So selecting files from a certain group to make a new group will create a new group Like a fresh group where the selected files are in and they are removed from the previous group

Also, I would like to be able to click on the group name, which is now by default group 1, group 2, group 3, and rename them, the group. Actually, so the default name is just group and the number, but if you rename them they will keep the order in the groups like a the the the specific number of of the group but they will have a label to allow the user to identify them easier Group numbering should be based on when they are created and not based on their order because now it's if I Move group two and drag and drop it to group one. It will change their names. This is unexpected behavior. Unclear for the user

The delete group button should actually say ungroup.

So actually when selecting a few items or a few files in a group, you can group selection next to publish and discard, but there should also be ungroup selection. and uh the button delete group should actually be called ungroup all

Daanvr renamed this task from Manual photo groups in table view (persistent, stacked-tables layout) to Manual photo groups in table view (persistent, stacked-tables layout) (!20).May 11 2026, 2:12 PM

Iterated per feedback (rebased onto current main, which now includes T425833 focus mode + T425828 frozen columns):

  • Per-group Delete group button renamed to Ungroup all and recolored from destructive red to a neutral subtle button. Tooltip on hover: "dissolves this group; the files themselves stay in the workbench". No inline UI copy added; the wording carries the meaning.
  • New paired bulk action Ungroup selection sits next to Group selection in the bulk drawer. Disabled when no selected row is in a group.
  • Group labels now use a stable creation-order seq stored on the group. Default label Group {seq} does not change when groups are dragged. Legacy groups without seq get back-filled by their array position on next load.
  • Group label is now click-to-rename; clearing the input restores the default label by stripping the name field. Custom names persist via the user-store.
  • Re-confirmed selecting rows from group A + Group selection moves them out of A into a fresh group (already correct in the prior commit; the spec comment is now explicit).

Preview updated: https://upload-workbench.toolforge.org/mr-20/

perfect. only wird thing is i have a empty groep. that should not be posible. a groep that is empty should be removed automaticaly

Iterated per feedback (rebased onto current main, which now includes T425825/T425836/T425874/T425876 — v0.8.0):

  • Empty groups are no longer reachable. Added a useEffect in App that watches groups × stashItems (the live, non-hidden stash) and prunes any group whose stored sha1s/filekeys no longer match anything in the workbench. So when the last live member of a group leaves the stash for any reason — published, soft-deleted (Discard), or expired from the 48h stash — the group itself is removed automatically.
  • Filter-driven emptiness is deliberately preserved: if every member of a group is currently hidden by your search/filter, the group still renders (with its header so you can drag/rename/ungroup) and the placeholder copy was reworded to make that explicit ("All rows in this group are hidden by your current filter. Clear the filter to see them."). Reaching that placeholder now unambiguously means "filter narrowed your view", not "orphan group from a stale state".
  • Doubles as a load-time safety net for groups inherited from prior sessions whose files have since been published/expired/discarded from another device.

Preview updated: https://upload-workbench.toolforge.org/mr-20/

Clicking the header of the toggle column should select only all the files in that specific group. All the interactions on the column header should only apply to that specific group. Also when clearing uh values, also when applying defaults.

Iterated per feedback (round 3): the master select-all checkbox in each mini-table's header is now scoped to that table's rows. Implementation: it now calls onSetSelection(items, !allSelected) using the table's local sorted items, so toggling group A selects/deselects only group A's rows and leaves selections in sibling groups (and the implicit Ungrouped section) untouched. Single-table view (the All toggle, history table) is unchanged because its scope already *is* the full filtered list. Title attribute reads "Select / Deselect all rows here" for clarity in both modes.

The other column-header interactions called out in the feedback — clearing values, applying defaults — already operate per-group via the table-local items prop: the Templates and columns modal's "Fill empty cells" / "Overwrite selected" / "Overwrite all" each iterate the items of whichever mini-table you opened the modal from, and per-column sort/resize/auto-fit are per-table because each Table instance keeps its own state. So no further code change there; the changelog entry calls it out so reviewers don't have to chase it down. Also rebased onto current main (now v0.12.1, includes the orphaned-sort-deps hotfix and the templates work).

Drive-by: dropped the now-unused onSelectAll / onClearAll Table props (and the selectAllStash helper in App). The history table's previous select-all wiped global selection too, including stash; the new scoped behaviour is also a quiet UX improvement there.

Preview updated: https://upload-workbench.toolforge.org/mr-20/

Released as v0.23.0 (MR !20 merged into main).