From 0104bf822c70eef7793b8a31b78d5c72c0f1514e Mon Sep 17 00:00:00 2001 From: test Date: Fri, 26 Jun 2015 11:07:22 -0700 Subject: [PATCH 01/13] gui: fix mentions dropdown --- web/react/components/mention.jsx | 4 ++-- web/sass-files/sass/partials/_mentions.scss | 25 ++++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index ba758688b3..86a4231387 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -8,8 +8,8 @@ module.exports = React.createClass({ render: function() { return (
- - @{this.props.username}{this.props.name} + + @{this.props.username}{this.props.name}
); } diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss index 11cd4e9e45..cb6ff16c55 100644 --- a/web/sass-files/sass/partials/_mentions.scss +++ b/web/sass-files/sass/partials/_mentions.scss @@ -3,21 +3,19 @@ background: $primary-color; position: relative; z-index: 10; - padding-bottom: 1px; + padding-bottom: 2px; @include border-radius(3px); - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } .mentions--top { position: absolute; - z-index:99999; + z-index: 1040; .mentions-box { position:absolute; background-color:#fff; - border:1px solid #ddd; - overflow:scroll; + border: $border-gray; + overflow-x: hidden; + overflow-y: scroll; bottom:0; } } @@ -29,10 +27,10 @@ height:37px; padding:2px; z-index:101; -} - -.mentions-name:hover { - background-color:#e8eaed; + cursor: pointer; + &:hover { + background-color:#e8eaed; + } } .mentions-text { @@ -46,6 +44,11 @@ border-radius: 10%; } +.mention-fullname { + color: grey; + padding-left: 10px; +} + .mention-highlight { background-color:#fff2bb; } From f25cdbdb08c11fc9cd3e6bbf0748b67d2635ff47 Mon Sep 17 00:00:00 2001 From: ralder Date: Fri, 26 Jun 2015 15:58:34 -0700 Subject: [PATCH 02/13] gui: fix broken mailto link in user-popover --- web/react/components/user_profile.jsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index 8ffad737d8..648960471a 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -10,8 +10,7 @@ function getStateFromStores(userId) { if (profile == null) { return { profile: { id: "0", username: "..."} }; - } - else { + } else { return { profile: profile }; } } @@ -54,12 +53,11 @@ module.exports = React.createClass({ var name = this.props.overwriteName ? this.props.overwriteName : this.state.profile.username; - var data_content = "" - data_content += "" + var data_content = ""; if (!config.ShowEmail) { - data_content += "
Email not shared
"; + data_content += "
Email not shared
"; } else { - data_content += "
" + this.state.profile.email + "
"; + data_content += "
" + this.state.profile.email + "
"; } return ( From e1714eaa3f3363c0e38c947c2cc438b201a16a02 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Sun, 28 Jun 2015 04:18:24 +0500 Subject: [PATCH 03/13] Updating settings UI --- web/react/components/setting_item_max.jsx | 2 +- web/react/components/user_settings.jsx | 30 +++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx index 03f05b0cf2..b8b667e1a7 100644 --- a/web/react/components/setting_item_max.jsx +++ b/web/react/components/setting_item_max.jsx @@ -13,7 +13,7 @@ module.exports = React.createClass({
  • {this.props.title}
    • -
    • +
    • {inputs}
    • diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index 7d542a8b7e..147973fb79 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -155,7 +155,7 @@ var NotificationsTab = React.createClass({ var inputs = []; inputs.push( -
      +
    • +
    • { theme_buttons }
      From 2cd5f3629ff2410127a8fdc5df92fb16f52fa457 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sun, 28 Jun 2015 19:25:39 -0700 Subject: [PATCH 04/13] Fixed typo in team domain look-up page --- config/config.json | 6 +++--- web/react/components/login.jsx | 2 +- web/react/utils/utils.jsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/config.json b/config/config.json index e38f1701a1..90ec9540ec 100644 --- a/config/config.json +++ b/config/config.json @@ -8,9 +8,9 @@ "FileLocation": "" }, "ServiceSettings": { - "SiteName": "Mattermost", - "Domain": "xxxxxxmustbefilledin.com", - "Mode" : "dev", + "SiteName": "Battlehouse", + "Domain": "battlehouse.com", + "Mode" : "prod", "AllowTesting" : false, "UseSSL": false, "Port": "8065", diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index 85df5f797c..3b6f96c2d4 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -37,7 +37,7 @@ var FindTeamDomain = React.createClass({ window.location.href = window.location.protocol + "//" + domain + "." + utils.getDomainWithOutSub(); } else { - this.state.server_error = "We couldn't find your " + strings.TeamPlural + "."; + this.state.server_error = "We couldn't find your " + strings.Team + "."; this.setState(this.state); } }.bind(this), diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 75c583c8fc..128d9d1e70 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -25,7 +25,7 @@ module.exports.cleanUpUrlable = function(input) { module.exports.isTestDomain = function() { - if ((/^localhost/).test(window.location.hostname)) + /*if ((/^localhost/).test(window.location.hostname)) return true; if ((/^dockerhost/).test(window.location.hostname)) @@ -44,7 +44,7 @@ module.exports.isTestDomain = function() { return true; if ((/^176./).test(window.location.hostname)) - return true; + return true;*/ return false; }; From e621950364c93365fad4dbbbb6e94360d2fce809 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sun, 28 Jun 2015 19:38:19 -0700 Subject: [PATCH 05/13] Removed test code --- config/config.json | 6 +++--- web/react/utils/utils.jsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/config.json b/config/config.json index 90ec9540ec..e38f1701a1 100644 --- a/config/config.json +++ b/config/config.json @@ -8,9 +8,9 @@ "FileLocation": "" }, "ServiceSettings": { - "SiteName": "Battlehouse", - "Domain": "battlehouse.com", - "Mode" : "prod", + "SiteName": "Mattermost", + "Domain": "xxxxxxmustbefilledin.com", + "Mode" : "dev", "AllowTesting" : false, "UseSSL": false, "Port": "8065", diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 128d9d1e70..75c583c8fc 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -25,7 +25,7 @@ module.exports.cleanUpUrlable = function(input) { module.exports.isTestDomain = function() { - /*if ((/^localhost/).test(window.location.hostname)) + if ((/^localhost/).test(window.location.hostname)) return true; if ((/^dockerhost/).test(window.location.hostname)) @@ -44,7 +44,7 @@ module.exports.isTestDomain = function() { return true; if ((/^176./).test(window.location.hostname)) - return true;*/ + return true; return false; }; From 2b4888d062d65546325470d2d9181f4a66a2fb00 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Mon, 22 Jun 2015 14:23:59 -0400 Subject: [PATCH 06/13] fixes mm-1316 improves channel notifications UI and updates channellist etag --- model/channel_list.go | 6 + model/channel_member.go | 5 + store/sql_channel_store.go | 12 +- web/react/components/channel_header.jsx | 30 +-- .../components/channel_notifications.jsx | 208 ++++++++++++++---- 5 files changed, 185 insertions(+), 76 deletions(-) diff --git a/model/channel_list.go b/model/channel_list.go index 088dbea2a1..09f14a9862 100644 --- a/model/channel_list.go +++ b/model/channel_list.go @@ -53,6 +53,12 @@ func (o *ChannelList) Etag() string { t = member.LastViewedAt id = v.Id } + + if member.LastUpdateAt > t { + t = member.LastUpdateAt + id = v.Id + } + } } diff --git a/model/channel_member.go b/model/channel_member.go index 720ac4c42d..50f51304bc 100644 --- a/model/channel_member.go +++ b/model/channel_member.go @@ -25,6 +25,7 @@ type ChannelMember struct { MsgCount int64 `json:"msg_count"` MentionCount int64 `json:"mention_count"` NotifyLevel string `json:"notify_level"` + LastUpdateAt int64 `json:"last_update_at"` } func (o *ChannelMember) ToJson() string { @@ -70,6 +71,10 @@ func (o *ChannelMember) IsValid() *AppError { return nil } +func (o *ChannelMember) PreSave() { + o.LastUpdateAt = GetMillis() +} + func IsChannelNotifyLevelValid(notifyLevel string) bool { return notifyLevel == CHANNEL_NOTIFY_ALL || notifyLevel == CHANNEL_NOTIFY_MENTION || notifyLevel == CHANNEL_NOTIFY_NONE || notifyLevel == CHANNEL_NOTIFY_QUIET } diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 592657c1c3..0a1ea23fe9 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -37,6 +37,7 @@ func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore { } func (s SqlChannelStore) UpgradeSchemaIfNeeded() { + s.CreateColumnIfNotExists("ChannelMembers", "LastUpdateAt", "NotifyLevel", "bigint(20)", "0") } func (s SqlChannelStore) CreateIndexesIfNotExists() { @@ -273,6 +274,7 @@ func (s SqlChannelStore) SaveMember(member *model.ChannelMember) StoreChannel { go func() { result := StoreResult{} + member.PreSave() if result.Err = member.IsValid(); result.Err != nil { storeChannel <- result return @@ -484,7 +486,8 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelId string, userId string) Sto SET ChannelMembers.MentionCount = 0, ChannelMembers.MsgCount = Channels.TotalMsgCount, - ChannelMembers.LastViewedAt = Channels.LastPostAt + ChannelMembers.LastViewedAt = Channels.LastPostAt, + ChannelMembers.LastUpdateAt = Channels.LastPostAt WHERE Channels.Id = ChannelMembers.ChannelId AND UserId = ? @@ -533,15 +536,18 @@ func (s SqlChannelStore) UpdateNotifyLevel(channelId, userId, notifyLevel string go func() { result := StoreResult{} + updateAt := model.GetMillis() + _, err := s.GetMaster().Exec( `UPDATE ChannelMembers SET - NotifyLevel = ? + NotifyLevel = ?, + LastUpdateAt = ? WHERE UserId = ? AND ChannelId = ?`, - notifyLevel, userId, channelId) + notifyLevel, updateAt, userId, channelId) if err != nil { result.Err = model.NewAppError("SqlChannelStore.UpdateNotifyLevel", "We couldn't update the notify level", "channel_id="+channelId+", user_id="+userId+", "+err.Error()) } diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index ade58a10a3..428d3ed818 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -15,17 +15,8 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -function getExtraInfoStateFromStores() { - return { - extra_info: ChannelStore.getCurrentExtraInfo() - }; -} - var ExtraMembers = React.createClass({ componentDidMount: function() { - ChannelStore.addExtraInfoChangeListener(this._onChange); - ChannelStore.addChangeListener(this._onChange); - var originalLeave = $.fn.popover.Constructor.prototype.leave; $.fn.popover.Constructor.prototype.leave = function(obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type); @@ -49,25 +40,12 @@ var ExtraMembers = React.createClass({ }); }, - componentWillUnmount: function() { - ChannelStore.removeExtraInfoChangeListener(this._onChange); - ChannelStore.removeChangeListener(this._onChange); - }, - _onChange: function() { - var newState = getExtraInfoStateFromStores(); - if (!utils.areStatesEqual(newState, this.state)) { - this.setState(newState); - } - }, - getInitialState: function() { - return getExtraInfoStateFromStores(); - }, render: function() { - var count = this.state.extra_info.members.length == 0 ? "-" : this.state.extra_info.members.length; - count = this.state.extra_info.members.length > 19 ? "20+" : count; + var count = this.props.members.length == 0 ? "-" : this.props.members.length; + count = this.props.members.length > 19 ? "20+" : count; var data_content = ""; - this.state.extra_info.members.forEach(function(m) { + this.props.members.forEach(function(m) { data_content += "
      " + m.username + "
      "; }); @@ -228,7 +206,7 @@ module.exports = React.createClass({ {channelTitle} } - + { searchForm }
      diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx index 085536a0aa..fa9ab42ae8 100644 --- a/web/react/components/channel_notifications.jsx +++ b/web/react/components/channel_notifications.jsx @@ -1,6 +1,8 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. +var SettingItemMin = require('./setting_item_min.jsx'); +var SettingItemMax = require('./setting_item_max.jsx'); var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); @@ -9,26 +11,50 @@ var ChannelStore = require('../stores/channel_store.jsx'); module.exports = React.createClass({ componentDidMount: function() { + ChannelStore.addChangeListener(this._onChange); + var self = this; $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { var button = e.relatedTarget; var channel_id = button.dataset.channelid; var notifyLevel = ChannelStore.getMember(channel_id).notify_level; - self.setState({ notify_level: notifyLevel, title: button.dataset.title, channel_id: channel_id }); + var quietMode = false; + if (notifyLevel === "quiet") quietMode = true; + self.setState({ notify_level: notifyLevel, quiet_mode: quietMode, title: button.dataset.title, channel_id: channel_id }); }); }, - getInitialState: function() { - return { notify_level: "", title: "", channel_id: "" }; + componentWillUnmount: function() { + ChannelStore.removeChangeListener(this._onChange); }, - handleUpdate: function(e) { + _onChange: function() { + if (!this.state.channel_id) return; + var notifyLevel = ChannelStore.getMember(this.state.channel_id).notify_level; + var quietMode = false; + if (notifyLevel === "quiet") quietMode = true; + + var newState = this.state; + newState.notify_level = notifyLevel; + newState.quiet_mode = quietMode; + + if (!utils.areStatesEqual(this.state, newState)) { + this.setState(newState); + } + }, + updateSection: function(section) { + this.setState({ activeSection: section }); + }, + getInitialState: function() { + return { notify_level: "", title: "", channel_id: "", activeSection: "" }; + }, + handleUpdate: function() { var channel_id = this.state.channel_id; - var notify_level = this.state.notify_level; + var notify_level = this.state.quiet_mode ? "quiet" : this.state.notify_level; var data = {}; data["channel_id"] = channel_id; data["user_id"] = UserStore.getCurrentId(); - data["notify_level"] = this.state.notify_level; + data["notify_level"] = notify_level; if (!data["notify_level"] || data["notify_level"].length === 0) return; @@ -37,7 +63,7 @@ module.exports = React.createClass({ var member = ChannelStore.getMember(channel_id); member.notify_level = notify_level; ChannelStore.setChannelMember(member); - $(this.refs.modal.getDOMNode()).modal('hide'); + this.updateSection(""); }.bind(this), function(err) { this.setState({ server_error: err.message }); @@ -45,42 +71,138 @@ module.exports = React.createClass({ ); }, handleRadioClick: function(notifyLevel) { - this.setState({ notify_level: notifyLevel }); + this.setState({ notify_level: notifyLevel, quiet_mode: false }); this.refs.modal.getDOMNode().focus(); }, - handleQuietToggle: function() { - if (this.state.notify_level === "quiet") { - this.setState({ notify_level: "none" }); - this.refs.modal.getDOMNode().focus(); - } else { - this.setState({ notify_level: "quiet" }); - this.refs.modal.getDOMNode().focus(); - } + handleQuietToggle: function(quietMode) { + this.setState({ notify_level: "none", quiet_mode: quietMode }); + this.refs.modal.getDOMNode().focus(); }, render: function() { var server_error = this.state.server_error ?
      : null; - var allActive = ""; - var mentionActive = ""; - var noneActive = ""; - var quietActive = ""; - var desktopHidden = ""; + var self = this; - if (this.state.notify_level === "quiet") { - desktopHidden = "hidden"; - quietActive = "active"; - } else if (this.state.notify_level === "mention") { - mentionActive = "active"; - } else if (this.state.notify_level === "none") { - noneActive = "active"; + var desktopSection; + if (this.state.activeSection === 'desktop') { + var notifyActive = [false, false, false]; + if (this.state.notify_level === "mention") { + notifyActive[1] = true; + } else if (this.state.notify_level === "all") { + notifyActive[0] = true; + } else { + notifyActive[2] = true; + } + + var inputs = []; + + inputs.push( +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      +
      + ); + + desktopSection = ( + + ); } else { - allActive = "active"; + var describe = ""; + if (this.state.notify_level === "mention") { + describe = "Only for mentions"; + } else if (this.state.notify_level === "all") { + describe = "For all activity"; + } else { + describe = "Never"; + } + + desktopSection = ( + + ); + } + + var quietSection; + if (this.state.activeSection === 'quiet') { + var quietActive = ["",""]; + if (this.state.quiet_mode) { + quietActive[0] = "active"; + } else { + quietActive[1] = "active"; + } + + var inputs = []; + + inputs.push( +
      +
      + + +
      +
      + ); + + inputs.push( +
      +
      + Enabling quiet mode will turn off desktop notifications and only mark the channel as unread if you have been mentioned. +
      + ); + + quietSection = ( + + ); + } else { + var describe = ""; + if (this.state.quiet_mode) { + describe = "On"; + } else { + describe = "Off"; + } + + quietSection = ( + + ); } var self = this; return (