@@ -14,9 +14,13 @@ import { Spinner } from "@/components/ui/spinner";
1414import { useGuaranteedHydrateComponentReference } from "@/hooks/useHydrateComponentReference" ;
1515import { cn } from "@/lib/utils" ;
1616import { useComponentLibrary } from "@/providers/ComponentLibraryProvider" ;
17- import { isFavoriteComponent } from "@/providers/ComponentLibraryProvider/componentLibrary" ;
17+ import {
18+ flattenFolders ,
19+ isFavoriteComponent ,
20+ } from "@/providers/ComponentLibraryProvider/componentLibrary" ;
1821import { hydrateComponentReference } from "@/services/componentService" ;
19- import type { ComponentReference } from "@/utils/componentSpec" ;
22+ import { type ComponentReference } from "@/utils/componentSpec" ;
23+ import { MINUTES } from "@/utils/constants" ;
2024import { getComponentName } from "@/utils/getComponentName" ;
2125
2226import { withSuspenseWrapper } from "./SuspenseWrapper" ;
@@ -132,41 +136,64 @@ const FavoriteToggleButton = withSuspenseWrapper(
132136 ) ,
133137) ;
134138
135- export const ComponentFavoriteToggle = ( {
139+ const useComponentFlags = ( component : ComponentReference ) => {
140+ const { checkIfUserComponent, componentLibrary } = useComponentLibrary ( ) ;
141+
142+ const isUserComponent = useMemo (
143+ ( ) => checkIfUserComponent ( component ) ,
144+ [ checkIfUserComponent , component ] ,
145+ ) ;
146+
147+ const flatComponentList = useMemo (
148+ ( ) => ( componentLibrary ? flattenFolders ( componentLibrary ) : [ ] ) ,
149+ [ componentLibrary ] ,
150+ ) ;
151+
152+ const { data : isInLibrary } = useSuspenseQuery ( {
153+ queryKey : [ "component" , "flags" , component . digest ] ,
154+ queryFn : async ( ) => {
155+ if ( ! componentLibrary ) return false ;
156+
157+ for ( const c of flatComponentList ) {
158+ if ( c . name && c . name !== component . name ) {
159+ // micro optimization to skip components with different names
160+ continue ;
161+ }
162+
163+ const hydratedComponent = await hydrateComponentReference ( c ) ;
164+ const digest = c . digest ?? hydratedComponent ?. digest ;
165+
166+ if ( digest === component . digest ) {
167+ return true ;
168+ }
169+ }
170+
171+ return false ;
172+ } ,
173+ staleTime : 10 * MINUTES ,
174+ } ) ;
175+
176+ return { isInLibrary, isUserComponent } ;
177+ } ;
178+
179+ const ComponentFavoriteToggleInternal = ( {
136180 component,
137181 hideDelete = false ,
138182} : ComponentFavoriteToggleProps ) => {
139- const {
140- addToComponentLibrary,
141- removeFromComponentLibrary,
142- checkIfUserComponent,
143- checkLibraryContainsComponent,
144- } = useComponentLibrary ( ) ;
183+ const { addToComponentLibrary, removeFromComponentLibrary } =
184+ useComponentLibrary ( ) ;
145185
146186 const [ isOpen , setIsOpen ] = useState ( false ) ;
147187
148188 const { spec, url } = component ;
149189
150- const isUserComponent = useMemo (
151- ( ) => checkIfUserComponent ( component ) ,
152- [ component , checkIfUserComponent ] ,
153- ) ;
154-
155- const isInLibrary = useMemo (
156- ( ) => checkLibraryContainsComponent ( component ) ,
157- [ component , checkLibraryContainsComponent ] ,
158- ) ;
190+ const { isInLibrary, isUserComponent } = useComponentFlags ( component ) ;
159191
160192 const displayName = useMemo (
161193 ( ) => getComponentName ( { spec, url } ) ,
162194 [ spec , url ] ,
163195 ) ;
164196
165- // Delete User Components
166- const handleDelete = useCallback ( async ( ) => {
167- removeFromComponentLibrary ( component ) ;
168- } , [ removeFromComponentLibrary ] ) ;
169-
170197 /* Confirmation Dialog handlers */
171198 const openConfirmationDialog = useCallback ( ( ) => {
172199 setIsOpen ( true ) ;
@@ -180,7 +207,7 @@ export const ComponentFavoriteToggle = ({
180207 const handleConfirm = useCallback ( async ( ) => {
181208 setIsOpen ( false ) ;
182209
183- if ( ! isInLibrary ) {
210+ if ( ! isUserComponent ) {
184211 const hydratedComponent = await hydrateComponentReference ( component ) ;
185212
186213 if ( ! hydratedComponent ) {
@@ -189,42 +216,56 @@ export const ComponentFavoriteToggle = ({
189216 return ;
190217 }
191218
192- addToComponentLibrary ( hydratedComponent ) ;
193- return ;
219+ await addToComponentLibrary ( hydratedComponent ) ;
220+ } else {
221+ await removeFromComponentLibrary ( component ) ;
194222 }
223+ } , [
224+ component ,
225+ isUserComponent ,
226+ addToComponentLibrary ,
227+ removeFromComponentLibrary ,
228+ ] ) ;
195229
196- handleDelete ( ) ;
197- } , [ component , isInLibrary , addToComponentLibrary , handleDelete ] ) ;
198-
199- const showDeleteButton = isInLibrary && isUserComponent && ! hideDelete ;
230+ const showDeleteButton = ! isInLibrary && isUserComponent && ! hideDelete ;
200231
201232 return (
202233 < >
203- { ! isInLibrary && < AddToLibraryButton onClick = { openConfirmationDialog } /> }
204-
205- { isInLibrary && ! isUserComponent && (
206- < FavoriteToggleButton component = { component } />
234+ { ! isInLibrary && ! isUserComponent && (
235+ < AddToLibraryButton onClick = { openConfirmationDialog } />
207236 ) }
208237
238+ { isInLibrary && < FavoriteToggleButton component = { component } /> }
239+
209240 { showDeleteButton && (
210241 < DeleteFromLibraryButton onClick = { openConfirmationDialog } />
211242 ) }
212243
213244 < ConfirmationDialog
214245 isOpen = { isOpen }
215246 title = {
216- ! isInLibrary
247+ ! isUserComponent
217248 ? "Add to Component Library?"
218249 : "Delete custom component?"
219250 }
220251 description = {
221- ! isInLibrary
252+ ! isUserComponent
222253 ? `This will add "${ displayName } " to your Component Library for use in your pipelines.`
223- : `"${ displayName } " is a custom user component. Unstarring it will remove it from your library. This action cannot be undone.`
254+ : `"${ displayName } " is a custom user component. This will remove it from your library. This action cannot be undone.`
224255 }
225256 onConfirm = { handleConfirm }
226257 onCancel = { handleCancel }
227258 />
228259 </ >
229260 ) ;
230261} ;
262+
263+ export const ComponentFavoriteToggle = withSuspenseWrapper (
264+ ComponentFavoriteToggleInternal ,
265+ ( ) => < Spinner size = { 10 } /> ,
266+ ( ) => (
267+ < IconStateButton disabled >
268+ < Icon name = "Star" />
269+ </ IconStateButton >
270+ ) ,
271+ ) ;
0 commit comments