From 263fd5088760827f370e6f238fa9152abe4884f7 Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Thu, 9 Feb 2017 09:12:44 -0800 Subject: [PATCH] Channel info delete (#222) * Initial setup to delete channels * Combine leave and delete * Working delete when removing the current channel id delete * Initial working delete with handling web sockets * Move go back out of component * Refactor alert function and handle after leave navigation * Remove promise then * Allow channel membership to default to empty object * Ensure that channel is present to avoid method on undefined * Remove view channel delete action * Remove unused type --- app/actions/views/channel.js | 9 ++- app/components/channel_list/channel_list.js | 2 +- app/scenes/channel_info/channel_info.js | 70 +++++++++++-------- .../channel_info/channel_info_container.js | 4 +- assets/base/i18n/en.json | 2 + service/actions/channels.js | 25 ++++++- service/actions/websocket.js | 3 +- service/selectors/entities/channels.js | 2 +- 8 files changed, 74 insertions(+), 43 deletions(-) diff --git a/app/actions/views/channel.js b/app/actions/views/channel.js index 396abb35a..be9a12b50 100644 --- a/app/actions/views/channel.js +++ b/app/actions/views/channel.js @@ -195,10 +195,9 @@ export function unmarkFavorite(channelId) { export function leaveChannel(channel, reset = false) { return async (dispatch, getState) => { const {currentId: teamId} = getState().entities.teams; - serviceLeaveChannel(teamId, channel.id)(dispatch, getState).then(() => { - if (channel.isCurrent || reset) { - selectInitialChannel(teamId)(dispatch, getState); - } - }); + await serviceLeaveChannel(teamId, channel.id)(dispatch, getState); + if (channel.isCurrent || reset) { + await selectInitialChannel(teamId)(dispatch, getState); + } }; } diff --git a/app/components/channel_list/channel_list.js b/app/components/channel_list/channel_list.js index a9f65de2d..f1cae858a 100644 --- a/app/components/channel_list/channel_list.js +++ b/app/components/channel_list/channel_list.js @@ -283,7 +283,7 @@ class ChannelList extends React.Component { const member = this.props.channelMembers[channel.id]; let mentions = 0; let unreadCount = 0; - if (member) { + if (member && channel) { mentions = member.mention_count; unreadCount = channel.total_msg_count - member.msg_count; diff --git a/app/scenes/channel_info/channel_info.js b/app/scenes/channel_info/channel_info.js index 0007a764b..81901281d 100644 --- a/app/scenes/channel_info/channel_info.js +++ b/app/scenes/channel_info/channel_info.js @@ -47,11 +47,12 @@ class ChannelInfo extends PureComponent { actions: PropTypes.shape({ getChannelStats: PropTypes.func.isRequired, goToChannelMembers: PropTypes.func.isRequired, + goBack: PropTypes.func.isRequired, goToChannelAddMembers: PropTypes.func.isRequired, markFavorite: PropTypes.func.isRequired, unmarkFavorite: PropTypes.func.isRequired, - goBack: PropTypes.func.isRequired, - leaveChannel: PropTypes.func.isRequired + leaveChannel: PropTypes.func.isRequired, + deleteChannel: PropTypes.func.isRequired }) }; @@ -72,16 +73,6 @@ class ChannelInfo extends PureComponent { if (isFavorite !== this.state.isFavorite) { this.setState({isFavorite}); } - this.navigateAfterLeave(nextProps.leaveChannelRequest); - } - - navigateAfterLeave(leaveChannelRequest) { - if ( - leaveChannelRequest !== this.props.leaveChannelRequest && - leaveChannelRequest.status === 'success' - ) { - this.props.actions.goBack(); - } } handleFavorite = () => { @@ -92,34 +83,54 @@ class ChannelInfo extends PureComponent { toggleFavorite(currentChannel.id); }; - handleLeave() { + handleDeleteOrLeave(eventType) { const {formatMessage} = this.props.intl; const channel = this.props.currentChannel; const term = channel.type === Constants.OPEN_CHANNEL ? formatMessage({id: 'mobile.channel_info.publicChannel', defaultMessage: 'Public Channel'}) : formatMessage({id: 'mobile.channel_info.privateChannel', defaultMessage: 'Private Channel'}); + let title; + let message; + let onPressAction; + if (eventType === 'leave') { + title = {id: 'mobile.channel_info.alertTitleLeaveChannel', defaultMessage: 'Leave {term}'}; + message = { + id: 'mobile.channel_info.alertMessageLeaveChannel', + defaultMessage: 'Are you sure you want to leave the {term} {name}?' + }; + onPressAction = () => { + this.props.actions.leaveChannel(channel, true).then(this.props.actions.goBack); + }; + } else if (eventType === 'delete') { + title = {id: 'mobile.channel_info.alertTitleDeleteChannel', defaultMessage: 'Delete {term}'}; + message = { + id: 'mobile.channel_info.alertMessageDeleteChannel', + defaultMessage: 'Are you sure you want to delete the {term} {name}?' + }; + onPressAction = () => { + this.props.actions.deleteChannel(channel.team_id, channel.id).then(this.props.actions.goBack); + }; + } Alert.alert( - formatMessage({id: 'mobile.channel_info.alertTitleLeaveChannel', defaultMessage: 'Leave {term}'}, {term}), - formatMessage({ - id: 'mobile.channel_info.alertMessageLeaveChannel', - defaultMessage: 'Are you sure you want to leave the {term} with {name}?' - }, { - term: term.toLowerCase(), - name: channel.display_name - }), + formatMessage(title, {term}), + formatMessage( + message, + { + term: term.toLowerCase(), + name: channel.display_name + } + ), [{ text: formatMessage({id: 'mobile.channel_info.alertNo', defaultMessage: 'No'}) }, { text: formatMessage({id: 'mobile.channel_info.alertYes', defaultMessage: 'Yes'}), - onPress: () => { - this.props.actions.leaveChannel(channel, true); - } - }] + onPress: onPressAction + }], ); } - renderLeaveChannelRow() { + renderLeaveOrDeleteChannelRow() { const channel = this.props.currentChannel; const isDefaultChannel = channel.name === Constants.DEFAULT_CHANNEL; const isDirectMessage = channel.type === Constants.DM_CHANNEL; @@ -190,20 +201,21 @@ class ChannelInfo extends PureComponent { } this.handleLeave()} + action={() => this.handleDeleteOrLeave('leave')} defaultMessage='Leave Channel' icon='sign-out' textId='navbar.leave' - shouldRender={this.renderLeaveChannelRow()} + shouldRender={this.renderLeaveOrDeleteChannelRow()} /> true} + action={() => this.handleDeleteOrLeave('delete')} defaultMessage='Delete Channel' icon='trash' iconColor='#DA4A4A' textId='mobile.routes.channelInfo.delete_channel' textColor='#DA4A4A' + shouldRender={this.renderLeaveOrDeleteChannelRow()} /> diff --git a/app/scenes/channel_info/channel_info_container.js b/app/scenes/channel_info/channel_info_container.js index 1697a6d8d..c42b86ec8 100644 --- a/app/scenes/channel_info/channel_info_container.js +++ b/app/scenes/channel_info/channel_info_container.js @@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux'; import navigationSceneConnect from '../navigationSceneConnect'; import {goToChannelMembers, goToChannelAddMembers, goBack} from 'app/actions/navigation'; -import {getChannelStats} from 'service/actions/channels'; +import {getChannelStats, deleteChannel} from 'service/actions/channels'; import {markFavorite, unmarkFavorite, leaveChannel} from 'app/actions/views/channel'; import {getCurrentChannel, getCurrentChannelStats, getChannelsByCategory} from 'service/selectors/entities/channels'; import {getTheme} from 'service/selectors/entities/preferences'; @@ -23,7 +23,6 @@ function mapStateToProps(state, ownProps) { const isFavorite = favoriteChannels.indexOf(currentChannel.id) > -1; const leaveChannelRequest = state.requests.channels.leaveChannel; const currentUserRoles = getCurrentUserRoles(state); - const isAdmin = currentUserRoles.includes('_admin'); return { @@ -47,6 +46,7 @@ function mapDispatchToProps(dispatch) { markFavorite, unmarkFavorite, leaveChannel, + deleteChannel, goBack }, dispatch) }; diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index a2b861b95..d771611cf 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -1486,6 +1486,8 @@ "mobile.channel_info.privateChannel": "Private Channel", "mobile.channel_info.alertTitleLeaveChannel": "Leave {term}", "mobile.channel_info.alertMessageLeaveChannel": "Are you sure you want to leave the {term} with {name}?", + "mobile.channel_info.alertTitleDeleteChannel": "Delete {term}", + "mobile.channel_info.alertMessageDeleteChannel": "Are you sure you want to delete the {term} with {name}?", "mobile.channel_info.alertNo": "No", "mobile.channel_info.alertYes": "Yes", "mobile.channel_list.alertTitleLeaveChannel": "Leave {term}", diff --git a/service/actions/channels.js b/service/actions/channels.js index 7cc95b4dd..fe909bfe0 100644 --- a/service/actions/channels.js +++ b/service/actions/channels.js @@ -381,6 +381,18 @@ export function deleteChannel(teamId, channelId) { return; } + const entities = getState().entities; + const {channels, currentId} = entities.channels; + if (channelId === currentId) { + const channel = Object.keys(channels).filter((key) => channels[key].name === Constants.DEFAULT_CHANNEL); + let defaultChannelId = ''; + if (channel.length) { + defaultChannelId = channel[0]; + } + + dispatch({type: ChannelTypes.SELECT_CHANNEL, data: defaultChannelId}, getState); + } + dispatch(batchActions([ { type: ChannelTypes.RECEIVED_CHANNEL_DELETED, @@ -407,23 +419,30 @@ export function viewChannel(teamId, channelId, prevChannelId = '') { } const {channels} = getState().entities.channels; - + let totalMsgCount = 0; + if (channels[channelId]) { + totalMsgCount = channels[channelId].total_msg_count; + } const actions = [{ type: ChannelTypes.RECEIVED_LAST_VIEWED, data: { channel_id: channelId, last_viewed_at: new Date().getTime(), - total_msg_count: channels[channelId].total_msg_count + total_msg_count: totalMsgCount } }]; if (prevChannelId) { + let prevTotalMsgCount = 0; + if (channels[channelId]) { + prevTotalMsgCount = channels[channelId].total_msg_count; + } actions.push({ type: ChannelTypes.RECEIVED_LAST_VIEWED, data: { channel_id: prevChannelId, last_viewed_at: new Date().getTime(), - total_msg_count: channels[prevChannelId].total_msg_count + total_msg_count: prevTotalMsgCount } }); } diff --git a/service/actions/websocket.js b/service/actions/websocket.js index a1e032efb..3be7a1cd3 100644 --- a/service/actions/websocket.js +++ b/service/actions/websocket.js @@ -313,8 +313,6 @@ function handleChannelDeletedEvent(msg, dispatch, getState) { const teams = entities.teams; if (msg.broadcast.team_id === teams.currentId) { - dispatch({type: ChannelTypes.RECEIVED_CHANNEL_DELETED, data: msg.data.channel_id}, getState); - if (msg.data.channel_id === currentId) { let channelId = ''; const channel = Object.keys(channels).filter((key) => channels[key].name === Constants.DEFAULT_CHANNEL); @@ -325,6 +323,7 @@ function handleChannelDeletedEvent(msg, dispatch, getState) { dispatch({type: ChannelTypes.SELECT_CHANNEL, data: channelId}, getState); } + dispatch({type: ChannelTypes.RECEIVED_CHANNEL_DELETED, data: msg.data.channel_id}, getState); fetchMyChannelsAndMembers(teams.currentId)(dispatch, getState); } diff --git a/service/selectors/entities/channels.js b/service/selectors/entities/channels.js index ce6f9053d..7bfd75b7d 100644 --- a/service/selectors/entities/channels.js +++ b/service/selectors/entities/channels.js @@ -39,7 +39,7 @@ export const getCurrentChannelMembership = createSelector( getCurrentChannelId, getChannelMemberships, (currentChannelId, channelMemberships) => { - return channelMemberships[currentChannelId]; + return channelMemberships[currentChannelId] || {}; } );