forked from Ivasoft/mattermost-mobile
[Gekidou] app entry (#5609)
* Add channelEntry action * Refactor appEntry * Lint fix * Some duplication removed * Refactor app entry * Refactor app entry and add prepareDestroyPermanentlyWithAssociations * Handle team deletion * Sync channels * Lint fix * Address review comments, take 1 * Address review comments, take 2 * Set initial channel ID to empty * Fix types * sort imports * clean up app entry point * Fix post header & highlight reply bar * Style channel list text * iOS Pods sync * Rename goToChannel to goToHome * Sync ios Gemfile.lock Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
@@ -8,22 +8,26 @@ import {General, Preferences} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getPreferenceValue, getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import {selectDefaultTeam} from '@helpers/api/team';
|
||||
import {DEFAULT_LOCALE} from '@i18n';
|
||||
import NetworkManager from '@init/network_manager';
|
||||
import {prepareMyChannelsForTeam} from '@queries/servers/channel';
|
||||
import {prepareMyPreferences} from '@queries/servers/preference';
|
||||
import {prepareCommonSystemValues} from '@queries/servers/system';
|
||||
import {addChannelToTeamHistory, prepareMyTeams} from '@queries/servers/team';
|
||||
import {queryAllChannelsForTeam, queryChannelsById} from '@queries/servers/channel';
|
||||
import {prepareModels} from '@queries/servers/entry';
|
||||
import {prepareCommonSystemValues, queryCommonSystemValues, queryConfig, queryCurrentTeamId, queryWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {addChannelToTeamHistory, deleteMyTeams, queryAvailableTeamIds, queryMyTeams, queryTeamsById} from '@queries/servers/team';
|
||||
import {queryCurrentUser} from '@queries/servers/user';
|
||||
import {selectDefaultChannelForTeam} from '@utils/channel';
|
||||
|
||||
import {fetchMissingSidebarInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel';
|
||||
import {fetchGroupsForTeam} from './group';
|
||||
import {fetchPostsForChannel, fetchPostsForUnreadChannels} from './post';
|
||||
import {MyPreferencesRequest, fetchMyPreferences} from './preference';
|
||||
import {fetchRolesIfNeeded} from './role';
|
||||
import {fetchRoles, fetchRolesIfNeeded} from './role';
|
||||
import {ConfigAndLicenseRequest, fetchConfigAndLicense} from './systems';
|
||||
import {fetchMyTeams, fetchTeamsChannelsAndUnreadPosts, MyTeamsRequest} from './team';
|
||||
import {fetchMe, MyUserRequest} from './user';
|
||||
|
||||
import type {Client} from '@client/rest';
|
||||
import type ClientError from '@client/rest/error';
|
||||
|
||||
type AfterLoginArgs = {
|
||||
serverUrl: string;
|
||||
@@ -31,10 +35,77 @@ type AfterLoginArgs = {
|
||||
deviceToken?: string;
|
||||
}
|
||||
|
||||
type AppEntryData = {
|
||||
initialTeamId: string;
|
||||
teamData: MyTeamsRequest;
|
||||
chData?: MyChannelsRequest;
|
||||
prefData: MyPreferencesRequest;
|
||||
meData: MyUserRequest;
|
||||
removeTeamIds?: string[];
|
||||
removeChannelIds?: string[];
|
||||
}
|
||||
|
||||
type AppEntryError = {
|
||||
error?: Error | ClientError | string;
|
||||
}
|
||||
|
||||
export const appEntry = async (serverUrl: string) => {
|
||||
const dt = Date.now();
|
||||
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
const {database} = operator;
|
||||
|
||||
const currentTeamId = await queryCurrentTeamId(database);
|
||||
const fetchedData = await fetchAppEntryData(serverUrl, currentTeamId);
|
||||
const fetchedError = (fetchedData as AppEntryError).error;
|
||||
|
||||
if (fetchedError) {
|
||||
return {error: fetchedError, time: Date.now() - dt};
|
||||
}
|
||||
|
||||
const {initialTeamId, teamData, chData, prefData, meData, removeTeamIds, removeChannelIds} = fetchedData as AppEntryData;
|
||||
|
||||
if (initialTeamId !== currentTeamId) {
|
||||
// Immediately set the new team as the current team in the database so that the UI
|
||||
// renders the correct team.
|
||||
setCurrentTeamAndChannelId(operator, initialTeamId, '');
|
||||
}
|
||||
|
||||
let removeTeams;
|
||||
if (removeTeamIds?.length) {
|
||||
// Immediately delete myTeams so that the UI renders only teams the user is a member of.
|
||||
removeTeams = await queryTeamsById(database, removeTeamIds);
|
||||
await deleteMyTeams(operator, removeTeams!);
|
||||
}
|
||||
|
||||
fetchRoles(serverUrl, teamData?.memberships, chData?.memberships, meData?.user);
|
||||
|
||||
let removeChannels;
|
||||
if (removeChannelIds?.length) {
|
||||
removeChannels = await queryChannelsById(database, removeChannelIds);
|
||||
}
|
||||
|
||||
const modelPromises = await prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData});
|
||||
const models = await Promise.all(modelPromises);
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models.flat() as Model[]);
|
||||
}
|
||||
|
||||
const {id: currentUserId, locale: currentUserLocale} = meData.user || (await queryCurrentUser(database))!;
|
||||
const {config, license} = await queryCommonSystemValues(database);
|
||||
deferredAppEntryActions(serverUrl, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, initialTeamId);
|
||||
|
||||
const error = teamData.error || chData?.error || prefData.error || meData.error;
|
||||
return {error, time: Date.now() - dt};
|
||||
};
|
||||
|
||||
export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs) => {
|
||||
const dt = Date.now();
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (!database) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
@@ -111,29 +182,7 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
}
|
||||
}
|
||||
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
const {operator} = DatabaseManager.serverDatabases[serverUrl];
|
||||
|
||||
if (prefData.preferences) {
|
||||
const prefModel = prepareMyPreferences(operator, prefData.preferences!);
|
||||
if (prefModel) {
|
||||
modelPromises.push(prefModel);
|
||||
}
|
||||
}
|
||||
|
||||
if (teamData.teams) {
|
||||
const teamModels = prepareMyTeams(operator, teamData.teams!, teamData.memberships!, teamData.unreads!);
|
||||
if (teamModels) {
|
||||
modelPromises.push(...teamModels);
|
||||
}
|
||||
}
|
||||
|
||||
if (chData?.channels?.length) {
|
||||
const channelModels = await prepareMyChannelsForTeam(operator, initialTeam!.id, chData.channels, chData.memberships!);
|
||||
if (channelModels) {
|
||||
modelPromises.push(...channelModels);
|
||||
}
|
||||
}
|
||||
const modelPromises = await prepareModels({operator, teamData, chData, prefData, initialTeamId: initialTeam?.id});
|
||||
|
||||
const systemModels = prepareCommonSystemValues(
|
||||
operator,
|
||||
@@ -167,7 +216,6 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
const error = clData.error || prefData.error || teamData.error || chData?.error;
|
||||
return {error, time: Date.now() - dt, hasTeams: Boolean((myTeams?.length || 0) > 0 && !teamData.error)};
|
||||
} catch (error) {
|
||||
const {operator} = DatabaseManager.serverDatabases[serverUrl];
|
||||
const systemModels = await prepareCommonSystemValues(operator, {
|
||||
config: ({} as ClientConfig),
|
||||
license: ({} as ClientLicense),
|
||||
@@ -182,6 +230,145 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
}
|
||||
};
|
||||
|
||||
const fetchAppEntryData = async (serverUrl: string, initialTeamId: string): Promise<AppEntryData | AppEntryError> => {
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (!database) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
const lastDisconnected = await queryWebSocketLastDisconnected(database);
|
||||
const includeDeletedChannels = true;
|
||||
const fetchOnly = true;
|
||||
|
||||
// Fetch in parallel teams / team membership / team unreads / channels for current team / user preferences / user
|
||||
const promises: [Promise<MyTeamsRequest>, Promise<MyChannelsRequest | undefined>, Promise<MyPreferencesRequest>, Promise<MyUserRequest>] = [
|
||||
fetchMyTeams(serverUrl, fetchOnly),
|
||||
initialTeamId ? fetchMyChannelsForTeam(serverUrl, initialTeamId, includeDeletedChannels, lastDisconnected, fetchOnly) : Promise.resolve(undefined),
|
||||
fetchMyPreferences(serverUrl, fetchOnly),
|
||||
fetchMe(serverUrl, fetchOnly),
|
||||
];
|
||||
|
||||
const resolution = await Promise.all(promises);
|
||||
const [teamData, , prefData, meData] = resolution;
|
||||
let [, chData] = resolution;
|
||||
|
||||
if (!initialTeamId && teamData.teams?.length && teamData.memberships?.length) {
|
||||
// If no initial team was set in the database but got teams in the response
|
||||
const config = await queryConfig(database);
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string;
|
||||
const teamMembers = teamData.memberships.map((m) => m.team_id);
|
||||
const myTeams = teamData.teams!.filter((t) => teamMembers?.includes(t.id));
|
||||
const defaultTeam = selectDefaultTeam(myTeams, meData.user?.locale || DEFAULT_LOCALE, teamOrderPreference, config.ExperimentalPrimaryTeam);
|
||||
if (defaultTeam?.id) {
|
||||
chData = await fetchMyChannelsForTeam(serverUrl, defaultTeam.id, includeDeletedChannels, lastDisconnected, fetchOnly);
|
||||
}
|
||||
}
|
||||
|
||||
let data: AppEntryData = {
|
||||
initialTeamId,
|
||||
teamData,
|
||||
chData,
|
||||
prefData,
|
||||
meData,
|
||||
};
|
||||
|
||||
if (teamData.teams?.length === 0) {
|
||||
// User is no longer a member of any team
|
||||
const myTeams = await queryMyTeams(database);
|
||||
const removeTeamIds: string[] = myTeams?.map((myTeam) => myTeam.id) || [];
|
||||
|
||||
return {
|
||||
...data,
|
||||
initialTeamId: '',
|
||||
removeTeamIds,
|
||||
};
|
||||
}
|
||||
|
||||
const inTeam = teamData.teams?.find((t) => t.id === initialTeamId);
|
||||
const chError = chData?.error as ClientError | undefined;
|
||||
if (!inTeam || chError?.status_code === 403) {
|
||||
// User is no longer a member of the current team
|
||||
const removeTeamIds = [initialTeamId];
|
||||
|
||||
const availableTeamIds = await queryAvailableTeamIds(database, initialTeamId, teamData.teams, prefData.preferences, meData.user?.locale);
|
||||
const alternateTeamData = await fetchAlternateTeamData(serverUrl, availableTeamIds, removeTeamIds, includeDeletedChannels, lastDisconnected, fetchOnly);
|
||||
|
||||
data = {
|
||||
...data,
|
||||
...alternateTeamData,
|
||||
};
|
||||
}
|
||||
|
||||
if (data.chData?.channels) {
|
||||
const removeChannelIds: string[] = [];
|
||||
const fetchedChannelIds = data.chData.channels.map((channel) => channel.id);
|
||||
|
||||
const channels = await queryAllChannelsForTeam(database, initialTeamId);
|
||||
for (const channel of channels) {
|
||||
if (!fetchedChannelIds.includes(channel.id)) {
|
||||
removeChannelIds.push(channel.id);
|
||||
}
|
||||
}
|
||||
|
||||
data = {
|
||||
...data,
|
||||
removeChannelIds,
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const fetchAlternateTeamData = async (serverUrl: string, availableTeamIds: string[], removeTeamIds: string[], includeDeleted = true, since = 0, fetchOnly = false) => {
|
||||
let initialTeamId = '';
|
||||
let chData;
|
||||
|
||||
for (const teamId of availableTeamIds) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
chData = await fetchMyChannelsForTeam(serverUrl, teamId, includeDeleted, since, fetchOnly);
|
||||
const chError = chData.error as ClientError | undefined;
|
||||
if (chError?.status_code === 403) {
|
||||
removeTeamIds.push(teamId);
|
||||
} else {
|
||||
initialTeamId = teamId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (chData) {
|
||||
return {initialTeamId, chData, removeTeamIds};
|
||||
}
|
||||
|
||||
return {initialTeamId, removeTeamIds};
|
||||
};
|
||||
|
||||
const deferredAppEntryActions = async (
|
||||
serverUrl: string, currentUserId: string, currentUserLocale: string, preferences: PreferenceType[] | undefined, config: ClientConfig, license: ClientLicense, teamData: MyTeamsRequest,
|
||||
chData: MyChannelsRequest | undefined, initialTeamId: string) => {
|
||||
// defer sidebar DM & GM profiles
|
||||
if (chData?.channels?.length && chData.memberships?.length) {
|
||||
const directChannels = chData.channels.filter((c) => c.type === General.DM_CHANNEL || c.type === General.GM_CHANNEL);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
if (channelsToFetchProfiles.size) {
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config, license);
|
||||
await fetchMissingSidebarInfo(serverUrl, Array.from(channelsToFetchProfiles), currentUserLocale, teammateDisplayNameSetting, currentUserId);
|
||||
}
|
||||
|
||||
// defer fetching posts for unread channels on initial team
|
||||
fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships);
|
||||
}
|
||||
|
||||
// defer groups for team
|
||||
if (initialTeamId) {
|
||||
await fetchGroupsForTeam(serverUrl, initialTeamId);
|
||||
}
|
||||
|
||||
// defer fetch channels and unread posts for other teams
|
||||
if (teamData.teams?.length && teamData.memberships?.length) {
|
||||
fetchTeamsChannelsAndUnreadPosts(serverUrl, teamData.teams, teamData.memberships, initialTeamId);
|
||||
}
|
||||
};
|
||||
|
||||
const deferredLoginActions = async (
|
||||
serverUrl: string, user: UserProfile, prefData: MyPreferencesRequest, clData: ConfigAndLicenseRequest, teamData: MyTeamsRequest,
|
||||
chData?: MyChannelsRequest, initialTeam?: Team, initialChannel?: Channel) => {
|
||||
|
||||
@@ -56,3 +56,30 @@ export const fetchRolesIfNeeded = async (serverUrl: string, updatedRoles: string
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchRoles = async (serverUrl: string, teamMembership?: TeamMembership[], channelMembership?: ChannelMembership[], user?: UserProfile) => {
|
||||
const rolesToFetch = new Set<string>(user?.roles.split(' ') || []);
|
||||
|
||||
if (teamMembership?.length) {
|
||||
const teamRoles: string[] = [];
|
||||
const teamMembers: string[] = [];
|
||||
|
||||
teamMembership?.forEach((tm) => {
|
||||
teamRoles.push(...tm.roles.split(' '));
|
||||
teamMembers.push(tm.team_id);
|
||||
});
|
||||
|
||||
teamRoles.forEach(rolesToFetch.add, rolesToFetch);
|
||||
}
|
||||
|
||||
if (channelMembership?.length) {
|
||||
for (let i = 0; i < channelMembership!.length; i++) {
|
||||
const member = channelMembership[i];
|
||||
member.roles.split(' ').forEach(rolesToFetch.add, rolesToFetch);
|
||||
}
|
||||
}
|
||||
|
||||
if (rolesToFetch.size > 0) {
|
||||
fetchRolesIfNeeded(serverUrl, Array.from(rolesToFetch));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,6 +20,11 @@ import type {LoadMeArgs} from '@typings/database/database';
|
||||
import type RoleModel from '@typings/database/models/servers/role';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
export type MyUserRequest = {
|
||||
user?: UserProfile;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export type ProfilesPerChannelRequest = {
|
||||
data?: ProfilesInChannelRequest[];
|
||||
error?: unknown;
|
||||
@@ -31,6 +36,31 @@ export type ProfilesInChannelRequest = {
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise<MyUserRequest> => {
|
||||
let client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await client.getMe();
|
||||
|
||||
if (!fetchOnly) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (operator) {
|
||||
operator.handleUsers({users: [user], prepareRecordsOnly: false});
|
||||
}
|
||||
}
|
||||
|
||||
return {user};
|
||||
} catch (error) {
|
||||
await forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchProfilesInChannel = async (serverUrl: string, channelId: string, excludeUserId?: string, fetchOnly = false): Promise<ProfilesInChannelRequest> => {
|
||||
let client: Client;
|
||||
try {
|
||||
|
||||
@@ -21,6 +21,7 @@ import type SystemModel from '@typings/database/models/servers/system';
|
||||
|
||||
type HeaderInputProps = {
|
||||
config: ClientConfig;
|
||||
differentThreadSequence: boolean;
|
||||
license: ClientLicense;
|
||||
preferences: PreferenceModel[];
|
||||
post: PostModel;
|
||||
@@ -33,8 +34,8 @@ const withBaseHeaderProps = withObservables([], ({database}: WithDatabaseArgs &
|
||||
}));
|
||||
|
||||
const withHeaderProps = withObservables(
|
||||
['preferences', 'post'],
|
||||
({config, post, license, database, preferences}: WithDatabaseArgs & HeaderInputProps) => {
|
||||
['preferences', 'post', 'differentThreadSequence'],
|
||||
({config, post, license, database, preferences, differentThreadSequence}: WithDatabaseArgs & HeaderInputProps) => {
|
||||
const author = post.author.observe();
|
||||
const enablePostUsernameOverride = of$(config.EnablePostUsernameOverride === 'true');
|
||||
const isTimezoneEnabled = of$(config.ExperimentalTimezone === 'true');
|
||||
@@ -47,13 +48,13 @@ const withHeaderProps = withObservables(
|
||||
Q.where('delete_at', Q.eq(0)),
|
||||
),
|
||||
).observeCount();
|
||||
const rootPostAuthor = post.root.observe().pipe(switchMap((root) => {
|
||||
const rootPostAuthor = differentThreadSequence ? post.root.observe().pipe(switchMap((root) => {
|
||||
if (root.length) {
|
||||
return root[0].author.observe();
|
||||
}
|
||||
|
||||
return of$(null);
|
||||
}));
|
||||
})) : of$(null);
|
||||
|
||||
return {
|
||||
author,
|
||||
|
||||
@@ -40,7 +40,7 @@ async function shouldHighlightReplyBar(currentUser: UserModel, post: PostModel,
|
||||
let rootPost: PostModel | undefined;
|
||||
const myPosts = await postsInThread.collections.get(POST).query(
|
||||
Q.and(
|
||||
Q.where('root_id', post.id || post.rootId),
|
||||
Q.where('root_id', post.rootId || post.id),
|
||||
Q.where('create_at', Q.between(postsInThread.earliest, postsInThread.latest)),
|
||||
Q.where('user_id', currentUser.id),
|
||||
),
|
||||
@@ -59,8 +59,8 @@ async function shouldHighlightReplyBar(currentUser: UserModel, post: PostModel,
|
||||
commentsNotifyLevel = currentUser.notifyProps.comments;
|
||||
}
|
||||
|
||||
const fromCurrentUser = post.userId !== currentUser.id || Boolean(post.props?.from_webhook);
|
||||
if (!fromCurrentUser) {
|
||||
const notCurrentUser = post.userId !== currentUser.id || Boolean(post.props?.from_webhook);
|
||||
if (notCurrentUser) {
|
||||
if (commentsNotifyLevel === Preferences.COMMENTS_ANY && (threadCreatedByCurrentUser || threadRepliedToByCurrentUser)) {
|
||||
return true;
|
||||
} else if (commentsNotifyLevel === Preferences.COMMENTS_ROOT && threadCreatedByCurrentUser) {
|
||||
@@ -109,8 +109,9 @@ const withPost = withObservables(
|
||||
return of$(false);
|
||||
}));
|
||||
|
||||
let differentThreadSequence = true;
|
||||
if (post.rootId) {
|
||||
const differentThreadSequence = previousPost?.rootId ? previousPost?.rootId !== post.rootId : previousPost?.id !== post.rootId;
|
||||
differentThreadSequence = previousPost?.rootId ? previousPost?.rootId !== post.rootId : previousPost?.id !== post.rootId;
|
||||
isFirstReply = of$(differentThreadSequence || (previousPost?.id === post.rootId || previousPost?.rootId === post.rootId));
|
||||
isLastReply = of$(!(nextPost?.rootId === post.rootId));
|
||||
}
|
||||
@@ -130,6 +131,7 @@ const withPost = withObservables(
|
||||
appsEnabled: of$(appsEnabled(partialConfig)),
|
||||
canDelete,
|
||||
currentUser,
|
||||
differentThreadSequence: of$(differentThreadSequence),
|
||||
files: post.files.observe(),
|
||||
highlightReplyBar,
|
||||
isConsecutivePost,
|
||||
|
||||
@@ -32,6 +32,7 @@ type PostProps = {
|
||||
appsEnabled: boolean;
|
||||
canDelete: boolean;
|
||||
currentUser: UserModel;
|
||||
differentThreadSequence: boolean;
|
||||
files: FileModel[];
|
||||
highlight?: boolean;
|
||||
highlightPinnedOrFlagged?: boolean;
|
||||
@@ -95,7 +96,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
});
|
||||
|
||||
const Post = ({
|
||||
appsEnabled, canDelete, currentUser, files, highlight, highlightPinnedOrFlagged = true, highlightReplyBar,
|
||||
appsEnabled, canDelete, currentUser, differentThreadSequence, files, highlight, highlightPinnedOrFlagged = true, highlightReplyBar,
|
||||
isConsecutivePost, isEphemeral, isFirstReply, isFlagged, isJumboEmoji, isLastReply, isPostAddChannelMember,
|
||||
location, post, reactionsCount, shouldRenderReplyButton, skipFlaggedHeader, skipPinnedHeader, showAddReaction = true, style,
|
||||
testID,
|
||||
@@ -207,6 +208,7 @@ const Post = ({
|
||||
header = (
|
||||
<Header
|
||||
currentUser={currentUser}
|
||||
differentThreadSequence={differentThreadSequence}
|
||||
isAutoResponse={isAutoResponder}
|
||||
isEphemeral={isEphemeral}
|
||||
isPendingOrFailed={isPendingOrFailed}
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
import {Linking} from 'react-native';
|
||||
import {Notifications} from 'react-native-notifications';
|
||||
|
||||
import {appEntry} from '@actions/remote/entry';
|
||||
import {Screens} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getActiveServerUrl, getServerCredentials} from '@init/credentials';
|
||||
import {queryThemeForCurrentTeam} from '@queries/servers/preference';
|
||||
import {goToScreen, resetToChannel, resetToSelectServer} from '@screens/navigation';
|
||||
import {goToScreen, resetToHome, resetToSelectServer} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {DeepLinkChannel, DeepLinkDM, DeepLinkGM, DeepLinkPermalink, DeepLinkType, DeepLinkWithData, LaunchProps, LaunchType} from '@typings/launch';
|
||||
import {parseDeepLink} from '@utils/url';
|
||||
@@ -69,7 +70,9 @@ const launchApp = async (props: LaunchProps, resetNavigation = true) => {
|
||||
if (database) {
|
||||
EphemeralStore.theme = await queryThemeForCurrentTeam(database);
|
||||
}
|
||||
launchToChannel({...props, serverUrl}, resetNavigation);
|
||||
|
||||
launchToHome({...props, serverUrl}, resetNavigation);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -77,8 +80,20 @@ const launchApp = async (props: LaunchProps, resetNavigation = true) => {
|
||||
launchToServer(props, resetNavigation);
|
||||
};
|
||||
|
||||
const launchToChannel = (props: LaunchProps, resetNavigation: Boolean) => {
|
||||
// TODO: Use LaunchProps to fetch posts for channel and then load user profile, etc...
|
||||
const launchToHome = (props: LaunchProps, resetNavigation: Boolean) => {
|
||||
switch (props.launchType) {
|
||||
case LaunchType.DeepLink:
|
||||
// TODO:
|
||||
// deepLinkEntry({props.serverUrl, props.extra});
|
||||
break;
|
||||
case LaunchType.Notification: {
|
||||
// TODO:
|
||||
// pushNotificationEntry({props.serverUrl, props.extra})
|
||||
break;
|
||||
}
|
||||
default:
|
||||
appEntry(props.serverUrl!);
|
||||
}
|
||||
|
||||
const passProps = {
|
||||
skipMetrics: true,
|
||||
@@ -87,8 +102,8 @@ const launchToChannel = (props: LaunchProps, resetNavigation: Boolean) => {
|
||||
|
||||
if (resetNavigation) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Launch app in Channel screen');
|
||||
resetToChannel(passProps);
|
||||
console.log('Launch app in Home screen');
|
||||
resetToHome(passProps);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import {prepareDeletePost} from './post';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type ChannelInfoModel from '@typings/database/models/servers/channel_info';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
|
||||
const {SERVER: {CHANNEL, MY_CHANNEL}} = MM_TABLES;
|
||||
|
||||
@@ -53,6 +56,41 @@ export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, tea
|
||||
}
|
||||
};
|
||||
|
||||
export const prepareDeleteChannel = async (channel: ChannelModel): Promise<Model[]> => {
|
||||
const preparedModels: Model[] = [channel.prepareDestroyPermanently()];
|
||||
|
||||
const relations: Array<Relation<Model>> = [channel.membership, channel.info, channel.settings];
|
||||
for await (const relation of relations) {
|
||||
try {
|
||||
const model = await relation.fetch();
|
||||
if (model) {
|
||||
preparedModels.push(model.prepareDestroyPermanently());
|
||||
}
|
||||
} catch {
|
||||
// Record not found, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
const associatedChildren: Array<Query<any>> = [
|
||||
channel.members,
|
||||
channel.drafts,
|
||||
channel.groupsChannel,
|
||||
channel.postsInChannel,
|
||||
];
|
||||
for await (const children of associatedChildren) {
|
||||
const models = await children.fetch() as Model[];
|
||||
models.forEach((model) => preparedModels.push(model.prepareDestroyPermanently()));
|
||||
}
|
||||
|
||||
const posts = await channel.posts.fetch() as PostModel[];
|
||||
for await (const post of posts) {
|
||||
const preparedPost = await prepareDeletePost(post);
|
||||
preparedModels.push(...preparedPost);
|
||||
}
|
||||
|
||||
return preparedModels;
|
||||
};
|
||||
|
||||
export const queryAllChannelsForTeam = (database: Database, teamId: string) => {
|
||||
return database.get(CHANNEL).query(Q.where('team_id', teamId)).fetch() as Promise<ChannelModel[]>;
|
||||
};
|
||||
@@ -78,3 +116,12 @@ export const queryChannelByName = async (database: Database, channelName: string
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const queryChannelsById = async (database: Database, channelIds: string[]): Promise<ChannelModel[]|undefined> => {
|
||||
try {
|
||||
const channels = (await database.get(CHANNEL).query(Q.where('id', Q.oneOf(channelIds))).fetch()) as ChannelModel[];
|
||||
return channels;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
74
app/queries/servers/entry.ts
Normal file
74
app/queries/servers/entry.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import ServerDataOperator from '@database/operator/server_data_operator';
|
||||
|
||||
import {prepareDeleteChannel, prepareMyChannelsForTeam} from './channel';
|
||||
import {prepareMyPreferences} from './preference';
|
||||
import {prepareDeleteTeam, prepareMyTeams} from './team';
|
||||
import {prepareUsers} from './user';
|
||||
|
||||
import type {MyChannelsRequest} from '@actions/remote/channel';
|
||||
import type {MyPreferencesRequest} from '@actions/remote/preference';
|
||||
import type {MyTeamsRequest} from '@actions/remote/team';
|
||||
import type {MyUserRequest} from '@actions/remote/user';
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
|
||||
type PrepareModelsArgs = {
|
||||
operator: ServerDataOperator;
|
||||
initialTeamId?: string;
|
||||
removeTeams?: TeamModel[];
|
||||
removeChannels?: ChannelModel[];
|
||||
teamData?: MyTeamsRequest;
|
||||
chData?: MyChannelsRequest;
|
||||
prefData?: MyPreferencesRequest;
|
||||
meData?: MyUserRequest;
|
||||
}
|
||||
|
||||
export const prepareModels = async ({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData}: PrepareModelsArgs): Promise<Array<Promise<Model[]>>> => {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
|
||||
if (removeTeams?.length) {
|
||||
removeTeams.forEach((team) => {
|
||||
modelPromises.push(prepareDeleteTeam(team));
|
||||
});
|
||||
}
|
||||
|
||||
if (removeChannels?.length) {
|
||||
removeChannels.forEach((channel) => {
|
||||
modelPromises.push(prepareDeleteChannel(channel));
|
||||
});
|
||||
}
|
||||
|
||||
if (teamData?.teams?.length) {
|
||||
const teamModels = prepareMyTeams(operator, teamData.teams, teamData.memberships || [], teamData.unreads || []);
|
||||
if (teamModels) {
|
||||
modelPromises.push(...teamModels);
|
||||
}
|
||||
}
|
||||
|
||||
if (initialTeamId && chData?.channels?.length) {
|
||||
const channelModels = await prepareMyChannelsForTeam(operator, initialTeamId, chData.channels, chData.memberships || []);
|
||||
if (channelModels) {
|
||||
modelPromises.push(...channelModels);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefData?.preferences?.length) {
|
||||
const prefModel = prepareMyPreferences(operator, prefData.preferences);
|
||||
if (prefModel) {
|
||||
modelPromises.push(prefModel);
|
||||
}
|
||||
}
|
||||
|
||||
if (meData?.user) {
|
||||
const userModels = prepareUsers(operator, [meData.user]);
|
||||
if (userModels) {
|
||||
modelPromises.push(userModels);
|
||||
}
|
||||
}
|
||||
|
||||
return modelPromises;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
@@ -10,6 +10,33 @@ import type PostInChannelModel from '@typings/database/models/servers/posts_in_c
|
||||
|
||||
const {SERVER: {POST, POSTS_IN_CHANNEL}} = MM_TABLES;
|
||||
|
||||
export const prepareDeletePost = async (post: PostModel): Promise<Model[]> => {
|
||||
const preparedModels: Model[] = [post.prepareDestroyPermanently()];
|
||||
const relations: Array<Relation<Model> | Query<Model>> = [post.drafts, post.postsInThread];
|
||||
for await (const relation of relations) {
|
||||
try {
|
||||
const model = await relation.fetch();
|
||||
if (model) {
|
||||
if (Array.isArray(model)) {
|
||||
model.forEach((m) => preparedModels.push(m.prepareDestroyPermanently()));
|
||||
} else {
|
||||
preparedModels.push(model.prepareDestroyPermanently());
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Record not found, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
const associatedChildren: Array<Query<any>> = [post.files, post.reactions];
|
||||
for await (const children of associatedChildren) {
|
||||
const models = await children.fetch() as Model[];
|
||||
models.forEach((model) => preparedModels.push(model.prepareDestroyPermanently()));
|
||||
}
|
||||
|
||||
return preparedModels;
|
||||
};
|
||||
|
||||
export const queryPostById = async (database: Database, postId: string) => {
|
||||
try {
|
||||
const userRecord = (await database.collections.get(MM_TABLES.SERVER.POST).find(postId)) as PostModel;
|
||||
|
||||
@@ -81,6 +81,15 @@ export const queryCommonSystemValues = async (serverDatabase: Database) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const queryConfig = async (serverDatabase: Database) => {
|
||||
try {
|
||||
const config = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CONFIG) as SystemModel;
|
||||
return (config?.value || {}) as ClientConfig;
|
||||
} catch {
|
||||
return {} as ClientConfig;
|
||||
}
|
||||
};
|
||||
|
||||
export const queryExpandedLinks = async (serverDatabase: Database) => {
|
||||
try {
|
||||
const expandedLinks = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.EXPANDED_LINKS) as SystemModel;
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb';
|
||||
|
||||
import {Database as DatabaseConstants} from '@constants';
|
||||
import {Database as DatabaseConstants, Preferences} from '@constants';
|
||||
import {getPreferenceValue} from '@helpers/api/preference';
|
||||
import {selectDefaultTeam} from '@helpers/api/team';
|
||||
|
||||
import {prepareDeleteChannel} from './channel';
|
||||
import {queryPreferencesByCategoryAndName} from './preference';
|
||||
import {queryConfig} from './system';
|
||||
import {queryCurrentUser} from './user';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyTeamModel from '@typings/database/models/servers/my_team';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
import type TeamChannelHistoryModel from '@typings/database/models/servers/team_channel_history';
|
||||
@@ -58,6 +66,51 @@ export const prepareMyTeams = (operator: ServerDataOperator, teams: Team[], memb
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMyTeams = async (operator: ServerDataOperator, teams: TeamModel[]) => {
|
||||
const preparedModels: Model[] = [];
|
||||
for await (const team of teams) {
|
||||
const myTeam = await team.myTeam.fetch() as MyTeamModel;
|
||||
preparedModels.push(myTeam.prepareDestroyPermanently());
|
||||
}
|
||||
|
||||
await operator.batchRecords(preparedModels);
|
||||
};
|
||||
|
||||
export const prepareDeleteTeam = async (team: TeamModel): Promise<Model[]> => {
|
||||
const preparedModels: Model[] = [team.prepareDestroyPermanently()];
|
||||
|
||||
const relations: Array<Relation<Model>> = [team.myTeam, team.teamChannelHistory];
|
||||
for await (const relation of relations) {
|
||||
try {
|
||||
const model = await relation.fetch();
|
||||
if (model) {
|
||||
preparedModels.push(model.prepareDestroyPermanently());
|
||||
}
|
||||
} catch {
|
||||
// Record not found, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
const associatedChildren: Array<Query<any>> = [
|
||||
team.members,
|
||||
team.groupsTeam,
|
||||
team.slashCommands,
|
||||
team.teamSearchHistories,
|
||||
];
|
||||
for await (const children of associatedChildren) {
|
||||
const models = await children.fetch() as Model[];
|
||||
models.forEach((model) => preparedModels.push(model.prepareDestroyPermanently()));
|
||||
}
|
||||
|
||||
const channels = await team.channels.fetch() as ChannelModel[];
|
||||
for await (const channel of channels) {
|
||||
const preparedChannel = await prepareDeleteChannel(channel);
|
||||
preparedModels.push(...preparedChannel);
|
||||
}
|
||||
|
||||
return preparedModels;
|
||||
};
|
||||
|
||||
export const queryMyTeamById = async (database: Database, teamId: string): Promise<MyTeamModel|undefined> => {
|
||||
try {
|
||||
const myTeam = (await database.get(MY_TEAM).find(teamId)) as MyTeamModel;
|
||||
@@ -76,6 +129,15 @@ export const queryTeamById = async (database: Database, teamId: string): Promise
|
||||
}
|
||||
};
|
||||
|
||||
export const queryTeamsById = async (database: Database, teamIds: string[]): Promise<TeamModel[]|undefined> => {
|
||||
try {
|
||||
const teams = (await database.get(TEAM).query(Q.where('id', Q.oneOf(teamIds))).fetch()) as TeamModel[];
|
||||
return teams;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const queryTeamByName = async (database: Database, teamName: string): Promise<TeamModel|undefined> => {
|
||||
try {
|
||||
const team = (await database.get(TEAM).query(Q.where('name', teamName)).fetch()) as TeamModel[];
|
||||
@@ -88,3 +150,39 @@ export const queryTeamByName = async (database: Database, teamName: string): Pro
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const queryMyTeams = async (database: Database): Promise<MyTeamModel[]|undefined> => {
|
||||
try {
|
||||
const teams = (await database.get(MY_TEAM).query().fetch()) as MyTeamModel[];
|
||||
return teams;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const queryAvailableTeamIds = async (database: Database, excludeTeamId: string, teams?: Team[], preferences?: PreferenceType[], locale?: string): Promise<string[]> => {
|
||||
let availableTeamIds: string[] = [];
|
||||
|
||||
if (teams) {
|
||||
let teamOrderPreference;
|
||||
if (preferences) {
|
||||
teamOrderPreference = getPreferenceValue(preferences, Preferences.TEAMS_ORDER, '', '') as string;
|
||||
} else {
|
||||
const dbPreferences = await queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER, '');
|
||||
teamOrderPreference = dbPreferences[0].value;
|
||||
}
|
||||
|
||||
const userLocale = locale || (await queryCurrentUser(database))?.locale;
|
||||
const config = await queryConfig(database);
|
||||
const defaultTeam = selectDefaultTeam(teams, userLocale, teamOrderPreference, config.ExperimentalPrimaryTeam);
|
||||
|
||||
availableTeamIds = [defaultTeam!.id];
|
||||
} else {
|
||||
const dbTeams = await queryMyTeams(database);
|
||||
if (dbTeams) {
|
||||
availableTeamIds = dbTeams.map((team) => team.id);
|
||||
}
|
||||
}
|
||||
|
||||
return availableTeamIds.filter((id) => id !== excludeTeamId);
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ const ChannelListScreen = (props: ChannelProps) => {
|
||||
<View style={[styles.flex, {alignItems: 'center', justifyContent: 'center'}, tabletSidebarStyle]}>
|
||||
<Text
|
||||
onPress={() => goToScreen('Channel', '', undefined, {topBar: {visible: false}})}
|
||||
style={{fontSize: 20, color: '#fff'}}
|
||||
style={{fontSize: 20, color: theme.centerChannelColor}}
|
||||
>
|
||||
{'Channel List'}
|
||||
</Text>
|
||||
|
||||
@@ -27,7 +27,7 @@ import ErrorText from '@components/error_text';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {FORGOT_PASSWORD, MFA} from '@constants/screens';
|
||||
import {t} from '@i18n';
|
||||
import {goToScreen, resetToChannel} from '@screens/navigation';
|
||||
import {goToScreen, resetToHome} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
@@ -136,13 +136,13 @@ const Login: NavigationFunctionComponent = ({config, extra, launchError, launchT
|
||||
console.log('GO TO NO TEAMS');
|
||||
return;
|
||||
}
|
||||
await goToChannel(result.time || 0, result.error as never);
|
||||
goToHome(result.time || 0, result.error as never);
|
||||
}
|
||||
};
|
||||
|
||||
const goToChannel = async (time: number, loginError?: never) => {
|
||||
const goToHome = (time: number, loginError?: never) => {
|
||||
const hasError = launchError || Boolean(loginError);
|
||||
resetToChannel({extra, launchError: hasError, launchType, serverUrl, time});
|
||||
resetToHome({extra, launchError: hasError, launchType, serverUrl, time});
|
||||
};
|
||||
|
||||
const checkLoginResponse = (data: LoginActionResponse) => {
|
||||
@@ -172,7 +172,7 @@ const Login: NavigationFunctionComponent = ({config, extra, launchError, launchT
|
||||
const goToMfa = () => {
|
||||
const screen = MFA;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.mfa', defaultMessage: 'Multi-factor Authentication'});
|
||||
goToScreen(screen, title, {goToChannel, loginId, password, config, license, serverUrl, theme});
|
||||
goToScreen(screen, title, {goToHome, loginId, password, config, license, serverUrl, theme});
|
||||
};
|
||||
|
||||
const getLoginErrorMessage = (loginError: string | ClientErrorProps | Error) => {
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('Login', () => {
|
||||
'MFA',
|
||||
'Multi-factor Authentication',
|
||||
{
|
||||
goToChannel: expect.anything(),
|
||||
goToHome: expect.anything(),
|
||||
loginId,
|
||||
password,
|
||||
config: {EnableSignInWithEmail: 'true', EnableSignInWithUsername: 'true'},
|
||||
|
||||
@@ -27,7 +27,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
type MFAProps = {
|
||||
config: Partial<ClientConfig>;
|
||||
goToChannel: (time: number, error?: never) => void;
|
||||
goToHome: (time: number, error?: never) => void;
|
||||
license: Partial<ClientLicense>;
|
||||
loginId: string;
|
||||
password: string;
|
||||
@@ -35,7 +35,7 @@ type MFAProps = {
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const MFA = ({config, goToChannel, license, loginId, password, serverUrl, theme}: MFAProps) => {
|
||||
const MFA = ({config, goToHome, license, loginId, password, serverUrl, theme}: MFAProps) => {
|
||||
const intl = useIntl();
|
||||
const [token, setToken] = useState<string>('');
|
||||
const [error, setError] = useState<string>('');
|
||||
@@ -98,7 +98,7 @@ const MFA = ({config, goToChannel, license, loginId, password, serverUrl, theme}
|
||||
console.log('GO TO NO TEAMS');
|
||||
return;
|
||||
}
|
||||
goToChannel(result.time || 0, result.error as never);
|
||||
goToHome(result.time || 0, result.error as never);
|
||||
});
|
||||
|
||||
const getProceedView = () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ jest.mock('@actions/remote/session', () => {
|
||||
describe('*** MFA Screen ***', () => {
|
||||
const baseProps = {
|
||||
config: {},
|
||||
goToChannel: jest.fn(),
|
||||
goToHome: jest.fn(),
|
||||
loginId: 'loginId',
|
||||
password: 'passwd',
|
||||
license: {},
|
||||
@@ -34,10 +34,10 @@ describe('*** MFA Screen ***', () => {
|
||||
test('should call login method on submit', async () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
goToChannel: jest.fn(),
|
||||
goToHome: jest.fn(),
|
||||
};
|
||||
|
||||
const spyOnGoToChannel = jest.spyOn(props, 'goToChannel');
|
||||
const spyOnGoToHome = jest.spyOn(props, 'goToHome');
|
||||
const {getByTestId} = renderWithIntl(<Mfa {...props}/>);
|
||||
const submitBtn = getByTestId('login_mfa.submit');
|
||||
const inputText = getByTestId('login_mfa.input');
|
||||
@@ -47,6 +47,6 @@ describe('*** MFA Screen ***', () => {
|
||||
fireEvent.press(submitBtn);
|
||||
});
|
||||
|
||||
expect(spyOnGoToChannel).toHaveBeenCalled();
|
||||
expect(spyOnGoToHome).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ function getThemeFromState() {
|
||||
return Preferences.THEMES.denim;
|
||||
}
|
||||
|
||||
export function resetToChannel(passProps = {}) {
|
||||
export function resetToHome(passProps = {}) {
|
||||
const theme = getThemeFromState();
|
||||
|
||||
EphemeralStore.clearNavigationComponents();
|
||||
|
||||
@@ -7,7 +7,7 @@ import React, {useState} from 'react';
|
||||
import {ssoLogin} from '@actions/remote/session';
|
||||
import ClientError from '@client/rest/error';
|
||||
import {SSO as SSOEnum} from '@constants';
|
||||
import {resetToChannel} from '@screens/navigation';
|
||||
import {resetToHome} from '@screens/navigation';
|
||||
import {isMinimumServerVersion} from '@utils/helpers';
|
||||
|
||||
import SSOWithRedirectURL from './sso_with_redirect_url';
|
||||
@@ -83,12 +83,12 @@ const SSO = ({config, extra, launchError, launchType, serverUrl, ssoType, theme}
|
||||
console.log('GO TO NO TEAMS');
|
||||
return;
|
||||
}
|
||||
goToChannel(result.time || 0, result.error as never);
|
||||
goToHome(result.time || 0, result.error as never);
|
||||
};
|
||||
|
||||
const goToChannel = (time: number, error?: never) => {
|
||||
const goToHome = (time: number, error?: never) => {
|
||||
const hasError = launchError || Boolean(error);
|
||||
resetToChannel({extra, launchError: hasError, launchType, serverUrl, time});
|
||||
resetToHome({extra, launchError: hasError, launchType, serverUrl, time});
|
||||
};
|
||||
|
||||
const isSSOWithRedirectURLAvailable = isMinimumServerVersion(config.Version!, 5, 33, 0);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "cocoapods", "1.10.1"
|
||||
gem "cocoapods", "1.10.2"
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.3)
|
||||
activesupport (5.2.5)
|
||||
CFPropertyList (3.0.4)
|
||||
rexml
|
||||
activesupport (5.2.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.7.0)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
claide (1.0.3)
|
||||
cocoapods (1.10.1)
|
||||
cocoapods (1.10.2)
|
||||
addressable (~> 2.6)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.10.1)
|
||||
cocoapods-core (= 1.10.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
@@ -32,7 +33,7 @@ GEM
|
||||
nap (~> 1.0)
|
||||
ruby-macho (~> 1.4)
|
||||
xcodeproj (>= 1.19.0, < 2.0)
|
||||
cocoapods-core (1.10.1)
|
||||
cocoapods-core (1.10.2)
|
||||
activesupport (> 5.0, < 6)
|
||||
addressable (~> 2.6)
|
||||
algoliasearch (~> 1.0)
|
||||
@@ -42,21 +43,21 @@ GEM
|
||||
netrc (~> 0.11)
|
||||
public_suffix
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.4)
|
||||
cocoapods-downloader (1.4.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (1.5.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.0)
|
||||
cocoapods-trunk (1.5.0)
|
||||
cocoapods-search (1.0.1)
|
||||
cocoapods-trunk (1.6.0)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.1.8)
|
||||
concurrent-ruby (1.1.9)
|
||||
escape (0.0.4)
|
||||
ethon (0.13.0)
|
||||
ethon (0.14.0)
|
||||
ffi (>= 1.15.0)
|
||||
ffi (1.15.0)
|
||||
ffi (1.15.4)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
@@ -70,24 +71,26 @@ GEM
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
public_suffix (4.0.6)
|
||||
rexml (3.2.5)
|
||||
ruby-macho (1.4.0)
|
||||
thread_safe (0.3.6)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
xcodeproj (1.19.0)
|
||||
xcodeproj (1.21.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods (= 1.10.1)
|
||||
cocoapods (= 1.10.2)
|
||||
|
||||
BUNDLED WITH
|
||||
2.1.4
|
||||
|
||||
Reference in New Issue
Block a user