Skip to content

feat(stream): add an actions menu to each activity entry - #2838

Open
karlitschek wants to merge 2 commits into
masterfrom
feat/entry-actions
Open

feat(stream): add an actions menu to each activity entry#2838
karlitschek wants to merge 2 commits into
masterfrom
feat/entry-actions

Conversation

@karlitschek

Copy link
Copy Markdown
Member

Activity entries were read-only: seeing that a file changed gave no way to act on it without leaving for the Files app and finding it again. Only third parties could contribute anything interactive, through registerSidebarAction(), and that applies to the panel as a whole rather than to a single entry.

Each entry now carries an actions menu offering, where applicable:

  • View, opening the file in the Viewer overlay without leaving the stream
  • Show in Files, revealing the file inside its folder
  • Copy link, always putting an absolute URL on the clipboard so it stays usable elsewhere

Links deliberately do not reuse the host from the activity's own link. Providers build absolute URLs out of the server's configuration rather than the current request, so an activity recorded by a background job carries overwrite.cli.url — routinely http://localhost:8080, which no browser visiting the instance can reach. Only the path is trustworthy. Where a file id is known the link is rebuilt from scratch as /f/, which also addresses the file itself rather than the containing directory a provider link often points at, and lets the server redirect to whichever view holds it. Otherwise the provider's path is kept and re-anchored onto the origin the session is actually using.

Show in Files reveals rather than opens. /f/<fileid> opens the file in the Viewer on arrival, because the server sets openfile=true for anything that is not a folder, so the link asks for openfile=false explicitly. Opening is what the View action is for, and one menu entry doing both is merely surprising. The copied link is left at the server's default instead: whoever receives it wants the file, not this menu's idea of how to look at the folder around it.

Actions appear only when they can do something. View needs a MIME type the Viewer advertises, which is only known where previews were requested, so it is absent in the sidebar. An activity with no file behind it renders no menu at all rather than an empty one.

The menu is faded out until its row is hovered, and only on devices with a pointer, so a touch user is never asked to hover to discover it. Fading rather than hiding keeps it reachable by keyboard, and the row owns the hover rule because the row owns its layout. GenericActivity already had an unused &__actions style block, which now has markup to match.

Clipboard failures are reported rather than swallowed, including the insecure-origin case where navigator.clipboard does not exist at all.

The test config gains @nextcloud/dialogs in vitest's list of inlined dependencies. It ships CSS imports that Node cannot load, which previously went unnoticed because every test touching that module mocked it; pulling it into the entry components made three unrelated suites fail to collect.

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Activity entries were read-only: seeing that a file changed gave no way to
act on it without leaving for the Files app and finding it again. Only
third parties could contribute anything interactive, through
registerSidebarAction(), and that applies to the panel as a whole rather
than to a single entry.

Each entry now carries an actions menu offering, where applicable:

- View, opening the file in the Viewer overlay without leaving the stream
- Show in Files, revealing the file inside its folder
- Copy link, always putting an absolute URL on the clipboard so it stays
  usable elsewhere

Links deliberately do not reuse the host from the activity's own link.
Providers build absolute URLs out of the server's configuration rather than
the current request, so an activity recorded by a background job carries
overwrite.cli.url — routinely http://localhost:8080, which no browser
visiting the instance can reach. Only the path is trustworthy. Where a file
id is known the link is rebuilt from scratch as /f/<fileid>, which also
addresses the file itself rather than the containing directory a provider
link often points at, and lets the server redirect to whichever view holds
it. Otherwise the provider's path is kept and re-anchored onto the origin
the session is actually using.

Show in Files reveals rather than opens. `/f/<fileid>` opens the file in the
Viewer on arrival, because the server sets `openfile=true` for anything that
is not a folder, so the link asks for `openfile=false` explicitly. Opening
is what the View action is for, and one menu entry doing both is merely
surprising. The copied link is left at the server's default instead:
whoever receives it wants the file, not this menu's idea of how to look at
the folder around it.

Actions appear only when they can do something. View needs a MIME type the
Viewer advertises, which is only known where previews were requested, so it
is absent in the sidebar. An activity with no file behind it renders no menu
at all rather than an empty one.

The menu is faded out until its row is hovered, and only on devices with a
pointer, so a touch user is never asked to hover to discover it. Fading
rather than hiding keeps it reachable by keyboard, and the row owns the
hover rule because the row owns its layout. GenericActivity already had an
unused `&__actions` style block, which now has markup to match.

Clipboard failures are reported rather than swallowed, including the
insecure-origin case where navigator.clipboard does not exist at all.

The test config gains @nextcloud/dialogs in vitest's list of inlined
dependencies. It ships CSS imports that Node cannot load, which previously
went unnoticed because every test touching that module mocked it; pulling
it into the entry components made three unrelated suites fail to collect.

Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@karlitschek
karlitschek requested a review from miaulalala August 1, 2026 16:25
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.59259% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/components/ActivityEntryActions.vue 92.45% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

The actions menu sat once per row, at its end. That is the wrong scope: an
activity can carry several files — a bulk upload arrives as one grouped
entry with a thumbnail each — so a single menu per row could only ever act
on one of them, silently picking the first preview it found. It also put the
menu a long way from the thing it acted on.

Each thumbnail now carries its own menu, overlaid on its top corner, and the
actions are scoped to that file: View uses that preview's own MIME type and
path, Show in Files resolves that preview's file id, and Copy link copies
that file. Each menu is named after its file, so several on one row are
distinguishable rather than three identical "Actions for this activity".

The overlay is absolutely positioned so adding it changes neither the size
nor the spacing of the thumbnail grid, and the toggle carries its own surface
because a thumbnail can be any colour and there is nothing to rely on behind
it. It stays hidden until its thumbnail is hovered, faded rather than removed
so it remains keyboard reachable, and only where a real pointer exists.

The preview item needed `display: flex` and `line-height: 0`: the link inside
is inline, so the item's box was taller than the image it wraps and the
overlay anchored above the thumbnail's real top edge.

Comment entries lose their menu, since they render no thumbnails to hang one
on, and so do entries in the Files sidebar, which loads without previews.
Both follow from attaching the actions to files rather than to rows.

Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant