Improvements on team switch performance (#6255)

* Improvements on channel switch performance

* Revert removal from channel observables

* Fix team switch dependancies

* Fix lint

* Use events to signal channel switch

* Add check for hasMembership

* Address feedback

* add useTeamSwitch hook

* Fix team switch perceived performance on tablets

* align custom status in channel list

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Daniel Espino García 2022-05-10 20:08:24 +02:00 committed by GitHub
parent ba976dadc2
commit 67461322a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 197 additions and 86 deletions

View file

@ -269,6 +269,7 @@ export async function handleTeamChange(serverUrl: string, teamId: string) {
}
let channelId = '';
DeviceEventEmitter.emit(Events.TEAM_SWITCH, true);
if (await isTablet()) {
channelId = await getNthLastChannelFromTeam(database, teamId);
if (channelId) {
@ -277,6 +278,7 @@ export async function handleTeamChange(serverUrl: string, teamId: string) {
} else {
await switchToChannelById(serverUrl, channelId, teamId);
}
DeviceEventEmitter.emit(Events.TEAM_SWITCH, false);
completeTeamChange(serverUrl, teamId, channelId);
return;
}
@ -295,6 +297,7 @@ export async function handleTeamChange(serverUrl: string, teamId: string) {
if (models.length) {
await operator.batchRecords(models);
}
DeviceEventEmitter.emit(Events.TEAM_SWITCH, false);
completeTeamChange(serverUrl, teamId, channelId);
}

View file

@ -35,11 +35,13 @@ describe('components/channel_list/categories/body/channel_item', () => {
hasDraft={false}
isActive={false}
membersCount={0}
myChannel={myChannel}
isMuted={false}
currentUserId={'id'}
testID='channel_list_item'
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}
hasMember={Boolean(myChannel)}
/>,
);
@ -53,11 +55,13 @@ describe('components/channel_list/categories/body/channel_item', () => {
hasDraft={true}
isActive={false}
membersCount={3}
myChannel={myChannel}
isMuted={false}
currentUserId={'id'}
testID='channel_list_item'
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}
hasMember={Boolean(myChannel)}
/>,
);

View file

@ -17,7 +17,6 @@ import {getUserIdFromChannelName} from '@utils/user';
import CustomStatus from './custom_status';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
type Props = {
channel: ChannelModel;
@ -27,8 +26,10 @@ type Props = {
isInfo?: boolean;
isMuted: boolean;
membersCount: number;
myChannel?: MyChannelModel;
isUnread: boolean;
mentionsCount: number;
onPress: (channelId: string) => void;
hasMember: boolean;
teamDisplayName?: string;
testID?: string;
}
@ -114,15 +115,15 @@ export const textStyle = StyleSheet.create({
const ChannelListItem = ({
channel, currentUserId, hasDraft,
isActive, isInfo, isMuted, membersCount,
myChannel, onPress, teamDisplayName, testID}: Props) => {
isActive, isInfo, isMuted, membersCount, hasMember,
isUnread, mentionsCount, onPress, teamDisplayName, testID}: Props) => {
const {formatMessage} = useIntl();
const theme = useTheme();
const isTablet = useIsTablet();
const styles = getStyleSheet(theme);
// Make it brighter if it's not muted, and highlighted or has unreads
const isBright = myChannel && (myChannel.isUnread || myChannel.mentionsCount > 0);
const isBright = isUnread || mentionsCount > 0;
const height = useMemo(() => {
let h = 40;
@ -133,8 +134,8 @@ const ChannelListItem = ({
}, [teamDisplayName, isInfo, isTablet]);
const handleOnPress = useCallback(() => {
onPress(myChannel?.id || channel.id);
}, [channel.id, myChannel?.id]);
onPress(channel.id);
}, [channel.id]);
const textStyles = useMemo(() => [
isBright ? textStyle.bright : textStyle.regular,
@ -153,7 +154,7 @@ const ChannelListItem = ({
],
[height, isActive, isInfo, styles]);
if (!myChannel) {
if (!hasMember) {
return null;
}
@ -196,14 +197,14 @@ const ChannelListItem = ({
{displayName}
</Text>
{isInfo && Boolean(teamDisplayName) && !isTablet &&
<Text
ellipsizeMode='tail'
numberOfLines={1}
testID={`${testID}.${teamDisplayName}.display_name`}
style={styles.teamName}
>
{teamDisplayName}
</Text>
<Text
ellipsizeMode='tail'
numberOfLines={1}
testID={`${testID}.${teamDisplayName}.display_name`}
style={styles.teamName}
>
{teamDisplayName}
</Text>
}
</View>
{Boolean(teammateId) &&
@ -224,8 +225,8 @@ const ChannelListItem = ({
}
</View>
<Badge
visible={myChannel.mentionsCount > 0}
value={myChannel.mentionsCount}
visible={mentionsCount > 0}
value={mentionsCount}
style={[styles.badge, isMuted && styles.mutedBadge, isInfo && styles.infoBadge]}
/>
</View>

View file

@ -16,7 +16,7 @@ type Props = {
const style = StyleSheet.create({
customStatusEmoji: {
color: '#000',
marginHorizontal: 5,
marginHorizontal: -5,
top: 3,
},
info: {

View file

@ -32,9 +32,13 @@ const enhance = withObservables(['channel', 'showTeamName', 'isCategoryMuted'],
const hasDraft = queryDraft(database, channel.id).observeWithColumns(['message', 'files']).pipe(
switchMap((draft) => of$(draft.length > 0)),
distinctUntilChanged(),
);
const isActive = observeCurrentChannelId(database).pipe(switchMap((id) => of$(id ? id === channel.id : false)), distinctUntilChanged());
const isActive = observeCurrentChannelId(database).pipe(
switchMap((id) => of$(id ? id === channel.id : false)),
distinctUntilChanged(),
);
const isMuted = myChannel.pipe(
switchMap((mc) => {
@ -50,6 +54,7 @@ const enhance = withObservables(['channel', 'showTeamName', 'isCategoryMuted'],
if (channel.teamId && showTeamName) {
teamDisplayName = channel.team.observe().pipe(
switchMap((team) => of$(team?.displayName || '')),
distinctUntilChanged(),
);
}
@ -58,6 +63,21 @@ const enhance = withObservables(['channel', 'showTeamName', 'isCategoryMuted'],
membersCount = channel.members.observeCount();
}
const isUnread = myChannel.pipe(
switchMap((mc) => of$(mc?.isUnread)),
distinctUntilChanged(),
);
const mentionsCount = myChannel.pipe(
switchMap((mc) => of$(mc?.mentionsCount)),
distinctUntilChanged(),
);
const hasMember = myChannel.pipe(
switchMap((mc) => of$(Boolean(mc))),
distinctUntilChanged(),
);
return {
channel: channel.observe(),
currentUserId,
@ -65,8 +85,10 @@ const enhance = withObservables(['channel', 'showTeamName', 'isCategoryMuted'],
isActive,
isMuted,
membersCount,
myChannel,
isUnread,
mentionsCount,
teamDisplayName,
hasMember,
};
});

View file

@ -3,7 +3,8 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {combineLatestWith, map} from 'rxjs/operators';
import {of as of$} from 'rxjs';
import {combineLatestWith, map, switchMap, distinctUntilChanged} from 'rxjs/operators';
import {observeAllMyChannelNotifyProps, queryMyChannelsByTeam} from '@queries/servers/channel';
import {observeCurrentTeamId} from '@queries/servers/system';
@ -30,8 +31,13 @@ const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs)
}, false)),
);
const selected = observeCurrentTeamId(database).pipe(
switchMap((ctid) => of$(ctid === myTeam.id)),
distinctUntilChanged(),
);
return {
currentTeamId: observeCurrentTeamId(database),
selected,
team: myTeam.team.observe(),
mentionCount: observeMentionCount(database, myTeam.id, false),
hasUnreads,

View file

@ -19,7 +19,7 @@ type Props = {
team: TeamModel;
hasUnreads: boolean;
mentionCount: number;
currentTeamId: string;
selected: boolean;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
@ -57,11 +57,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
export default function TeamItem({team, hasUnreads, mentionCount, currentTeamId}: Props) {
export default function TeamItem({team, hasUnreads, mentionCount, selected}: Props) {
const theme = useTheme();
const styles = getStyleSheet(theme);
const serverUrl = useServerUrl();
const selected = team.id === currentTeamId;
const hasBadge = Boolean(mentionCount || hasUnreads);
let badgeStyle = styles.unread;

View file

@ -19,6 +19,7 @@ export default keyMirror({
SERVER_VERSION_CHANGED: null,
TAB_BAR_VISIBLE: null,
TEAM_LOAD_ERROR: null,
TEAM_SWITCH: null,
USER_TYPING: null,
USER_STOP_TYPING: null,
POST_LIST_SCROLL_TO_BOTTOM: null,

33
app/hooks/team_switch.ts Normal file
View file

@ -0,0 +1,33 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useEffect, useState} from 'react';
import {DeviceEventEmitter} from 'react-native';
import {Events} from '@constants';
export const useTeamSwitch = () => {
const [loading, setLoading] = useState(false);
useEffect(() => {
let time: NodeJS.Timeout | undefined;
const l = DeviceEventEmitter.addListener(Events.TEAM_SWITCH, (switching: boolean) => {
if (time) {
clearTimeout(time);
}
if (switching) {
setLoading(true);
} else {
// eslint-disable-next-line max-nested-callbacks
time = setTimeout(() => setLoading(false), 200);
}
});
return () => {
l.remove();
if (time) {
clearTimeout(time);
}
};
}, []);
return loading;
};

View file

@ -174,6 +174,12 @@ export const queryAllMyChannel = (database: Database) => {
return database.get<MyChannelModel>(MY_CHANNEL).query();
};
export const queryAllMyChannelsForTeam = (database: Database, teamId: string) => {
return database.get<ChannelModel>(MY_CHANNEL).query(
Q.on(CHANNEL, Q.where('team_id', Q.oneOf([teamId, '']))),
);
};
export const getMyChannel = async (database: Database, channelId: string) => {
try {
const member = await database.get<MyChannelModel>(MY_CHANNEL).find(channelId);

View file

@ -12,6 +12,7 @@ import {Events} from '@constants';
import {ACCESSORIES_CONTAINER_NATIVE_ID} from '@constants/post_draft';
import {useAppState, useIsTablet} from '@hooks/device';
import {useDefaultHeaderHeight} from '@hooks/header';
import {useTeamSwitch} from '@hooks/team_switch';
import ChannelPostList from './channel_post_list';
import ChannelHeader from './header';
@ -34,6 +35,7 @@ const Channel = ({channelId, componentId}: ChannelProps) => {
const isTablet = useIsTablet();
const insets = useSafeAreaInsets();
const [shouldRenderPosts, setShouldRenderPosts] = useState(false);
const switchingTeam = useTeamSwitch();
const defaultHeight = useDefaultHeaderHeight();
const postDraftRef = useRef<KeyboardTrackingViewRef>(null);
@ -70,7 +72,7 @@ const Channel = ({channelId, componentId}: ChannelProps) => {
testID='channel.screen'
>
<ChannelHeader componentId={componentId}/>
{shouldRenderPosts && Boolean(channelId) &&
{!switchingTeam && shouldRenderPosts && Boolean(channelId) &&
<>
<View style={[styles.flex, {marginTop}]}>
<ChannelPostList

View file

@ -9,6 +9,7 @@ import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-cont
import NavigationHeader from '@components/navigation_header';
import {useAppState, useIsTablet} from '@hooks/device';
import {useDefaultHeaderHeight} from '@hooks/header';
import {useTeamSwitch} from '@hooks/team_switch';
import {popTopScreen} from '@screens/navigation';
import ThreadsList from './threads_list';
@ -29,6 +30,7 @@ const GlobalThreads = ({componentId}: Props) => {
const appState = useAppState();
const intl = useIntl();
const insets = useSafeAreaInsets();
const switchingTeam = useTeamSwitch();
const isTablet = useIsTablet();
const defaultHeight = useDefaultHeaderHeight();
@ -63,6 +65,7 @@ const GlobalThreads = ({componentId}: Props) => {
})
}
/>
{!switchingTeam &&
<View style={containerStyle}>
<ThreadsList
forceQueryAfterAppState={appState}
@ -71,6 +74,7 @@ const GlobalThreads = ({componentId}: Props) => {
testID={'global_threads.threads_list'}
/>
</View>
}
</SafeAreaView>
);
};

View file

@ -14,6 +14,7 @@ type SelectedView = {
}
type Props = {
onTeam: boolean;
currentChannelId: string;
isCRTEnabled: boolean;
}
@ -26,7 +27,7 @@ const ComponentsList: Record<string, React.ReactNode> = {
const channelScreen: SelectedView = {id: Screens.CHANNEL, Component: Channel};
const globalScreen: SelectedView = {id: Screens.GLOBAL_THREADS, Component: GlobalThreads};
const AdditionalTabletView = ({currentChannelId, isCRTEnabled}: Props) => {
const AdditionalTabletView = ({onTeam, currentChannelId, isCRTEnabled}: Props) => {
const [selected, setSelected] = useState<SelectedView>(isCRTEnabled && !currentChannelId ? globalScreen : channelScreen);
useEffect(() => {
@ -43,7 +44,7 @@ const AdditionalTabletView = ({currentChannelId, isCRTEnabled}: Props) => {
return () => listener.remove();
}, []);
if (!selected) {
if (!selected || !onTeam) {
return null;
}

View file

@ -3,8 +3,10 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {of as of$} from 'rxjs';
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
import {observeCurrentChannelId} from '@queries/servers/system';
import {observeCurrentChannelId, observeCurrentTeamId} from '@queries/servers/system';
import {observeIsCRTEnabled} from '@queries/servers/thread';
import AdditionalTabletView from './additional_tablet_view';
@ -12,6 +14,10 @@ import AdditionalTabletView from './additional_tablet_view';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
onTeam: observeCurrentTeamId(database).pipe(
switchMap((id) => of$(Boolean(id))),
distinctUntilChanged(),
),
currentChannelId: observeCurrentChannelId(database),
isCRTEnabled: observeIsCRTEnabled(database),
}));

View file

@ -18,7 +18,7 @@ describe('components/channel_list/categories', () => {
it('render without error', () => {
const wrapper = renderWithEverything(
<Categories currentTeamId={TestHelper.basicTeam!.id}/>,
<Categories/>,
{database},
);

View file

@ -1,13 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import React, {useCallback, useMemo, useRef} from 'react';
import {useIntl} from 'react-intl';
import {FlatList, StyleSheet} from 'react-native';
import {FlatList, StyleSheet, View} from 'react-native';
import {switchToChannelById} from '@actions/remote/channel';
import Loading from '@components/loading';
import {useServerUrl} from '@context/server';
import {useIsTablet} from '@hooks/device';
import {useTeamSwitch} from '@hooks/team_switch';
import CategoryBody from './body';
import LoadCategoriesError from './error';
@ -18,7 +20,6 @@ import type CategoryModel from '@typings/database/models/servers/category';
type Props = {
categories: CategoryModel[];
currentTeamId: string;
onlyUnreads: boolean;
unreadsOnTop: boolean;
}
@ -29,15 +30,27 @@ const styles = StyleSheet.create({
marginLeft: -18,
marginRight: -20,
},
loadingView: {
alignItems: 'center',
justifyContent: 'center',
flex: 1,
},
loading: {
justifyContent: 'center',
height: 32,
width: 32,
},
});
const extractKey = (item: CategoryModel | 'UNREADS') => (item === 'UNREADS' ? 'UNREADS' : item.id);
const Categories = ({categories, currentTeamId, onlyUnreads, unreadsOnTop}: Props) => {
const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
const intl = useIntl();
const listRef = useRef<FlatList>(null);
const serverUrl = useServerUrl();
const isTablet = useIsTablet();
const switchingTeam = useTeamSwitch();
const teamId = categories[0]?.teamId;
const onChannelSwitch = useCallback(async (channelId: string) => {
switchToChannelById(serverUrl, channelId);
@ -47,7 +60,7 @@ const Categories = ({categories, currentTeamId, onlyUnreads, unreadsOnTop}: Prop
if (data.item === 'UNREADS') {
return (
<UnreadCategories
currentTeamId={currentTeamId}
currentTeamId={teamId}
isTablet={isTablet}
onChannelSwitch={onChannelSwitch}
onlyUnreads={onlyUnreads}
@ -65,11 +78,7 @@ const Categories = ({categories, currentTeamId, onlyUnreads, unreadsOnTop}: Prop
/>
</>
);
}, [currentTeamId, intl.locale, isTablet, onChannelSwitch, onlyUnreads]);
useEffect(() => {
listRef.current?.scrollToOffset({animated: false, offset: 0});
}, [currentTeamId]);
}, [teamId, intl.locale, isTablet, onChannelSwitch, onlyUnreads]);
const categoriesToShow = useMemo(() => {
if (onlyUnreads && !unreadsOnTop) {
@ -88,19 +97,28 @@ const Categories = ({categories, currentTeamId, onlyUnreads, unreadsOnTop}: Prop
}
return (
<FlatList
data={categoriesToShow}
ref={listRef}
renderItem={renderCategory}
style={styles.mainList}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
keyExtractor={extractKey}
initialNumToRender={categoriesToShow.length}
<>
{!switchingTeam && (
<FlatList
data={categoriesToShow}
ref={listRef}
renderItem={renderCategory}
style={styles.mainList}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
keyExtractor={extractKey}
initialNumToRender={categoriesToShow.length}
// @ts-expect-error strictMode not included in the types
strictMode={true}
/>
// @ts-expect-error strictMode not included in the types
strictMode={true}
/>
)}
{switchingTeam && (
<View style={styles.loadingView}>
<Loading style={styles.loading}/>
</View>
)}
</>
);
};

View file

@ -10,19 +10,18 @@ import {Preferences} from '@constants';
import {getPreferenceAsBool} from '@helpers/api/preference';
import {queryCategoriesByTeamIds} from '@queries/servers/categories';
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
import {observeOnlyUnreads} from '@queries/servers/system';
import {observeCurrentTeamId, observeOnlyUnreads} from '@queries/servers/system';
import Categories from './categories';
import type {WithDatabaseArgs} from '@typings/database/database';
import type PreferenceModel from '@typings/database/models/servers/preference';
type WithDatabaseProps = { currentTeamId: string } & WithDatabaseArgs
const enhanced = withObservables(
['currentTeamId'],
({currentTeamId, database}: WithDatabaseProps) => {
const categories = queryCategoriesByTeamIds(database, [currentTeamId]).observeWithColumns(['sort_order']);
[],
({database}: WithDatabaseArgs) => {
const currentTeamId = observeCurrentTeamId(database);
const categories = currentTeamId.pipe(switchMap((ctid) => queryCategoriesByTeamIds(database, [ctid]).observeWithColumns(['sort_order'])));
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
observeWithColumns(['value']).

View file

@ -6,7 +6,6 @@ import withObservables from '@nozbe/with-observables';
import {of as of$, combineLatest} from 'rxjs';
import {combineLatestWith, concatAll, map, switchMap} from 'rxjs/operators';
import {MyChannelModel} from '@app/database/models/server';
import {Preferences} from '@constants';
import {getPreferenceAsBool} from '@helpers/api/preference';
import {getChannelById, observeAllMyChannelNotifyProps, queryMyChannelUnreads} from '@queries/servers/channel';
@ -19,6 +18,7 @@ import UnreadCategories from './unreads';
import type {WithDatabaseArgs} from '@typings/database/database';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
import type PreferenceModel from '@typings/database/models/servers/preference';
type WithDatabaseProps = WithDatabaseArgs & {

View file

@ -27,6 +27,8 @@ type UnreadCategoriesProps = {
onChannelSwitch: (channelId: string) => void;
}
const extractKey = (item: ChannelModel) => item.id;
const UnreadCategories = ({onChannelSwitch, unreadChannels}: UnreadCategoriesProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
@ -54,6 +56,8 @@ const UnreadCategories = ({onChannelSwitch, unreadChannels}: UnreadCategoriesPro
<FlatList
data={unreadChannels}
renderItem={renderItem}
keyExtractor={extractKey}
removeClippedSubviews={true}
/>
</>
);

View file

@ -4,7 +4,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {combineLatest, of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
import {Permissions} from '@constants';
import {observePermissionForTeam} from '@queries/servers/role';
@ -23,14 +23,17 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
const canJoinChannels = combineLatest([currentUser, team]).pipe(
switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true) : of$(false))),
distinctUntilChanged(),
);
const canCreatePublicChannels = combineLatest([currentUser, team]).pipe(
switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true) : of$(false))),
distinctUntilChanged(),
);
const canCreatePrivateChannels = combineLatest([currentUser, team]).pipe(
switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false) : of$(false))),
distinctUntilChanged(),
);
const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe(

View file

@ -33,7 +33,6 @@ describe('components/categories_list', () => {
<CategoriesList
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={1}
/>,
{database},
@ -47,7 +46,6 @@ describe('components/categories_list', () => {
isCRTEnabled={true}
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={1}
/>,
{database},
@ -65,7 +63,6 @@ describe('components/categories_list', () => {
<CategoriesList
isTablet={false}
teamsCount={0}
currentTeamId='TestHelper.basicTeam!.id'
channelsCount={1}
/>,
{database},
@ -84,7 +81,6 @@ describe('components/categories_list', () => {
<CategoriesList
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={0}
/>,
{database},

View file

@ -11,7 +11,6 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
import Categories from './categories';
import ChannelListHeader from './header';
import LoadChannelsError from './load_channels_error';
import LoadTeamsError from './load_teams_error';
import SubHeader from './subheader';
import ThreadsButton from './threads_button';
@ -27,7 +26,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
type ChannelListProps = {
channelsCount: number;
currentTeamId?: string;
iconPad?: boolean;
isCRTEnabled?: boolean;
isTablet: boolean;
@ -38,7 +36,7 @@ const getTabletWidth = (teamsCount: number) => {
return TABLET_SIDEBAR_WIDTH - (teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0);
};
const CategoriesList = ({channelsCount, currentTeamId, iconPad, isCRTEnabled, isTablet, teamsCount}: ChannelListProps) => {
const CategoriesList = ({channelsCount, iconPad, isCRTEnabled, isTablet, teamsCount}: ChannelListProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const tabletWidth = useSharedValue(isTablet ? getTabletWidth(teamsCount) : 0);
@ -61,18 +59,14 @@ const CategoriesList = ({channelsCount, currentTeamId, iconPad, isCRTEnabled, is
let content;
if (!currentTeamId) {
content = (<LoadTeamsError/>);
} else if (channelsCount < 1) {
content = (<LoadChannelsError teamId={currentTeamId}/>);
if (channelsCount < 1) {
content = (<LoadChannelsError/>);
} else {
content = (
<>
<SubHeader/>
{isCRTEnabled && <ThreadsButton/>}
<Categories
currentTeamId={currentTeamId}
/>
<Categories/>
</>
);
}

View file

@ -6,19 +6,22 @@ import withObservables from '@nozbe/with-observables';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {observeTeam} from '@queries/servers/team';
import {observeCurrentTeam} from '@queries/servers/team';
import LoadChannelsError from './load_channel_error';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables(['teamId'], ({database, teamId}: {teamId: string} & WithDatabaseArgs) => {
const team = observeTeam(database, teamId);
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
const team = observeCurrentTeam(database);
return {
teamDisplayName: team.pipe(
switchMap((t) => of$(t?.displayName)),
),
teamId: team.pipe(
switchMap((t) => of$(t?.id)),
),
};
});

View file

@ -8,6 +8,8 @@ import {retryInitialChannel} from '@actions/remote/retry';
import LoadingError from '@components/loading_error';
import {useServerUrl} from '@context/server';
import LoadTeamsError from '../load_teams_error';
type Props = {
teamDisplayName: string;
teamId: string;
@ -27,6 +29,9 @@ const LoadChannelsError = ({teamDisplayName, teamId}: Props) => {
}
}, [teamId]);
if (!teamId) {
<LoadTeamsError/>;
}
return (
<LoadingError
loading={loading}

View file

@ -20,7 +20,6 @@ import Servers from './servers';
type ChannelProps = {
channelsCount: number;
currentTeamId?: string;
isCRTEnabled: boolean;
teamsCount: number;
time?: number;
@ -95,9 +94,8 @@ const ChannelListScreen = (props: ChannelProps) => {
isTablet={isTablet}
teamsCount={props.teamsCount}
channelsCount={props.channelsCount}
currentTeamId={props.currentTeamId}
/>
{isTablet && Boolean(props.currentTeamId) &&
{isTablet &&
<AdditionalTabletView/>
}
</Animated.View>

View file

@ -3,8 +3,10 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {queryAllMyChannel} from '@queries/servers/channel';
import {queryAllMyChannelsForTeam} from '@queries/servers/channel';
import {observeCurrentTeamId} from '@queries/servers/system';
import {queryMyTeams} from '@queries/servers/team';
import {observeIsCRTEnabled} from '@queries/servers/thread';
@ -14,10 +16,11 @@ import ChannelsList from './channel_list';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
currentTeamId: observeCurrentTeamId(database),
isCRTEnabled: observeIsCRTEnabled(database),
teamsCount: queryMyTeams(database).observeCount(false),
channelsCount: queryAllMyChannel(database).observeCount(),
channelsCount: observeCurrentTeamId(database).pipe(
switchMap((id) => (id ? queryAllMyChannelsForTeam(database, id).observeCount() : of$(0))),
),
}));
export default withDatabase(enhanced(ChannelsList));