Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ import { useLazyModal } from '../../../hooks/useLazyModal';
import { LazyModal } from '../../modals/common/types';
import { checkIsExtension } from '../../../lib/func';

export const ExtensionSection = (): ReactElement | null => {
const ExtensionSectionContent = (): ReactElement => {
const { openModal } = useLazyModal();
const { isActive: isDndActive, setShowDnd } = useDndContext();
const { optOutCompanion, toggleOptOutCompanion } = useSettingsContext();

if (!checkIsExtension()) {
return null;
}

return (
<>
<HorizontalSeparator />
Expand All @@ -33,7 +29,7 @@ export const ExtensionSection = (): ReactElement | null => {
{
title: `${isDndActive ? 'Resume' : 'Pause'} new tab`,
icon: isDndActive ? PlayIcon : PauseIcon,
onClick: () => setShowDnd?.(true),
onClick: () => setShowDnd(true),
},
{
title: `${optOutCompanion ? 'Enable' : 'Disable'} companion widget`,
Expand All @@ -45,3 +41,11 @@ export const ExtensionSection = (): ReactElement | null => {
</>
);
};

export const ExtensionSection = (): ReactElement | null => {
if (!checkIsExtension()) {
return null;
}

return <ExtensionSectionContent />;
};
47 changes: 24 additions & 23 deletions packages/shared/src/contexts/AlertContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ReactNode, ReactElement } from 'react';
import React, { useMemo, useContext } from 'react';
import type { UseMutateAsyncFunction } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { Alerts, AlertsUpdate } from '../graphql/alerts';
import type { Alerts } from '../graphql/alerts';
import {
UPDATE_ALERTS,
UPDATE_LAST_BOOT_POPUP,
Expand All @@ -13,36 +13,33 @@ import { gqlClient } from '../graphql/common';

export const ALERT_DEFAULTS: Alerts = {
filter: true,
rankLastSeen: null,
myFeed: null,
rankLastSeen: undefined,
myFeed: undefined,
squadTour: true,
showGenericReferral: false,
showStreakMilestone: false,
showAchievementUnlock: null,
lastBootPopup: null,
briefBannerLastSeen: null,
lastBootPopup: undefined,
briefBannerLastSeen: undefined,
};

export interface AlertContextData {
alerts: Alerts;
isFetched?: boolean;
loadedAlerts?: boolean;
updateAlerts?: UseMutateAsyncFunction<
updateAlerts: UseMutateAsyncFunction<unknown, unknown, Alerts, unknown>;
updateLastReferralReminder?: UseMutateAsyncFunction<unknown, unknown, void>;
updateLastBootPopup?: UseMutateAsyncFunction<unknown, unknown, void>;
updateHasSeenOpportunity?: UseMutateAsyncFunction<
unknown,
unknown,
AlertsUpdate,
() => Promise<void>
boolean | void
>;
updateLastReferralReminder?: UseMutateAsyncFunction;
updateLastBootPopup?: UseMutateAsyncFunction;
updateHasSeenOpportunity?: UseMutateAsyncFunction;
clearOpportunityAlert?: UseMutateAsyncFunction;
clearOpportunityAlert?: UseMutateAsyncFunction<unknown, unknown, void>;
}

const AlertContext = React.createContext<AlertContextData>({
alerts: ALERT_DEFAULTS,
loadedAlerts: false,
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const AlertContext = React.createContext<AlertContextData>(null!);

export interface AlertContextProviderProps {
children: ReactNode;
Expand All @@ -60,6 +57,10 @@ export const AlertContextProvider = ({
isFetched,
updateAlerts,
}: AlertContextProviderProps): ReactElement => {
const applyUpdatedAlerts = (updatedAlerts: Alerts): void => {
updateAlerts?.(updatedAlerts);
};

const { mutateAsync: updateRemoteAlerts } = useMutation<
unknown,
unknown,
Expand All @@ -69,15 +70,15 @@ export const AlertContextProvider = ({
gqlClient.request(UPDATE_ALERTS, {
data: params,
}),
onMutate: (params) => updateAlerts({ ...alerts, ...params }),
onMutate: (params) => applyUpdatedAlerts({ ...alerts, ...params }),

onError: (_, params) => {
const rollback = Object.keys(params).reduce(
const rollback = (Object.keys(params) as Array<keyof Alerts>).reduce(
(values, key) => ({ ...values, [key]: alerts[key] }),
{},
{} as Alerts,
);

updateAlerts({ ...alerts, ...rollback });
applyUpdatedAlerts({ ...alerts, ...rollback });
},
});

Expand All @@ -89,16 +90,16 @@ export const AlertContextProvider = ({
mutationFn: () => gqlClient.request(UPDATE_LAST_BOOT_POPUP),

onMutate: () =>
updateAlerts({
applyUpdatedAlerts({
...alerts,
lastBootPopup: new Date(),
bootPopup: false,
}),

onError: () => {
updateAlerts({
applyUpdatedAlerts({
...alerts,
lastBootPopup: null,
lastBootPopup: undefined,
bootPopup: true,
});
},
Expand Down
37 changes: 25 additions & 12 deletions packages/shared/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export interface AuthContextData {
shouldShowLogin: boolean;
showLogin: ({ trigger, options }: ShowLoginParams) => void;
closeLogin: () => void;
loginState?: LoginState;
loginState?: LoginState | null;
logout: (reason: string) => Promise<void>;
updateUser: (user: LoggedUser) => Promise<void>;
loadingUser?: boolean;
Expand All @@ -75,7 +75,8 @@ export interface AuthContextData {
}

const isExtension = checkIsExtension();
const AuthContext = React.createContext<AuthContextData>(null);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const AuthContext = React.createContext<AuthContextData>(null!);
export const useAuthContext = (): AuthContextData => useContext(AuthContext);
export default AuthContext;

Expand All @@ -93,10 +94,11 @@ export const REGISTRATION_PATH = '/register';
export const logout = async (reason: string): Promise<void> => {
await dispatchLogout(reason);
const params = getQueryParams();
if (params.redirect_uri) {
window.location.replace(params.redirect_uri);
const redirectUri = params.redirect_uri;
if (redirectUri) {
window.location.replace(redirectUri);
} else if (window.location.pathname === REGISTRATION_PATH) {
window.location.replace(process.env.NEXT_PUBLIC_WEBAPP_URL);
window.location.replace(process.env.NEXT_PUBLIC_WEBAPP_URL ?? '/');
}

if (isExtension) {
Expand All @@ -107,9 +109,15 @@ export const logout = async (reason: string): Promise<void> => {
};

export function checkIfGdprCovered(geo?: Boot['geo']): boolean {
const region = geo?.region;

if (!region) {
return true;
}

return (
geo?.continent === Continent.Europe ||
!outsideGdpr.includes(geo?.region) ||
!outsideGdpr.includes(region) ||
isIOSNative()
);
}
Expand Down Expand Up @@ -152,7 +160,7 @@ export const AuthContextProvider = ({
isAndroidApp,
}: AuthContextProviderProps): ReactElement => {
const [loginState, setLoginState] = useState<LoginState | null>(null);
const endUser = user && 'providers' in user ? user : null;
const endUser = user && 'providers' in user ? user : undefined;
const referral = user?.referralId || user?.referrer;
const referralOrigin = user?.referralOrigin;
const router = useRouter();
Expand All @@ -169,10 +177,15 @@ export const AuthContextProvider = ({
logout(LogoutReason.IncomleteOnboarding);
}

const isValidRegion = useMemo(
() => !invalidPlusRegions.includes(geo?.region),
[geo?.region],
);
const isValidRegion = useMemo(() => {
const region = geo?.region;

if (!region) {
return true;
}

return !invalidPlusRegions.includes(region);
}, [geo?.region]);

return (
<AuthContext.Provider
Expand All @@ -185,7 +198,7 @@ export const AuthContextProvider = ({
referralOrigin: loginState?.referralOrigin ?? referralOrigin,
firstVisit: user?.firstVisit,
trackingId: user?.id,
shouldShowLogin: loginState !== null,
shouldShowLogin: !!loginState,
showLogin: useCallback(
({ trigger, options = {} }) => {
const hasCompanion = !!isCompanionActivated();
Expand Down
32 changes: 26 additions & 6 deletions packages/shared/src/contexts/BootProvider.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ beforeEach(() => {
localStorage.clear();
});

const defaultAlerts: Alerts = { filter: true, rankLastSeen: null };
const defaultAlerts: Alerts = { filter: true, rankLastSeen: undefined };

const defaultSettings: RemoteSettings = {
theme: 'bright',
Expand Down Expand Up @@ -162,7 +162,13 @@ const SettingsMock = ({
Sidebar
</button>
<button
onClick={() => setTheme(toTheme)}
onClick={() => {
if (!toTheme) {
return;
}

setTheme(toTheme);
}}
type="button"
data-test-value={themeMode}
>
Expand All @@ -176,14 +182,20 @@ const SettingsMock = ({
Show Weekly Goal widget
</button>
<button
onClick={() => setSpaciness(toSpaciness)}
onClick={() => {
if (!toSpaciness) {
return;
}

setSpaciness(toSpaciness);
}}
type="button"
data-test-value={spaciness}
>
Spaciness
</button>
<button
onClick={() => toggleInsaneMode(toInsaneMode)}
onClick={() => toggleInsaneMode(toInsaneMode ?? false)}
type="button"
data-test-value={insaneMode}
>
Expand Down Expand Up @@ -391,7 +403,13 @@ const AuthMock = ({ updatedUser, loginTrigger }: AuthMockProps) => {
return (
<>
<button
onClick={() => updateUser(updatedUser)}
onClick={() => {
if (!updatedUser) {
return;
}

updateUser(updatedUser);
}}
type="button"
data-test-value={user?.name || 'anonymous'}
>
Expand All @@ -404,7 +422,9 @@ const AuthMock = ({ updatedUser, loginTrigger }: AuthMockProps) => {
Logout
</button>
<button
onClick={() => showLogin({ trigger: loginTrigger })}
onClick={() =>
showLogin({ trigger: loginTrigger ?? AuthTriggers.Comment })
}
type="button"
data-test-value={JSON.stringify(loginState)}
>
Expand Down
Loading