diff --git a/README.md b/README.md
index 9fb46572..2fec84fc 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@ Each user in Mattermost is connected with their own personal GitLab account. Use
### Sidebar buttons
-Team members can stay up-to-date with how many reviews, unread messages, assignments, and open merge requests they have by using buttons in the Mattermost sidebar.
+Team members can stay up-to-date with how many reviews, todos, assigned issues, and assigned merge requests they have by using buttons in the Mattermost sidebar.
## Admin guide
@@ -142,7 +142,7 @@ Connect your Mattermost account to your GitLab account using `/gitlab connect` a
### Get "To Do" items
-Use `/gitlab todo` to get a list of unread messages and merge requests awaiting your review.
+Use `/gitlab todo` to get a list of todos, assigned issues, assigned merge requests and merge requests awaiting your review.
### Update settings
diff --git a/plugin.json b/plugin.json
index f6c9348f..db8fc2da 100644
--- a/plugin.json
+++ b/plugin.json
@@ -6,7 +6,7 @@
"support_url": "https://github.com/mattermost/mattermost-plugin-gitlab/issues",
"release_notes_url": "https://github.com/mattermost/mattermost-plugin-gitlab/releases/tag/v1.8.0",
"icon_path": "assets/icon.svg",
- "version": "1.8.0",
+ "version": "1.8.1",
"min_server_version": "7.1.0",
"server": {
"executables": {
diff --git a/server/api.go b/server/api.go
index 32ff418f..27fd31d4 100644
--- a/server/api.go
+++ b/server/api.go
@@ -392,10 +392,10 @@ func (p *Plugin) completeConnectUserToGitlab(c *Context, w http.ResponseWriter,
"Turn off notifications with `/gitlab settings notifications off`.\n\n"+
"##### Sidebar Buttons\n"+
"Check out the buttons in the left-hand sidebar of Mattermost.\n"+
- "* The first button tells you how many merge requests you have submitted.\n"+
+ "* The first button tells you how many merge requests you are assigned to.\n"+
"* The second shows the number of merge requests that are awaiting your review.\n"+
- "* The third shows the number of merge requests and issues you are assigned to.\n"+
- "* The fourth tracks the number of unread messages you have.\n"+
+ "* The third shows the number of issues you are assigned to.\n"+
+ "* The fourth tracks the number of todos you have.\n"+
"* The fifth will refresh the numbers.\n\n"+
"Click on them!\n\n"+
"##### Slash Commands\n"+
diff --git a/server/command.go b/server/command.go
index f115b0d2..9d3f3b53 100644
--- a/server/command.go
+++ b/server/command.go
@@ -19,7 +19,7 @@ import (
const commandHelp = `* |/gitlab connect| - Connect your Mattermost account to your GitLab account
* |/gitlab disconnect| - Disconnect your Mattermost account from your GitLab account
-* |/gitlab todo| - Get a list of unread messages and merge requests awaiting your review
+* |/gitlab todo| - Get a list of todos, assigned issues, assigned merge requests and merge requests awaiting your review
* |/gitlab subscriptions list| - Will list the current channel subscriptions
* |/gitlab subscriptions add owner[/repo] [features]| - Subscribe the current channel to receive notifications about opened merge requests and issues for a group or repository
* |features| is a comma-delimited list of one or more the following:
@@ -248,7 +248,7 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (res
_, text, err := p.GetToDo(ctx, info)
if err != nil {
p.client.Log.Warn("can't get todo in command", "err", err.Error())
- return p.getCommandResponse(args, "Encountered an error getting your to do items."), nil
+ return p.getCommandResponse(args, "Encountered an error getting your todo items."), nil
}
return p.getCommandResponse(args, text), nil
case "me":
@@ -773,7 +773,7 @@ func getAutocompleteData(config *configuration) *model.AutocompleteData {
disconnect := model.NewAutocompleteData("disconnect", "", "disconnect your GitLab account")
gitlab.AddCommand(disconnect)
- todo := model.NewAutocompleteData("todo", "", "Get a list of unread messages and merge requests awaiting your review")
+ todo := model.NewAutocompleteData("todo", "", "Get a list of todos, assigned issues, assigned merge requests and merge requests awaiting your review")
gitlab.AddCommand(todo)
subscriptions := model.NewAutocompleteData("subscriptions", "[command]", "Available commands: Add, List, Delete")
diff --git a/server/command_test.go b/server/command_test.go
index 15a94b55..eeeb2180 100644
--- a/server/command_test.go
+++ b/server/command_test.go
@@ -380,7 +380,7 @@ func TestAddWebhookCommand(t *testing.T) {
p.GitlabClient = mockedClient
conf := &model.Config{}
- conf.ServiceSettings.SiteURL = &test.siteURL
+ conf.ServiceSettings.SiteURL = model.NewString(test.siteURL)
encryptedToken, _ := encrypt([]byte(testEncryptionKey), testGitlabToken)
diff --git a/server/configuration_test.go b/server/configuration_test.go
index dd85d1ea..a306716c 100644
--- a/server/configuration_test.go
+++ b/server/configuration_test.go
@@ -66,7 +66,7 @@ func TestSetDefaults(t *testing.T) {
for _, testCase := range []struct {
description string
isCloud bool
- config configuration
+ config *configuration
shouldChange bool
outputCheck func(*testing.T, *configuration)
@@ -74,7 +74,7 @@ func TestSetDefaults(t *testing.T) {
}{
{
description: "noop",
- config: configuration{
+ config: &configuration{
EncryptionKey: "abcd",
WebhookSecret: "efgh",
},
@@ -85,7 +85,7 @@ func TestSetDefaults(t *testing.T) {
},
}, {
description: "set encryption key",
- config: configuration{
+ config: &configuration{
EncryptionKey: "",
},
shouldChange: true,
@@ -94,7 +94,7 @@ func TestSetDefaults(t *testing.T) {
},
}, {
description: "set webhook key",
- config: configuration{
+ config: &configuration{
WebhookSecret: "",
},
shouldChange: true,
@@ -103,7 +103,7 @@ func TestSetDefaults(t *testing.T) {
},
}, {
description: "set webhook and encryption key",
- config: configuration{
+ config: &configuration{
EncryptionKey: "",
WebhookSecret: "",
},
@@ -115,7 +115,7 @@ func TestSetDefaults(t *testing.T) {
}, {
description: "Should not set UsePreregisteredApplication in on-prem",
isCloud: false,
- config: configuration{
+ config: &configuration{
EncryptionKey: "abcd",
WebhookSecret: "efgh",
UsePreregisteredApplication: false,
@@ -128,7 +128,7 @@ func TestSetDefaults(t *testing.T) {
}, {
description: "Should set UsePreregisteredApplication in cloud if no OAuth secret is configured",
isCloud: true,
- config: configuration{
+ config: &configuration{
EncryptionKey: "abcd",
WebhookSecret: "efgh",
UsePreregisteredApplication: false,
@@ -143,7 +143,7 @@ func TestSetDefaults(t *testing.T) {
}, {
description: "Should set not UsePreregisteredApplication in cloud if OAuth secret is configured",
isCloud: true,
- config: configuration{
+ config: &configuration{
EncryptionKey: "abcd",
WebhookSecret: "efgh",
UsePreregisteredApplication: false,
@@ -163,7 +163,7 @@ func TestSetDefaults(t *testing.T) {
changed, err := testCase.config.setDefaults(testCase.isCloud)
assert.Equal(t, testCase.shouldChange, changed)
- testCase.outputCheck(t, &testCase.config)
+ testCase.outputCheck(t, testCase.config)
if testCase.errMsg != "" {
require.Error(t, err)
diff --git a/server/flow.go b/server/flow.go
index 4f7d77aa..c9a3cfb1 100644
--- a/server/flow.go
+++ b/server/flow.go
@@ -509,8 +509,8 @@ func (fm *FlowManager) submitOAuthConfig(f *flow.Flow, submitted map[string]inte
clientID = strings.TrimSpace(clientID)
- if len(clientID) != 64 {
- errorList["client_id"] = "Client ID should be 64 characters long"
+ if len(clientID) < 64 {
+ errorList["client_id"] = "Client ID should be at least 64 characters long"
}
clientSecretRaw, ok := submitted["client_secret"]
@@ -524,8 +524,8 @@ func (fm *FlowManager) submitOAuthConfig(f *flow.Flow, submitted map[string]inte
clientSecret = strings.TrimSpace(clientSecret)
- if len(clientSecret) != 64 {
- errorList["client_secret"] = "Client Secret should be 64 characters long"
+ if len(clientSecret) < 64 {
+ errorList["client_secret"] = "Client Secret should be at least 64 characters long"
}
if len(errorList) != 0 {
diff --git a/server/gitlab/api.go b/server/gitlab/api.go
index 0260fb25..951c117d 100644
--- a/server/gitlab/api.go
+++ b/server/gitlab/api.go
@@ -41,10 +41,10 @@ type Issue struct {
}
type LHSContent struct {
- PRs []*MergeRequest `json:"prs"`
- Reviews []*MergeRequest `json:"reviews"`
- Assignments []*Issue `json:"assignments"`
- Unreads []*internGitlab.Todo `json:"unreads"`
+ AssignedPRs []*MergeRequest `json:"yourAssignedPrs"`
+ Reviews []*MergeRequest `json:"reviews"`
+ AssignedIssues []*Issue `json:"yourAssignedIssues"`
+ Todos []*internGitlab.Todo `json:"todos"`
}
// NewGroupHook creates a webhook associated with a GitLab group
@@ -303,21 +303,21 @@ func (g *gitlab) GetLHSData(ctx context.Context, user *UserInfo, token *oauth2.T
return err
})
- var assignments []*Issue
+ var issues []*Issue
grp.Go(func() error {
- assignments, err = g.GetYourAssignments(ctx, user, client)
+ issues, err = g.GetYourAssignedIssues(ctx, user, client)
return err
})
var mergeRequests []*MergeRequest
grp.Go(func() error {
- mergeRequests, err = g.GetYourPrs(ctx, user, client)
+ mergeRequests, err = g.GetYourAssignedPrs(ctx, user, client)
return err
})
- var unreads []*internGitlab.Todo
+ var todos []*internGitlab.Todo
grp.Go(func() error {
- unreads, err = g.GetUnreads(ctx, user, client)
+ todos, err = g.GetToDoList(ctx, user, client)
return err
})
@@ -326,10 +326,10 @@ func (g *gitlab) GetLHSData(ctx context.Context, user *UserInfo, token *oauth2.T
}
return &LHSContent{
- Reviews: reviews,
- PRs: mergeRequests,
- Assignments: assignments,
- Unreads: unreads,
+ Reviews: reviews,
+ AssignedPRs: mergeRequests,
+ AssignedIssues: issues,
+ Todos: todos,
}, nil
}
@@ -394,13 +394,13 @@ func (g *gitlab) GetReviews(ctx context.Context, user *UserInfo, client *internG
return mergeRequests, nil
}
-func (g *gitlab) GetYourPrs(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*MergeRequest, error) {
+func (g *gitlab) GetYourAssignedPrs(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*MergeRequest, error) {
opened := stateOpened
scope := scopeAll
var mrs []*internGitlab.MergeRequest
if g.gitlabGroup == "" {
opt := &internGitlab.ListMergeRequestsOptions{
- AuthorID: &user.GitlabUserID,
+ AssigneeID: internGitlab.AssigneeID(user.GitlabUserID),
State: &opened,
Scope: &scope,
ListOptions: internGitlab.ListOptions{Page: 1, PerPage: perPage},
@@ -418,7 +418,7 @@ func (g *gitlab) GetYourPrs(ctx context.Context, user *UserInfo, client *internG
}
} else {
opt := &internGitlab.ListGroupMergeRequestsOptions{
- AuthorID: &user.GitlabUserID,
+ AssigneeID: internGitlab.AssigneeID(user.GitlabUserID),
State: &opened,
Scope: &scope,
ListOptions: internGitlab.ListOptions{Page: 1, PerPage: perPage},
@@ -547,7 +547,7 @@ func (g *gitlab) fetchYourPrDetails(c context.Context, log logger.Logger, client
return nil
}
-func (g *gitlab) GetYourAssignments(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*Issue, error) {
+func (g *gitlab) GetYourAssignedIssues(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*Issue, error) {
opened := stateOpened
scope := scopeAll
var issues []*internGitlab.Issue
@@ -607,7 +607,7 @@ func (g *gitlab) GetYourAssignments(ctx context.Context, user *UserInfo, client
return result, nil
}
-func (g *gitlab) GetUnreads(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*internGitlab.Todo, error) {
+func (g *gitlab) GetToDoList(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*internGitlab.Todo, error) {
var todos []*internGitlab.Todo
opt := &internGitlab.ListTodosOptions{
diff --git a/server/gitlab/gitlab.go b/server/gitlab/gitlab.go
index 20bbb0e5..eede91d1 100644
--- a/server/gitlab/gitlab.go
+++ b/server/gitlab/gitlab.go
@@ -29,10 +29,10 @@ type Gitlab interface {
GetProject(ctx context.Context, user *UserInfo, token *oauth2.Token, owner, repo string) (*internGitlab.Project, error)
GetYourPrDetails(ctx context.Context, log logger.Logger, user *UserInfo, token *oauth2.Token, prList []*PRDetails) ([]*PRDetails, error)
GetReviews(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*MergeRequest, error)
- GetYourPrs(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*MergeRequest, error)
+ GetYourAssignedPrs(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*MergeRequest, error)
GetLHSData(ctx context.Context, user *UserInfo, token *oauth2.Token) (*LHSContent, error)
- GetYourAssignments(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*Issue, error)
- GetUnreads(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*internGitlab.Todo, error)
+ GetYourAssignedIssues(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*Issue, error)
+ GetToDoList(ctx context.Context, user *UserInfo, client *internGitlab.Client) ([]*internGitlab.Todo, error)
GetProjectHooks(ctx context.Context, user *UserInfo, token *oauth2.Token, owner string, repo string) ([]*WebhookInfo, error)
GetGroupHooks(ctx context.Context, user *UserInfo, token *oauth2.Token, owner string) ([]*WebhookInfo, error)
NewProjectHook(ctx context.Context, user *UserInfo, token *oauth2.Token, projectID interface{}, projectHookOptions *AddWebhookOptions) (*WebhookInfo, error)
diff --git a/server/gitlab/mocks/mock_gitlab.go b/server/gitlab/mocks/mock_gitlab.go
index 668bed95..d75c91e2 100644
--- a/server/gitlab/mocks/mock_gitlab.go
+++ b/server/gitlab/mocks/mock_gitlab.go
@@ -128,19 +128,19 @@ func (mr *MockGitlabMockRecorder) GetReviews(arg0, arg1, arg2 interface{}) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviews", reflect.TypeOf((*MockGitlab)(nil).GetReviews), arg0, arg1, arg2)
}
-// GetUnreads mocks base method.
-func (m *MockGitlab) GetUnreads(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab0.Todo, error) {
+// GetToDoList mocks base method.
+func (m *MockGitlab) GetToDoList(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab0.Todo, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetUnreads", arg0, arg1, arg2)
+ ret := m.ctrl.Call(m, "GetToDoList", arg0, arg1, arg2)
ret0, _ := ret[0].([]*gitlab0.Todo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetUnreads indicates an expected call of GetUnreads.
-func (mr *MockGitlabMockRecorder) GetUnreads(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetToDoList indicates an expected call of GetToDoList.
+func (mr *MockGitlabMockRecorder) GetToDoList(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnreads", reflect.TypeOf((*MockGitlab)(nil).GetUnreads), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetToDoList", reflect.TypeOf((*MockGitlab)(nil).GetToDoList), arg0, arg1, arg2)
}
// GetUserDetails mocks base method.
@@ -158,49 +158,49 @@ func (mr *MockGitlabMockRecorder) GetUserDetails(arg0, arg1, arg2 interface{}) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserDetails", reflect.TypeOf((*MockGitlab)(nil).GetUserDetails), arg0, arg1, arg2)
}
-// GetYourAssignments mocks base method.
-func (m *MockGitlab) GetYourAssignments(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.Issue, error) {
+// GetYourAssignedIssues mocks base method.
+func (m *MockGitlab) GetYourAssignedIssues(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.Issue, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetYourAssignments", arg0, arg1, arg2)
+ ret := m.ctrl.Call(m, "GetYourAssignedIssues", arg0, arg1, arg2)
ret0, _ := ret[0].([]*gitlab.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetYourAssignments indicates an expected call of GetYourAssignments.
-func (mr *MockGitlabMockRecorder) GetYourAssignments(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetYourAssignedIssues indicates an expected call of GetYourAssignedIssues.
+func (mr *MockGitlabMockRecorder) GetYourAssignedIssues(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourAssignments", reflect.TypeOf((*MockGitlab)(nil).GetYourAssignments), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourAssignedIssues", reflect.TypeOf((*MockGitlab)(nil).GetYourAssignedIssues), arg0, arg1, arg2)
}
-// GetYourPrDetails mocks base method.
-func (m *MockGitlab) GetYourPrDetails(arg0 context.Context, arg1 logger.Logger, arg2 *gitlab.UserInfo, arg3 *oauth2.Token, arg4 []*gitlab.PRDetails) ([]*gitlab.PRDetails, error) {
+// GetYourAssignedPrs mocks base method.
+func (m *MockGitlab) GetYourAssignedPrs(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.MergeRequest, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetYourPrDetails", arg0, arg1, arg2, arg3, arg4)
- ret0, _ := ret[0].([]*gitlab.PRDetails)
+ ret := m.ctrl.Call(m, "GetYourAssignedPrs", arg0, arg1, arg2)
+ ret0, _ := ret[0].([]*gitlab.MergeRequest)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetYourPrDetails indicates an expected call of GetYourPrDetails.
-func (mr *MockGitlabMockRecorder) GetYourPrDetails(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+// GetYourAssignedPrs indicates an expected call of GetYourAssignedPrs.
+func (mr *MockGitlabMockRecorder) GetYourAssignedPrs(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourPrDetails", reflect.TypeOf((*MockGitlab)(nil).GetYourPrDetails), arg0, arg1, arg2, arg3, arg4)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourAssignedPrs", reflect.TypeOf((*MockGitlab)(nil).GetYourAssignedPrs), arg0, arg1, arg2)
}
-// GetYourPrs mocks base method.
-func (m *MockGitlab) GetYourPrs(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.MergeRequest, error) {
+// GetYourPrDetails mocks base method.
+func (m *MockGitlab) GetYourPrDetails(arg0 context.Context, arg1 logger.Logger, arg2 *gitlab.UserInfo, arg3 *oauth2.Token, arg4 []*gitlab.PRDetails) ([]*gitlab.PRDetails, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetYourPrs", arg0, arg1, arg2)
- ret0, _ := ret[0].([]*gitlab.MergeRequest)
+ ret := m.ctrl.Call(m, "GetYourPrDetails", arg0, arg1, arg2, arg3, arg4)
+ ret0, _ := ret[0].([]*gitlab.PRDetails)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetYourPrs indicates an expected call of GetYourPrs.
-func (mr *MockGitlabMockRecorder) GetYourPrs(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetYourPrDetails indicates an expected call of GetYourPrDetails.
+func (mr *MockGitlabMockRecorder) GetYourPrDetails(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourPrs", reflect.TypeOf((*MockGitlab)(nil).GetYourPrs), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourPrDetails", reflect.TypeOf((*MockGitlab)(nil).GetYourPrDetails), arg0, arg1, arg2, arg3, arg4)
}
// GitlabConnect mocks base method.
diff --git a/server/mocks/mock_gitlab.go b/server/mocks/mock_gitlab.go
index 668bed95..d75c91e2 100644
--- a/server/mocks/mock_gitlab.go
+++ b/server/mocks/mock_gitlab.go
@@ -128,19 +128,19 @@ func (mr *MockGitlabMockRecorder) GetReviews(arg0, arg1, arg2 interface{}) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviews", reflect.TypeOf((*MockGitlab)(nil).GetReviews), arg0, arg1, arg2)
}
-// GetUnreads mocks base method.
-func (m *MockGitlab) GetUnreads(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab0.Todo, error) {
+// GetToDoList mocks base method.
+func (m *MockGitlab) GetToDoList(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab0.Todo, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetUnreads", arg0, arg1, arg2)
+ ret := m.ctrl.Call(m, "GetToDoList", arg0, arg1, arg2)
ret0, _ := ret[0].([]*gitlab0.Todo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetUnreads indicates an expected call of GetUnreads.
-func (mr *MockGitlabMockRecorder) GetUnreads(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetToDoList indicates an expected call of GetToDoList.
+func (mr *MockGitlabMockRecorder) GetToDoList(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnreads", reflect.TypeOf((*MockGitlab)(nil).GetUnreads), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetToDoList", reflect.TypeOf((*MockGitlab)(nil).GetToDoList), arg0, arg1, arg2)
}
// GetUserDetails mocks base method.
@@ -158,49 +158,49 @@ func (mr *MockGitlabMockRecorder) GetUserDetails(arg0, arg1, arg2 interface{}) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserDetails", reflect.TypeOf((*MockGitlab)(nil).GetUserDetails), arg0, arg1, arg2)
}
-// GetYourAssignments mocks base method.
-func (m *MockGitlab) GetYourAssignments(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.Issue, error) {
+// GetYourAssignedIssues mocks base method.
+func (m *MockGitlab) GetYourAssignedIssues(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.Issue, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetYourAssignments", arg0, arg1, arg2)
+ ret := m.ctrl.Call(m, "GetYourAssignedIssues", arg0, arg1, arg2)
ret0, _ := ret[0].([]*gitlab.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetYourAssignments indicates an expected call of GetYourAssignments.
-func (mr *MockGitlabMockRecorder) GetYourAssignments(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetYourAssignedIssues indicates an expected call of GetYourAssignedIssues.
+func (mr *MockGitlabMockRecorder) GetYourAssignedIssues(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourAssignments", reflect.TypeOf((*MockGitlab)(nil).GetYourAssignments), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourAssignedIssues", reflect.TypeOf((*MockGitlab)(nil).GetYourAssignedIssues), arg0, arg1, arg2)
}
-// GetYourPrDetails mocks base method.
-func (m *MockGitlab) GetYourPrDetails(arg0 context.Context, arg1 logger.Logger, arg2 *gitlab.UserInfo, arg3 *oauth2.Token, arg4 []*gitlab.PRDetails) ([]*gitlab.PRDetails, error) {
+// GetYourAssignedPrs mocks base method.
+func (m *MockGitlab) GetYourAssignedPrs(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.MergeRequest, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetYourPrDetails", arg0, arg1, arg2, arg3, arg4)
- ret0, _ := ret[0].([]*gitlab.PRDetails)
+ ret := m.ctrl.Call(m, "GetYourAssignedPrs", arg0, arg1, arg2)
+ ret0, _ := ret[0].([]*gitlab.MergeRequest)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetYourPrDetails indicates an expected call of GetYourPrDetails.
-func (mr *MockGitlabMockRecorder) GetYourPrDetails(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+// GetYourAssignedPrs indicates an expected call of GetYourAssignedPrs.
+func (mr *MockGitlabMockRecorder) GetYourAssignedPrs(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourPrDetails", reflect.TypeOf((*MockGitlab)(nil).GetYourPrDetails), arg0, arg1, arg2, arg3, arg4)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourAssignedPrs", reflect.TypeOf((*MockGitlab)(nil).GetYourAssignedPrs), arg0, arg1, arg2)
}
-// GetYourPrs mocks base method.
-func (m *MockGitlab) GetYourPrs(arg0 context.Context, arg1 *gitlab.UserInfo, arg2 *gitlab0.Client) ([]*gitlab.MergeRequest, error) {
+// GetYourPrDetails mocks base method.
+func (m *MockGitlab) GetYourPrDetails(arg0 context.Context, arg1 logger.Logger, arg2 *gitlab.UserInfo, arg3 *oauth2.Token, arg4 []*gitlab.PRDetails) ([]*gitlab.PRDetails, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetYourPrs", arg0, arg1, arg2)
- ret0, _ := ret[0].([]*gitlab.MergeRequest)
+ ret := m.ctrl.Call(m, "GetYourPrDetails", arg0, arg1, arg2, arg3, arg4)
+ ret0, _ := ret[0].([]*gitlab.PRDetails)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// GetYourPrs indicates an expected call of GetYourPrs.
-func (mr *MockGitlabMockRecorder) GetYourPrs(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetYourPrDetails indicates an expected call of GetYourPrDetails.
+func (mr *MockGitlabMockRecorder) GetYourPrDetails(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourPrs", reflect.TypeOf((*MockGitlab)(nil).GetYourPrs), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetYourPrDetails", reflect.TypeOf((*MockGitlab)(nil).GetYourPrDetails), arg0, arg1, arg2, arg3, arg4)
}
// GitlabConnect mocks base method.
diff --git a/server/plugin.go b/server/plugin.go
index b980f3dd..52d93318 100644
--- a/server/plugin.go
+++ b/server/plugin.go
@@ -586,11 +586,11 @@ func (p *Plugin) GetToDo(ctx context.Context, user *gitlab.UserInfo) (bool, stri
return err
}
- unreads := resp.Unreads
+ todos := resp.Todos
notificationCount := 0
notificationContent := ""
- for _, n := range unreads {
+ for _, n := range todos {
if n == nil {
continue
}
@@ -610,9 +610,9 @@ func (p *Plugin) GetToDo(ctx context.Context, user *gitlab.UserInfo) (bool, stri
}
if notificationCount == 0 {
- notificationText += "You don't have any unread messages.\n"
+ notificationText += "You don't have any todos.\n"
} else {
- notificationText += fmt.Sprintf("You have %v unread messages:\n", notificationCount)
+ notificationText += fmt.Sprintf("You have %v todos:\n", notificationCount)
notificationText += notificationContent
hasTodo = true
@@ -631,25 +631,24 @@ func (p *Plugin) GetToDo(ctx context.Context, user *gitlab.UserInfo) (bool, stri
hasTodo = true
}
- yourAssignments := resp.Assignments
-
- if len(yourAssignments) == 0 {
+ yourAssignedIssues := resp.AssignedIssues
+ if len(yourAssignedIssues) == 0 {
assignmentText += "You don't have any issues awaiting your dev.\n"
} else {
- assignmentText += fmt.Sprintf("You have %v issues awaiting dev:\n", len(yourAssignments))
+ assignmentText += fmt.Sprintf("You have %v issues awaiting dev:\n", len(yourAssignedIssues))
- for _, pr := range yourAssignments {
+ for _, pr := range yourAssignedIssues {
assignmentText += fmt.Sprintf("* [%v](%v)\n", pr.Title, pr.WebURL)
}
hasTodo = true
}
- mergeRequests := resp.PRs
+ mergeRequests := resp.AssignedPRs
if len(mergeRequests) == 0 {
- mergeRequestText += "You don't have any open merge requests.\n"
+ mergeRequestText += "You don't have any merge requests assigned.\n"
} else {
- mergeRequestText += fmt.Sprintf("You have %v open merge requests:\n", len(mergeRequests))
+ mergeRequestText += fmt.Sprintf("You have %v merge requests assigned:\n", len(mergeRequests))
for _, pr := range mergeRequests {
mergeRequestText += fmt.Sprintf("* [%v](%v)\n", pr.Title, pr.WebURL)
@@ -664,16 +663,16 @@ func (p *Plugin) GetToDo(ctx context.Context, user *gitlab.UserInfo) (bool, stri
return false, "", err
}
- text := "##### Unread Messages\n"
+ text := "##### To-Do list\n"
text += notificationText
text += "##### Review Requests\n"
text += reviewText
- text += "##### Assignments\n"
+ text += "##### Issues\n"
text += assignmentText
- text += "##### Your Open Merge Requests\n"
+ text += "##### Merge Requests Assigned\n"
text += mergeRequestText
return hasTodo, text, nil
diff --git a/server/webhook/merge_request.go b/server/webhook/merge_request.go
index 11b50fe9..cfc82399 100644
--- a/server/webhook/merge_request.go
+++ b/server/webhook/merge_request.go
@@ -23,6 +23,11 @@ func (w *webhook) handleDMMergeRequest(event *gitlab.MergeEvent) ([]*HandleWebho
authorGitlabUsername := w.gitlabRetreiver.GetUsernameByID(event.ObjectAttributes.AuthorID)
senderGitlabUsername := event.User.Username
+ toUsers := []string{authorGitlabUsername}
+ for _, assigneeID := range event.ObjectAttributes.AssigneeIDs {
+ toUsers = append(toUsers, w.gitlabRetreiver.GetUsernameByID(assigneeID))
+ }
+
message := ""
switch event.ObjectAttributes.State {
@@ -31,9 +36,28 @@ func (w *webhook) handleDMMergeRequest(event *gitlab.MergeEvent) ([]*HandleWebho
case actionOpen:
message = fmt.Sprintf("[%s](%s) requested your review on [%s!%v](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.Target.PathWithNamespace, event.ObjectAttributes.IID, event.ObjectAttributes.URL)
case actionReopen:
- message = fmt.Sprintf("[%s](%s) reopen your merge request [%s!%v](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.Target.PathWithNamespace, event.ObjectAttributes.IID, event.ObjectAttributes.URL)
+ message = fmt.Sprintf("[%s](%s) reopened your merge request [%s!%v](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.Target.PathWithNamespace, event.ObjectAttributes.IID, event.ObjectAttributes.URL)
case actionUpdate:
- // TODO not enough check (opened/update) to say assigned to you...
+ toUsers = []string{authorGitlabUsername}
+
+ // Not going to show notification in case of commit push.
+ if event.ObjectAttributes.OldRev != "" {
+ break
+ }
+
+ for _, currentAssigneeID := range event.ObjectAttributes.AssigneeIDs {
+ assignedInPrevious := false
+ for _, previousAssignee := range event.Changes.Assignees.Previous {
+ if previousAssignee.ID == currentAssigneeID {
+ assignedInPrevious = true
+ break
+ }
+ }
+ if !assignedInPrevious {
+ toUsers = append(toUsers, w.gitlabRetreiver.GetUsernameByID(currentAssigneeID))
+ }
+ }
+
message = fmt.Sprintf("[%s](%s) assigned you to merge request [%s!%v](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.Target.PathWithNamespace, event.ObjectAttributes.IID, event.ObjectAttributes.URL)
case actionApproved:
message = fmt.Sprintf("[%s](%s) approved your merge request [%s!%v](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.Target.PathWithNamespace, event.ObjectAttributes.IID, event.ObjectAttributes.URL)
@@ -51,7 +75,7 @@ func (w *webhook) handleDMMergeRequest(event *gitlab.MergeEvent) ([]*HandleWebho
if len(message) > 0 {
handlers := []*HandleWebhook{{
Message: message,
- ToUsers: []string{w.gitlabRetreiver.GetUsernameByID(event.ObjectAttributes.AssigneeID), authorGitlabUsername},
+ ToUsers: toUsers,
ToChannels: []string{},
From: senderGitlabUsername,
}}
diff --git a/server/webhook/merge_request_fixture_test.go b/server/webhook/merge_request_fixture_test.go
index fcdd71e9..a92451cc 100644
--- a/server/webhook/merge_request_fixture_test.go
+++ b/server/webhook/merge_request_fixture_test.go
@@ -105,6 +105,9 @@ const OpenMergeRequest = `{
"total_time_spent":0,
"human_total_time_spent":null,
"human_time_estimate":null,
+ "assignee_ids": [
+ 50
+ ],
"action":"open"
},
"labels":[],
@@ -650,6 +653,9 @@ const AssigneeMergeRequest = `{
"total_time_spent":0,
"human_total_time_spent":null,
"human_time_estimate":null,
+ "assignee_ids": [
+ 50
+ ],
"action":"update"
},
"labels":[],
diff --git a/server/webhook/merge_request_test.go b/server/webhook/merge_request_test.go
index e557dbf8..fa5dcbd3 100644
--- a/server/webhook/merge_request_test.go
+++ b/server/webhook/merge_request_test.go
@@ -72,13 +72,13 @@ var testDataMergeRequest = []testDataMergeRequestStr{
From: "manland",
}},
}, {
- testTitle: "manland reopen merge request of root and display in channel1",
+ testTitle: "manland reopened merge request of root and display in channel1",
fixture: ReopenMerge,
gitlabRetreiver: newFakeWebhook([]*subscription.Subscription{
{ChannelID: "channel1", CreatorID: "1", Features: "merges", Repository: "manland/webhook"},
}),
res: []*HandleWebhook{{
- Message: "[manland](http://my.gitlab.com/manland) reopen your merge request [manland/webhook!1](http://localhost:3000/manland/webhook/merge_requests/1)",
+ Message: "[manland](http://my.gitlab.com/manland) reopened your merge request [manland/webhook!1](http://localhost:3000/manland/webhook/merge_requests/1)",
ToUsers: []string{"root"},
ToChannels: []string{},
From: "manland",
diff --git a/server/webhook/note.go b/server/webhook/note.go
index d188bd62..4865bc9e 100644
--- a/server/webhook/note.go
+++ b/server/webhook/note.go
@@ -42,7 +42,7 @@ func (w *webhook) handleDMIssueComment(event *gitlab.IssueCommentEvent) ([]*Hand
pathWithNamespace: event.Project.PathWithNamespace,
IID: fmt.Sprintf("%d", event.Issue.IID),
URL: event.ObjectAttributes.URL,
- body: event.ObjectAttributes.Note,
+ body: event.ObjectAttributes.Description,
}); mention != nil {
handlers = append(handlers, mention)
}
@@ -53,7 +53,7 @@ func (w *webhook) handleDMIssueComment(event *gitlab.IssueCommentEvent) ([]*Hand
func (w *webhook) handleChannelIssueComment(ctx context.Context, event *gitlab.IssueCommentEvent) ([]*HandleWebhook, error) {
senderGitlabUsername := event.User.Username
repo := event.Project
- body := event.ObjectAttributes.Note
+ body := event.ObjectAttributes.Description
res := []*HandleWebhook{}
message := fmt.Sprintf("[%s](%s) New comment by [%s](%s) on [#%v %s](%s):\n\n%s", repo.PathWithNamespace, repo.WebURL, senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.Issue.IID, event.Issue.Title, event.ObjectAttributes.URL, body)
@@ -113,7 +113,7 @@ func (w *webhook) handleDMMergeRequestComment(event *gitlab.MergeCommentEvent) (
pathWithNamespace: event.Project.PathWithNamespace,
IID: fmt.Sprintf("%d", event.MergeRequest.IID),
URL: event.ObjectAttributes.URL,
- body: event.ObjectAttributes.Note,
+ body: event.ObjectAttributes.Description,
}); mention != nil {
handlers = append(handlers, mention)
}
@@ -123,7 +123,7 @@ func (w *webhook) handleDMMergeRequestComment(event *gitlab.MergeCommentEvent) (
func (w *webhook) handleChannelMergeRequestComment(ctx context.Context, event *gitlab.MergeCommentEvent) ([]*HandleWebhook, error) {
senderGitlabUsername := event.User.Username
repo := event.Project
- body := event.ObjectAttributes.Note
+ body := event.ObjectAttributes.Description
res := []*HandleWebhook{}
message := fmt.Sprintf("[%s](%s) New comment by [%s](%s) on [#%v %s](%s):\n\n%s", repo.PathWithNamespace, repo.WebURL, senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.MergeRequest.IID, event.MergeRequest.Title, event.ObjectAttributes.URL, body)
diff --git a/webapp/src/actions/index.js b/webapp/src/actions/index.js
index 1b99348f..95f20fe4 100644
--- a/webapp/src/actions/index.js
+++ b/webapp/src/actions/index.js
@@ -1,3 +1,7 @@
+import {getCurrentChannelId, getCurrentUserId} from 'mattermost-redux/selectors/entities/common';
+
+import {PostTypes} from 'mattermost-redux/action_types';
+
import Client from '../client';
import ActionTypes from '../action_types';
import {id} from '../manifest';
@@ -223,3 +227,28 @@ export function getChannelSubscriptions(channelId) {
return {subscriptions};
};
}
+
+export function sendEphemeralPost(message) {
+ return (dispatch, getState) => {
+ const timestamp = Date.now();
+ const state = getState();
+
+ const post = {
+ id: 'gitlabPlugin' + Date.now(),
+ user_id: getCurrentUserId(state),
+ channel_id: getCurrentChannelId(state),
+ message,
+ type: 'system_ephemeral',
+ create_at: timestamp,
+ update_at: timestamp,
+ root_id: '',
+ parent_id: '',
+ props: {},
+ };
+
+ dispatch({
+ type: PostTypes.RECEIVED_NEW_POST,
+ data: post,
+ });
+ };
+}
diff --git a/webapp/src/components/rhs_sidebar/index.js b/webapp/src/components/rhs_sidebar/index.js
index 0bf219c5..2dc45b81 100644
--- a/webapp/src/components/rhs_sidebar/index.js
+++ b/webapp/src/components/rhs_sidebar/index.js
@@ -7,6 +7,7 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {id} from '../../manifest';
import {
getChannelSubscriptions,
+ sendEphemeralPost,
} from '../../actions';
import {getPluginServerRoute} from '../../selectors';
@@ -34,6 +35,7 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators(
{
getChannelSubscriptions,
+ sendEphemeralPost,
},
dispatch,
),
diff --git a/webapp/src/components/rhs_sidebar/rhs_sidebar.jsx b/webapp/src/components/rhs_sidebar/rhs_sidebar.jsx
index 1e560984..44166f35 100644
--- a/webapp/src/components/rhs_sidebar/rhs_sidebar.jsx
+++ b/webapp/src/components/rhs_sidebar/rhs_sidebar.jsx
@@ -1,6 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
+import {isDesktopApp} from 'src/utils/user_agent';
+import {connectUsingBrowserMessage} from 'src/constants';
+
import MattermostGitLabSVG from './mattermost_gitlab';
import NoSubscriptionsSVG from './no_subscriptions';
@@ -9,6 +12,10 @@ import './rhs_sidebar.css';
const NotSignedIn = (props) => {
const openConnectWindow = (e) => {
e.preventDefault();
+ if (isDesktopApp()) {
+ props.sendEphemeralPost(connectUsingBrowserMessage);
+ return;
+ }
window.open(`${props.pluginServerRoute}/oauth/connect`, 'Connect Mattermost to GitLab', 'height=570,width=520');
};
@@ -33,6 +40,7 @@ const NotSignedIn = (props) => {
NotSignedIn.propTypes = {
pluginServerRoute: PropTypes.string.isRequired,
+ sendEphemeralPost: PropTypes.func.isRequired,
};
const UserHeader = (props) => (
@@ -140,6 +148,7 @@ export default class RHSSidebar extends React.PureComponent {
pluginServerRoute: PropTypes.string.isRequired,
actions: PropTypes.shape({
getChannelSubscriptions: PropTypes.func.isRequired,
+ sendEphemeralPost: PropTypes.func.isRequired,
}).isRequired,
};
@@ -182,6 +191,7 @@ export default class RHSSidebar extends React.PureComponent {
return (
);
}
diff --git a/webapp/src/components/sidebar_buttons/button_icons.tsx b/webapp/src/components/sidebar_buttons/button_icons.tsx
new file mode 100644
index 00000000..518bb71d
--- /dev/null
+++ b/webapp/src/components/sidebar_buttons/button_icons.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+export const GitLabIssuesIcon = ({fill}: {fill: string}) => (
+
+)
+
+export const GitLabMergeRequestIcon = ({fill}: {fill: string}) => (
+
+)
+
+export const GitLabReviewsIcon = ({fill}: {fill: string}) => (
+
+)
+
+export const GitLabTodosIcon = ({fill}: {fill: string}) => (
+
+)
diff --git a/webapp/src/components/sidebar_buttons/index.js b/webapp/src/components/sidebar_buttons/index.js
index 4ad03720..45ef74c3 100644
--- a/webapp/src/components/sidebar_buttons/index.js
+++ b/webapp/src/components/sidebar_buttons/index.js
@@ -3,6 +3,7 @@ import {bindActionCreators} from 'redux';
import {
updateRHSState,
+ sendEphemeralPost,
getLHSData,
} from '../../actions';
@@ -18,9 +19,9 @@ function mapStateToProps(state) {
username: state[`plugins-${id}`].username,
clientId: state[`plugins-${id}`].clientId,
reviews: state[`plugins-${id}`].lhsData?.reviews,
- yourPrs: state[`plugins-${id}`].lhsData?.prs,
- yourAssignments: state[`plugins-${id}`].lhsData?.assignments,
- unreads: state[`plugins-${id}`].lhsData?.unreads,
+ yourAssignedPrs: state[`plugins-${id}`].lhsData?.yourAssignedPrs,
+ yourAssignedIssues: state[`plugins-${id}`].lhsData?.yourAssignedIssues,
+ todos: state[`plugins-${id}`].lhsData?.todos,
gitlabURL: state[`plugins-${id}`].gitlabURL,
org: state[`plugins-${id}`].organization,
pluginServerRoute: getPluginServerRoute(state),
@@ -33,6 +34,7 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators(
{
updateRHSState,
+ sendEphemeralPost,
getLHSData,
},
dispatch,
diff --git a/webapp/src/components/sidebar_buttons/sidebar_buttons.jsx b/webapp/src/components/sidebar_buttons/sidebar_buttons.jsx
index 3b4c1f17..1fdad391 100644
--- a/webapp/src/components/sidebar_buttons/sidebar_buttons.jsx
+++ b/webapp/src/components/sidebar_buttons/sidebar_buttons.jsx
@@ -3,7 +3,10 @@ import {Tooltip, OverlayTrigger} from 'react-bootstrap';
import PropTypes from 'prop-types';
import {makeStyleFromTheme, changeOpacity} from 'mattermost-redux/utils/theme_utils';
-import {RHSStates} from 'src/constants';
+import {RHSStates, connectUsingBrowserMessage} from 'src/constants';
+import {isDesktopApp} from 'src/utils/user_agent';
+
+import {GitLabIssuesIcon, GitLabMergeRequestIcon, GitLabReviewsIcon, GitLabTodosIcon} from './button_icons';
export default class SidebarButtons extends React.PureComponent {
static propTypes = {
@@ -14,14 +17,15 @@ export default class SidebarButtons extends React.PureComponent {
clientId: PropTypes.string,
gitlabURL: PropTypes.string,
reviews: PropTypes.arrayOf(PropTypes.object),
- unreads: PropTypes.arrayOf(PropTypes.object),
- yourPrs: PropTypes.arrayOf(PropTypes.object),
- yourAssignments: PropTypes.arrayOf(PropTypes.object),
+ todos: PropTypes.arrayOf(PropTypes.object),
+ yourAssignedPrs: PropTypes.arrayOf(PropTypes.object),
+ yourAssignedIssues: PropTypes.arrayOf(PropTypes.object),
isTeamSidebar: PropTypes.bool,
pluginServerRoute: PropTypes.string.isRequired,
showRHSPlugin: PropTypes.func.isRequired,
actions: PropTypes.shape({
updateRHSState: PropTypes.func.isRequired,
+ sendEphemeralPost: PropTypes.func.isRequired,
getLHSData: PropTypes.func.isRequired,
}).isRequired,
};
@@ -62,6 +66,10 @@ export default class SidebarButtons extends React.PureComponent {
openConnectWindow = (e) => {
e.preventDefault();
+ if (isDesktopApp()) {
+ this.props.actions.sendEphemeralPost(connectUsingBrowserMessage);
+ return;
+ }
window.open(`${this.props.pluginServerRoute}/oauth/connect`, 'Connect Mattermost to GitLab', 'height=570,width=520');
};
@@ -106,9 +114,9 @@ export default class SidebarButtons extends React.PureComponent {
const baseURL = this.props.gitlabURL || 'https://gitlab.com';
const reviews = this.props.reviews || [];
- const yourPrs = this.props.yourPrs || [];
- const unreads = this.props.unreads || [];
- const yourAssignments = this.props.yourAssignments || [];
+ const yourAssignedPrs = this.props.yourAssignedPrs || [];
+ const todos = this.props.todos || [];
+ const yourAssignedIssues = this.props.yourAssignedIssues || [];
const refreshClass = this.state.refreshing ? ' fa-spin' : '';
return (
@@ -123,55 +131,55 @@ export default class SidebarButtons extends React.PureComponent {
{'Your open merge requests'}}
+ overlay={{'Merge requests assigned'}}
>
this.openRHS(RHSStates.PRS)}
style={button}
>
-
- {' ' + yourPrs.length}
+
+ {yourAssignedPrs.length}
{'Merge requests that need review'}}
+ overlay={{'Merge requests needing review'}}
>
this.openRHS(RHSStates.REVIEWS)}
style={button}
>
-
- {' ' + reviews.length}
+
+ {reviews.length}
{'Your assignments'}}
+ overlay={{'Issues'}}
>
this.openRHS(RHSStates.ASSIGNMENTS)}
+ onClick={() => this.openRHS(RHSStates.ISSUES)}
style={button}
>
-
- {' ' + yourAssignments.length}
+
+ {yourAssignedIssues.length}
{'Unread messages'}}
+ overlay={{'To-Do list'}}
>
this.openRHS(RHSStates.UNREADS)}
+ onClick={() => this.openRHS(RHSStates.TODOS)}
style={button}
>
-
- {' ' + unreads.length}
+
+ {todos.length}
{
color: changeOpacity(theme.sidebarText, 0.6),
textAlign: 'center',
cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ },
+ buttonCount: {
+ marginLeft: '2px',
},
containerHeader: {
marginTop: '10px',
diff --git a/webapp/src/components/sidebar_right/index.tsx b/webapp/src/components/sidebar_right/index.tsx
index b161f137..a5f23942 100644
--- a/webapp/src/components/sidebar_right/index.tsx
+++ b/webapp/src/components/sidebar_right/index.tsx
@@ -24,9 +24,9 @@ interface Props {
org: string;
gitlabURL: string;
reviews: Item[];
- unreads: Item[],
- yourPrs: Item[],
- yourAssignments: Item[],
+ todos: Item[],
+ yourAssignedPrs: Item[],
+ yourAssignedIssues: Item[],
rhsState: string,
theme: Theme,
}
@@ -72,18 +72,18 @@ function shouldUpdateDetails(prs: Item[], prevPrs: Item[], targetState: string,
function SidebarRight({theme}: {theme: Theme}) {
const sidebarData = useSelector(getSidebarData);
- const {username, yourAssignments, org, unreads, gitlabURL, rhsState, reviews, yourPrs} = sidebarData;
+ const {username, yourAssignedIssues, org, todos, gitlabURL, rhsState, reviews, yourAssignedPrs} = sidebarData;
const dispatch = useDispatch();
- const prevPrs = usePrevious- (yourPrs)
+ const prevPrs = usePrevious
- (yourAssignedPrs)
const prevReviews = usePrevious
- (reviews)
useEffect(() => {
- if (yourPrs && (!prevPrs || shouldUpdateDetails(yourPrs, prevPrs, RHSStates.PRS, rhsState))) {
- dispatch(getYourPrDetails(yourPrs));
+ if (yourAssignedPrs && (!prevPrs || shouldUpdateDetails(yourAssignedPrs, prevPrs, RHSStates.PRS, rhsState))) {
+ dispatch(getYourPrDetails(yourAssignedPrs));
}
- }, [yourPrs, rhsState, prevPrs]);
+ }, [yourAssignedPrs, rhsState, prevPrs]);
useEffect(() => {
if (reviews && (!prevReviews || shouldUpdateDetails(reviews, prevReviews, RHSStates.REVIEWS, rhsState))) {
@@ -104,23 +104,23 @@ function SidebarRight({theme}: {theme: Theme}) {
switch (rhsState) {
case RHSStates.PRS:
- gitlabItems = yourPrs;
- title = 'Your Open Merge Requests';
- listUrl = `${baseURL}${orgQuery}/merge_requests?state=opened&author_username=${username}`;
+ gitlabItems = yourAssignedPrs;
+ title = 'Merge Requests Assigned';
+ listUrl = `${baseURL}${orgQuery}/merge_requests?state=opened&assignee_username=${username}`;
break;
case RHSStates.REVIEWS:
gitlabItems = reviews;
listUrl = `${baseURL}${orgQuery}/merge_requests?reviewer_username=${username}`;
title = 'Merge Requests Needing Review';
break;
- case RHSStates.UNREADS:
- gitlabItems = unreads;
- title = 'Unread Messages';
+ case RHSStates.TODOS:
+ gitlabItems = todos;
+ title = 'To-Do List';
listUrl = `${baseURL}/dashboard/todos`;
break;
- case RHSStates.ASSIGNMENTS:
- gitlabItems = yourAssignments;
- title = 'Your Assignments';
+ case RHSStates.ISSUES:
+ gitlabItems = yourAssignedIssues;
+ title = 'Issues';
listUrl = `${baseURL}${orgQuery}/issues?assignee_username=${username}`;
break;
default:
diff --git a/webapp/src/constants/index.js b/webapp/src/constants/index.js
index 2c1da24b..eb02975f 100644
--- a/webapp/src/constants/index.js
+++ b/webapp/src/constants/index.js
@@ -7,6 +7,8 @@ export default {
export const RHSStates = {
PRS: 'pullRequests',
REVIEWS: 'reviews',
- UNREADS: 'unreads',
- ASSIGNMENTS: 'assignments',
+ TODOS: 'todos',
+ ISSUES: 'issues',
};
+
+export const connectUsingBrowserMessage = 'Mattermost desktop client does not support authenticating between Gitlab and Mattermost directly. To connect your Gitlab account with Mattermost, please log in to Mattermost via your web browser and type `/gitlab connect`.';
diff --git a/webapp/src/hooks/index.ts b/webapp/src/hooks/index.ts
new file mode 100644
index 00000000..af4882bc
--- /dev/null
+++ b/webapp/src/hooks/index.ts
@@ -0,0 +1,37 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {isDesktopApp} from 'src/utils/user_agent';
+import {connectUsingBrowserMessage} from 'src/constants';
+import {sendEphemeralPost} from '../actions';
+import {Store} from 'redux';
+
+type ContextArgs = {channel_id: string};
+
+const connectCommand = '/gitlab connect';
+
+export default class Hooks {
+ private store: Store;
+
+ constructor(store: Store) {
+ this.store = store;
+ }
+
+ slashCommandWillBePostedHook = (rawMessage: string, contextArgs: ContextArgs) => {
+ let message;
+ if (rawMessage) {
+ message = rawMessage.trim();
+ }
+
+ if (!message) {
+ return Promise.resolve({message, args: contextArgs});
+ }
+
+ if (message.startsWith(connectCommand) && isDesktopApp()) {
+ sendEphemeralPost(connectUsingBrowserMessage)(this.store.dispatch, this.store.getState);
+ return Promise.resolve({});
+ }
+
+ return Promise.resolve({message, args: contextArgs});
+ }
+}
diff --git a/webapp/src/index.js b/webapp/src/index.js
index 1a6fb841..1dec0ad0 100644
--- a/webapp/src/index.js
+++ b/webapp/src/index.js
@@ -7,8 +7,9 @@ import SidebarHeader from './components/sidebar_header';
import TeamSidebar from './components/team_sidebar';
import RHSSidebar from './components/rhs_sidebar';
import UserAttribute from './components/user_attribute';
-import Reducer from './reducers';
import SidebarRight from './components/sidebar_right';
+
+import Reducer from './reducers';
import {getConnected, setShowRHSAction} from './actions';
import {
handleConnect,
@@ -20,6 +21,7 @@ import {
import {id} from './manifest';
import Client from './client';
import {getPluginServerRoute} from './selectors';
+import Hooks from './hooks';
let activityFunc;
let lastActivityTime = Number.MAX_SAFE_INTEGER;
@@ -38,6 +40,9 @@ class PluginClass {
registry.registerBottomTeamSidebarComponent(TeamSidebar);
registry.registerPopoverUserAttributesComponent(UserAttribute);
+ const hooks = new Hooks(store);
+ registry.registerSlashCommandWillBePostedHook(hooks.slashCommandWillBePostedHook);
+
const {showRHSPlugin} = registry.registerRightHandSidebarComponent(SidebarRight, 'GitLab Plugin');
store.dispatch(setShowRHSAction(() => store.dispatch(showRHSPlugin)));
diff --git a/webapp/src/selectors/index.js b/webapp/src/selectors/index.js
index f9fbc364..79041778 100644
--- a/webapp/src/selectors/index.js
+++ b/webapp/src/selectors/index.js
@@ -48,10 +48,10 @@ export const getSidebarData = createSelector(
username: pluginState.username,
reviewDetails: pluginState.reviewDetails,
reviews: mapPrsToDetails(pluginState.lhsData?.reviews, pluginState.reviewDetails),
- yourPrs: mapPrsToDetails(pluginState.lhsData?.prs, pluginState.yourPrDetails),
+ yourAssignedPrs: mapPrsToDetails(pluginState.lhsData?.yourAssignedPrs, pluginState.yourPrDetails),
yourPrDetails: pluginState.yourPrDetails,
- yourAssignments: pluginState.lhsData?.assignments,
- unreads: pluginState.lhsData?.unreads,
+ yourAssignedIssues: pluginState.lhsData?.yourAssignedIssues,
+ todos: pluginState.lhsData?.todos,
org: pluginState.organization,
gitlabURL: pluginState.gitlabURL,
rhsState: pluginState.rhsState,
diff --git a/webapp/src/utils/user_agent.js b/webapp/src/utils/user_agent.js
new file mode 100644
index 00000000..6f980f4e
--- /dev/null
+++ b/webapp/src/utils/user_agent.js
@@ -0,0 +1,3 @@
+const userAgent = window.navigator.userAgent;
+
+export const isDesktopApp = () => userAgent.indexOf('Mattermost') !== -1 && userAgent.indexOf('Electron') !== -1;