Skip to content

Commit 0c9525e

Browse files
committed
wip: commit current role pill progress
1 parent 2fdbd65 commit 0c9525e

File tree

1 file changed

+56
-16
lines changed

1 file changed

+56
-16
lines changed

site/src/pages/UsersPage/UsersTable/UserRoleCell.tsx

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,30 +93,63 @@ export function UserRoleCell({
9393
}: Props) {
9494
const theme = useTheme();
9595

96+
// Unless the user happens to be an owner, it is physically impossible for
97+
// React to know how many pills should be omitted for space reasons each time
98+
// a new set of roles comes in. Have to do a smoke-and-mirrors routine to help
99+
// mask that and avoid UI flickering
96100
const cellRef = useRef<HTMLDivElement>(null);
97101
const pillContainerRef = useRef<HTMLDivElement>(null);
98102
const overflowButtonRef = useRef<HTMLButtonElement>(null);
99103

100-
// Unless the user happens to be an owner, it is physically impossible for
101-
// React to know how many pills should be omitted for space reasons on the
102-
// first render, because that info comes from real DOM nodes, which can't
103-
// exist until the first render pass. Have to do a smoke-and-mirrors routine
104-
// to help mask that and avoid UI flickering
104+
/**
105+
* @todo – The logic only works properly on the first render - the
106+
* moment you update a user's permissions, the UI doesn't do anything, even
107+
* with the gnarly manual state sync in place. The cached update is
108+
* triggering, but not the
109+
*
110+
* Likely causes:
111+
* 1. Mutation logic isn't getting applied properly
112+
* 2. Trace through the parent component logic and see if I need to update
113+
* things to reference the roles prop instead of user.roles
114+
*/
115+
105116
const roleDisplayInfo = getRoleDisplayInfo(user.roles);
117+
118+
// Have to do manual state syncs to make sure that cells change as roles get
119+
// updated; there isn't a good render key to use to simplify this, and the
120+
// MutationObserver API doesn't work with React's order of operations well
121+
// enough to avoid flickering - it's just not fast enough in the right ways
122+
const [cachedUser, setCachedUser] = useState(user);
106123
const [rolesToTruncate, setRolesToTruncate] = useState(
107124
roleDisplayInfo.hasOwner
108125
? user.roles.length - roleDisplayInfo.roles.length
109126
: null,
110127
);
111128

129+
if (user !== cachedUser) {
130+
const needTruncationSync =
131+
!roleDisplayInfo.hasOwner &&
132+
(user.roles.length !== cachedUser.roles.length ||
133+
user.roles.every((role, index) => role === cachedUser.roles[index]));
134+
135+
setCachedUser(user);
136+
console.log("huh");
137+
138+
// This isn't ever triggering, even if you update the permissions for a
139+
// user
140+
if (needTruncationSync) {
141+
setRolesToTruncate(null);
142+
}
143+
}
144+
112145
// Mutates the contents of the pill container to hide overflowing content on
113146
// the first render, and then updates rolesToTruncate so that these overflow
114147
// calculations can be done with 100% pure state/props calculations for all
115-
// re-renders
148+
// re-renders (at least until the roles list changes by content again)
116149
useLayoutEffect(() => {
117150
const cell = cellRef.current;
118151
const pillContainer = pillContainerRef.current;
119-
if (roleDisplayInfo.hasOwner || cell === null || pillContainer === null) {
152+
if (rolesToTruncate !== null || cell === null || pillContainer === null) {
120153
return;
121154
}
122155

@@ -140,7 +173,7 @@ export function UserRoleCell({
140173
const mutationText = getOverflowButtonText(nodesRemoved);
141174
overflowButtonRef.current.innerText = mutationText;
142175
}
143-
}, [roleDisplayInfo.hasOwner]);
176+
}, [rolesToTruncate]);
144177

145178
const finalRoleList = roleDisplayInfo.roles;
146179

@@ -169,29 +202,36 @@ export function UserRoleCell({
169202
<Stack direction="row" spacing={1} ref={pillContainerRef}>
170203
{finalRoleList.map((role) => {
171204
const isOwnerRole = role.name === "owner";
172-
const { palette } = theme;
173205

174206
return (
175207
<Pill
176208
key={role.name}
177209
text={role.display_name}
178210
css={{
179211
backgroundColor: isOwnerRole
180-
? palette.info.dark
181-
: palette.background.paperLight,
212+
? theme.palette.info.dark
213+
: theme.palette.background.paperLight,
182214
borderColor: isOwnerRole
183-
? palette.info.light
184-
: palette.divider,
215+
? theme.palette.info.light
216+
: theme.palette.divider,
185217
}}
186218
/>
187219
);
188220
})}
189221
</Stack>
190222

223+
{/*
224+
* Have to render this, even when rolesToTruncate is null, in order
225+
* for the layoutEffect trick to work properly
226+
*/}
191227
{rolesToTruncate !== 0 && (
192-
<button ref={overflowButtonRef}>
193-
{getOverflowButtonText(rolesToTruncate ?? 0)}
194-
</button>
228+
<Pill
229+
text={getOverflowButtonText(rolesToTruncate ?? 0)}
230+
css={{
231+
backgroundColor: theme.palette.background.paperLight,
232+
borderColor: theme.palette.divider,
233+
}}
234+
/>
195235
)}
196236
</Stack>
197237
</Stack>

0 commit comments

Comments
 (0)