From 75eaa3d49c164a7f6cd81b2ac3f8ba4267ae8ed6 Mon Sep 17 00:00:00 2001 From: Innocent-akim Date: Wed, 8 Jan 2025 10:30:47 +0200 Subject: [PATCH] feat: implement automatic offline page display --- apps/web/app/[locale]/layout.tsx | 19 ++++++---- apps/web/app/[locale]/page-component.tsx | 18 +++------ apps/web/components/offline-wrapper/index.tsx | 37 +++++++++++++++++++ apps/web/components/pages/offline/index.tsx | 3 ++ 4 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 apps/web/components/offline-wrapper/index.tsx diff --git a/apps/web/app/[locale]/layout.tsx b/apps/web/app/[locale]/layout.tsx index 7353f53b4..85dc858c7 100644 --- a/apps/web/app/[locale]/layout.tsx +++ b/apps/web/app/[locale]/layout.tsx @@ -17,6 +17,7 @@ import { PropsWithChildren, useEffect } from 'react'; import { useCheckAPI } from '@app/hooks/useCheckAPI'; import GlobalSkeleton from '@components/ui/global-skeleton'; +import OfflineWrapper from '@components/offline-wrapper'; import { JitsuOptions } from '@jitsu/jitsu-react/dist/useJitsu'; import { PHProvider } from './integration/posthog/provider'; @@ -145,14 +146,16 @@ const LocaleLayout = ({ children, params: { locale }, pageProps }: PropsWithChil enableSystem disableTransitionOnChange > - {loading && !pathname?.startsWith('/auth') ? ( - - ) : ( - <> - - {children} - - )} + + {loading && !pathname?.startsWith('/auth') ? ( + + ) : ( + <> + + {children} + + )} + diff --git a/apps/web/app/[locale]/page-component.tsx b/apps/web/app/[locale]/page-component.tsx index 2a1ab3151..f8c5f696d 100644 --- a/apps/web/app/[locale]/page-component.tsx +++ b/apps/web/app/[locale]/page-component.tsx @@ -2,7 +2,7 @@ 'use client'; import React, { useEffect, useState } from 'react'; -import { useOrganizationTeams, useTimerView } from '@app/hooks'; +import { useOrganizationTeams } from '@app/hooks'; import { clsxm } from '@app/utils'; import NoTeam from '@components/pages/main/no-team'; import { withAuthentication } from 'lib/app/authenticator'; @@ -10,8 +10,6 @@ import { Breadcrumb, Card, Container } from 'lib/components'; import { AuthUserTaskInput, TeamInvitations, TeamMembers, Timer, UnverifiedEmail } from 'lib/features'; import { MainLayout } from 'lib/layout'; import { IssuesView } from '@app/constants'; -import { useNetworkState } from '@uidotdev/usehooks'; -import Offline from '@components/pages/offline'; import { useTranslations } from 'next-intl'; import { Analytics } from '@vercel/analytics/react'; @@ -34,7 +32,6 @@ function MainPage() { const t = useTranslations(); const [headerSize] = useState(10); const { isTeamMember, isTrackingEnabled, activeTeam } = useOrganizationTeams(); - const { timerStatus } = useTimerView(); const [fullWidth, setFullWidth] = useAtom(fullWidthState); const [view, setView] = useAtom(headerTabs); @@ -44,7 +41,7 @@ function MainPage() { { title: activeTeam?.name || '', href: '/' }, { title: t(`common.${view}`), href: `/` } ]; - const { online } = useNetworkState(); + useEffect(() => { if (view == IssuesView.KANBAN && path == '/') { setView(IssuesView.CARDS); @@ -57,13 +54,10 @@ function MainPage() { setFullWidth(JSON.parse(window?.localStorage.getItem('conf-fullWidth-mode') || 'true')); }, [setFullWidth]); - if (!online) { - return ; - } return ( <>
- {/*
*/} + {/*
*/}
-
+
-
+
@@ -100,7 +94,7 @@ function MainPage() { footerClassName={clsxm('')} > -
{isTeamMember ? +
{isTeamMember ? diff --git a/apps/web/components/offline-wrapper/index.tsx b/apps/web/components/offline-wrapper/index.tsx new file mode 100644 index 000000000..d56797850 --- /dev/null +++ b/apps/web/components/offline-wrapper/index.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { useNetworkState } from '@uidotdev/usehooks'; +import Offline from '@components/pages/offline'; +import { useTimerView } from '@app/hooks'; +import { usePathname } from 'next/navigation'; + +interface OfflineWrapperProps { + children: React.ReactNode; +} + +/** + * A wrapper component that conditionally renders the Offline component if the user is not online. + * The Offline component is not shown on authentication pages (paths starting with /auth). + * When the user is offline, the Offline component is rendered with the showTimer prop set to + * whether the timer is running or not. + * + * @example + * + * + * + * @param {React.ReactNode} children - The children components to render when the user is online + * @returns {React.ReactElement} - The Offline component if the user is offline (except on auth pages), or the children components if the user is online + */ +export default function OfflineWrapper({ children }: OfflineWrapperProps) { + const { online } = useNetworkState(); + const { timerStatus } = useTimerView(); + const pathname = usePathname(); + + const isAuthPage = pathname?.startsWith('/auth'); + + if (!online && !isAuthPage) { + return ; + } + + return <>{children}; +} diff --git a/apps/web/components/pages/offline/index.tsx b/apps/web/components/pages/offline/index.tsx index 198e689df..dec2f2754 100644 --- a/apps/web/components/pages/offline/index.tsx +++ b/apps/web/components/pages/offline/index.tsx @@ -3,11 +3,14 @@ import { cn } from '@/lib/utils'; import SadCry from '@components/ui/svgs/sad-cry'; import { Text } from 'lib/components'; import { useTranslations } from 'next-intl'; + interface IPropsOffline { showTimer?: boolean } + function Offline({ showTimer }: IPropsOffline) { const t = useTranslations(); + return (