Show loading only when team channels are being loaded (#6872)

* Show loading only when team channels are being loaded

* Fix tests

* Remove unneeded event

* Refactor into using hooks
This commit is contained in:
Daniel Espino García 2022-12-19 12:25:23 +01:00 committed by GitHub
parent 63e6b0f626
commit 191a640007
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 125 additions and 135 deletions

View file

@ -24,6 +24,7 @@ import {getNthLastChannelFromTeam, getMyTeamById, getTeamByName, queryMyTeams, r
import {getCurrentUser} from '@queries/servers/user';
import {dismissAllModals, popToRoot} from '@screens/navigation';
import EphemeralStore from '@store/ephemeral_store';
import {setTeamLoading} from '@store/team_load_store';
import {generateChannelNameFromDisplayName, getDirectChannelName, isDMorGM} from '@utils/channel';
import {isTablet} from '@utils/helpers';
import {logDebug, logError, logInfo} from '@utils/log';
@ -359,6 +360,9 @@ export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string,
}
try {
if (!fetchOnly) {
setTeamLoading(serverUrl, true);
}
const [allChannels, channelMemberships, categoriesWithOrder] = await Promise.all([
client.getMyChannels(teamId, includeDeleted, since),
client.getMyChannelMembers(teamId),
@ -387,10 +391,14 @@ export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string,
if (models.length) {
await operator.batchRecords(models);
}
setTeamLoading(serverUrl, false);
}
return {channels, memberships, categories};
} catch (error) {
if (!fetchOnly) {
setTeamLoading(serverUrl, false);
}
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
return {error};
}

View file

@ -6,6 +6,7 @@ import {fetchConfigAndLicense} from '@actions/remote/systems';
import DatabaseManager from '@database/manager';
import {prepareCommonSystemValues, getCurrentTeamId, getWebSocketLastDisconnected, getCurrentChannelId, getConfig, getLicense} from '@queries/servers/system';
import {getCurrentUser} from '@queries/servers/user';
import {setTeamLoading} from '@store/team_load_store';
import {deleteV1Data} from '@utils/file';
import {logInfo} from '@utils/log';
@ -37,8 +38,10 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
const currentChannelId = await getCurrentChannelId(database);
const lastDisconnectedAt = (await getWebSocketLastDisconnected(database)) || since;
setTeamLoading(serverUrl, true);
const entryData = await entry(serverUrl, currentTeamId, currentChannelId, since);
if ('error' in entryData) {
setTeamLoading(serverUrl, false);
return {error: entryData.error};
}
@ -55,6 +58,7 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
const dt = Date.now();
await operator.batchRecords(models);
logInfo('ENTRY MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
setTeamLoading(serverUrl, false);
const {id: currentUserId, locale: currentUserLocale} = meData?.user || (await getCurrentUser(database))!;
const config = await getConfig(database);

View file

@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {Database, Model} from '@nozbe/watermelondb';
import {DeviceEventEmitter} from 'react-native';
import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, handleKickFromChannel, MyChannelsRequest} from '@actions/remote/channel';
import {fetchGroupsForMember} from '@actions/remote/groups';
@ -14,7 +13,7 @@ import {fetchAllTeams, fetchMyTeams, fetchTeamsChannelsAndUnreadPosts, handleKic
import {syncTeamThreads} from '@actions/remote/thread';
import {autoUpdateTimezone, fetchMe, MyUserRequest, updateAllUsersSince} from '@actions/remote/user';
import {gqlAllChannels} from '@client/graphQL/entry';
import {Events, General, Preferences, Screens} from '@constants';
import {General, Preferences, Screens} from '@constants';
import {SYSTEM_IDENTIFIERS} from '@constants/database';
import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy';
import DatabaseManager from '@database/manager';
@ -320,9 +319,7 @@ export async function restDeferredAppEntryActions(
channelsToFetchProfiles = new Set<Channel>(directChannels);
// defer fetching posts for unread channels on initial team
fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships, initialChannelId, true);
} else {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, false);
fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships, initialChannelId);
}
}, FETCH_UNREADS_TIMEOUT);

View file

@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {Database} from '@nozbe/watermelondb';
import {DeviceEventEmitter} from 'react-native';
import {storeConfigAndLicense} from '@actions/local/systems';
import {MyChannelsRequest} from '@actions/remote/channel';
@ -12,7 +11,7 @@ import {MyTeamsRequest} from '@actions/remote/team';
import {syncTeamThreads} from '@actions/remote/thread';
import {autoUpdateTimezone, updateAllUsersSince} from '@actions/remote/user';
import {gqlEntry, gqlEntryChannels, gqlOtherChannels} from '@client/graphQL/entry';
import {Events, Preferences} from '@constants';
import {Preferences} from '@constants';
import DatabaseManager from '@database/manager';
import {getPreferenceValue} from '@helpers/api/preference';
import {selectDefaultTeam} from '@helpers/api/team';
@ -49,9 +48,7 @@ export async function deferredAppEntryGraphQLActions(
setTimeout(() => {
if (chData?.channels?.length && chData.memberships?.length) {
// defer fetching posts for unread channels on initial team
fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships, initialChannelId, true);
} else {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, false);
fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships, initialChannelId);
}
}, FETCH_UNREADS_TIMEOUT);

View file

@ -6,6 +6,7 @@ import {fetchConfigAndLicense} from '@actions/remote/systems';
import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';
import {setCurrentTeamAndChannelId} from '@queries/servers/system';
import {setTeamLoading} from '@store/team_load_store';
import {isTablet} from '@utils/helpers';
import {deferredAppEntryActions, entry} from './gql_common';
@ -47,9 +48,11 @@ export async function loginEntry({serverUrl, user, deviceToken}: AfterLoginArgs)
return {error: clData.error};
}
setTeamLoading(serverUrl, true);
const entryData = await entry(serverUrl, '', '');
if ('error' in entryData) {
setTeamLoading(serverUrl, false);
return {error: entryData.error};
}
@ -66,6 +69,7 @@ export async function loginEntry({serverUrl, user, deviceToken}: AfterLoginArgs)
}
await operator.batchRecords(models);
setTeamLoading(serverUrl, false);
const config = clData.config || {} as ClientConfig;
const license = clData.license || {} as ClientLicense;

View file

@ -14,6 +14,7 @@ import {getIsCRTEnabled} from '@queries/servers/thread';
import {getCurrentUser} from '@queries/servers/user';
import EphemeralStore from '@store/ephemeral_store';
import NavigationStore from '@store/navigation_store';
import {setTeamLoading} from '@store/team_load_store';
import {isTablet} from '@utils/helpers';
import {emitNotificationError} from '@utils/notification';
import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme';
@ -81,8 +82,10 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
switchedToScreen = true;
}
setTeamLoading(serverUrl, true);
const entryData = await entry(serverUrl, teamId, channelId);
if ('error' in entryData) {
setTeamLoading(serverUrl, false);
return {error: entryData.error};
}
const {models, initialTeamId, initialChannelId, prefData, teamData, chData} = entryData;
@ -134,6 +137,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
}
await operator.batchRecords(models);
setTeamLoading(serverUrl, false);
const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(operator.database))!;
const config = await getConfig(database);

View file

@ -326,12 +326,9 @@ export async function fetchPostsForChannel(serverUrl: string, channelId: string,
}
}
export const fetchPostsForUnreadChannels = async (serverUrl: string, channels: Channel[], memberships: ChannelMembership[], excludeChannelId?: string, emitEvent = false) => {
export const fetchPostsForUnreadChannels = async (serverUrl: string, channels: Channel[], memberships: ChannelMembership[], excludeChannelId?: string) => {
try {
const promises = [];
if (emitEvent) {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, true);
}
for (const member of memberships) {
const channel = channels.find((c) => c.id === member.channel_id);
if (channel && (channel.total_msg_count - member.msg_count) > 0 && channel.id !== excludeChannelId) {
@ -339,13 +336,7 @@ export const fetchPostsForUnreadChannels = async (serverUrl: string, channels: C
}
}
await Promise.all(promises);
if (emitEvent) {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, false);
}
} catch (error) {
if (emitEvent) {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, false);
}
return {error};
}

View file

@ -15,6 +15,7 @@ import {prepareCommonSystemValues, getCurrentTeamId, getCurrentUserId} from '@qu
import {addTeamToTeamHistory, prepareDeleteTeam, prepareMyTeams, getNthLastChannelFromTeam, queryTeamsById, syncTeamTable, getLastTeam, getTeamById, removeTeamFromTeamHistory} from '@queries/servers/team';
import {dismissAllModals, popToRoot, resetToTeams} from '@screens/navigation';
import EphemeralStore from '@store/ephemeral_store';
import {setTeamLoading} from '@store/team_load_store';
import {isTablet} from '@utils/helpers';
import {logDebug} from '@utils/log';
@ -56,12 +57,16 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
return {error};
}
let loadEventSent = false;
try {
EphemeralStore.startAddingToTeam(teamId);
const team = await client.getTeam(teamId);
const member = await client.addToTeam(teamId, userId);
if (!fetchOnly) {
setTeamLoading(serverUrl, true);
loadEventSent = true;
fetchRolesIfNeeded(serverUrl, member.roles.split(' '));
const {channels, memberships: channelMembers, categories} = await fetchMyChannelsForTeam(serverUrl, teamId, false, 0, true);
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
@ -80,6 +85,8 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
])).flat();
await operator.batchRecords(models);
setTeamLoading(serverUrl, false);
loadEventSent = false;
if (await isTablet()) {
const channel = await getDefaultChannelForTeam(operator.database, teamId);
@ -87,11 +94,17 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
fetchPostsForChannel(serverUrl, channel.id);
}
}
} else {
setTeamLoading(serverUrl, false);
loadEventSent = false;
}
}
EphemeralStore.finishAddingToTeam(teamId);
return {member};
} catch (error) {
if (loadEventSent) {
setTeamLoading(serverUrl, false);
}
EphemeralStore.finishAddingToTeam(teamId);
forceLogoutIfNecessary(serverUrl, error as ClientError);
return {error};

View file

@ -1,8 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {DeviceEventEmitter} from 'react-native';
import {markChannelAsViewed} from '@actions/local/channel';
import {markChannelAsRead} from '@actions/remote/channel';
import {handleEntryAfterLoadNavigation} from '@actions/remote/entry/common';
@ -30,11 +28,10 @@ import {
handleCallUserVoiceOn,
} from '@calls/connection/websocket_event_handlers';
import {isSupportedServerCalls} from '@calls/utils';
import {Events, Screens, WebsocketEvents} from '@constants';
import {Screens, WebsocketEvents} from '@constants';
import {SYSTEM_IDENTIFIERS} from '@constants/database';
import DatabaseManager from '@database/manager';
import AppsManager from '@managers/apps_manager';
import {getActiveServerUrl} from '@queries/app/servers';
import {getCurrentChannel} from '@queries/servers/channel';
import {getLastPostInThread} from '@queries/servers/post';
import {
@ -50,6 +47,7 @@ import {getIsCRTEnabled} from '@queries/servers/thread';
import {getCurrentUser} from '@queries/servers/user';
import EphemeralStore from '@store/ephemeral_store';
import NavigationStore from '@store/navigation_store';
import {setTeamLoading} from '@store/team_load_store';
import {isTablet} from '@utils/helpers';
import {logDebug, logInfo} from '@utils/log';
@ -141,15 +139,10 @@ async function doReconnect(serverUrl: string) {
const currentTeam = await getCurrentTeam(database);
const currentChannel = await getCurrentChannel(database);
const currentActiveServerUrl = await getActiveServerUrl();
if (serverUrl === currentActiveServerUrl) {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, true);
}
setTeamLoading(serverUrl, true);
const entryData = await entry(serverUrl, currentTeam?.id, currentChannel?.id, lastDisconnectedAt);
if ('error' in entryData) {
if (serverUrl === currentActiveServerUrl) {
DeviceEventEmitter.emit(Events.FETCHING_POSTS, false);
}
setTeamLoading(serverUrl, false);
return;
}
const {models, initialTeamId, initialChannelId, prefData, teamData, chData} = entryData;
@ -159,6 +152,7 @@ async function doReconnect(serverUrl: string) {
const dt = Date.now();
await operator.batchRecords(models);
logInfo('WEBSOCKET RECONNECT MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
setTeamLoading(serverUrl, false);
await fetchPostDataIfNeeded(serverUrl);

View file

@ -14,6 +14,7 @@ import {prepareMyChannelsForTeam} from '@queries/servers/channel';
import {getCurrentTeam, prepareMyTeams} from '@queries/servers/team';
import {getCurrentUser} from '@queries/servers/user';
import EphemeralStore from '@store/ephemeral_store';
import {setTeamLoading} from '@store/team_load_store';
import {logDebug} from '@utils/log';
export async function handleLeaveTeamEvent(serverUrl: string, msg: WebSocketMessage) {
@ -62,10 +63,6 @@ export async function handleUpdateTeamEvent(serverUrl: string, msg: WebSocketMes
}
export async function handleUserAddedToTeamEvent(serverUrl: string, msg: WebSocketMessage) {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return;
}
const {team_id: teamId} = msg.data;
// Ignore duplicated team join events sent by the server
@ -74,24 +71,32 @@ export async function handleUserAddedToTeamEvent(serverUrl: string, msg: WebSock
}
EphemeralStore.startAddingToTeam(teamId);
const {teams, memberships: teamMemberships} = await fetchMyTeam(serverUrl, teamId, true);
try {
setTeamLoading(serverUrl, true);
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
const {teams, memberships: teamMemberships} = await fetchMyTeam(serverUrl, teamId, true);
const modelPromises: Array<Promise<Model[]>> = [];
if (teams?.length && teamMemberships?.length) {
const {channels, memberships, categories} = await fetchMyChannelsForTeam(serverUrl, teamId, false, 0, true);
modelPromises.push(prepareCategoriesAndCategoriesChannels(operator, categories || [], true));
modelPromises.push(...await prepareMyChannelsForTeam(operator, teamId, channels || [], memberships || []));
const modelPromises: Array<Promise<Model[]>> = [];
if (teams?.length && teamMemberships?.length) {
const {channels, memberships, categories} = await fetchMyChannelsForTeam(serverUrl, teamId, false, 0, true);
modelPromises.push(prepareCategoriesAndCategoriesChannels(operator, categories || [], true));
modelPromises.push(...await prepareMyChannelsForTeam(operator, teamId, channels || [], memberships || []));
const {roles} = await fetchRoles(serverUrl, teamMemberships, memberships, undefined, true);
modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true}));
const {roles} = await fetchRoles(serverUrl, teamMemberships, memberships, undefined, true);
modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true}));
}
if (teams && teamMemberships) {
modelPromises.push(...prepareMyTeams(operator, teams, teamMemberships));
}
const models = await Promise.all(modelPromises);
await operator.batchRecords(models.flat());
setTeamLoading(serverUrl, false);
} catch (error) {
logDebug('could not handle user added to team websocket event');
setTeamLoading(serverUrl, false);
}
if (teams && teamMemberships) {
modelPromises.push(...prepareMyTeams(operator, teams, teamMemberships));
}
const models = await Promise.all(modelPromises);
await operator.batchRecords(models.flat());
EphemeralStore.finishAddingToTeam(teamId);
}

View file

@ -9,7 +9,6 @@ export default keyMirror({
CHANNEL_SWITCH: null,
CLOSE_BOTTOM_SHEET: null,
CONFIG_CHANGED: null,
FETCHING_POSTS: null,
FREEZE_SCREEN: null,
GALLERY_ACTIONS: null,
LEAVE_CHANNEL: null,

View file

@ -0,0 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useEffect, useState} from 'react';
import {of as of$} from 'rxjs';
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
import {getLoadingTeamChannelsSubject} from '@store/team_load_store';
export const useTeamsLoading = (serverUrl: string) => {
// const subject = getLoadingTeamChannelsSubject(serverUrl);
// const [loading, setLoading] = useState(subject.getValue() !== 0);
const [loading, setLoading] = useState(false);
useEffect(() => {
const sub = getLoadingTeamChannelsSubject(serverUrl).pipe(
switchMap((v) => of$(v !== 0)),
distinctUntilChanged(),
).subscribe(setLoading);
return () => sub.unsubscribe();
}, []);
return loading;
};

View file

@ -144,40 +144,6 @@ exports[`components/categories_list should render channels error 1`] = `
}
testID="channel_list_header.server_display_name"
/>
<View
animatedStyle={
{
"value": {
"opacity": 1,
"transform": [
{
"rotateZ": "0deg",
},
],
},
}
}
collapsable={false}
style={
{
"borderBottomColor": "rgba(255,255,255,0.16)",
"borderLeftColor": "#ffffff",
"borderRadius": 7,
"borderRightColor": "rgba(255,255,255,0.16)",
"borderTopColor": "rgba(255,255,255,0.16)",
"borderWidth": 2,
"height": 14,
"marginLeft": 5,
"opacity": 1,
"transform": [
{
"rotateZ": "0deg",
},
],
"width": 14,
}
}
/>
</View>
</View>
<View

View file

@ -7,6 +7,7 @@ import {useIntl} from 'react-intl';
import {retryInitialTeamAndChannel} from '@actions/remote/retry';
import LoadingError from '@components/loading_error';
import {useServerDisplayName, useServerUrl} from '@context/server';
import {setTeamLoading} from '@store/team_load_store';
const LoadCategoriesError = () => {
const {formatMessage} = useIntl();
@ -16,7 +17,10 @@ const LoadCategoriesError = () => {
const onRetryTeams = useCallback(async () => {
setLoading(true);
setTeamLoading(serverUrl, true);
const {error} = await retryInitialTeamAndChannel(serverUrl);
setTeamLoading(serverUrl, false);
if (error) {
setLoading(false);

View file

@ -124,40 +124,6 @@ exports[`components/channel_list/header Channel List Header Component should mat
}
testID="channel_list_header.server_display_name"
/>
<View
animatedStyle={
{
"value": {
"opacity": 1,
"transform": [
{
"rotateZ": "0deg",
},
],
},
}
}
collapsable={false}
style={
{
"borderBottomColor": "rgba(255,255,255,0.16)",
"borderLeftColor": "#ffffff",
"borderRadius": 7,
"borderRightColor": "rgba(255,255,255,0.16)",
"borderTopColor": "rgba(255,255,255,0.16)",
"borderWidth": 2,
"height": 14,
"marginLeft": 5,
"opacity": 1,
"transform": [
{
"rotateZ": "0deg",
},
],
"width": 14,
}
}
/>
</View>
</View>
`;

View file

@ -1,12 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect, useState} from 'react';
import {DeviceEventEmitter} from 'react-native';
import React, {useEffect} from 'react';
import Animated, {cancelAnimation, Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming} from 'react-native-reanimated';
import {Events} from '@constants';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useTeamsLoading} from '@hooks/teams_loading';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
@ -26,9 +26,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
const LoadingUnreads = () => {
const theme = useTheme();
const style = getStyleSheet(theme);
const [loading, setLoading] = useState(true);
const opacity = useSharedValue(1);
const rotation = useSharedValue(0);
const serverUrl = useServerUrl();
const loading = useTeamsLoading(serverUrl);
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
@ -52,17 +53,6 @@ const LoadingUnreads = () => {
};
}, [loading]);
useEffect(() => {
const listener = DeviceEventEmitter.addListener(Events.FETCHING_POSTS, (value: boolean) => {
setLoading(value);
if (value) {
rotation.value = 0;
}
});
return () => listener.remove();
}, []);
if (!loading) {
return null;
}

View file

@ -7,6 +7,7 @@ import {useIntl} from 'react-intl';
import {retryInitialChannel} from '@actions/remote/retry';
import LoadingError from '@components/loading_error';
import {useServerUrl} from '@context/server';
import {setTeamLoading} from '@store/team_load_store';
import LoadTeamsError from '../load_teams_error';
@ -22,7 +23,10 @@ const LoadChannelsError = ({teamDisplayName, teamId}: Props) => {
const onRetryTeams = useCallback(async () => {
setLoading(true);
setTeamLoading(serverUrl, true);
const {error} = await retryInitialChannel(serverUrl, teamId);
setTeamLoading(serverUrl, false);
if (error) {
setLoading(false);

View file

@ -7,6 +7,7 @@ import {useIntl} from 'react-intl';
import {retryInitialTeamAndChannel} from '@actions/remote/retry';
import LoadingError from '@components/loading_error';
import {useServerDisplayName, useServerUrl} from '@context/server';
import {setTeamLoading} from '@store/team_load_store';
const LoadTeamsError = () => {
const {formatMessage} = useIntl();
@ -16,7 +17,10 @@ const LoadTeamsError = () => {
const onRetryTeams = useCallback(async () => {
setLoading(true);
setTeamLoading(serverUrl, true);
const {error} = await retryInitialTeamAndChannel(serverUrl);
setTeamLoading(serverUrl, false);
if (error) {
setLoading(false);

View file

@ -252,7 +252,6 @@ export function resetToHome(passProps: LaunchProps = {launchType: Launch.Normal}
dismissModal({componentId: Screens.LOGIN});
dismissModal({componentId: Screens.SSO});
dismissModal({componentId: Screens.BOTTOM_SHEET});
DeviceEventEmitter.emit(Events.FETCHING_POSTS, false);
if (passProps.launchType === Launch.AddServerFromDeepLink) {
Navigation.updateProps(Screens.HOME, {launchType: Launch.DeepLink, extra: passProps.extra});
}

View file

@ -0,0 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BehaviorSubject} from 'rxjs';
const loadingTeamChannels: {[serverUrl: string]: BehaviorSubject<number>} = {};
export const getLoadingTeamChannelsSubject = (serverUrl: string) => {
if (!loadingTeamChannels[serverUrl]) {
loadingTeamChannels[serverUrl] = new BehaviorSubject(0);
}
return loadingTeamChannels[serverUrl];
};
export const setTeamLoading = (serverUrl: string, loading: boolean) => {
const subject = getLoadingTeamChannelsSubject(serverUrl);
subject.next(subject.value + (loading ? 1 : -1));
};