diff --git a/app/components/channel_list/__snapshots__/index.test.tsx.snap b/app/components/channel_list/__snapshots__/index.test.tsx.snap index 777c4c52a..6cc69754e 100644 --- a/app/components/channel_list/__snapshots__/index.test.tsx.snap +++ b/app/components/channel_list/__snapshots__/index.test.tsx.snap @@ -193,587 +193,147 @@ exports[`components/channel_list should match snapshot 1`] = ` style={ Object { "alignItems": "center", - "backgroundColor": "rgba(255,255,255,0.12)", - "borderRadius": 8, "flex": 1, - "flexDirection": "row", - "height": 40, - "justifyContent": "flex-start", - "marginVertical": 20, - "maxHeight": 40, - "padding": 8, - "width": "100%", + "justifyContent": "center", + "padding": 20, } } > - - + + + - - + Couldn't load + + - + > + There was a problem loading content for this server. + + - - - - - - Threads - - - - - - - - - MY FIRST CATEGORY - - - + - - - - - Just a channel - - - - - - - - A Highlighted Channel!!! - - - - - - - - And a longer channel name. - - - - - - - - - ANOTHER CAT - - - - - - - - Just a channel - - - - - - - - A Highlighted Channel!!! - - - - - - - - And a longer channel name. - - - - + Retry + - + `; diff --git a/app/components/channel_list/header/header.tsx b/app/components/channel_list/header/header.tsx index 00b8fc829..ac5e92ccc 100644 --- a/app/components/channel_list/header/header.tsx +++ b/app/components/channel_list/header/header.tsx @@ -67,6 +67,7 @@ const ChannelListHeader = ({displayName, iconPad}: Props) => { return ( + {Boolean(displayName) && @@ -86,6 +87,7 @@ const ChannelListHeader = ({displayName, iconPad}: Props) => { /> + } {serverDisplayName} diff --git a/app/components/channel_list/header/index.ts b/app/components/channel_list/header/index.ts index 03d895392..fd8f25167 100644 --- a/app/components/channel_list/header/index.ts +++ b/app/components/channel_list/header/index.ts @@ -3,7 +3,7 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; +import {catchError, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; @@ -15,9 +15,10 @@ import type {WithDatabaseArgs} from '@typings/database/database'; import type SystemModel from '@typings/database/models/servers/system'; import type TeamModel from '@typings/database/models/servers/team'; -const withCurrentTeam = withObservables([], ({database}: WithDatabaseArgs) => { +const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const team = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( switchMap((id) => database.get(TEAM).findAndObserve(id.value)), + catchError(() => of$({displayName: ''})), ); return { @@ -27,4 +28,4 @@ const withCurrentTeam = withObservables([], ({database}: WithDatabaseArgs) => { }; }); -export default withDatabase(withCurrentTeam(ChannelListHeader)); +export default withDatabase(enhanced(ChannelListHeader)); diff --git a/app/components/channel_list/index.tsx b/app/components/channel_list/index.tsx index 1afce0e96..c1e2b67f5 100644 --- a/app/components/channel_list/index.tsx +++ b/app/components/channel_list/index.tsx @@ -11,7 +11,8 @@ import {makeStyleSheetFromTheme} from '@utils/theme'; import Categories from './categories'; import ChannelListHeader from './header'; -import LoadingError from './loading_error'; +import LoadChannelsError from './load_channels_error'; +import LoadTeamsError from './load_teams_error'; import SearchField from './search'; // import Loading from '@components/loading'; @@ -38,12 +39,13 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ })); type ChannelListProps = { + currentTeamId?: string; iconPad?: boolean; isTablet: boolean; teamsCount: number; } -const ChannelList = ({iconPad, isTablet, teamsCount}: ChannelListProps) => { +const ChannelList = ({currentTeamId, iconPad, isTablet, teamsCount}: ChannelListProps) => { const theme = useTheme(); const styles = getStyleSheet(theme); const tabletWidth = useSharedValue(TABLET_SIDEBAR_WIDTH); @@ -64,6 +66,20 @@ const ChannelList = ({iconPad, isTablet, teamsCount}: ChannelListProps) => { }, [isTablet, teamsCount]); const [showCats, setShowCats] = useState(true); + let content; + if (!currentTeamId) { + content = (); + } else if (showCats) { + content = ( + <> + + + + ); + } else { + content = (); + } + return ( setShowCats(!showCats)}> @@ -71,15 +87,7 @@ const ChannelList = ({iconPad, isTablet, teamsCount}: ChannelListProps) => { iconPad={iconPad} /> - - {showCats && ( - <> - - - - )} - {/* */} - {!showCats && ()} + {content} ); }; diff --git a/app/components/channel_list/load_channels_error/index.ts b/app/components/channel_list/load_channels_error/index.ts new file mode 100644 index 000000000..6f99af810 --- /dev/null +++ b/app/components/channel_list/load_channels_error/index.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import withObservables from '@nozbe/with-observables'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {MM_TABLES} from '@constants/database'; + +const {SERVER: {TEAM}} = MM_TABLES; +import LoadChannelsError from './load_channel_error'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type TeamModel from '@typings/database/models/servers/team'; + +const enhanced = withObservables(['teamId'], ({database, teamId}: {teamId: string} & WithDatabaseArgs) => { + const team = database.get(TEAM).findAndObserve(teamId); + + return { + teamDisplayName: team.pipe( + switchMap((t) => of$(t.displayName)), + ), + }; +}); + +export default withDatabase(enhanced(LoadChannelsError)); diff --git a/app/components/channel_list/load_channels_error/load_channel_error.tsx b/app/components/channel_list/load_channels_error/load_channel_error.tsx new file mode 100644 index 000000000..d1a9c8cdc --- /dev/null +++ b/app/components/channel_list/load_channels_error/load_channel_error.tsx @@ -0,0 +1,40 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import {retryInitialChannel} from '@actions/remote/retry'; +import LoadingError from '@components/channel_list/loading_error'; +import {useServerUrl} from '@context/server'; + +type Props = { + teamDisplayName: string; + teamId: string; +} + +const LoadChannelsError = ({teamDisplayName, teamId}: Props) => { + const {formatMessage} = useIntl(); + const serverUrl = useServerUrl(); + const [loading, setLoading] = useState(false); + + const onRetryTeams = useCallback(async () => { + setLoading(true); + const {error} = await retryInitialChannel(serverUrl, teamId); + + if (error) { + setLoading(false); + } + }, [teamId]); + + return ( + + ); +}; + +export default LoadChannelsError; diff --git a/app/components/channel_list/load_teams_error/index.tsx b/app/components/channel_list/load_teams_error/index.tsx new file mode 100644 index 000000000..d81f286c7 --- /dev/null +++ b/app/components/channel_list/load_teams_error/index.tsx @@ -0,0 +1,36 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import {retryInitialTeamAndChannel} from '@actions/remote/retry'; +import LoadingError from '@components/channel_list/loading_error'; +import {useServerDisplayName, useServerUrl} from '@context/server'; + +const LoadTeamsError = () => { + const {formatMessage} = useIntl(); + const serverUrl = useServerUrl(); + const serverName = useServerDisplayName(); + const [loading, setLoading] = useState(false); + + const onRetryTeams = useCallback(async () => { + setLoading(true); + const {error} = await retryInitialTeamAndChannel(serverUrl); + + if (error) { + setLoading(false); + } + }, []); + + return ( + + ); +}; + +export default LoadTeamsError; diff --git a/app/components/channel_list/loading_error/__snapshots__/index.test.tsx.snap b/app/components/channel_list/loading_error/__snapshots__/index.test.tsx.snap index c6df4de1d..f9fb459ee 100644 --- a/app/components/channel_list/loading_error/__snapshots__/index.test.tsx.snap +++ b/app/components/channel_list/loading_error/__snapshots__/index.test.tsx.snap @@ -51,7 +51,7 @@ exports[`Loading Error should match snapshot 1`] = ` ] } > - Couldn’t load Staff + Error title - There was a problem loading the content for this team. + Error description { const {toJSON} = renderWithIntlAndTheme( - , + true} + title='Error title' + />, ); expect(toJSON()).toMatchSnapshot(); diff --git a/app/components/channel_list/loading_error/index.tsx b/app/components/channel_list/loading_error/index.tsx index d928d4b8f..ab46c0312 100644 --- a/app/components/channel_list/loading_error/index.tsx +++ b/app/components/channel_list/loading_error/index.tsx @@ -1,15 +1,24 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; + +import React, {useMemo} from 'react'; import {Text, View} from 'react-native'; import CompassIcon from '@components/compass_icon'; +import Loading from '@components/loading'; import TouchableWithFeedback from '@components/touchable_with_feedback'; import {useTheme} from '@context/theme'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; +type Props = { + loading: boolean; + message: string; + onRetry: () => void; + title: string; +} + const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ container: { flex: 1, @@ -42,9 +51,21 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ }, })); -const LoadingError = () => { +const LoadingError = ({loading, message, onRetry, title}: Props) => { const theme = useTheme(); const styles = getStyleSheet(theme); + const buttonStyle = useMemo(() => { + return [{marginTop: 24}, buttonBackgroundStyle(theme, 'lg', 'primary', 'inverted')]; + }, [theme]); + + if (loading) { + return ( + + ); + } return ( @@ -55,12 +76,15 @@ const LoadingError = () => { /> - {'Couldn’t load Staff'} + {title} - {'There was a problem loading the content for this team.'} + {message} - + {'Retry'} diff --git a/app/components/failed_action/cloud_svg.tsx b/app/components/failed_action/cloud_svg.tsx deleted file mode 100644 index 586ca25af..000000000 --- a/app/components/failed_action/cloud_svg.tsx +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {View} from 'react-native'; -import Svg, {Path} from 'react-native-svg'; - -type CloudSvgProps = { - color: string; - height: number; - width: number; -} - -const CloudSvg = ({color, height, width}: CloudSvgProps) => { - return ( - - - - - - ); -}; - -export default CloudSvg; diff --git a/app/components/failed_action/index.tsx b/app/components/failed_action/index.tsx deleted file mode 100644 index 2ad74f2bd..000000000 --- a/app/components/failed_action/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {useIntl} from 'react-intl'; -import {Text, View} from 'react-native'; -import Button from 'react-native-button'; - -import {useTheme} from '@context/theme'; -import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; - -import CloudSvg from './cloud_svg'; - -type FailedActionProps = { - action?: string; - message: string; - title: string; - onAction: () => void; -} - -const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { - return { - container: { - alignItems: 'center', - flex: 1, - justifyContent: 'center', - paddingHorizontal: 20, - paddingBottom: 15, - }, - title: { - color: changeOpacity(theme.centerChannelColor, 0.8), - fontSize: 20, - fontFamily: 'OpenSans-SemiBold', - marginBottom: 15, - marginTop: 10, - }, - description: { - color: changeOpacity(theme.centerChannelColor, 0.4), - fontSize: 17, - lineHeight: 25, - textAlign: 'center', - }, - link: { - color: theme.buttonColor, - fontSize: 15, - }, - buttonContainer: { - backgroundColor: theme.buttonBg, - borderRadius: 5, - height: 42, - justifyContent: 'center', - marginTop: 20, - paddingHorizontal: 12, - }, - }; -}); - -const FailedAction = ({action, message, title, onAction}: FailedActionProps) => { - const intl = useIntl(); - const theme = useTheme(); - const style = getStyleFromTheme(theme); - - const text = action || intl.formatMessage({id: 'failed_action.try_again', defaultMessage: 'Try again'}); - - return ( - - - - {title} - - - {message} - - - - ); -}; - -export default FailedAction; diff --git a/app/screens/channel/channel.tsx b/app/screens/channel/channel.tsx index d826c75e6..6a6083e1a 100644 --- a/app/screens/channel/channel.tsx +++ b/app/screens/channel/channel.tsx @@ -16,8 +16,6 @@ import {popTopScreen} from '@screens/navigation'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import ChannelPostList from './channel_post_list'; -import FailedChannels from './failed_channels'; -import FailedTeams from './failed_teams'; import OtherMentionsBadge from './other_mentions_badge'; import type {HeaderRightButton} from '@components/navigation_header/header'; @@ -97,14 +95,6 @@ const Channel = ({channelId, componentId, displayName, isOwnDirectMessage, membe console.log('Title Press go to Channel Info', displayName); }, [channelId]); - if (!teamId) { - return ; - } - - if (!channelId) { - return ; - } - let title = displayName; if (isOwnDirectMessage) { title = formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName}); diff --git a/app/screens/channel/failed_channels/index.tsx b/app/screens/channel/failed_channels/index.tsx deleted file mode 100644 index 2d5eb7f46..000000000 --- a/app/screens/channel/failed_channels/index.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; -import withObservables from '@nozbe/with-observables'; -import React, {useCallback, useState} from 'react'; -import {useIntl} from 'react-intl'; -import {StyleSheet} from 'react-native'; - -import {retryInitialChannel} from '@actions/remote/retry'; -import FailedAction from '@components/failed_action'; -import Loading from '@components/loading'; -import {MM_TABLES} from '@constants/database'; -import {useServerUrl} from '@context/server'; -import {useTheme} from '@context/theme'; - -import type {WithDatabaseArgs} from '@typings/database/database'; -import type TeamModel from '@typings/database/models/servers/team'; - -type FailedChannelsProps = { - team: TeamModel; -} - -const style = StyleSheet.create({ - loadingContainer: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, -}); - -const FailedChannels = ({team}: FailedChannelsProps) => { - const intl = useIntl(); - const serverUrl = useServerUrl(); - const theme = useTheme(); - const [loading, setLoading] = useState(false); - - const title = intl.formatMessage({id: 'failed_action.something_wrong', defaultMessage: 'Something went wrong'}); - const message = intl.formatMessage({id: 'failed_action.fetch_channels', defaultMessage: 'Channels could not be loaded for {teamName}.'}, {teamName: team.displayName}); - - const onAction = useCallback(async () => { - setLoading(true); - const {error} = await retryInitialChannel(serverUrl, team.id); - - if (error) { - setLoading(false); - } - }, []); - - if (loading) { - return ( - - ); - } - - return ( - - ); -}; - -const withTeam = withObservables(['teamId'], ({teamId, database}: {teamId: string} & WithDatabaseArgs) => ({ - team: database.get(MM_TABLES.SERVER.TEAM).findAndObserve(teamId), -})); - -export default withDatabase(withTeam(FailedChannels)); diff --git a/app/screens/channel/failed_teams/index.tsx b/app/screens/channel/failed_teams/index.tsx deleted file mode 100644 index 7cb549578..000000000 --- a/app/screens/channel/failed_teams/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React, {useCallback, useState} from 'react'; -import {useIntl} from 'react-intl'; - -import {retryInitialTeamAndChannel} from '@actions/remote/retry'; -import FailedAction from '@components/failed_action'; -import Loading from '@components/loading'; -import {useServerUrl} from '@context/server'; -import {useTheme} from '@context/theme'; - -const FailedTeams = () => { - const intl = useIntl(); - const serverUrl = useServerUrl(); - const theme = useTheme(); - const [loading, setLoading] = useState(false); - - const title = intl.formatMessage({id: 'failed_action.something_wrong', defaultMessage: 'Something went wrong'}); - const message = intl.formatMessage({id: 'failed_action.fetch_teams', defaultMessage: 'An error ocurred while loading the teams of this server'}); - - const onAction = useCallback(async () => { - setLoading(true); - const {error} = await retryInitialTeamAndChannel(serverUrl); - - if (error) { - setLoading(false); - } - }, []); - - if (loading) { - return ( - - ); - } - - return ( - - ); -}; - -export default FailedTeams; diff --git a/app/screens/home/channel_list/channel_list.tsx b/app/screens/home/channel_list/channel_list.tsx index 2ec01ab1e..688dcccd8 100644 --- a/app/screens/home/channel_list/channel_list.tsx +++ b/app/screens/home/channel_list/channel_list.tsx @@ -16,6 +16,7 @@ import ServerIcon from '@screens/home/channel_list/server_icon/server_icon'; import {makeStyleSheetFromTheme} from '@utils/theme'; type ChannelProps = { + currentTeamId?: string; teamsCount: number; time?: number; }; @@ -89,11 +90,12 @@ const ChannelListScreen = (props: ChannelProps) => { teamsCount={props.teamsCount} /> - {isTablet && + {isTablet && Boolean(props.currentTeamId) && } diff --git a/app/screens/home/channel_list/index.ts b/app/screens/home/channel_list/index.ts index 89773fec1..ee1890448 100644 --- a/app/screens/home/channel_list/index.ts +++ b/app/screens/home/channel_list/index.ts @@ -3,17 +3,24 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; +import {catchError, of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; -import {MM_TABLES} from '@constants/database'; +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import MyTeamModel from '@typings/database/models/servers/my_team'; import ChannelsList from './channel_list'; import type {WithDatabaseArgs} from '@typings/database/database'; +import type SystemModel from '@typings/database/models/servers/system'; -const {SERVER: {MY_TEAM}} = MM_TABLES; +const {SERVER: {MY_TEAM, SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ + currentTeamId: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( + switchMap((id) => of$(id.value)), + catchError(() => of$(undefined)), + ), teamsCount: database.get(MY_TEAM).query().observeCount(), })); diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 55bbd9378..2361ea1bf 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -102,10 +102,6 @@ "emoji_skin.medium_dark_skin_tone": "medium dark skin tone", "emoji_skin.medium_light_skin_tone": "medium light skin tone", "emoji_skin.medium_skin_tone": "medium skin tone", - "failed_action.fetch_channels": "Channels could not be loaded for {teamName}.", - "failed_action.fetch_teams": "An error ocurred while loading the teams of this server", - "failed_action.something_wrong": "Something went wrong", - "failed_action.try_again": "Try again", "intro.add_people": "Add People", "intro.channel_details": "Details", "intro.created_by": "created by {creator} on {date}.", @@ -129,6 +125,10 @@ "last_users_message.others": "{numOthers} others ", "last_users_message.removed_from_channel.type": "were **removed from the channel**.", "last_users_message.removed_from_team.type": "were **removed from the team**.", + "load_channels_error.message": "There was a problem loading content for this team.", + "load_channels_error.title": "Couldn't load {teamDisplayName}", + "load_teams_error.message": "There was a problem loading content for this server.", + "load_teams_error.title": "Couldn't load {serverName}", "login_mfa.enterToken": "To complete the sign in process, please enter the code from your mobile device's authenticator app.", "login_mfa.token": "Enter MFA Token", "login_mfa.tokenReq": "Please enter an MFA token",