forked from Ivasoft/mattermost-mobile
422 lines
15 KiB
TypeScript
422 lines
15 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {ChannelTypes, PreferenceTypes, RoleTypes, UserTypes} from '@mm-redux/action_types';
|
|
import {Client4} from '@mm-redux/client';
|
|
import {General, Preferences} from '@mm-redux/constants';
|
|
import {getCurrentChannelId, getRedirectChannelNameForTeam, getChannelsNameMapInTeam} from '@mm-redux/selectors/entities/channels';
|
|
import {getConfig} from '@mm-redux/selectors/entities/general';
|
|
import {getMyPreferences} from '@mm-redux/selectors/entities/preferences';
|
|
import {getCurrentUserId, getUsers, getUserIdsInChannels} from '@mm-redux/selectors/entities/users';
|
|
import {getChannelByName as selectChannelByName, getUserIdFromChannelName, isAutoClosed} from '@mm-redux/utils/channel_utils';
|
|
import {getPreferenceKey} from '@mm-redux/utils/preference_utils';
|
|
|
|
import {ActionResult, GenericAction} from '@mm-redux/types/actions';
|
|
import {Channel, ChannelMembership} from '@mm-redux/types/channels';
|
|
import {PreferenceType} from '@mm-redux/types/preferences';
|
|
import {GlobalState} from '@mm-redux/types/store';
|
|
import {UserProfile} from '@mm-redux/types/users';
|
|
import {RelationOneToMany} from '@mm-redux/types/utilities';
|
|
|
|
import {isDirectChannelVisible, isGroupChannelVisible} from '@utils/channels';
|
|
import {buildPreference} from '@utils/preferences';
|
|
|
|
export async function loadSidebarDirectMessagesProfiles(state: GlobalState, channels: Array<Channel>, channelMembers: Array<ChannelMembership>) {
|
|
const currentUserId = getCurrentUserId(state);
|
|
const usersInChannel: RelationOneToMany<Channel, UserProfile> = getUserIdsInChannels(state);
|
|
const directChannels = Object.values(channels).filter((c) => c.type === General.DM_CHANNEL || c.type === General.GM_CHANNEL);
|
|
const prefs: Array<PreferenceType> = [];
|
|
const promises: Array<Promise<ActionResult>> = []; //only fetch profiles that we don't have and the Direct channel should be visible
|
|
const actions = [];
|
|
const userIds: Array<string> = [];
|
|
|
|
// Prepare preferences and start fetching profiles to batch them
|
|
directChannels.forEach((c) => {
|
|
const profileIds = Array.from(usersInChannel[c.id] || []);
|
|
const profilesInChannel: Array<string> = profileIds.filter((u: string) => u !== currentUserId);
|
|
userIds.push(...profilesInChannel);
|
|
|
|
switch (c.type) {
|
|
case General.DM_CHANNEL: {
|
|
const dm = fetchDirectMessageProfileIfNeeded(state, c, channelMembers, profilesInChannel);
|
|
if (dm.preferences.length) {
|
|
prefs.push(...dm.preferences);
|
|
}
|
|
|
|
if (dm.promise) {
|
|
promises.push(dm.promise);
|
|
}
|
|
break;
|
|
}
|
|
case General.GM_CHANNEL: {
|
|
const gm = fetchGroupMessageProfilesIfNeeded(state, c, channelMembers, profilesInChannel);
|
|
|
|
if (gm.preferences.length) {
|
|
prefs.push(...gm.preferences);
|
|
}
|
|
|
|
if (gm.promise) {
|
|
promises.push(gm.promise);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Save preferences if there are any changes
|
|
if (prefs.length) {
|
|
Client4.savePreferences(currentUserId, prefs);
|
|
actions.push({
|
|
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
|
data: prefs,
|
|
});
|
|
}
|
|
|
|
const profilesAction = await getProfilesFromPromises(promises);
|
|
const userIdsSet: Set<string> = new Set(userIds);
|
|
|
|
if (profilesAction) {
|
|
actions.push(profilesAction);
|
|
profilesAction.data.forEach((d: any) => {
|
|
const {users} = d.data;
|
|
users.forEach((u: UserProfile) => userIdsSet.add(u.id));
|
|
});
|
|
}
|
|
|
|
if (userIdsSet.size > 0) {
|
|
try {
|
|
const statuses = await Client4.getStatusesByIds(Array.from(userIdsSet));
|
|
if (statuses.length) {
|
|
actions.push({
|
|
type: UserTypes.RECEIVED_STATUSES,
|
|
data: statuses,
|
|
});
|
|
}
|
|
} catch {
|
|
// do nothing (status will get fetched later on regardless)
|
|
}
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
export async function fetchMyChannel(channelId: string) {
|
|
try {
|
|
const data = await Client4.getChannel(channelId);
|
|
|
|
return {data};
|
|
} catch (error) {
|
|
return {error};
|
|
}
|
|
}
|
|
|
|
export async function fetchMyChannelMember(channelId: string) {
|
|
try {
|
|
const data = await Client4.getMyChannelMember(channelId);
|
|
|
|
return {data};
|
|
} catch (error) {
|
|
return {error};
|
|
}
|
|
}
|
|
|
|
export function markChannelAsUnread(state: GlobalState, teamId: string, channelId: string, mentions: Array<string>): Array<GenericAction> {
|
|
const {myMembers} = state.entities.channels;
|
|
const {currentUserId} = state.entities.users;
|
|
|
|
const actions: GenericAction[] = [{
|
|
type: ChannelTypes.INCREMENT_TOTAL_MSG_COUNT,
|
|
data: {
|
|
channelId,
|
|
amount: 1,
|
|
},
|
|
}, {
|
|
type: ChannelTypes.INCREMENT_UNREAD_MSG_COUNT,
|
|
data: {
|
|
teamId,
|
|
channelId,
|
|
amount: 1,
|
|
onlyMentions: myMembers[channelId] && myMembers[channelId].notify_props &&
|
|
myMembers[channelId].notify_props.mark_unread === General.MENTION,
|
|
},
|
|
}];
|
|
|
|
if (mentions && mentions.indexOf(currentUserId) !== -1) {
|
|
actions.push({
|
|
type: ChannelTypes.INCREMENT_UNREAD_MENTION_COUNT,
|
|
data: {
|
|
teamId,
|
|
channelId,
|
|
amount: 1,
|
|
},
|
|
});
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
export function makeDirectChannelVisibleIfNecessary(state: GlobalState, otherUserId: string): GenericAction|null {
|
|
const myPreferences = getMyPreferences(state);
|
|
const currentUserId = getCurrentUserId(state);
|
|
|
|
let preference = myPreferences[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUserId)];
|
|
|
|
if (!preference || preference.value === 'false') {
|
|
preference = {
|
|
user_id: currentUserId,
|
|
category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
|
name: otherUserId,
|
|
value: 'true',
|
|
};
|
|
|
|
Client4.savePreferences(currentUserId, [preference]);
|
|
return {
|
|
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
|
data: [preference],
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export async function makeGroupMessageVisibleIfNecessary(state: GlobalState, channelId: string) {
|
|
try {
|
|
const myPreferences = getMyPreferences(state);
|
|
const currentUserId = getCurrentUserId(state);
|
|
|
|
let preference = myPreferences[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, channelId)];
|
|
|
|
if (!preference || preference.value === 'false') {
|
|
preference = {
|
|
user_id: currentUserId,
|
|
category: Preferences.CATEGORY_GROUP_CHANNEL_SHOW,
|
|
name: channelId,
|
|
value: 'true',
|
|
};
|
|
|
|
Client4.savePreferences(currentUserId, [preference]);
|
|
|
|
const profilesInChannel = await fetchUsersInChannel(state, channelId);
|
|
|
|
return [{
|
|
type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL,
|
|
data: [profilesInChannel],
|
|
}, {
|
|
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
|
data: [preference],
|
|
}];
|
|
}
|
|
|
|
return null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function fetchChannelAndMyMember(channelId: string): Promise<Array<GenericAction>> {
|
|
const actions: Array<GenericAction> = [];
|
|
|
|
try {
|
|
const [channel, member] = await Promise.all([
|
|
Client4.getChannel(channelId),
|
|
Client4.getMyChannelMember(channelId),
|
|
]);
|
|
|
|
actions.push({
|
|
type: ChannelTypes.RECEIVED_CHANNEL,
|
|
data: channel,
|
|
},
|
|
{
|
|
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
|
|
data: member,
|
|
});
|
|
|
|
const roles = await Client4.getRolesByNames(member.roles.split(' '));
|
|
if (roles.length) {
|
|
actions.push({
|
|
type: RoleTypes.RECEIVED_ROLES,
|
|
data: roles,
|
|
});
|
|
}
|
|
} catch {
|
|
// do nothing
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
export async function getAddedDmUsersIfNecessary(state: GlobalState, preferences: PreferenceType[]): Promise<Array<GenericAction>> {
|
|
const userIds: string[] = [];
|
|
const actions: Array<GenericAction> = [];
|
|
|
|
for (const preference of preferences) {
|
|
if (preference.category === Preferences.CATEGORY_DIRECT_CHANNEL_SHOW && preference.value === 'true') {
|
|
userIds.push(preference.name);
|
|
}
|
|
}
|
|
|
|
if (userIds.length !== 0) {
|
|
const profiles = getUsers(state);
|
|
const currentUserId = getCurrentUserId(state);
|
|
|
|
const needProfiles: string[] = [];
|
|
|
|
for (const userId of userIds) {
|
|
if (!profiles[userId] && userId !== currentUserId) {
|
|
needProfiles.push(userId);
|
|
}
|
|
}
|
|
|
|
if (needProfiles.length > 0) {
|
|
const data = await Client4.getProfilesByIds(userIds);
|
|
if (profiles.lenght) {
|
|
actions.push({
|
|
type: UserTypes.RECEIVED_PROFILES_LIST,
|
|
data,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
export function lastChannelIdForTeam(state: GlobalState, teamId: string): string {
|
|
const {channels, myMembers} = state.entities.channels;
|
|
const {currentUserId} = state.entities.users;
|
|
const {myPreferences} = state.entities.preferences;
|
|
const lastChannelForTeam = state.views.team.lastChannelForTeam[teamId];
|
|
const lastChannelId = lastChannelForTeam && lastChannelForTeam.length ? lastChannelForTeam[0] : '';
|
|
const lastChannel = channels[lastChannelId];
|
|
|
|
const isDMVisible = lastChannel && lastChannel.type === General.DM_CHANNEL &&
|
|
isDirectChannelVisible(currentUserId, myPreferences, lastChannel);
|
|
|
|
const isGMVisible = lastChannel && lastChannel.type === General.GM_CHANNEL &&
|
|
isGroupChannelVisible(myPreferences, lastChannel);
|
|
|
|
if (
|
|
myMembers[lastChannelId] &&
|
|
lastChannel &&
|
|
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)
|
|
) {
|
|
return lastChannelId;
|
|
}
|
|
|
|
// Fallback to default channel
|
|
const channelsInTeam = getChannelsNameMapInTeam(state, teamId);
|
|
const channel = selectChannelByName(channelsInTeam, getRedirectChannelNameForTeam(state, teamId));
|
|
|
|
if (channel) {
|
|
return channel.id;
|
|
}
|
|
|
|
// Handle case when the default channel cannot be found
|
|
// so we need to get the first available channel of the team
|
|
const teamChannels = Object.values(channelsInTeam);
|
|
const firstChannel = teamChannels.length ? teamChannels[0].id : '';
|
|
return firstChannel;
|
|
}
|
|
|
|
function fetchDirectMessageProfileIfNeeded(state: GlobalState, channel: Channel, channelMembers: Array<ChannelMembership>, profilesInChannel: Array<string>) {
|
|
const currentUserId = getCurrentUserId(state);
|
|
const myPreferences = getMyPreferences(state);
|
|
const users = getUsers(state);
|
|
const config = getConfig(state);
|
|
const currentChannelId = getCurrentChannelId(state);
|
|
const otherUserId = getUserIdFromChannelName(currentUserId, channel.name);
|
|
const otherUser = users[otherUserId];
|
|
const dmVisible = isDirectChannelVisible(currentUserId, myPreferences, channel);
|
|
const dmAutoClosed = isAutoClosed(config, myPreferences, channel, channel.last_post_at, otherUser ? otherUser.delete_at : 0, currentChannelId);
|
|
const member = channelMembers.find((cm) => cm.channel_id === channel.id);
|
|
const dmIsUnread = member ? member.mention_count > 0 : false;
|
|
const dmFetchProfile = dmIsUnread || (dmVisible && !dmAutoClosed);
|
|
const preferences = [];
|
|
|
|
// when then DM is hidden but has new messages
|
|
if ((!dmVisible || dmAutoClosed) && dmIsUnread) {
|
|
preferences.push(buildPreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, currentUserId, otherUserId));
|
|
preferences.push(buildPreference(Preferences.CATEGORY_CHANNEL_OPEN_TIME, currentUserId, channel.id, Date.now().toString()));
|
|
}
|
|
|
|
if (dmFetchProfile && !profilesInChannel.includes(otherUserId) && otherUserId !== currentUserId) {
|
|
return {
|
|
preferences,
|
|
promise: fetchUsersInChannel(state, channel.id),
|
|
};
|
|
}
|
|
|
|
return {preferences};
|
|
}
|
|
|
|
function fetchGroupMessageProfilesIfNeeded(state: GlobalState, channel: Channel, channelMembers: Array<ChannelMembership>, profilesInChannel: Array<string>) {
|
|
const currentUserId = getCurrentUserId(state);
|
|
const myPreferences = getMyPreferences(state);
|
|
const config = getConfig(state);
|
|
const gmVisible = isGroupChannelVisible(myPreferences, channel);
|
|
const gmAutoClosed = isAutoClosed(config, myPreferences, channel, channel.last_post_at, 0);
|
|
const channelMember = channelMembers.find((cm) => cm.channel_id === channel.id);
|
|
let hasMentions = false;
|
|
let isUnread = false;
|
|
|
|
if (channelMember) {
|
|
hasMentions = channelMember.mention_count > 0;
|
|
isUnread = channelMember.msg_count < channel.total_msg_count;
|
|
}
|
|
|
|
const gmIsUnread = hasMentions || isUnread;
|
|
const gmFetchProfile = gmIsUnread || (gmVisible && !gmAutoClosed);
|
|
const preferences = [];
|
|
|
|
// when then GM is hidden but has new messages
|
|
if ((!gmVisible || gmAutoClosed) && gmIsUnread) {
|
|
preferences.push(buildPreference(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, currentUserId, channel.id));
|
|
preferences.push(buildPreference(Preferences.CATEGORY_CHANNEL_OPEN_TIME, currentUserId, channel.id, Date.now().toString()));
|
|
}
|
|
|
|
if (gmFetchProfile && !profilesInChannel.length) {
|
|
return {
|
|
preferences,
|
|
promise: fetchUsersInChannel(state, channel.id),
|
|
};
|
|
}
|
|
|
|
return {preferences};
|
|
}
|
|
|
|
async function fetchUsersInChannel(state: GlobalState, channelId: string): Promise<ActionResult> {
|
|
try {
|
|
const currentUserId = getCurrentUserId(state);
|
|
const profiles = await Client4.getProfilesInChannel(channelId);
|
|
|
|
// When fetching profiles in channels we exclude our own user
|
|
const users = profiles.filter((p: UserProfile) => p.id !== currentUserId);
|
|
const data = {
|
|
channelId,
|
|
users,
|
|
};
|
|
|
|
return {data};
|
|
} catch (error) {
|
|
return {error};
|
|
}
|
|
}
|
|
|
|
async function getProfilesFromPromises(promises: Array<Promise<ActionResult>>): Promise<GenericAction | null> {
|
|
// Get the profiles returned by the promises
|
|
if (!promises.length) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const result = await Promise.all(promises);
|
|
const data = result.filter((p: any) => !p.error);
|
|
|
|
return {
|
|
type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL,
|
|
data,
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
} |