fix(apps): action button role filter never matching room scoped roles

Merged
D-
2mo ago
+120-5

Proposed changes (including videos or screenshots)

An app action button filtered by when.hasOneRole / when.hasAllRoles never matched a role scoped to Subscriptions — the built-in owner, moderator and leader, or any custom role created with that scope.

useApplyButtonFilters resolves the room it is rendered in, but called the auth filter without it:

applyAuthFilter(button) && (!room || applyRoomFilter(button, room)) && ...

useApplyButtonAuthFilter's inner callback did accept a room, but the hook's declared return type was (button: IUIActionButton) => boolean, so the parameter was invisible to callers and the compiler never flagged the omission. With no scope reaching it, hasRole takes the early return for a subscription-scoped role (createAuthorizationFunctions.ts:41-44):

case 'Subscriptions':
    if (!scope) return false;

So the button stayed hidden even for a user who did hold the role in that room.

This forwards the room as the scope and puts it on the auth filter's public type, so a caller can no longer drop it silently. A role scoped to Users ignores the scope argument, so workspace-wide filters (admin, user, custom Users roles) behave exactly as before — the change only makes the previously unreachable per-room grants reachable. The four room-bound surfaces (room toolbox, room header AI actions, message actions, message box) all go through useApplyButtonFilters and are fixed by this; the user dropdown has no room of its own and correctly keeps passing none.

withRoleScoped(role, scope) is added to MockedAppRootBuilder because withRole grants workspace-wide (it pushes into the user's roles and matches any scope) and cannot express a per-room grant.

Issue(s)

Found while reviewing #41765, which touches the same filter but does not fix this.

Steps to test or reproduce

  1. Install an app whose action button declares when: { hasOneRole: ['owner'] } in a room context (roomAction, messageAction, or messageBoxAction).
  2. Open a room you own.
  3. Before: the button never appears, for anyone. After: it appears for the room owner and stays hidden for other members.

Also covered by unit tests: the auth filter with the room, without it, and with the role held in a different room, plus useApplyButtonFilters inside a room context. The room-context test fails on develop and passes with this change.

Further comments

The permission half of the same filter has the same gap — queryAllPermissions(hasAllPermissions) and queryAtLeastOnePermission(hasOnePermission) are also called with no scope, so a button gated on a permission that is granted through owner/moderator (delete-message, say) stays hidden for a room owner too. Threading the room there is now a one-token change, but it widens what hasAllPermissions means for app authors from "workspace-wide" to "here", so it is left out of this fix deliberately. Happy to add it if that is the semantics we want.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed action buttons so room-scoped roles are correctly recognized within their assigned rooms.
    • Ensured buttons for owners, moderators, leaders, and custom room roles appear only when the relevant room context matches.
    • Preserved support for workspace-wide roles and roomless surfaces.
  • Tests

    • Added coverage for matching and non-matching room-scoped roles and room-aware button filtering.