@@ -93,30 +93,63 @@ export function UserRoleCell({
93
93
} : Props ) {
94
94
const theme = useTheme ( ) ;
95
95
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
96
100
const cellRef = useRef < HTMLDivElement > ( null ) ;
97
101
const pillContainerRef = useRef < HTMLDivElement > ( null ) ;
98
102
const overflowButtonRef = useRef < HTMLButtonElement > ( null ) ;
99
103
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
+
105
116
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 ) ;
106
123
const [ rolesToTruncate , setRolesToTruncate ] = useState (
107
124
roleDisplayInfo . hasOwner
108
125
? user . roles . length - roleDisplayInfo . roles . length
109
126
: null ,
110
127
) ;
111
128
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
+
112
145
// Mutates the contents of the pill container to hide overflowing content on
113
146
// the first render, and then updates rolesToTruncate so that these overflow
114
147
// 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)
116
149
useLayoutEffect ( ( ) => {
117
150
const cell = cellRef . current ;
118
151
const pillContainer = pillContainerRef . current ;
119
- if ( roleDisplayInfo . hasOwner || cell === null || pillContainer === null ) {
152
+ if ( rolesToTruncate !== null || cell === null || pillContainer === null ) {
120
153
return ;
121
154
}
122
155
@@ -140,7 +173,7 @@ export function UserRoleCell({
140
173
const mutationText = getOverflowButtonText ( nodesRemoved ) ;
141
174
overflowButtonRef . current . innerText = mutationText ;
142
175
}
143
- } , [ roleDisplayInfo . hasOwner ] ) ;
176
+ } , [ rolesToTruncate ] ) ;
144
177
145
178
const finalRoleList = roleDisplayInfo . roles ;
146
179
@@ -169,29 +202,36 @@ export function UserRoleCell({
169
202
< Stack direction = "row" spacing = { 1 } ref = { pillContainerRef } >
170
203
{ finalRoleList . map ( ( role ) => {
171
204
const isOwnerRole = role . name === "owner" ;
172
- const { palette } = theme ;
173
205
174
206
return (
175
207
< Pill
176
208
key = { role . name }
177
209
text = { role . display_name }
178
210
css = { {
179
211
backgroundColor : isOwnerRole
180
- ? palette . info . dark
181
- : palette . background . paperLight ,
212
+ ? theme . palette . info . dark
213
+ : theme . palette . background . paperLight ,
182
214
borderColor : isOwnerRole
183
- ? palette . info . light
184
- : palette . divider ,
215
+ ? theme . palette . info . light
216
+ : theme . palette . divider ,
185
217
} }
186
218
/>
187
219
) ;
188
220
} ) }
189
221
</ Stack >
190
222
223
+ { /*
224
+ * Have to render this, even when rolesToTruncate is null, in order
225
+ * for the layoutEffect trick to work properly
226
+ */ }
191
227
{ 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
+ />
195
235
) }
196
236
</ Stack >
197
237
</ Stack >
0 commit comments