Skip to content

fix(desktop): make user delete work, refetch every panel on a project change - #94

Merged
pyramation merged 1 commit into
mainfrom
feat/user-delete-and-refetch
Aug 9, 2026
Merged

fix(desktop): make user delete work, refetch every panel on a project change#94
pyramation merged 1 commit into
mainfrom
feat/user-delete-and-refetch

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Delete did nothing because the button was disabled. The Users tab blocked removal for the only admin — which, in a one-user project, is the only row there is. The store's rule is narrower: losing the last admin is only a lock-out while other users remain; removing the very last account is allowed (it just disables UI logins, and is undone by adding a user). The two rules now come from one place:

userRemovalBlock(users, target) // null = allowed, otherwise the reason (shown as the button's title)
  target.role !== 'admin'       null
  users.length <= 1             null           // ← the case the UI got wrong
  admins <= 1                   'The last admin cannot be removed while other users exist.'

roleChangeBlock(users, target, next)  // demoting the only admin stays blocked, always

And a refused write is no longer invisible: every runAction(...) failure lands in a dismissible banner, where before void withBusy(...) turned a store rejection into an unhandled promise rejection and a button that looked dead.

Panels kept mount-time data. Each project-scoped hook refetched on the project name, so nothing refetched when the contents changed underneath the same name — switching the project in use, importing over a project, clear-all:

-useProjectUsers(project: string | null)
+useProjectUsers({ project, rev }: ProjectScope)
   const refresh = useCallback(…, [project, rev]);

App holds one dataRev and hands out activeScope / editingScope, bumping it on use / create / remove / import / clear. That's the general version of the fix in #92, which only cleared the pinned config project and restarted the brain.

Link to Devin session: https://app.devin.ai/sessions/972698f89f494b86828010666a002b8f
Requested by: @pyramation

Deleting a user did nothing in the most common case: the trash button was
disabled for the only admin, which in a one-user project is the only row
there is. The store allows it (removing the last account just disables UI
logins) — only losing the last admin *while other users remain* is a
lock-out. The rule now lives in one place both the store and the UI agree
on, and rejected store writes are shown instead of being swallowed by an
unhandled promise rejection.

Panels also refetched on the project *name* only, so switching the project
in use — or importing over it, or clearing the store — could leave a panel
showing what it fetched on mount. Every project-scoped hook now takes a
ProjectScope { project, rev }, and rev is bumped whenever the store
changes underneath the panels.
@pyramation pyramation self-assigned this Aug 9, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 82605d7 into main Aug 9, 2026
5 checks passed
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