-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Role page various fixes 2 #12416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Role page various fixes 2 #12416
Conversation
Weiko
commented
Jun 2, 2025
- Fix: AvatarURL signedPath for workspace members were not consistent when queried multiple times and it was causing the frontend to wrongly interpret this as a change in the deepEqual condition
- Use SaveAndCancel button to be consistent with data model page
- When applying all object permission changes, a "smarter" logic applies and removes all permissions if read is unchecked for example
- Hide settings permissions when Settings All Access is toggled
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR addresses role management and avatar URL inconsistencies, focusing on workspace member data handling and permissions logic. Here are the key changes:
- Fixed avatar URL signedPath inconsistency by removing server-side signing and using enriched workspace member data from Recoil state
- Implemented smarter permission cascade logic where disabling read access automatically disables other permissions (update, soft delete, destroy)
- Added collapsible settings permissions section that hides when 'Settings All Access' is enabled
- Replaced single Save button with SaveAndCancelButtons component for consistency with data model page
- Improved role assignment workflow by using CurrentWorkspaceMember type instead of SearchRecord for better type safety
13 file(s) reviewed, 3 comment(s)
Edit PR Review Bot Settings | Greptile
if (!isDeeplyEqual(currentDraftRole, role)) { | ||
set(settingsDraftRoleFamilyState(role.id), role); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: This check prevents unnecessary updates but could mask legitimate changes if currentDraftRole contains local modifications that should be overwritten by the server state.
placeholder: workspaceMember?.name.firstName ?? '', | ||
placeholderColorSeed: workspaceMember?.id, | ||
avatarUrl: workspaceMember?.avatarUrl, | ||
}} | ||
text={workspaceMember.label} | ||
text={workspaceMember?.name.firstName ?? ''} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Optional chaining is redundant here since isDefined filter was already applied. Remove the optional chaining operators.
placeholder: workspaceMember?.name.firstName ?? '', | |
placeholderColorSeed: workspaceMember?.id, | |
avatarUrl: workspaceMember?.avatarUrl, | |
}} | |
text={workspaceMember.label} | |
text={workspaceMember?.name.firstName ?? ''} | |
placeholder: workspaceMember.name.firstName ?? '', | |
placeholderColorSeed: workspaceMember.id, | |
avatarUrl: workspaceMember.avatarUrl, | |
}} | |
text={workspaceMember.name.firstName ?? ''} |
...(value === true | ||
? { | ||
canReadAllObjectRecords: value, | ||
} | ||
: {}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: This logic is repeated for multiple permissions. Consider extracting to a shared utility function to maintain DRY principles and ensure consistent behavior
🚀 Preview Environment Ready! Your preview environment is available at: http://bore.pub:46042 This environment will automatically shut down when the PR is closed or after 5 hours. |
@@ -188,17 +188,6 @@ export class RoleResolver { | |||
workspace.id, | |||
); | |||
|
|||
await Promise.all( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was added in the last PR to include avatarUrls to the response but this is what broke the dirty state in the FE due to the sign being different after multiple requests (which could happen but the FE was able to not update the state via a isDeeplyEqual but this function was always returning false due to the avatarUrl containing the different hash). I'm reverting this and now we use the existing currentWorkspaceMembersState state to retrieve the correct avatarURL. I think this issue is not only scoped to permissions though and could happen in other surfaces 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"but this is what broke the dirty state in the FE due to the sign being different"
What is the sign?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you query the API to retrieve an image, we need to sign it so the user can view its content, it looks like this:
http://localhost:3000/files/profile-picture/original/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlbmFtZSI6ImE5MDRhOTk4LTY0YjItNDcx....
which is then verified on the backend when opened to see if the image has been correctly signed (and it has an expiration date).
This encoding sign was added every time we were doing getRoles and is not idempotent (each query was returning a different sign) so the payload was always different even though the role was not changed
set(settingsPersistedRoleFamilyState(role.id), role); | ||
|
||
if (!isDeeplyEqual(currentDraftRole, role)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably redundant actually, the root cause was the avatarUrl thing, I can remove this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe introduced by the form update ? (on a role i m creating)
Enregistrement.de.l.ecran.2025-06-02.a.16.49.16.mov
@@ -62,6 +65,16 @@ export const SettingsRolesTableRow = ({ role }: SettingsRolesTableRowProps) => { | |||
const { getIcon } = useIcons(); | |||
const Icon = getIcon(role.icon ?? 'IconUser'); | |||
|
|||
const currentWorkspaceMembers = useRecoilValue(currentWorkspaceMembersState); | |||
|
|||
const enrichedWorkspaceMembers = role.workspaceMembers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does enrich mean here ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It means the workspaceMember returned by role only contains the id then we "enrich" it through the currentWorkspaceMembersState to get the rest such as the avatarUrl (with a sign in the URL that's not supposed to change since getCurrentUser is only called when we refresh the page)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can change the name if it's not clear!
id: workspaceMemberSearchRecord.recordId, | ||
name: `${workspaceMemberSearchRecord.label}`, | ||
id: workspaceMember.id, | ||
name: `${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these always present (they are nullable in the db)? if not we may be displaying "undefined"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nevermind, they are nullable in the db but not in the schema
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They could be empty strings in theory but not undefined yes, I believe label from the search query is already the concatenation of firstName + lastName so the result should be the same
<AnimatedExpandableContainer | ||
isExpanded={!settingsDraftRole.canUpdateAllSettings} | ||
dimension="height" | ||
animationDurations={{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should these be in a constant ? I suppose we would like all animations to be similar through the app
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is one constant ADVANCED_SETTINGS_ANIMATION_DURATION I believe, I can rename it to make it more generic if we indeed want to it be consistant through the app or create a new one
@@ -188,17 +188,6 @@ export class RoleResolver { | |||
workspace.id, | |||
); | |||
|
|||
await Promise.all( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"but this is what broke the dirty state in the FE due to the sign being different"
What is the sign?
I'm not sure this is new, I'll handle this in another ticket but when creating a role the input (where you see "Role Name" at the top) should be opened and force you to type something otherwise the role name will be empty (even though we display a placeholder "Role Name", actually the placeholder should also have a different color, that's also for the other ticket). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great ! role creation / update form is much clearer now i feel !
- Fix: AvatarURL signedPath for workspace members were not consistent when queried multiple times and it was causing the frontend to wrongly interpret this as a change in the deepEqual condition - Use SaveAndCancel button to be consistent with data model page - When applying all object permission changes, a "smarter" logic applies and removes all permissions if read is unchecked for example - Hide settings permissions when Settings All Access is toggled
- Fix: AvatarURL signedPath for workspace members were not consistent when queried multiple times and it was causing the frontend to wrongly interpret this as a change in the deepEqual condition - Use SaveAndCancel button to be consistent with data model page - When applying all object permission changes, a "smarter" logic applies and removes all permissions if read is unchecked for example - Hide settings permissions when Settings All Access is toggled