diff --git a/src/common/context/ProjectsContext.ts b/src/common/context/ProjectsContext.ts index f4d30699..4666e695 100644 --- a/src/common/context/ProjectsContext.ts +++ b/src/common/context/ProjectsContext.ts @@ -8,9 +8,11 @@ export const SidebarTogglableContext = createContext(true) type ProjectsContextValue = { refreshing: boolean, projects: Project[], + refreshProjects: () => void, } export const ProjectsContext = createContext({ refreshing: false, projects: [], + refreshProjects: () => {}, }) diff --git a/src/features/projects/view/ProjectsContextProvider.tsx b/src/features/projects/view/ProjectsContextProvider.tsx index 639363e1..c61c728c 100644 --- a/src/features/projects/view/ProjectsContextProvider.tsx +++ b/src/features/projects/view/ProjectsContextProvider.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, useCallback } from "react"; import { ProjectsContext } from "@/common"; import { Project } from "@/features/projects/domain"; @@ -15,47 +15,43 @@ const ProjectsContextProvider = ({ const [refreshing, setRefreshing] = useState(false); const isLoadingRef = useRef(false); + const setProjectsAndRefreshed = (value: Project[]) => { setProjects(value); }; +const refreshProjects = useCallback(() => { + if (isLoadingRef.current) return; + isLoadingRef.current = true; + setRefreshing(true); + fetch("/api/refresh-projects", { method: "POST" }) + .then((res) => res.json()) + .then(({ projects }) => { + if (projects) setProjectsAndRefreshed(projects); + }) + .catch((error) => console.error("Failed to refresh projects", error)) + .finally(() => { + isLoadingRef.current = false; + setRefreshing(false); + }); +}, []); + // Trigger background refresh after initial mount - useEffect(() => { - const refreshProjects = () => { - if (isLoadingRef.current) { - return; - } - isLoadingRef.current = true; - setRefreshing(true); - fetch("/api/refresh-projects", { method: "POST" }) - .then((res) => res.json()) - .then(({ projects }) => { - if (projects) setProjectsAndRefreshed(projects); - }) - .catch((error) => console.error("Failed to refresh projects", error)) - .finally(() => { - isLoadingRef.current = false; - setRefreshing(false); - }); - }; - // Initial refresh - refreshProjects(); - const handleVisibilityChange = () => { - if (!document.hidden) refreshProjects(); - }; - document.addEventListener("visibilitychange", handleVisibilityChange); - return () => - document.removeEventListener("visibilitychange", handleVisibilityChange); - }, []); +useEffect(() => { + // Initial refresh + refreshProjects(); + const handleVisibilityChange = () => { + if (!document.hidden) refreshProjects(); + }; + document.addEventListener("visibilitychange", handleVisibilityChange); + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; +}, [refreshProjects]); return ( - + {children} ); diff --git a/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx b/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx index ec9b852d..cfa9294b 100644 --- a/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx +++ b/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { Box, @@ -7,26 +7,33 @@ import { ListItemButton, Skeleton as MuiSkeleton, Stack, - Typography -} from "@mui/material" -import MenuItemHover from "@/common/ui/MenuItemHover" -import { Project } from "@/features/projects/domain" -import { useProjectSelection } from "@/features/projects/data" -import ProjectAvatar, { Squircle as ProjectAvatarSquircle } from "./ProjectAvatar" -import { useCloseSidebarOnSelection } from "@/features/sidebar/data" + Typography, +} from "@mui/material"; +import MenuItemHover from "@/common/ui/MenuItemHover"; +import { Project } from "@/features/projects/domain"; +import { useProjectSelection } from "@/features/projects/data"; +import { useContext } from "react"; +import { ProjectsContext } from "@/common"; +import ProjectAvatar, { + Squircle as ProjectAvatarSquircle, +} from "./ProjectAvatar"; +import { useCloseSidebarOnSelection } from "@/features/sidebar/data"; -const AVATAR_SIZE = { width: 40, height: 40 } +const AVATAR_SIZE = { width: 40, height: 40 }; const ProjectListItem = ({ project }: { project: Project }) => { - const { project: selectedProject, selectProject } = useProjectSelection() - const selected = project.id === selectedProject?.id - const { closeSidebarIfNeeded } = useCloseSidebarOnSelection() + const { project: selectedProject, selectProject } = useProjectSelection(); + const { refreshProjects } = useContext(ProjectsContext); + const selected = project.id === selectedProject?.id; + const { closeSidebarIfNeeded } = useCloseSidebarOnSelection(); + return (