summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/post.go2
-rw-r--r--i18n/en.json24
-rw-r--r--model/command.go9
-rw-r--r--model/command_test.go20
-rw-r--r--model/incoming_webhook.go24
-rw-r--r--model/incoming_webhook_test.go20
-rw-r--r--model/outgoing_webhook.go10
-rw-r--r--model/outgoing_webhook_test.go20
-rw-r--r--store/sql_command_store.go2
-rw-r--r--store/sql_webhook_store.go9
-rw-r--r--webapp/action_creators/global_actions.jsx1
-rw-r--r--webapp/components/backstage/add_incoming_webhook.jsx27
-rw-r--r--webapp/components/backstage/add_outgoing_webhook.jsx29
-rw-r--r--webapp/components/backstage/installed_incoming_webhook.jsx16
-rw-r--r--webapp/components/backstage/installed_outgoing_webhook.jsx18
-rw-r--r--webapp/components/channel_header.jsx4
-rw-r--r--webapp/components/more_channels.jsx4
-rw-r--r--webapp/components/more_direct_channels.jsx4
-rw-r--r--webapp/components/new_channel_flow.jsx60
-rw-r--r--webapp/components/popover_list_members.jsx4
-rw-r--r--webapp/components/removed_from_channel_modal.jsx9
-rw-r--r--webapp/components/rename_channel_modal.jsx4
-rw-r--r--webapp/i18n/en.json1
-rw-r--r--webapp/root.jsx6
-rw-r--r--webapp/sass/routes/_backstage.scss7
-rw-r--r--webapp/stores/channel_store.jsx10
-rw-r--r--webapp/utils/async_client.jsx8
-rw-r--r--webapp/utils/utils.jsx29
28 files changed, 295 insertions, 86 deletions
diff --git a/api/post.go b/api/post.go
index 2fe5feb8e..6f88c815b 100644
--- a/api/post.go
+++ b/api/post.go
@@ -544,7 +544,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
}
for _, userId := range userIds {
- if post.UserId == userId {
+ if post.UserId == userId && post.Props["from_webhook"] != "true" {
continue
}
sendEmail := true
diff --git a/i18n/en.json b/i18n/en.json
index 6292c1e03..0dca6ca04 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -2060,6 +2060,14 @@
"translation": "Create at must be a valid time"
},
{
+ "id": "model.command.is_valid.description.app_error",
+ "translation": "Invalid description"
+ },
+ {
+ "id": "model.command.is_valid.display_name.app_error",
+ "translation": "Invalid display name"
+ },
+ {
"id": "model.command.is_valid.id.app_error",
"translation": "Invalid Id"
},
@@ -2216,6 +2224,14 @@
"translation": "Create at must be a valid time"
},
{
+ "id": "model.incoming_hook.description.app_error",
+ "translation": "Invalid description"
+ },
+ {
+ "id": "model.incoming_hook.display_name.app_error",
+ "translation": "Invalid display name"
+ },
+ {
"id": "model.incoming_hook.id.app_error",
"translation": "Invalid Id"
},
@@ -2280,6 +2296,14 @@
"translation": "Create at must be a valid time"
},
{
+ "id": "model.outgoing_hook.is_valid.description.app_error",
+ "translation": "Invalid description"
+ },
+ {
+ "id": "model.outgoing_hook.is_valid.display_name.app_error",
+ "translation": "Invalid display name"
+ },
+ {
"id": "model.outgoing_hook.is_valid.id.app_error",
"translation": "Invalid Id"
},
diff --git a/model/command.go b/model/command.go
index 56d88f13c..ca3038122 100644
--- a/model/command.go
+++ b/model/command.go
@@ -29,6 +29,7 @@ type Command struct {
AutoCompleteDesc string `json:"auto_complete_desc"`
AutoCompleteHint string `json:"auto_complete_hint"`
DisplayName string `json:"display_name"`
+ Description string `json:"description"`
URL string `json:"url"`
}
@@ -114,6 +115,14 @@ func (o *Command) IsValid() *AppError {
return NewLocAppError("Command.IsValid", "model.command.is_valid.method.app_error", nil, "")
}
+ if len(o.DisplayName) > 64 {
+ return NewLocAppError("Command.IsValid", "model.command.is_valid.display_name.app_error", nil, "")
+ }
+
+ if len(o.Description) > 128 {
+ return NewLocAppError("Command.IsValid", "model.command.is_valid.description.app_error", nil, "")
+ }
+
return nil
}
diff --git a/model/command_test.go b/model/command_test.go
index 0581625d9..d362d8f2c 100644
--- a/model/command_test.go
+++ b/model/command_test.go
@@ -84,6 +84,26 @@ func TestCommandIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.DisplayName = strings.Repeat("1", 65)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.DisplayName = strings.Repeat("1", 64)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Description = strings.Repeat("1", 129)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Description = strings.Repeat("1", 128)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestCommandPreSave(t *testing.T) {
diff --git a/model/incoming_webhook.go b/model/incoming_webhook.go
index 8432f5fea..0763b443e 100644
--- a/model/incoming_webhook.go
+++ b/model/incoming_webhook.go
@@ -14,13 +14,15 @@ const (
)
type IncomingWebhook struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- DeleteAt int64 `json:"delete_at"`
- UserId string `json:"user_id"`
- ChannelId string `json:"channel_id"`
- TeamId string `json:"team_id"`
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ UserId string `json:"user_id"`
+ ChannelId string `json:"channel_id"`
+ TeamId string `json:"team_id"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
}
type IncomingWebhookRequest struct {
@@ -99,6 +101,14 @@ func (o *IncomingWebhook) IsValid() *AppError {
return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "")
}
+ if len(o.DisplayName) > 64 {
+ return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "")
+ }
+
+ if len(o.Description) > 128 {
+ return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "")
+ }
+
return nil
}
diff --git a/model/incoming_webhook_test.go b/model/incoming_webhook_test.go
index 5297d7d90..3f1754244 100644
--- a/model/incoming_webhook_test.go
+++ b/model/incoming_webhook_test.go
@@ -69,6 +69,26 @@ func TestIncomingWebhookIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.DisplayName = strings.Repeat("1", 65)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.DisplayName = strings.Repeat("1", 64)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Description = strings.Repeat("1", 129)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Description = strings.Repeat("1", 128)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestIncomingWebhookPreSave(t *testing.T) {
diff --git a/model/outgoing_webhook.go b/model/outgoing_webhook.go
index 70de4d26e..e13de9080 100644
--- a/model/outgoing_webhook.go
+++ b/model/outgoing_webhook.go
@@ -20,6 +20,8 @@ type OutgoingWebhook struct {
TeamId string `json:"team_id"`
TriggerWords StringArray `json:"trigger_words"`
CallbackURLs StringArray `json:"callback_urls"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
}
func (o *OutgoingWebhook) ToJson() string {
@@ -106,6 +108,14 @@ func (o *OutgoingWebhook) IsValid() *AppError {
}
}
+ if len(o.DisplayName) > 64 {
+ return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "")
+ }
+
+ if len(o.Description) > 128 {
+ return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "")
+ }
+
return nil
}
diff --git a/model/outgoing_webhook_test.go b/model/outgoing_webhook_test.go
index 665b85b6f..72c842e03 100644
--- a/model/outgoing_webhook_test.go
+++ b/model/outgoing_webhook_test.go
@@ -89,6 +89,26 @@ func TestOutgoingWebhookIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.DisplayName = strings.Repeat("1", 65)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.DisplayName = strings.Repeat("1", 64)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Description = strings.Repeat("1", 129)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Description = strings.Repeat("1", 128)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestOutgoingWebhookPreSave(t *testing.T) {
diff --git a/store/sql_command_store.go b/store/sql_command_store.go
index 074a6e588..02b3a0cc5 100644
--- a/store/sql_command_store.go
+++ b/store/sql_command_store.go
@@ -28,12 +28,14 @@ func NewSqlCommandStore(sqlStore *SqlStore) CommandStore {
tableo.ColMap("AutoCompleteDesc").SetMaxSize(1024)
tableo.ColMap("AutoCompleteHint").SetMaxSize(1024)
tableo.ColMap("DisplayName").SetMaxSize(64)
+ tableo.ColMap("Description").SetMaxSize(128)
}
return s
}
func (s SqlCommandStore) UpgradeSchemaIfNeeded() {
+ s.CreateColumnIfNotExists("Commands", "Description", "varchar(128)", "varchar(128)", "")
}
func (s SqlCommandStore) CreateIndexesIfNotExists() {
diff --git a/store/sql_webhook_store.go b/store/sql_webhook_store.go
index 740c9a33f..3c1f404e1 100644
--- a/store/sql_webhook_store.go
+++ b/store/sql_webhook_store.go
@@ -20,6 +20,8 @@ func NewSqlWebhookStore(sqlStore *SqlStore) WebhookStore {
table.ColMap("UserId").SetMaxSize(26)
table.ColMap("ChannelId").SetMaxSize(26)
table.ColMap("TeamId").SetMaxSize(26)
+ table.ColMap("DisplayName").SetMaxSize(64)
+ table.ColMap("Description").SetMaxSize(128)
tableo := db.AddTableWithName(model.OutgoingWebhook{}, "OutgoingWebhooks").SetKeys(false, "Id")
tableo.ColMap("Id").SetMaxSize(26)
@@ -29,12 +31,19 @@ func NewSqlWebhookStore(sqlStore *SqlStore) WebhookStore {
tableo.ColMap("TeamId").SetMaxSize(26)
tableo.ColMap("TriggerWords").SetMaxSize(1024)
tableo.ColMap("CallbackURLs").SetMaxSize(1024)
+ tableo.ColMap("DisplayName").SetMaxSize(64)
+ tableo.ColMap("Description").SetMaxSize(128)
}
return s
}
func (s SqlWebhookStore) UpgradeSchemaIfNeeded() {
+ s.CreateColumnIfNotExists("IncomingWebhooks", "DisplayName", "varchar(64)", "varchar(64)", "")
+ s.CreateColumnIfNotExists("IncomingWebhooks", "Description", "varchar(128)", "varchar(128)", "")
+
+ s.CreateColumnIfNotExists("OutgoingWebhooks", "DisplayName", "varchar(64)", "varchar(64)", "")
+ s.CreateColumnIfNotExists("OutgoingWebhooks", "Description", "varchar(128)", "varchar(128)", "")
}
func (s SqlWebhookStore) CreateIndexesIfNotExists() {
diff --git a/webapp/action_creators/global_actions.jsx b/webapp/action_creators/global_actions.jsx
index 9c38d8955..fd447ec93 100644
--- a/webapp/action_creators/global_actions.jsx
+++ b/webapp/action_creators/global_actions.jsx
@@ -335,4 +335,3 @@ export function emitRemoteUserTypingEvent(channelId, userId, postParentId) {
postParentId
});
}
-
diff --git a/webapp/components/backstage/add_incoming_webhook.jsx b/webapp/components/backstage/add_incoming_webhook.jsx
index 83027c6b3..f3601cb07 100644
--- a/webapp/components/backstage/add_incoming_webhook.jsx
+++ b/webapp/components/backstage/add_incoming_webhook.jsx
@@ -18,12 +18,12 @@ export default class AddIncomingWebhook extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
- this.updateName = this.updateName.bind(this);
+ this.updateDisplayName = this.updateDisplayName.bind(this);
this.updateDescription = this.updateDescription.bind(this);
this.updateChannelId = this.updateChannelId.bind(this);
this.state = {
- name: '',
+ displayName: '',
description: '',
channelId: '',
saving: false,
@@ -60,7 +60,9 @@ export default class AddIncomingWebhook extends React.Component {
}
const hook = {
- channel_id: this.state.channelId
+ channel_id: this.state.channelId,
+ display_name: this.state.displayName,
+ description: this.state.description
};
AsyncClient.addIncomingHook(
@@ -70,15 +72,16 @@ export default class AddIncomingWebhook extends React.Component {
},
(err) => {
this.setState({
+ saving: false,
serverError: err.message
});
}
);
}
- updateName(e) {
+ updateDisplayName(e) {
this.setState({
- name: e.target.value
+ displayName: e.target.value
});
}
@@ -112,20 +115,21 @@ export default class AddIncomingWebhook extends React.Component {
<div className='form-group'>
<label
className='control-label col-sm-3'
- htmlFor='name'
+ htmlFor='displayName'
>
<FormattedMessage
- id='add_incoming_webhook.name'
- defaultMessage='Name'
+ id='add_incoming_webhook.displayName'
+ defaultMessage='Display Name'
/>
</label>
<div className='col-md-5 col-sm-9'>
<input
- id='name'
+ id='displayName'
type='text'
+ maxLength='64'
className='form-control'
- value={this.state.name}
- onChange={this.updateName}
+ value={this.state.displayName}
+ onChange={this.updateDisplayName}
/>
</div>
</div>
@@ -143,6 +147,7 @@ export default class AddIncomingWebhook extends React.Component {
<input
id='description'
type='text'
+ maxLength='128'
className='form-control'
value={this.state.description}
onChange={this.updateDescription}
diff --git a/webapp/components/backstage/add_outgoing_webhook.jsx b/webapp/components/backstage/add_outgoing_webhook.jsx
index 5d98138df..ef57c6d05 100644
--- a/webapp/components/backstage/add_outgoing_webhook.jsx
+++ b/webapp/components/backstage/add_outgoing_webhook.jsx
@@ -18,14 +18,14 @@ export default class AddOutgoingWebhook extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
- this.updateName = this.updateName.bind(this);
+ this.updateDisplayName = this.updateDisplayName.bind(this);
this.updateDescription = this.updateDescription.bind(this);
this.updateChannelId = this.updateChannelId.bind(this);
this.updateTriggerWords = this.updateTriggerWords.bind(this);
this.updateCallbackUrls = this.updateCallbackUrls.bind(this);
this.state = {
- name: '',
+ displayName: '',
description: '',
channelId: '',
triggerWords: '',
@@ -80,7 +80,9 @@ export default class AddOutgoingWebhook extends React.Component {
const hook = {
channel_id: this.state.channelId,
trigger_words: this.state.triggerWords.split('\n').map((word) => word.trim()),
- callback_urls: this.state.callbackUrls.split('\n').map((url) => url.trim())
+ callback_urls: this.state.callbackUrls.split('\n').map((url) => url.trim()),
+ display_name: this.state.displayName,
+ description: this.state.description
};
AsyncClient.addOutgoingHook(
@@ -90,15 +92,16 @@ export default class AddOutgoingWebhook extends React.Component {
},
(err) => {
this.setState({
+ saving: false,
serverError: err.message
});
}
);
}
- updateName(e) {
+ updateDisplayName(e) {
this.setState({
- name: e.target.value
+ displayName: e.target.value
});
}
@@ -144,20 +147,21 @@ export default class AddOutgoingWebhook extends React.Component {
<div className='form-group'>
<label
className='control-label col-sm-3'
- htmlFor='name'
+ htmlFor='displayName'
>
<FormattedMessage
- id='add_outgoing_webhook.name'
- defaultMessage='Name'
+ id='add_outgoing_webhook.displayName'
+ defaultMessage='Display Name'
/>
</label>
<div className='col-md-5 col-sm-9'>
<input
- id='name'
+ id='displayName'
type='text'
+ maxLength='64'
className='form-control'
- value={this.state.name}
- onChange={this.updateName}
+ value={this.state.displayName}
+ onChange={this.updateDisplayName}
/>
</div>
</div>
@@ -175,6 +179,7 @@ export default class AddOutgoingWebhook extends React.Component {
<input
id='description'
type='text'
+ maxLength='128'
className='form-control'
value={this.state.description}
onChange={this.updateDescription}
@@ -213,6 +218,7 @@ export default class AddOutgoingWebhook extends React.Component {
<textarea
id='triggerWords'
rows='3'
+ maxLength='1000'
className='form-control'
value={this.state.triggerWords}
onChange={this.updateTriggerWords}
@@ -233,6 +239,7 @@ export default class AddOutgoingWebhook extends React.Component {
<textarea
id='callbackUrls'
rows='3'
+ maxLength='1000'
className='form-control'
value={this.state.callbackUrls}
onChange={this.updateCallbackUrls}
diff --git a/webapp/components/backstage/installed_incoming_webhook.jsx b/webapp/components/backstage/installed_incoming_webhook.jsx
index f65cf6327..95a303edc 100644
--- a/webapp/components/backstage/installed_incoming_webhook.jsx
+++ b/webapp/components/backstage/installed_incoming_webhook.jsx
@@ -39,7 +39,7 @@ export default class InstalledIncomingWebhook extends React.Component {
<div className='item-details'>
<div className='item-details__row'>
<span className='item-details__name'>
- {channelName}
+ {incomingWebhook.display_name || channelName}
</span>
<span className='item-details__type'>
<FormattedMessage
@@ -50,7 +50,19 @@ export default class InstalledIncomingWebhook extends React.Component {
</div>
<div className='item-details__row'>
<span className='item-details__description'>
- {Utils.getWindowLocationOrigin() + '/hooks/' + incomingWebhook.id}
+ {incomingWebhook.description}
+ </span>
+ </div>
+ <div className='tem-details__row'>
+ <span className='item-details__creation'>
+ <FormattedMessage
+ id='installed_integrations.creation'
+ defaultMessage='Created by {creator} on {createAt, date, full}'
+ values={{
+ creator: Utils.displayUsername(incomingWebhook.user_id),
+ createAt: incomingWebhook.create_at
+ }}
+ />
</span>
</div>
</div>
diff --git a/webapp/components/backstage/installed_outgoing_webhook.jsx b/webapp/components/backstage/installed_outgoing_webhook.jsx
index fee427260..530474dc3 100644
--- a/webapp/components/backstage/installed_outgoing_webhook.jsx
+++ b/webapp/components/backstage/installed_outgoing_webhook.jsx
@@ -47,7 +47,7 @@ export default class InstalledOutgoingWebhook extends React.Component {
<div className='item-details'>
<div className='item-details__row'>
<span className='item-details__name'>
- {channelName}
+ {outgoingWebhook.display_name || channelName}
</span>
<span className='item-details__type'>
<FormattedMessage
@@ -58,9 +58,19 @@ export default class InstalledOutgoingWebhook extends React.Component {
</div>
<div className='item-details__row'>
<span className='item-details__description'>
- {Utils.getWindowLocationOrigin() + '/hooks/' + outgoingWebhook.id}
- {' - '}
- {outgoingWebhook.token}
+ {outgoingWebhook.description}
+ </span>
+ </div>
+ <div className='item-details__row'>
+ <span className='item-details__creation'>
+ <FormattedMessage
+ id='installed_integrations.creation'
+ defaultMessage='Created by {creator} on {createAt, date, full}'
+ values={{
+ creator: Utils.displayUsername(outgoingWebhook.creator_id),
+ createAt: outgoingWebhook.create_at
+ }}
+ />
</span>
</div>
</div>
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 482aabc01..16d9ea536 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -26,10 +26,10 @@ import * as Utils from 'utils/utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Client from 'utils/client.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
+import {browserHistory} from 'react-router';
const ActionTypes = Constants.ActionTypes;
@@ -106,7 +106,7 @@ export default class ChannelHeader extends React.Component {
});
const townsquare = ChannelStore.getByName('town-square');
- GlobalActions.emitChannelClickEvent(townsquare);
+ browserHistory.push(Utils.getTeamURLNoOriginFromAddressBar() + '/channels/' + townsquare.name);
},
(err) => {
AsyncClient.dispatchError(err, 'handleLeave');
diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx
index 811bb8101..5ccf9c11e 100644
--- a/webapp/components/more_channels.jsx
+++ b/webapp/components/more_channels.jsx
@@ -9,9 +9,9 @@ import * as AsyncClient from 'utils/async_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import LoadingScreen from './loading_screen.jsx';
import NewChannelFlow from './new_channel_flow.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import {FormattedMessage} from 'react-intl';
+import {browserHistory} from 'react-router';
import loadingGif from 'images/load.gif';
@@ -65,7 +65,7 @@ export default class MoreChannels extends React.Component {
client.joinChannel(channel.id,
() => {
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- GlobalActions.emitChannelClickEvent(channel);
+ browserHistory.push(Utils.getTeamURLNoOriginFromAddressBar() + '/channels/' + channel.name);
this.setState({joiningChannel: -1});
},
(err) => {
diff --git a/webapp/components/more_direct_channels.jsx b/webapp/components/more_direct_channels.jsx
index 29d64517e..a83816c40 100644
--- a/webapp/components/more_direct_channels.jsx
+++ b/webapp/components/more_direct_channels.jsx
@@ -5,9 +5,9 @@ import {Modal} from 'react-bootstrap';
import FilteredUserList from './filtered_user_list.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import {FormattedMessage} from 'react-intl';
+import {browserHistory} from 'react-router';
import SpinnerButton from 'components/spinner_button.jsx';
import React from 'react';
@@ -69,7 +69,7 @@ export default class MoreDirectChannels extends React.Component {
Utils.openDirectChannelToUser(
teammate,
(channel) => {
- GlobalActions.emitChannelClickEvent(channel);
+ browserHistory.push(Utils.getTeamURLNoOriginFromAddressBar() + '/channels/' + channel.name);
this.setState({loadingDMChannel: -1});
this.handleHide();
},
diff --git a/webapp/components/new_channel_flow.jsx b/webapp/components/new_channel_flow.jsx
index 8c66ef3ce..82494dac0 100644
--- a/webapp/components/new_channel_flow.jsx
+++ b/webapp/components/new_channel_flow.jsx
@@ -4,25 +4,21 @@
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import UserStore from 'stores/user_store.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import NewChannelModal from './new_channel_modal.jsx';
import ChangeURLModal from './change_url_modal.jsx';
import {intlShape, injectIntl, defineMessages} from 'react-intl';
+import {browserHistory} from 'react-router';
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
const SHOW_NEW_CHANNEL = 1;
const SHOW_EDIT_URL = 2;
const SHOW_EDIT_URL_THEN_COMPLETE = 3;
const messages = defineMessages({
- invalidName: {
- id: 'channel_flow.invalidName',
- defaultMessage: 'Invalid Channel Name'
- },
- alreadyExist: {
- id: 'channel_flow.alreadyExist',
- defaultMessage: 'A channel with that URL already exists'
- },
channel: {
id: 'channel_flow.channel',
defaultMessage: 'Channel'
@@ -87,37 +83,51 @@ class NewChannelFlow extends React.Component {
}
}
doSubmit() {
- var channel = {};
-
- const {formatMessage} = this.props.intl;
- channel.display_name = this.state.channelDisplayName;
- if (!channel.display_name) {
- this.setState({serverError: formatMessage(messages.invalidName)});
+ if (!this.state.channelDisplayName) {
+ this.setState({serverError: Utils.localizeMessage('channel_flow.invalidName', 'Invalid Channel Name')});
return;
}
- channel.name = this.state.channelName;
- if (channel.name.length < 2) {
+ if (this.state.channelName < 2) {
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
return;
}
const cu = UserStore.getCurrentUser();
- channel.team_id = cu.team_id;
- channel.purpose = this.state.channelPurpose;
- channel.type = this.state.channelType;
-
- Client.createChannel(channel,
+ const channel = {
+ team_id: cu.team_id,
+ name: this.state.channelName,
+ display_name: this.state.channelDisplayName,
+ purpose: this.state.channelPurpose,
+ type: this.state.channelType
+ };
+ Client.createChannel(
+ channel,
(data) => {
- this.props.onModalDismissed();
- GlobalActions.emitChannelClickEvent(data);
+ Client.getChannel(
+ data.id,
+ (data2, textStatus, xhr) => {
+ if (xhr.status === 304 || !data2) {
+ return;
+ }
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_CHANNEL,
+ channel: data2.channel,
+ member: data2.member
+ });
+
+ this.props.onModalDismissed();
+ browserHistory.push(Utils.getTeamURLNoOriginFromAddressBar() + '/channels/' + data2.channel.name);
+ }
+ );
},
(err) => {
if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
}
if (err.id === 'store.sql_channel.update.exists.app_error') {
- this.setState({serverError: formatMessage(messages.alreadyExist)});
+ this.setState({serverError: Utils.localizeMessage('channel_flow.alreadyExist', 'A channel with that URL already exists')});
return;
}
this.setState({serverError: err.message});
diff --git a/webapp/components/popover_list_members.jsx b/webapp/components/popover_list_members.jsx
index 226a1889c..8c3c381af 100644
--- a/webapp/components/popover_list_members.jsx
+++ b/webapp/components/popover_list_members.jsx
@@ -6,10 +6,10 @@ import $ from 'jquery';
import UserStore from 'stores/user_store.jsx';
import {Popover, Overlay} from 'react-bootstrap';
import * as Utils from 'utils/utils.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
+import {browserHistory} from 'react-router';
import React from 'react';
@@ -35,7 +35,7 @@ export default class PopoverListMembers extends React.Component {
Utils.openDirectChannelToUser(
teammate,
(channel, channelAlreadyExisted) => {
- GlobalActions.emitChannelClickEvent(channel);
+ browserHistory.push(Utils.getTeamURLNoOriginFromAddressBar() + '/channels/' + channel.name);
if (channelAlreadyExisted) {
this.closePopover();
}
diff --git a/webapp/components/removed_from_channel_modal.jsx b/webapp/components/removed_from_channel_modal.jsx
index 45018ac99..d037c089d 100644
--- a/webapp/components/removed_from_channel_modal.jsx
+++ b/webapp/components/removed_from_channel_modal.jsx
@@ -6,9 +6,10 @@ import ReactDOM from 'react-dom';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
import BrowserStore from 'stores/browser_store.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
+import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
+import {browserHistory} from 'react-router';
import React from 'react';
@@ -33,7 +34,11 @@ export default class RemovedFromChannelModal extends React.Component {
}
var townSquare = ChannelStore.getByName('town-square');
- setTimeout(() => GlobalActions.emitChannelClickEvent(townSquare), 1);
+ setTimeout(
+ () => {
+ browserHistory.push(Utils.getTeamURLNoOriginFromAddressBar() + '/channels/' + townSquare.name);
+ },
+ 1);
this.setState(newState);
}
diff --git a/webapp/components/rename_channel_modal.jsx b/webapp/components/rename_channel_modal.jsx
index ced3c2d2b..3e47847e7 100644
--- a/webapp/components/rename_channel_modal.jsx
+++ b/webapp/components/rename_channel_modal.jsx
@@ -4,7 +4,7 @@
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
@@ -165,7 +165,7 @@ export default class RenameChannelModal extends React.Component {
Client.updateChannel(
channel,
() => {
- GlobalActions.emitChannelClickEvent(channel);
+ AsyncClient.getChannel(channel.id);
this.handleHide();
},
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 7dc6486ab..8fdb42b4c 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -814,6 +814,7 @@
"get_team_invite_link_modal.title": "Team Invite Link",
"installed_integrations.add": "Add Integration",
"installed_integrations.allFilter": "All ({count})",
+ "installed_integrations.creation": "Created by {creator} on {createAt, date, full}",
"installed_integrations.delete": "Delete",
"installed_integrations.header": "Installed Integrations",
"installed_integrations.incomingWebhookType": "(Incoming Webhook)",
diff --git a/webapp/root.jsx b/webapp/root.jsx
index 417a13659..c88b0a7b3 100644
--- a/webapp/root.jsx
+++ b/webapp/root.jsx
@@ -180,6 +180,12 @@ function doChannelChange(state) {
channel = JSON.parse(state.location.query.fakechannel);
} else {
channel = ChannelStore.getByName(state.params.channel);
+ if (!channel) {
+ channel = ChannelStore.getMoreByName(state.params.channel);
+ }
+ if (!channel) {
+ console.error('Unable to get channel to change to.'); //eslint-disable-line no-console
+ }
}
GlobalActions.emitChannelClickEvent(channel);
}
diff --git a/webapp/sass/routes/_backstage.scss b/webapp/sass/routes/_backstage.scss
index 729c8c912..13c51c8b4 100644
--- a/webapp/sass/routes/_backstage.scss
+++ b/webapp/sass/routes/_backstage.scss
@@ -196,7 +196,12 @@
margin-bottom: 1em;
}
- .list-item__actions {
+ .item-details__creation {
+ color: $dark-gray;
+ margin-bottom: 1em;
+ }
+
+ .item-actions {
flex-grow: 0;
flex-shrink: 0;
padding-left: 20px;
diff --git a/webapp/stores/channel_store.jsx b/webapp/stores/channel_store.jsx
index b2946e326..9437d5e44 100644
--- a/webapp/stores/channel_store.jsx
+++ b/webapp/stores/channel_store.jsx
@@ -95,7 +95,12 @@ class ChannelStoreClass extends EventEmitter {
this.removeListener(LEAVE_EVENT, callback);
}
findFirstBy(field, value) {
- var channels = this.getChannels();
+ return this.doFindFirst(field, value, this.getChannels());
+ }
+ findFirstMoreBy(field, value) {
+ return this.doFindFirst(field, value, this.getMoreChannels());
+ }
+ doFindFirst(field, value, channels) {
for (var i = 0; i < channels.length; i++) {
if (channels[i][field] === value) {
return channels[i];
@@ -113,6 +118,9 @@ class ChannelStoreClass extends EventEmitter {
getByName(name) {
return this.findFirstBy('name', name);
}
+ getMoreByName(name) {
+ return this.findFirstMoreBy('name', name);
+ }
getAll() {
return this.getChannels();
}
diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx
index cc19baa7e..db0b2258c 100644
--- a/webapp/utils/async_client.jsx
+++ b/webapp/utils/async_client.jsx
@@ -1182,10 +1182,10 @@ export function addIncomingHook(hook, success, error) {
}
},
(err) => {
- dispatchError(err, 'addIncomingHook');
-
if (error) {
error(err);
+ } else {
+ dispatchError(err, 'addIncomingHook');
}
}
);
@@ -1205,10 +1205,10 @@ export function addOutgoingHook(hook, success, error) {
}
},
(err) => {
- dispatchError(err, 'addOutgoingHook');
-
if (error) {
error(err);
+ } else {
+ dispatchError(err, 'addOutgoingHook');
}
}
);
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 3b7583f15..399dd0985 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -3,7 +3,6 @@
import $ from 'jquery';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
import LocalizationStore from 'stores/localization_store.jsx';
@@ -168,7 +167,7 @@ export function notifyMe(title, body, channel) {
notification.onclick = () => {
window.focus();
if (channel) {
- GlobalActions.emitChannelClickEvent(channel);
+ browserHistory.push(getTeamURLNoOriginFromAddressBar() + '/channels/' + channel.name);
} else {
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
}
@@ -1216,6 +1215,10 @@ export function getTeamURLFromAddressBar() {
return window.location.origin + '/' + window.location.pathname.split('/')[1];
}
+export function getTeamURLNoOriginFromAddressBar() {
+ return '/' + window.location.pathname.split('/')[1];
+}
+
export function getShortenedTeamURL() {
const teamURL = getTeamURLFromAddressBar();
if (teamURL.length > 35) {
@@ -1261,10 +1264,24 @@ export function openDirectChannelToUser(user, successCb, errorCb) {
channel,
user.id,
(data) => {
- AsyncClient.getChannel(data.id);
- if ($.isFunction(successCb)) {
- successCb(data, false);
- }
+ Client.getChannel(
+ data.id,
+ (data2, textStatus, xhr) => {
+ if (xhr.status === 304 || !data2) {
+ return;
+ }
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_CHANNEL,
+ channel: data2.channel,
+ member: data2.member
+ });
+
+ if ($.isFunction(successCb)) {
+ successCb(data2.channel, false);
+ }
+ }
+ );
},
() => {
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/' + channelName);