diff options
28 files changed, 138 insertions, 47 deletions
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index 27959ec7e..ab5686720 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -54,7 +54,7 @@ export default class AccessHistoryModal extends React.Component { } onAuditChange() { var newState = this.getStateFromStoresForAudits(); - if (!Utils.areStatesEqual(newState.audits, this.state.audits)) { + if (!Utils.areObjectsEqual(newState.audits, this.state.audits)) { this.setState(newState); } } diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx index ef3077470..af423a601 100644 --- a/web/react/components/activity_log_modal.jsx +++ b/web/react/components/activity_log_modal.jsx @@ -73,7 +73,7 @@ export default class ActivityLogModal extends React.Component { } onListenerChange() { const newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(newState.sessions, this.state.sessions)) { + if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) { this.setState(newState); } } diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index dcc3bf87d..a8d4ec100 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -66,7 +66,7 @@ export default class ChannelHeader extends React.Component { } onListenerChange() { const newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}}); diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx index 7c1032321..47bc50971 100644 --- a/web/react/components/channel_invite_modal.jsx +++ b/web/react/components/channel_invite_modal.jsx @@ -78,7 +78,7 @@ export default class ChannelInviteModal extends React.Component { } onListenerChange() { var newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(this.state, newState)) { + if (!Utils.areObjectsEqual(this.state, newState)) { this.setState(newState); } } diff --git a/web/react/components/channel_members_modal.jsx b/web/react/components/channel_members_modal.jsx index 2fa7ae8ff..5cf3511f4 100644 --- a/web/react/components/channel_members_modal.jsx +++ b/web/react/components/channel_members_modal.jsx @@ -91,7 +91,7 @@ export default class ChannelMembersModal extends React.Component { } onChange() { const newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(this.state, newState)) { + if (!Utils.areObjectsEqual(this.state, newState)) { this.setState(newState); } } diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx index 43700bf36..f57fc12c5 100644 --- a/web/react/components/channel_notifications.jsx +++ b/web/react/components/channel_notifications.jsx @@ -69,7 +69,7 @@ export default class ChannelNotifications extends React.Component { newState.notifyLevel = notifyLevel; newState.markUnreadLevel = markUnreadLevel; - if (!Utils.areStatesEqual(this.state, newState)) { + if (!Utils.areObjectsEqual(this.state, newState)) { this.setState(newState); } } diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx index 3a3dabce5..f3bead1c2 100644 --- a/web/react/components/delete_post_modal.jsx +++ b/web/react/components/delete_post_modal.jsx @@ -81,7 +81,7 @@ export default class DeletePostModal extends React.Component { } onListenerChange() { var newList = PostStore.getSelectedPost(); - if (!Utils.areStatesEqual(this.state.selectedList, newList)) { + if (!Utils.areObjectsEqual(this.state.selectedList, newList)) { this.setState({selectedList: newList}); } } diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index e707e32f5..d6a30abf9 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -67,7 +67,7 @@ export default class FileAttachment extends React.Component { this.canSetState = false; } shouldComponentUpdate(nextProps, nextState) { - if (!utils.areStatesEqual(nextProps, this.props)) { + if (!utils.areObjectsEqual(nextProps, this.props)) { return true; } diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx index c4f831c2e..8a6dd84a4 100644 --- a/web/react/components/more_channels.jsx +++ b/web/react/components/more_channels.jsx @@ -46,7 +46,7 @@ export default class MoreChannels extends React.Component { } onListenerChange() { var newState = getStateFromStores(); - if (!utils.areStatesEqual(newState.channels, this.state.channels)) { + if (!utils.areObjectsEqual(newState.channels, this.state.channels)) { this.setState(newState); } } diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 0b755f377..cf9db055d 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -70,7 +70,7 @@ export default class NavbarDropdown extends React.Component { } onListenerChange() { var newState = getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/notify_counts.jsx b/web/react/components/notify_counts.jsx index 54b9e4289..0a4f60989 100644 --- a/web/react/components/notify_counts.jsx +++ b/web/react/components/notify_counts.jsx @@ -39,7 +39,7 @@ export default class NotifyCounts extends React.Component { } onListenerChange() { var newState = getCountsStateFromStores(); - if (!utils.areStatesEqual(newState, this.state)) { + if (!utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index c3c5b3e0b..2b9586345 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -77,7 +77,7 @@ export default class Post extends React.Component { this.forceUpdate(); } shouldComponentUpdate(nextProps) { - if (!utils.areStatesEqual(nextProps.post, this.props.post)) { + if (!utils.areObjectsEqual(nextProps.post, this.props.post)) { return true; } diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index 52722aefc..087ca1df2 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -242,7 +242,7 @@ export default class PostsView extends React.Component { if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) { return true; } - if (!Utils.areStatesEqual(this.props.postList, nextProps.postList)) { + if (!Utils.areObjectsEqual(this.props.postList, nextProps.postList)) { return true; } diff --git a/web/react/components/posts_view_container.jsx b/web/react/components/posts_view_container.jsx index 5037a86cd..c76c8a57e 100644 --- a/web/react/components/posts_view_container.jsx +++ b/web/react/components/posts_view_container.jsx @@ -223,7 +223,7 @@ export default class PostsViewContainer extends React.Component { } } shouldComponentUpdate(nextProps, nextState) { - if (Utils.areStatesEqual(this.state, nextState)) { + if (Utils.areObjectsEqual(this.state, nextState)) { return false; } diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx index 8c6324c72..58cc1cac7 100644 --- a/web/react/components/rhs_comment.jsx +++ b/web/react/components/rhs_comment.jsx @@ -61,7 +61,7 @@ export default class RhsComment extends React.Component { this.parseEmojis(); } shouldComponentUpdate(nextProps) { - if (!Utils.areStatesEqual(nextProps.post, this.props.post)) { + if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) { return true; } diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx index e3b023841..69de5d523 100644 --- a/web/react/components/rhs_root_post.jsx +++ b/web/react/components/rhs_root_post.jsx @@ -26,7 +26,7 @@ export default class RhsRootPost extends React.Component { this.parseEmojis(); } shouldComponentUpdate(nextProps) { - if (!utils.areStatesEqual(nextProps.post, this.props.post)) { + if (!utils.areObjectsEqual(nextProps.post, this.props.post)) { return true; } diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index fe57bed28..7c11de7cf 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -82,7 +82,7 @@ export default class RhsThread extends React.Component { } onChange() { var newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } @@ -112,7 +112,7 @@ export default class RhsThread extends React.Component { } var newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index 90865475b..0f749f2cf 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -46,7 +46,7 @@ export default class SearchBar extends React.Component { onListenerChange(doSearch, isMentionSearch) { if (this.mounted) { var newState = this.getSearchTermStateFromStores(); - if (!utils.areStatesEqual(newState, this.state)) { + if (!utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } if (doSearch) { diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index b56a7b006..2f0068908 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -55,7 +55,7 @@ export default class SearchResults extends React.Component { onChange() { if (this.mounted) { var newState = getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index e3ee9f14b..b02ec0692 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -175,7 +175,7 @@ export default class Sidebar extends React.Component { window.addEventListener('resize', this.handleResize); } shouldComponentUpdate(nextProps, nextState) { - if (!Utils.areStatesEqual(nextState, this.state)) { + if (!Utils.areObjectsEqual(nextState, this.state)) { return true; } return false; @@ -207,10 +207,7 @@ export default class Sidebar extends React.Component { } } onChange() { - var newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { - this.setState(newState); - } + this.setState(this.getStateFromStores()); } updateTitle() { const channel = ChannelStore.getCurrent(); diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx index e2ef60959..ab558ad0f 100644 --- a/web/react/components/sidebar_right.jsx +++ b/web/react/components/sidebar_right.jsx @@ -66,13 +66,13 @@ export default class SidebarRight extends React.Component { onSelectedChange(fromSearch) { var newState = getStateFromStores(fromSearch); newState.from_search = fromSearch; - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } onSearchChange() { var newState = getStateFromStores(); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/team_members.jsx b/web/react/components/team_members.jsx index ac1ebf52d..afe7f46ec 100644 --- a/web/react/components/team_members.jsx +++ b/web/react/components/team_members.jsx @@ -59,7 +59,7 @@ export default class TeamMembers extends React.Component { onChange() { var newState = getStateFromStores(); - if (!utils.areStatesEqual(newState, this.state)) { + if (!utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx index 09674f1ef..862f3c528 100644 --- a/web/react/components/team_settings.jsx +++ b/web/react/components/team_settings.jsx @@ -23,7 +23,7 @@ export default class TeamSettings extends React.Component { } onChange() { var team = TeamStore.getCurrent(); - if (!Utils.areStatesEqual(this.state.team, team)) { + if (!Utils.areObjectsEqual(this.state.team, team)) { this.setState({team}); } } diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index eb0a8f0ca..8c6b1d89c 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -43,7 +43,7 @@ export default class UserProfile extends React.Component { onChange(userId) { if (!userId || userId === this.props.userId) { var newState = this.getStateFromStores(this.props.userId); - if (!Utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx index e089ce973..40825ba93 100644 --- a/web/react/components/user_settings/user_settings.jsx +++ b/web/react/components/user_settings/user_settings.jsx @@ -36,7 +36,7 @@ export default class UserSettings extends React.Component { onListenerChange() { var user = UserStore.getCurrentUser(); - if (!utils.areStatesEqual(this.state.user, user)) { + if (!utils.areObjectsEqual(this.state.user, user)) { this.setState({user}); } } diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx index d73b5f476..029a1af5e 100644 --- a/web/react/components/user_settings/user_settings_appearance.jsx +++ b/web/react/components/user_settings/user_settings_appearance.jsx @@ -1,13 +1,15 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var UserStore = require('../../stores/user_store.jsx'); -var Client = require('../../utils/client.jsx'); -var Utils = require('../../utils/utils.jsx'); - const CustomThemeChooser = require('./custom_theme_chooser.jsx'); const PremadeThemeChooser = require('./premade_theme_chooser.jsx'); + +const UserStore = require('../../stores/user_store.jsx'); + const AppDispatcher = require('../../dispatcher/app_dispatcher.jsx'); +const Client = require('../../utils/client.jsx'); +const Utils = require('../../utils/utils.jsx'); + const Constants = require('../../utils/constants.jsx'); const ActionTypes = Constants.ActionTypes; @@ -66,7 +68,7 @@ export default class UserSettingsAppearance extends React.Component { onChange() { const newState = this.getStateFromStores(); - if (!Utils.areStatesEqual(this.state, newState)) { + if (!Utils.areObjectsEqual(this.state, newState)) { this.setState(newState); } diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx index c6f47804f..c958bf5bc 100644 --- a/web/react/components/user_settings/user_settings_notifications.jsx +++ b/web/react/components/user_settings/user_settings_notifications.jsx @@ -1,16 +1,18 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var UserStore = require('../../stores/user_store.jsx'); -var SettingItemMin = require('../setting_item_min.jsx'); -var SettingItemMax = require('../setting_item_max.jsx'); -var client = require('../../utils/client.jsx'); -var AsyncClient = require('../../utils/async_client.jsx'); -var utils = require('../../utils/utils.jsx'); +const SettingItemMin = require('../setting_item_min.jsx'); +const SettingItemMax = require('../setting_item_max.jsx'); + +const UserStore = require('../../stores/user_store.jsx'); + +const Client = require('../../utils/client.jsx'); +const AsyncClient = require('../../utils/async_client.jsx'); +const Utils = require('../../utils/utils.jsx'); function getNotificationsStateFromStores() { var user = UserStore.getCurrentUser(); - var soundNeeded = !utils.isBrowserFirefox(); + var soundNeeded = !Utils.isBrowserFirefox(); var sound = 'true'; if (user.notify_props && user.notify_props.desktop_sound) { @@ -116,7 +118,7 @@ export default class NotificationsTab extends React.Component { data.all = this.state.allKey.toString(); data.channel = this.state.channelKey.toString(); - client.updateUserNotifyProps(data, + Client.updateUserNotifyProps(data, function success() { this.props.updateSection(''); AsyncClient.getMe(); @@ -138,7 +140,7 @@ export default class NotificationsTab extends React.Component { } onListenerChange() { var newState = getNotificationsStateFromStores(); - if (!utils.areStatesEqual(newState, this.state)) { + if (!Utils.areObjectsEqual(newState, this.state)) { this.setState(newState); } } diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 38f91b35f..6f3924829 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -311,8 +311,98 @@ export function escapeRegExp(string) { return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); } -export function areStatesEqual(state1, state2) { - return JSON.stringify(state1) === JSON.stringify(state2); +// Taken from http://stackoverflow.com/questions/1068834/object-comparison-in-javascript and modified slightly +export function areObjectsEqual(x, y) { + let p; + const leftChain = []; + const rightChain = []; + + // Remember that NaN === NaN returns false + // and isNaN(undefined) returns true + if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') { + return true; + } + + // Compare primitives and functions. + // Check if both arguments link to the same object. + // Especially useful on step when comparing prototypes + if (x === y) { + return true; + } + + // Works in case when functions are created in constructor. + // Comparing dates is a common scenario. Another built-ins? + // We can even handle functions passed across iframes + if ((typeof x === 'function' && typeof y === 'function') || + (x instanceof Date && y instanceof Date) || + (x instanceof RegExp && y instanceof RegExp) || + (x instanceof String && y instanceof String) || + (x instanceof Number && y instanceof Number)) { + return x.toString() === y.toString(); + } + + // At last checking prototypes as good a we can + if (!(x instanceof Object && y instanceof Object)) { + return false; + } + + if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) { + return false; + } + + if (x.constructor !== y.constructor) { + return false; + } + + if (x.prototype !== y.prototype) { + return false; + } + + // Check for infinitive linking loops + if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) { + return false; + } + + // Quick checking of one object beeing a subset of another. + for (p in y) { + if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { + return false; + } else if (typeof y[p] !== typeof x[p]) { + return false; + } + } + + for (p in x) { + if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { + return false; + } else if (typeof y[p] !== typeof x[p]) { + return false; + } + + switch (typeof (x[p])) { + case 'object': + case 'function': + + leftChain.push(x); + rightChain.push(y); + + if (!areObjectsEqual(x[p], y[p])) { + return false; + } + + leftChain.pop(); + rightChain.pop(); + break; + + default: + if (x[p] !== y[p]) { + return false; + } + break; + } + } + + return true; } export function replaceHtmlEntities(text) { |