diff --git a/server/bot.go b/server/bot.go index dfabdd0e..142e1b34 100644 --- a/server/bot.go +++ b/server/bot.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strings" "github.com/mattermost/mattermost/server/public/model" @@ -16,16 +17,17 @@ func (p *Plugin) PostBotDM(userID string, message string) { } // PostBotCustomDM posts a DM as the cloud bot user using custom post with action buttons. -func (p *Plugin) PostBotCustomDM(userID string, message string, todo string, issueID string) { +func (p *Plugin) PostBotCustomDM(userID, message, todo, postPermalink, issueID string) { p.createBotPostDM(&model.Post{ UserId: p.BotUserID, Message: message + ": " + todo, Type: "custom_todo", Props: map[string]interface{}{ - "type": "custom_todo", - "message": message, - "todo": todo, - "issueId": issueID, + "type": "custom_todo", + "message": message, + "todo": todo, + "postPermalink": postPermalink, + "issueId": issueID, }, }, userID) } @@ -51,7 +53,7 @@ func (p *Plugin) createBotPostDM(post *model.Post, userID string) { } // ReplyPostBot post a message and a todo in the same thread as the post postID -func (p *Plugin) ReplyPostBot(postID, message, todo string) error { +func (p *Plugin) ReplyPostBot(postID, message, todo, postPermalink string) error { if postID == "" { return errors.New("post ID not defined") } @@ -65,7 +67,8 @@ func (p *Plugin) ReplyPostBot(postID, message, todo string) error { rootID = post.RootId } - quotedTodo := "\n> " + strings.Join(strings.Split(todo, "\n"), "\n> ") + postPermalink = fmt.Sprintf("[Permalink](%s)", postPermalink) + quotedTodo := "\n> " + strings.Join([]string{todo, postPermalink}, "\n> ") _, appErr = p.API.CreatePost(&model.Post{ UserId: p.BotUserID, ChannelId: post.ChannelId, diff --git a/server/command.go b/server/command.go index a03192c8..c3c04857 100644 --- a/server/command.go +++ b/server/command.go @@ -185,7 +185,7 @@ func (p *Plugin) runSendCommand(args []string, extra *model.CommandArgs) (bool, message := strings.Join(args[1:], " ") - receiverIssueID, err := p.listManager.SendIssue(extra.UserId, receiver.Id, message, "", "") + receiverIssueID, err := p.listManager.SendIssue(extra.UserId, receiver.Id, message, "", "", "") if err != nil { return false, err } @@ -201,7 +201,8 @@ func (p *Plugin) runSendCommand(args []string, extra *model.CommandArgs) (bool, receiverMessage := fmt.Sprintf("You have received a new Todo from @%s", senderName) - p.PostBotCustomDM(receiver.Id, receiverMessage, message, receiverIssueID) + postPermalink := "" + p.PostBotCustomDM(receiver.Id, receiverMessage, message, postPermalink, receiverIssueID) p.postCommandResponse(extra, responseMessage) return false, nil } @@ -214,7 +215,7 @@ func (p *Plugin) runAddCommand(args []string, extra *model.CommandArgs) (bool, e return false, nil } - newIssue, err := p.listManager.AddIssue(extra.UserId, message, "", "") + newIssue, err := p.listManager.AddIssue(extra.UserId, message, "", "", "") if err != nil { return false, err } @@ -311,7 +312,7 @@ func (p *Plugin) runPopCommand(_ []string, extra *model.CommandArgs) (bool, erro responseMessage := "Removed top Todo." replyMessage := fmt.Sprintf("@%s popped a todo attached to this thread", userName) - p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message) + p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message, issue.PostPermalink) issues, err := p.listManager.GetIssueList(extra.UserId, MyListKey) if err != nil { diff --git a/server/issue.go b/server/issue.go index 085c865a..249e383d 100644 --- a/server/issue.go +++ b/server/issue.go @@ -9,11 +9,12 @@ import ( // Issue represents a Todo issue type Issue struct { - ID string `json:"id"` - Message string `json:"message"` - Description string `json:"description,omitempty"` - CreateAt int64 `json:"create_at"` - PostID string `json:"post_id"` + ID string `json:"id"` + Message string `json:"message"` + PostPermalink string `json:"postPermalink"` + Description string `json:"description,omitempty"` + CreateAt int64 `json:"create_at"` + PostID string `json:"post_id"` } // ExtendedIssue extends the information on Issue to be used on the front-end @@ -24,13 +25,14 @@ type ExtendedIssue struct { ForeignPosition int `json:"position"` } -func newIssue(message string, description, postID string) *Issue { +func newIssue(message, postPermalink, description, postID string) *Issue { return &Issue{ - ID: model.NewId(), - CreateAt: model.GetMillis(), - Message: message, - Description: description, - PostID: postID, + ID: model.NewId(), + CreateAt: model.GetMillis(), + Message: message, + PostPermalink: postPermalink, + Description: description, + PostID: postID, } } diff --git a/server/list.go b/server/list.go index e95a2919..52c9a66b 100644 --- a/server/list.go +++ b/server/list.go @@ -58,8 +58,8 @@ func NewListManager(api plugin.API) ListManager { } } -func (l *listManager) AddIssue(userID, message, description, postID string) (*Issue, error) { - issue := newIssue(message, description, postID) +func (l *listManager) AddIssue(userID, message, postPermalink, description, postID string) (*Issue, error) { + issue := newIssue(message, postPermalink, description, postID) if err := l.store.SaveIssue(issue); err != nil { return nil, err @@ -75,13 +75,13 @@ func (l *listManager) AddIssue(userID, message, description, postID string) (*Is return issue, nil } -func (l *listManager) SendIssue(senderID, receiverID, message, description, postID string) (string, error) { - senderIssue := newIssue(message, description, postID) +func (l *listManager) SendIssue(senderID, receiverID, message, postPermalink, description, postID string) (string, error) { + senderIssue := newIssue(message, postPermalink, description, postID) if err := l.store.SaveIssue(senderIssue); err != nil { return "", err } - receiverIssue := newIssue(message, description, postID) + receiverIssue := newIssue(message, postPermalink, description, postID) if err := l.store.SaveIssue(receiverIssue); err != nil { if rollbackError := l.store.RemoveIssue(senderIssue.ID); rollbackError != nil { l.api.LogError("cannot rollback sender issue after send error, Err=", err.Error()) @@ -201,30 +201,30 @@ func (l *listManager) EditIssue(userID, issueID, newMessage, newDescription stri return ir.ForeignUserID, list, oldMessage, nil } -func (l *listManager) ChangeAssignment(issueID string, userID string, sendTo string) (issueMessage, oldOwner string, err error) { - issue, err := l.store.GetIssue(issueID) +func (l *listManager) ChangeAssignment(issueID string, userID string, sendTo string) (issue *Issue, oldOwner string, err error) { + issue, err = l.store.GetIssue(issueID) if err != nil { - return "", "", err + return nil, "", err } list, ir, _ := l.store.GetIssueListAndReference(userID, issueID) if ir == nil { - return "", "", errors.New("reference not found") + return nil, "", errors.New("reference not found") } if (list == InListKey) || (ir.ForeignIssueID != "" && list == MyListKey) { - return "", "", errors.New("trying to change the assignment of a todo not owned") + return nil, "", errors.New("trying to change the assignment of a todo not owned") } if ir.ForeignUserID != "" { // Remove reference from foreign user foreignList, foreignIR, _ := l.store.GetIssueListAndReference(ir.ForeignUserID, ir.ForeignIssueID) if foreignIR == nil { - return "", "", errors.New("reference not found") + return nil, "", errors.New("reference not found") } if err := l.store.RemoveReference(ir.ForeignUserID, ir.ForeignIssueID, foreignList); err != nil { - return "", "", err + return nil, "", err } _, err := l.store.GetAndRemoveIssue(ir.ForeignIssueID) @@ -235,36 +235,36 @@ func (l *listManager) ChangeAssignment(issueID string, userID string, sendTo str if userID == sendTo && list == OutListKey { if err := l.store.RemoveReference(userID, issueID, OutListKey); err != nil { - return "", "", err + return nil, "", err } if err := l.store.AddReference(userID, issueID, MyListKey, "", ""); err != nil { - return "", "", err + return nil, "", err } - return issue.Message, ir.ForeignUserID, nil + return issue, ir.ForeignUserID, nil } if userID != sendTo { if err := l.store.RemoveReference(userID, issueID, list); err != nil { - return "", "", err + return nil, "", err } } - receiverIssue := newIssue(issue.Message, issue.Description, issue.PostID) + receiverIssue := newIssue(issue.Message, issue.PostPermalink, issue.Description, issue.PostID) if err := l.store.SaveIssue(receiverIssue); err != nil { - return "", "", err + return nil, "", err } if err := l.store.AddReference(userID, issueID, OutListKey, sendTo, receiverIssue.ID); err != nil { - return "", "", err + return nil, "", err } if err := l.store.AddReference(sendTo, receiverIssue.ID, InListKey, userID, issue.ID); err != nil { - return "", "", err + return nil, "", err } - return issue.Message, ir.ForeignUserID, nil + return issue, ir.ForeignUserID, nil } func (l *listManager) AcceptIssue(userID, issueID string) (todoMessage string, foreignUserID string, outErr error) { @@ -294,6 +294,10 @@ func (l *listManager) AcceptIssue(userID, issueID string) (todoMessage string, f return "", "", err } + if issue.PostPermalink != "" { + issue.Message = fmt.Sprintf("%s\n[Permalink](%s)", issue.Message, issue.PostPermalink) + } + return issue.Message, ir.ForeignUserID, nil } @@ -362,28 +366,28 @@ func (l *listManager) PopIssue(userID string) (issue *Issue, foreignID string, e return issue, ir.ForeignUserID, nil } -func (l *listManager) BumpIssue(userID, issueID string) (todoMessage string, receiver string, foreignIssueID string, outErr error) { +func (l *listManager) BumpIssue(userID, issueID string) (todo *Issue, receiver string, foreignIssueID string, outErr error) { ir, _, err := l.store.GetIssueReference(userID, issueID, OutListKey) if err != nil { - return "", "", "", err + return nil, "", "", err } if ir == nil { - return "", "", "", fmt.Errorf("cannot find sender issue") + return nil, "", "", fmt.Errorf("cannot find sender issue") } err = l.store.BumpReference(ir.ForeignUserID, ir.ForeignIssueID, InListKey) if err != nil { - return "", "", "", err + return nil, "", "", err } issue, err := l.store.GetIssue(ir.ForeignIssueID) if err != nil { l.api.LogError("cannot find foreigner issue after bump, Err=", err.Error()) - return "", "", "", nil + return nil, "", "", nil } - return issue.Message, ir.ForeignUserID, ir.ForeignIssueID, nil + return issue, ir.ForeignUserID, ir.ForeignIssueID, nil } func (l *listManager) GetUserName(userID string) string { diff --git a/server/plugin.go b/server/plugin.go index ae349f37..e9651fa5 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -28,9 +28,9 @@ const ( // ListManager represents the logic on the lists type ListManager interface { // AddIssue adds a todo to userID's myList with the message - AddIssue(userID, message, description, postID string) (*Issue, error) + AddIssue(userID, message, postPermalink, description, postID string) (*Issue, error) // SendIssue sends the todo with the message from senderID to receiverID and returns the receiver's issueID - SendIssue(senderID, receiverID, message, description, postID string) (string, error) + SendIssue(senderID, receiverID, message, postPermalink, description, postID string) (string, error) // GetIssueList gets the todos on listID for userID GetIssueList(userID, listID string) ([]*ExtendedIssue, error) // CompleteIssue completes the todo issueID for userID, and returns the issue and the foreign ID if any @@ -42,11 +42,11 @@ type ListManager interface { // PopIssue the first element of myList for userID and returns the issue and the foreign ID if any PopIssue(userID string) (issue *Issue, foreignID string, err error) // BumpIssue moves a issueID sent by userID to the top of its receiver inbox list - BumpIssue(userID string, issueID string) (todoMessage string, receiver string, foreignIssueID string, err error) + BumpIssue(userID string, issueID string) (todo *Issue, receiver string, foreignIssueID string, err error) // EditIssue updates the message on an issue EditIssue(userID string, issueID string, newMessage string, newDescription string) (foreignUserID string, list string, oldMessage string, err error) // ChangeAssignment updates an issue to assign a different person - ChangeAssignment(issueID string, userID string, sendTo string) (issueMessage, oldOwner string, err error) + ChangeAssignment(issueID string, userID string, sendTo string) (issue *Issue, oldOwner string, err error) // GetUserName returns the readable username from userID GetUserName(userID string) string } @@ -207,7 +207,7 @@ func (p *Plugin) handleAdd(w http.ResponseWriter, r *http.Request) { senderName := p.listManager.GetUserName(userID) if addRequest.SendTo == "" { - _, err = p.listManager.AddIssue(userID, addRequest.Message, addRequest.Description, addRequest.PostID) + _, err = p.listManager.AddIssue(userID, addRequest.Message, addRequest.PostPermalink, addRequest.Description, addRequest.PostID) if err != nil { p.API.LogError("Unable to add the issue err=" + err.Error()) p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to add issue", err) @@ -219,7 +219,7 @@ func (p *Plugin) handleAdd(w http.ResponseWriter, r *http.Request) { p.sendRefreshEvent(userID, []string{MyListKey}) replyMessage := fmt.Sprintf("@%s attached a todo to this thread", senderName) - p.postReplyIfNeeded(addRequest.PostID, replyMessage, addRequest.Message) + p.postReplyIfNeeded(addRequest.PostID, replyMessage, addRequest.Message, addRequest.PostPermalink) return } @@ -232,7 +232,7 @@ func (p *Plugin) handleAdd(w http.ResponseWriter, r *http.Request) { } if receiver.Id == userID { - _, err = p.listManager.AddIssue(userID, addRequest.Message, addRequest.Description, addRequest.PostID) + _, err = p.listManager.AddIssue(userID, addRequest.Message, addRequest.Description, addRequest.PostID, addRequest.PostPermalink) if err != nil { p.API.LogError("Unable to add issue err=" + err.Error()) p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to add issue", err) @@ -244,7 +244,7 @@ func (p *Plugin) handleAdd(w http.ResponseWriter, r *http.Request) { p.sendRefreshEvent(userID, []string{MyListKey}) replyMessage := fmt.Sprintf("@%s attached a todo to this thread", senderName) - p.postReplyIfNeeded(addRequest.PostID, replyMessage, addRequest.Message) + p.postReplyIfNeeded(addRequest.PostID, replyMessage, addRequest.Message, addRequest.PostPermalink) return } @@ -259,7 +259,7 @@ func (p *Plugin) handleAdd(w http.ResponseWriter, r *http.Request) { return } - issueID, err := p.listManager.SendIssue(userID, receiver.Id, addRequest.Message, addRequest.Description, addRequest.PostID) + issueID, err := p.listManager.SendIssue(userID, receiver.Id, addRequest.Message, addRequest.PostPermalink, addRequest.Description, addRequest.PostID) if err != nil { p.API.LogError("Unable to send issue err=" + err.Error()) p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to send issue", err) @@ -272,15 +272,15 @@ func (p *Plugin) handleAdd(w http.ResponseWriter, r *http.Request) { p.sendRefreshEvent(receiver.Id, []string{InListKey}) receiverMessage := fmt.Sprintf("You have received a new Todo from @%s", senderName) - p.PostBotCustomDM(receiver.Id, receiverMessage, addRequest.Message, issueID) + p.PostBotCustomDM(receiver.Id, receiverMessage, addRequest.Message, addRequest.PostPermalink, issueID) replyMessage := fmt.Sprintf("@%s sent @%s a todo attached to this thread", senderName, addRequest.SendTo) - p.postReplyIfNeeded(addRequest.PostID, replyMessage, addRequest.Message) + p.postReplyIfNeeded(addRequest.PostID, replyMessage, addRequest.Message, addRequest.PostPermalink) } -func (p *Plugin) postReplyIfNeeded(postID, message, todo string) { +func (p *Plugin) postReplyIfNeeded(postID, message, todo, postPermalink string) { if postID != "" { - err := p.ReplyPostBot(postID, message, todo) + err := p.ReplyPostBot(postID, message, todo, postPermalink) if err != nil { p.API.LogError(err.Error()) } @@ -408,7 +408,7 @@ func (p *Plugin) handleChangeAssignment(w http.ResponseWriter, r *http.Request) return } - issueMessage, oldOwner, err := p.listManager.ChangeAssignment(changeRequest.ID, userID, receiver.Id) + issue, oldOwner, err := p.listManager.ChangeAssignment(changeRequest.ID, userID, receiver.Id) if err != nil { p.API.LogError("Unable to change the assignment of an issue: err=" + err.Error()) p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to change the assignment", err) @@ -423,11 +423,11 @@ func (p *Plugin) handleChangeAssignment(w http.ResponseWriter, r *http.Request) if receiver.Id != userID { p.sendRefreshEvent(receiver.Id, []string{InListKey}) receiverMessage := fmt.Sprintf("You have received a new Todo from @%s", userName) - p.PostBotCustomDM(receiver.Id, receiverMessage, issueMessage, changeRequest.ID) + p.PostBotCustomDM(receiver.Id, receiverMessage, issue.Message, issue.PostPermalink, changeRequest.ID) } if oldOwner != "" { p.sendRefreshEvent(oldOwner, []string{InListKey, MyListKey}) - oldOwnerMessage := fmt.Sprintf("@%s removed you from Todo:\n%s", userName, issueMessage) + oldOwnerMessage := fmt.Sprintf("@%s removed you from Todo:\n%s", userName, issue.Message) p.PostBotDM(oldOwner, oldOwnerMessage) } } @@ -492,7 +492,7 @@ func (p *Plugin) handleComplete(w http.ResponseWriter, r *http.Request) { userName := p.listManager.GetUserName(userID) replyMessage := fmt.Sprintf("@%s completed a todo attached to this thread", userName) - p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message) + p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message, issue.PostPermalink) if foreignID == "" { return @@ -500,6 +500,10 @@ func (p *Plugin) handleComplete(w http.ResponseWriter, r *http.Request) { p.sendRefreshEvent(foreignID, []string{OutListKey}) + if issue.PostPermalink != "" { + issue.Message = fmt.Sprintf("%s\n[Permalink](%s)", issue.Message, issue.PostPermalink) + } + message := fmt.Sprintf("@%s completed a Todo you sent: %s", userName, issue.Message) p.PostBotDM(foreignID, message) } @@ -531,7 +535,7 @@ func (p *Plugin) handleRemove(w http.ResponseWriter, r *http.Request) { userName := p.listManager.GetUserName(userID) replyMessage := fmt.Sprintf("@%s removed a todo attached to this thread", userName) - p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message) + p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message, issue.PostPermalink) if foreignID == "" { return @@ -545,6 +549,10 @@ func (p *Plugin) handleRemove(w http.ResponseWriter, r *http.Request) { list = OutListKey } + if issue.PostPermalink != "" { + message = fmt.Sprintf("%s\n[Permalink](%s)", message, issue.PostPermalink) + } + p.sendRefreshEvent(foreignID, []string{list}) p.PostBotDM(foreignID, message) @@ -565,7 +573,7 @@ func (p *Plugin) handleBump(w http.ResponseWriter, r *http.Request) { return } - todoMessage, foreignUser, foreignIssueID, err := p.listManager.BumpIssue(userID, bumpRequest.ID) + todo, foreignUser, foreignIssueID, err := p.listManager.BumpIssue(userID, bumpRequest.ID) if err != nil { p.API.LogError("Unable to bump issue, err=" + err.Error()) p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to bump issue", err) @@ -582,7 +590,7 @@ func (p *Plugin) handleBump(w http.ResponseWriter, r *http.Request) { userName := p.listManager.GetUserName(userID) message := fmt.Sprintf("@%s bumped a Todo you received.", userName) - p.PostBotCustomDM(foreignUser, message, todoMessage, foreignIssueID) + p.PostBotCustomDM(foreignUser, message, todo.Message, todo.PostPermalink, foreignIssueID) } // API endpoint to retrieve plugin configurations diff --git a/server/serializer.go b/server/serializer.go index ddad9a78..656a42b8 100644 --- a/server/serializer.go +++ b/server/serializer.go @@ -34,10 +34,11 @@ func (t *TelemetryAPIRequest) IsValid() error { } type AddAPIRequest struct { - Message string `json:"message"` - Description string `json:"description"` - SendTo string `json:"send_to"` - PostID string `json:"post_id"` + Message string `json:"message"` + PostPermalink string `json:"postPermalink"` + Description string `json:"description"` + SendTo string `json:"send_to"` + PostID string `json:"post_id"` } func GetAddIssuePayloadFromJSON(data io.Reader) (*AddAPIRequest, error) { diff --git a/webapp/src/actions.js b/webapp/src/actions.js index 3c1d29ef..485d1997 100644 --- a/webapp/src/actions.js +++ b/webapp/src/actions.js @@ -120,10 +120,10 @@ export const telemetry = (event, properties) => async (dispatch, getState) => { })); }; -export const add = (message, description, sendTo, postID) => async (dispatch, getState) => { +export const add = (message, postPermalink, description, sendTo, postID) => async (dispatch, getState) => { await fetch(getPluginServerRoute(getState()) + '/add', Client4.getOptions({ method: 'post', - body: JSON.stringify({send_to: sendTo, message, description, post_id: postID}), + body: JSON.stringify({send_to: sendTo, message, postPermalink, description, post_id: postID}), })); }; diff --git a/webapp/src/components/add_issue/add_issue.jsx b/webapp/src/components/add_issue/add_issue.jsx index 3e521b85..5d7fe7f9 100644 --- a/webapp/src/components/add_issue/add_issue.jsx +++ b/webapp/src/components/add_issue/add_issue.jsx @@ -22,6 +22,7 @@ export default class AddIssue extends React.PureComponent { static propTypes = { visible: PropTypes.bool.isRequired, message: PropTypes.string.isRequired, + postPermalink: PropTypes.string, postID: PropTypes.string.isRequired, assignee: PropTypes.object, closeAddBox: PropTypes.func.isRequired, @@ -37,6 +38,7 @@ export default class AddIssue extends React.PureComponent { this.state = { message: props.message || '', + postPermalink: props.postPermalink || '', description: '', sendTo: null, attachToThread: false, @@ -46,12 +48,16 @@ export default class AddIssue extends React.PureComponent { } static getDerivedStateFromProps(props, state) { - if (props.visible && !state.message && props.message !== state.message) { - return {message: props.message}; + if (props.visible && (props.message !== state.message || props.postPermalink !== state.postPermalink)) { + return { + message: props.message, + postPermalink: props.postPermalink, + }; } if (!props.visible && (state.message || state.sendTo)) { return { message: '', + postPermalink: '', sendTo: null, attachToThread: false, previewMarkdown: false, @@ -77,22 +83,23 @@ export default class AddIssue extends React.PureComponent { submit = () => { const {submit, postID, assignee, closeAddBox, removeAssignee} = this.props; - const {message, description, attachToThread, sendTo} = this.state; + const {message, postPermalink, description, attachToThread, sendTo} = this.state; this.setState({ message: '', description: '', + postPermalink: '', }); if (attachToThread) { if (assignee) { - submit(message, description, assignee.username, postID); + submit(message, postPermalink, description, assignee.username, postID); } else { - submit(message, description, sendTo, postID); + submit(message, postPermalink, description, sendTo, postID); } } else if (assignee) { - submit(message, description, assignee.username); + submit(message, postPermalink, description, assignee.username); } else { - submit(message, description); + submit(message, postPermalink, description); } removeAssignee(); @@ -113,6 +120,12 @@ export default class AddIssue extends React.PureComponent { } } + handleInputChange = (e, field) => { + this.setState({ + [field]: e.target.value, + }); + } + render() { const {assignee, visible, theme} = this.props; @@ -120,9 +133,9 @@ export default class AddIssue extends React.PureComponent { return null; } - const {message, description} = this.state; - + const {message, description, postPermalink} = this.state; const style = getStyle(theme); + const formattedMessage = message.includes(`[Permalink](${postPermalink})`) ? message : message + (postPermalink ? `\n[Permalink](${postPermalink})` : ''); return (
@@ -136,7 +149,7 @@ export default class AddIssue extends React.PureComponent { style={style.markdown} > {PostUtils.messageHtmlToComponent( - PostUtils.formatText(this.state.message), + PostUtils.formatText(formattedMessage), )}
) : ( @@ -146,23 +159,15 @@ export default class AddIssue extends React.PureComponent { placeholder='Enter a title' autoFocus={true} onKeyDown={(e) => this.onKeyDown(e)} - value={message} - onChange={(e) => - this.setState({ - message: e.target.value, - }) - } + value={formattedMessage} + onChange={(e) => this.handleInputChange(e, 'message')} /> this.onKeyDown(e)} value={description} - onChange={(e) => - this.setState({ - description: e.target.value, - }) - } + onChange={(e) => this.handleInputChange(e, 'description')} /> )} diff --git a/webapp/src/components/add_issue/index.js b/webapp/src/components/add_issue/index.js index b16052cb..f742dada 100644 --- a/webapp/src/components/add_issue/index.js +++ b/webapp/src/components/add_issue/index.js @@ -9,16 +9,17 @@ import AddIssue from './add_issue'; function mapStateToProps(state) { const postID = getPostID(state); - let permalink = ''; + let postPermalink = ''; if (postID) { - permalink = `\n[Permalink](${getCurrentTeamRoute(state)}pl/${postID})`; + postPermalink = `${getCurrentTeamRoute(state)}pl/${postID}`; } - const message = getMessage(state) + permalink; + const message = getMessage(state); return { visible: isAddCardVisible(state), message, + postPermalink, postID: getPostID(state), assignee: getAssignee(state), }; diff --git a/webapp/src/components/post_type_todo/post_type_todo.jsx b/webapp/src/components/post_type_todo/post_type_todo.jsx index 75aa1320..5d32a29c 100644 --- a/webapp/src/components/post_type_todo/post_type_todo.jsx +++ b/webapp/src/components/post_type_todo/post_type_todo.jsx @@ -7,6 +7,8 @@ import RemoveButton from '../buttons/remove'; import CompleteButton from '../buttons/complete'; import AcceptButton from '../buttons/accept'; +import PostPermalink from '../todo_item/post_permalink'; + const PostUtils = window.PostUtils; // import the post utilities export default class PostTypeTodo extends React.PureComponent { @@ -83,6 +85,7 @@ export default class PostTypeTodo extends React.PureComponent { {title} {subtitle} + {this.props.post.props.postPermalink && }
{this.props.pendingAnswer && content}
diff --git a/webapp/src/components/todo_item/post_permalink.tsx b/webapp/src/components/todo_item/post_permalink.tsx new file mode 100644 index 00000000..107cb44e --- /dev/null +++ b/webapp/src/components/todo_item/post_permalink.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +type PostPermalinkProps = { + postPermalink: string; +} + +const PostPermalink = ({postPermalink}: PostPermalinkProps) => ( + + + {'Permalink'} + + +); + +export default PostPermalink; diff --git a/webapp/src/components/todo_item/todo_item.jsx b/webapp/src/components/todo_item/todo_item.jsx index 917bc10a..cb43b8ed 100644 --- a/webapp/src/components/todo_item/todo_item.jsx +++ b/webapp/src/components/todo_item/todo_item.jsx @@ -19,6 +19,8 @@ import MenuItem from '../../widget/menuItem'; import MenuWrapper from '../../widget/menuWrapper'; import Button from '../../widget/buttons/button'; +import PostPermalink from './post_permalink'; + const PostUtils = window.PostUtils; // import the post utilities function TodoItem(props) { @@ -190,6 +192,7 @@ function TodoItem(props) { onClick={handleClick} > {issueMessage} + {issue.postPermalink && }
{issueDescription}
{(canRemove(list, issue.list) || canComplete(list) ||