Files
mattermost-mobile/app/screens/home/channel_list/channel_list.tsx
Daniel Espino García 9f84ab79ce Only call app entry on websocket reconnect (#7065)
* Only call app entry on websocket reconnect

* Handle notification on its own entry and run app entry on websocket initialization

* Fix notification entry issues

* Fix login entry and add retry on entry failure

* feedback review

* Put back handleEntryAfterLoadNavigation before the batching

---------

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-02-23 10:11:34 +01:00

198 lines
6.7 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useManagedConfig} from '@mattermost/react-native-emm';
import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native';
import React, {useCallback, useEffect} from 'react';
import {useIntl} from 'react-intl';
import {BackHandler, DeviceEventEmitter, StyleSheet, ToastAndroid, View} from 'react-native';
import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
import AnnouncementBanner from '@components/announcement_banner';
import ConnectionBanner from '@components/connection_banner';
import TeamSidebar from '@components/team_sidebar';
import {Navigation as NavigationConstants, Screens} from '@constants';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {resetToTeams, openToS} from '@screens/navigation';
import NavigationStore from '@store/navigation_store';
import {isMainActivity} from '@utils/helpers';
import {tryRunAppReview} from '@utils/reviews';
import {addSentryContext} from '@utils/sentry';
import AdditionalTabletView from './additional_tablet_view';
import CategoriesList from './categories_list';
import Servers from './servers';
import type {LaunchType} from '@typings/launch';
type ChannelProps = {
channelsCount: number;
isCRTEnabled: boolean;
teamsCount: number;
isLicensed: boolean;
showToS: boolean;
launchType: LaunchType;
coldStart?: boolean;
};
const edges: Edge[] = ['bottom', 'left', 'right'];
const styles = StyleSheet.create({
content: {
flex: 1,
flexDirection: 'row',
},
flex: {
flex: 1,
},
});
let backPressedCount = 0;
let backPressTimeout: NodeJS.Timeout|undefined;
// This is needed since the Database Provider is recreating this component
// when the database is changed (couldn't find exactly why), re-triggering
// the effect. This makes sure the rate logic is only handle on the first
// run. Most of the normal users won't see this issue, but on edge times
// (near the time you will see the rate dialog) will show when switching
// servers.
let hasRendered = false;
const ChannelListScreen = (props: ChannelProps) => {
const theme = useTheme();
const managedConfig = useManagedConfig<ManagedConfig>();
const intl = useIntl();
const isTablet = useIsTablet();
const route = useRoute();
const isFocused = useIsFocused();
const navigation = useNavigation();
const insets = useSafeAreaInsets();
const serverUrl = useServerUrl();
const params = route.params as {direction: string};
const canAddOtherServers = managedConfig?.allowOtherServers !== 'false';
const handleBackPress = useCallback(() => {
const isHomeScreen = NavigationStore.getVisibleScreen() === Screens.HOME;
const homeTab = NavigationStore.getVisibleTab() === Screens.HOME;
const focused = navigation.isFocused() && isHomeScreen && homeTab;
if (isMainActivity()) {
if (!backPressedCount && focused) {
backPressedCount++;
ToastAndroid.show(intl.formatMessage({
id: 'mobile.android.back_handler_exit',
defaultMessage: 'Press back again to exit',
}), ToastAndroid.SHORT);
if (backPressTimeout) {
clearTimeout(backPressTimeout);
}
backPressTimeout = setTimeout(() => {
clearTimeout(backPressTimeout!);
backPressedCount = 0;
}, 2000);
return true;
} else if (isHomeScreen && !homeTab) {
DeviceEventEmitter.emit(NavigationConstants.NAVIGATION_HOME);
return true;
}
}
return false;
}, [intl]);
const animated = useAnimatedStyle(() => {
if (!isFocused) {
let initial = 0;
if (params?.direction) {
initial = -25;
}
return {
opacity: withTiming(0, {duration: 150}),
transform: [{translateX: withTiming(initial, {duration: 150})}],
};
}
return {
opacity: withTiming(1, {duration: 150}),
transform: [{translateX: withTiming(0, {duration: 150})}],
};
}, [isFocused, params]);
const top = useAnimatedStyle(() => {
return {height: insets.top, backgroundColor: theme.sidebarBg};
}, [theme, insets.top]);
useEffect(() => {
if (!props.teamsCount) {
resetToTeams();
}
}, [Boolean(props.teamsCount)]);
useEffect(() => {
const back = BackHandler.addEventListener('hardwareBackPress', handleBackPress);
return () => back.remove();
}, [handleBackPress]);
useEffect(() => {
addSentryContext(serverUrl);
}, [serverUrl]);
useEffect(() => {
if (props.showToS && !NavigationStore.isToSOpen()) {
openToS();
}
}, [props.showToS]);
// Init the rate app. Only run the effect on the first render if ToS is not open
useEffect(() => {
if (hasRendered) {
return;
}
hasRendered = true;
if (!NavigationStore.isToSOpen()) {
tryRunAppReview(props.launchType, props.coldStart);
}
}, []);
return (
<>
<Animated.View style={top}/>
<SafeAreaView
style={styles.flex}
edges={edges}
testID='channel_list.screen'
>
<ConnectionBanner/>
{props.isLicensed &&
<AnnouncementBanner/>
}
<View style={styles.content}>
{canAddOtherServers && <Servers/>}
<Animated.View
style={[styles.content, animated]}
>
<TeamSidebar
iconPad={canAddOtherServers}
teamsCount={props.teamsCount}
/>
<CategoriesList
iconPad={canAddOtherServers && props.teamsCount <= 1}
isCRTEnabled={props.isCRTEnabled}
teamsCount={props.teamsCount}
channelsCount={props.channelsCount}
/>
{isTablet &&
<AdditionalTabletView/>
}
</Animated.View>
</View>
</SafeAreaView>
</>
);
};
export default ChannelListScreen;