From 1ab7034574de2229d3cfc49391e6579db37a3064 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Wed, 14 Oct 2015 18:27:03 +0300 Subject: Use team display name in team switcher menu - /teams/find_teams returns team objects instead of just team names - use display_name in UI instead of name in the team switch menu --- web/react/components/navbar_dropdown.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'web') diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 49d517419..b3f8e4418 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -145,7 +145,7 @@ export default class NavbarDropdown extends React.Component { var teams = []; - if (this.state.teams.length > 1) { + if (Object.keys(this.state.teams).length > 1) { teams.push(
  • ); - this.state.teams.forEach((teamName) => { - if (teamName !== this.props.teamName) { - teams.push(
  • {'Switch to ' + teamName}
  • ); + for (let teamId in this.state.teams) { + if (this.state.teams.hasOwnProperty(teamId)) { + let team = this.state.teams[teamId]; + if (team.name !== this.props.teamName) { + teams.push(
  • {'Switch to ' + team.display_name}
  • ); + } } - }); + } } if (global.window.config.EnableTeamCreation === 'true') { -- cgit v1.2.3-1-g7c22 From 76e278a13de936c09068ad1c2c2a16be0c419249 Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Wed, 14 Oct 2015 20:36:12 +0300 Subject: added display tab under account settings. added 24h time option --- web/react/components/post_list.jsx | 3 ++ .../components/user_settings/user_settings.jsx | 12 ++++++++ .../user_settings/user_settings_modal.jsx | 1 + web/react/utils/constants.jsx | 3 +- web/react/utils/utils.jsx | 33 +++++++++++++--------- 5 files changed, 38 insertions(+), 14 deletions(-) (limited to 'web') diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 643b38af5..ebbe319b5 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -4,6 +4,7 @@ var PostStore = require('../stores/post_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); +var PreferenceStore = require('../stores/preference_store.jsx'); var UserProfile = require('./user_profile.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var Post = require('./post.jsx'); @@ -105,6 +106,7 @@ export default class PostList extends React.Component { PostStore.clearUnseenDeletedPosts(this.props.channelId); PostStore.addChangeListener(this.onChange); UserStore.addStatusesChangeListener(this.onTimeChange); + PreferenceStore.addChangeListener(this.onTimeChange); SocketStore.addChangeListener(this.onSocketChange); const postHolder = $(React.findDOMNode(this.refs.postlist)); @@ -156,6 +158,7 @@ export default class PostList extends React.Component { PostStore.removeChangeListener(this.onChange); UserStore.removeStatusesChangeListener(this.onTimeChange); SocketStore.removeChangeListener(this.onSocketChange); + PreferenceStore.removeChangeListener(this.onTimeChange); $('body').off('click.userpopover'); $(window).off('resize'); var postHolder = $(React.findDOMNode(this.refs.postlist)); diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx index 5ce9b6330..15bf961d6 100644 --- a/web/react/components/user_settings/user_settings.jsx +++ b/web/react/components/user_settings/user_settings.jsx @@ -9,6 +9,7 @@ var GeneralTab = require('./user_settings_general.jsx'); var AppearanceTab = require('./user_settings_appearance.jsx'); var DeveloperTab = require('./user_settings_developer.jsx'); var IntegrationsTab = require('./user_settings_integrations.jsx'); +var DisplayTab = require('./user_settings_display.jsx'); export default class UserSettings extends React.Component { constructor(props) { @@ -98,6 +99,17 @@ export default class UserSettings extends React.Component { /> ); + } else if (this.props.activeTab === 'display') { + return ( +
    + +
    + ); } return
    ; diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx index 19b97fc85..692fb26ee 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -41,6 +41,7 @@ export default class UserSettingsModal extends React.Component { if (global.window.config.EnableIncomingWebhooks === 'true') { tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'}); } + tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'}); return (
    = 12) { - ampm = 'PM'; - } + const d = new Date(ticks); + let hours = d.getHours(); + let minutes = d.getMinutes(); + let ampm = ''; - hours = hours % 12; - if (!hours) { - hours = '12'; - } if (minutes <= 9) { minutes = '0' + minutes; } - return hours + ':' + minutes + ' ' + ampm; + + const useMilitaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}).value; + if(useMilitaryTime === 'false') { + ampm = ' AM'; + if (hours >= 12) { + ampm = ' PM'; + } + + hours = hours % 12; + if (!hours) { + hours = '12'; + } + } + + return hours + ':' + minutes + ampm; } export function displayDateTime(ticks) { -- cgit v1.2.3-1-g7c22 From 29a0c182ef376de8368552e7b472afc960c4ba8a Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Wed, 14 Oct 2015 21:39:33 +0300 Subject: added file with display settings --- .../user_settings/user_settings_display.jsx | 168 +++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 web/react/components/user_settings/user_settings_display.jsx (limited to 'web') diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx new file mode 100644 index 000000000..ec209c218 --- /dev/null +++ b/web/react/components/user_settings/user_settings_display.jsx @@ -0,0 +1,168 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import { savePreferences } from '../../utils/client.jsx'; +import SettingItemMin from '../setting_item_min.jsx'; +import SettingItemMax from '../setting_item_max.jsx'; +import Constants from '../../utils/constants.jsx'; +import PreferenceStore from '../../stores/preference_store.jsx'; + +function getDisplayStateFromStores() { + const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}); + + return {militaryTime: militaryTime.value}; +} + +export default class UserSettingsDisplay extends React.Component { + constructor(props) { + super(props); + + this.handleSubmit = this.handleSubmit.bind(this); + this.handleClockRadio = this.handleClockRadio.bind(this); + this.updateSection = this.updateSection.bind(this); + this.handleClose = this.handleClose.bind(this); + + this.state = getDisplayStateFromStores(); + } + handleSubmit() { + const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', this.state.militaryTime); + + savePreferences([preference], + () => { + PreferenceStore.emitChange(); + this.updateSection(''); + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + } + handleClockRadio(militaryTime) { + this.setState({militaryTime: militaryTime}); + } + updateSection(section) { + this.setState(getDisplayStateFromStores()); + this.props.updateSection(section); + } + handleClose() { + this.updateSection(''); + } + componentDidMount() { + $('#user_settings').on('hidden.bs.modal', this.handleClose); + } + componentWillUnmount() { + $('#user_settings').off('hidden.bs.modal', this.handleClose); + } + render() { + const serverError = this.state.serverError || null; + let clockSection; + if (this.props.activeSection === 'clock') { + let clockFormat = [false, false]; + if (this.state.militaryTime === 'true') { + clockFormat[1] = true; + } else { + clockFormat[0] = true; + } + + const handleUpdateClockSection = (e) => { + this.updateSection(''); + e.preventDefault(); + }; + + const inputs = [ +
    +
    + +
    +
    +
    + +
    +
    +

    {'Select how you prefer time displayed.'}
    +
    + ]; + + + clockSection = ( + + ); + } else { + let describe = ''; + if (this.state.militaryTime === 'true') { + describe = '24-hour clock (example: 16:00)'; + } else { + describe = '12-hour clock (example: 4:00 PM)'; + } + + const handleUpdateClockSection = () => { + this.props.updateSection('clock'); + }; + + clockSection = ( + + ); + } + + return ( +
    +
    + +

    + + {'Display Settings'} +

    +
    +
    +

    {'Display Settings'}

    +
    + {clockSection} +
    +
    +
    + ); + } +} + +UserSettingsDisplay.propTypes = { + user: React.PropTypes.object, + updateSection: React.PropTypes.func, + updateTab: React.PropTypes.func, + activeSection: React.PropTypes.string +}; -- cgit v1.2.3-1-g7c22 From 440faabc604fa6763515efd9009a7c276e140f09 Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Thu, 15 Oct 2015 00:05:39 +0300 Subject: fixed style guide error --- web/react/utils/utils.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'web') diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index a5057b5ca..8f01b3e33 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -175,7 +175,7 @@ export function displayTime(ticks) { } const useMilitaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}).value; - if(useMilitaryTime === 'false') { + if (useMilitaryTime === 'false') { ampm = ' AM'; if (hours >= 12) { ampm = ' PM'; -- cgit v1.2.3-1-g7c22 From 059df3de0126f2a506b525f92911035eba02bbd3 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Thu, 15 Oct 2015 08:41:52 +0300 Subject: Sort teams by display name First we need to convert the object to array, because objects cannot be sorted. --- web/react/components/navbar_dropdown.jsx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'web') diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index b3f8e4418..d4308ad72 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -11,7 +11,25 @@ var AboutBuildModal = require('./about_build_modal.jsx'); var Constants = require('../utils/constants.jsx'); function getStateFromStores() { - return {teams: UserStore.getTeams()}; + let teams = []; + let teamsObject = UserStore.getTeams(); + for (let teamId in teamsObject) { + if (teamsObject.hasOwnProperty(teamId)) { + teams.push(teamsObject[teamId]) + } + } + teams.sort(function (teamA, teamB) { + let teamADisplayName = teamA.display_name.toLowerCase(); + let teamBDisplayName = teamB.display_name.toLowerCase(); + if (teamADisplayName < teamBDisplayName) { + return -1 + } else if (teamADisplayName > teamBDisplayName) { + return 1; + } else { + return 0; + } + }); + return {teams}; } export default class NavbarDropdown extends React.Component { -- cgit v1.2.3-1-g7c22 From c0809bbc3fc7a9630659872850c313eb6df701d9 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Thu, 15 Oct 2015 08:54:20 +0300 Subject: Treat teams as an array instead of object --- web/react/components/navbar_dropdown.jsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'web') diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index d4308ad72..564a64f48 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -163,7 +163,7 @@ export default class NavbarDropdown extends React.Component { var teams = []; - if (Object.keys(this.state.teams).length > 1) { + if (this.state.teams.length > 1) { teams.push(
  • ); - for (let teamId in this.state.teams) { - if (this.state.teams.hasOwnProperty(teamId)) { - let team = this.state.teams[teamId]; - if (team.name !== this.props.teamName) { - teams.push(
  • {'Switch to ' + team.display_name}
  • ); - } + this.state.teams.forEach((team) => { + if (team.name !== this.props.teamName) { + teams.push(
  • {'Switch to ' + team.display_name}
  • ); } - } + }); } if (global.window.config.EnableTeamCreation === 'true') { -- cgit v1.2.3-1-g7c22 From 7abde676a1828714c71b25184fdd91a81c2d77a1 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Thu, 15 Oct 2015 19:35:45 +0300 Subject: Fix lint --- web/react/components/navbar_dropdown.jsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'web') diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 564a64f48..dc41775c7 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -15,19 +15,18 @@ function getStateFromStores() { let teamsObject = UserStore.getTeams(); for (let teamId in teamsObject) { if (teamsObject.hasOwnProperty(teamId)) { - teams.push(teamsObject[teamId]) + teams.push(teamsObject[teamId]); } } - teams.sort(function (teamA, teamB) { + teams.sort(function sortByDisplayName(teamA, teamB) { let teamADisplayName = teamA.display_name.toLowerCase(); let teamBDisplayName = teamB.display_name.toLowerCase(); if (teamADisplayName < teamBDisplayName) { - return -1 + return -1; } else if (teamADisplayName > teamBDisplayName) { return 1; - } else { - return 0; } + return 0; }); return {teams}; } -- cgit v1.2.3-1-g7c22 From 2bd81ff379acb347534e4d6a1a27624e0262deb0 Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Thu, 15 Oct 2015 19:49:34 +0300 Subject: update posts time in the right sidebar when user changes time format --- web/react/components/rhs_thread.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'web') diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 41fd74adb..748cb16a8 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -3,6 +3,7 @@ var PostStore = require('../stores/post_store.jsx'); var UserStore = require('../stores/user_store.jsx'); +var PreferenceStore = require('../stores/preference_store.jsx'); var utils = require('../utils/utils.jsx'); var SearchBox = require('./search_bar.jsx'); var CreateComment = require('./create_comment.jsx'); @@ -18,6 +19,7 @@ export default class RhsThread extends React.Component { this.onChange = this.onChange.bind(this); this.onChangeAll = this.onChangeAll.bind(this); + this.forceUpdateInfo = this.forceUpdateInfo.bind(this); this.state = this.getStateFromStores(); } @@ -43,6 +45,7 @@ export default class RhsThread extends React.Component { componentDidMount() { PostStore.addSelectedPostChangeListener(this.onChange); PostStore.addChangeListener(this.onChangeAll); + PreferenceStore.addChangeListener(this.forceUpdateInfo); this.resize(); $(window).resize(function resize() { this.resize(); @@ -59,6 +62,16 @@ export default class RhsThread extends React.Component { componentWillUnmount() { PostStore.removeSelectedPostChangeListener(this.onChange); PostStore.removeChangeListener(this.onChangeAll); + PreferenceStore.removeChangeListener(this.forceUpdateInfo); + } + forceUpdateInfo() { + if (this.state.postList) { + for (var postId in this.state.postList.posts) { + if (this.refs[postId]) { + this.refs[postId].forceUpdate(); + } + } + } } onChange() { var newState = this.getStateFromStores(); @@ -174,6 +187,7 @@ export default class RhsThread extends React.Component { />
    -- cgit v1.2.3-1-g7c22 From 327b0b5a2119ae888c812f682b3934061b8f59bf Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 15 Oct 2015 15:09:40 -0400 Subject: Added an initial call to get all user preferences on page load --- web/react/components/sidebar.jsx | 6 +----- web/react/pages/channel.jsx | 3 +++ web/react/stores/preference_store.jsx | 1 + web/react/utils/async_client.jsx | 15 +++++++-------- web/react/utils/client.jsx | 13 +++++++++++++ 5 files changed, 25 insertions(+), 13 deletions(-) (limited to 'web') diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index e7d90bb88..5ab0c4f9e 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -132,11 +132,7 @@ export default class Sidebar extends React.Component { SocketStore.addChangeListener(this.onSocketChange); PreferenceStore.addChangeListener(this.onChange); - AsyncClient.getDirectChannelPreferences(); - - if ($(window).width() > 768) { - $('.nav-pills__container').perfectScrollbar(); - } + $('.nav-pills__container').perfectScrollbar(); this.updateTitle(); this.updateUnreadIndicators(); diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 698213eef..19e158aa6 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -37,6 +37,7 @@ var RegisterAppModal = require('../components/register_app_modal.jsx'); var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx'); var TeamStore = require('../stores/team_store.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -54,6 +55,8 @@ function setupChannelPage(props) { id: props.TeamId }); + AsyncClient.getAllPreferences(); + // ChannelLoader must be rendered first React.render( , diff --git a/web/react/stores/preference_store.jsx b/web/react/stores/preference_store.jsx index d71efa10f..f630d150d 100644 --- a/web/react/stores/preference_store.jsx +++ b/web/react/stores/preference_store.jsx @@ -120,3 +120,4 @@ class PreferenceStoreClass extends EventEmitter { const PreferenceStore = new PreferenceStoreClass(); export default PreferenceStore; +window.PreferenceStore = PreferenceStore; diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx index 1bf8a6fee..b22d7237e 100644 --- a/web/react/utils/async_client.jsx +++ b/web/react/utils/async_client.jsx @@ -638,16 +638,15 @@ export function getMyTeam() { ); } -export function getDirectChannelPreferences() { - if (isCallInProgress('getDirectChannelPreferences')) { +export function getAllPreferences() { + if (isCallInProgress('getAllPreferences')) { return; } - callTracker.getDirectChannelPreferences = utils.getTimestamp(); - client.getPreferenceCategory( - Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, + callTracker.getAllPreferences = utils.getTimestamp(); + client.getAllPreferences( (data, textStatus, xhr) => { - callTracker.getDirectChannelPreferences = 0; + callTracker.getAllPreferences = 0; if (xhr.status === 304 || !data) { return; @@ -659,8 +658,8 @@ export function getDirectChannelPreferences() { }); }, (err) => { - callTracker.getDirectChannelPreferences = 0; - dispatchError(err, 'getDirectChannelPreferences'); + callTracker.getAllPreferences = 0; + dispatchError(err, 'getAllPreferences'); } ); } diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 76a402855..f6aee362c 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -1142,6 +1142,19 @@ export function listIncomingHooks(success, error) { }); } +export function getAllPreferences(success, error) { + $.ajax({ + url: `/api/v1/preferences/`, + dataType: 'json', + type: 'GET', + success, + error: (xhr, status, err) => { + var e = handleError('getAllPreferences', xhr, status, err); + error(e); + } + }); +} + export function getPreferenceCategory(category, success, error) { $.ajax({ url: `/api/v1/preferences/${category}`, -- cgit v1.2.3-1-g7c22 From 551b07960ed8ec36c24341f96f2b06fee25ceab3 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Thu, 15 Oct 2015 02:13:48 +0200 Subject: PLT-74: Enable Up Arrow keyboard shortcut to edit your last message --- web/react/components/create_post.jsx | 23 +++++++++++++++++++- web/react/components/edit_post_modal.jsx | 22 +++++++++++++++++++ web/react/components/textbox.jsx | 8 +++++-- web/react/stores/post_store.jsx | 37 ++++++++++++++++++++++++++++++++ web/react/utils/constants.jsx | 10 +++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) (limited to 'web') diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 6203e567a..ed6818dc8 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -16,6 +16,7 @@ const Utils = require('../utils/utils.jsx'); const Constants = require('../utils/constants.jsx'); const ActionTypes = Constants.ActionTypes; +const KeyCodes = Constants.KeyCodes; export default class CreatePost extends React.Component { constructor(props) { @@ -35,6 +36,7 @@ export default class CreatePost extends React.Component { this.removePreview = this.removePreview.bind(this); this.onChange = this.onChange.bind(this); this.getFileCount = this.getFileCount.bind(this); + this.handleArrowUp = this.handleArrowUp.bind(this); PostStore.clearDraftUploads(); @@ -172,7 +174,7 @@ export default class CreatePost extends React.Component { } } postMsgKeyPress(e) { - if (e.which === 13 && !e.shiftKey && !e.altKey) { + if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) { e.preventDefault(); ReactDOM.findDOMNode(this.refs.textbox).blur(); this.handleSubmit(e); @@ -292,6 +294,24 @@ export default class CreatePost extends React.Component { const draft = PostStore.getDraft(channelId); return draft.previews.length + draft.uploadsInProgress.length; } + handleArrowUp(e) { + if (e.keyCode === KeyCodes.UP && this.state.messageText === '') { + e.preventDefault(); + + const channelId = ChannelStore.getCurrentId(); + const lastPost = PostStore.getCurrentUsersLatestPost(channelId); + var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post'; + + AppDispatcher.handleViewAction({ + type: ActionTypes.RECIEVED_EDIT_POST, + refoucsId: '#post_textbox', + title: type, + message: lastPost.message, + lastPostId: lastPost.id, + channelId: lastPost.channel_id + }); + } + } render() { let serverError = null; if (this.state.serverError) { @@ -336,6 +356,7 @@ export default class CreatePost extends React.Component {
    ); diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index d51fb9523..86bb42f62 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -9,6 +9,7 @@ const ErrorStore = require('../stores/error_store.jsx'); const Utils = require('../utils/utils.jsx'); const Constants = require('../utils/constants.jsx'); const ActionTypes = Constants.ActionTypes; +const KeyCodes = Constants.KeyCodes; export default class Textbox extends React.Component { constructor(props) { @@ -148,8 +149,10 @@ export default class Textbox extends React.Component { this.doProcessMentions = true; } - if (e.keyCode === 8) { + if (e.keyCode === KeyCodes.BACKSPACE) { this.handleBackspace(e); + } else if (this.props.onKeyDown) { + this.props.onKeyDown(e); } } @@ -318,5 +321,6 @@ Textbox.propTypes = { onUserInput: React.PropTypes.func.isRequired, onKeyPress: React.PropTypes.func.isRequired, onHeightChange: React.PropTypes.func, - createMessage: React.PropTypes.string.isRequired + createMessage: React.PropTypes.string.isRequired, + onKeyDown: React.PropTypes.func }; diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index d8da48000..8609d8bbf 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -6,6 +6,7 @@ var EventEmitter = require('events').EventEmitter; var ChannelStore = require('../stores/channel_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); +var UserStore = require('../stores/user_store.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -16,6 +17,7 @@ var SEARCH_TERM_CHANGE_EVENT = 'search_term_change'; var SELECTED_POST_CHANGE_EVENT = 'selected_post_change'; var MENTION_DATA_CHANGE_EVENT = 'mention_data_change'; var ADD_MENTION_EVENT = 'add_mention'; +var EDIT_POST_EVENT = 'edit_post'; class PostStoreClass extends EventEmitter { constructor() { @@ -75,6 +77,10 @@ class PostStoreClass extends EventEmitter { this.clearCommentDraftUploads = this.clearCommentDraftUploads.bind(this); this.storeLatestUpdate = this.storeLatestUpdate.bind(this); this.getLatestUpdate = this.getLatestUpdate.bind(this); + this.emitEditPost = this.emitEditPost.bind(this); + this.addEditPostListener = this.addEditPostListener.bind(this); + this.removeEditPostListener = this.removeEditPostListener.bind(this); + this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this); } emitChange() { this.emit(CHANGE_EVENT); @@ -148,6 +154,18 @@ class PostStoreClass extends EventEmitter { this.removeListener(ADD_MENTION_EVENT, callback); } + emitEditPost(post) { + this.emit(EDIT_POST_EVENT, post); + } + + addEditPostListener(callback) { + this.on(EDIT_POST_EVENT, callback); + } + + removeEditPostListener(callback) { + this.removeListener(EDIT_POST_EVENT, callback); + } + getCurrentPosts() { var currentId = ChannelStore.getCurrentId(); @@ -212,6 +230,22 @@ class PostStoreClass extends EventEmitter { getPosts(channelId) { return BrowserStore.getItem('posts_' + channelId); } + getCurrentUsersLatestPost(channelId) { + const userId = UserStore.getCurrentId(); + var postList = makePostListNonNull(this.getPosts(channelId)); + var i = 0; + var len = postList.order.length; + var lastPost = null; + + for (i; i < len; i++) { + if (postList.posts[postList.order[i]].user_id === userId) { + lastPost = postList.posts[postList.order[i]]; + break; + } + } + + return lastPost; + } storePost(post) { this.pStorePost(post); this.emitChange(); @@ -446,6 +480,9 @@ PostStore.dispatchToken = AppDispatcher.register(function registry(payload) { case ActionTypes.RECIEVED_ADD_MENTION: PostStore.emitAddMention(action.id, action.username); break; + case ActionTypes.RECIEVED_EDIT_POST: + PostStore.emitEditPost(action); + break; default: } }); diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index cee2ec114..b8200db54 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -17,6 +17,7 @@ module.exports = { RECIEVED_POSTS: null, RECIEVED_POST: null, + RECIEVED_EDIT_POST: null, RECIEVED_SEARCH: null, RECIEVED_POST_SELECTED: null, RECIEVED_MENTION_DATA: null, @@ -289,5 +290,14 @@ module.exports = { ], Preferences: { CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show' + }, + KeyCodes: { + UP: 38, + DOWN: 40, + LEFT: 37, + RIGHT: 39, + BACKSPACE: 8, + ENTER: 13, + ESCAPE: 27 } }; -- cgit v1.2.3-1-g7c22 From fcce3168b2776af6baf87096704465fc71fe28ac Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Thu, 15 Oct 2015 21:22:08 +0200 Subject: PLT-74: Enable Up Arrow keyboard shortcut to edit your last message - fix bug where channel_id was not set if update_post ajax was "too fast" - fix bug js error if there is no last post, i.e. empty channel - renamed lastPostId to postId so it has a nicer api to be triggered from everywhere - fix a typo - automatically focus textarea on modal open --- web/react/components/create_post.jsx | 7 +++++-- web/react/components/edit_post_modal.jsx | 14 +++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'web') diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index ed6818dc8..2581bdcca 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -300,14 +300,17 @@ export default class CreatePost extends React.Component { const channelId = ChannelStore.getCurrentId(); const lastPost = PostStore.getCurrentUsersLatestPost(channelId); + if (!lastPost) { + return; + } var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post'; AppDispatcher.handleViewAction({ type: ActionTypes.RECIEVED_EDIT_POST, - refoucsId: '#post_textbox', + refocusId: '#post_textbox', title: type, message: lastPost.message, - lastPostId: lastPost.id, + postId: lastPost.id, channelId: lastPost.channel_id }); } diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx index 38d31d66f..90d9696e7 100644 --- a/web/react/components/edit_post_modal.jsx +++ b/web/react/components/edit_post_modal.jsx @@ -37,16 +37,15 @@ export default class EditPostModal extends React.Component { Client.updatePost(updatedPost, function success() { - AsyncClient.getPosts(this.state.channel_id); + AsyncClient.getPosts(updatedPost.channel_id); window.scrollTo(0, 0); - }.bind(this), + }, function error(err) { AsyncClient.dispatchError(err, 'updatePost'); } ); $('#edit_post').modal('hide'); - $(this.state.refocusId).focus(); } handleEditInput(editMessage) { this.setState({editText: editMessage}); @@ -90,6 +89,15 @@ export default class EditPostModal extends React.Component { $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() { self.refs.editbox.resize(); + $('#edit_textbox').get(0).focus(); + }); + + $(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() { + if (self.state.refocusId !== '') { + setTimeout(() => { + $(self.state.refocusId).get(0).focus(); + }); + } }); PostStore.addEditPostListener(this.handleEditPostEvent); -- cgit v1.2.3-1-g7c22 From 12f47b10606b88ab0fbc16f1800ffeffe6eb19bf Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Fri, 16 Oct 2015 12:37:51 +0500 Subject: Adding momentum scrolling on IOS --- web/sass-files/sass/partials/_post_right.scss | 3 ++- web/sass-files/sass/partials/_search.scss | 3 ++- web/sass-files/sass/partials/_sidebar--left.scss | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'web') diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss index 9557d7570..91f9355de 100644 --- a/web/sass-files/sass/partials/_post_right.scss +++ b/web/sass-files/sass/partials/_post_right.scss @@ -93,7 +93,8 @@ .post-right__scroll { position: relative; - overflow: auto; + overflow: scroll; + -webkit-overflow-scrolling: touch; } .post-right-comment-time { diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss index ca90ec46d..2f15a445f 100644 --- a/web/sass-files/sass/partials/_search.scss +++ b/web/sass-files/sass/partials/_search.scss @@ -67,8 +67,9 @@ } .search-items-container { - overflow: auto; position: relative; + overflow: scroll; + -webkit-overflow-scrolling: touch; } .search-results-header { diff --git a/web/sass-files/sass/partials/_sidebar--left.scss b/web/sass-files/sass/partials/_sidebar--left.scss index 831c19cff..585a51f08 100644 --- a/web/sass-files/sass/partials/_sidebar--left.scss +++ b/web/sass-files/sass/partials/_sidebar--left.scss @@ -47,7 +47,8 @@ .nav-pills__container { height: 100%; position: relative; - overflow: auto; + overflow: scroll; + -webkit-overflow-scrolling: touch; } .nav-pills__unread-indicator { -- cgit v1.2.3-1-g7c22 From 58c33f01d563d729e5fa5f8f037369210f8a1962 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Fri, 16 Oct 2015 08:16:34 -0400 Subject: Add default username and icon to incoming webhooks. --- web/static/images/webhook_icon.jpg | Bin 0 -> 68190 bytes web/web.go | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 web/static/images/webhook_icon.jpg (limited to 'web') diff --git a/web/static/images/webhook_icon.jpg b/web/static/images/webhook_icon.jpg new file mode 100644 index 000000000..af5303421 Binary files /dev/null and b/web/static/images/webhook_icon.jpg differ diff --git a/web/web.go b/web/web.go index 7ab50a073..00e00b3b9 100644 --- a/web/web.go +++ b/web/web.go @@ -971,12 +971,20 @@ func incomingWebhook(c *api.Context, w http.ResponseWriter, r *http.Request) { post := &model.Post{UserId: hook.UserId, ChannelId: channel.Id, Message: text} post.AddProp("from_webhook", "true") - if len(overrideUsername) != 0 && utils.Cfg.ServiceSettings.EnablePostUsernameOverride { - post.AddProp("override_username", overrideUsername) + if utils.Cfg.ServiceSettings.EnablePostUsernameOverride { + if len(overrideUsername) != 0 { + post.AddProp("override_username", overrideUsername) + } else { + post.AddProp("override_username", model.DEFAULT_WEBHOOK_USERNAME) + } } - if len(overrideIconUrl) != 0 && utils.Cfg.ServiceSettings.EnablePostIconOverride { - post.AddProp("override_icon_url", overrideIconUrl) + if utils.Cfg.ServiceSettings.EnablePostIconOverride { + if len(overrideIconUrl) != 0 { + post.AddProp("override_icon_url", overrideIconUrl) + } else { + post.AddProp("override_icon_url", model.DEFAULT_WEBHOOK_ICON) + } } if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN { -- cgit v1.2.3-1-g7c22