[Gekidou] Adding logic to hide the list of teams on the teams sidebar when needed (#5790)
This commit is contained in:
parent
3a8f1f1946
commit
3f91c3a47d
9 changed files with 201 additions and 144 deletions
|
|
@ -38,7 +38,21 @@ exports[`components/channel_list should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
animatedStyle={
|
||||
Object {
|
||||
"value": Object {
|
||||
"marginLeft": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {TouchableOpacity} from 'react-native-gesture-handler';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {TABLET_SIDEBAR_WIDTH, TEAM_SIDEBAR_WIDTH} from '@constants/view';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import Categories from './categories';
|
||||
import ChannelListHeader from './header';
|
||||
import LoadingError from './loading_error';
|
||||
import SearchField from './search';
|
||||
|
||||
// import Loading from '@components/loading';
|
||||
|
||||
const channels: TempoChannel[] = [
|
||||
{id: '1', name: 'Just a channel'},
|
||||
{id: '2', name: 'A Highlighted Channel!!!', highlight: true},
|
||||
{id: '3', name: 'And a longer channel name.'},
|
||||
];
|
||||
|
||||
const categories: TempoCategory[] = [
|
||||
{id: '1', title: 'My first Category', channels},
|
||||
{id: '2', title: 'Another Cat', channels},
|
||||
];
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.sidebarBg,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
|
||||
}));
|
||||
|
||||
type ChannelListProps = {
|
||||
iconPad?: boolean;
|
||||
isTablet: boolean;
|
||||
teamsCount: number;
|
||||
}
|
||||
|
||||
const ChannelList = ({iconPad, isTablet, teamsCount}: ChannelListProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const tabletWidth = useSharedValue(TABLET_SIDEBAR_WIDTH);
|
||||
const tabletStyle = useAnimatedStyle(() => {
|
||||
if (!isTablet) {
|
||||
return {
|
||||
maxWidth: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return {maxWidth: withTiming(tabletWidth.value, {duration: 250})};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTablet) {
|
||||
tabletWidth.value = TABLET_SIDEBAR_WIDTH - (teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0);
|
||||
}
|
||||
}, [isTablet, teamsCount]);
|
||||
|
||||
// @to-do; remove after testing
|
||||
const [showCats, setShowCats] = useState<boolean>(true);
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container, tabletStyle]}>
|
||||
<TouchableOpacity onPress={() => setShowCats(!showCats)}>
|
||||
<ChannelListHeader
|
||||
iconPad={iconPad}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{showCats && (
|
||||
<>
|
||||
<SearchField/>
|
||||
<Categories categories={categories}/>
|
||||
</>
|
||||
)}
|
||||
{/* <Loading/> */}
|
||||
{!showCats && (<LoadingError/>)}
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelList;
|
||||
|
|
@ -1,7 +1,21 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/channel_list/header Channel List Header Component should match snapshot 1`] = `
|
||||
<View>
|
||||
<View
|
||||
animatedStyle={
|
||||
Object {
|
||||
"value": Object {
|
||||
"marginLeft": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import React, {useEffect} from 'react';
|
||||
import {Text, View} from 'react-native';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
|
|
@ -25,9 +26,6 @@ const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
color: changeOpacity(theme.sidebarText, 0.64),
|
||||
...typography('Heading', 50),
|
||||
},
|
||||
iconPad: {
|
||||
marginLeft: 44,
|
||||
},
|
||||
headerRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
|
@ -57,10 +55,18 @@ const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
const ChannelListHeader = ({displayName, iconPad}: Props) => {
|
||||
const theme = useTheme();
|
||||
const serverDisplayName = useServerDisplayName();
|
||||
const marginLeft = useSharedValue(iconPad ? 44 : 0);
|
||||
const styles = getStyles(theme);
|
||||
const animatedStyle = useAnimatedStyle(() => ({
|
||||
marginLeft: withTiming(marginLeft.value, {duration: 350}),
|
||||
}), []);
|
||||
|
||||
useEffect(() => {
|
||||
marginLeft.value = iconPad ? 44 : 0;
|
||||
}, [iconPad]);
|
||||
|
||||
return (
|
||||
<View style={iconPad && styles.iconPad}>
|
||||
<Animated.View style={animatedStyle}>
|
||||
<View style={styles.headerRow}>
|
||||
<View style={styles.headerRow}>
|
||||
<Text style={styles.headingStyles}>
|
||||
|
|
@ -83,7 +89,7 @@ const ChannelListHeader = ({displayName, iconPad}: Props) => {
|
|||
<Text style={styles.subHeadingStyles}>
|
||||
{serverDisplayName}
|
||||
</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {TeamModel} from '@database/models/server';
|
|||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import ChannelsList from './channel_list';
|
||||
import ChannelsList from './index';
|
||||
|
||||
describe('components/channel_list', () => {
|
||||
let database: Database;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,88 @@
|
|||
// 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, {useEffect, useState} from 'react';
|
||||
import {TouchableOpacity} from 'react-native-gesture-handler';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import MyTeamModel from '@typings/database/models/servers/my_team';
|
||||
import {TABLET_SIDEBAR_WIDTH, TEAM_SIDEBAR_WIDTH} from '@constants/view';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import ChannelsList from './channel_list';
|
||||
import Categories from './categories';
|
||||
import ChannelListHeader from './header';
|
||||
import LoadingError from './loading_error';
|
||||
import SearchField from './search';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
// import Loading from '@components/loading';
|
||||
|
||||
const {SERVER: {MY_TEAM}} = MM_TABLES;
|
||||
const channels: TempoChannel[] = [
|
||||
{id: '1', name: 'Just a channel'},
|
||||
{id: '2', name: 'A Highlighted Channel!!!', highlight: true},
|
||||
{id: '3', name: 'And a longer channel name.'},
|
||||
];
|
||||
|
||||
const categories: TempoCategory[] = [
|
||||
{id: '1', title: 'My first Category', channels},
|
||||
{id: '2', title: 'Another Cat', channels},
|
||||
];
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.sidebarBg,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
|
||||
teamsCount: database.get<MyTeamModel>(MY_TEAM).query().observeCount(),
|
||||
}));
|
||||
|
||||
export default withDatabase(enhanced(ChannelsList));
|
||||
type ChannelListProps = {
|
||||
iconPad?: boolean;
|
||||
isTablet: boolean;
|
||||
teamsCount: number;
|
||||
}
|
||||
|
||||
const ChannelList = ({iconPad, isTablet, teamsCount}: ChannelListProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const tabletWidth = useSharedValue(TABLET_SIDEBAR_WIDTH);
|
||||
const tabletStyle = useAnimatedStyle(() => {
|
||||
if (!isTablet) {
|
||||
return {
|
||||
maxWidth: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return {maxWidth: withTiming(tabletWidth.value, {duration: 350})};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTablet) {
|
||||
tabletWidth.value = TABLET_SIDEBAR_WIDTH - (teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0);
|
||||
}
|
||||
}, [isTablet, teamsCount]);
|
||||
|
||||
const [showCats, setShowCats] = useState<boolean>(true);
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container, tabletStyle]}>
|
||||
<TouchableOpacity onPress={() => setShowCats(!showCats)}>
|
||||
<ChannelListHeader
|
||||
iconPad={iconPad}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{showCats && (
|
||||
<>
|
||||
<SearchField/>
|
||||
<Categories categories={categories}/>
|
||||
</>
|
||||
)}
|
||||
{/* <Loading/> */}
|
||||
{!showCats && (<LoadingError/>)}
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelList;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useEffect} from 'react';
|
||||
import {View} from 'react-native';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {fetchAllTeams} from '@actions/remote/team';
|
||||
import {TEAM_SIDEBAR_WIDTH} from '@constants/view';
|
||||
|
|
@ -17,34 +17,9 @@ import type TeamModel from '@typings/database/models/servers/team';
|
|||
|
||||
type Props = {
|
||||
canCreateTeams: boolean;
|
||||
otherTeams: TeamModel[];
|
||||
iconPad?: boolean;
|
||||
}
|
||||
|
||||
export default function TeamSidebar({canCreateTeams, otherTeams, iconPad}: Props) {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllTeams(serverUrl);
|
||||
}, [serverUrl]);
|
||||
|
||||
const showAddTeam = canCreateTeams || otherTeams.length > 0;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.listContainer, iconPad && styles.iconMargin]}>
|
||||
<TeamList/>
|
||||
{showAddTeam && (
|
||||
<AddTeam
|
||||
canCreateTeams={canCreateTeams}
|
||||
otherTeams={otherTeams}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
otherTeams: TeamModel[];
|
||||
teamsCount: number;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
|
|
@ -53,7 +28,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
width: TEAM_SIDEBAR_WIDTH,
|
||||
height: '100%',
|
||||
backgroundColor: theme.sidebarBg,
|
||||
display: 'flex',
|
||||
paddingTop: 10,
|
||||
},
|
||||
listContainer: {
|
||||
|
|
@ -63,7 +37,52 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
},
|
||||
iconMargin: {
|
||||
marginTop: 44,
|
||||
paddingTop: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default function TeamSidebar({canCreateTeams, iconPad, otherTeams, teamsCount}: Props) {
|
||||
const showAddTeam = canCreateTeams || otherTeams.length > 0;
|
||||
const initialWidth = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0;
|
||||
const width = useSharedValue(initialWidth);
|
||||
const marginTop = useSharedValue(iconPad ? 44 : 0);
|
||||
const theme = useTheme();
|
||||
const serverUrl = useServerUrl();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const transform = useAnimatedStyle(() => {
|
||||
return {
|
||||
width: withTiming(width.value, {duration: 350}),
|
||||
};
|
||||
}, []);
|
||||
|
||||
const serverStyle = useAnimatedStyle(() => ({
|
||||
marginTop: withTiming(marginTop.value, {duration: 350}),
|
||||
}), []);
|
||||
|
||||
useEffect(() => {
|
||||
marginTop.value = iconPad ? 44 : 0;
|
||||
}, [iconPad]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllTeams(serverUrl);
|
||||
}, [serverUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
width.value = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0;
|
||||
}, [teamsCount]);
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container, transform]}>
|
||||
<Animated.View style={[styles.listContainer, serverStyle]}>
|
||||
<TeamList/>
|
||||
{showAddTeam && (
|
||||
<AddTeam
|
||||
canCreateTeams={canCreateTeams}
|
||||
otherTeams={otherTeams}
|
||||
/>
|
||||
)}
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useManagedConfig} from '@mattermost/react-native-emm';
|
||||
import {useIsFocused, useRoute} from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
|
@ -18,6 +19,7 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
type ChannelProps = LaunchProps & {
|
||||
teamsCount: number;
|
||||
time?: number;
|
||||
};
|
||||
|
||||
|
|
@ -43,12 +45,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
const ChannelListScreen = (props: ChannelProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const managedConfig = useManagedConfig();
|
||||
|
||||
const isTablet = useIsTablet();
|
||||
const route = useRoute();
|
||||
const isFocused = useIsFocused();
|
||||
const insets = useSafeAreaInsets();
|
||||
const params = route.params as {direction: string};
|
||||
const canAddOtherServers = managedConfig?.allowOtherServers !== 'false';
|
||||
|
||||
const animated = useAnimatedStyle(() => {
|
||||
if (!isFocused) {
|
||||
|
|
@ -74,17 +78,18 @@ const ChannelListScreen = (props: ChannelProps) => {
|
|||
style={styles.content}
|
||||
edges={['bottom', 'left', 'right']}
|
||||
>
|
||||
<ServerIcon/>
|
||||
{canAddOtherServers && <ServerIcon/>}
|
||||
<Animated.View
|
||||
style={[styles.content, animated]}
|
||||
>
|
||||
{/* @to-do: Server Icon requires padding in the team and channel components:
|
||||
* https://mattermost.atlassian.net/browse/MM-39702
|
||||
*/}
|
||||
<TeamSidebar iconPad={true}/>
|
||||
<TeamSidebar
|
||||
iconPad={canAddOtherServers}
|
||||
teamsCount={props.teamsCount}
|
||||
/>
|
||||
<ChannelList
|
||||
iconPad={false}
|
||||
iconPad={canAddOtherServers && props.teamsCount <= 1}
|
||||
isTablet={isTablet}
|
||||
teamsCount={props.teamsCount}
|
||||
/>
|
||||
{isTablet &&
|
||||
<Channel {...props}/>
|
||||
20
app/screens/home/channel_list/index.ts
Normal file
20
app/screens/home/channel_list/index.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// 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 {MM_TABLES} from '@constants/database';
|
||||
import MyTeamModel from '@typings/database/models/servers/my_team';
|
||||
|
||||
import ChannelsList from './channel_list';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const {SERVER: {MY_TEAM}} = MM_TABLES;
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
|
||||
teamsCount: database.get<MyTeamModel>(MY_TEAM).query().observeCount(),
|
||||
}));
|
||||
|
||||
export default withDatabase(enhanced(ChannelsList));
|
||||
Loading…
Reference in a new issue