Skip to content
This repository has been archived by the owner on May 7, 2023. It is now read-only.

Commit

Permalink
Handle app loading more seamlessly (#398)
Browse files Browse the repository at this point in the history
Previously, the app somehow blinks when the app starts the first time in the dark mode because of the different background colors. Handle these more seamlessly for a better user experience.

Add [CustomAppLoading] for future changes. Changing the loading screen in the current UI component will affect all other loading screens.

* Expo now supports `useAssets` to load assets with simpler code. Refactored it.
* Handle background color on `react-navigations`.
* Add `useTimeout` hook which is not needed currently but for a utility hook.
  • Loading branch information
hyochan authored May 25, 2021
1 parent 623ddae commit e56166e
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 81 deletions.
63 changes: 16 additions & 47 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
import * as Notifications from 'expo-notifications';
import * as SplashScreen from 'expo-splash-screen';

import {Alert, Image} from 'react-native';
import {AppearanceProvider, useColorScheme} from 'react-native-appearance';
import {LoadingIndicator, ThemeProvider, ThemeType} from 'dooboo-ui';
import React, {
FC,
ReactElement,
ReactNode,
Suspense,
useEffect,
useState,
} from 'react';
import React, {FC, ReactElement, ReactNode, Suspense, useEffect} from 'react';
import {ThemeProvider, ThemeType} from 'dooboo-ui';
import {dark, light} from './theme';

import {ActionSheetProvider} from '@expo/react-native-action-sheet';
import {Alert} from 'react-native';
import AppLoading from 'expo-app-loading';
import {Asset} from 'expo-asset';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {AuthProvider} from './providers/AuthProvider';
import ComponentWrapper from './utils/ComponentWrapper';
import CustomLoadingIndicator from './components/uis/CustomLoadingIndicator';
import {DeviceProvider} from './providers/DeviceProvider';
import Icons from './utils/Icons';
import {ResettableRelayProvider} from './providers/ResettableProvider';
import RootNavigator from './components/navigations/RootStackNavigator';
import {createRelayEnvironment} from './relay';
import {getString} from '../STRINGS';
import {registerForPushNotificationsAsync} from './utils/noti';
import {useAssets} from 'expo-asset';

Notifications.setNotificationHandler({
handleNotification: async () => ({
Expand All @@ -36,27 +30,12 @@ Notifications.setNotificationHandler({
}),
});

function cacheImages(images: (number | string)[]): any[] {
return images.map((image) => {
if (typeof image === 'string') return Image.prefetch(image);
else return Asset.fromModule(image as number).downloadAsync();
});
}

const prepareAutoHide = async (): Promise<void> => {
const preventAutoHide = async (): Promise<void> => {
await SplashScreen.preventAutoHideAsync();
};

function App(): ReactElement {
const [assetLoaded, setAssetLoaded] = useState<boolean>(false);

const loadAssetsAsync = async (): Promise<void> => {
const imageAssets = cacheImages(Icons);

await Promise.all([...imageAssets]);

setAssetLoaded(true);
};
const HackatalkThemeProvider: FC<{children: ReactElement}> = ({children}) => {
const colorScheme = useColorScheme();

const hideSplashScreenThenRegisterNotification = async (): Promise<void> => {
try {
Expand Down Expand Up @@ -86,26 +65,16 @@ function App(): ReactElement {
});
};

const [assets] = useAssets(Icons);

useEffect(() => {
if (!assetLoaded) {
SplashScreen.preventAutoHideAsync();
loadAssetsAsync();
} else hideSplashScreenThenRegisterNotification();
}, [assetLoaded]);
if (!assets) preventAutoHide();
else hideSplashScreenThenRegisterNotification();
}, [assets]);

if (!assetLoaded)
if (!assets)
return <AppLoading autoHideSplash={false} onError={console.warn} />;

return <RootNavigator />;
}

const HackatalkThemeProvider: FC<{children: ReactElement}> = ({children}) => {
const colorScheme = useColorScheme();

useEffect(() => {
prepareAutoHide();
}, []);

return (
<ThemeProvider
customTheme={{
Expand All @@ -131,10 +100,10 @@ function ActionSheetProviderWithChildren(props: {
}

// Add all required providers for App.
const WrappedApp = new ComponentWrapper(App)
const WrappedApp = new ComponentWrapper(RootNavigator)
.wrap(ActionSheetProviderWithChildren, {})
.wrap(AuthProvider, {})
.wrap(Suspense, {fallback: <LoadingIndicator />})
.wrap(Suspense, {fallback: <CustomLoadingIndicator />})
.wrap(ResettableRelayProvider, {createRelayEnvironment})
.wrap(DeviceProvider, {})
.wrap(HackatalkThemeProvider, {})
Expand Down
3 changes: 3 additions & 0 deletions client/src/components/navigations/AuthStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ function AuthNavigator(): ReactElement {
borderBottomWidth: 0,
elevation: 0,
},
cardStyle: {
backgroundColor: theme.background,
},
headerTintColor: theme.text,
}}>
<Stack.Screen
Expand Down
13 changes: 9 additions & 4 deletions client/src/components/navigations/MainStackNavigator/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as Notifications from 'expo-notifications';

import {Channel, User} from '../../../types/graphql';
import {CompositeNavigationProp, useNavigation} from '@react-navigation/native';
import {Image, Platform, TouchableOpacity, View} from 'react-native';
import React, {ReactElement, useEffect, useMemo} from 'react';
Expand Down Expand Up @@ -155,6 +154,9 @@ function MainStackNavigator(): ReactElement {
initialRouteName="MainTab"
screenOptions={{
headerBackTitle: '',
cardStyle: {
backgroundColor: theme.background,
},
}}>
<Stack.Screen
name="MainTab"
Expand Down Expand Up @@ -227,12 +229,15 @@ function MainStackNavigator(): ReactElement {
);
}

function RootNavigator(): ReactElement {
function MainNavigator(): ReactElement {
const {theme} = useTheme();

return (
<View
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: theme.background,
}}>
<StatusBar />
<MainStackNavigator />
Expand All @@ -241,10 +246,10 @@ function RootNavigator(): ReactElement {
);
}

export default function RootNavigatorWrapper(): ReactElement {
export default function MainNavigatorWrapper(): ReactElement {
return (
<ProfileModalProvider>
<RootNavigator />
<MainNavigator />
</ProfileModalProvider>
);
}
31 changes: 17 additions & 14 deletions client/src/components/navigations/RootStackNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as SplashScreen from 'expo-splash-screen';

import AuthStack, {AuthStackParamList} from './AuthStackNavigator';
import {LoadingIndicator, useTheme} from 'dooboo-ui';
import MainStack, {MainStackParamList} from './MainStackNavigator';
import {
NavigationContainer,
Expand All @@ -17,10 +16,12 @@ import {
import {meQuery, useAuthContext} from '../../providers/AuthProvider';

import {AuthProviderMeQuery} from '../../__generated__/AuthProviderMeQuery.graphql';
import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import ImageSlider from '../pages/ImageSlider';
import NotFound from '../pages/NotFound';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import WebView from '../pages/WebView';
import {useTheme} from 'dooboo-ui';

export type RootStackParamList = {
default: undefined;
Expand Down Expand Up @@ -183,26 +184,28 @@ function AuthNavigatorOnly(): React.ReactElement {
}

const RootNavigatorWrapper: FC = () => {
const {theme} = useTheme();
const {user, loadMeQuery, meQueryReference} = useAuthContext();
const {theme} = useTheme();

useEffect(() => {
if (!user) loadMeQuery({});
}, [loadMeQuery, user]);

return (
<Suspense
fallback={
<View style={{flex: 1, backgroundColor: theme.background}}>
<LoadingIndicator />
</View>
}>
{meQueryReference ? (
<RootNavigator queryReference={meQueryReference} />
) : (
<AuthNavigatorOnly />
)}
</Suspense>
<View
style={{
flex: 1,
alignSelf: 'stretch',
backgroundColor: theme.background,
}}>
<Suspense fallback={<CustomLoadingIndicator />}>
{meQueryReference ? (
<RootNavigator queryReference={meQueryReference} />
) : (
<AuthNavigatorOnly />
)}
</Suspense>
</View>
);
};

Expand Down
4 changes: 2 additions & 2 deletions client/src/components/pages/BlockedUser.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {FlatList, ListRenderItem, View} from 'react-native';
import React, {FC, Suspense, useMemo} from 'react';

import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import EmptyListItem from '../uis/EmptyListItem';
import {LoadingIndicator} from 'dooboo-ui';
import type {UserBlockedUsersQuery} from '../../__generated__/UserBlockedUsersQuery.graphql';
import UserListItem from '../uis/UserListItem';
import {blockedUsersQuery} from '../../relay/queries/User';
Expand Down Expand Up @@ -75,7 +75,7 @@ const ContentContainer: FC = () => {
const Page: FC = () => {
return (
<Container>
<Suspense fallback={<LoadingIndicator />}>
<Suspense fallback={<CustomLoadingIndicator />}>
<ContentContainer />
</Suspense>
</Container>
Expand Down
7 changes: 4 additions & 3 deletions client/src/components/pages/ChannelCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
ChannelFindOrCreatePrivateChannelMutationResponse,
} from '../../__generated__/ChannelFindOrCreatePrivateChannelMutation.graphql';
import {IC_CIRCLE_X, IC_NO_IMAGE} from '../../utils/Icons';
import {LoadingIndicator, useTheme} from 'dooboo-ui';
import React, {FC, ReactElement, Suspense, useMemo, useState} from 'react';
import {
graphql,
Expand All @@ -27,6 +26,7 @@ import {
} from 'react-relay';

import {ChannelCreate_friends$key} from '../../__generated__/ChannelCreate_friends.graphql';
import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import ErroView from '../uis/ErrorView';
import {FriendsQuery} from '../../__generated__/FriendsQuery.graphql';
import {MainStackNavigationProps} from '../navigations/MainStackNavigator';
Expand All @@ -40,6 +40,7 @@ import {showAlertForError} from '../../utils/common';
import styled from '@emotion/native';
import useDebounce from '../../hooks/useDebounce';
import {useNavigation} from '@react-navigation/native';
import {useTheme} from 'dooboo-ui';

const ITEM_CNT = 20;

Expand Down Expand Up @@ -369,15 +370,15 @@ const ChannelCreate: FC = () => {
}}
value={searchText}
/>
<Suspense fallback={<LoadingIndicator />}>
<Suspense fallback={<CustomLoadingIndicator />}>
<ContentContainer
scrollY={scrollY}
searchArgs={searchArgs}
selectedUsers={selectedUsers}
setSelectedUsers={setSelectedUsers}
/>
</Suspense>
{isChannelInFlight ? <LoadingIndicator /> : null}
{isChannelInFlight ? <CustomLoadingIndicator /> : null}
</Container>
);
};
Expand Down
7 changes: 4 additions & 3 deletions client/src/components/pages/MainChannel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {Channel, User} from '../../types/graphql';
import type {
ChannelsQuery,
ChannelsQueryResponse,
ChannelsQueryVariables,
} from '../../__generated__/ChannelsQuery.graphql';
import {FlatList, Platform, TouchableOpacity, View} from 'react-native';
import {LoadingIndicator, useTheme} from 'dooboo-ui';
import React, {FC, Suspense, useMemo, useState} from 'react';
import {graphql, useLazyLoadQuery, usePaginationFragment} from 'react-relay';
import useOrientation, {Orientation} from '../../hooks/useOrientation';

import {AdMobBanner} from 'expo-ads-admob';
import {Channel} from '../../types/graphql';
import ChannelListItem from '../uis/ChannelListItem';
import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import EmptyListItem from '../uis/EmptyListItem';
import type {MainChannelComponent_channel$key} from '../../__generated__/MainChannelComponent_channel.graphql';
import {MainStackNavigationProps} from '../navigations/MainStackNavigator';
Expand All @@ -21,6 +21,7 @@ import {channelsQuery} from '../../relay/queries/Channel';
import {getString} from '../../../STRINGS';
import styled from '@emotion/native';
import {useNavigation} from '@react-navigation/native';
import {useTheme} from 'dooboo-ui';

const Container = styled.View`
flex: 1;
Expand Down Expand Up @@ -214,7 +215,7 @@ const Screen: FC = () => {

return (
<Container>
<Suspense fallback={<LoadingIndicator />}>
<Suspense fallback={<CustomLoadingIndicator />}>
<ContentContainer searchArgs={searchArgs} />
</Suspense>
<TouchableOpacity
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/pages/MainFriend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import {FlatList, ListRenderItem} from 'react-native';
import React, {FC, Suspense, useMemo} from 'react';
import {graphql, useLazyLoadQuery, usePaginationFragment} from 'react-relay';

import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import EmptyListItem from '../uis/EmptyListItem';
import {FriendFriendsPaginationQuery} from '../../__generated__/FriendFriendsPaginationQuery.graphql';
import {LoadingIndicator} from 'dooboo-ui';
import {MainFriend_friends$key} from '../../__generated__/MainFriend_friends.graphql';
import {ProfileModal_user$key} from '../../__generated__/ProfileModal_user.graphql';
import UserListItem from '../uis/UserListItem';
Expand Down Expand Up @@ -124,7 +124,7 @@ const Friend: FC = () => {
const Screen: FC = () => {
return (
<Container>
<Suspense fallback={<LoadingIndicator />}>
<Suspense fallback={<CustomLoadingIndicator />}>
<Friend />
</Suspense>
</Container>
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/pages/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import {Button, LoadingIndicator, useTheme} from 'dooboo-ui';
import {Button, useTheme} from 'dooboo-ui';
import {
MainStackNavigationProps,
MainStackParamList,
Expand Down Expand Up @@ -38,6 +38,7 @@ import {
} from '../../utils/ImagePicker';

import {ChannelQuery} from '../../__generated__/ChannelQuery.graphql';
import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import EmptyListItem from '../uis/EmptyListItem';
import GiftedChat from '../uis/GiftedChat';
import {IC_SMILE} from '../../utils/Icons';
Expand Down Expand Up @@ -441,7 +442,7 @@ const MessageScreen: FC = () => {

return (
<Container>
<Suspense fallback={<LoadingIndicator />}>
<Suspense fallback={<CustomLoadingIndicator />}>
<ContentContainer searchArgs={searchArgs} channelId={channelId} />
</Suspense>
</Container>
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/pages/SearchUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import type {
} from '../../__generated__/UserUsersPaginationQuery.graphql';
import {graphql, useLazyLoadQuery, usePaginationFragment} from 'react-relay';

import CustomLoadingIndicator from '../uis/CustomLoadingIndicator';
import EmptyListItem from '../uis/EmptyListItem';
import {FontAwesome} from '@expo/vector-icons';
import {LoadingIndicator} from 'dooboo-ui';
import {MainStackNavigationProps} from '../navigations/MainStackNavigator';
import SearchTextInput from '../uis/SearchTextInput';
import type {SearchUserComponent_user$key} from '../../__generated__/SearchUserComponent_user.graphql';
Expand Down Expand Up @@ -199,7 +199,7 @@ const Screen: FC = () => {
onChangeText={onChangeText}
value={searchText}
/>
<Suspense fallback={<LoadingIndicator />}>
<Suspense fallback={<CustomLoadingIndicator />}>
<ContentContainer scrollY={scrollY} searchArgs={searchArgs} />
</Suspense>
</Container>
Expand Down
Loading

0 comments on commit e56166e

Please sign in to comment.