From 7d03c24b44a2f4eba86adf86954280fa73e726e4 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 2 Oct 2015 09:50:34 -0400 Subject: Changed direct channels in the sidebar to be displayed based on user preferences --- web/react/components/sidebar.jsx | 166 ++++++++++++++++++++++++++-------- web/react/stores/preference_store.jsx | 135 +++++++++++++++++++++++++++ web/react/utils/async_client.jsx | 29 ++++++ web/react/utils/client.jsx | 14 +++ web/react/utils/constants.jsx | 1 + 5 files changed, 308 insertions(+), 37 deletions(-) create mode 100644 web/react/stores/preference_store.jsx (limited to 'web/react') diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 4ac1fd4a0..115e9c9c6 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -1,19 +1,20 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var ChannelStore = require('../stores/channel_store.jsx'); -var Client = require('../utils/client.jsx'); -var AsyncClient = require('../utils/async_client.jsx'); -var SocketStore = require('../stores/socket_store.jsx'); -var UserStore = require('../stores/user_store.jsx'); -var TeamStore = require('../stores/team_store.jsx'); -var BrowserStore = require('../stores/browser_store.jsx'); -var Utils = require('../utils/utils.jsx'); -var SidebarHeader = require('./sidebar_header.jsx'); -var SearchBox = require('./search_bar.jsx'); -var Constants = require('../utils/constants.jsx'); -var NewChannelFlow = require('./new_channel_flow.jsx'); -var UnreadChannelIndicator = require('./unread_channel_indicator.jsx'); +const AsyncClient = require('../utils/async_client.jsx'); +const BrowserStore = require('../stores/browser_store.jsx'); +const ChannelStore = require('../stores/channel_store.jsx'); +const Client = require('../utils/client.jsx'); +const Constants = require('../utils/constants.jsx'); +const PreferenceStore = require('../stores/preference_store.jsx'); +const NewChannelFlow = require('./new_channel_flow.jsx'); +const SearchBox = require('./search_bar.jsx'); +const SidebarHeader = require('./sidebar_header.jsx'); +const SocketStore = require('../stores/socket_store.jsx'); +const TeamStore = require('../stores/team_store.jsx'); +const UnreadChannelIndicator = require('./unread_channel_indicator.jsx'); +const UserStore = require('../stores/user_store.jsx'); +const Utils = require('../utils/utils.jsx'); export default class Sidebar extends React.Component { constructor(props) { @@ -23,6 +24,9 @@ export default class Sidebar extends React.Component { this.firstUnreadChannel = null; this.lastUnreadChannel = null; + this.getStateFromStores = this.getStateFromStores.bind(this); + //this.getDirectChannelsFromStores = this.getDirectChannelsFromStores.bind(this); + this.onChange = this.onChange.bind(this); this.onScroll = this.onScroll.bind(this); this.onResize = this.onResize.bind(this); @@ -36,7 +40,7 @@ export default class Sidebar extends React.Component { this.state = state; } getStateFromStores() { - var members = ChannelStore.getAllMembers(); + const members = ChannelStore.getAllMembers(); var teamMemberMap = UserStore.getActiveOnlyProfiles(); var currentId = ChannelStore.getCurrentId(); @@ -48,11 +52,13 @@ export default class Sidebar extends React.Component { teammates.push(teamMemberMap[id]); } + const preferences = PreferenceStore.getPreferences('direct_channels', 'show_hide'); + // Create lists of all read and unread direct channels - var showDirectChannels = []; - var readDirectChannels = []; + var visibleDirectChannels = []; + var hiddenDirectChannels = []; for (var i = 0; i < teammates.length; i++) { - var teammate = teammates[i]; + const teammate = teammates[i]; if (teammate.id === UserStore.getCurrentId()) { continue; @@ -65,7 +71,7 @@ export default class Sidebar extends React.Component { channelName = teammate.id + '__' + UserStore.getCurrentId(); } - var channel = ChannelStore.getByName(channelName); + let channel = ChannelStore.getByName(channelName); if (channel == null) { var tempChannel = {}; @@ -84,21 +90,44 @@ export default class Sidebar extends React.Component { channel.status = UserStore.getStatus(teammate.id); - var channelMember = members[channel.id]; + /*var channelMember = members[channel.id]; var msgCount = channel.total_msg_count - channelMember.msg_count; if (msgCount > 0) { - showDirectChannels.push(channel); + visibleDirectChannels.push(channel); } else if (currentId === channel.id) { - showDirectChannels.push(channel); + visibleDirectChannels.push(channel); } else { - readDirectChannels.push(channel); - } + hiddenDirectChannels.push(channel); + }*/ + } else { + channel = {}; + channel.fake = true; + channel.name = channelName; + channel.display_name = teammate.username; + channel.teammate_username = teammate.username; + channel.status = UserStore.getStatus(teammate.id); + channel.last_post_at = 0; + channel.total_msg_count = 0; + channel.type = 'D'; + } + + if (preferences.some((preference) => (preference.alt_id === teammate.id && preference.value !== 'false'))) { + visibleDirectChannels.push(channel); + } else { + hiddenDirectChannels.push(channel); } } - // If we don't have MAX_DMS unread channels, sort the read list by last_post_at - if (showDirectChannels.length < Constants.MAX_DMS) { - readDirectChannels.sort(function sortByLastPost(a, b) { + function sortByDisplayName(a, b) { + return a.display_name.localeCompare(b.display_name); + } + + visibleDirectChannels.sort(sortByDisplayName); + hiddenDirectChannels.sort(sortByDisplayName); + + /*// If we don't have MAX_DMS unread channels, sort the read list by last_post_at + if (visibleDirectChannels.length < Constants.MAX_DMS) { + hiddenDirectChannels.sort(function sortByLastPost(a, b) { // sort by last_post_at first if (a.last_post_at > b.last_post_at) { return -1; @@ -118,13 +147,13 @@ export default class Sidebar extends React.Component { }); var index = 0; - while (showDirectChannels.length < Constants.MAX_DMS && index < readDirectChannels.length) { - showDirectChannels.push(readDirectChannels[index]); + while (visibleDirectChannels.length < Constants.MAX_DMS && index < hiddenDirectChannels.length) { + visibleDirectChannels.push(hiddenDirectChannels[index]); index++; } - readDirectChannels = readDirectChannels.slice(index); + hiddenDirectChannels = hiddenDirectChannels.slice(index); - showDirectChannels.sort(function directSort(a, b) { + visibleDirectChannels.sort(function directSort(a, b) { if (a.display_name < b.display_name) { return -1; } @@ -133,22 +162,84 @@ export default class Sidebar extends React.Component { } return 0; }); - } + }*/ return { activeId: currentId, channels: ChannelStore.getAll(), members: members, - showDirectChannels: showDirectChannels, - hideDirectChannels: readDirectChannels + visibleDirectChannels: visibleDirectChannels, + hiddenDirectChannels: hiddenDirectChannels }; } + + /*getDirectChannelsFromStores() { + const id = UserStore.getCurrentId(); + + const channels = []; + const preferences = PreferenceStore.getPreferences('direct_channels', 'show_hide'); + for (const preference of preferences) { + if (preference.value !== 'true') { + continue; + } + + const otherId = preference.alt_id; + + if (otherId === id) { + continue; + } + + const teammate = UserStore.getProfile(otherId); + + if (!teammate) { + continue; + } + + let channelName = ''; + if (otherId > id) { + channelName = `${id}__${otherId}`; + } else { + channelName = `${otherId}__${id}`; + } + + const channel = ChannelStore.getByName(channelName); + + if (channel != null) { + channel.display_name = teammate.username; + channel.teammate_username = teammate.username; + + channel.status = UserStore.getStatus(otherId); + + channels.push(channel); + } else { + const tempChannel = {}; + tempChannel.fake = true; + tempChannel.name = channelName; + tempChannel.display_name = teammate.username; + tempChannel.teammate_username = teammate.username; + tempChannel.status = UserStore.getStatus(teammate.id); + tempChannel.last_post_at = 0; + tempChannel.total_msg_count = 0; + tempChannel.type = 'D'; + channels.push(tempChannel); + } + } + + channels.sort((a, b) => a.display_name.localeCompare(b)); + + return channels; + }*/ + componentDidMount() { ChannelStore.addChangeListener(this.onChange); UserStore.addChangeListener(this.onChange); UserStore.addStatusesChangeListener(this.onChange); TeamStore.addChangeListener(this.onChange); SocketStore.addChangeListener(this.onSocketChange); + PreferenceStore.addChangeListener(this.onChange); + + AsyncClient.getDirectChannels(); + $('.nav-pills__container').perfectScrollbar(); this.updateTitle(); @@ -178,6 +269,7 @@ export default class Sidebar extends React.Component { UserStore.removeStatusesChangeListener(this.onChange); TeamStore.removeChangeListener(this.onChange); SocketStore.removeChangeListener(this.onSocketChange); + PreferenceStore.removeChangeListener(this.onChange); } onChange() { var newState = this.getStateFromStores(); @@ -464,7 +556,7 @@ export default class Sidebar extends React.Component { const privateChannels = this.state.channels.filter((channel) => channel.type === 'P'); const privateChannelItems = privateChannels.map(this.createChannelElement); - const directMessageItems = this.state.showDirectChannels.map(this.createChannelElement); + const directMessageItems = this.state.visibleDirectChannels.map(this.createChannelElement); // update the favicon to show if there are any notifications var link = document.createElement('link'); @@ -484,7 +576,7 @@ export default class Sidebar extends React.Component { head.appendChild(link); var directMessageMore = null; - if (this.state.hideDirectChannels.length > 0) { + if (this.state.hiddenDirectChannels.length > 0) { directMessageMore = (
  • - {'More (' + this.state.hideDirectChannels.length + ')'} + {'More (' + this.state.hiddenDirectChannels.length + ')'}
  • ); diff --git a/web/react/stores/preference_store.jsx b/web/react/stores/preference_store.jsx new file mode 100644 index 000000000..c13c61e1d --- /dev/null +++ b/web/react/stores/preference_store.jsx @@ -0,0 +1,135 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +const ActionTypes = require('../utils/constants.jsx').ActionTypes; +const AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); +const BrowserStore = require('./browser_store.jsx'); +const EventEmitter = require('events').EventEmitter; +const UserStore = require('../stores/user_store.jsx'); + +const CHANGE_EVENT = 'change'; + +class PreferenceStoreClass extends EventEmitter { + constructor() { + super(); + + this.getAllPreferences = this.getAllPreferences.bind(this); + this.getPreference = this.getPreference.bind(this); + this.getPreferenceWithAltId = this.getPreferenceWithAltId.bind(this); + this.getPreferences = this.getPreferences.bind(this); + this.getPreferencesWhere = this.getPreferencesWhere.bind(this); + this.setAllPreferences = this.setAllPreferences.bind(this); + this.setPreference = this.setPreference.bind(this); + this.setPreferenceWithAltId = this.setPreferenceWithAltId.bind(this); + + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + + this.handleEventPayload = this.handleEventPayload.bind(this); + this.dispatchToken = AppDispatcher.register(this.handleEventPayload); + } + + getKey(category, name, altId = '') { + return `${category}-${name}-${altId}`; + } + + getKeyForModel(preference) { + return `${preference.category}-${preference.name}-${preference.alt_id}`; + } + + getAllPreferences() { + console.log('getting preferences'); // eslint-disable-line no-console + return new Map(BrowserStore.getItem('preferences', [])); + } + + getPreference(category, name, defaultValue = '') { + return this.getAllPreferences().get(this.getKey(category, name)) || defaultValue; + } + + getPreferenceWithAltId(category, name, altId, defaultValue = '') { + return this.getAllPreferences().get(this.getKey(category, name, altId)) || defaultValue; + } + + getPreferences(category, name) { + return this.getPreferencesWhere((preference) => (preference.category === category && preference.name === name)); + } + + getPreferencesWhere(pred) { + const all = this.getAllPreferences(); + const preferences = []; + + for (const [, preference] of all) { + if (pred(preference)) { + preferences.push(preference); + } + } + + return preferences; + } + + setAllPreferences(preferences) { + // note that we store the preferences as an array of key-value pairs so that we can deserialize + // it as a proper Map instead of an object + BrowserStore.setItem('preferences', [...preferences]); + } + + setPreference(category, name, value) { + this.setPreferenceWithAltId(category, name, '', value); + } + + setPreferenceWithAltId(category, name, altId, value) { + const preferences = this.getAllPreferences(); + + const key = this.getKey(category, name); + let preference = preferences.get(key); + + if (!preference) { + preference = { + user_id: UserStore.getCurrentId(), + category, + name, + alt_id: altId + }; + } + preference.value = value; + + preferences.set(key, preference); + + this.setAllPreferences(preferences); + } + + emitChange(preferences) { + this.emit(CHANGE_EVENT, preferences); + } + + addChangeListener(callback) { + this.on(CHANGE_EVENT, callback); + } + + removeChangeListener(callback) { + this.removeListener(CHANGE_EVENT, callback); + } + + handleEventPayload(payload) { + const action = payload.action; + + switch (action.type) { + case ActionTypes.RECIEVED_PREFERENCES: + const preferences = this.getAllPreferences(); + + for (const preference of action.preferences) { + preferences.set(this.getKeyForModel(preference), preference); + } + + this.setAllPreferences(preferences); + this.emitChange(preferences); + } + } +} + +const PreferenceStore = new PreferenceStoreClass(); +export default PreferenceStore; + +// TODO remove me +global.PreferenceStore = PreferenceStore; diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx index a903f055b..3f084578a 100644 --- a/web/react/utils/async_client.jsx +++ b/web/react/utils/async_client.jsx @@ -637,3 +637,32 @@ export function getMyTeam() { } ); } + +export function getDirectChannels() { + if (isCallInProgress('getDirectChannels')) { + return; + } + + callTracker.getDirectChannels = utils.getTimestamp(); + client.getPreferencesByName( + 'direct_channels', + 'show_hide', + (data, textStatus, xhr) => { + callTracker.getDirectChannels = 0; + + if (xhr.status === 304 || !data) { + return; + } + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_PREFERENCES, + preferences: data + }); + }, + (err) => { + callTracker.getDirectChannels = 0; + dispatchError(err, 'getDirectChannels'); + } + ); +} + diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 6dccfcdeb..f1827f296 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -1141,3 +1141,17 @@ export function listIncomingHooks(success, error) { } }); } + +export function getPreferencesByName(category, name, success, error) { + $.ajax({ + url: `/api/v1/preferences/${category}/${name}`, + dataType: 'json', + type: 'GET', + success, + error: (xhr, status, err) => { + var e = handleError('getPreferencesByName', xhr, status, err); + error(e); + } + }); +} + diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index e3cbfccde..56c47a244 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -28,6 +28,7 @@ module.exports = { RECIEVED_AUDITS: null, RECIEVED_TEAMS: null, RECIEVED_STATUSES: null, + RECIEVED_PREFERENCES: null, RECIEVED_MSG: null, -- cgit v1.2.3-1-g7c22 From ed31538893ad2790de46ace7eeac5c1aa015a7f1 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 2 Oct 2015 14:25:55 -0400 Subject: Changed direct messages channels so users can show/hide them --- web/react/components/more_direct_channels.jsx | 32 +++- web/react/components/sidebar.jsx | 229 +++++++++----------------- web/react/stores/preference_store.jsx | 9 +- web/react/utils/async_client.jsx | 24 +++ web/react/utils/client.jsx | 13 ++ 5 files changed, 143 insertions(+), 164 deletions(-) (limited to 'web/react') diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index 31ecb4c5d..fc720e928 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -1,10 +1,10 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var ChannelStore = require('../stores/channel_store.jsx'); var TeamStore = require('../stores/team_store.jsx'); var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); +var PreferenceStore = require('../stores/preference_store.jsx'); var utils = require('../utils/utils.jsx'); export default class MoreDirectChannels extends React.Component { @@ -22,16 +22,32 @@ export default class MoreDirectChannels extends React.Component { }); } + handleJoinDirectChannel(channel) { + const preference = PreferenceStore.setPreferenceWithAltId('direct_channels', 'show_hide', channel.teammate_id, 'true'); + AsyncClient.setPreferences([preference]); + } + render() { var self = this; - var directMessageItems = this.state.channels.map(function mapActivityToChannel(channel, index) { + var directMessageItems = this.state.channels.map((channel, index) => { var badge = ''; var titleClass = ''; - var active = ''; var handleClick = null; - if (channel.fake) { + if (!channel.fake) { + if (channel.unread) { + badge = {channel.unread}; + titleClass = 'unread-title'; + } + + handleClick = (e) => { + e.preventDefault(); + this.handleJoinDirectChannel(channel); + utils.switchChannel(channel); + $(React.findDOMNode(self.refs.modal)).modal('hide'); + }; + } else { // It's a direct message channel that doesn't exist yet so let's create it now var otherUserId = utils.getUserIdFromChannelName(channel); @@ -45,9 +61,10 @@ export default class MoreDirectChannels extends React.Component { } if (self.state.loadingDMChannel === -1) { - handleClick = function clickHandler(e) { + handleClick = (e) => { e.preventDefault(); self.setState({loadingDMChannel: index}); + this.handleJoinDirectChannel(channel); Client.createDirectChannel(channel, otherUserId, function success(data) { @@ -81,10 +98,7 @@ export default class MoreDirectChannels extends React.Component { } return ( -
  • +
  • 0) { - visibleDirectChannels.push(channel); - } else if (currentId === channel.id) { - visibleDirectChannels.push(channel); - } else { - hiddenDirectChannels.push(channel); - }*/ - } else { + if (!channel) { channel = {}; channel.fake = true; channel.name = channelName; - channel.display_name = teammate.username; - channel.teammate_username = teammate.username; - channel.status = UserStore.getStatus(teammate.id); channel.last_post_at = 0; channel.total_msg_count = 0; channel.type = 'D'; } + channel.display_name = teammate.username; + channel.teammate_id = teammate.id; + channel.status = UserStore.getStatus(teammate.id); + if (preferences.some((preference) => (preference.alt_id === teammate.id && preference.value !== 'false'))) { visibleDirectChannels.push(channel); } else { @@ -118,51 +93,8 @@ export default class Sidebar extends React.Component { } } - function sortByDisplayName(a, b) { - return a.display_name.localeCompare(b.display_name); - } - - visibleDirectChannels.sort(sortByDisplayName); - hiddenDirectChannels.sort(sortByDisplayName); - - /*// If we don't have MAX_DMS unread channels, sort the read list by last_post_at - if (visibleDirectChannels.length < Constants.MAX_DMS) { - hiddenDirectChannels.sort(function sortByLastPost(a, b) { - // sort by last_post_at first - if (a.last_post_at > b.last_post_at) { - return -1; - } - if (a.last_post_at < b.last_post_at) { - return 1; - } - - // if last_post_at is equal, sort by name - if (a.display_name < b.display_name) { - return -1; - } - if (a.display_name > b.display_name) { - return 1; - } - return 0; - }); - - var index = 0; - while (visibleDirectChannels.length < Constants.MAX_DMS && index < hiddenDirectChannels.length) { - visibleDirectChannels.push(hiddenDirectChannels[index]); - index++; - } - hiddenDirectChannels = hiddenDirectChannels.slice(index); - - visibleDirectChannels.sort(function directSort(a, b) { - if (a.display_name < b.display_name) { - return -1; - } - if (a.display_name > b.display_name) { - return 1; - } - return 0; - }); - }*/ + visibleDirectChannels.sort(this.sortChannelsByDisplayName); + hiddenDirectChannels.sort(this.sortChannelsByDisplayName); return { activeId: currentId, @@ -173,63 +105,6 @@ export default class Sidebar extends React.Component { }; } - /*getDirectChannelsFromStores() { - const id = UserStore.getCurrentId(); - - const channels = []; - const preferences = PreferenceStore.getPreferences('direct_channels', 'show_hide'); - for (const preference of preferences) { - if (preference.value !== 'true') { - continue; - } - - const otherId = preference.alt_id; - - if (otherId === id) { - continue; - } - - const teammate = UserStore.getProfile(otherId); - - if (!teammate) { - continue; - } - - let channelName = ''; - if (otherId > id) { - channelName = `${id}__${otherId}`; - } else { - channelName = `${otherId}__${id}`; - } - - const channel = ChannelStore.getByName(channelName); - - if (channel != null) { - channel.display_name = teammate.username; - channel.teammate_username = teammate.username; - - channel.status = UserStore.getStatus(otherId); - - channels.push(channel); - } else { - const tempChannel = {}; - tempChannel.fake = true; - tempChannel.name = channelName; - tempChannel.display_name = teammate.username; - tempChannel.teammate_username = teammate.username; - tempChannel.status = UserStore.getStatus(teammate.id); - tempChannel.last_post_at = 0; - tempChannel.total_msg_count = 0; - tempChannel.type = 'D'; - channels.push(tempChannel); - } - } - - channels.sort((a, b) => a.display_name.localeCompare(b)); - - return channels; - }*/ - componentDidMount() { ChannelStore.addChangeListener(this.onChange); UserStore.addChangeListener(this.onChange); @@ -414,7 +289,35 @@ export default class Sidebar extends React.Component { showBottomUnread }); } - createChannelElement(channel, index) { + + handleLeaveDirectChannel(channel) { + if (!channel.leaving) { + channel.leaving = true; + + const preference = PreferenceStore.setPreferenceWithAltId('direct_channels', 'show_hide', channel.teammate_id, 'false'); + AsyncClient.setPreferences( + [preference], + () => { + channel.leaving = false; + }, + () => { + channel.leaving = false; + } + ); + + this.setState(this.getStateFromStores()); + } + + if (channel.id === this.state.activeId) { + Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL)); + } + } + + sortChannelsByDisplayName(a, b) { + return a.display_name.localeCompare(b.display_name); + } + + createChannelElement(channel, index, arr, handleClose) { var members = this.state.members; var activeId = this.state.activeId; var channelMember = members[channel.id]; @@ -497,8 +400,13 @@ export default class Sidebar extends React.Component { if (!channel.fake) { handleClick = function clickHandler(e) { + if (!e.target.attributes.getNamedItem('data-close')) { + Utils.switchChannel(channel); + } else { + handleClose(channel); + } + e.preventDefault(); - Utils.switchChannel(channel); }; } else if (channel.fake && teamURL) { // It's a direct message channel that doesn't exist yet so let's create it now @@ -507,23 +415,40 @@ export default class Sidebar extends React.Component { if (this.state.loadingDMChannel === -1) { handleClick = function clickHandler(e) { e.preventDefault(); - this.setState({loadingDMChannel: index}); - - Client.createDirectChannel(channel, otherUserId, - function success(data) { - this.setState({loadingDMChannel: -1}); - AsyncClient.getChannel(data.id); - Utils.switchChannel(data); - }.bind(this), - function error() { - this.setState({loadingDMChannel: -1}); - window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; - }.bind(this) - ); + + if (!e.target.attributes.getNamedItem('data-close')) { + this.setState({loadingDMChannel: index}); + + Client.createDirectChannel(channel, otherUserId, + function success(data) { + this.setState({loadingDMChannel: -1}); + AsyncClient.getChannel(data.id); + Utils.switchChannel(data); + }.bind(this), + function error() { + this.setState({loadingDMChannel: -1}); + window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; + }.bind(this) + ); + } else { + handleClose(channel); + } }.bind(this); } } + let closeButton = null; + if (handleClose) { + closeButton = ( + + {'×'} + + ); + } + return (
  • ); @@ -556,7 +482,9 @@ export default class Sidebar extends React.Component { const privateChannels = this.state.channels.filter((channel) => channel.type === 'P'); const privateChannelItems = privateChannels.map(this.createChannelElement); - const directMessageItems = this.state.visibleDirectChannels.map(this.createChannelElement); + const directMessageItems = this.state.visibleDirectChannels.map((channel, index, arr) => { + return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel); + }); // update the favicon to show if there are any notifications var link = document.createElement('link'); @@ -578,8 +506,9 @@ export default class Sidebar extends React.Component { var directMessageMore = null; if (this.state.hiddenDirectChannels.length > 0) { directMessageMore = ( -
  • +
  • { + if (xhr.status !== 304) { + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_PREFERENCES, + preferences + }); + } + + if (success) { + success(data); + } + }, + (err) => { + dispatchError(err, 'setPreferences'); + + if (error) { + error(); + } + } + ); +} diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index f1827f296..2134dc0f5 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -1155,3 +1155,16 @@ export function getPreferencesByName(category, name, success, error) { }); } +export function setPreferences(preferences, success, error) { + $.ajax({ + url: '/api/v1/preferences/set', + dataType: 'json', + type: 'POST', + data: JSON.stringify(preferences), + success, + error: (xhr, status, err) => { + var e = handleError('setPreferences', xhr, status, err); + error(e); + } + }); +} -- cgit v1.2.3-1-g7c22 From 415f959614f6a3fd7fcb6551e32b722df2b015c5 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 5 Oct 2015 11:47:34 -0400 Subject: Made direct channels visible when receiving a message on one --- web/react/components/sidebar.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'web/react') diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 449a3eaea..de353deb4 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -71,6 +71,7 @@ export default class Sidebar extends React.Component { channelName = teammate.id + '__' + UserStore.getCurrentId(); } + let forceShow = false; let channel = ChannelStore.getByName(channelName); if (!channel) { @@ -80,6 +81,11 @@ export default class Sidebar extends React.Component { channel.last_post_at = 0; channel.total_msg_count = 0; channel.type = 'D'; + } else { + const member = members[channel.id]; + const msgCount = channel.total_msg_count - member.msg_count; + + forceShow = currentId === channel.id || msgCount > 0; } channel.display_name = teammate.username; @@ -87,6 +93,12 @@ export default class Sidebar extends React.Component { channel.status = UserStore.getStatus(teammate.id); if (preferences.some((preference) => (preference.alt_id === teammate.id && preference.value !== 'false'))) { + visibleDirectChannels.push(channel); + } else if (forceShow) { + // make sure that unread direct channels are visible + const preference = PreferenceStore.setPreferenceWithAltId('direct_channels', 'show_hide', teammate.id, 'true'); + AsyncClient.setPreferences([preference]); + visibleDirectChannels.push(channel); } else { hiddenDirectChannels.push(channel); -- cgit v1.2.3-1-g7c22 From 599644fb2fa75d1760420806c8c821959fc6b645 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 5 Oct 2015 12:03:27 -0400 Subject: Renamed show_hide preference to show --- web/react/components/more_direct_channels.jsx | 4 +++- web/react/components/sidebar.jsx | 8 +++++--- web/react/utils/async_client.jsx | 4 ++-- web/react/utils/constants.jsx | 6 +++++- 4 files changed, 15 insertions(+), 7 deletions(-) (limited to 'web/react') diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index fc720e928..ed9c6fc58 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -3,6 +3,7 @@ var TeamStore = require('../stores/team_store.jsx'); var Client = require('../utils/client.jsx'); +var Constants = require('../utils/constants.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var PreferenceStore = require('../stores/preference_store.jsx'); var utils = require('../utils/utils.jsx'); @@ -23,7 +24,8 @@ export default class MoreDirectChannels extends React.Component { } handleJoinDirectChannel(channel) { - const preference = PreferenceStore.setPreferenceWithAltId('direct_channels', 'show_hide', channel.teammate_id, 'true'); + const preference = PreferenceStore.setPreferenceWithAltId(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, + Constants.Preferences.NAME_SHOW, channel.teammate_id, 'true'); AsyncClient.setPreferences([preference]); } diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index de353deb4..97f705f32 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -52,7 +52,7 @@ export default class Sidebar extends React.Component { teammates.push(teamMemberMap[id]); } - const preferences = PreferenceStore.getPreferences('direct_channels', 'show_hide'); + const preferences = PreferenceStore.getPreferences(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, Constants.Preferences.NAME_SHOW); // Create lists of all read and unread direct channels var visibleDirectChannels = []; @@ -96,7 +96,8 @@ export default class Sidebar extends React.Component { visibleDirectChannels.push(channel); } else if (forceShow) { // make sure that unread direct channels are visible - const preference = PreferenceStore.setPreferenceWithAltId('direct_channels', 'show_hide', teammate.id, 'true'); + const preference = PreferenceStore.setPreferenceWithAltId(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, + Constants.Preferences.NAME_SHOW, teammate.id, 'true'); AsyncClient.setPreferences([preference]); visibleDirectChannels.push(channel); @@ -306,7 +307,8 @@ export default class Sidebar extends React.Component { if (!channel.leaving) { channel.leaving = true; - const preference = PreferenceStore.setPreferenceWithAltId('direct_channels', 'show_hide', channel.teammate_id, 'false'); + const preference = PreferenceStore.setPreferenceWithAltId(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, + Constants.Preferences.NAME_SHOW, channel.teammate_id, 'false'); AsyncClient.setPreferences( [preference], () => { diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx index d665dfc94..a0ccccd88 100644 --- a/web/react/utils/async_client.jsx +++ b/web/react/utils/async_client.jsx @@ -645,8 +645,8 @@ export function getDirectChannels() { callTracker.getDirectChannels = utils.getTimestamp(); client.getPreferencesByName( - 'direct_channels', - 'show_hide', + Constants.Preferences.CATEGORY_DIRECT_CHANNELS, + Constants.Preferences.NAME_SHOW, (data, textStatus, xhr) => { callTracker.getDirectChannels = 0; diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 56c47a244..a576b9098 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -286,5 +286,9 @@ module.exports = { id: 'mentionHighlightLink', uiName: 'Mention Highlight Link' } - ] + ], + Preferences: { + CATEGORY_DIRECT_CHANNELS: 'direct_channels', + NAME_SHOW: 'show' + } }; -- cgit v1.2.3-1-g7c22 From ae0eb91180130e27767d797bc47376b88f62f88b Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 5 Oct 2015 13:16:39 -0400 Subject: Hid the close button when the unread badge is visible and moved it to the right side of the sidebar --- web/react/components/sidebar.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'web/react') diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 97f705f32..2619798eb 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -452,10 +452,10 @@ export default class Sidebar extends React.Component { } let closeButton = null; - if (handleClose) { + if (handleClose && !badge) { closeButton = ( {'×'} -- cgit v1.2.3-1-g7c22 From 097d236f437b0c5af167cd383c6ee4c3ee45f495 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Wed, 7 Oct 2015 11:34:29 -0400 Subject: Fixed edge cases with leaving a direct channel while viewing that channel --- web/react/components/more_direct_channels.jsx | 15 --------------- web/react/components/sidebar.jsx | 15 ++++++++++----- web/react/stores/preference_store.jsx | 1 - web/react/utils/client.jsx | 1 + 4 files changed, 11 insertions(+), 21 deletions(-) (limited to 'web/react') diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index ed9c6fc58..96c08c441 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -82,21 +82,6 @@ export default class MoreDirectChannels extends React.Component { ); }; } - } else { - if (channel.id === ChannelStore.getCurrentId()) { - active = 'active'; - } - - if (channel.unread) { - badge = {channel.unread}; - titleClass = 'unread-title'; - } - - handleClick = function clickHandler(e) { - e.preventDefault(); - utils.switchChannel(channel); - $(React.findDOMNode(self.refs.modal)).modal('hide'); - }; } return ( diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 2619798eb..54edb3c31 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -33,6 +33,8 @@ export default class Sidebar extends React.Component { this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this); this.createChannelElement = this.createChannelElement.bind(this); + this.isLeaving = new Map(); + const state = this.getStateFromStores(); state.modal = ''; state.loadingDMChannel = -1; @@ -85,7 +87,8 @@ export default class Sidebar extends React.Component { const member = members[channel.id]; const msgCount = channel.total_msg_count - member.msg_count; - forceShow = currentId === channel.id || msgCount > 0; + // always show a channel if either it is the current one or if it is unread, but it is not currently being left + forceShow = (currentId === channel.id || msgCount > 0) && !this.isLeaving.get(channel.id); } channel.display_name = teammate.username; @@ -93,8 +96,10 @@ export default class Sidebar extends React.Component { channel.status = UserStore.getStatus(teammate.id); if (preferences.some((preference) => (preference.alt_id === teammate.id && preference.value !== 'false'))) { + console.log(teammate.id + " is visible"); visibleDirectChannels.push(channel); } else if (forceShow) { + console.log(teammate.id + " needs to be visible"); // make sure that unread direct channels are visible const preference = PreferenceStore.setPreferenceWithAltId(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, Constants.Preferences.NAME_SHOW, teammate.id, 'true'); @@ -304,18 +309,18 @@ export default class Sidebar extends React.Component { } handleLeaveDirectChannel(channel) { - if (!channel.leaving) { - channel.leaving = true; + if (!this.isLeaving.get(channel.id)) { + this.isLeaving.set(channel.id, true); const preference = PreferenceStore.setPreferenceWithAltId(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, Constants.Preferences.NAME_SHOW, channel.teammate_id, 'false'); AsyncClient.setPreferences( [preference], () => { - channel.leaving = false; + this.isLeaving.set(channel.id, false); }, () => { - channel.leaving = false; + this.isLeaving.set(channel.id, false); } ); diff --git a/web/react/stores/preference_store.jsx b/web/react/stores/preference_store.jsx index d731e7d27..8101452ed 100644 --- a/web/react/stores/preference_store.jsx +++ b/web/react/stores/preference_store.jsx @@ -39,7 +39,6 @@ class PreferenceStoreClass extends EventEmitter { } getAllPreferences() { - console.log('getting preferences'); // eslint-disable-line no-console return new Map(BrowserStore.getItem('preferences', [])); } diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 2134dc0f5..4e3505ad2 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -1159,6 +1159,7 @@ export function setPreferences(preferences, success, error) { $.ajax({ url: '/api/v1/preferences/set', dataType: 'json', + contentType: 'application/json', type: 'POST', data: JSON.stringify(preferences), success, -- cgit v1.2.3-1-g7c22 From a2517ee991df84803ca508b556910e543c535c1d Mon Sep 17 00:00:00 2001 From: hmhealey Date: Wed, 7 Oct 2015 13:07:59 -0400 Subject: Cleaned up JSX errors --- web/react/components/more_direct_channels.jsx | 57 +++++++++++++-------------- web/react/components/sidebar.jsx | 50 +++++++++++------------ 2 files changed, 51 insertions(+), 56 deletions(-) (limited to 'web/react') diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index 96c08c441..0fbd90096 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -16,10 +16,9 @@ export default class MoreDirectChannels extends React.Component { } componentDidMount() { - var self = this; - $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function showModal(e) { + $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => { var button = e.relatedTarget; - self.setState({channels: $(button).data('channels')}); + this.setState({channels: $(button).data('channels')}); // eslint-disable-line react/no-did-mount-set-state }); } @@ -30,30 +29,16 @@ export default class MoreDirectChannels extends React.Component { } render() { - var self = this; - var directMessageItems = this.state.channels.map((channel, index) => { var badge = ''; var titleClass = ''; var handleClick = null; - if (!channel.fake) { - if (channel.unread) { - badge = {channel.unread}; - titleClass = 'unread-title'; - } - - handleClick = (e) => { - e.preventDefault(); - this.handleJoinDirectChannel(channel); - utils.switchChannel(channel); - $(React.findDOMNode(self.refs.modal)).modal('hide'); - }; - } else { + if (channel.fake) { // It's a direct message channel that doesn't exist yet so let's create it now var otherUserId = utils.getUserIdFromChannelName(channel); - if (self.state.loadingDMChannel === index) { + if (this.state.loadingDMChannel === index) { badge = ( { e.preventDefault(); - self.setState({loadingDMChannel: index}); + this.setState({loadingDMChannel: index}); this.handleJoinDirectChannel(channel); Client.createDirectChannel(channel, otherUserId, - function success(data) { - $(React.findDOMNode(self.refs.modal)).modal('hide'); - self.setState({loadingDMChannel: -1}); + (data) => { + $(React.findDOMNode(this.refs.modal)).modal('hide'); + this.setState({loadingDMChannel: -1}); AsyncClient.getChannel(data.id); utils.switchChannel(data); }, - function error() { - self.setState({loadingDMChannel: -1}); + () => { + this.setState({loadingDMChannel: -1}); window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; } ); }; } + } else { + if (channel.unread) { + badge = {channel.unread}; + titleClass = 'unread-title'; + } + + handleClick = (e) => { + e.preventDefault(); + this.handleJoinDirectChannel(channel); + utils.switchChannel(channel); + $(React.findDOMNode(this.refs.modal)).modal('hide'); + }; } return ( @@ -112,10 +109,10 @@ export default class MoreDirectChannels extends React.Component { className='close' data-dismiss='modal' > - - Close + + {'Close'} -

    More Direct Messages

    +

    {'More Direct Messages'}

      @@ -127,7 +124,7 @@ export default class MoreDirectChannels extends React.Component { type='button' className='btn btn-default' data-dismiss='modal' - >Close + >{'Close'}
    diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 54edb3c31..431f2ce1b 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -76,19 +76,19 @@ export default class Sidebar extends React.Component { let forceShow = false; let channel = ChannelStore.getByName(channelName); - if (!channel) { + if (channel) { + const member = members[channel.id]; + const msgCount = channel.total_msg_count - member.msg_count; + + // always show a channel if either it is the current one or if it is unread, but it is not currently being left + forceShow = (currentId === channel.id || msgCount > 0) && !this.isLeaving.get(channel.id); + } else { channel = {}; channel.fake = true; channel.name = channelName; channel.last_post_at = 0; channel.total_msg_count = 0; channel.type = 'D'; - } else { - const member = members[channel.id]; - const msgCount = channel.total_msg_count - member.msg_count; - - // always show a channel if either it is the current one or if it is unread, but it is not currently being left - forceShow = (currentId === channel.id || msgCount > 0) && !this.isLeaving.get(channel.id); } channel.display_name = teammate.username; @@ -96,10 +96,8 @@ export default class Sidebar extends React.Component { channel.status = UserStore.getStatus(teammate.id); if (preferences.some((preference) => (preference.alt_id === teammate.id && preference.value !== 'false'))) { - console.log(teammate.id + " is visible"); visibleDirectChannels.push(channel); } else if (forceShow) { - console.log(teammate.id + " needs to be visible"); // make sure that unread direct channels are visible const preference = PreferenceStore.setPreferenceWithAltId(Constants.Preferences.CATEGORY_DIRECT_CHANNELS, Constants.Preferences.NAME_SHOW, teammate.id, 'true'); @@ -117,9 +115,9 @@ export default class Sidebar extends React.Component { return { activeId: currentId, channels: ChannelStore.getAll(), - members: members, - visibleDirectChannels: visibleDirectChannels, - hiddenDirectChannels: hiddenDirectChannels + members, + visibleDirectChannels, + hiddenDirectChannels }; } @@ -419,10 +417,10 @@ export default class Sidebar extends React.Component { if (!channel.fake) { handleClick = function clickHandler(e) { - if (!e.target.attributes.getNamedItem('data-close')) { - Utils.switchChannel(channel); - } else { + if (e.target.attributes.getNamedItem('data-close')) { handleClose(channel); + } else { + Utils.switchChannel(channel); } e.preventDefault(); @@ -435,22 +433,22 @@ export default class Sidebar extends React.Component { handleClick = function clickHandler(e) { e.preventDefault(); - if (!e.target.attributes.getNamedItem('data-close')) { + if (e.target.attributes.getNamedItem('data-close')) { + handleClose(channel); + } else { this.setState({loadingDMChannel: index}); Client.createDirectChannel(channel, otherUserId, - function success(data) { + (data) => { this.setState({loadingDMChannel: -1}); AsyncClient.getChannel(data.id); Utils.switchChannel(data); - }.bind(this), - function error() { + }, + () => { this.setState({loadingDMChannel: -1}); window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; - }.bind(this) + } ); - } else { - handleClose(channel); } }.bind(this); } @@ -578,7 +576,7 @@ export default class Sidebar extends React.Component {
    @@ -605,7 +603,7 @@ export default class Sidebar extends React.Component {