summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile9
-rw-r--r--api/command.go73
-rw-r--r--api/command_test.go56
-rw-r--r--doc/README.md20
-rw-r--r--doc/help/Markdown.md180
-rw-r--r--doc/help/README.md18
-rw-r--r--doc/integrations/Single-Sign-On/Gitlab.md2
-rw-r--r--doc/process/overview.md2
-rw-r--r--doc/usage/Markdown.md179
-rw-r--r--web/react/components/channel_header.jsx27
-rw-r--r--web/react/components/command_list.jsx2
-rw-r--r--web/react/components/delete_post_modal.jsx9
-rw-r--r--web/react/components/edit_channel_header_modal.jsx126
-rw-r--r--web/react/components/edit_channel_modal.jsx150
-rw-r--r--web/react/components/edit_post_modal.jsx4
-rw-r--r--web/react/components/get_link_modal.jsx144
-rw-r--r--web/react/components/get_team_invite_link_modal.jsx45
-rw-r--r--web/react/components/invite_member_modal.jsx36
-rw-r--r--web/react/components/member_list_team.jsx47
-rw-r--r--web/react/components/navbar.jsx56
-rw-r--r--web/react/components/navbar_dropdown.jsx19
-rw-r--r--web/react/components/post_info.jsx4
-rw-r--r--web/react/components/rhs_comment.jsx4
-rw-r--r--web/react/components/rhs_root_post.jsx84
-rw-r--r--web/react/components/sidebar_right_menu.jsx28
-rw-r--r--web/react/components/team_members.jsx130
-rw-r--r--web/react/components/team_members_modal.jsx69
-rw-r--r--web/react/components/toggle_modal_button.jsx17
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx4
-rw-r--r--web/react/dispatcher/event_helpers.jsx23
-rw-r--r--web/react/pages/channel.jsx18
-rw-r--r--web/react/stores/modal_store.jsx1
-rw-r--r--web/react/stores/team_store.jsx11
-rw-r--r--web/react/utils/channel_intro_mssages.jsx72
-rw-r--r--web/react/utils/constants.jsx3
-rw-r--r--web/react/utils/utils.jsx17
-rw-r--r--web/templates/channel.html11
37 files changed, 912 insertions, 788 deletions
diff --git a/Makefile b/Makefile
index 8e7cfe3ed..b3875ffc7 100644
--- a/Makefile
+++ b/Makefile
@@ -52,9 +52,11 @@ start-docker:
echo starting mattermost-postgres; \
docker run --name mattermost-postgres -p 5432:5432 -e POSTGRES_USER=mmuser -e POSTGRES_PASSWORD=mostest \
-d postgres:9.4 > /dev/null; \
+ sleep 10; \
elif [ $(shell docker ps | grep -ci mattermost-postgres) -eq 0 ]; then \
echo restarting mattermost-postgres; \
docker start mattermost-postgres > /dev/null; \
+ sleep 10; \
fi
build-server:
@@ -74,9 +76,10 @@ build-server:
fi
cp ./model/version.go ./model/version.go.bak
- sed -i 's|_BUILD_NUMBER_|$(BUILD_NUMBER)|g' ./model/version.go
- sed -i 's|_BUILD_DATE_|$(BUILD_DATE)|g' ./model/version.go
- sed -i 's|_BUILD_HASH_|$(BUILD_HASH)|g' ./model/version.go
+ sed -i'.make_mac_work' 's|_BUILD_NUMBER_|$(BUILD_NUMBER)|g' ./model/version.go
+ sed -i'.make_mac_work' 's|_BUILD_DATE_|$(BUILD_DATE)|g' ./model/version.go
+ sed -i'.make_mac_work' 's|_BUILD_HASH_|$(BUILD_HASH)|g' ./model/version.go
+ rm ./model/version.go.make_mac_work
$(GO) build $(GOFLAGS) ./...
$(GO) install $(GOFLAGS) ./...
diff --git a/api/command.go b/api/command.go
index 50ca41155..db57f0bae 100644
--- a/api/command.go
+++ b/api/command.go
@@ -4,7 +4,9 @@
package api
import (
+ "io"
"net/http"
+ "path"
"strconv"
"strings"
"time"
@@ -325,6 +327,9 @@ func loadTestCommand(c *Context, command *model.Command) bool {
if loadTestPostsCommand(c, command) {
return true
}
+ if loadTestUrlCommand(c, command) {
+ return true
+ }
} else if strings.Index(cmd, command.Command) == 0 {
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Debug Load Testing"})
}
@@ -571,3 +576,71 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool {
return false
}
+
+func loadTestUrlCommand(c *Context, command *model.Command) bool {
+ cmd := cmds["loadTestCommand"] + " url"
+
+ if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
+ url := ""
+
+ parameters := strings.SplitN(command.Command, " ", 3)
+ if len(parameters) != 3 {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Command must contain a url", "")
+ return true
+ } else {
+ url = parameters[2]
+ }
+
+ // provide a shortcut to easily access tests stored in doc/developer/tests
+ if !strings.HasPrefix(url, "http") {
+ url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
+
+ if path.Ext(url) == "" {
+ url += ".md"
+ }
+ }
+
+ var contents io.ReadCloser
+ if r, err := http.Get(url); err != nil {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Unable to get file", err.Error())
+ return false
+ } else if r.StatusCode > 400 {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Unable to get file", r.Status)
+ return false
+ } else {
+ contents = r.Body
+ }
+
+ bytes := make([]byte, 4000)
+
+ // break contents into 4000 byte posts
+ for {
+ length, err := contents.Read(bytes)
+ if err != nil && err != io.EOF {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Encountered error reading file", err.Error())
+ return false
+ }
+
+ if length == 0 {
+ break
+ }
+
+ post := &model.Post{}
+ post.Message = string(bytes[:length])
+ post.ChannelId = command.ChannelId
+
+ if _, err := CreatePost(c, post, false); err != nil {
+ l4g.Error("Unable to create post, err=%v", err)
+ return false
+ }
+ }
+
+ command.Response = model.RESP_EXECUTED
+
+ return true
+ } else if strings.Index(cmd, command.Command) == 0 && strings.Index(command.Command, "/loadtest posts") != 0 {
+ command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Add a post containing the text from a given url to current channel <Url>"})
+ }
+
+ return false
+}
diff --git a/api/command_test.go b/api/command_test.go
index 476748c6b..9327850f3 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -10,6 +10,7 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
)
func TestSuggestRootCommands(t *testing.T) {
@@ -184,3 +185,58 @@ func TestEchoCommand(t *testing.T) {
t.Fatal("Echo command failed to send")
}
}
+
+func TestLoadTestUrlCommand(t *testing.T) {
+ Setup()
+
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
+
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ command := "/loadtest url "
+ if _, err := Client.Command(channel.Id, command, false); err == nil {
+ t.Fatal("/loadtest url with no url should've failed")
+ }
+
+ command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
+ if _, err := Client.Command(channel.Id, command, false); err == nil {
+ t.Fatal("/loadtest url with invalid url should've failed")
+ }
+
+ command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for README.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for test-emoticons.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for test-emoticons should've executed")
+ }
+
+ posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
+ // note that this may make more than 3 posts if files are too long to fit in an individual post
+ if len(posts.Order) < 3 {
+ t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
+ }
+}
diff --git a/doc/README.md b/doc/README.md
index d062dee65..15d1d731b 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -51,22 +51,4 @@ Procedures for upgrading the Mattermost server
## Help
-_Note: End user help documentation is a new feature being completed for the v1.2 release. The materials below are work in progress._
-
-- Getting Started
- - [Sign-in](help/Sign-in.md)
-
-- User Interface
- - Main Menu
- - [Team Settings ](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md)
- - [General Settings](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#general)
- - [Slack Import](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#import-from-slack-beta)
- - [Manage Members](help/Manage-Members.md)
- - Messaging
- - [Mattermost Markdown Formatting](usage/Markdown.md)
- - [Search](help/Search.md)
-
-- System Console
- - Team
- - [Team Statistics](help/system-console/Team-Statistics.md)
-
+See [End User Help](help/README.md).
diff --git a/doc/help/Markdown.md b/doc/help/Markdown.md
new file mode 100644
index 000000000..1befed8d4
--- /dev/null
+++ b/doc/help/Markdown.md
@@ -0,0 +1,180 @@
+# Markdown Help
+
+Markdown makes it easy to format messages. Type a message as you normally would, and use these rules to render it with special formatting.
+
+## Text Style:
+
+You can use either `_` or `*` around a word to make it italic. Use two to make it bold.
+
+* `_italics_` renders as _italics_
+* `**bold**` renders as **bold**
+* `**_bold-italic_**` renders as **_bold-italics_**
+* `~~strikethrough~~` renders as ~~strikethrough~~
+
+## Code Block:
+
+Create a code block by indenting each line by four spaces, or by placing ``` on the line above and below your code.
+
+Example:
+
+ ```
+ code block
+ ```
+
+Renders as:
+```
+code block
+```
+
+### Syntax Highlighting
+
+To add syntax highlighting, type the language to be highlighted after the ``` at the beginning of the code block.
+
+Supported languages are:
+`diff, apache, makefile, http, json, markdown, javascript, css, nginx, objectivec, python, xml, perl, bash, php, coffee (CoffeeScript), cs (C#), cpp (C++), sql, go, ruby, java, ini, latex`
+
+Example:
+
+ ``` go
+ package main
+ import "fmt"
+ func main() {
+ fmt.Println("Hello, 世界")
+ }
+ ```
+
+Renders as:
+``` go
+package main
+import "fmt"
+func main() {
+ fmt.Println("Hello, 世界")
+}
+```
+
+## In-line Code:
+
+Create in-line monospaced font by surrounding it with backticks.
+```
+`monospace`
+```
+Renders as: `monospace`.
+
+## Links:
+
+Create labeled links by putting the desired text in square brackets and the associated link in normal brackets.
+
+`[Check out Mattermost!](www.mattermost.com)`
+
+Renders as: [Check out Mattermost!](www.mattermost.com)
+
+## In-line Images
+
+Create in-line images using an `!` followed by the alt text in square brackets and the link in normal brackets. Add hover text by placing it in quotes after the link.
+```
+![alt text](link "hover text")
+
+and
+
+[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
+```
+Renders as:
+
+![alt text](link "hover text")
+
+and
+
+[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
+
+## Emojis
+
+Check out a full list of emojis [here](http://www.emoji-cheat-sheet.com/).
+
+```
+:smile: :+1: :sheep:
+```
+Renders as:
+:smile: :+1: :sheep:
+
+## Lines:
+
+Create a line by using three `*`, `_`, or `-`.
+
+`***` renders as:
+***
+
+## Block quotes:
+
+Create block quotes using `>`.
+
+`> block quotes` renders as:
+> block quotes
+
+## Lists:
+
+Create a list by using `*` or `-` as bullets. Indent a bullet point by adding two spaces in front of it.
+```
+* list item one
+* list item two
+ * item two sub-point
+```
+Renders as:
+* list item one
+* list item two
+ * item two sub-point
+
+Make it an ordered list by using numbers instead:
+```
+1. Item one
+2. Item two
+```
+Renders as:
+1. Item one
+2. Item two
+
+## Tables:
+
+Create a table by placing a dashed line under the header row and separating the columns with a pipe `|`. (The columns don’t need to line up exactly for it to work). Choose how to align table columns by including colons `:` within the header row.
+```
+| Left-Aligned | Center Aligned | Right Aligned |
+| :------------ |:---------------:| -----:|
+| Left column 1 | this text | $100 |
+| Left column 2 | is | $10 |
+| Left column 3 | centered | $1 |
+```
+
+Renders as:
+
+| Left-Aligned | Center Aligned | Right Aligned |
+| :------------ |:---------------:| -----:|
+| Left column 1 | this text | $100 |
+| Left column 2 | is | $10 |
+| Left column 3 | centered | $1 |
+
+## Headings:
+
+Make a heading by typing # and a space before your title. For smaller headings, use more #’s.
+```
+# Large heading
+## Smaller heading
+### Even smaller heading
+```
+Renders as:
+# Large Heading
+## Smaller Heading
+### Even smaller heading
+
+Alternatively, for the large heading you can underline the text using `===`. For the smaller heading you can underline using `---`
+```
+Large Heading
+=============
+
+Smaller Heading
+--------------
+```
+Renders as:
+Large Heading
+=============
+
+Smaller Heading
+--------------
diff --git a/doc/help/README.md b/doc/help/README.md
new file mode 100644
index 000000000..3b3c1709b
--- /dev/null
+++ b/doc/help/README.md
@@ -0,0 +1,18 @@
+## Help
+
+- Getting Started
+ - [Sign-in](Sign-in.md)
+
+- User Interface
+ - Main Menu
+ - [Team Settings ](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md)
+ - [General Settings](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#general)
+ - [Slack Import](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#import-from-slack-beta)
+ - [Manage Members](Manage-Members.md)
+ - Messaging
+ - [Mattermost Markdown Formatting](help/Markdown.md)
+ - [Search](Search.md)
+
+- System Console
+ - Team
+ - [Team Statistics](system-console/Team-Statistics.md)
diff --git a/doc/integrations/Single-Sign-On/Gitlab.md b/doc/integrations/Single-Sign-On/Gitlab.md
index 1242fd13e..f0acc0e66 100644
--- a/doc/integrations/Single-Sign-On/Gitlab.md
+++ b/doc/integrations/Single-Sign-On/Gitlab.md
@@ -9,7 +9,7 @@ Follow these steps to configure Mattermost to use GitLab as a single-sign-on (SS
(Note: If your GitLab instance is set up to use SSL, your URIs must begin with https://. Otherwise, use http://).
-3. Submit the application and copy the given _Id_ and _Secret_ into the appropriate _SSOSettings_ fields in config/config.json
+3. Submit the application and copy the given _Id_ and _Secret_ into the appropriate _GitLabSettings_ fields in config/config.json
4. Also in config/config.json, set _Enable_ to `true` for the _gitlab_ section, leave _Scope_ blank and use the following for the endpoints:
* _AuthEndpoint_: `https://<your-gitlab-url>/oauth/authorize` (example https://example.com/oauth/authorize)
diff --git a/doc/process/overview.md b/doc/process/overview.md
index ca15de3a7..8109163d9 100644
--- a/doc/process/overview.md
+++ b/doc/process/overview.md
@@ -136,8 +136,6 @@ Examples:
- [Do not clear LastActivityAt for GetProfiles #1396](https://github.com/mattermost/platform/pull/1396/files)
- [Update to proxy_pass #1331](https://github.com/mattermost/platform/pull/1331)
-In general, it's
-
## Release
Mattermost ships stable releases on the 16th of the month. Releases begin with a planning process reviewing internal designs and community feedback in the context of the product purpose. Feature development is done in weekly sprints, and releases end with feature complete, stablization, code complete and release candidate milestones prior to final release.
diff --git a/doc/usage/Markdown.md b/doc/usage/Markdown.md
index dd90ede19..65e6f2121 100644
--- a/doc/usage/Markdown.md
+++ b/doc/usage/Markdown.md
@@ -1,180 +1,3 @@
# Markdown Help
-Markdown makes it easy to format messages. Type a message as you normally would, and use these rules to render it with special formatting.
-
-## Text Style:
-
-You can use either `_` or `*` around a word to make it italic. Use two to make it bold.
-
-* `_italics_` renders as _italics_
-* `**bold**` renders as **bold**
-* `**_bold-italic_**` renders as **_bold-italics_**
-* `~~strikethrough~~` renders as ~~strikethrough~~
-
-## Code Block:
-
-Create a code block by indenting each line by four spaces, or by placing ``` on the line above and below your code.
-
-Example:
-
- ```
- code block
- ```
-
-Renders as:
-```
-code block
-```
-
-### Syntax Highlighting
-
-To add syntax highlighting, type the language to be highlighted after the ``` at the beginning of the code block.
-
-Supported languages are:
-`diff, apache, makefile, http, json, markdown, javascript, css, nginx, objectivec, python, xml, perl, bash, php, coffee (CoffeeScript), cs (C#), cpp (C++), sql, go, ruby, java, ini, latex`
-
-Example:
-
- ``` go
- package main
- import "fmt"
- func main() {
- fmt.Println("Hello, 世界")
- }
- ```
-
-Renders as:
-``` go
-package main
-import "fmt"
-func main() {
- fmt.Println("Hello, 世界")
-}
-```
-
-## In-line Code:
-
-Create in-line monospaced font by surrounding it with backticks.
-```
-`monospace`
-```
-Renders as: `monospace`.
-
-## Links:
-
-Create labeled links by putting the desired text in square brackets and the associated link in normal brackets.
-
-`[Check out Mattermost!](www.mattermost.com)`
-
-Renders as: [Check out Mattermost!](www.mattermost.com)
-
-## In-line Images
-
-Create in-line images using an `!` followed by the alt text in square brackets and the link in normal brackets. Add hover text by placing it in quotes after the link.
-```
-![alt text](link "hover text")
-
-and
-
-[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
-```
-Renders as:
-
-![alt text](link "hover text")
-
-and
-
-[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
-
-## Emojis
-
-Check out a full list of emojis [here](http://www.emoji-cheat-sheet.com/).
-
-```
-:smile: :+1: :sheep:
-```
-Renders as:
-:smile: :+1: :sheep:
-
-## Lines:
-
-Create a line by using three `*`, `_`, or `-`.
-
-`***` renders as:
-***
-
-## Block quotes:
-
-Create block quotes using `>`.
-
-`> block quotes` renders as:
-> block quotes
-
-## Lists:
-
-Create a list by using `*` or `-` as bullets. Indent a bullet point by adding two spaces in front of it.
-```
-* list item one
-* list item two
- * item two sub-point
-```
-Renders as:
-* list item one
-* list item two
- * item two sub-point
-
-Make it an ordered list by using numbers instead:
-```
-1. Item one
-2. Item two
-```
-Renders as:
-1. Item one
-2. Item two
-
-## Tables:
-
-Create a table by placing a dashed line under the header row and separating the columns with a pipe `|`. (The columns don’t need to line up exactly for it to work). Choose how to align table columns by including colons `:` within the header row.
-```
-| Left-Aligned  | Center Aligned  | Right Aligned |
-| :------------ |:---------------:| -----:|
-| Left column 1 | this text       |  $100 |
-| Left column 2 | is              |   $10 |
-| Left column 3 | centered        |    $1 |
-```
-
-Renders as:
-
-| Left-Aligned  | Center Aligned  | Right Aligned |
-| :------------ |:---------------:| -----:|
-| Left column 1 | this text       |  $100 |
-| Left column 2 | is              |   $10 |
-| Left column 3 | centered        |    $1 |
-
-## Headings:
-
-Make a heading by typing # and a space before your title. For smaller headings, use more #’s.
-```
-# Large heading
-## Smaller heading
-### Even smaller heading
-```
-Renders as:
-# Large Heading
-## Smaller Heading
-### Even smaller heading
-
-Alternatively, for the large heading you can underline the text using `===`. For the smaller heading you can underline using `---`
-```
-Large Heading
-=============
-
-Smaller Heading
---------------
-```
-Renders as:
-Large Heading
-=============
-
-Smaller Heading
---------------
+Moved to [help/Markdown.md](../help/Markdown.md)
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 8c721348f..6e12c7c14 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -4,6 +4,7 @@
import NavbarSearchBox from './search_bar.jsx';
import MessageWrapper from './message_wrapper.jsx';
import PopoverListMembers from './popover_list_members.jsx';
+import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
import ChannelInfoModal from './channel_info_modal.jsx';
import ChannelInviteModal from './channel_invite_modal.jsx';
@@ -167,17 +168,13 @@ export default class ChannelHeader extends React.Component {
key='edit_header_direct'
role='presentation'
>
- <a
+ <ToggleModalButton
role='menuitem'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelHeaderModal}
+ dialogProps={{channel}}
>
{'Set Channel Header...'}
- </a>
+ </ToggleModalButton>
</li>
);
} else {
@@ -235,17 +232,13 @@ export default class ChannelHeader extends React.Component {
key='set_channel_header'
role='presentation'
>
- <a
+ <ToggleModalButton
role='menuitem'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelHeaderModal}
+ dialogProps={{channel}}
>
- {'Set '}{channelTerm}{' Header...'}
- </a>
+ {`Set ${channelTerm} Header...`}
+ </ToggleModalButton>
</li>
);
dropdownContents.push(
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx
index ff83d0420..7fc0f79cf 100644
--- a/web/react/components/command_list.jsx
+++ b/web/react/components/command_list.jsx
@@ -81,7 +81,7 @@ export default class CommandList extends React.Component {
<div
ref='mentionlist'
className='command-box'
- style={{height: (this.state.suggestions.length * 56) + 2}}
+ style={{height: (suggestions.length * 56) + 2}}
>
{suggestions}
</div>
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
index fab5b60ea..3c4b17905 100644
--- a/web/react/components/delete_post_modal.jsx
+++ b/web/react/components/delete_post_modal.jsx
@@ -159,13 +159,4 @@ export default class DeletePostModal extends React.Component {
</Modal>
);
}
-
- static show(post, commentCount) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_DELETE_POST_MODAL,
- value: true,
- post,
- commentCount: commentCount || 0
- });
- }
}
diff --git a/web/react/components/edit_channel_header_modal.jsx b/web/react/components/edit_channel_header_modal.jsx
new file mode 100644
index 000000000..5529a419d
--- /dev/null
+++ b/web/react/components/edit_channel_header_modal.jsx
@@ -0,0 +1,126 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as Client from '../utils/client.jsx';
+import * as AsyncClient from '../utils/async_client.jsx';
+import * as Utils from '../utils/utils.jsx';
+
+const Modal = ReactBootstrap.Modal;
+
+export default class EditChannelHeaderModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleEdit = this.handleEdit.bind(this);
+
+ this.onShow = this.onShow.bind(this);
+ this.onHide = this.onHide.bind(this);
+
+ this.state = {
+ serverError: ''
+ };
+ }
+
+ componentDidMount() {
+ if (this.props.show) {
+ this.onShow();
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.onShow();
+ }
+ }
+
+ handleEdit() {
+ var data = {};
+ data.channel_id = this.props.channel.id;
+
+ if (data.channel_id.length !== 26) {
+ return;
+ }
+
+ data.channel_header = ReactDOM.findDOMNode(this.refs.textarea).value;
+
+ Client.updateChannelHeader(data,
+ () => {
+ this.setState({serverError: ''});
+ AsyncClient.getChannel(this.props.channel.id);
+ this.onHide();
+ },
+ (err) => {
+ if (err.message === 'Invalid channel_header parameter') {
+ this.setState({serverError: 'This channel header is too long, please enter a shorter one'});
+ } else {
+ this.setState({serverError: err.message});
+ }
+ }
+ );
+ }
+
+ onShow() {
+ const textarea = ReactDOM.findDOMNode(this.refs.textarea);
+ Utils.placeCaretAtEnd(textarea);
+ }
+
+ onHide() {
+ this.setState({
+ serverError: ''
+ });
+
+ this.props.onHide();
+ }
+
+ render() {
+ var serverError = null;
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
+ }
+
+ return (
+ <Modal
+ show={this.props.show}
+ onHide={this.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {'Edit Header for ' + this.props.channel.display_name}
+ </Modal.Header>
+ <Modal.Body>
+ <p>{'Edit the text appearing next to the channel name in the channel header.'}</p>
+ <textarea
+ ref='textarea'
+ className='form-control no-resize'
+ rows='6'
+ id='edit_header'
+ maxLength='1024'
+ defaultValue={this.props.channel.header}
+ />
+ {serverError}
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.props.onHide}
+ >
+ {'Cancel'}
+ </button>
+ <button
+ type='button'
+ className='btn btn-primary'
+ onClick={this.handleEdit}
+ >
+ {'Save'}
+ </button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+EditChannelHeaderModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired,
+ channel: React.PropTypes.object.isRequired
+};
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
deleted file mode 100644
index 80dab4a57..000000000
--- a/web/react/components/edit_channel_modal.jsx
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-
-export default class EditChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleEdit = this.handleEdit.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleClose = this.handleClose.bind(this);
- this.onShow = this.onShow.bind(this);
- this.handleShown = this.handleShown.bind(this);
-
- this.state = {
- header: '',
- title: '',
- channelId: '',
- serverError: ''
- };
- }
- handleEdit() {
- var data = {};
- data.channel_id = this.state.channelId;
-
- if (data.channel_id.length !== 26) {
- return;
- }
-
- data.channel_header = this.state.header.trim();
-
- Client.updateChannelHeader(data,
- () => {
- this.setState({serverError: ''});
- AsyncClient.getChannel(this.state.channelId);
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- },
- (err) => {
- if (err.message === 'Invalid channel_header parameter') {
- this.setState({serverError: 'This channel header is too long, please enter a shorter one'});
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- }
- handleUserInput(e) {
- this.setState({header: e.target.value});
- }
- handleClose() {
- this.setState({header: '', serverError: ''});
- }
- onShow(e) {
- const button = e.relatedTarget;
- this.setState({header: $(button).attr('data-header'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
- }
- handleShown() {
- $('#edit_channel #edit_header').focus();
- }
- componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.handleShown);
- }
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
- }
- render() {
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var editTitle = (
- <h4
- className='modal-title'
- ref='title'
- >
- {'Edit Header'}
- </h4>
- );
- if (this.state.title) {
- editTitle = (
- <h4
- className='modal-title'
- ref='title'
- >
- {'Edit Header for '}<span className='name'>{this.state.title}</span>
- </h4>
- );
- }
-
- return (
- <div
- className='modal fade'
- ref='modal'
- id='edit_channel'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>{'×'}</span>
- </button>
- {editTitle}
- </div>
- <div className='modal-body'>
- <p>{'Edit the text appearing next to the channel name in the channel header.'}</p>
- <textarea
- className='form-control no-resize'
- rows='6'
- id='edit_header'
- maxLength='1024'
- value={this.state.header}
- onChange={this.handleUserInput}
- />
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- {'Cancel'}
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleEdit}
- >
- {'Save'}
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index ddbdee8a4..eb58fe721 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -3,7 +3,7 @@
import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
-import DeletePostModal from './delete_post_modal.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Textbox from './textbox.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import PostStore from '../stores/post_store.jsx';
@@ -35,7 +35,7 @@ export default class EditPostModal extends React.Component {
delete tempState.editText;
BrowserStore.setItem('edit_state_transfer', tempState);
$('#edit_post').modal('hide');
- DeletePostModal.show(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
+ EventHelpers.showDeletePostModal(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
return;
}
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index 2bd2c42d6..df5d6b8e1 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -1,32 +1,28 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import UserStore from '../stores/user_store.jsx';
+const Modal = ReactBootstrap.Modal;
export default class GetLinkModal extends React.Component {
constructor(props) {
super(props);
- this.handleClick = this.handleClick.bind(this);
- this.onShow = this.onShow.bind(this);
this.onHide = this.onHide.bind(this);
- this.state = {copiedLink: false};
- }
- onShow(e) {
- var button = e.relatedTarget;
- this.setState({title: $(button).attr('data-title'), value: $(button).attr('data-value')});
+ this.copyLink = this.copyLink.bind(this);
+
+ this.state = {
+ copiedLink: false
+ };
}
+
onHide() {
this.setState({copiedLink: false});
+
+ this.props.onHide();
}
- componentDidMount() {
- if (this.refs.modal) {
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', this.onHide);
- }
- }
- handleClick() {
+
+ copyLink() {
var copyTextarea = $(ReactDOM.findDOMNode(this.refs.textarea));
copyTextarea.select();
@@ -41,8 +37,18 @@ export default class GetLinkModal extends React.Component {
this.setState({copiedLink: false});
}
}
+
render() {
- var currentUser = UserStore.getCurrentUser();
+ let helpText = null;
+ if (this.props.helpText) {
+ helpText = (
+ <p>
+ {this.props.helpText}
+ <br />
+ <br />
+ </p>
+ );
+ }
let copyLink = null;
if (document.queryCommandSupported('copy')) {
@@ -51,75 +57,59 @@ export default class GetLinkModal extends React.Component {
data-copy-btn='true'
type='button'
className='btn btn-primary pull-left'
- onClick={this.handleClick}
- data-clipboard-text={this.state.value}
+ onClick={this.copyLink}
>
- Copy Link
+ {'Copy Link'}
</button>
);
}
var copyLinkConfirm = null;
if (this.state.copiedLink) {
- copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i> Link copied to clipboard.</p>;
+ copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i>{' Link copied to clipboard.'}</p>;
}
- if (currentUser != null) {
- return (
- <div
- className='modal fade'
- ref='modal'
- id='get_link'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4
- className='modal-title'
- id='myModalLabel'
- >
- {this.state.title} Link
- </h4>
- </div>
- <div className='modal-body'>
- <p>
- Send teammates the link below for them to sign-up to this team site.
- <br /><br />
- </p>
- <textarea
- className='form-control no-resize min-height'
- readOnly='true'
- ref='textarea'
- value={this.state.value}
- />
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- Close
- </button>
- {copyLink}
- {copyLinkConfirm}
- </div>
- </div>
- </div>
- </div>
- );
- }
- return <div/>;
+ return (
+ <Modal
+ show={this.props.show}
+ onHide={this.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {this.props.title}
+ </Modal.Header>
+ <Modal.Body>
+ {helpText}
+ <textarea
+ className='form-control no-resize min-height'
+ readOnly='true'
+ ref='textarea'
+ value={this.props.link}
+ />
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.onHide}
+ >
+ {'Close'}
+ </button>
+ {copyLink}
+ {copyLinkConfirm}
+ </Modal.Footer>
+ </Modal>
+ );
}
}
+
+GetLinkModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired,
+ title: React.PropTypes.string.isRequired,
+ helpText: React.PropTypes.string,
+ link: React.PropTypes.string.isRequired
+};
+
+GetLinkModal.defaultProps = {
+ helpText: null
+};
diff --git a/web/react/components/get_team_invite_link_modal.jsx b/web/react/components/get_team_invite_link_modal.jsx
new file mode 100644
index 000000000..a926c4451
--- /dev/null
+++ b/web/react/components/get_team_invite_link_modal.jsx
@@ -0,0 +1,45 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import Constants from '../utils/constants.jsx';
+import GetLinkModal from './get_link_modal.jsx';
+import ModalStore from '../stores/modal_store.jsx';
+import TeamStore from '../stores/team_store.jsx';
+
+export default class GetTeamInviteLinkModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleToggle = this.handleToggle.bind(this);
+
+ this.state = {
+ show: false
+ };
+ }
+
+ componentDidMount() {
+ ModalStore.addModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
+ }
+
+ componentWillUnmount() {
+ ModalStore.removeModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
+ }
+
+ handleToggle(value) {
+ this.setState({
+ show: value
+ });
+ }
+
+ render() {
+ return (
+ <GetLinkModal
+ show={this.state.show}
+ onHide={() => this.setState({show: false})}
+ title='Team Invite Link'
+ helpText='Send teammates the link below for them to sign-up to this team site.'
+ link={TeamStore.getCurrentInviteLink()}
+ />
+ );
+ }
+}
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 7df75252e..76f52faa9 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -4,8 +4,8 @@
import * as utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Client from '../utils/client.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import ModalStore from '../stores/modal_store.jsx';
import UserStore from '../stores/user_store.jsx';
import TeamStore from '../stores/team_store.jsx';
@@ -23,6 +23,7 @@ export default class InviteMemberModal extends React.Component {
this.addInviteFields = this.addInviteFields.bind(this);
this.clearFields = this.clearFields.bind(this);
this.removeInviteFields = this.removeInviteFields.bind(this);
+ this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
this.state = {
show: false,
@@ -188,6 +189,12 @@ export default class InviteMemberModal extends React.Component {
this.setState({inviteIds: inviteIds, idCount: count});
}
+ showGetTeamInviteLinkModal() {
+ this.handleHide(false);
+
+ EventHelpers.showGetTeamInviteLinkModal();
+ }
+
render() {
var currentUser = UserStore.getCurrentUser();
@@ -333,22 +340,18 @@ export default class InviteMemberModal extends React.Component {
} else {
var teamInviteLink = null;
if (currentUser && TeamStore.getCurrent().type === 'O') {
- var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id;
- var link =
- (
- <a
- href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={linkUrl}
- onClick={() => this.handleHide(this, false)}
- >Team Invite Link</a>
+ var link = (
+ <a
+ href='#'
+ onClick={this.showGetTeamInviteLinkModal}
+ >
+ {'Team Invite Link'}
+ </a>
);
teamInviteLink = (
<p>
- You can also invite people using the {link}.
+ {'You can also invite people using the '}{link}{'.'}
</p>
);
}
@@ -405,13 +408,6 @@ export default class InviteMemberModal extends React.Component {
return null;
}
-
- static show() {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_INVITE_MEMBER_MODAL,
- value: true
- });
- }
}
InviteMemberModal.propTypes = {
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
index 72fdb7be9..f1c31131f 100644
--- a/web/react/components/member_list_team.jsx
+++ b/web/react/components/member_list_team.jsx
@@ -2,17 +2,56 @@
// See License.txt for license information.
import MemberListTeamItem from './member_list_team_item.jsx';
+import UserStore from '../stores/user_store.jsx';
export default class MemberListTeam extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.getUsers = this.getUsers.bind(this);
+ this.onChange = this.onChange.bind(this);
+
+ this.state = {
+ users: this.getUsers()
+ };
+ }
+
+ componentDidMount() {
+ UserStore.addChangeListener(this.onChange);
+ }
+
+ componentWillUnmount() {
+ UserStore.removeChangeListener(this.onChange);
+ }
+
+ getUsers() {
+ const profiles = UserStore.getProfiles();
+ const users = [];
+
+ for (const id of Object.keys(profiles)) {
+ users.push(profiles[id]);
+ }
+
+ users.sort((a, b) => a.username.localeCompare(b.username));
+
+ return users;
+ }
+
+ onChange() {
+ this.setState({
+ users: this.getUsers()
+ });
+ }
+
render() {
- const memberList = this.props.users.map(function makeListItem(user) {
+ const memberList = this.state.users.map((user) => {
return (
<MemberListTeamItem
key={user.id}
user={user}
/>
);
- }, this);
+ });
return (
<table className='table more-table member-list-holder'>
@@ -23,7 +62,3 @@ export default class MemberListTeam extends React.Component {
);
}
}
-
-MemberListTeam.propTypes = {
- users: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
-};
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 6848ee5da..03cc75a08 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
import MessageWrapper from './message_wrapper.jsx';
import NotifyCounts from './notify_counts.jsx';
@@ -33,11 +34,15 @@ export default class Navbar extends React.Component {
this.onChange = this.onChange.bind(this);
this.handleLeave = this.handleLeave.bind(this);
this.showSearch = this.showSearch.bind(this);
+
+ this.showEditChannelHeaderModal = this.showEditChannelHeaderModal.bind(this);
+
this.createCollapseButtons = this.createCollapseButtons.bind(this);
this.createDropdown = this.createDropdown.bind(this);
const state = this.getStateFromStores();
state.showEditChannelPurposeModal = false;
+ state.showEditChannelHeaderModal = false;
state.showMembersModal = false;
state.showInviteModal = false;
this.state = state;
@@ -110,6 +115,16 @@ export default class Navbar extends React.Component {
this.setState(this.getStateFromStores());
$('#navbar .navbar-brand .description').popover({placement: 'bottom', trigger: 'click', html: true});
}
+ showEditChannelHeaderModal() {
+ // this can't be done using a ToggleModalButton because we can't use one inside an OverlayTrigger
+ if (this.refs.headerOverlay) {
+ this.refs.headerOverlay.hide();
+ }
+
+ this.setState({
+ showEditChannelHeaderModal: true
+ });
+ }
createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent) {
if (channel) {
var viewInfoOption = (
@@ -129,11 +144,7 @@ export default class Navbar extends React.Component {
<a
role='menuitem'
href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ onClick={this.showEditChannelHeaderModal}
>
{'Set Channel Header...'}
</a>
@@ -239,7 +250,7 @@ export default class Navbar extends React.Component {
dialogType={ChannelNotificationsModal}
dialogProps={{channel}}
>
- {'Notification Preferences'}
+ {'Notification Preferences'}
</ToggleModalButton>
</li>
);
@@ -249,6 +260,7 @@ export default class Navbar extends React.Component {
<div className='navbar-brand'>
<div className='dropdown'>
<OverlayTrigger
+ ref='headerOverlay'
trigger='click'
placement='bottom'
overlay={popoverContent}
@@ -358,6 +370,9 @@ export default class Navbar extends React.Component {
var isAdmin = false;
var isDirect = false;
+ var editChannelHeaderModal = null;
+ var editChannelPurposeModal = null;
+
if (channel) {
popoverContent = (
<Popover
@@ -400,11 +415,7 @@ export default class Navbar extends React.Component {
<br/>
<a
href='#'
- data-toggle='modal'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- data-target='#edit_channel'
+ onClick={this.showEditChannelHeaderModal}
>
{'Click here'}
</a>
@@ -413,6 +424,22 @@ export default class Navbar extends React.Component {
</Popover>
);
}
+
+ editChannelHeaderModal = (
+ <EditChannelHeaderModal
+ show={this.state.showEditChannelHeaderModal}
+ onHide={() => this.setState({showEditChannelHeaderModal: false})}
+ channel={channel}
+ />
+ );
+
+ editChannelPurposeModal = (
+ <EditChannelPurposeModal
+ show={this.state.showEditChannelPurposeModal}
+ onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
+ channel={channel}
+ />
+ );
}
var collapseButtons = this.createCollapseButtons(currentId);
@@ -443,11 +470,8 @@ export default class Navbar extends React.Component {
</div>
</div>
</nav>
- <EditChannelPurposeModal
- show={this.state.showEditChannelPurposeModal}
- onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
- channel={channel}
- />
+ {editChannelHeaderModal}
+ {editChannelPurposeModal}
<ChannelMembersModal
show={this.state.showMembersModal}
onModalDismissed={() => this.setState({showMembersModal: false})}
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index c0230fe5f..c286ee6f9 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -5,9 +5,11 @@ import * as Utils from '../utils/utils.jsx';
import * as client from '../utils/client.jsx';
import UserStore from '../stores/user_store.jsx';
import TeamStore from '../stores/team_store.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import AboutBuildModal from './about_build_modal.jsx';
-import InviteMemberModal from './invite_member_modal.jsx';
+import TeamMembersModal from './team_members_modal.jsx';
+import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import Constants from '../utils/constants.jsx';
@@ -93,7 +95,7 @@ export default class NavbarDropdown extends React.Component {
<li>
<a
href='#'
- onClick={InviteMemberModal.show}
+ onClick={EventHelpers.showInviteMemberModal}
>
{'Invite New Member'}
</a>
@@ -105,10 +107,7 @@ export default class NavbarDropdown extends React.Component {
<li>
<a
href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id}
+ onClick={EventHelpers.showGetTeamInviteLinkModal}
>
{'Get Team Invite Link'}
</a>
@@ -120,13 +119,9 @@ export default class NavbarDropdown extends React.Component {
if (isAdmin) {
manageLink = (
<li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_members'
- >
+ <ToggleModalButton dialogType={TeamMembersModal}>
{'Manage Members'}
- </a>
+ </ToggleModalButton>
</li>
);
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index bc6e8470d..cedb2b59b 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -1,11 +1,11 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import DeletePostModal from './delete_post_modal.jsx';
import UserStore from '../stores/user_store.jsx';
import TeamStore from '../stores/team_store.jsx';
import * as Utils from '../utils/utils.jsx';
import TimeSince from './time_since.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Constants from '../utils/constants.jsx';
@@ -74,7 +74,7 @@ export default class PostInfo extends React.Component {
<a
href='#'
role='menuitem'
- onClick={() => DeletePostModal.show(post, dataComments)}
+ onClick={() => EventHelpers.showDeletePostModal(post, dataComments)}
>
{'Delete'}
</a>
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index 3e555c85a..7aae5177e 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -8,13 +8,13 @@ import UserStore from '../stores/user_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
-import DeletePostModal from './delete_post_modal.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
var ActionTypes = Constants.ActionTypes;
import * as TextFormatting from '../utils/text_formatting.jsx';
import twemoji from 'twemoji';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export default class RhsComment extends React.Component {
constructor(props) {
@@ -115,7 +115,7 @@ export default class RhsComment extends React.Component {
<a
href='#'
role='menuitem'
- onClick={() => DeletePostModal.show(post, 0)}
+ onClick={() => EventHelpers.showDeletePostModal(post, 0)}
>
{'Delete'}
</a>
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 96f43bdb5..3d3d9e13f 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -6,11 +6,11 @@ import UserProfile from './user_profile.jsx';
import UserStore from '../stores/user_store.jsx';
import * as TextFormatting from '../utils/text_formatting.jsx';
import * as utils from '../utils/utils.jsx';
-import DeletePostModal from './delete_post_modal.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
import twemoji from 'twemoji';
import Constants from '../utils/constants.jsx';
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export default class RhsRootPost extends React.Component {
constructor(props) {
@@ -38,7 +38,9 @@ export default class RhsRootPost extends React.Component {
}
render() {
var post = this.props.post;
- var isOwner = UserStore.getCurrentId() === post.user_id;
+ var currentUser = UserStore.getCurrentUser();
+ var isOwner = currentUser.id === post.user_id;
+ var isAdmin = utils.isAdmin(currentUser.roles);
var timestamp = UserStore.getProfile(post.user_id).update_at;
var channel = ChannelStore.get(post.channel_id);
@@ -61,11 +63,54 @@ export default class RhsRootPost extends React.Component {
}
}
- var ownerOptions;
+ var dropdownContents = [];
+
if (isOwner) {
- ownerOptions = (
- <div>
- <a href='#'
+ dropdownContents.push(
+ <li
+ key='rhs-root-edit'
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#edit_post'
+ data-refocusid='#reply_textbox'
+ data-title={type}
+ data-message={post.message}
+ data-postid={post.id}
+ data-channelid={post.channel_id}
+ >
+ {'Edit'}
+ </a>
+ </li>
+ );
+ }
+
+ if (isOwner || isAdmin) {
+ dropdownContents.push(
+ <li
+ key='rhs-root-delete'
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ onClick={() => EventHelpers.showDeletePostModal(post, this.props.commentCount)}
+ >
+ {'Delete'}
+ </a>
+ </li>
+ );
+ }
+
+ var rootOptions = '';
+ if (dropdownContents.length > 0) {
+ rootOptions = (
+ <div className='dropdown'>
+ <a
+ href='#'
className='post__dropdown dropdown-toggle'
type='button'
data-toggle='dropdown'
@@ -75,30 +120,7 @@ export default class RhsRootPost extends React.Component {
className='dropdown-menu'
role='menu'
>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- {'Edit'}
- </a>
- </li>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- onClick={() => DeletePostModal.show(post, this.props.commentCount)}
- >
- {'Delete'}
- </a>
- </li>
+ {dropdownContents}
</ul>
</div>
);
@@ -166,7 +188,7 @@ export default class RhsRootPost extends React.Component {
</li>
<li className='col col__reply'>
<div className='dropdown'>
- {ownerOptions}
+ {rootOptions}
</div>
</li>
</ul>
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index f6c0c8adb..1f268a2f7 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -1,11 +1,12 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import InviteMemberModal from './invite_member_modal.jsx';
+import TeamMembersModal from './team_members_modal.jsx';
+import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import UserStore from '../stores/user_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
import * as client from '../utils/client.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import * as utils from '../utils/utils.jsx';
export default class SidebarRightMenu extends React.Component {
@@ -46,7 +47,7 @@ export default class SidebarRightMenu extends React.Component {
<li>
<a
href='#'
- onClick={InviteMemberModal.show}
+ onClick={EventHelpers.showInviteMemberModal}
>
<i className='fa fa-user'></i>Invite New Member
</a>
@@ -56,12 +57,12 @@ export default class SidebarRightMenu extends React.Component {
if (this.props.teamType === 'O') {
teamLink = (
<li>
- <a href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id}
- ><i className='fa fa-link'></i>Get Team Invite Link</a>
+ <a
+ href='#'
+ onClick={EventHelpers.showGetTeamInviteLinkModal}
+ >
+ <i className='glyphicon glyphicon-link'></i>{'Get Team Invite Link'}
+ </a>
</li>
);
}
@@ -79,12 +80,9 @@ export default class SidebarRightMenu extends React.Component {
);
manageLink = (
<li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_members'
- >
- <i className='fa fa-users'></i>Manage Members</a>
+ <ToggleModalButton dialogType={TeamMembersModal}>
+ <i className='fa fa-users'></i>{'Manage Members'}
+ </ToggleModalButton>
</li>
);
}
diff --git a/web/react/components/team_members.jsx b/web/react/components/team_members.jsx
deleted file mode 100644
index cd0766012..000000000
--- a/web/react/components/team_members.jsx
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import MemberListTeam from './member_list_team.jsx';
-import * as utils from '../utils/utils.jsx';
-
-function getStateFromStores() {
- var users = UserStore.getProfiles();
- var memberList = [];
- for (var id in users) {
- if (users.hasOwnProperty(id)) {
- memberList.push(users[id]);
- }
- }
-
- memberList.sort(function sort(a, b) {
- if (a.username < b.username) {
- return -1;
- }
-
- if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- });
-
- return {
- member_list: memberList
- };
-}
-
-export default class TeamMembers extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = getStateFromStores();
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.onChange);
-
- var self = this;
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function show() {
- self.setState({render_members: false});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function hide() {
- self.setState({render_members: true});
- });
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onChange);
- }
-
- onChange() {
- var newState = getStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
-
- render() {
- var serverError = null;
-
- if (this.state.server_error) {
- serverError = <label className='has-error control-label'>{this.state.server_error}</label>;
- }
-
- var renderMembers = '';
-
- if (this.state.render_members) {
- renderMembers = <MemberListTeam users={this.state.member_list} />;
- }
-
- return (
- <div
- className='modal fade more-modal'
- ref='modal'
- id='team_members'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>×</span>
- </button>
- <h4
- className='modal-title'
- id='myModalLabel'
- >{this.props.teamDisplayName + ' Members'}</h4>
- </div>
- <div
- ref='modalBody'
- className='modal-body'
- >
- <div className='team-member-list'>
- {renderMembers}
- </div>
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >Close</button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamMembers.propTypes = {
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
new file mode 100644
index 000000000..0a30a2202
--- /dev/null
+++ b/web/react/components/team_members_modal.jsx
@@ -0,0 +1,69 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import MemberListTeam from './member_list_team.jsx';
+import TeamStore from '../stores/team_store.jsx';
+
+const Modal = ReactBootstrap.Modal;
+
+export default class TeamMembersModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.onShow = this.onShow.bind(this);
+ }
+
+ componentDidMount() {
+ if (this.props.show) {
+ this.onShow();
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.onShow();
+ }
+ }
+
+ onShow() {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300);
+ if ($(window).width() > 768) {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ }
+ }
+
+ render() {
+ const team = TeamStore.getCurrent();
+
+ return (
+ <Modal
+ dialogClassName='team-members-modal'
+ show={this.props.show}
+ onHide={this.props.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {team.display_name + ' Members'}
+ </Modal.Header>
+ <Modal.Body ref='modalBody'>
+ <div className='team-member-list'>
+ <MemberListTeam />
+ </div>
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.props.onHide}
+ >
+ {'Close'}
+ </button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+TeamMembersModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/toggle_modal_button.jsx b/web/react/components/toggle_modal_button.jsx
index eae4a024d..ce8ff3f60 100644
--- a/web/react/components/toggle_modal_button.jsx
+++ b/web/react/components/toggle_modal_button.jsx
@@ -22,7 +22,17 @@ export default class ModalToggleButton extends React.Component {
}
render() {
- const {children, dialogType, dialogProps, ...props} = this.props; //eslint-disable-line no-redeclare
+ const {children, dialogType, dialogProps, onClick, ...props} = this.props; // eslint-disable-line no-redeclare
+
+ // allow callers to provide an onClick which will be called before the modal is shown
+ let clickHandler = this.show;
+ if (onClick) {
+ clickHandler = () => {
+ onClick();
+
+ this.show();
+ };
+ }
// this assumes that all modals will have a show property and an onHide event
const dialog = React.createElement(this.props.dialogType, Object.assign({}, dialogProps, {
@@ -42,7 +52,7 @@ export default class ModalToggleButton extends React.Component {
<a
{...props}
href='#'
- onClick={this.show}
+ onClick={clickHandler}
>
{children}
{dialog}
@@ -54,7 +64,8 @@ export default class ModalToggleButton extends React.Component {
ModalToggleButton.propTypes = {
children: React.PropTypes.node.isRequired,
dialogType: React.PropTypes.func.isRequired,
- dialogProps: React.PropTypes.object
+ dialogProps: React.PropTypes.object,
+ onClick: React.PropTypes.func
};
ModalToggleButton.defaultProps = {
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index b3ec7ddd7..962efd7a2 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -438,12 +438,12 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'email') {
const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
- let helpText = 'Email is used for notifications, and requires verification if changed.';
+ let helpText = 'Email is used for sign-in, notifications, and password reset. Email requires verification if changed.';
if (!emailEnabled) {
helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
} else if (!emailVerificationEnabled) {
- helpText = 'Email is used for notifications.';
+ helpText = 'Email is used for sign-in, notifications, and password reset.';
} else if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
diff --git a/web/react/dispatcher/event_helpers.jsx b/web/react/dispatcher/event_helpers.jsx
index 85329b38f..d7f255aaa 100644
--- a/web/react/dispatcher/event_helpers.jsx
+++ b/web/react/dispatcher/event_helpers.jsx
@@ -81,3 +81,26 @@ export function emitPostDeletedEvent(post) {
post
});
}
+
+export function showDeletePostModal(post, commentCount = 0) {
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.TOGGLE_DELETE_POST_MODAL,
+ value: true,
+ post,
+ commentCount
+ });
+}
+
+export function showGetTeamInviteLinkModal() {
+ AppDispatcher.handleViewAction({
+ type: Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL,
+ value: true
+ });
+}
+
+export function showInviteMemberModal() {
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.TOGGLE_INVITE_MEMBER_MODAL,
+ value: true
+ });
+}
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 5cc1be741..b73dfdafe 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -7,15 +7,13 @@ import ErrorBar from '../components/error_bar.jsx';
import ErrorStore from '../stores/error_store.jsx';
import MentionList from '../components/mention_list.jsx';
-import GetLinkModal from '../components/get_link_modal.jsx';
-import EditChannelModal from '../components/edit_channel_modal.jsx';
+import GetTeamInviteLinkModal from '../components/get_team_invite_link_modal.jsx';
import RenameChannelModal from '../components/rename_channel_modal.jsx';
import EditPostModal from '../components/edit_post_modal.jsx';
import DeletePostModal from '../components/delete_post_modal.jsx';
import MoreChannelsModal from '../components/more_channels.jsx';
import PostDeletedModal from '../components/post_deleted_modal.jsx';
import TeamSettingsModal from '../components/team_settings_modal.jsx';
-import TeamMembersModal from '../components/team_members.jsx';
import RemovedFromChannelModal from '../components/removed_from_channel_modal.jsx';
import RegisterAppModal from '../components/register_app_modal.jsx';
import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx';
@@ -68,8 +66,8 @@ function setupChannelPage(props, team, channel) {
// Modals
//
ReactDOM.render(
- <GetLinkModal />,
- document.getElementById('get_link_modal')
+ <GetTeamInviteLinkModal />,
+ document.getElementById('get_team_invite_link_modal')
);
ReactDOM.render(
@@ -88,16 +86,6 @@ function setupChannelPage(props, team, channel) {
);
ReactDOM.render(
- <TeamMembersModal teamDisplayName={props.TeamDisplayName} />,
- document.getElementById('team_members_modal')
- );
-
- ReactDOM.render(
- <EditChannelModal />,
- document.getElementById('edit_channel_modal')
- );
-
- ReactDOM.render(
<RenameChannelModal />,
document.getElementById('rename_channel_modal')
);
diff --git a/web/react/stores/modal_store.jsx b/web/react/stores/modal_store.jsx
index 69f43a5cf..a26a97f53 100644
--- a/web/react/stores/modal_store.jsx
+++ b/web/react/stores/modal_store.jsx
@@ -34,6 +34,7 @@ class ModalStoreClass extends EventEmitter {
case ActionTypes.TOGGLE_IMPORT_THEME_MODAL:
case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL:
case ActionTypes.TOGGLE_DELETE_POST_MODAL:
+ case ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL:
this.emit(type, value, args);
break;
}
diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx
index 26c83cc8c..2d518d9e7 100644
--- a/web/react/stores/team_store.jsx
+++ b/web/react/stores/team_store.jsx
@@ -31,6 +31,7 @@ class TeamStoreClass extends EventEmitter {
this.getCurrentId = this.getCurrentId.bind(this);
this.getCurrent = this.getCurrent.bind(this);
this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this);
+ this.getCurrentInviteLink = this.getCurrentInviteLink.bind(this);
this.saveTeam = this.saveTeam.bind(this);
}
@@ -92,6 +93,16 @@ class TeamStoreClass extends EventEmitter {
return null;
}
+ getCurrentInviteLink() {
+ const current = this.getCurrent();
+
+ if (current) {
+ return getWindowLocationOrigin() + '/signup_user_complete/?id=' + current.invite_id;
+ }
+
+ return '';
+ }
+
saveTeam(team) {
var teams = this.getAll();
teams[team.id] = team;
diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx
index 0bbc7366e..6f83778c9 100644
--- a/web/react/utils/channel_intro_mssages.jsx
+++ b/web/react/utils/channel_intro_mssages.jsx
@@ -1,13 +1,14 @@
-
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from './utils.jsx';
-import InviteMemberModal from '../components/invite_member_modal.jsx';
+import EditChannelHeaderModal from '../components/edit_channel_header_modal.jsx';
+import ToggleModalButton from '../components/toggle_modal_button.jsx';
import UserProfile from '../components/user_profile.jsx';
import ChannelStore from '../stores/channel_store.jsx';
import Constants from '../utils/constants.jsx';
import TeamStore from '../stores/team_store.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export function createChannelIntroMessage(channel, showInviteModal) {
if (channel.type === 'D') {
@@ -49,17 +50,7 @@ export function createDMIntroMessage(channel) {
{'This is the start of your direct message history with ' + teammateName + '.'}<br/>
{'Direct messages and files shared here are not shown to people outside this area.'}
</p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
</div>
);
}
@@ -79,17 +70,7 @@ export function createOffTopicIntroMessage(channel, showInviteModal) {
{'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
<br/>
</p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
<a
href='#'
className='intro-links'
@@ -109,7 +90,7 @@ export function createDefaultIntroMessage(channel) {
<a
className='intro-links'
href='#'
- onClick={InviteMemberModal.show}
+ onClick={EventHelpers.showInviteMemberModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this team'}
</a>
@@ -119,10 +100,7 @@ export function createDefaultIntroMessage(channel) {
<a
className='intro-links'
href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
+ onClick={EventHelpers.showGetTeamInviteLinkModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this team'}
</a>
@@ -138,17 +116,7 @@ export function createDefaultIntroMessage(channel) {
{'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
</p>
{inviteModalLink}
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
<br/>
</div>
);
@@ -193,17 +161,7 @@ export function createStandardIntroMessage(channel, showInviteModal) {
{memberMessage}
<br/>
</p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
<a
className='intro-links'
href='#'
@@ -214,3 +172,15 @@ export function createStandardIntroMessage(channel, showInviteModal) {
</div>
);
}
+
+function createSetHeaderButton(channel) {
+ return (
+ <ToggleModalButton
+ className='intro-links'
+ dialogType={EditChannelHeaderModal}
+ dialogProps={{channel}}
+ >
+ <i className='fa fa-pencil'></i>{'Set a header'}
+ </ToggleModalButton>
+ );
+}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 1ac9a1b98..6281813e9 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -48,7 +48,8 @@ export default {
TOGGLE_IMPORT_THEME_MODAL: null,
TOGGLE_INVITE_MEMBER_MODAL: null,
- TOGGLE_DELETE_POST_MODAL: null
+ TOGGLE_DELETE_POST_MODAL: null,
+ TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null
}),
PayloadSources: keyMirror({
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 764bdf763..9b2f7e057 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -749,19 +749,10 @@ export function updateCodeTheme(theme) {
export function placeCaretAtEnd(el) {
el.focus();
- if (typeof window.getSelection != 'undefined' && typeof document.createRange != 'undefined') {
- var range = document.createRange();
- range.selectNodeContents(el);
- range.collapse(false);
- var sel = window.getSelection();
- sel.removeAllRanges();
- sel.addRange(range);
- } else if (typeof document.body.createTextRange != 'undefined') {
- var textRange = document.body.createTextRange();
- textRange.moveToElementText(el);
- textRange.collapse(false);
- textRange.select();
- }
+ el.selectionStart = el.value.length;
+ el.selectionEnd = el.value.length;
+
+ return;
}
export function getCaretPosition(el) {
diff --git a/web/templates/channel.html b/web/templates/channel.html
index 33bb252b1..7b8f6a243 100644
--- a/web/templates/channel.html
+++ b/web/templates/channel.html
@@ -10,26 +10,17 @@
<div id="post_mention_tab"></div>
<div id="reply_mention_tab"></div>
<div id="edit_mention_tab"></div>
- <div id="get_link_modal"></div>
- <div id="user_settings_modal"></div>
+ <div id="get_team_invite_link_modal"></div>
<div id="import_theme_modal"></div>
<div id="team_settings_modal"></div>
<div id="invite_member_modal"></div>
- <div id="edit_channel_modal"></div>
- <div id="delete_channel_modal"></div>
<div id="rename_channel_modal"></div>
- <div id="rename_team_modal"></div>
<div id="edit_post_modal"></div>
<div id="delete_post_modal"></div>
<div id="more_channels_modal"></div>
- <div id="new_channel_modal"></div>
<div id="post_deleted_modal"></div>
<div id="channel_notifications_modal"></div>
<div id="team_members_modal"></div>
- <div id="direct_channel_modal"></div>
- <div id="channel_info_modal"></div>
- <div id="access_history_modal"></div>
- <div id="activity_log_modal"></div>
<div id="removed_from_channel_modal"></div>
<div id="register_app_modal"></div>
<script>