Files
mattermost/webapp/stores/channel_store.jsx
Joram Wilander ab67f6e257 PLT-6215 Major post list refactor (#6501)
* Major post list refactor

* Fix post and thread deletion

* Fix preferences not selecting correctly

* Fix military time displaying

* Fix UP key for editing posts

* Fix ESLint error

* Various fixes and updates per feedback

* Fix for permalink view

* Revert to old scrolling method and various fixes

* Add floating timestamp, new message indicator, scroll arrows

* Update post loading for focus mode and add visibility limit

* Fix pinning posts and a react warning

* Add loading UI updates from Asaad

* Fix refreshing loop

* Temporarily bump post visibility limit

* Update infinite scrolling

* Remove infinite scrolling
2017-06-18 14:42:32 -04:00

585 lines
15 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import EventEmitter from 'events';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
var ChannelUtils;
var Utils;
import {ActionTypes, Constants} from 'utils/constants.jsx';
import {isSystemMessage, isFromWebhook} from 'utils/post_utils.jsx';
const NotificationPrefs = Constants.NotificationPrefs;
const CHANGE_EVENT = 'change';
const STATS_EVENT = 'stats';
const LAST_VIEVED_EVENT = 'last_viewed';
import store from 'stores/redux_store.jsx';
import * as Selectors from 'mattermost-redux/selectors/entities/channels';
import {ChannelTypes, UserTypes} from 'mattermost-redux/action_types';
import {batchActions} from 'redux-batched-actions';
class ChannelStoreClass extends EventEmitter {
constructor(props) {
super(props);
this.setMaxListeners(600);
this.clear();
this.entities = store.getState().entities.channels;
store.subscribe(() => {
const newEntities = store.getState().entities.channels;
let doEmit = false;
if (newEntities.currentChannelId !== this.entities.currentChannelId) {
doEmit = true;
}
if (newEntities.channels !== this.entities.channels) {
this.setUnreadCountsByChannels(Object.values(newEntities.channels));
doEmit = true;
}
if (newEntities.myMembers !== this.entities.myMembers) {
this.setUnreadCountsByMembers(Object.values(newEntities.myMembers));
this.emitLastViewed();
doEmit = true;
}
if (newEntities.membersInChannel !== this.entities.membersInChannel) {
doEmit = true;
}
if (newEntities.stats !== this.entities.stats) {
this.emitStatsChange();
}
if (doEmit) {
this.emitChange();
}
this.entities = newEntities;
});
}
clear() {
this.postMode = this.POST_MODE_CHANNEL;
this.unreadCounts = {};
}
get POST_MODE_CHANNEL() {
return 1;
}
get POST_MODE_FOCUS() {
return 2;
}
emitChange() {
this.emit(CHANGE_EVENT);
}
addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
}
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
emitStatsChange() {
this.emit(STATS_EVENT);
}
addStatsChangeListener(callback) {
this.on(STATS_EVENT, callback);
}
removeStatsChangeListener(callback) {
this.removeListener(STATS_EVENT, callback);
}
emitLastViewed() {
this.emit(LAST_VIEVED_EVENT);
}
addLastViewedListener(callback) {
this.on(LAST_VIEVED_EVENT, callback);
}
removeLastViewedListener(callback) {
this.removeListener(LAST_VIEVED_EVENT, callback);
}
findFirstBy(field, value) {
return this.doFindFirst(field, value, this.getChannels());
}
findFirstMoreBy(field, value) {
return this.doFindFirst(field, value, this.getMoreChannels());
}
doFindFirst(field, value, channels) {
for (var i = 0; i < channels.length; i++) {
if (channels[i][field] === value) {
return channels[i];
}
}
return null;
}
get(id) {
return this.findFirstBy('id', id);
}
getMyMember(id) {
return this.getMyMembers()[id];
}
getByName(name) {
return this.findFirstBy('name', name);
}
getByDisplayName(displayName) {
return this.findFirstBy('display_name', displayName);
}
getMoreByName(name) {
return this.findFirstMoreBy('name', name);
}
getAll() {
return this.getChannels();
}
getMoreAll() {
return this.getMoreChannels();
}
setCurrentId(id) {
store.dispatch({
type: ChannelTypes.SELECT_CHANNEL,
data: id,
member: this.getMyMember(id)
});
}
resetCounts(ids) {
const membersToStore = [];
ids.forEach((id) => {
const member = this.getMyMember(id);
const channel = this.get(id);
if (member && channel) {
const memberToStore = {...member};
memberToStore.msg_count = channel.total_msg_count;
memberToStore.mention_count = 0;
membersToStore.push(memberToStore);
this.setUnreadCountByChannel(id);
}
});
this.storeMyChannelMembersList(membersToStore);
}
getCurrentId() {
return Selectors.getCurrentChannelId(store.getState());
}
getCurrent() {
var currentId = this.getCurrentId();
if (currentId) {
return this.get(currentId);
}
return null;
}
getCurrentMember() {
var currentId = this.getCurrentId();
if (currentId) {
return this.getMyMembers()[currentId];
}
return null;
}
getCurrentStats() {
return this.getStats(this.getCurrentId());
}
getStats(channelId) {
let stats;
if (channelId) {
stats = Selectors.getAllChannelStats(store.getState())[channelId];
}
if (stats) {
// create a defensive copy
stats = Object.assign({}, stats);
} else {
stats = {member_count: 0};
}
return stats;
}
storeChannel(channel) {
var channels = this.getChannels();
var found;
for (var i = 0; i < channels.length; i++) {
if (channels[i].id === channel.id) {
channels[i] = channel;
found = true;
break;
}
}
if (!found) {
channels.push(channel);
}
if (!ChannelUtils) {
ChannelUtils = require('utils/channel_utils.jsx'); //eslint-disable-line global-require
}
channels = channels.sort(ChannelUtils.sortChannelsByDisplayName);
this.storeChannels(channels);
}
storeChannels(channels) {
store.dispatch({
type: ChannelTypes.RECEIVED_CHANNELS,
data: channels,
teamId: channels[0].team_id
});
}
getChannels() {
return Selectors.getMyChannels(store.getState());
}
getChannelById(id) {
return this.get(id);
}
storeMyChannelMember(channelMember) {
store.dispatch({
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
data: channelMember
});
}
storeMyChannelMembers(channelMembers) {
store.dispatch({
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS,
data: Object.values(channelMembers)
});
}
storeMyChannelMembersList(channelMembers) {
store.dispatch({
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS,
data: channelMembers
});
}
getMyMembers() {
return Selectors.getMyChannelMemberships(store.getState());
}
saveMembersInChannel(channelId = this.getCurrentId(), members) {
store.dispatch({
type: ChannelTypes.RECEIVED_CHANNEL_MEMBERS,
data: Object.values(members)
});
}
removeMemberInChannel(channelId = this.getCurrentId(), userId) {
store.dispatch({
type: UserTypes.RECEIVED_PROFILE_NOT_IN_CHANNEL,
data: {id: channelId, user_id: userId}
});
}
getMembersInChannel(channelId = this.getCurrentId()) {
return Selectors.getChannelMembersInChannels(store.getState())[channelId] || {};
}
hasActiveMemberInChannel(channelId = this.getCurrentId(), userId) {
const members = this.getMembersInChannel(channelId);
if (members && members[userId]) {
return true;
}
return false;
}
storeMoreChannels(channels, teamId = TeamStore.getCurrentId()) {
store.dispatch({
type: ChannelTypes.RECEIVED_CHANNELS,
data: channels,
teamId
});
}
getMoreChannels() {
const channels = Selectors.getOtherChannels(store.getState());
const channelMap = {};
channels.forEach((c) => {
channelMap[c.id] = c;
});
return channelMap;
}
getMoreChannelsList() {
return Selectors.getOtherChannels(store.getState());
}
isDefault(channel) {
return channel.name === Constants.DEFAULT_CHANNEL;
}
setPostMode(mode) {
this.postMode = mode;
}
getPostMode() {
return this.postMode;
}
setUnreadCountsByMembers(members) {
members.forEach((m) => {
this.setUnreadCountByChannel(m.channel_id);
});
}
setUnreadCountsByCurrentMembers() {
Object.keys(this.getMyMembers()).forEach((key) => {
this.setUnreadCountByChannel(this.getMyMember(key).channel_id);
});
}
setUnreadCountsByChannels(channels) {
channels.forEach((c) => {
this.setUnreadCountByChannel(c.id);
});
}
setUnreadCountByChannel(id) {
const ch = this.get(id);
const chMember = this.getMyMember(id);
if (ch == null || chMember == null) {
return;
}
const chMentionCount = chMember.mention_count;
let chUnreadCount = ch.total_msg_count - chMember.msg_count;
if (chMember.notify_props && chMember.notify_props.mark_unread === NotificationPrefs.MENTION) {
chUnreadCount = 0;
}
this.unreadCounts[id] = {msgs: chUnreadCount, mentions: chMentionCount};
}
getUnreadCount(id) {
return this.unreadCounts[id] || {msgs: 0, mentions: 0};
}
getUnreadCounts() {
return this.unreadCounts;
}
getChannelNamesMap() {
var channelNamesMap = {};
var channels = this.getChannels();
for (var key in channels) {
if (channels.hasOwnProperty(key)) {
var channel = channels[key];
channelNamesMap[channel.name] = channel;
}
}
var moreChannels = this.getMoreChannels();
for (var moreKey in moreChannels) {
if (moreChannels.hasOwnProperty(moreKey)) {
var moreChannel = moreChannels[moreKey];
channelNamesMap[moreChannel.name] = moreChannel;
}
}
return channelNamesMap;
}
isChannelAdminForCurrentChannel() {
if (!Utils) {
Utils = require('utils/utils.jsx'); //eslint-disable-line global-require
}
const member = this.getMyMember(this.getCurrentId());
if (!member) {
return false;
}
return Utils.isChannelAdmin(member.roles);
}
isChannelAdmin(userId, channelId) {
if (!Utils) {
Utils = require('utils/utils.jsx'); //eslint-disable-line global-require
}
const channelMembers = this.getMembersInChannel(channelId);
const channelMember = channelMembers[userId];
if (channelMember) {
return Utils.isChannelAdmin(channelMember.roles);
}
return false;
}
incrementMessages(id, markRead = false) {
if (!this.unreadCounts[id]) {
return;
}
const member = this.getMyMember(id);
if (member && member.notify_props && member.notify_props.mark_unread === NotificationPrefs.MENTION) {
return;
}
const channel = {...this.get(id)};
channel.total_msg_count++;
const actions = [];
if (markRead) {
actions.push({
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
data: {...member, msg_count: channel.total_msg_count}
});
}
actions.push(
{
type: ChannelTypes.RECEIVED_CHANNEL,
data: channel
}
);
store.dispatch(batchActions(actions));
}
incrementMentionsIfNeeded(id, msgProps) {
let mentions = [];
if (msgProps && msgProps.mentions) {
mentions = JSON.parse(msgProps.mentions);
}
if (!this.unreadCounts[id]) {
return;
}
if (mentions.indexOf(UserStore.getCurrentId()) !== -1) {
this.unreadCounts[id].mentions++;
const member = {...this.getMyMember(id)};
member.mention_count++;
store.dispatch({
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
data: member
});
}
}
}
var ChannelStore = new ChannelStoreClass();
ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
var action = payload.action;
switch (action.type) {
case ActionTypes.CLICK_CHANNEL:
ChannelStore.setCurrentId(action.id);
ChannelStore.setPostMode(ChannelStore.POST_MODE_CHANNEL);
break;
case ActionTypes.RECEIVED_FOCUSED_POST: {
const post = action.post_list.posts[action.postId];
ChannelStore.setCurrentId(post.channel_id);
ChannelStore.setPostMode(ChannelStore.POST_MODE_FOCUS);
ChannelStore.emitChange();
break;
}
case ActionTypes.RECEIVED_CHANNELS:
ChannelStore.storeChannels(action.channels);
break;
case ActionTypes.RECEIVED_CHANNEL:
ChannelStore.storeChannel(action.channel);
if (action.member) {
ChannelStore.storeMyChannelMember(action.member);
}
break;
case ActionTypes.RECEIVED_MY_CHANNEL_MEMBERS:
ChannelStore.storeMyChannelMembersList(action.members);
break;
case ActionTypes.RECEIVED_CHANNEL_MEMBER:
ChannelStore.storeMyChannelMember(action.member);
break;
case ActionTypes.RECEIVED_MORE_CHANNELS:
ChannelStore.storeMoreChannels(action.channels);
break;
case ActionTypes.RECEIVED_MEMBERS_IN_CHANNEL:
ChannelStore.saveMembersInChannel(action.channel_id, action.channel_members);
break;
case ActionTypes.RECEIVED_CHANNEL_STATS:
store.dispatch({
type: ChannelTypes.RECEIVED_CHANNEL_STATS,
data: action.stats
});
break;
case ActionTypes.RECEIVED_POST:
if (Constants.IGNORE_POST_TYPES.indexOf(action.post.type) !== -1) {
return;
}
if (action.post.user_id === UserStore.getCurrentId() && !isSystemMessage(action.post) && !isFromWebhook(action.post)) {
return;
}
var id = action.post.channel_id;
var teamId = action.websocketMessageProps ? action.websocketMessageProps.team_id : null;
var markRead = id === ChannelStore.getCurrentId() && window.isActive;
if (TeamStore.getCurrentId() === teamId || teamId === '') {
if (!markRead) {
ChannelStore.incrementMentionsIfNeeded(id, action.websocketMessageProps);
}
ChannelStore.incrementMessages(id, markRead);
}
break;
case ActionTypes.CREATE_POST:
ChannelStore.incrementMessages(action.post.channel_id, true);
break;
case ActionTypes.CREATE_COMMENT:
ChannelStore.incrementMessages(action.post.channel_id, true);
break;
default:
break;
}
});
export default ChannelStore;