From d02abd079fe58bae467672dc7d2a140f9f07bb85 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 19 Feb 2020 01:14:02 +0200 Subject: [PATCH] Add support for editing messages --- go.mod | 4 +-- go.sum | 4 +++ interface/matrix.go | 2 +- matrix/event/event.go | 8 +++++ matrix/matrix.go | 21 ++++++++++-- ui/messages/base.go | 5 ++- ui/messages/parser.go | 1 + ui/room-view.go | 75 ++++++++++++++++++++++++++++++++++++++++--- 8 files changed, 108 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index da9478b..9c51bcc 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( golang.org/x/net v0.0.0-20200202094626-16171245cfb2 gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 gopkg.in/yaml.v2 v2.2.8 - maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218191514-cb8e637f1c62 - maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176 + maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218231230-3f49fda72ac9 + maunium.net/go/mauview v0.0.0-20200218231215-04d01c601d5b maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09 ) diff --git a/go.sum b/go.sum index 4a235af..1a6e7ff 100644 --- a/go.sum +++ b/go.sum @@ -72,7 +72,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218183645-fea33ed88d03 h1:0fvOe9KeB/JAkMAzJTmj6mg1P9xGPAgFhJcCSxNe1Rk= maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218183645-fea33ed88d03/go.mod h1:g10T1fh2Q2HkJWycVs93eBXdWpqD67f1YVQhNxdIDr4= maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218191514-cb8e637f1c62/go.mod h1:g10T1fh2Q2HkJWycVs93eBXdWpqD67f1YVQhNxdIDr4= +maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218231230-3f49fda72ac9 h1:emsSg9ZDiSqI2RrxU3+JddoF4rxshpNn71NNHcy3HUI= +maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218231230-3f49fda72ac9/go.mod h1:g10T1fh2Q2HkJWycVs93eBXdWpqD67f1YVQhNxdIDr4= maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176 h1:KoTm7ASEzFIZ1SvPWuWYzpkeA+wiR1fuUu4l7TCHcE0= maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176/go.mod h1:jwg3Ow7akzsCX3q38pZAfmEC5gGN8gXwMyyjy/yZVMg= +maunium.net/go/mauview v0.0.0-20200218231215-04d01c601d5b h1:Bfov5IkJQpkqDexiFioHIZpx4XL7AILDA1GwLVdqtBw= +maunium.net/go/mauview v0.0.0-20200218231215-04d01c601d5b/go.mod h1:jwg3Ow7akzsCX3q38pZAfmEC5gGN8gXwMyyjy/yZVMg= maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09 h1:hu+R+0nodoZPS19WGyYiw/d63+/NQS/R3Duw3d9HqAU= maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09/go.mod h1:Ru7KmI5AU7xHUx6hGltgJvknrS+8jlGGMKK15pZuc9k= diff --git a/interface/matrix.go b/interface/matrix.go index 424273b..af20509 100644 --- a/interface/matrix.go +++ b/interface/matrix.go @@ -35,7 +35,7 @@ type MatrixContainer interface { Logout() SendPreferencesToMatrix() - PrepareMarkdownMessage(roomID string, msgtype mautrix.MessageType, message string) *event.Event + PrepareMarkdownMessage(roomID string, msgtype mautrix.MessageType, message string, edit *event.Event) *event.Event SendEvent(evt *event.Event) (string, error) SendTyping(roomID string, typing bool) MarkRead(roomID, eventID string) diff --git a/matrix/event/event.go b/matrix/event/event.go index 143d35d..8506c9c 100644 --- a/matrix/event/event.go +++ b/matrix/event/event.go @@ -25,6 +25,14 @@ type Event struct { Gomuks GomuksContent `json:"-"` } +func (evt *Event) SomewhatDangerousCopy() *Event { + base := *evt.Event + return &Event{ + Event: &base, + Gomuks: evt.Gomuks, + } +} + func Wrap(event *mautrix.Event) *Event { return &Event{Event: event} } diff --git a/matrix/matrix.go b/matrix/matrix.go index f77a257..c33a749 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -218,7 +218,7 @@ func (c *Container) SingleSignOn() error { if err != nil { return err } - err = <- errChan + err = <-errChan return err } @@ -690,7 +690,7 @@ func (c *Container) MarkRead(roomID, eventID string) { var mentionRegex = regexp.MustCompile("\\[(.+?)]\\(https://matrix.to/#/@.+?:.+?\\)") var roomRegex = regexp.MustCompile("\\[.+?]\\(https://matrix.to/#/(#.+?:[^/]+?)\\)") -func (c *Container) PrepareMarkdownMessage(roomID string, msgtype mautrix.MessageType, text string) *event.Event { +func (c *Container) PrepareMarkdownMessage(roomID string, msgtype mautrix.MessageType, text string, edit *event.Event) *event.Event { content := format.RenderMarkdown(text) content.MsgType = msgtype @@ -698,6 +698,19 @@ func (c *Container) PrepareMarkdownMessage(roomID string, msgtype mautrix.Messag content.Body = mentionRegex.ReplaceAllString(content.Body, "$1") content.Body = roomRegex.ReplaceAllString(content.Body, "$1") + if edit != nil { + contentCopy := content + content.NewContent = &contentCopy + content.Body = "* " + content.Body + if len(content.FormattedBody) > 0 { + content.FormattedBody = "* " + content.FormattedBody + } + content.RelatesTo = &mautrix.RelatesTo{ + Type: mautrix.RelReplace, + EventID: edit.ID, + } + } + txnID := c.client.TxnID() localEcho := event.Wrap(&mautrix.Event{ ID: txnID, @@ -711,6 +724,10 @@ func (c *Container) PrepareMarkdownMessage(roomID string, msgtype mautrix.Messag }, }) localEcho.Gomuks.OutgoingState = event.StateLocalEcho + if edit != nil { + localEcho.ID = edit.ID + localEcho.Gomuks.Edits = []*event.Event{localEcho} + } return localEcho } diff --git a/ui/messages/base.go b/ui/messages/base.go index 567e614..b54691e 100644 --- a/ui/messages/base.go +++ b/ui/messages/base.go @@ -17,7 +17,6 @@ package messages import ( - "encoding/json" "fmt" "time" @@ -55,7 +54,7 @@ type UIMessage struct { IsHighlight bool IsService bool Edited bool - Source json.RawMessage + Event *event.Event ReplyTo *UIMessage Renderer MessageRenderer } @@ -82,7 +81,7 @@ func newUIMessage(evt *event.Event, displayname string, renderer MessageRenderer IsHighlight: false, IsService: false, Edited: len(evt.Gomuks.Edits) > 0, - Source: evt.Content.VeryRaw, + Event: evt, Renderer: renderer, } } diff --git a/ui/messages/parser.go b/ui/messages/parser.go index e1c0053..53d30e3 100644 --- a/ui/messages/parser.go +++ b/ui/messages/parser.go @@ -128,6 +128,7 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *event.Event evt.Content.RemoveReplyFallback() } if len(evt.Gomuks.Edits) > 0 { + evt = evt.SomewhatDangerousCopy() evt.Content = *evt.Gomuks.Edits[len(evt.Gomuks.Edits)-1].Content.NewContent } switch evt.Content.MsgType { diff --git a/ui/room-view.go b/ui/room-view.go index 31da3c6..27c42c8 100644 --- a/ui/room-view.go +++ b/ui/room-view.go @@ -67,6 +67,9 @@ type RoomView struct { typing []string + editing *event.Event + editMoveText string + completions struct { list []string textCache string @@ -107,7 +110,9 @@ func NewRoomView(parent *MainView, room *rooms.Room) *RoomView { SetBackgroundColor(tcell.ColorDefault). SetPlaceholder("Send a message..."). SetPlaceholderTextColor(tcell.ColorGray). - SetTabCompleteFunc(view.InputTabComplete) + SetTabCompleteFunc(view.InputTabComplete). + SetPressKeyUpAtStartFunc(view.EditPrevious). + SetPressKeyDownAtEndFunc(view.EditNext) view.topic. SetTextColor(tcell.ColorWhite). @@ -149,8 +154,12 @@ func (view *RoomView) Blur() { func (view *RoomView) GetStatus() string { var buf strings.Builder + if view.editing != nil { + buf.WriteString("Editing message - ") + } + if len(view.completions.list) > 0 { - if view.completions.textCache != view.input.GetText() || view.completions.time.Add(10 * time.Second).Before(time.Now()) { + if view.completions.textCache != view.input.GetText() || view.completions.time.Add(10*time.Second).Before(time.Now()) { view.completions.list = []string{} } else { buf.WriteString(strings.Join(view.completions.list, ", ")) @@ -354,6 +363,61 @@ func (view *RoomView) autocompleteEmoji(word string) (completions []string) { return } +func (view *RoomView) SetEditing(evt *event.Event) { + if evt == nil { + view.editing = nil + view.SetInputText(view.editMoveText) + view.editMoveText = "" + } else { + if view.editing == nil { + view.editMoveText = view.GetInputText() + } + view.editing = evt + view.SetInputText(view.editing.Content.Body) + } + view.status.SetText(view.GetStatus()) +} + +func (view *RoomView) EditNext() { + if view.editing == nil { + return + } + var foundEvent *event.Event + currentFound := view.editing == nil + self := view.parent.matrix.Client().UserID + for _, msg := range view.MessageView().messages { + if currentFound { + if msg.SenderID == self { + foundEvent = msg.Event + break + } + } else if msg.EventID == view.editing.ID { + currentFound = true + } + } + view.SetEditing(foundEvent) +} + +func (view *RoomView) EditPrevious() { + var foundEvent *event.Event + currentFound := view.editing == nil + self := view.parent.matrix.Client().UserID + msgs := view.MessageView().messages + for i := len(msgs) - 1; i >= 0; i-- { + if currentFound { + if msgs[i].SenderID == self { + foundEvent = msgs[i].Event + break + } + } else if msgs[i].EventID == view.editing.ID { + currentFound = true + } + } + if foundEvent != nil { + view.SetEditing(foundEvent) + } +} + func (view *RoomView) InputTabComplete(text string, cursorOffset int) { debug.Print("Tab completing", cursorOffset, text) str := runewidth.Truncate(text, cursorOffset, "") @@ -396,11 +460,12 @@ func (view *RoomView) InputTabComplete(text string, cursorOffset int) { func (view *RoomView) InputSubmit(text string) { if len(text) == 0 { return - } else if cmd := view.parent.cmdProcessor.ParseCommand(view, text); cmd != nil { + } else if cmd := view.parent.cmdProcessor.ParseCommand(view, text); view.editing == nil && cmd != nil { go view.parent.cmdProcessor.HandleCommand(cmd) } else { go view.SendMessage(mautrix.MsgText, text) } + view.editMoveText = "" view.SetInputText("") } @@ -410,9 +475,11 @@ func (view *RoomView) SendMessage(msgtype mautrix.MessageType, text string) { if !view.config.Preferences.DisableEmojis { text = emoji.Sprint(text) } - evt := view.parent.matrix.PrepareMarkdownMessage(view.Room.ID, msgtype, text) + evt := view.parent.matrix.PrepareMarkdownMessage(view.Room.ID, msgtype, text, view.editing) msg := view.parseEvent(evt) view.content.AddMessage(msg, AppendMessage) + view.editing = nil + view.status.SetText(view.GetStatus()) eventID, err := view.parent.matrix.SendEvent(evt) if err != nil { msg.State = event.StateSendFail