feat: manage blocked and muted accounts, and drop the settings entries that never worked - #2073
Merged
Conversation
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>
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>
This was referenced Sep 8, 2026
This was referenced Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/blocksandGET /api/v1/mutesanswered 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:
unblockAccount/unmuteAccount), so the relationship stays in step wherever it is displayed rather than going stale behind a second code path.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_requestsbefore 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-usingNavigation#navigatewithout one silently drops every declaration but the last. I hit exactly that — the first attempt left/blockedworking and/follow_requestsstill 404ing, which is also how the duplicate would have clobbered/.Also: the two settings entries that never worked are gone
apps/social/api/v1/cache/refresh— a 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 -Sdates the call to the v0.9.3 design refresh, added without a backend to talk to. (What it was meant to do already exists asocc social:cache:refresh; wiring the button up is a separate question, so this just removes the pretence.)https://github.com/SchBenedikt/social/, a personal fork rather than any documentation of this app.Their handler in
App.vue, thereset-cacheevent, 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
🤖 Generated with Claude Code