diff --git a/packages/extension/src/newtab/App.tsx b/packages/extension/src/newtab/App.tsx index e2dd46bee68..5376f830e7a 100644 --- a/packages/extension/src/newtab/App.tsx +++ b/packages/extension/src/newtab/App.tsx @@ -20,7 +20,6 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { useWebVitals } from '@dailydotdev/shared/src/hooks/useWebVitals'; import { useGrowthBookContext } from '@dailydotdev/shared/src/components/GrowthBookProvider'; import { isTesting } from '@dailydotdev/shared/src/lib/constants'; -import ExtensionOnboarding from '@dailydotdev/shared/src/components/ExtensionOnboarding'; import { withFeaturesBoundary } from '@dailydotdev/shared/src/components/withFeaturesBoundary'; import { LazyModalElement } from '@dailydotdev/shared/src/components/modals/LazyModalElement'; import { useHostStatus } from '@dailydotdev/shared/src/hooks/useHostPermissionStatus'; @@ -39,6 +38,7 @@ import { ExtensionContextProvider } from '../contexts/ExtensionContext'; import CustomRouter from '../lib/CustomRouter'; import { version } from '../../package.json'; import MainFeedPage from './MainFeedPage'; +import HijackingLoginStrip from './HijackingLoginStrip'; import { BootDataProvider } from '../../../shared/src/contexts/BootProvider'; import { getContentScriptPermissionAndRegister } from '../lib/extensionScripts'; import { useContentScriptStatus } from '../../../shared/src/hooks'; @@ -67,6 +67,31 @@ const feedErrorFallback: ReactElement = ( ); +function HijackingPage({ + onPageChanged, +}: { + onPageChanged: (page: string) => void; +}): ReactElement { + const { setCurrentPage } = useExtensionContext(); + + useEffect(() => { + setCurrentPage('/hijacking'); + + return () => { + setCurrentPage('/'); + }; + }, [setCurrentPage]); + + return ( + } + /> + ); +} + function InternalApp(): ReactElement { const { isOnboardingComplete } = useOnboardingActions(); useError(); @@ -122,7 +147,13 @@ function InternalApp(): ReactElement { } if (shouldRedirectOnboarding) { - return ; + return ( + + + + + + ); } return ( diff --git a/packages/extension/src/newtab/HijackingLoginStrip.tsx b/packages/extension/src/newtab/HijackingLoginStrip.tsx new file mode 100644 index 00000000000..00d9db29c16 --- /dev/null +++ b/packages/extension/src/newtab/HijackingLoginStrip.tsx @@ -0,0 +1,70 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import classNames from 'classnames'; +import { + Button, + ButtonVariant, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext'; +import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; +import { AuthTriggers } from '@dailydotdev/shared/src/lib/auth'; +import { LogEvent, TargetType } from '@dailydotdev/shared/src/lib/log'; +import feedStyles from '@dailydotdev/shared/src/components/Feed.module.css'; +import { cloudinaryReadingReminderCat } from '@dailydotdev/shared/src/lib/image'; + +export default function HijackingLoginStrip(): ReactElement { + const { showLogin } = useAuthContext(); + const { logEvent } = useLogContext(); + + return ( + + + + + + + + + + + + Log in to unlock the full daily.dev experience + + + If you were in the middle of onboarding, you can pick up right + where you left off. + + { + logEvent({ + event_name: LogEvent.Click, + target_type: TargetType.LoginButton, + target_id: 'hijacking', + }); + + showLogin({ + trigger: AuthTriggers.Onboarding, + options: { isLogin: true }, + }); + }} + > + Log in to continue + + + + + + + + + + + ); +} diff --git a/packages/extension/src/newtab/MainFeedPage.tsx b/packages/extension/src/newtab/MainFeedPage.tsx index 5826efd3c88..fd29b9f71fa 100644 --- a/packages/extension/src/newtab/MainFeedPage.tsx +++ b/packages/extension/src/newtab/MainFeedPage.tsx @@ -1,13 +1,17 @@ -import type { ReactElement } from 'react'; -import React, { useCallback, useContext, useMemo, useState } from 'react'; +import type { ReactElement, ReactNode } from 'react'; +import React, { + useCallback, + useContext, + useLayoutEffect, + useMemo, + useState, +} from 'react'; import MainLayout from '@dailydotdev/shared/src/components/MainLayout'; import MainFeedLayout from '@dailydotdev/shared/src/components/MainFeedLayout'; import ScrollToTopButton from '@dailydotdev/shared/src/components/ScrollToTopButton'; import { getShouldRedirect } from '@dailydotdev/shared/src/components/utilities'; import dynamic from 'next/dynamic'; import AuthContext from '@dailydotdev/shared/src/contexts/AuthContext'; -import AlertContext from '@dailydotdev/shared/src/contexts/AlertContext'; -import { getFeedName } from '@dailydotdev/shared/src/lib/feed'; import { SearchProviderEnum } from '@dailydotdev/shared/src/graphql/search'; import { LogEvent } from '@dailydotdev/shared/src/lib/log'; import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; @@ -33,34 +37,71 @@ const DndModal = dynamic( export type MainFeedPageProps = { onPageChanged: (page: string) => unknown; + initialPage?: string; + shouldInitializeCurrentPage?: boolean; + shortcuts?: ReactNode; +}; + +const normalizePage = (page: string): string => + page.startsWith('/') ? page : `/${page}`; + +const getInitialFeedName = (page?: string): string => { + if (!page) { + return 'default'; + } + + const normalizedPage = normalizePage(page); + + if (normalizedPage === '/') { + return 'default'; + } + + return normalizedPage; }; export default function MainFeedPage({ onPageChanged, + initialPage, + shouldInitializeCurrentPage = true, + shortcuts, }: MainFeedPageProps): ReactElement { - const { alerts } = useContext(AlertContext); const { logEvent } = useLogContext(); const [isSearchOn, setIsSearchOn] = useState(false); const { user, loadingUser } = useContext(AuthContext); - const [feedName, setFeedName] = useState('default'); + const [feedName, setFeedName] = useState(() => + getInitialFeedName(initialPage), + ); const [searchQuery, setSearchQuery] = useState(); const { shouldUseListFeedLayout } = useFeedLayout({ feedRelated: false }); useCompanionSettings(); const { isActive: isDndActive, showDnd, setShowDnd } = useDndContext(); const { isCustomDefaultFeed } = useCustomDefaultFeed(); + useLayoutEffect(() => { + if (!initialPage || !shouldInitializeCurrentPage) { + return; + } + + onPageChanged(normalizePage(initialPage)); + }, [initialPage, onPageChanged, shouldInitializeCurrentPage]); + const onNavTabClick = useCallback( (tab: string): void => { - if (tab !== 'search') { + const normalizedTab = normalizePage(tab); + + if (normalizedTab !== '/search') { setIsSearchOn(false); } - setFeedName(tab); - const isMyFeed = tab === '/my-feed'; + + setFeedName(getInitialFeedName(normalizedTab)); + const isMyFeed = normalizedTab === '/my-feed'; + if (getShouldRedirect(isMyFeed, !!user)) { - onPageChanged(`/`); - } else { - onPageChanged(`/${tab}`); + onPageChanged('/'); + return; } + + onPageChanged(normalizedTab); }, [onPageChanged, user], ); @@ -70,20 +111,19 @@ export default function MainFeedPage({ return '/search'; } + if (feedName === 'default') { + return '/'; + } + // default page when user selected custom default feed if (isCustomDefaultFeed && feedName === 'default') { return '/'; } - const feed = getFeedName(feedName, { - hasUser: !!user, - hasFiltered: !alerts?.filter, - }); - - return `/${feed}`; + return normalizePage(feedName); // @NOTE see https://dailydotdev.atlassian.net/l/cp/dK9h1zoM // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isSearchOn, feedName]); + }, [isSearchOn, isCustomDefaultFeed, feedName]); const onLogoClick = (e: React.MouseEvent): void => { e.preventDefault(); @@ -134,9 +174,11 @@ export default function MainFeedPage({ /> } shortcuts={ - + shortcuts ?? ( + + ) } /> diff --git a/packages/shared/src/components/MainLayout.tsx b/packages/shared/src/components/MainLayout.tsx index 4af27936ce0..55592d5f7da 100644 --- a/packages/shared/src/components/MainLayout.tsx +++ b/packages/shared/src/components/MainLayout.tsx @@ -33,6 +33,7 @@ import PlusMobileEntryBanner from './banners/PlusMobileEntryBanner'; import usePlusEntry from '../hooks/usePlusEntry'; import { SearchProvider } from '../contexts/search/SearchContext'; import { FeedbackWidget } from './feedback'; +import { isExtension } from '../lib/func'; const GoBackHeaderMobile = dynamic( () => @@ -116,7 +117,11 @@ function MainLayoutComponent({ const isPageApplicableForOnboarding = !page || feeds.includes(page) || isCustomFeed; const shouldRedirectOnboarding = - !user && isPageReady && isPageApplicableForOnboarding && !isTesting; + !isExtension && + !user && + isPageReady && + isPageApplicableForOnboarding && + !isTesting; useEffect(() => { if (!shouldRedirectOnboarding) { diff --git a/packages/shared/src/components/banners/ReadingReminderCatLaptop.tsx b/packages/shared/src/components/banners/ReadingReminderCatLaptop.tsx index 789c09aa223..0547fa19652 100644 --- a/packages/shared/src/components/banners/ReadingReminderCatLaptop.tsx +++ b/packages/shared/src/components/banners/ReadingReminderCatLaptop.tsx @@ -1,6 +1,7 @@ import type { ReactElement } from 'react'; import React from 'react'; import classNames from 'classnames'; +import { cloudinaryReadingReminderCat } from '../../lib/image'; interface ReadingReminderCatLaptopProps { className?: string; @@ -11,7 +12,7 @@ const ReadingReminderCatLaptop = ({ }: ReadingReminderCatLaptopProps): ReactElement => { return (
+ If you were in the middle of onboarding, you can pick up right + where you left off. +