forked from Ivasoft/mattermost-mobile
[Gekidou] Handle gracefully teams or channels are fetch errors out (#5913)
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="magnify"
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"width": 24,
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(255,255,255,0.08)",
|
||||
"borderRadius": 60,
|
||||
"height": 120,
|
||||
"justifyContent": "center",
|
||||
"width": 120,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Find Channels"
|
||||
placeholderTextColor="rgba(255,255,255,0.72)"
|
||||
>
|
||||
<Icon
|
||||
name="alert-circle-outline"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.48)",
|
||||
"fontSize": 72,
|
||||
"lineHeight": 72,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"textAlignVertical": "center",
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"alignContent": "center",
|
||||
"alignItems": "center",
|
||||
"fontFamily": "Metropolis-SemiBold",
|
||||
"fontSize": 20,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 28,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"height": 40,
|
||||
"marginLeft": 5,
|
||||
"marginTop": -2,
|
||||
"marginTop": 20,
|
||||
"textAlign": "center",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<RCTScrollView
|
||||
ListHeaderComponent={[Function]}
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"channels": Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "A Highlighted Channel!!!",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"name": "And a longer channel name.",
|
||||
},
|
||||
],
|
||||
"id": "1",
|
||||
"title": "My first Category",
|
||||
},
|
||||
Object {
|
||||
"channels": Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "A Highlighted Channel!!!",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"name": "And a longer channel name.",
|
||||
},
|
||||
],
|
||||
"id": "2",
|
||||
"title": "Another Cat",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
>
|
||||
Couldn't load
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"marginTop": 4,
|
||||
"textAlign": "center",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View>
|
||||
>
|
||||
There was a problem loading content for this server.
|
||||
</Text>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="message-text-outline"
|
||||
style={
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Threads
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"marginTop": 12,
|
||||
"paddingVertical": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.64)",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
MY FIRST CATEGORY
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
data={
|
||||
"marginTop": 24,
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
"alignItems": "center",
|
||||
"borderRadius": 4,
|
||||
"flex": 0,
|
||||
"justifyContent": "center",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "A Highlighted Channel!!!",
|
||||
"height": 48,
|
||||
"paddingHorizontal": 24,
|
||||
"paddingVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"name": "And a longer channel name.",
|
||||
"backgroundColor": "#ffffff",
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontWeight": "600",
|
||||
"justifyContent": "center",
|
||||
"padding": 1,
|
||||
"textAlignVertical": "center",
|
||||
},
|
||||
Object {
|
||||
"fontSize": 16,
|
||||
"lineHeight": 16,
|
||||
"marginTop": 1,
|
||||
},
|
||||
Object {
|
||||
"color": "#1c58d9",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Just a channel
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
A Highlighted Channel!!!
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
And a longer channel name.
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 12,
|
||||
"paddingVertical": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.64)",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
ANOTHER CAT
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "A Highlighted Channel!!!",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"name": "And a longer channel name.",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Just a channel
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
A Highlighted Channel!!!
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
And a longer channel name.
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
Retry
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
@@ -67,6 +67,7 @@ const ChannelListHeader = ({displayName, iconPad}: Props) => {
|
||||
|
||||
return (
|
||||
<Animated.View style={animatedStyle}>
|
||||
{Boolean(displayName) &&
|
||||
<View style={styles.headerRow}>
|
||||
<View style={styles.headerRow}>
|
||||
<Text style={styles.headingStyles}>
|
||||
@@ -86,6 +87,7 @@ const ChannelListHeader = ({displayName, iconPad}: Props) => {
|
||||
/>
|
||||
</TouchableWithFeedback>
|
||||
</View>
|
||||
}
|
||||
<Text style={styles.subHeadingStyles}>
|
||||
{serverDisplayName}
|
||||
</Text>
|
||||
|
||||
@@ -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<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe(
|
||||
switchMap((id) => database.get<TeamModel>(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));
|
||||
|
||||
@@ -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<boolean>(true);
|
||||
let content;
|
||||
if (!currentTeamId) {
|
||||
content = (<LoadTeamsError/>);
|
||||
} else if (showCats) {
|
||||
content = (
|
||||
<>
|
||||
<SearchField/>
|
||||
<Categories categories={categories}/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
content = (<LoadChannelsError teamId={currentTeamId}/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container, tabletStyle]}>
|
||||
<TouchableOpacity onPress={() => setShowCats(!showCats)}>
|
||||
@@ -71,15 +87,7 @@ const ChannelList = ({iconPad, isTablet, teamsCount}: ChannelListProps) => {
|
||||
iconPad={iconPad}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{showCats && (
|
||||
<>
|
||||
<SearchField/>
|
||||
<Categories categories={categories}/>
|
||||
</>
|
||||
)}
|
||||
{/* <Loading/> */}
|
||||
{!showCats && (<LoadingError/>)}
|
||||
{content}
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
27
app/components/channel_list/load_channels_error/index.ts
Normal file
27
app/components/channel_list/load_channels_error/index.ts
Normal file
@@ -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<TeamModel>(TEAM).findAndObserve(teamId);
|
||||
|
||||
return {
|
||||
teamDisplayName: team.pipe(
|
||||
switchMap((t) => of$(t.displayName)),
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(LoadChannelsError));
|
||||
@@ -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 (
|
||||
<LoadingError
|
||||
loading={loading}
|
||||
message={formatMessage({id: 'load_channels_error.message', defaultMessage: 'There was a problem loading content for this team.'})}
|
||||
onRetry={onRetryTeams}
|
||||
title={formatMessage({id: 'load_channels_error.title', defaultMessage: "Couldn't load {teamDisplayName}"}, {teamDisplayName})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadChannelsError;
|
||||
36
app/components/channel_list/load_teams_error/index.tsx
Normal file
36
app/components/channel_list/load_teams_error/index.tsx
Normal file
@@ -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 (
|
||||
<LoadingError
|
||||
loading={loading}
|
||||
message={formatMessage({id: 'load_teams_error.message', defaultMessage: 'There was a problem loading content for this server.'})}
|
||||
onRetry={onRetryTeams}
|
||||
title={formatMessage({id: 'load_teams_error.title', defaultMessage: "Couldn't load {serverName}"}, {serverName})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadTeamsError;
|
||||
@@ -51,7 +51,7 @@ exports[`Loading Error should match snapshot 1`] = `
|
||||
]
|
||||
}
|
||||
>
|
||||
Couldn’t load Staff
|
||||
Error title
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
@@ -70,7 +70,7 @@ exports[`Loading Error should match snapshot 1`] = `
|
||||
]
|
||||
}
|
||||
>
|
||||
There was a problem loading the content for this team.
|
||||
Error description
|
||||
</Text>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
@@ -88,7 +88,7 @@ exports[`Loading Error should match snapshot 1`] = `
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={false}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
|
||||
@@ -9,7 +9,12 @@ import ErrorComponent from './index';
|
||||
|
||||
test('Loading Error should match snapshot', () => {
|
||||
const {toJSON} = renderWithIntlAndTheme(
|
||||
<ErrorComponent/>,
|
||||
<ErrorComponent
|
||||
loading={false}
|
||||
message='Error description'
|
||||
onRetry={() => true}
|
||||
title='Error title'
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
|
||||
@@ -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 (
|
||||
<Loading
|
||||
containerStyle={styles.container}
|
||||
color={theme.buttonBg}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@@ -55,12 +76,15 @@ const LoadingError = () => {
|
||||
/>
|
||||
</View>
|
||||
<Text style={[typography('Heading', 400), styles.header]}>
|
||||
{'Couldn’t load Staff'}
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={[typography('Body', 200), styles.body]}>
|
||||
{'There was a problem loading the content for this team.'}
|
||||
{message}
|
||||
</Text>
|
||||
<TouchableWithFeedback style={[{marginTop: 24}, buttonBackgroundStyle(theme, 'lg', 'primary', 'inverted')]}>
|
||||
<TouchableWithFeedback
|
||||
style={buttonStyle}
|
||||
onPress={onRetry}
|
||||
>
|
||||
<Text style={buttonTextStyle(theme, 'lg', 'primary', 'inverted')}>{'Retry'}</Text>
|
||||
</TouchableWithFeedback>
|
||||
</View>
|
||||
|
||||
@@ -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 (
|
||||
<View
|
||||
style={{height, width, alignItems: 'flex-start'}}
|
||||
testID='failed_network_action.cloud_icon'
|
||||
>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 72 47'
|
||||
>
|
||||
<Path
|
||||
d='M58.464,19.072c0,-5.181 -1.773,-9.599 -5.316,-13.249c-3.545,-3.649 -7.854,-5.474 -12.932,-5.474c-3.597,0 -6.902,0.979 -9.917,2.935c-3.014,1.959 -5.263,4.523 -6.743,7.696c-1.483,-0.739 -2.856,-1.111 -4.126,-1.111c-2.328,0 -4.363,0.769 -6.109,2.301c-1.745,1.535 -2.831,3.466 -3.252,5.792c-2.856,0.952 -5.185,2.672 -6.982,5.156c-1.8,2.487 -2.697,5.316 -2.697,8.489c0,3.915 1.4,7.299 4.204,10.155c2.802,2.857 6.161,4.285 10.076,4.285l43.794,0c3.595,0 6.664,-1.295 9.203,-3.888c2.538,-2.591 3.808,-5.685 3.808,-9.282c0,-3.702 -1.27,-6.848 -3.808,-9.441c-2.539,-2.591 -5.608,-3.888 -9.203,-3.888l0,-0.476Zm-31.294,16.424l17.17,0c-0.842,-1.62 -2.02,-2.92 -3.535,-3.898c-1.515,-0.977 -3.198,-1.467 -5.05,-1.467c-1.852,0 -3.535,0.49 -5.05,1.467c-1.515,0.978 -2.693,2.278 -3.535,3.898l0,0Zm17.338,-12.407c0,-0.782 -0.252,-1.411 -0.757,-1.886c-0.505,-0.474 -1.124,-0.713 -1.852,-0.713c-0.73,0 -1.347,0.239 -1.852,0.713c-0.505,0.475 -0.757,1.104 -0.757,1.886c0,0.783 0.252,1.412 0.757,1.886c0.505,0.476 1.122,0.713 1.852,0.713c0.728,0 1.347,-0.237 1.852,-0.713c0.505,-0.474 0.757,-1.103 0.757,-1.886Zm-12.288,0c0,-0.782 -0.253,-1.411 -0.758,-1.886c-0.505,-0.474 -1.123,-0.713 -1.851,-0.713c-0.73,0 -1.347,0.239 -1.852,0.713c-0.505,0.475 -0.757,1.104 -0.757,1.886c0,0.783 0.252,1.412 0.757,1.886c0.505,0.476 1.122,0.713 1.852,0.713c0.728,0 1.346,-0.237 1.851,-0.713c0.505,-0.474 0.758,-1.103 0.758,-1.886Z'
|
||||
fillRule='evenodd'
|
||||
strokeLinejoin='round'
|
||||
fill={color}
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloudSvg;
|
||||
@@ -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 (
|
||||
<View style={style.container}>
|
||||
<CloudSvg
|
||||
color={changeOpacity(theme.centerChannelColor, 0.15)}
|
||||
height={76}
|
||||
width={76}
|
||||
/>
|
||||
<Text
|
||||
style={style.title}
|
||||
testID='error_title'
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<Text
|
||||
style={style.description}
|
||||
testID='error_text'
|
||||
>
|
||||
{message}
|
||||
</Text>
|
||||
<Button
|
||||
containerStyle={style.buttonContainer}
|
||||
onPress={onAction}
|
||||
>
|
||||
<Text style={style.link}>{text}</Text>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default FailedAction;
|
||||
Reference in New Issue
Block a user