Browse Source

fix(app): flash of fallback icon for projects

Adam 1 month ago
parent
commit
14db336e3a

+ 1 - 0
packages/app/src/components/dialog-edit-project.tsx

@@ -85,6 +85,7 @@ export function DialogEditProject(props: { project: LocalProject }) {
         icon: { color: store.color, override: store.iconUrl },
         commands: { start },
       })
+      globalSync.project.icon(props.project.worktree, store.iconUrl || undefined)
       setStore("saving", false)
       dialog.close()
       return

+ 34 - 1
packages/app/src/context/global-sync.tsx

@@ -61,6 +61,7 @@ type State = {
   command: Command[]
   project: string
   projectMeta: ProjectMeta | undefined
+  icon: string | undefined
   provider: ProviderListResponse
   config: Config
   path: Path
@@ -107,6 +108,12 @@ type MetaCache = {
   ready: Accessor<boolean>
 }
 
+type IconCache = {
+  store: Store<{ value: string | undefined }>
+  setStore: SetStoreFunction<{ value: string | undefined }>
+  ready: Accessor<boolean>
+}
+
 type ChildOptions = {
   bootstrap?: boolean
 }
@@ -119,6 +126,7 @@ function createGlobalSync() {
   if (!owner) throw new Error("GlobalSync must be created within owner")
   const vcsCache = new Map<string, VcsCache>()
   const metaCache = new Map<string, MetaCache>()
+  const iconCache = new Map<string, IconCache>()
 
   const [projectCache, setProjectCache, , projectCacheReady] = persisted(
     Persist.global("globalSync.project", ["globalSync.project.v1"]),
@@ -126,12 +134,13 @@ function createGlobalSync() {
   )
 
   const sanitizeProject = (project: Project) => {
-    if (!project.icon?.url) return project
+    if (!project.icon?.url && !project.icon?.override) return project
     return {
       ...project,
       icon: {
         ...project.icon,
         url: undefined,
+        override: undefined,
       },
     }
   }
@@ -207,10 +216,20 @@ function createGlobalSync() {
       if (!meta) throw new Error("Failed to create persisted project metadata")
       metaCache.set(directory, { store: meta[0], setStore: meta[1], ready: meta[3] })
 
+      const icon = runWithOwner(owner, () =>
+        persisted(
+          Persist.workspace(directory, "icon", ["icon.v1"]),
+          createStore({ value: undefined as string | undefined }),
+        ),
+      )
+      if (!icon) throw new Error("Failed to create persisted project icon")
+      iconCache.set(directory, { store: icon[0], setStore: icon[1], ready: icon[3] })
+
       const init = () => {
         const child = createStore<State>({
           project: "",
           projectMeta: meta[0].value,
+          icon: icon[0].value,
           provider: { all: [], connected: [], default: {} },
           config: {},
           path: { state: "", config: "", worktree: "", directory: "", home: "" },
@@ -237,6 +256,10 @@ function createGlobalSync() {
         createEffect(() => {
           child[1]("projectMeta", meta[0].value)
         })
+
+        createEffect(() => {
+          child[1]("icon", icon[0].value)
+        })
       }
 
       runWithOwner(owner, init)
@@ -811,6 +834,15 @@ function createGlobalSync() {
     setStore("projectMeta", next)
   }
 
+  function projectIcon(directory: string, value: string | undefined) {
+    const [store, setStore] = ensureChild(directory)
+    const cached = iconCache.get(directory)
+    if (!cached) return
+    if (store.icon === value) return
+    cached.setStore("value", value)
+    setStore("icon", value)
+  }
+
   return {
     data: globalStore,
     set: setGlobalStore,
@@ -833,6 +865,7 @@ function createGlobalSync() {
     project: {
       loadSessions,
       meta: projectMeta,
+      icon: projectIcon,
     },
   }
 }

+ 9 - 1
packages/app/src/context/layout.tsx

@@ -235,7 +235,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
         ...project,
         icon: {
           url: metadata?.icon?.url,
-          override: metadata?.icon?.override,
+          override: metadata?.icon?.override ?? childStore.icon,
           color: metadata?.icon?.color,
         },
       }
@@ -306,6 +306,14 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
       const projects = enriched()
       if (projects.length === 0) return
 
+      if (globalSync.ready) {
+        for (const project of projects) {
+          if (!project.id) continue
+          if (project.id === "global") continue
+          globalSync.project.icon(project.worktree, project.icon?.override)
+        }
+      }
+
       const used = new Set<string>()
       for (const project of projects) {
         const color = project.icon?.color ?? colors[project.worktree]