Skip to content

Commit

Permalink
Maintenance mode improvements (#2367)
Browse files Browse the repository at this point in the history
* maintenance stuff

* remove unnecessary check

* add bottom sheet for maintenance notice

* fix alert icon stroke width

---------

Co-authored-by: kaito <80802871+kaitoo1@users.noreply.github.com>
  • Loading branch information
Robinnnnn and kaitoo1 authored Mar 14, 2024
1 parent a8ab5df commit 92983a6
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 217 deletions.
116 changes: 51 additions & 65 deletions apps/mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ import { MobileErrorReportingProvider } from '~/contexts/MobileErrorReportingPro
import { createRelayEnvironment } from '~/contexts/relay/RelayProvider';
import { env } from '~/env/runtime';
import { RootStackNavigator } from '~/navigation/RootStackNavigator';
import MaintenanceStatusProvider from '~/shared/contexts/MaintenanceStatusContext';
import { ReportingErrorBoundary } from '~/shared/errors/ReportingErrorBoundary';

import { DevMenuItems } from './components/DevMenuItems';
import { LoadingView } from './components/LoadingView';
import { CheckMaintenanceOnAppForeground, MaintenanceScreen } from './components/MaintenanceScreen';
import SearchProvider from './components/Search/SearchContext';
import { Typography } from './components/Typography';
import BottomSheetModalProvider from './contexts/BottomSheetModalContext';
import ManageWalletProvider from './contexts/ManageWalletContext';
import SyncTokensProvider from './contexts/SyncTokensContext';
import ToastProvider from './contexts/ToastContext';
import { TokenStateManagerProvider } from './contexts/TokenStateManagerContext';
import { magic } from './magic';
import { useCacheIntroVideo } from './screens/Onboarding/useCacheIntroVideo';
import { useSanityMaintenanceCheckMobile } from './utils/useSanityMaintenanceCheckMobile';

SplashScreen.preventAutoHideAsync();

Expand Down Expand Up @@ -115,82 +115,68 @@ export default function App() {
[colorScheme, colorSchemeLoaded]
);

// NOTE: this is deprecated and should use shared/MaintenanceStatusContext instead
const { maintenanceCheckLoadedOrError, maintenanceModeResponse } =
useSanityMaintenanceCheckMobile();

useEffect(
function markTheAppAsReadyWhenTheFontsAndColorSchemeHaveLoaded() {
if (fontsLoaded && colorSchemeLoaded && maintenanceCheckLoadedOrError) {
if (fontsLoaded && colorSchemeLoaded) {
SplashScreen.hideAsync();
}
},
[colorSchemeLoaded, fontsLoaded, maintenanceCheckLoadedOrError]
[colorSchemeLoaded, fontsLoaded]
);

if (!fontsLoaded || !colorSchemeLoaded || !maintenanceCheckLoadedOrError || !introVideoLoaded) {
if (!fontsLoaded || !colorSchemeLoaded || !introVideoLoaded) {
return null;
}

if (maintenanceModeResponse?.isActive) {
return (
<View className="flex-1 bg-white dark:bg-black-900 flex justify-center items-center p-6">
<Typography className="text-l mb-1" font={{ family: 'ABCDiatype', weight: 'Bold' }}>
Maintenance in Progress
</Typography>
<Typography
className="text-l text-center leading-6"
font={{ family: 'ABCDiatype', weight: 'Regular' }}
>
{maintenanceModeResponse?.message}
</Typography>
</View>
);
}

return (
<View className="flex-1 bg-white dark:bg-black-900">
<ReportingErrorBoundary fallback={<LoadingView />}>
<RelayEnvironmentProvider environment={relayEnvironment}>
<SWRConfig>
<Suspense fallback={<LoadingView />}>
<PrivyProvider appId={env.PRIVY_APP_ID}>
<MobileAnalyticsProvider>
<MobileErrorReportingProvider>
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaProvider>
<magic.Relayer />
<SearchProvider>
<NavigationContainer ref={navigationRef}>
<ToastProvider>
<TokenStateManagerProvider>
<PortalProvider>
<BottomSheetModalProvider>
<SyncTokensProvider>
<ManageWalletProvider>
{/* Register the user's push token if one exists (does not prompt the user) */}
<NotificationRegistrar />
<DevMenuItems />
<DeepLinkRegistrar />
<RootStackNavigator
navigationContainerRef={navigationRef}
/>
</ManageWalletProvider>
</SyncTokensProvider>
</BottomSheetModalProvider>
</PortalProvider>
</TokenStateManagerProvider>
</ToastProvider>
</NavigationContainer>
</SearchProvider>
</SafeAreaProvider>
</GestureHandlerRootView>
</MobileErrorReportingProvider>
</MobileAnalyticsProvider>
</PrivyProvider>
</Suspense>
</SWRConfig>
</RelayEnvironmentProvider>
<MaintenanceStatusProvider
sanityProjectId={env.EXPO_PUBLIC_SANITY_PROJECT_ID}
MaintenancePageComponent={<MaintenanceScreen />}
MaintenanceChecker={<CheckMaintenanceOnAppForeground />}
>
<RelayEnvironmentProvider environment={relayEnvironment}>
<SWRConfig>
<Suspense fallback={<LoadingView />}>
<PrivyProvider appId={env.PRIVY_APP_ID}>
<MobileAnalyticsProvider>
<MobileErrorReportingProvider>
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaProvider>
<magic.Relayer />
<SearchProvider>
<NavigationContainer ref={navigationRef}>
<ToastProvider>
<TokenStateManagerProvider>
<PortalProvider>
<BottomSheetModalProvider>
<SyncTokensProvider>
<ManageWalletProvider>
{/* Register the user's push token if one exists (does not prompt the user) */}
<NotificationRegistrar />
<DevMenuItems />
<DeepLinkRegistrar />
<RootStackNavigator
navigationContainerRef={navigationRef}
/>
</ManageWalletProvider>
</SyncTokensProvider>
</BottomSheetModalProvider>
</PortalProvider>
</TokenStateManagerProvider>
</ToastProvider>
</NavigationContainer>
</SearchProvider>
</SafeAreaProvider>
</GestureHandlerRootView>
</MobileErrorReportingProvider>
</MobileAnalyticsProvider>
</PrivyProvider>
</Suspense>
</SWRConfig>
</RelayEnvironmentProvider>
</MaintenanceStatusProvider>
</ReportingErrorBoundary>
</View>
);
Expand Down
107 changes: 107 additions & 0 deletions apps/mobile/src/components/MaintenanceScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useCallback, useEffect, useState } from 'react';
import { View } from 'react-native';
import { contexts } from 'shared/analytics/constants';
import {
MaintenanceContent,
useMaintenanceContext,
} from 'shared/contexts/MaintenanceStatusContext';
import { AlertIcon } from 'src/icons/AlertIcon';
import { useEffectOnAppForeground } from 'src/utils/useEffectOnAppForeground';

import { useBottomSheetModalActions } from '~/contexts/BottomSheetModalContext';

import { Button } from './Button';
import { BaseM, TitleL } from './Text';
import { Typography } from './Typography';

export function MaintenanceScreen() {
const { currentlyActiveMaintenanceNoticeContent } = useMaintenanceContext();

return (
<View className="flex-1 bg-white dark:bg-black-900 flex justify-center items-center p-6">
<Typography className="text-l mb-1" font={{ family: 'ABCDiatype', weight: 'Bold' }}>
Maintenance in Progress
</Typography>
<Typography
className="text-l text-center leading-6"
font={{ family: 'ABCDiatype', weight: 'Regular' }}
>
{currentlyActiveMaintenanceNoticeContent?.message}
</Typography>
</View>
);
}

export function CheckMaintenanceOnAppForeground() {
const { fetchMaintenanceModeStatus } = useMaintenanceContext();

useEffectOnAppForeground(fetchMaintenanceModeStatus);

return <></>;
}

export function MaintenanceNoticeBottomSheet({ onClose }: { onClose: () => void }) {
const { upcomingMaintenanceNoticeContent } = useMaintenanceContext();

const handleContinuePress = useCallback(() => {
onClose();
}, [onClose]);

return (
<View className="flex items-center space-y-2 ">
<View className="bg-offWhite rounded-full p-3">
<AlertIcon color="#000000" width={40} height={40} />
</View>
<TitleL classNameOverride="m-4">Upcoming Maintenance</TitleL>
<BaseM classNameOverride="text-center mb-4">
{upcomingMaintenanceNoticeContent?.message}
</BaseM>
<Button
className="w-full "
containerClassName="rounded-none"
text="continue"
eventElementId="Maintenance Notice Continue Button"
eventName="Pressed Maintenance Notice Continue Button"
eventContext={contexts.Maintenance}
onPress={handleContinuePress}
/>
</View>
);
}

export function MaintenanceNoticeBottomSheetWrapper({
noticeContent,
}: {
noticeContent: MaintenanceContent;
}) {
const [hasSeenNotice, setHasSeenNotice] = useState('loading');

const { showBottomSheetModal, hideBottomSheetModal } = useBottomSheetModalActions();
const handleBottomSheetClose = useCallback(() => {
AsyncStorage.setItem(noticeContent.id, 'true');
}, [noticeContent.id]);

useEffect(() => {
const getNoticeStatus = async () => {
try {
const seen = await AsyncStorage.getItem(noticeContent.id);

setHasSeenNotice(seen === 'true' ? 'true' : 'false');
} catch (error) {}
};

getNoticeStatus();
});

useEffect(() => {
if (hasSeenNotice === 'false') {
showBottomSheetModal({
content: <MaintenanceNoticeBottomSheet onClose={hideBottomSheetModal} />,
onDismiss: handleBottomSheetClose,
});
}
}, [handleBottomSheetClose, hasSeenNotice, hideBottomSheetModal, showBottomSheetModal]);

return null;
}
21 changes: 20 additions & 1 deletion apps/mobile/src/components/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,35 @@ export const TitleXS = ({ children }: { children: React.ReactNode }) => {
);
};

export const TitleL = ({
children,
classNameOverride,
}: {
children: React.ReactNode;
classNameOverride: string;
}) => {
return (
<Typography
className={`text-black-900 dark:text-white text-lg leading-6 ${classNameOverride}`}
font={{ family: 'ABCDiatype', weight: 'Bold' }}
>
{children}
</Typography>
);
};

export const BaseM = ({
children,
weight = 'Regular',
classNameOverride,
}: {
children: React.ReactNode;
weight?: Weight;
classNameOverride?: string;
}) => {
return (
<Typography
className="text-black-900 dark:text-white text-sm leading-5"
className={`text-black-900 dark:text-white text-sm leading-5 ${classNameOverride}`}
font={{ family: 'ABCDiatype', weight }}
>
{children}
Expand Down
22 changes: 15 additions & 7 deletions apps/mobile/src/contexts/BottomSheetModalContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,30 @@ type BottomSheetModalProviderProps = {

type BottomSheetModal = {
content: React.ReactNode;
onDismiss?: () => void;
};

function BottomSheetModalProvider({ children }: BottomSheetModalProviderProps) {
const [bottomSheetModalContent, setBottomSheetModalContent] = useState<BottomSheetModal>();
const [bottomSheetModal, setBottomSheetModal] = useState<BottomSheetModal>();
const bottomSheetModalRef = useRef<GalleryBottomSheetModalType | null>(null);

const showBottomSheetModal = useCallback((modal: BottomSheetModal) => {
setBottomSheetModalContent(modal);
setBottomSheetModal(modal);
}, []);

const hideBottomSheetModal = useCallback(() => {
bottomSheetModalRef.current?.dismiss();
// delay clearing the content to allow the modal to animate out
setTimeout(() => setBottomSheetModalContent(undefined), 300);
setTimeout(() => setBottomSheetModal(undefined), 300);
}, []);

const handleDismissBottomSheetModal = useCallback(() => {
if (bottomSheetModal?.onDismiss) {
bottomSheetModal.onDismiss();
}
hideBottomSheetModal();
}, [bottomSheetModal, hideBottomSheetModal]);

const { bottom } = useSafeAreaPadding(); // Use this for handling safe area, if necessary

const { animatedHandleHeight, animatedSnapPoints, animatedContentHeight, handleContentLayout } =
Expand All @@ -88,18 +96,18 @@ function BottomSheetModalProvider({ children }: BottomSheetModalProviderProps) {
// immediately present the modal when content is set
useEffect(() => {
bottomSheetModalRef?.current?.present();
}, [bottomSheetModalContent]);
}, [bottomSheetModal]);

return (
<GorhomBottomSheetModalProvider>
<BottomSheetModalActionsContext.Provider value={actions}>
{children}
{bottomSheetModalContent && (
{bottomSheetModal && (
<GalleryBottomSheetModal
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
contentHeight={animatedContentHeight}
onDismiss={hideBottomSheetModal}
onDismiss={handleDismissBottomSheetModal}
index={0}
ref={bottomSheetModalRef}
>
Expand All @@ -108,7 +116,7 @@ function BottomSheetModalProvider({ children }: BottomSheetModalProviderProps) {
style={{ paddingBottom: bottom }}
className="p-4 flex flex-col space-y-6"
>
{bottomSheetModalContent.content}
{bottomSheetModal.content}
</View>
</GalleryBottomSheetModal>
)}
Expand Down
13 changes: 11 additions & 2 deletions apps/mobile/src/icons/AlertIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import Svg, { Path, SvgProps } from 'react-native-svg';
export function AlertIcon(props: SvgProps) {
const color = props.color ?? '#F00000';
return (
<Svg width={16} height={16} fill="none" {...props}>
<Path stroke="#F00000" d="M8 6.333V10M8 11.667V11M8 2l6.667 11.333H1.334L8.001 2Z" />
<Svg
width={props.width || 16}
height={props.height || 16}
viewBox="0 0 40 40"
fill="none"
{...props}
>
<Path d="M20 15.833V24.9997" stroke={color} strokeWidth="2" />
<Path d="M20 29.1667V27.5" stroke={color} strokeWidth="2" />
<Path d="M20.0002 5L36.6668 33.3333H3.3335L20.0002 5Z" stroke={color} strokeWidth="2" />
</Svg>
);
}
Loading

0 comments on commit 92983a6

Please sign in to comment.