* Add linter rules for import order and type member delimiters * Remove unneeded group * Group all app/* imports before the internal imports * Move app/ imports before parent imports * Separate @node_modules imports into a different group * Substitute app paths by aliases * Fix @node_modules import order and add test related modules * Add aliases for types and test, and group import types
952 lines
30 KiB
TypeScript
952 lines
30 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
import {Client4} from '@client/rest';
|
|
import {analytics} from '@init/analytics';
|
|
import {UserTypes, TeamTypes} from '@mm-redux/action_types';
|
|
import {getConfig, getServerVersion} from '@mm-redux/selectors/entities/general';
|
|
import {getCurrentUserId, getUsers} from '@mm-redux/selectors/entities/users';
|
|
import {Action, ActionFunc, ActionResult, batchActions, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
|
|
import {TeamMembership} from '@mm-redux/types/teams';
|
|
import {UserProfile, UserStatus} from '@mm-redux/types/users';
|
|
import {Dictionary} from '@mm-redux/types/utilities';
|
|
import {getUserIdFromChannelName, isDirectChannel, isDirectChannelVisible, isGroupChannel, isGroupChannelVisible} from '@mm-redux/utils/channel_utils';
|
|
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
|
import {removeUserFromList} from '@mm-redux/utils/user_utils';
|
|
|
|
import {General} from '../constants';
|
|
|
|
import {getAllCustomEmojis} from './emojis';
|
|
import {logError} from './errors';
|
|
import {getClientConfig, setServerVersion} from './general';
|
|
import {bindClientFunc, forceLogoutIfNecessary, debounce} from './helpers';
|
|
import {getMyPreferences, makeDirectChannelVisibleIfNecessary, makeGroupMessageVisibleIfNecessary} from './preferences';
|
|
import {loadRolesIfNeeded} from './roles';
|
|
import {getMyTeams} from './teams';
|
|
|
|
export function checkMfa(loginId: string): ActionFunc {
|
|
return async (dispatch: DispatchFunc) => {
|
|
dispatch({type: UserTypes.CHECK_MFA_REQUEST, data: null});
|
|
try {
|
|
const data = await Client4.checkUserMfa(loginId);
|
|
dispatch({type: UserTypes.CHECK_MFA_SUCCESS, data: null});
|
|
return {data: data.mfa_required};
|
|
} catch (error) {
|
|
dispatch(batchActions([
|
|
{type: UserTypes.CHECK_MFA_FAILURE, error},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
};
|
|
}
|
|
|
|
export function createUser(user: UserProfile, token: string, inviteId: string): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
let created;
|
|
|
|
try {
|
|
created = await Client4.createUser(user, token, inviteId);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
const profiles: {
|
|
[userId: string]: UserProfile;
|
|
} = {
|
|
[created.id]: created,
|
|
};
|
|
dispatch({type: UserTypes.RECEIVED_PROFILES, data: profiles});
|
|
|
|
return {data: created};
|
|
};
|
|
}
|
|
|
|
export function login(loginId: string, password: string, mfaToken = '', ldapOnly = false): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
dispatch({type: UserTypes.LOGIN_REQUEST, data: null});
|
|
|
|
const deviceId = getState().entities.general.deviceToken;
|
|
let data;
|
|
|
|
try {
|
|
data = await Client4.login(loginId, password, mfaToken, deviceId, ldapOnly);
|
|
} catch (error) {
|
|
dispatch(batchActions([
|
|
{
|
|
type: UserTypes.LOGIN_FAILURE,
|
|
error,
|
|
},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
|
|
return dispatch(completeLogin(data));
|
|
};
|
|
}
|
|
|
|
export function loginById(id: string, password: string, mfaToken = ''): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
dispatch({type: UserTypes.LOGIN_REQUEST, data: null});
|
|
|
|
const deviceId = getState().entities.general.deviceToken;
|
|
let data;
|
|
|
|
try {
|
|
data = await Client4.loginById(id, password, mfaToken, deviceId);
|
|
} catch (error) {
|
|
dispatch(batchActions([
|
|
{
|
|
type: UserTypes.LOGIN_FAILURE,
|
|
error,
|
|
},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
|
|
return dispatch(completeLogin(data));
|
|
};
|
|
}
|
|
|
|
function completeLogin(data: UserProfile): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
dispatch({
|
|
type: UserTypes.RECEIVED_ME,
|
|
data,
|
|
});
|
|
|
|
analytics.setUserId(data.id);
|
|
analytics.setUserRoles(data.roles);
|
|
let teamMembers;
|
|
|
|
try {
|
|
const membersRequest: Promise<Array<TeamMembership>> = Client4.getMyTeamMembers();
|
|
const unreadsRequest = Client4.getMyTeamUnreads();
|
|
|
|
teamMembers = await membersRequest;
|
|
const teamUnreads = await unreadsRequest;
|
|
|
|
if (teamUnreads) {
|
|
for (const u of teamUnreads) {
|
|
const index = teamMembers.findIndex((m) => m.team_id === u.team_id);
|
|
const member = teamMembers[index];
|
|
member.mention_count = u.mention_count;
|
|
member.msg_count = u.msg_count;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
dispatch(batchActions([
|
|
{type: UserTypes.LOGIN_FAILURE, error},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
|
|
const promises = [
|
|
dispatch(getMyPreferences()),
|
|
dispatch(getMyTeams()),
|
|
dispatch(getClientConfig()),
|
|
];
|
|
|
|
const serverVersion = Client4.getServerVersion();
|
|
dispatch(setServerVersion(serverVersion));
|
|
if (!isMinimumServerVersion(serverVersion, 4, 7) && getConfig(getState()).EnableCustomEmoji === 'true') {
|
|
dispatch(getAllCustomEmojis());
|
|
}
|
|
|
|
try {
|
|
await Promise.all(promises);
|
|
} catch (error) {
|
|
dispatch(batchActions([
|
|
{type: UserTypes.LOGIN_FAILURE, error},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
|
|
dispatch(batchActions([
|
|
{
|
|
type: TeamTypes.RECEIVED_MY_TEAM_MEMBERS,
|
|
data: teamMembers,
|
|
},
|
|
{
|
|
type: UserTypes.LOGIN_SUCCESS,
|
|
},
|
|
]));
|
|
const roles = new Set<string>();
|
|
for (const role of data.roles.split(' ')) {
|
|
roles.add(role);
|
|
}
|
|
for (const teamMember of teamMembers) {
|
|
for (const role of teamMember.roles.split(' ')) {
|
|
roles.add(role);
|
|
}
|
|
}
|
|
if (roles.size > 0) {
|
|
dispatch(loadRolesIfNeeded(roles));
|
|
}
|
|
|
|
return {data: true};
|
|
};
|
|
}
|
|
|
|
export function getProfiles(page = 0, perPage: number = General.PROFILE_CHUNK_SIZE, options: any = {}): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let profiles: UserProfile[];
|
|
|
|
try {
|
|
profiles = await Client4.getProfiles(page, perPage, options);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
});
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getMissingProfilesByIds(userIds: Array<string>): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {profiles} = getState().entities.users;
|
|
const missingIds: string[] = [];
|
|
userIds.forEach((id) => {
|
|
if (!profiles[id]) {
|
|
missingIds.push(id);
|
|
}
|
|
});
|
|
|
|
if (missingIds.length > 0) {
|
|
dispatch(getStatusesByIds(missingIds));
|
|
return dispatch(getProfilesByIds(missingIds));
|
|
}
|
|
|
|
return {data: []};
|
|
};
|
|
}
|
|
|
|
export function getMissingProfilesByUsernames(usernames: Array<string>): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {profiles} = getState().entities.users;
|
|
|
|
const usernameProfiles = Object.values(profiles).reduce((acc, profile: any) => {
|
|
acc[profile.username] = profile;
|
|
return acc;
|
|
}, {} as Dictionary<UserProfile>);
|
|
const missingUsernames: string[] = [];
|
|
usernames.forEach((username) => {
|
|
if (!usernameProfiles[username]) {
|
|
missingUsernames.push(username);
|
|
}
|
|
});
|
|
|
|
if (missingUsernames.length > 0) {
|
|
return dispatch(getProfilesByUsernames(missingUsernames));
|
|
}
|
|
|
|
return {data: []};
|
|
};
|
|
}
|
|
|
|
export function getProfilesByIds(userIds: Array<string>, options?: any): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let profiles: UserProfile[];
|
|
|
|
try {
|
|
profiles = await Client4.getProfilesByIds(userIds, options);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
});
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesByUsernames(usernames: Array<string>): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let profiles;
|
|
|
|
try {
|
|
profiles = await Client4.getProfilesByUsernames(usernames);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
});
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesInTeam(teamId: string, page: number, perPage: number = General.PROFILE_CHUNK_SIZE, sort = '', options: any = {}): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let profiles;
|
|
|
|
try {
|
|
profiles = await Client4.getProfilesInTeam(teamId, page, perPage, sort, options);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch(batchActions([
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_TEAM,
|
|
data: profiles,
|
|
id: teamId,
|
|
},
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
},
|
|
]));
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesNotInTeam(teamId: string, groupConstrained: boolean, page: number, perPage: number = General.PROFILE_CHUNK_SIZE): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
let profiles;
|
|
try {
|
|
profiles = await Client4.getProfilesNotInTeam(teamId, groupConstrained, page, perPage);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
const receivedProfilesListActionType = groupConstrained ?
|
|
UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_TEAM_AND_REPLACE :
|
|
UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_TEAM;
|
|
|
|
dispatch(batchActions([
|
|
{
|
|
type: receivedProfilesListActionType,
|
|
data: profiles,
|
|
id: teamId,
|
|
},
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: profiles,
|
|
},
|
|
]));
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesWithoutTeam(page: number, perPage: number = General.PROFILE_CHUNK_SIZE, options: any = {}): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
let profiles = null;
|
|
try {
|
|
profiles = await Client4.getProfilesWithoutTeam(page, perPage, options);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch(batchActions([
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_WITHOUT_TEAM,
|
|
data: profiles,
|
|
},
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: profiles,
|
|
},
|
|
]));
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesInChannel(channelId: string, page: number, perPage: number = General.PROFILE_CHUNK_SIZE, sort = ''): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let profiles;
|
|
|
|
try {
|
|
profiles = await Client4.getProfilesInChannel(channelId, page, perPage, sort);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch(batchActions([
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_CHANNEL,
|
|
data: profiles,
|
|
id: channelId,
|
|
},
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
},
|
|
]));
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesInGroupChannels(channelsIds: Array<string>): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let channelProfiles;
|
|
|
|
try {
|
|
channelProfiles = await Client4.getProfilesInGroupChannels(channelsIds.slice(0, General.MAX_GROUP_CHANNELS_FOR_PROFILES));
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
const actions: Action[] = [];
|
|
for (const channelId in channelProfiles) {
|
|
if (channelProfiles.hasOwnProperty(channelId)) {
|
|
const profiles = channelProfiles[channelId];
|
|
|
|
actions.push(
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_CHANNEL,
|
|
data: profiles,
|
|
id: channelId,
|
|
},
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
dispatch(batchActions(actions));
|
|
|
|
return {data: channelProfiles};
|
|
};
|
|
}
|
|
|
|
export function getProfilesNotInChannel(teamId: string, channelId: string, groupConstrained: boolean, page: number, perPage: number = General.PROFILE_CHUNK_SIZE): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
let profiles;
|
|
|
|
try {
|
|
profiles = await Client4.getProfilesNotInChannel(teamId, channelId, groupConstrained, page, perPage);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
const receivedProfilesListActionType = groupConstrained ?
|
|
UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_CHANNEL_AND_REPLACE :
|
|
UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_CHANNEL;
|
|
|
|
dispatch(batchActions([
|
|
{
|
|
type: receivedProfilesListActionType,
|
|
data: profiles,
|
|
id: channelId,
|
|
},
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: removeUserFromList(currentUserId, [...profiles]),
|
|
},
|
|
]));
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
export function getMe(): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const getMeFunc = bindClientFunc({
|
|
clientFunc: Client4.getMe,
|
|
onSuccess: UserTypes.RECEIVED_ME,
|
|
});
|
|
const me = await getMeFunc(dispatch, getState);
|
|
|
|
if ('error' in me) {
|
|
return me;
|
|
}
|
|
if ('data' in me) {
|
|
dispatch(loadRolesIfNeeded(me.data.roles.split(' ')));
|
|
}
|
|
return me;
|
|
};
|
|
}
|
|
|
|
export function updateMyTermsOfServiceStatus(termsOfServiceId: string, accepted: boolean): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const response: ActionResult = await dispatch(bindClientFunc({
|
|
clientFunc: Client4.updateMyTermsOfServiceStatus,
|
|
params: [
|
|
termsOfServiceId,
|
|
accepted,
|
|
],
|
|
}));
|
|
|
|
if ('data' in response) {
|
|
if (accepted) {
|
|
dispatch({
|
|
type: UserTypes.RECEIVED_TERMS_OF_SERVICE_STATUS,
|
|
data: {
|
|
terms_of_service_create_at: new Date().getTime(),
|
|
terms_of_service_id: accepted ? termsOfServiceId : null,
|
|
user_id: getCurrentUserId(getState()),
|
|
},
|
|
});
|
|
}
|
|
|
|
return {
|
|
data: response.data,
|
|
};
|
|
}
|
|
|
|
return {
|
|
error: response.error,
|
|
};
|
|
};
|
|
}
|
|
|
|
export function getTermsOfService(): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getTermsOfService,
|
|
});
|
|
}
|
|
|
|
export function demoteUserToGuest(userId: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.demoteUserToGuest,
|
|
params: [userId],
|
|
});
|
|
}
|
|
|
|
export function getUser(id: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getUser,
|
|
onSuccess: UserTypes.RECEIVED_PROFILE,
|
|
params: [
|
|
id,
|
|
],
|
|
});
|
|
}
|
|
|
|
export function getUserByUsername(username: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getUserByUsername,
|
|
onSuccess: UserTypes.RECEIVED_PROFILE,
|
|
params: [
|
|
username,
|
|
],
|
|
});
|
|
}
|
|
|
|
export function getUserByEmail(email: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getUserByEmail,
|
|
onSuccess: UserTypes.RECEIVED_PROFILE,
|
|
params: [
|
|
email,
|
|
],
|
|
});
|
|
}
|
|
|
|
// We create an array to hold the id's that we want to get a status for. We build our
|
|
// debounced function that will get called after a set period of idle time in which
|
|
// the array of id's will be passed to the getStatusesByIds with a cb that clears out
|
|
// the array. Helps with performance because instead of making 75 different calls for
|
|
// statuses, we are only making one call for 75 ids.
|
|
// We could maybe clean it up somewhat by storing the array of ids in redux state possbily?
|
|
let ids: Array<string> = [];
|
|
const debouncedGetStatusesByIds = debounce(async (dispatch: DispatchFunc) => {
|
|
dispatch(getStatusesByIds([...new Set(ids)]));
|
|
}, 20, false, () => {
|
|
ids = [];
|
|
});
|
|
export function getStatusesByIdsBatchedDebounced(id: string) {
|
|
ids = [...ids, id];
|
|
return debouncedGetStatusesByIds;
|
|
}
|
|
|
|
export function getStatusesByIds(userIds: Array<string>): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getStatusesByIds,
|
|
onSuccess: UserTypes.RECEIVED_STATUSES,
|
|
params: [
|
|
userIds,
|
|
],
|
|
});
|
|
}
|
|
|
|
export function getStatus(userId: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getStatus,
|
|
onSuccess: UserTypes.RECEIVED_STATUS,
|
|
params: [
|
|
userId,
|
|
],
|
|
});
|
|
}
|
|
|
|
export function setStatus(status: UserStatus): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
try {
|
|
await Client4.updateStatus(status);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch({
|
|
type: UserTypes.RECEIVED_STATUS,
|
|
data: status,
|
|
});
|
|
|
|
return {data: status};
|
|
};
|
|
}
|
|
|
|
export function getSessions(userId: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.getSessions,
|
|
onSuccess: UserTypes.RECEIVED_SESSIONS,
|
|
params: [
|
|
userId,
|
|
],
|
|
});
|
|
}
|
|
|
|
export function loadProfilesForDirect(): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const state = getState();
|
|
const config = state.entities.general.config;
|
|
const {channels, myMembers} = state.entities.channels;
|
|
const {myPreferences} = state.entities.preferences;
|
|
const {currentUserId, profiles} = state.entities.users;
|
|
|
|
const values = Object.values(channels);
|
|
for (let i = 0; i < values.length; i++) {
|
|
const channel: any = values[i];
|
|
const member = myMembers[channel.id];
|
|
if (!isDirectChannel(channel) && !isGroupChannel(channel)) {
|
|
continue;
|
|
}
|
|
|
|
if (member) {
|
|
if (member.mention_count > 0 && isDirectChannel(channel)) {
|
|
const otherUserId = getUserIdFromChannelName(currentUserId, channel.name);
|
|
if (!isDirectChannelVisible(profiles[otherUserId] || otherUserId, config, myPreferences, channel)) {
|
|
dispatch(makeDirectChannelVisibleIfNecessary(otherUserId));
|
|
}
|
|
} else if ((member.mention_count > 0 || member.msg_count < channel.total_msg_count) &&
|
|
isGroupChannel(channel) && !isGroupChannelVisible(config, myPreferences, channel)) {
|
|
dispatch(makeGroupMessageVisibleIfNecessary(channel.id));
|
|
}
|
|
}
|
|
}
|
|
|
|
return {data: true};
|
|
};
|
|
}
|
|
|
|
export function autocompleteUsers(term: string, teamId = '', channelId = '', options: {
|
|
limit: number;
|
|
} = {
|
|
limit: General.AUTOCOMPLETE_LIMIT_DEFAULT,
|
|
}): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
dispatch({type: UserTypes.AUTOCOMPLETE_USERS_REQUEST, data: null});
|
|
|
|
const {currentUserId} = getState().entities.users;
|
|
|
|
let data;
|
|
try {
|
|
data = await Client4.autocompleteUsers(term, teamId, channelId, options);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(batchActions([
|
|
{type: UserTypes.AUTOCOMPLETE_USERS_FAILURE, error},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
|
|
let users = [...data.users];
|
|
if (data.out_of_channel) {
|
|
users = [...users, ...data.out_of_channel];
|
|
}
|
|
removeUserFromList(currentUserId, users);
|
|
const actions: Action[] = [{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data: users,
|
|
}, {
|
|
type: UserTypes.AUTOCOMPLETE_USERS_SUCCESS,
|
|
}];
|
|
|
|
if (channelId) {
|
|
actions.push(
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_CHANNEL,
|
|
data: data.users,
|
|
id: channelId,
|
|
},
|
|
);
|
|
actions.push(
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_CHANNEL,
|
|
data: data.out_of_channel,
|
|
id: channelId,
|
|
},
|
|
);
|
|
}
|
|
|
|
if (teamId) {
|
|
actions.push(
|
|
{
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_TEAM,
|
|
data: users,
|
|
id: teamId,
|
|
},
|
|
);
|
|
}
|
|
|
|
dispatch(batchActions(actions));
|
|
|
|
return {data};
|
|
};
|
|
}
|
|
|
|
export function searchProfiles(term: string, options: any = {}): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const {currentUserId} = getState().entities.users;
|
|
|
|
let profiles;
|
|
try {
|
|
profiles = await Client4.searchUsers(term, options);
|
|
} catch (error) {
|
|
forceLogoutIfNecessary(error, dispatch, getState);
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
const actions: Action[] = [{type: UserTypes.RECEIVED_PROFILES_LIST, data: removeUserFromList(currentUserId, [...profiles])}];
|
|
|
|
if (options.in_channel_id) {
|
|
actions.push({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_CHANNEL,
|
|
data: profiles,
|
|
id: options.in_channel_id,
|
|
});
|
|
}
|
|
|
|
if (options.not_in_channel_id) {
|
|
actions.push({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_CHANNEL,
|
|
data: profiles,
|
|
id: options.not_in_channel_id,
|
|
});
|
|
}
|
|
|
|
if (options.team_id) {
|
|
actions.push({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_IN_TEAM,
|
|
data: profiles,
|
|
id: options.team_id,
|
|
});
|
|
}
|
|
|
|
if (options.not_in_team_id) {
|
|
actions.push({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_TEAM,
|
|
data: profiles,
|
|
id: options.not_in_team_id,
|
|
});
|
|
}
|
|
|
|
dispatch(batchActions(actions));
|
|
|
|
return {data: profiles};
|
|
};
|
|
}
|
|
|
|
let statusIntervalId: NodeJS.Timeout|null;
|
|
export function startPeriodicStatusUpdates(forceStatusUpdate = false): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
if (statusIntervalId) {
|
|
clearInterval(statusIntervalId);
|
|
}
|
|
|
|
const getStatusForUsers = () => {
|
|
const {statuses} = getState().entities.users;
|
|
|
|
if (!statuses) {
|
|
return;
|
|
}
|
|
|
|
const userIds = Object.keys(statuses).filter((u) => u);
|
|
if (!userIds.length) {
|
|
return;
|
|
}
|
|
|
|
dispatch(getStatusesByIds(userIds));
|
|
};
|
|
|
|
statusIntervalId = setInterval(getStatusForUsers, General.STATUS_INTERVAL);
|
|
|
|
if (forceStatusUpdate) {
|
|
getStatusForUsers();
|
|
}
|
|
|
|
return {data: true};
|
|
};
|
|
}
|
|
|
|
export function stopPeriodicStatusUpdates(): ActionFunc {
|
|
return async () => {
|
|
if (statusIntervalId) {
|
|
clearInterval(statusIntervalId);
|
|
}
|
|
|
|
return {data: true};
|
|
};
|
|
}
|
|
|
|
export function updateMe(user: UserProfile): ActionFunc {
|
|
return async (dispatch: DispatchFunc) => {
|
|
dispatch({type: UserTypes.UPDATE_ME_REQUEST, data: null});
|
|
|
|
let data;
|
|
try {
|
|
data = await Client4.patchMe(user);
|
|
} catch (error) {
|
|
dispatch(batchActions([
|
|
{type: UserTypes.UPDATE_ME_FAILURE, error},
|
|
logError(error),
|
|
]));
|
|
return {error};
|
|
}
|
|
|
|
dispatch(batchActions([
|
|
{type: UserTypes.RECEIVED_ME, data},
|
|
{type: UserTypes.UPDATE_ME_SUCCESS},
|
|
]));
|
|
dispatch(loadRolesIfNeeded(data.roles.split(' ')));
|
|
|
|
return {data};
|
|
};
|
|
}
|
|
|
|
export function patchUser(user: UserProfile): ActionFunc {
|
|
return async (dispatch: DispatchFunc) => {
|
|
let data: UserProfile;
|
|
try {
|
|
data = await Client4.patchUser(user);
|
|
} catch (error) {
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
dispatch({type: UserTypes.RECEIVED_PROFILE, data});
|
|
|
|
return {data};
|
|
};
|
|
}
|
|
|
|
export function sendPasswordResetEmail(email: string): ActionFunc {
|
|
return bindClientFunc({
|
|
clientFunc: Client4.sendPasswordResetEmail,
|
|
params: [
|
|
email,
|
|
],
|
|
});
|
|
}
|
|
|
|
export function setDefaultProfileImage(userId: string): ActionFunc {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
try {
|
|
await Client4.setDefaultProfileImage(userId);
|
|
} catch (error) {
|
|
dispatch(logError(error));
|
|
return {error};
|
|
}
|
|
|
|
const profile = getState().entities.users.profiles[userId];
|
|
if (profile) {
|
|
dispatch({type: UserTypes.RECEIVED_PROFILE, data: {...profile, last_picture_update: 0}});
|
|
}
|
|
|
|
return {data: true};
|
|
};
|
|
}
|
|
|
|
export function checkForModifiedUsers() {
|
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
const state = getState();
|
|
const users = getUsers(state);
|
|
const lastDisconnectAt = state.websocket.lastDisconnectAt;
|
|
const serverVersion = getServerVersion(state);
|
|
|
|
if (!isMinimumServerVersion(serverVersion, 5, 14)) {
|
|
return {data: true};
|
|
}
|
|
|
|
await dispatch(getProfilesByIds(Object.keys(users), {since: lastDisconnectAt}));
|
|
return {data: true};
|
|
};
|
|
}
|
|
|
|
export default {
|
|
checkMfa,
|
|
login,
|
|
getProfiles,
|
|
getProfilesByIds,
|
|
getProfilesInTeam,
|
|
getProfilesInChannel,
|
|
getProfilesNotInChannel,
|
|
getUser,
|
|
getMe,
|
|
getUserByUsername,
|
|
getStatus,
|
|
getStatusesByIds,
|
|
getSessions,
|
|
loadProfilesForDirect,
|
|
searchProfiles,
|
|
startPeriodicStatusUpdates,
|
|
stopPeriodicStatusUpdates,
|
|
updateMe,
|
|
sendPasswordResetEmail,
|
|
getTermsOfService,
|
|
updateMyTermsOfServiceStatus,
|
|
checkForModifiedUsers,
|
|
};
|