diff --git a/packages/shared/src/components/ProfileMenu/sections/ExtensionSection.tsx b/packages/shared/src/components/ProfileMenu/sections/ExtensionSection.tsx index 2b33380290..7f2188bb79 100644 --- a/packages/shared/src/components/ProfileMenu/sections/ExtensionSection.tsx +++ b/packages/shared/src/components/ProfileMenu/sections/ExtensionSection.tsx @@ -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 ( <> @@ -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`, @@ -45,3 +41,11 @@ export const ExtensionSection = (): ReactElement | null => { ); }; + +export const ExtensionSection = (): ReactElement | null => { + if (!checkIsExtension()) { + return null; + } + + return ; +}; diff --git a/packages/shared/src/contexts/AlertContext.tsx b/packages/shared/src/contexts/AlertContext.tsx index ab31232653..3126578948 100644 --- a/packages/shared/src/contexts/AlertContext.tsx +++ b/packages/shared/src/contexts/AlertContext.tsx @@ -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, @@ -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; + updateLastReferralReminder?: UseMutateAsyncFunction; + updateLastBootPopup?: UseMutateAsyncFunction; + updateHasSeenOpportunity?: UseMutateAsyncFunction< unknown, unknown, - AlertsUpdate, - () => Promise + boolean | void >; - updateLastReferralReminder?: UseMutateAsyncFunction; - updateLastBootPopup?: UseMutateAsyncFunction; - updateHasSeenOpportunity?: UseMutateAsyncFunction; - clearOpportunityAlert?: UseMutateAsyncFunction; + clearOpportunityAlert?: UseMutateAsyncFunction; } -const AlertContext = React.createContext({ - alerts: ALERT_DEFAULTS, - loadedAlerts: false, -}); +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +const AlertContext = React.createContext(null!); export interface AlertContextProviderProps { children: ReactNode; @@ -60,6 +57,10 @@ export const AlertContextProvider = ({ isFetched, updateAlerts, }: AlertContextProviderProps): ReactElement => { + const applyUpdatedAlerts = (updatedAlerts: Alerts): void => { + updateAlerts?.(updatedAlerts); + }; + const { mutateAsync: updateRemoteAlerts } = useMutation< unknown, unknown, @@ -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).reduce( (values, key) => ({ ...values, [key]: alerts[key] }), - {}, + {} as Alerts, ); - updateAlerts({ ...alerts, ...rollback }); + applyUpdatedAlerts({ ...alerts, ...rollback }); }, }); @@ -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, }); }, diff --git a/packages/shared/src/contexts/AuthContext.tsx b/packages/shared/src/contexts/AuthContext.tsx index b9386a03d0..e3df5d2ce4 100644 --- a/packages/shared/src/contexts/AuthContext.tsx +++ b/packages/shared/src/contexts/AuthContext.tsx @@ -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; updateUser: (user: LoggedUser) => Promise; loadingUser?: boolean; @@ -75,7 +75,8 @@ export interface AuthContextData { } const isExtension = checkIsExtension(); -const AuthContext = React.createContext(null); +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +const AuthContext = React.createContext(null!); export const useAuthContext = (): AuthContextData => useContext(AuthContext); export default AuthContext; @@ -93,10 +94,11 @@ export const REGISTRATION_PATH = '/register'; export const logout = async (reason: string): Promise => { 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) { @@ -107,9 +109,15 @@ export const logout = async (reason: string): Promise => { }; 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() ); } @@ -152,7 +160,7 @@ export const AuthContextProvider = ({ isAndroidApp, }: AuthContextProviderProps): ReactElement => { const [loginState, setLoginState] = useState(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(); @@ -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 ( { const hasCompanion = !!isCompanionActivated(); diff --git a/packages/shared/src/contexts/BootProvider.spec.tsx b/packages/shared/src/contexts/BootProvider.spec.tsx index aba2441505..7bdee76c37 100644 --- a/packages/shared/src/contexts/BootProvider.spec.tsx +++ b/packages/shared/src/contexts/BootProvider.spec.tsx @@ -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', @@ -162,7 +162,13 @@ const SettingsMock = ({ Sidebar