Add support for editing messages

This commit is contained in:
Tulir Asokan 2020-02-19 01:14:02 +02:00
parent b4e27723d7
commit d02abd079f
8 changed files with 108 additions and 12 deletions

4
go.mod
View File

@ -19,7 +19,7 @@ require (
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 golang.org/x/net v0.0.0-20200202094626-16171245cfb2
gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8
maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218191514-cb8e637f1c62 maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218231230-3f49fda72ac9
maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176 maunium.net/go/mauview v0.0.0-20200218231215-04d01c601d5b
maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09 maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09
) )

4
go.sum
View File

@ -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 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.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.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 h1:KoTm7ASEzFIZ1SvPWuWYzpkeA+wiR1fuUu4l7TCHcE0=
maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176/go.mod h1:jwg3Ow7akzsCX3q38pZAfmEC5gGN8gXwMyyjy/yZVMg= 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 h1:hu+R+0nodoZPS19WGyYiw/d63+/NQS/R3Duw3d9HqAU=
maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09/go.mod h1:Ru7KmI5AU7xHUx6hGltgJvknrS+8jlGGMKK15pZuc9k= maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09/go.mod h1:Ru7KmI5AU7xHUx6hGltgJvknrS+8jlGGMKK15pZuc9k=

View File

@ -35,7 +35,7 @@ type MatrixContainer interface {
Logout() Logout()
SendPreferencesToMatrix() 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) SendEvent(evt *event.Event) (string, error)
SendTyping(roomID string, typing bool) SendTyping(roomID string, typing bool)
MarkRead(roomID, eventID string) MarkRead(roomID, eventID string)

View File

@ -25,6 +25,14 @@ type Event struct {
Gomuks GomuksContent `json:"-"` Gomuks GomuksContent `json:"-"`
} }
func (evt *Event) SomewhatDangerousCopy() *Event {
base := *evt.Event
return &Event{
Event: &base,
Gomuks: evt.Gomuks,
}
}
func Wrap(event *mautrix.Event) *Event { func Wrap(event *mautrix.Event) *Event {
return &Event{Event: event} return &Event{Event: event}
} }

View File

@ -690,7 +690,7 @@ func (c *Container) MarkRead(roomID, eventID string) {
var mentionRegex = regexp.MustCompile("\\[(.+?)]\\(https://matrix.to/#/@.+?:.+?\\)") var mentionRegex = regexp.MustCompile("\\[(.+?)]\\(https://matrix.to/#/@.+?:.+?\\)")
var roomRegex = 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 := format.RenderMarkdown(text)
content.MsgType = msgtype 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 = mentionRegex.ReplaceAllString(content.Body, "$1")
content.Body = roomRegex.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() txnID := c.client.TxnID()
localEcho := event.Wrap(&mautrix.Event{ localEcho := event.Wrap(&mautrix.Event{
ID: txnID, ID: txnID,
@ -711,6 +724,10 @@ func (c *Container) PrepareMarkdownMessage(roomID string, msgtype mautrix.Messag
}, },
}) })
localEcho.Gomuks.OutgoingState = event.StateLocalEcho localEcho.Gomuks.OutgoingState = event.StateLocalEcho
if edit != nil {
localEcho.ID = edit.ID
localEcho.Gomuks.Edits = []*event.Event{localEcho}
}
return localEcho return localEcho
} }

View File

@ -17,7 +17,6 @@
package messages package messages
import ( import (
"encoding/json"
"fmt" "fmt"
"time" "time"
@ -55,7 +54,7 @@ type UIMessage struct {
IsHighlight bool IsHighlight bool
IsService bool IsService bool
Edited bool Edited bool
Source json.RawMessage Event *event.Event
ReplyTo *UIMessage ReplyTo *UIMessage
Renderer MessageRenderer Renderer MessageRenderer
} }
@ -82,7 +81,7 @@ func newUIMessage(evt *event.Event, displayname string, renderer MessageRenderer
IsHighlight: false, IsHighlight: false,
IsService: false, IsService: false,
Edited: len(evt.Gomuks.Edits) > 0, Edited: len(evt.Gomuks.Edits) > 0,
Source: evt.Content.VeryRaw, Event: evt,
Renderer: renderer, Renderer: renderer,
} }
} }

View File

@ -128,6 +128,7 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *event.Event
evt.Content.RemoveReplyFallback() evt.Content.RemoveReplyFallback()
} }
if len(evt.Gomuks.Edits) > 0 { if len(evt.Gomuks.Edits) > 0 {
evt = evt.SomewhatDangerousCopy()
evt.Content = *evt.Gomuks.Edits[len(evt.Gomuks.Edits)-1].Content.NewContent evt.Content = *evt.Gomuks.Edits[len(evt.Gomuks.Edits)-1].Content.NewContent
} }
switch evt.Content.MsgType { switch evt.Content.MsgType {

View File

@ -67,6 +67,9 @@ type RoomView struct {
typing []string typing []string
editing *event.Event
editMoveText string
completions struct { completions struct {
list []string list []string
textCache string textCache string
@ -107,7 +110,9 @@ func NewRoomView(parent *MainView, room *rooms.Room) *RoomView {
SetBackgroundColor(tcell.ColorDefault). SetBackgroundColor(tcell.ColorDefault).
SetPlaceholder("Send a message..."). SetPlaceholder("Send a message...").
SetPlaceholderTextColor(tcell.ColorGray). SetPlaceholderTextColor(tcell.ColorGray).
SetTabCompleteFunc(view.InputTabComplete) SetTabCompleteFunc(view.InputTabComplete).
SetPressKeyUpAtStartFunc(view.EditPrevious).
SetPressKeyDownAtEndFunc(view.EditNext)
view.topic. view.topic.
SetTextColor(tcell.ColorWhite). SetTextColor(tcell.ColorWhite).
@ -149,6 +154,10 @@ func (view *RoomView) Blur() {
func (view *RoomView) GetStatus() string { func (view *RoomView) GetStatus() string {
var buf strings.Builder var buf strings.Builder
if view.editing != nil {
buf.WriteString("Editing message - ")
}
if len(view.completions.list) > 0 { 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{} view.completions.list = []string{}
@ -354,6 +363,61 @@ func (view *RoomView) autocompleteEmoji(word string) (completions []string) {
return 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) { func (view *RoomView) InputTabComplete(text string, cursorOffset int) {
debug.Print("Tab completing", cursorOffset, text) debug.Print("Tab completing", cursorOffset, text)
str := runewidth.Truncate(text, cursorOffset, "") str := runewidth.Truncate(text, cursorOffset, "")
@ -396,11 +460,12 @@ func (view *RoomView) InputTabComplete(text string, cursorOffset int) {
func (view *RoomView) InputSubmit(text string) { func (view *RoomView) InputSubmit(text string) {
if len(text) == 0 { if len(text) == 0 {
return 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) go view.parent.cmdProcessor.HandleCommand(cmd)
} else { } else {
go view.SendMessage(mautrix.MsgText, text) go view.SendMessage(mautrix.MsgText, text)
} }
view.editMoveText = ""
view.SetInputText("") view.SetInputText("")
} }
@ -410,9 +475,11 @@ func (view *RoomView) SendMessage(msgtype mautrix.MessageType, text string) {
if !view.config.Preferences.DisableEmojis { if !view.config.Preferences.DisableEmojis {
text = emoji.Sprint(text) 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) msg := view.parseEvent(evt)
view.content.AddMessage(msg, AppendMessage) view.content.AddMessage(msg, AppendMessage)
view.editing = nil
view.status.SetText(view.GetStatus())
eventID, err := view.parent.matrix.SendEvent(evt) eventID, err := view.parent.matrix.SendEvent(evt)
if err != nil { if err != nil {
msg.State = event.StateSendFail msg.State = event.StateSendFail