Refactor Jump to Conversation (#726)

* Refactor Jump to Conversation

Fixed channel loader not showing when switching channels
Refactored jump to conversation

* Review feedback
This commit is contained in:
Stan Chan 2017-07-10 05:30:51 -07:00 committed by Harrison Healey
parent c017afb392
commit b03189defb
7 changed files with 160 additions and 81 deletions

View file

@ -144,7 +144,7 @@ export default class ChannelDrawer extends PureComponent {
this.setState({openDrawer: true});
};
selectChannel = (id) => {
selectChannel = (channel) => {
const {
actions,
currentChannel
@ -154,15 +154,17 @@ export default class ChannelDrawer extends PureComponent {
handleSelectChannel,
markChannelAsRead,
setChannelLoading,
setChannelDisplayName,
viewChannel
} = actions;
markChannelAsRead(id, currentChannel.id);
setChannelLoading();
viewChannel(id, currentChannel.id);
markChannelAsRead(channel.id, currentChannel.id);
viewChannel(channel.id, currentChannel.id);
setChannelDisplayName(channel.display_name);
this.closeChannelDrawer();
InteractionManager.runAfterInteractions(() => {
handleSelectChannel(id);
handleSelectChannel(channel.id);
});
};

View file

@ -42,6 +42,8 @@ class FilteredList extends Component {
PropTypes.object,
PropTypes.array
),
searchOrder: PropTypes.array.isRequired,
pastDirectMessages: PropTypes.array,
statuses: PropTypes.object,
styles: PropTypes.object.isRequired,
term: PropTypes.string,
@ -50,7 +52,8 @@ class FilteredList extends Component {
static defaultProps = {
currentTeam: {},
currentChannel: {}
currentChannel: {},
pastDirectMessages: []
};
constructor(props) {
@ -116,72 +119,92 @@ class FilteredList extends Component {
});
};
buildChannelsForSearch = (props, term) => {
const data = [];
const {groupChannels, otherChannels, styles} = props;
const {
unreadChannels,
favoriteChannels,
publicChannels,
privateChannels
} = props.channels;
getSectionBuilders = () => ({
unreads: {
builder: this.buildUnreadChannelsForSearch,
id: 'mobile.channel_list.unreads',
defaultMessage: 'UNREADS'
},
channels: {
builder: this.buildChannelsForSearch,
id: 'sidebar.channels',
defaultMessage: 'CHANNELS'
},
dms: {
builder: this.buildCurrentDMSForSearch,
id: 'sidebar.direct',
defaultMessage: 'DIRECT MESSAGES'
},
members: {
builder: this.buildMembersForSearch,
id: 'mobile.channel_list.members',
defaultMessage: 'MEMBERS'
},
nonmembers: {
builder: this.buildOtherMembersForSearch,
id: 'mobile.channel_list.not_member',
defaultMessage: 'NOT A MEMBER'
}
});
buildUnreadChannelsForSearch = (props, term) => {
const {unreadChannels} = props.channels;
return this.filterChannels(unreadChannels, term);
}
buildCurrentDMSForSearch = (props, term) => {
const {channels, teammateNameDisplay, profiles, statuses, pastDirectMessages} = props;
const {favoriteChannels} = channels;
const favoriteDms = favoriteChannels.filter((c) => {
return c.type === General.DM_CHANNEL;
});
const directChannelUsers = [];
const groupChannels = [];
channels.directAndGroupChannels.forEach((c) => {
if (c.type === General.DM_CHANNEL) {
directChannelUsers.push(profiles[c.teammate_id]);
} else {
groupChannels.push(c);
}
});
const pastDirectMessageUsers = pastDirectMessages.map((p) => profiles[p]).filter((p) => typeof p !== 'undefined');
const dms = [...directChannelUsers, ...pastDirectMessageUsers].map((u) => {
const displayName = displayUsername(u, teammateNameDisplay);
const notMemberOf = otherChannels.map((o) => {
return {
...o,
id: u.id,
status: statuses[u.id],
display_name: displayName,
username: u.username,
email: u.email,
name: displayName,
type: General.DM_CHANNEL,
fake: true
};
});
const favorites = favoriteChannels.filter((c) => {
return c.type !== General.DM_CHANNEL && c.type !== General.GM_CHANNEL;
return this.filterChannels([...favoriteDms, ...dms, ...groupChannels], term).sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
}
buildMembersForSearch = (props, term) => {
const {channels, currentUserId, teammateNameDisplay, profiles, statuses, pastDirectMessages} = props;
const {favoriteChannels, unreadChannels} = channels;
const favoriteAndUnreadDms = [...favoriteChannels, ...unreadChannels].filter((c) => {
return c.type === General.DM_CHANNEL;
});
const unreads = this.filterChannels(unreadChannels, term);
const channels = this.filterChannels([...favorites, ...publicChannels, ...privateChannels], term).
sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
const directAndGroupChannelMembers = [...channels.directAndGroupChannels, ...favoriteAndUnreadDms].filter((c) => c.type === General.DM_CHANNEL).map((c) => c.teammate_id);
const others = this.filterChannels(notMemberOf, term);
const groups = this.filterChannels(groupChannels, term);
const fakeDms = this.filterChannels(this.buildFakeDms(props), term);
const directMessages = [...groups, ...fakeDms].sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
const userNotInDirectOrGroupChannels = Object.values(profiles).filter((u) => directAndGroupChannelMembers.indexOf(u.id) === -1 && pastDirectMessages.indexOf(u.id) === -1 && u.id !== currentUserId);
if (unreads.length) {
data.push(
this.renderTitle(styles, 'mobile.channel_list.unreads', 'UNREADS', null, false, true),
...unreads
);
}
if (channels.length) {
data.push(
this.renderTitle(styles, 'sidebar.channels', 'CHANNELS', null, unreads.length > 0, true),
...channels
);
}
if (others.length) {
data.push(
this.renderTitle(styles, 'mobile.channel_list.not_member', 'NOT A MEMBER', null, channels.length > 0, true),
...others
);
}
if (directMessages.length) {
data.push(
this.renderTitle(styles, 'sidebar.direct', 'DIRECT MESSAGES', null, others.length > 0, true),
...directMessages
);
}
return data;
};
buildFakeDms = (props) => {
const {currentUserId, teammateNameDisplay, profiles, statuses} = props;
const users = Object.values(profiles).filter((p) => p.id !== currentUserId);
return users.map((u) => {
const members = userNotInDirectOrGroupChannels.map((u) => {
const displayName = displayUsername(u, teammateNameDisplay);
return {
@ -193,6 +216,61 @@ class FilteredList extends Component {
fake: true
};
});
const fakeDms = this.filterChannels([...members], term);
return [...fakeDms].sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
}
buildChannelsForSearch = (props, term) => {
const {
favoriteChannels,
publicChannels,
privateChannels
} = props.channels;
const favorites = favoriteChannels.filter((c) => {
return c.type !== General.DM_CHANNEL && c.type !== General.GM_CHANNEL;
});
return this.filterChannels([...favorites, ...publicChannels, ...privateChannels], term).
sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
}
buildOtherMembersForSearch = (props, term) => {
const {otherChannels} = props;
const notMemberOf = otherChannels.map((o) => {
return {
...o,
fake: true
};
});
return this.filterChannels(notMemberOf, term);
}
buildSectionsForSearch = (props, term) => {
const items = [];
const {searchOrder, styles} = props;
const sectionBuilders = this.getSectionBuilders();
let previousDataLength = 0;
for (const section of searchOrder) {
if (sectionBuilders.hasOwnProperty(section)) {
const sectionBuilder = sectionBuilders[section];
const {builder, defaultMessage, id} = sectionBuilder;
const data = builder(props, term);
if (data.length) {
const title = this.renderTitle(styles, id, defaultMessage, null, previousDataLength > 0, true);
items.push(title, ...data);
previousDataLength = data.length;
}
}
}
return items;
};
buildData = (props, term) => {
@ -200,7 +278,7 @@ class FilteredList extends Component {
return null;
}
return this.buildChannelsForSearch(props, term);
return this.buildSectionsForSearch(props, term);
};
renderSectionAction = (styles, action) => {

View file

@ -3,6 +3,7 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import {searchChannels} from 'mattermost-redux/actions/channels';
import {searchProfiles} from 'mattermost-redux/actions/users';
@ -11,10 +12,19 @@ import {General} from 'mattermost-redux/constants';
import {getGroupChannels, getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getProfilesInCurrentTeam, getUsers, getUserStatuses} from 'mattermost-redux/selectors/entities/users';
import {getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
import {getDirectShowPreferences, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
import Config from 'assets/config';
import FilteredList from './filtered_list';
const DEFAULT_SEARCH_ORDER = ['unreads', 'dms', 'channels', 'members', 'nonmembers'];
const pastDirectMessages = createSelector(
getDirectShowPreferences,
(directChannelsFromPreferences) => directChannelsFromPreferences.filter((d) => d.value === 'false').map((d) => d.name)
);
function mapStateToProps(state, ownProps) {
const {currentUserId} = state.entities.users;
@ -25,6 +35,8 @@ function mapStateToProps(state, ownProps) {
profiles = getProfilesInCurrentTeam(state);
}
const searchOrder = Config.DrawerSearchOrder ? Config.DrawerSearchOrder : DEFAULT_SEARCH_ORDER;
return {
currentUserId,
otherChannels: getOtherChannels(state),
@ -32,6 +44,8 @@ function mapStateToProps(state, ownProps) {
profiles,
teammateNameDisplay: getTeammateNameDisplaySetting(state),
statuses: getUserStatuses(state),
searchOrder,
pastDirectMessages: pastDirectMessages(state),
...ownProps
};
}

View file

@ -62,7 +62,7 @@ class ChannelsList extends Component {
if (channel.fake) {
this.props.onJoinChannel(channel);
} else {
this.props.onSelectChannel(channel.id);
this.props.onSelectChannel(channel);
}
this.refs.search_bar.cancel();

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {General} from 'mattermost-redux/constants';
@ -9,8 +8,6 @@ import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {setChannelDisplayName} from 'app/actions/views/channel';
import List from './list';
function mapStateToProps(state, ownProps) {
@ -23,12 +20,4 @@ function mapStateToProps(state, ownProps) {
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
setChannelDisplayName
}, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(List);
export default connect(mapStateToProps, null)(List);

View file

@ -24,9 +24,6 @@ import UnreadIndicator from 'app/components/channel_drawer/channels_list/unread_
class List extends Component {
static propTypes = {
actions: PropTypes.shape({
setChannelDisplayName: PropTypes.func.isRequired
}).isRequired,
canCreatePrivateChannels: PropTypes.bool.isRequired,
channels: PropTypes.object.isRequired,
channelMembers: PropTypes.object,
@ -95,7 +92,6 @@ class List extends Component {
};
onSelectChannel = (channel) => {
this.props.actions.setChannelDisplayName(channel.display_name);
this.props.onSelectChannel(channel);
};

View file

@ -24,7 +24,7 @@ function makeMapStateToProps() {
const posts = getPostsInChannel(state, channelId) || [];
return {
channelIsLoading: (getPosts.status === RequestStatus.STARTED),
channelIsLoading: getPosts.status === RequestStatus.STARTED || state.views.channel.loading,
channelIsRefreshing: refreshing,
currentChannelId: getCurrentChannelId(state),
posts,