()
| 27 | // `ProjectRouteShell`'s chrome — archived banner, IAM-remind dialog, document |
| 28 | // title — stays with the deferred shell-mounting phase.) |
| 29 | export function ProjectRouteGate() { |
| 30 | const params = useParams(); |
| 31 | const projectId = params.projectId ?? ""; |
| 32 | const projectName = projectResourceNameFromId(projectId); |
| 33 | const route = useCurrentRoute(); |
| 34 | const navigate = useNavigate(); |
| 35 | const navigateRef = useRef(navigate); |
| 36 | navigateRef.current = navigate; |
| 37 | const notify = useNotify(); |
| 38 | |
| 39 | const project = useAppStore((state) => state.projectsByName[projectName]); |
| 40 | const fetchProject = useAppStore((state) => state.fetchProject); |
| 41 | const loadCurrentUser = useAppStore((state) => state.loadCurrentUser); |
| 42 | const loadServerInfo = useAppStore((state) => state.loadServerInfo); |
| 43 | const setRecentProject = useAppStore((state) => state.setRecentProject); |
| 44 | const removeRecentVisit = useAppStore((state) => state.removeRecentVisit); |
| 45 | const [loadedProjectName, setLoadedProjectName] = useState(""); |
| 46 | |
| 47 | useEffect(() => { |
| 48 | let stale = false; |
| 49 | setLoadedProjectName(""); |
| 50 | |
| 51 | const load = async () => { |
| 52 | await Promise.all([loadCurrentUser(), loadServerInfo()]); |
| 53 | const nextProject = await fetchProject(projectName); |
| 54 | if (stale) return; |
| 55 | if (!nextProject) { |
| 56 | // Project missing or not accessible (e.g. no `bb.projects.get`): drop |
| 57 | // it from recent visits and bounce to landing, like ProjectRouteShell. |
| 58 | const projectRoute = navigateRef.current.resolve({ |
| 59 | name: PROJECT_V1_ROUTE_DETAIL, |
| 60 | params: { projectId }, |
| 61 | }); |
| 62 | removeRecentVisit(projectRoute.fullPath); |
| 63 | const error = useAppStore.getState().projectErrorsByName[projectName]; |
| 64 | if (error) { |
| 65 | notify({ |
| 66 | module: "bytebase", |
| 67 | style: "CRITICAL", |
| 68 | title: `Failed to fetch project ${projectId}`, |
| 69 | description: error.message, |
| 70 | }); |
| 71 | } |
| 72 | void navigateRef.current.replace({ name: WORKSPACE_ROUTE_LANDING }); |
| 73 | return; |
| 74 | } |
| 75 | setRecentProject(nextProject.name); |
| 76 | if (!stale) { |
| 77 | setLoadedProjectName(projectName); |
| 78 | } |
| 79 | }; |
| 80 | |
| 81 | void load(); |
| 82 | return () => { |
| 83 | stale = true; |
| 84 | }; |
| 85 | }, [ |
| 86 | fetchProject, |
nothing calls this directly
no test coverage detected