Skip to content

feat: manage blocked and muted accounts, and drop the settings entries that never worked - #2073

Merged
karlitschek merged 3 commits into
masterfrom
feat/block-management
Sep 8, 2026
Merged

feat: manage blocked and muted accounts, and drop the settings entries that never worked#2073
karlitschek merged 3 commits into
masterfrom
feat/block-management

Conversation

@karlitschek

@karlitschek karlitschek commented Sep 8, 2026

Copy link
Copy Markdown
Member

Blocking and muting could only be done from an account's profile, and there was no way to see what you had blocked. GET /api/v1/blocks and GET /api/v1/mutes answered correctly, but nothing in the web client ever called them — so undoing a block meant remembering who it was and finding their profile again, or reaching for a third-party client.

What it adds

Settings → Blocked and muted accounts in the app's left sidebar (the settings section at the bottom, next to "Reset local cache"), listing both sets with unblock/unmute inline, each account linking to its profile, and a separate empty state per list.

Two details worth noting:

  • Unblocking dispatches the same store action the profile page uses (unblockAccount/unmuteAccount), so the relationship stays in step wherever it is displayed rather than going stale behind a second code path.
  • A row disappears only after the server confirms. The store action reports its own failure and resolves undefined; the view leaves the row in place then, so the UI never claims an account was unblocked when it wasn't.

A pre-existing bug fixed along the way

The paths owned by the client-side router had no server routes, so reloading or bookmarking them was a 404 — already true of /follow_requests before this change, and it would have been true of the new page. Both now render the app shell.

Their route definitions carry a postfix, because Nextcloud keys routes by name: re-using Navigation#navigate without one silently drops every declaration but the last. I hit exactly that — the first attempt left /blocked working and /follow_requests still 404ing, which is also how the duplicate would have clobbered /.

Also: the two settings entries that never worked are gone

  • "Reset local cache" posted to apps/social/api/v1/cache/refresha route that does not exist and never has. The request 404s, nothing catches the rejection, so the button reported nothing and did nothing. git log -S dates the call to the v0.9.3 design refresh, added without a backend to talk to. (What it was meant to do already exists as occ social:cache:refresh; wiring the button up is a separate question, so this just removes the pretence.)
  • "Help & documentation" linked to https://github.com/SchBenedikt/social/, a personal fork rather than any documentation of this app.

Their handler in App.vue, the reset-cache event, and the now-unused icon imports go with them, and the tests that pinned both entries are replaced by one asserting they are absent. The settings section is left with the single entry that does something.

Tests

10 view specs (both lists fetched from the endpoints, display-name fallback, profile links, per-list empty states, unblock and unmute dispatching through the store and clearing the row, a refusal keeping the row and re-enabling the button, a load failure reported without a stuck spinner, a non-list answer surviving), plus the settings entry in the navigation spec and the new route in the router spec. Suites: 1839 PHP, 626 vitest, php-cs/eslint/stylelint clean.

Verified on devel

block + mute a remote account   → blocking: true, muting: true
GET /api/v1/blocks              → [(259, spammer@spam.example, "spammer")]
GET /api/v1/mutes               → [(259, spammer@spam.example)]
unblock + unmute (string id, exactly as the view sends it) → false, false; both lists empty
/apps/social/ , /timeline/home , /follow_requests , /blocked → all 401 (page exists), none 404

🤖 Generated with Claude Code

Frank Karlitschek and others added 2 commits September 8, 2026 15:53
Blocking and muting could only be done from an account's profile, and
there was no way to see what you had blocked: `/api/v1/blocks` and
`/api/v1/mutes` answered correctly but nothing in the web client ever
called them, so undoing a block meant remembering who it was.

Settings → Blocked and muted accounts now lists both, with unblock and
unmute inline. Unblocking goes through the store action the profile page
uses, so a relationship stays in step wherever it is shown, and a row
only disappears once the server has confirmed — a refusal leaves it
where it was rather than claiming success.

The page paths of the client-side router also needed server routes:
/blocked, and /follow_requests which had the same defect already —
reloading or bookmarking either was a 404. They both render the app
shell now. The route names carry a postfix because routes are keyed by
name, and re-using one silently drops every declaration but the last.

Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
'Reset local cache' posted to /api/v1/cache/refresh, a route that does
not exist and never has: the request 404s, nothing catches it, so the
button reported nothing and did nothing. It arrived with the v0.9.3
design refresh without a backend to talk to.

'Help & documentation' pointed at a personal fork rather than any
documentation of this app.

That leaves the settings section with the one entry that does something.

Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
@karlitschek karlitschek changed the title feat: manage blocked and muted accounts from the settings menu feat: manage blocked and muted accounts, and drop the settings entries that never worked Sep 8, 2026
The app runs on Vue 3.5 but carried Vue 2 idioms that the compiler
accepts and then ignores, so each one silently did nothing:

- The timeline's fade was dead. Both <transition-group>s use the `list`
  transition, whose starting class was `.list-enter` — Vue 2's name.
  Vue 3 applies `.list-enter-from`, which nothing defined, so posts
  appeared abruptly. Corrected, given the `.list-move` that makes the
  neighbours slide instead of jump when an entry arrives, and defined
  once in the global block rather than duplicated in a scoped one.
- The welcome banner animated out but never in: `slide-fade` had no
  enter classes at all.
- The report dialog could not close itself. `:open.sync` is Vue 2; Vue 3
  ignores the modifier, so the binding was one-way and the dialog's own
  dismissal never reached the flag. Now `v-model:open`.
- eslint was linting a Vue 3 app against the Vue 2 ruleset. The shared
  config ships a `vue3` sub-config for exactly this; switching to it
  rejected nothing valid and immediately found four unused bindings,
  including an unused linkifyjs namespace import.
- Five components emitted events without declaring `emits`.
- Motion now respects prefers-reduced-motion everywhere, which nothing
  did before.
- Deleted the popoverMenu mixin, unused since NcActions replaced
  hand-rolled menus, and the App-level composer path that Navigation
  stopped emitting when it grew its own modal.

tests/js/vue3.test.js asserts all of it against the source, because none
of these fail loudly: it reports the file and line of a Vue 2 transition
class, a `.sync` binding, a removed lifecycle hook or API, an undeclared
emit, or an animation without a motion guard.

Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
@karlitschek
karlitschek merged commit aa9cd08 into master Sep 8, 2026
32 of 37 checks passed
@karlitschek
karlitschek deleted the feat/block-management branch September 8, 2026 14:51
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