Create pills when tab-completing or clicking nicks
This commit is contained in:
parent
127c896291
commit
3750d5007f
@ -21,7 +21,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
"maunium.net/go/gomuks/debug"
|
"maunium.net/go/gomuks/debug"
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
"maunium.net/go/gomuks/lib/open"
|
"maunium.net/go/gomuks/lib/open"
|
||||||
@ -278,22 +280,28 @@ func (view *MessageView) handleUsernameClick(message ifc.MessageMeta, prevMessag
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sender := []rune(uiMessage.Sender())
|
if len(uiMessage.Sender()) == 0 {
|
||||||
if len(sender) == 0 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
sender := fmt.Sprintf("[%s](https://matrix.to/#/%s)", uiMessage.Sender(), uiMessage.SenderID())
|
||||||
|
|
||||||
cursorPos := view.parent.input.GetCursorOffset()
|
cursorPos := view.parent.input.GetCursorOffset()
|
||||||
text := []rune(view.parent.input.GetText())
|
text := view.parent.input.GetText()
|
||||||
var newText []rune
|
var buf strings.Builder
|
||||||
if cursorPos == 0 {
|
if cursorPos == 0 {
|
||||||
newText = append(sender, ':', ' ')
|
buf.WriteString(sender)
|
||||||
newText = append(newText, text...)
|
buf.WriteRune(':')
|
||||||
|
buf.WriteRune(' ')
|
||||||
|
buf.WriteString(text)
|
||||||
} else {
|
} else {
|
||||||
newText = append(text[0:cursorPos], sender...)
|
textBefore := runewidth.Truncate(text, cursorPos, "")
|
||||||
newText = append(newText, ' ')
|
textAfter := text[len(textBefore):]
|
||||||
newText = append(newText, text[cursorPos:]...)
|
buf.WriteString(textBefore)
|
||||||
|
buf.WriteString(sender)
|
||||||
|
buf.WriteRune(' ')
|
||||||
|
buf.WriteString(textAfter)
|
||||||
}
|
}
|
||||||
|
newText := buf.String()
|
||||||
view.parent.input.SetText(string(newText))
|
view.parent.input.SetText(string(newText))
|
||||||
view.parent.input.SetCursorOffset(cursorPos + len(newText) - len(text))
|
view.parent.input.SetCursorOffset(cursorPos + len(newText) - len(text))
|
||||||
return true
|
return true
|
||||||
|
@ -33,6 +33,7 @@ func init() {
|
|||||||
type BaseMessage struct {
|
type BaseMessage struct {
|
||||||
MsgID string
|
MsgID string
|
||||||
MsgType string
|
MsgType string
|
||||||
|
MsgSenderID string
|
||||||
MsgSender string
|
MsgSender string
|
||||||
MsgSenderColor tcell.Color
|
MsgSenderColor tcell.Color
|
||||||
MsgTimestamp time.Time
|
MsgTimestamp time.Time
|
||||||
@ -43,9 +44,10 @@ type BaseMessage struct {
|
|||||||
prevBufferWidth int
|
prevBufferWidth int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBaseMessage(id, sender, msgtype string, timestamp time.Time) BaseMessage {
|
func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time) BaseMessage {
|
||||||
return BaseMessage{
|
return BaseMessage{
|
||||||
MsgSender: sender,
|
MsgSenderID: sender,
|
||||||
|
MsgSender: displayname,
|
||||||
MsgTimestamp: timestamp,
|
MsgTimestamp: timestamp,
|
||||||
MsgSenderColor: widget.GetHashColor(sender),
|
MsgSenderColor: widget.GetHashColor(sender),
|
||||||
MsgType: msgtype,
|
MsgType: msgtype,
|
||||||
@ -66,6 +68,7 @@ func (msg *BaseMessage) CopyFrom(from ifc.MessageMeta) {
|
|||||||
|
|
||||||
fromMsg, ok := from.(UIMessage)
|
fromMsg, ok := from.(UIMessage)
|
||||||
if ok {
|
if ok {
|
||||||
|
msg.MsgSenderID = fromMsg.SenderID()
|
||||||
msg.MsgSender = fromMsg.RealSender()
|
msg.MsgSender = fromMsg.RealSender()
|
||||||
msg.MsgID = fromMsg.ID()
|
msg.MsgID = fromMsg.ID()
|
||||||
msg.MsgType = fromMsg.Type()
|
msg.MsgType = fromMsg.Type()
|
||||||
@ -99,6 +102,10 @@ func (msg *BaseMessage) Sender() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *BaseMessage) SenderID() string {
|
||||||
|
return msg.MsgSenderID
|
||||||
|
}
|
||||||
|
|
||||||
func (msg *BaseMessage) RealSender() string {
|
func (msg *BaseMessage) RealSender() string {
|
||||||
return msg.MsgSender
|
return msg.MsgSender
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ type ExpandedTextMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state.
|
// NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state.
|
||||||
func NewExpandedTextMessage(id, sender, msgtype string, text tstring.TString, timestamp time.Time) UIMessage {
|
func NewExpandedTextMessage(id, sender, displayname, msgtype string, text tstring.TString, timestamp time.Time) UIMessage {
|
||||||
return &ExpandedTextMessage{
|
return &ExpandedTextMessage{
|
||||||
BaseTextMessage: newBaseTextMessage(id, sender, msgtype, timestamp),
|
BaseTextMessage: newBaseTextMessage(id, sender, displayname, msgtype, timestamp),
|
||||||
MsgText: text,
|
MsgText: text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,9 @@ type ImageMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewImageMessage creates a new ImageMessage object with the provided values and the default state.
|
// NewImageMessage creates a new ImageMessage object with the provided values and the default state.
|
||||||
func NewImageMessage(gmx ifc.Gomuks, id, sender, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
|
func NewImageMessage(gmx ifc.Gomuks, id, sender, displayname, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
|
||||||
return &ImageMessage{
|
return &ImageMessage{
|
||||||
newBaseMessage(id, sender, msgtype, timestamp),
|
newBaseMessage(id, sender, displayname, msgtype, timestamp),
|
||||||
homeserver,
|
homeserver,
|
||||||
fileID,
|
fileID,
|
||||||
data,
|
data,
|
||||||
|
@ -30,6 +30,7 @@ type UIMessage interface {
|
|||||||
Buffer() []tstring.TString
|
Buffer() []tstring.TString
|
||||||
Height() int
|
Height() int
|
||||||
|
|
||||||
|
SenderID() string
|
||||||
RealSender() string
|
RealSender() string
|
||||||
RegisterGomuks(gmx ifc.Gomuks)
|
RegisterGomuks(gmx ifc.Gomuks)
|
||||||
}
|
}
|
||||||
|
@ -31,15 +31,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ParseEvent(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
|
func ParseEvent(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
|
||||||
member := room.GetMember(evt.Sender)
|
|
||||||
if member != nil {
|
|
||||||
evt.Sender = member.DisplayName
|
|
||||||
}
|
|
||||||
switch evt.Type {
|
switch evt.Type {
|
||||||
case "m.room.message":
|
case "m.room.message":
|
||||||
return ParseMessage(gmx, room, evt)
|
return ParseMessage(gmx, room, evt)
|
||||||
case "m.room.member":
|
case "m.room.member":
|
||||||
return ParseMembershipEvent(evt)
|
return ParseMembershipEvent(room, evt)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -53,6 +49,11 @@ func unixToTime(unix int64) time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
|
func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
|
||||||
|
displayname := evt.Sender
|
||||||
|
member := room.GetMember(evt.Sender)
|
||||||
|
if member != nil {
|
||||||
|
displayname = member.DisplayName
|
||||||
|
}
|
||||||
msgtype, _ := evt.Content["msgtype"].(string)
|
msgtype, _ := evt.Content["msgtype"].(string)
|
||||||
ts := unixToTime(evt.Timestamp)
|
ts := unixToTime(evt.Timestamp)
|
||||||
switch msgtype {
|
switch msgtype {
|
||||||
@ -60,10 +61,10 @@ func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) message
|
|||||||
format, hasFormat := evt.Content["format"].(string)
|
format, hasFormat := evt.Content["format"].(string)
|
||||||
if hasFormat && format == "org.matrix.custom.html" {
|
if hasFormat && format == "org.matrix.custom.html" {
|
||||||
text := ParseHTMLMessage(room, evt)
|
text := ParseHTMLMessage(room, evt)
|
||||||
return messages.NewExpandedTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
|
return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts)
|
||||||
} else {
|
} else {
|
||||||
text, _ := evt.Content["body"].(string)
|
text, _ := evt.Content["body"].(string)
|
||||||
return messages.NewTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
|
return messages.NewTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts)
|
||||||
}
|
}
|
||||||
case "m.image":
|
case "m.image":
|
||||||
url, _ := evt.Content["url"].(string)
|
url, _ := evt.Content["url"].(string)
|
||||||
@ -71,12 +72,16 @@ func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) message
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Printf("Failed to download %s: %v", url, err)
|
debug.Printf("Failed to download %s: %v", url, err)
|
||||||
}
|
}
|
||||||
return messages.NewImageMessage(gmx, evt.ID, evt.Sender, msgtype, hs, id, data, ts)
|
return messages.NewImageMessage(gmx, evt.ID, evt.Sender, displayname, msgtype, hs, id, data, ts)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMembershipEventContent(evt *gomatrix.Event) (sender string, text tstring.TString) {
|
func getMembershipEventContent(room *rooms.Room, evt *gomatrix.Event) (sender string, text tstring.TString) {
|
||||||
|
member := room.GetMember(evt.Sender)
|
||||||
|
if member != nil {
|
||||||
|
evt.Sender = member.DisplayName
|
||||||
|
}
|
||||||
membership, _ := evt.Content["membership"].(string)
|
membership, _ := evt.Content["membership"].(string)
|
||||||
displayname, _ := evt.Content["displayname"].(string)
|
displayname, _ := evt.Content["displayname"].(string)
|
||||||
if len(displayname) == 0 {
|
if len(displayname) == 0 {
|
||||||
@ -121,8 +126,8 @@ func getMembershipEventContent(evt *gomatrix.Event) (sender string, text tstring
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseMembershipEvent(evt *gomatrix.Event) messages.UIMessage {
|
func ParseMembershipEvent(room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
|
||||||
sender, text := getMembershipEventContent(evt)
|
displayname, text := getMembershipEventContent(room, evt)
|
||||||
ts := unixToTime(evt.Timestamp)
|
ts := unixToTime(evt.Timestamp)
|
||||||
return messages.NewExpandedTextMessage(evt.ID, sender, "m.room.membership", text, ts)
|
return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, "m.room.membership", text, ts)
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,8 @@ type BaseTextMessage struct {
|
|||||||
BaseMessage
|
BaseMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBaseTextMessage(id, sender, msgtype string, timestamp time.Time) BaseTextMessage {
|
func newBaseTextMessage(id, sender, displayname, msgtype string, timestamp time.Time) BaseTextMessage {
|
||||||
return BaseTextMessage{newBaseMessage(id, sender, msgtype, timestamp)}
|
return BaseTextMessage{newBaseMessage(id, sender, displayname, msgtype, timestamp)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular expressions used to split lines when calculating the buffer.
|
// Regular expressions used to split lines when calculating the buffer.
|
||||||
|
@ -36,9 +36,9 @@ type TextMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewTextMessage creates a new UITextMessage object with the provided values and the default state.
|
// NewTextMessage creates a new UITextMessage object with the provided values and the default state.
|
||||||
func NewTextMessage(id, sender, msgtype, text string, timestamp time.Time) UIMessage {
|
func NewTextMessage(id, sender, displayname, msgtype, text string, timestamp time.Time) UIMessage {
|
||||||
return &TextMessage{
|
return &TextMessage{
|
||||||
BaseTextMessage: newBaseTextMessage(id, sender, msgtype, timestamp),
|
BaseTextMessage: newBaseTextMessage(id, sender, displayname, msgtype, timestamp),
|
||||||
MsgText: text,
|
MsgText: text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"maunium.net/go/tcell"
|
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
"maunium.net/go/gomuks/matrix/rooms"
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
"maunium.net/go/gomuks/ui/messages"
|
"maunium.net/go/gomuks/ui/messages"
|
||||||
"maunium.net/go/gomuks/ui/widget"
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
|
"maunium.net/go/tcell"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -213,16 +213,15 @@ func (view *RoomView) SetTyping(users []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *RoomView) AutocompleteUser(existingText string) (completions []string) {
|
func (view *RoomView) AutocompleteUser(existingText string) (completions []*rooms.Member) {
|
||||||
textWithoutPrefix := existingText
|
textWithoutPrefix := existingText
|
||||||
if strings.HasPrefix(existingText, "@") {
|
if strings.HasPrefix(existingText, "@") {
|
||||||
textWithoutPrefix = existingText[1:]
|
textWithoutPrefix = existingText[1:]
|
||||||
}
|
}
|
||||||
for _, user := range view.Room.GetMembers() {
|
for _, user := range view.Room.GetMembers() {
|
||||||
if strings.HasPrefix(user.DisplayName, textWithoutPrefix) {
|
if strings.HasPrefix(user.DisplayName, textWithoutPrefix) ||
|
||||||
completions = append(completions, user.DisplayName)
|
strings.HasPrefix(user.UserID, existingText) {
|
||||||
} else if strings.HasPrefix(user.UserID, existingText) {
|
completions = append(completions, user)
|
||||||
completions = append(completions, user.UserID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -257,10 +256,12 @@ func (view *RoomView) UpdateUserList() {
|
|||||||
|
|
||||||
func (view *RoomView) newUIMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage {
|
func (view *RoomView) newUIMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage {
|
||||||
member := view.Room.GetMember(sender)
|
member := view.Room.GetMember(sender)
|
||||||
|
displayname := sender
|
||||||
if member != nil {
|
if member != nil {
|
||||||
sender = member.DisplayName
|
displayname = member.DisplayName
|
||||||
}
|
}
|
||||||
return messages.NewTextMessage(id, sender, msgtype, text, timestamp)
|
msg := messages.NewTextMessage(id, sender, displayname, msgtype, text, timestamp)
|
||||||
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *RoomView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) ifc.Message {
|
func (view *RoomView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) ifc.Message {
|
||||||
|
@ -104,17 +104,22 @@ func findWordToTabComplete(text string) string {
|
|||||||
func (view *MainView) InputTabComplete(roomView *RoomView, text string, cursorOffset int) string {
|
func (view *MainView) InputTabComplete(roomView *RoomView, text string, cursorOffset int) string {
|
||||||
str := runewidth.Truncate(text, cursorOffset, "")
|
str := runewidth.Truncate(text, cursorOffset, "")
|
||||||
word := findWordToTabComplete(str)
|
word := findWordToTabComplete(str)
|
||||||
|
|
||||||
userCompletions := roomView.AutocompleteUser(word)
|
userCompletions := roomView.AutocompleteUser(word)
|
||||||
if len(userCompletions) == 1 {
|
if len(userCompletions) == 1 {
|
||||||
startIndex := len(str) - len(word)
|
startIndex := len(str) - len(word)
|
||||||
completion := userCompletions[0]
|
member := userCompletions[0]
|
||||||
|
completion := fmt.Sprintf("[%s](https://matrix.to/#/%s)", member.DisplayName, member.UserID)
|
||||||
if startIndex == 0 {
|
if startIndex == 0 {
|
||||||
completion = completion + ": "
|
completion = completion + ": "
|
||||||
}
|
}
|
||||||
text = str[0:startIndex] + completion + text[len(str):]
|
text = str[0:startIndex] + completion + text[len(str):]
|
||||||
} else if len(userCompletions) > 1 && len(userCompletions) < 6 {
|
} else if len(userCompletions) > 1 && len(userCompletions) <= 5 {
|
||||||
roomView.SetStatus(fmt.Sprintf("Completions: %s", strings.Join(userCompletions, ", ")))
|
// roomView.SetStatus(fmt.Sprintf("Completions: %s", strings.Join(userCompletions, ", ")))
|
||||||
|
} else if len(userCompletions) > 5 {
|
||||||
|
roomView.SetStatus("Over 5 completion options.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user