diff --git a/src/renderer/src/routes/ProjectsScreen.tsx b/src/renderer/src/routes/ProjectsScreen.tsx index 2e9e5978..b83e4950 100644 --- a/src/renderer/src/routes/ProjectsScreen.tsx +++ b/src/renderer/src/routes/ProjectsScreen.tsx @@ -16,6 +16,7 @@ import { remoteId, defaultWorkflow, useTeamWorkflowProcess } from '../crud'; import { UnsavedContext } from '../context/UnsavedContext'; import BigDialog from '../hoc/BigDialog'; import { StepEditor } from '../components/StepEditor'; +import { useIsPapLike } from '../utils/useIsPapLike'; interface ProjectBoxProps extends BoxProps { isMobile?: boolean; @@ -32,14 +33,8 @@ export const ProjectsScreenInner: React.FC = () => { const navigate = useMyNavigate(); const teamId = localStorage.getItem(localUserKey(LocalKey.team)); const ctx = React.useContext(TeamContext); - const { - teamProjects, - personalProjects, - personalTeam, - teams, - teamDirectoryReady, - isAdmin, - } = ctx.state; + const { teamProjects, personalProjects, personalTeam, teams, isAdmin } = + ctx.state; const t: ICardsStrings = useSelector(cardsSelector, shallowEqual); const { pathname } = useLocation(); const [plan] = useGlobal('plan'); @@ -74,11 +69,7 @@ export const ProjectsScreenInner: React.FC = () => { }, []); const isPersonal = teamId === personalTeam; - const isPapLike = - Boolean(personalTeam) && - isPersonal && - teams.length === 0 && - teamDirectoryReady; + const isPapLike = useIsPapLike() && isPersonal; const projects = React.useMemo( () => (isPersonal ? personalProjects : teamId ? teamProjects(teamId) : []), [isPersonal, personalProjects, teamId, teamProjects] diff --git a/src/renderer/src/routes/SwitchTeams.tsx b/src/renderer/src/routes/SwitchTeams.tsx index ab335524..a8f5b691 100644 --- a/src/renderer/src/routes/SwitchTeams.tsx +++ b/src/renderer/src/routes/SwitchTeams.tsx @@ -26,6 +26,7 @@ import { useGlobal } from '../context/useGlobal'; import { useTeamActions } from '../components/Team/useTeamActions'; import { SharedContentCreatorDialog } from '../components/Team/SharedContentCreatorDialog'; import StickyRedirect from '../components/StickyRedirect'; +import { useIsPapLike } from '../utils/useIsPapLike'; interface ISettingsButtonProps { label: string; @@ -404,16 +405,8 @@ export const SwitchTeamsGuard: React.FC<{ children: React.ReactNode }> = ({ children, }) => { const ctx = React.useContext(TeamContext); - const { teams, personalTeam, teamDirectoryReady } = ctx.state; - const [isOffline] = useGlobal('offline'); - const [offlineOnly] = useGlobal('offlineOnly'); - // When offline && !offlineOnly, getTeams() may be empty because shared teams - // without local projects are filtered out — not the same as true PAP-only. - const isPapLike = - Boolean(personalTeam) && - teams.length === 0 && - teamDirectoryReady && - (!isOffline || offlineOnly); + const { personalTeam } = ctx.state; + const isPapLike = useIsPapLike(); React.useEffect(() => { if (!isPapLike || !personalTeam) return; diff --git a/src/renderer/src/utils/useIsPapLike.ts b/src/renderer/src/utils/useIsPapLike.ts new file mode 100644 index 00000000..ae1e614a --- /dev/null +++ b/src/renderer/src/utils/useIsPapLike.ts @@ -0,0 +1,21 @@ +import React from 'react'; +import { TeamContext } from '../context/TeamContext'; +import { useGlobal } from '../context/useGlobal'; + +/** + * True when the signed-in user is Work-Alone-Offline / PAP-like + * + * Detect "Work Alone Offline" via the `offlineOnly` global — `teams.length + * === 0` alone is not enough as it is also true for a normal user who simply hasn't joined a team yet. + * And not `!offline` (an online first-time user is online but still needs Switch + * Teams / Add Team). `offlineOnly` is set at login for users with no remoteId + * (see Access.tsx) and is the single reliable signal. + */ +export const useIsPapLike = (): boolean => { + const ctx = React.useContext(TeamContext); + const { teams, personalTeam, teamDirectoryReady } = ctx.state; + const [offlineOnly] = useGlobal('offlineOnly'); + return Boolean( + personalTeam && teams.length === 0 && teamDirectoryReady && offlineOnly + ); +};