Skip to content

Commit

Permalink
feat: new tab overlay to keep it (#3623)
Browse files Browse the repository at this point in the history
  • Loading branch information
sshanzel authored Oct 8, 2024
1 parent 4f1e0f3 commit 65bcfb7
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 9 deletions.
3 changes: 3 additions & 0 deletions packages/extension/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { install, uninstall } from '@dailydotdev/shared/src/lib/constants';
import { BOOT_LOCAL_KEY } from '@dailydotdev/shared/src/contexts/common';
import { ExtensionMessageType } from '@dailydotdev/shared/src/lib/extension';
import { storageWrapper as storage } from '@dailydotdev/shared/src/lib/storageWrapper';
import { set as setCache } from 'idb-keyval';
import { getContentScriptPermissionAndRegister } from '../lib/extensionScripts';
import { INSTALLATION_STORAGE_KEY } from '../lib/common';

const client = new GraphQLClient(graphqlUrl, { fetch: globalThis.fetch });

Expand Down Expand Up @@ -169,6 +171,7 @@ browser.runtime.onInstalled.addListener(async (details) => {
}

if (details.reason === 'install') {
setCache(INSTALLATION_STORAGE_KEY, true);
browser.tabs.create({ url: install, active: true });
}
});
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/lib/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const INSTALLATION_STORAGE_KEY = 'new_installation';
36 changes: 33 additions & 3 deletions packages/extension/src/newtab/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import { usePrompt } from '@dailydotdev/shared/src/hooks/usePrompt';
import { defaultQueryClientConfig } from '@dailydotdev/shared/src/lib/query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useWebVitals } from '@dailydotdev/shared/src/hooks/useWebVitals';
import { useGrowthBookContext } from '@dailydotdev/shared/src/components/GrowthBookProvider';
import {
useFeature,
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';
Expand All @@ -42,13 +45,17 @@ import { DndContextProvider } from '@dailydotdev/shared/src/contexts/DndContext'
import usePersistentState from '@dailydotdev/shared/src/hooks/usePersistentState';
import { LazyModal } from '@dailydotdev/shared/src/components/modals/common/types';
import { structuredCloneJsonPolyfill } from '@dailydotdev/shared/src/lib/structuredClone';
import { get as getCache } from 'idb-keyval';
import { feature } from '@dailydotdev/shared/src/lib/featureManagement';
import { ExtensionContextProvider } from '../contexts/ExtensionContext';
import CustomRouter from '../lib/CustomRouter';
import { version } from '../../package.json';
import MainFeedPage from './MainFeedPage';
import { BootDataProvider } from '../../../shared/src/contexts/BootProvider';
import { getContentScriptPermissionAndRegister } from '../lib/extensionScripts';
import { useContentScriptStatus } from '../../../shared/src/hooks';
import { KeepItOverlay } from './KeepItOverlay';
import { INSTALLATION_STORAGE_KEY } from '../lib/common';

structuredCloneJsonPolyfill();

Expand Down Expand Up @@ -98,6 +105,7 @@ function InternalApp(): ReactElement {
}
}, [analyticsConsent, analyticsConsentPrompt]);

const [shouldShowOverlay, setShouldShowOverlay] = useState(false);
const { unreadCount } = useNotificationContext();
const { closeLogin, shouldShowLogin, loginState } = useContext(AuthContext);
const { contentScriptGranted } = useContentScriptStatus();
Expand All @@ -106,6 +114,7 @@ function InternalApp(): ReactElement {
const routeChangedCallbackRef = useLogPageView();
useConsoleLogo();

const extensionOverlay = useFeature(feature.extensionOverlay);
const { user, isAuthReady } = useAuthContext();
const { growthbook } = useGrowthBookContext();
const isPageReady =
Expand Down Expand Up @@ -134,22 +143,43 @@ function InternalApp(): ReactElement {
}
}, [contentScriptGranted]);

useEffect(() => {
if (shouldShowLogin) {
return;
}

getCache(INSTALLATION_STORAGE_KEY).then((value) => {
if (value) {
setShouldShowOverlay(true);
}
});
}, [shouldShowLogin]);

useEffect(() => {
document.title = unreadCount
? `(${unreadCount}) ${DEFAULT_TAB_TITLE}`
: DEFAULT_TAB_TITLE;
}, [unreadCount]);

const onClose = useCallback(() => setShouldShowOverlay(false), []);
const overlay =
shouldShowOverlay && extensionOverlay ? (
<KeepItOverlay onClose={onClose} />
) : null;

if (!hostGranted) {
return isCheckingHostPermissions ? null : <ExtensionPermissionsPrompt />;
return isCheckingHostPermissions ? null : (
<ExtensionPermissionsPrompt>{overlay}</ExtensionPermissionsPrompt>
);
}

if (shouldRedirectOnboarding) {
return <ExtensionOnboarding />;
return <ExtensionOnboarding>{overlay}</ExtensionOnboarding>;
}

return (
<DndContextProvider>
{overlay}
<MainFeedPage onPageChanged={onPageChanged} />
{shouldShowLogin && (
<AuthModal
Expand Down
98 changes: 98 additions & 0 deletions packages/extension/src/newtab/KeepItOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { ReactElement, useCallback, useEffect, useRef } from 'react';
import {
DailyIcon,
StraightArrowIcon,
} from '@dailydotdev/shared/src/components/icons';
import { IconSize } from '@dailydotdev/shared/src/components/Icon';
import {
Button,
ButtonSize,
ButtonVariant,
} from '@dailydotdev/shared/src/components/buttons/Button';

import {
Typography,
TypographyType,
} from '@dailydotdev/shared/src/components/typography/Typography';
import { del } from 'idb-keyval';
import { useEventListener } from '@dailydotdev/shared/src/hooks';
import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext';
import { LogEvent } from '@dailydotdev/shared/src/lib/log';
import { INSTALLATION_STORAGE_KEY } from '../lib/common';

interface KeepItOverlayProps {
onClose: () => void;
}

export function KeepItOverlay({ onClose }: KeepItOverlayProps): ReactElement {
const { logEvent } = useLogContext();
const timeoutRef = useRef<ReturnType<typeof window.setTimeout>>();
const onDelete = useCallback(() => del(INSTALLATION_STORAGE_KEY), []);

const onClick = useCallback(async () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}

logEvent({ event_name: LogEvent.DismissNewTabPermission });
await onDelete();
onClose();
}, [onDelete, onClose, logEvent]);

// when this overlay is opened, there is nothing the user can do, clicking anywhere should close it.
useEventListener(globalThis?.document, 'click', onClick);

useEffect(() => {
logEvent({ event_name: LogEvent.ShowNewTabPermission });
onDelete();
// logEvent is unstable
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [onDelete]);

useEffect(() => {
timeoutRef.current = setTimeout(() => {
onClick();
}, 5000);
}, [onClick]);

return (
<div className="fixed inset-0 z-max flex flex-col items-center justify-center bg-overlay-dark-dark3">
<div className="absolute flex flex-row-reverse">
<span className="h-[17.5rem] w-[17.5rem] -translate-x-12 bg-accent-onion-default blur-3xl" />
<span className="h-[17.5rem] w-[17.5rem] translate-x-12 bg-accent-cabbage-default blur-3xl" />
</div>
<Button
variant={ButtonVariant.Primary}
className="relative"
onClick={onClick}
>
ALREADY DID THAT!
</Button>
<div className="absolute ml-16 mt-8 flex translate-y-full flex-col">
<StraightArrowIcon
size={IconSize.XXXLarge}
className="z-3 rotate-180 text-brand-subtler"
/>
<span className="absolute right-0 top-[80%] flex min-w-[20rem] flex-row items-center gap-2 rounded-12 bg-surface-primary p-3">
<span className="rounded-full bg-surface-invert p-1">
<DailyIcon />
</span>
<span className="ml-1 flex flex-col text-surface-invert">
<Typography type={TypographyType.Footnote}>
Open daily.dev every new tab
</Typography>
<Typography type={TypographyType.Title3} bold>
Click &quot;Keep it&quot;
</Typography>
</span>
<Button
className="rounded-50 border border-border-subtlest-tertiary text-text-link"
size={ButtonSize.Small}
>
Keep it
</Button>
</span>
</div>
</div>
);
}
7 changes: 4 additions & 3 deletions packages/shared/src/components/ExtensionOnboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactElement, useEffect } from 'react';
import React, { PropsWithChildren, ReactElement, useEffect } from 'react';
import Link from './utilities/Link';
import Logo, { LogoPosition } from './Logo';
import { OnboardingTitleGradient } from './onboarding/common';
Expand All @@ -7,7 +7,7 @@ import { Button, ButtonSize, ButtonVariant } from './buttons/Button';
import { cloudinary } from '../lib/image';
import { useExtensionContext } from '../contexts/ExtensionContext';

const ExtensionOnboarding = (): ReactElement => {
const ExtensionOnboarding = ({ children }: PropsWithChildren): ReactElement => {
const { setCurrentPage } = useExtensionContext();

useEffect(() => {
Expand All @@ -18,7 +18,7 @@ const ExtensionOnboarding = (): ReactElement => {
}, [setCurrentPage]);

return (
<div className="flex max-h-[100vh] min-h-[100vh] flex-col items-center justify-center overflow-hidden px-7 text-center antialiased">
<div className="relative flex max-h-[100vh] min-h-[100vh] flex-col items-center justify-center overflow-hidden px-7 text-center antialiased">
<Logo
position={LogoPosition.Relative}
logoClassName={{ container: 'h-logo-big' }}
Expand Down Expand Up @@ -52,6 +52,7 @@ const ExtensionOnboarding = (): ReactElement => {
src={cloudinary.onboarding.glow}
alt="Gradient background"
/>
{children}
</div>
);
};
Expand Down
9 changes: 6 additions & 3 deletions packages/shared/src/components/ExtensionPermissionsPrompt.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { ReactElement, useEffect } from 'react';
import React, { PropsWithChildren, ReactElement, useEffect } from 'react';
import Logo, { LogoPosition } from './Logo';
import { OnboardingTitleGradient } from './onboarding/common';
import { Button, ButtonSize, ButtonVariant } from './buttons/Button';
import { cloudinary } from '../lib/image';
import { useExtensionContext } from '../contexts/ExtensionContext';
import { useHostStatus } from '../hooks/useHostPermissionStatus';

const ExtensionPermissionsPrompt = (): ReactElement => {
const ExtensionPermissionsPrompt = ({
children,
}: PropsWithChildren): ReactElement => {
const { requestHostPermissions, origins, setCurrentPage } =
useExtensionContext();
const { refetch: refetchHostPermissions } = useHostStatus();
Expand All @@ -27,7 +29,7 @@ const ExtensionPermissionsPrompt = (): ReactElement => {
};

return (
<div className="flex max-h-screen min-h-screen flex-col items-center justify-center overflow-hidden px-7 text-center antialiased">
<div className="relative flex max-h-screen min-h-screen flex-col items-center justify-center overflow-hidden px-7 text-center antialiased">
<Logo
position={LogoPosition.Relative}
logoClassName={{ container: 'h-logo-big' }}
Expand Down Expand Up @@ -58,6 +60,7 @@ const ExtensionPermissionsPrompt = (): ReactElement => {
src={cloudinary.onboarding.glow}
alt="Gradient background"
/>
{children}
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/lib/featureManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const feature = {
showCodeSnippets: new Feature('show_code_snippets', false),
searchPlaceholder: new Feature('search_placeholder', 'Search'),
onboardingShuffleTags: new Feature('onboarding_shuffle_tags', false),
extensionOverlay: new Feature('extension_overlay', false),
};

export { feature };
2 changes: 2 additions & 0 deletions packages/shared/src/lib/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export enum Origin {
}

export enum LogEvent {
ShowNewTabPermission = 'show new tab permission',
DismissNewTabPermission = 'dismiss new tab permission',
ReportSquad = 'report squad',
Click = 'click',
CommentPost = 'comment post',
Expand Down

0 comments on commit 65bcfb7

Please sign in to comment.