github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/StarredResourcesContext.tsx (about) 1 import React, { 2 PropsWithChildren, 3 useContext, 4 useEffect, 5 useState, 6 } from "react" 7 import { AnalyticsAction, incr } from "./analytics" 8 import { usePersistentState } from "./BrowserStorage" 9 10 export type StarredResourcesContext = { 11 starredResources: string[] 12 starResource: (name: string) => void 13 unstarResource: (name: string) => void 14 toggleStarResource: (name: string) => void 15 } 16 17 const starredResourceContext = React.createContext<StarredResourcesContext>({ 18 starredResources: [], 19 starResource: (s) => {}, 20 unstarResource: (s) => {}, 21 toggleStarResource: (s) => {}, 22 }) 23 24 export function useStarredResources(): StarredResourcesContext { 25 return useContext(starredResourceContext) 26 } 27 28 export function StarredResourceMemoryProvider( 29 props: PropsWithChildren<{ initialValueForTesting?: string[] }> 30 ) { 31 const [starredResources, setStarredResources] = useState<Array<string>>( 32 props.initialValueForTesting || [] 33 ) 34 35 function starResource(name: string) { 36 setStarredResources((prevState) => { 37 return prevState.includes(name) ? prevState : [...prevState, name] 38 }) 39 } 40 41 function unstarResource(name: string) { 42 setStarredResources((prevState) => { 43 return prevState.filter((s) => s !== name) 44 }) 45 } 46 47 function toggleStarResource(name: string) { 48 if (starredResources.includes(name)) { 49 unstarResource(name) 50 } else { 51 starResource(name) 52 } 53 } 54 55 return ( 56 <starredResourceContext.Provider 57 value={{ 58 starredResources: starredResources, 59 starResource: starResource, 60 unstarResource: unstarResource, 61 toggleStarResource: toggleStarResource, 62 }} 63 > 64 {props.children} 65 </starredResourceContext.Provider> 66 ) 67 } 68 69 export function StarredResourcesContextProvider( 70 props: PropsWithChildren<{ initialValueForTesting?: string[] }> 71 ) { 72 // we renamed pins to stars but kept the local storage name "pinned-resources" 73 // so that user's pinned resources show up as starred 74 let [starredResources, setStarredResources] = usePersistentState<string[]>( 75 "pinned-resources", 76 props.initialValueForTesting ?? [] 77 ) 78 79 useEffect(() => { 80 incr("ui.web.star", { 81 starCount: starredResources.length.toString(), 82 action: AnalyticsAction.Load, 83 }) 84 // empty deps because we only want to report the loaded star count once per app load 85 // eslint-disable-next-line react-hooks/exhaustive-deps 86 }, []) 87 88 function starResource(name: string) { 89 setStarredResources((prevState) => { 90 const ret = prevState.includes(name) ? prevState : [...prevState, name] 91 incr("ui.web.star", { 92 starCount: ret.length.toString(), 93 action: AnalyticsAction.Star, 94 }) 95 return ret 96 }) 97 } 98 99 function unstarResource(name: string) { 100 setStarredResources((prevState) => { 101 const ret = prevState.filter((n) => n !== name) 102 incr("ui.web.star", { 103 starCount: ret.length.toString(), 104 action: AnalyticsAction.Unstar, 105 }) 106 return ret 107 }) 108 } 109 110 function toggleStarResource(name: string) { 111 if (starredResources.includes(name)) { 112 unstarResource(name) 113 } else { 114 starResource(name) 115 } 116 } 117 118 return ( 119 <starredResourceContext.Provider 120 value={{ 121 starredResources: starredResources, 122 starResource: starResource, 123 unstarResource: unstarResource, 124 toggleStarResource: toggleStarResource, 125 }} 126 > 127 {props.children} 128 </starredResourceContext.Provider> 129 ) 130 }