Unknown changes that possibly fix and/or break things
This commit is contained in:
parent
7e5e802e90
commit
69c163cfe9
@ -119,8 +119,7 @@ type Message interface {
|
||||
SetType(msgtype string)
|
||||
Type() string
|
||||
|
||||
SetText(text string)
|
||||
Text() string
|
||||
NotificationContent() string
|
||||
|
||||
SetState(state MessageState)
|
||||
State() MessageState
|
||||
|
@ -124,8 +124,8 @@ func (ai *ANSImage) Render() []tstring.TString {
|
||||
rows := make([]tstring.TString, ai.h/2)
|
||||
for y := 0; y < ai.h; y += ai.maxprocs {
|
||||
ch := make(chan renderData, ai.maxprocs)
|
||||
for n, r := 0, y+1; (n <= ai.maxprocs) && (2*r+1 < ai.h); n, r = n+1, y+n+1 {
|
||||
go func(r, y int) {
|
||||
for n, row := 0, y; (n <= ai.maxprocs) && (2*row+1 < ai.h); n, row = n+1, y+n {
|
||||
go func(row, y int) {
|
||||
str := make(tstring.TString, ai.w)
|
||||
for x := 0; x < ai.w; x++ {
|
||||
topPixel := ai.pixmap[y][x]
|
||||
@ -139,10 +139,10 @@ func (ai *ANSImage) Render() []tstring.TString {
|
||||
Style: tcell.StyleDefault.Background(topColor).Foreground(bottomColor),
|
||||
}
|
||||
}
|
||||
ch <- renderData{row: r, render: str}
|
||||
}(r, 2*r)
|
||||
ch <- renderData{row: row, render: str}
|
||||
}(row, 2*row)
|
||||
}
|
||||
for n, r := 0, y+1; (n <= ai.maxprocs) && (2*r+1 < ai.h); n, r = n+1, y+n+1 {
|
||||
for n, row := 0, y; (n <= ai.maxprocs) && (2*row+1 < ai.h); n, row = n+1, y+n {
|
||||
data := <-ch
|
||||
rows[data.row] = data.render
|
||||
}
|
||||
|
@ -265,11 +265,11 @@ func (view *MessageView) HandleClick(x, y int, button tcell.ButtonMask) {
|
||||
}
|
||||
|
||||
message := view.metaBuffer[line]
|
||||
imageMessage, ok := message.(*messages.UIImageMessage)
|
||||
imageMessage, ok := message.(*messages.ImageMessage)
|
||||
if !ok {
|
||||
uiMessage, ok := message.(messages.UIMessage)
|
||||
if ok {
|
||||
debug.Print("Message clicked:", uiMessage.Text())
|
||||
debug.Print("Message clicked:", uiMessage.NotificationContent())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
236
ui/messages/base.go
Normal file
236
ui/messages/base.go
Normal file
@ -0,0 +1,236 @@
|
||||
// gomuks - A terminal Matrix client written in Go.
|
||||
// Copyright (C) 2018 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package messages
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/gomuks/interface"
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
"maunium.net/go/gomuks/ui/widget"
|
||||
"maunium.net/go/tcell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&BaseMessage{})
|
||||
}
|
||||
|
||||
type BaseMessage struct {
|
||||
MsgID string
|
||||
MsgType string
|
||||
MsgSender string
|
||||
MsgSenderColor tcell.Color
|
||||
MsgTimestamp time.Time
|
||||
MsgState ifc.MessageState
|
||||
MsgIsHighlight bool
|
||||
MsgIsService bool
|
||||
buffer []tstring.TString
|
||||
prevBufferWidth int
|
||||
}
|
||||
|
||||
func newBaseMessage(id, sender, msgtype string, timestamp time.Time) BaseMessage {
|
||||
return BaseMessage{
|
||||
MsgSender: sender,
|
||||
MsgTimestamp: timestamp,
|
||||
MsgSenderColor: widget.GetHashColor(sender),
|
||||
MsgType: msgtype,
|
||||
MsgID: id,
|
||||
prevBufferWidth: 0,
|
||||
MsgState: ifc.MessageStateDefault,
|
||||
MsgIsHighlight: false,
|
||||
MsgIsService: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) RegisterGomuks(gmx ifc.Gomuks) {}
|
||||
|
||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
||||
func (msg *BaseMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.MsgSender = from.Sender()
|
||||
msg.MsgSenderColor = from.SenderColor()
|
||||
|
||||
fromMsg, ok := from.(UIMessage)
|
||||
if ok {
|
||||
msg.MsgSender = fromMsg.RealSender()
|
||||
msg.MsgID = fromMsg.ID()
|
||||
msg.MsgType = fromMsg.Type()
|
||||
msg.MsgTimestamp = fromMsg.Timestamp()
|
||||
msg.MsgState = fromMsg.State()
|
||||
msg.MsgIsService = fromMsg.IsService()
|
||||
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
||||
msg.buffer = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Sender gets the string that should be displayed as the sender of this message.
|
||||
//
|
||||
// If the message is being sent, the sender is "Sending...".
|
||||
// If sending has failed, the sender is "Error".
|
||||
// If the message is an emote, the sender is blank.
|
||||
// In any other case, the sender is the display name of the user who sent the message.
|
||||
func (msg *BaseMessage) Sender() string {
|
||||
switch msg.MsgState {
|
||||
case ifc.MessageStateSending:
|
||||
return "Sending..."
|
||||
case ifc.MessageStateFailed:
|
||||
return "Error"
|
||||
}
|
||||
switch msg.MsgType {
|
||||
case "m.emote":
|
||||
// Emotes don't show a separate sender, it's included in the buffer.
|
||||
return ""
|
||||
default:
|
||||
return msg.MsgSender
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) RealSender() string {
|
||||
return msg.MsgSender
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) getStateSpecificColor() tcell.Color {
|
||||
switch msg.MsgState {
|
||||
case ifc.MessageStateSending:
|
||||
return tcell.ColorGray
|
||||
case ifc.MessageStateFailed:
|
||||
return tcell.ColorRed
|
||||
case ifc.MessageStateDefault:
|
||||
fallthrough
|
||||
default:
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
}
|
||||
|
||||
// SenderColor returns the color the name of the sender should be shown in.
|
||||
//
|
||||
// If the message is being sent, the color is gray.
|
||||
// If sending has failed, the color is red.
|
||||
//
|
||||
// In any other case, the color is whatever is specified in the Message struct.
|
||||
// Usually that means it is the hash-based color of the sender (see ui/widget/color.go)
|
||||
func (msg *BaseMessage) SenderColor() tcell.Color {
|
||||
stateColor := msg.getStateSpecificColor()
|
||||
switch {
|
||||
case stateColor != tcell.ColorDefault:
|
||||
return stateColor
|
||||
case msg.MsgIsService:
|
||||
return tcell.ColorGray
|
||||
default:
|
||||
return msg.MsgSenderColor
|
||||
}
|
||||
}
|
||||
|
||||
// TextColor returns the color the actual content of the message should be shown in.
|
||||
func (msg *BaseMessage) TextColor() tcell.Color {
|
||||
stateColor := msg.getStateSpecificColor()
|
||||
switch {
|
||||
case stateColor != tcell.ColorDefault:
|
||||
return stateColor
|
||||
case msg.MsgIsService:
|
||||
fallthrough
|
||||
case msg.MsgType == "m.notice":
|
||||
return tcell.ColorGray
|
||||
case msg.MsgIsHighlight:
|
||||
return tcell.ColorYellow
|
||||
case msg.MsgType == "m.room.member":
|
||||
return tcell.ColorGreen
|
||||
default:
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
}
|
||||
|
||||
// TimestampColor returns the color the timestamp should be shown in.
|
||||
//
|
||||
// As with SenderColor(), messages being sent and messages that failed to be sent are
|
||||
// gray and red respectively.
|
||||
//
|
||||
// However, other messages are the default color instead of a color stored in the struct.
|
||||
func (msg *BaseMessage) TimestampColor() tcell.Color {
|
||||
return msg.getStateSpecificColor()
|
||||
}
|
||||
|
||||
// Buffer returns the computed text buffer.
|
||||
//
|
||||
// The buffer contains the text of the message split into lines with a maximum
|
||||
// width of whatever was provided to CalculateBuffer().
|
||||
//
|
||||
// N.B. This will NOT automatically calculate the buffer if it hasn't been
|
||||
// calculated already, as that requires the target width.
|
||||
func (msg *BaseMessage) Buffer() []tstring.TString {
|
||||
return msg.buffer
|
||||
}
|
||||
|
||||
// Height returns the number of rows in the computed buffer (see Buffer()).
|
||||
func (msg *BaseMessage) Height() int {
|
||||
return len(msg.buffer)
|
||||
}
|
||||
|
||||
// Timestamp returns the full timestamp when the message was sent.
|
||||
func (msg *BaseMessage) Timestamp() time.Time {
|
||||
return msg.MsgTimestamp
|
||||
}
|
||||
|
||||
// FormatTime returns the formatted time when the message was sent.
|
||||
func (msg *BaseMessage) FormatTime() string {
|
||||
return msg.MsgTimestamp.Format(TimeFormat)
|
||||
}
|
||||
|
||||
// FormatDate returns the formatted date when the message was sent.
|
||||
func (msg *BaseMessage) FormatDate() string {
|
||||
return msg.MsgTimestamp.Format(DateFormat)
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) ID() string {
|
||||
return msg.MsgID
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetID(id string) {
|
||||
msg.MsgID = id
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) Type() string {
|
||||
return msg.MsgType
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetType(msgtype string) {
|
||||
msg.MsgType = msgtype
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) State() ifc.MessageState {
|
||||
return msg.MsgState
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetState(state ifc.MessageState) {
|
||||
msg.MsgState = state
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) IsHighlight() bool {
|
||||
return msg.MsgIsHighlight
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetIsHighlight(isHighlight bool) {
|
||||
msg.MsgIsHighlight = isHighlight
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) IsService() bool {
|
||||
return msg.MsgIsService
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetIsService(isService bool) {
|
||||
msg.MsgIsService = isService
|
||||
}
|
@ -22,65 +22,50 @@ import (
|
||||
|
||||
"maunium.net/go/gomuks/interface"
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
"maunium.net/go/gomuks/ui/widget"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&UITextMessage{})
|
||||
gob.Register(&UIExpandedTextMessage{})
|
||||
gob.Register(&ExpandedTextMessage{})
|
||||
}
|
||||
|
||||
type UIExpandedTextMessage struct {
|
||||
UITextMessage
|
||||
MsgTStringText tstring.TString
|
||||
type ExpandedTextMessage struct {
|
||||
BaseTextMessage
|
||||
MsgText tstring.TString
|
||||
}
|
||||
|
||||
// NewExpandedTextMessage creates a new UIExpandedTextMessage 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 {
|
||||
return &UIExpandedTextMessage{
|
||||
UITextMessage{
|
||||
MsgSender: sender,
|
||||
MsgTimestamp: timestamp,
|
||||
MsgSenderColor: widget.GetHashColor(sender),
|
||||
MsgType: msgtype,
|
||||
MsgText: text.String(),
|
||||
MsgID: id,
|
||||
prevBufferWidth: 0,
|
||||
MsgState: ifc.MessageStateDefault,
|
||||
MsgIsHighlight: false,
|
||||
MsgIsService: false,
|
||||
},
|
||||
text,
|
||||
return &ExpandedTextMessage{
|
||||
BaseTextMessage: newBaseTextMessage(id, sender, msgtype, timestamp),
|
||||
MsgText: text,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIExpandedTextMessage) GetTStringText() tstring.TString {
|
||||
return msg.MsgTStringText
|
||||
func (msg *ExpandedTextMessage) GenerateText() tstring.TString {
|
||||
return msg.MsgText
|
||||
}
|
||||
|
||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
||||
func (msg *UIExpandedTextMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.MsgSender = from.Sender()
|
||||
msg.MsgSenderColor = from.SenderColor()
|
||||
func (msg *ExpandedTextMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.BaseTextMessage.CopyFrom(from)
|
||||
|
||||
fromMsg, ok := from.(UIMessage)
|
||||
fromExpandedMsg, ok := from.(*ExpandedTextMessage)
|
||||
if ok {
|
||||
msg.MsgSender = fromMsg.RealSender()
|
||||
msg.MsgID = fromMsg.ID()
|
||||
msg.MsgType = fromMsg.Type()
|
||||
msg.MsgTimestamp = fromMsg.Timestamp()
|
||||
msg.MsgState = fromMsg.State()
|
||||
msg.MsgIsService = fromMsg.IsService()
|
||||
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
||||
msg.buffer = nil
|
||||
|
||||
fromExpandedMsg, ok := from.(*UIExpandedTextMessage)
|
||||
if ok {
|
||||
msg.MsgTStringText = fromExpandedMsg.MsgTStringText
|
||||
} else {
|
||||
msg.MsgTStringText = tstring.NewColorTString(fromMsg.Text(), from.TextColor())
|
||||
}
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
msg.MsgText = fromExpandedMsg.MsgText
|
||||
}
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
}
|
||||
|
||||
func (msg *ExpandedTextMessage) NotificationContent() string {
|
||||
return msg.MsgText.String()
|
||||
}
|
||||
|
||||
func (msg *ExpandedTextMessage) CalculateBuffer(width int) {
|
||||
msg.BaseTextMessage.calculateBufferWithText(msg.MsgText, width)
|
||||
}
|
||||
|
||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
|
||||
func (msg *ExpandedTextMessage) RecalculateBuffer() {
|
||||
msg.CalculateBuffer(msg.prevBufferWidth)
|
||||
}
|
||||
|
@ -28,16 +28,15 @@ import (
|
||||
"maunium.net/go/gomuks/interface"
|
||||
"maunium.net/go/gomuks/lib/ansimage"
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
"maunium.net/go/gomuks/ui/widget"
|
||||
"maunium.net/go/tcell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&UIImageMessage{})
|
||||
gob.Register(&ImageMessage{})
|
||||
}
|
||||
|
||||
type UIImageMessage struct {
|
||||
UITextMessage
|
||||
type ImageMessage struct {
|
||||
BaseMessage
|
||||
Homeserver string
|
||||
FileID string
|
||||
data []byte
|
||||
@ -45,20 +44,10 @@ type UIImageMessage struct {
|
||||
gmx ifc.Gomuks
|
||||
}
|
||||
|
||||
// NewImageMessage creates a new UIImageMessage 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 {
|
||||
return &UIImageMessage{
|
||||
UITextMessage{
|
||||
MsgSender: sender,
|
||||
MsgTimestamp: timestamp,
|
||||
MsgSenderColor: widget.GetHashColor(sender),
|
||||
MsgType: msgtype,
|
||||
MsgID: id,
|
||||
prevBufferWidth: 0,
|
||||
MsgState: ifc.MessageStateDefault,
|
||||
MsgIsHighlight: false,
|
||||
MsgIsService: false,
|
||||
},
|
||||
return &ImageMessage{
|
||||
newBaseMessage(id, sender, msgtype, timestamp),
|
||||
homeserver,
|
||||
fileID,
|
||||
data,
|
||||
@ -66,7 +55,7 @@ func NewImageMessage(gmx ifc.Gomuks, id, sender, msgtype, homeserver, fileID str
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
|
||||
func (msg *ImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
|
||||
msg.gmx = gmx
|
||||
|
||||
debug.Print(len(msg.data), msg.data)
|
||||
@ -78,7 +67,11 @@ func (msg *UIImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIImageMessage) updateData() {
|
||||
func (msg *ImageMessage) NotificationContent() string {
|
||||
return "Sent an image"
|
||||
}
|
||||
|
||||
func (msg *ImageMessage) updateData() {
|
||||
debug.Print("Loading image:", msg.Homeserver, msg.FileID)
|
||||
data, _, _, err := msg.gmx.Matrix().Download(fmt.Sprintf("mxc://%s/%s", msg.Homeserver, msg.FileID))
|
||||
if err != nil {
|
||||
@ -88,39 +81,26 @@ func (msg *UIImageMessage) updateData() {
|
||||
msg.data = data
|
||||
}
|
||||
|
||||
func (msg *UIImageMessage) Path() string {
|
||||
func (msg *ImageMessage) Path() string {
|
||||
return msg.gmx.Matrix().GetCachePath(msg.Homeserver, msg.FileID)
|
||||
}
|
||||
|
||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
||||
func (msg *UIImageMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.MsgSender = from.Sender()
|
||||
msg.MsgSenderColor = from.SenderColor()
|
||||
func (msg *ImageMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.BaseMessage.CopyFrom(from)
|
||||
|
||||
fromMsg, ok := from.(UIMessage)
|
||||
fromImgMsg, ok := from.(*ImageMessage)
|
||||
if ok {
|
||||
msg.MsgSender = fromMsg.RealSender()
|
||||
msg.MsgID = fromMsg.ID()
|
||||
msg.MsgType = fromMsg.Type()
|
||||
msg.MsgTimestamp = fromMsg.Timestamp()
|
||||
msg.MsgState = fromMsg.State()
|
||||
msg.MsgIsService = fromMsg.IsService()
|
||||
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
||||
msg.buffer = nil
|
||||
|
||||
fromImgMsg, ok := from.(*UIImageMessage)
|
||||
if ok {
|
||||
msg.data = fromImgMsg.data
|
||||
}
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
msg.data = fromImgMsg.data
|
||||
}
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
}
|
||||
|
||||
// CalculateBuffer generates the internal buffer for this message that consists
|
||||
// of the text of this message split into lines at most as wide as the width
|
||||
// parameter.
|
||||
func (msg *UIImageMessage) CalculateBuffer(width int) {
|
||||
func (msg *ImageMessage) CalculateBuffer(width int) {
|
||||
if width < 2 {
|
||||
return
|
||||
}
|
||||
@ -135,3 +115,9 @@ func (msg *UIImageMessage) CalculateBuffer(width int) {
|
||||
msg.buffer = image.Render()
|
||||
msg.prevBufferWidth = width
|
||||
}
|
||||
|
||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
|
||||
func (msg *ImageMessage) RecalculateBuffer() {
|
||||
msg.CalculateBuffer(msg.prevBufferWidth)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ func ParseMessage(gmx ifc.Gomuks, evt *gomatrix.Event) UIMessage {
|
||||
msgtype, _ := evt.Content["msgtype"].(string)
|
||||
ts := unixToTime(evt.Timestamp)
|
||||
switch msgtype {
|
||||
case "m.text", "m.notice":
|
||||
case "m.text", "m.notice", "m.emote":
|
||||
text, _ := evt.Content["body"].(string)
|
||||
return NewTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
|
||||
case "m.image":
|
||||
@ -86,7 +86,7 @@ func getMembershipEventContent(evt *gomatrix.Event) (sender string, text tstring
|
||||
switch membership {
|
||||
case "invite":
|
||||
sender = "---"
|
||||
text = tstring.NewColorTString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorYellow)
|
||||
text = tstring.NewColorTString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorGreen)
|
||||
text.Colorize(0, len(evt.Sender), widget.GetHashColor(evt.Sender))
|
||||
text.Colorize(len(evt.Sender)+len(" invited "), len(displayname), widget.GetHashColor(displayname))
|
||||
case "join":
|
||||
|
84
ui/messages/textbase.go
Normal file
84
ui/messages/textbase.go
Normal file
@ -0,0 +1,84 @@
|
||||
// gomuks - A terminal Matrix client written in Go.
|
||||
// Copyright (C) 2018 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package messages
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(BaseTextMessage{})
|
||||
}
|
||||
|
||||
type BaseTextMessage struct {
|
||||
BaseMessage
|
||||
}
|
||||
|
||||
func newBaseTextMessage(id, sender, msgtype string, timestamp time.Time) BaseTextMessage {
|
||||
return BaseTextMessage{newBaseMessage(id, sender, msgtype, timestamp)}
|
||||
}
|
||||
|
||||
// Regular expressions used to split lines when calculating the buffer.
|
||||
//
|
||||
// From tview/textview.go
|
||||
var (
|
||||
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
|
||||
spacePattern = regexp.MustCompile(`\s+`)
|
||||
)
|
||||
|
||||
// CalculateBuffer generates the internal buffer for this message that consists
|
||||
// of the text of this message split into lines at most as wide as the width
|
||||
// parameter.
|
||||
func (msg *BaseTextMessage) calculateBufferWithText(text tstring.TString, width int) {
|
||||
if width < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
msg.buffer = []tstring.TString{}
|
||||
|
||||
forcedLinebreaks := text.Split('\n')
|
||||
newlines := 0
|
||||
for _, str := range forcedLinebreaks {
|
||||
if len(str) == 0 && newlines < 1 {
|
||||
msg.buffer = append(msg.buffer, tstring.TString{})
|
||||
newlines++
|
||||
} else {
|
||||
newlines = 0
|
||||
}
|
||||
// Mostly from tview/textview.go#reindexBuffer()
|
||||
for len(str) > 0 {
|
||||
extract := str.Truncate(width)
|
||||
if len(extract) < len(str) {
|
||||
if spaces := spacePattern.FindStringIndex(str[len(extract):].String()); spaces != nil && spaces[0] == 0 {
|
||||
extract = str[:len(extract)+spaces[1]]
|
||||
}
|
||||
|
||||
matches := boundaryPattern.FindAllStringIndex(extract.String(), -1)
|
||||
if len(matches) > 0 {
|
||||
extract = extract[:matches[len(matches)-1][1]]
|
||||
}
|
||||
}
|
||||
msg.buffer = append(msg.buffer, extract)
|
||||
str = str[len(extract):]
|
||||
}
|
||||
}
|
||||
msg.prevBufferWidth = width
|
||||
}
|
@ -19,302 +19,84 @@ package messages
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/gomuks/interface"
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
"maunium.net/go/gomuks/ui/widget"
|
||||
"maunium.net/go/tcell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&UITextMessage{})
|
||||
gob.Register(&TextMessage{})
|
||||
}
|
||||
|
||||
type UITextMessage struct {
|
||||
MsgID string
|
||||
MsgType string
|
||||
MsgSender string
|
||||
MsgSenderColor tcell.Color
|
||||
MsgTimestamp time.Time
|
||||
MsgText string
|
||||
MsgState ifc.MessageState
|
||||
MsgIsHighlight bool
|
||||
MsgIsService bool
|
||||
text tstring.TString
|
||||
buffer []tstring.TString
|
||||
prevBufferWidth int
|
||||
type TextMessage struct {
|
||||
BaseTextMessage
|
||||
cache tstring.TString
|
||||
MsgText string
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return &UITextMessage{
|
||||
MsgSender: sender,
|
||||
MsgTimestamp: timestamp,
|
||||
MsgSenderColor: widget.GetHashColor(sender),
|
||||
MsgType: msgtype,
|
||||
return &TextMessage{
|
||||
BaseTextMessage: newBaseTextMessage(id, sender, msgtype, timestamp),
|
||||
MsgText: text,
|
||||
MsgID: id,
|
||||
prevBufferWidth: 0,
|
||||
MsgState: ifc.MessageStateDefault,
|
||||
MsgIsHighlight: false,
|
||||
MsgIsService: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) RegisterGomuks(gmx ifc.Gomuks) {}
|
||||
func (msg *TextMessage) getCache() tstring.TString {
|
||||
if msg.cache == nil {
|
||||
switch msg.MsgType {
|
||||
case "m.emote":
|
||||
msg.cache = tstring.NewColorTString(fmt.Sprintf("* %s %s", msg.MsgSender, msg.MsgText), msg.TextColor())
|
||||
msg.cache.Colorize(0, len(msg.MsgSender)+2, msg.SenderColor())
|
||||
default:
|
||||
msg.cache = tstring.NewColorTString(msg.MsgText, msg.TextColor())
|
||||
}
|
||||
}
|
||||
return msg.cache
|
||||
}
|
||||
|
||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
||||
func (msg *UITextMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.MsgSender = from.Sender()
|
||||
msg.MsgSenderColor = from.SenderColor()
|
||||
func (msg *TextMessage) CopyFrom(from ifc.MessageMeta) {
|
||||
msg.BaseTextMessage.CopyFrom(from)
|
||||
|
||||
fromMsg, ok := from.(UIMessage)
|
||||
fromTextMsg, ok := from.(*TextMessage)
|
||||
if ok {
|
||||
msg.MsgSender = fromMsg.RealSender()
|
||||
msg.MsgID = fromMsg.ID()
|
||||
msg.MsgType = fromMsg.Type()
|
||||
msg.MsgTimestamp = fromMsg.Timestamp()
|
||||
msg.MsgText = fromMsg.Text()
|
||||
msg.MsgState = fromMsg.State()
|
||||
msg.MsgIsService = fromMsg.IsService()
|
||||
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
||||
msg.buffer = nil
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
msg.MsgText = fromTextMsg.MsgText
|
||||
}
|
||||
|
||||
msg.cache = nil
|
||||
msg.RecalculateBuffer()
|
||||
}
|
||||
func (msg *TextMessage) SetType(msgtype string) {
|
||||
msg.BaseTextMessage.SetType(msgtype)
|
||||
msg.cache = nil
|
||||
}
|
||||
|
||||
// Sender gets the string that should be displayed as the sender of this message.
|
||||
//
|
||||
// If the message is being sent, the sender is "Sending...".
|
||||
// If sending has failed, the sender is "Error".
|
||||
// If the message is an emote, the sender is blank.
|
||||
// In any other case, the sender is the display name of the user who sent the message.
|
||||
func (msg *UITextMessage) Sender() string {
|
||||
switch msg.MsgState {
|
||||
case ifc.MessageStateSending:
|
||||
return "Sending..."
|
||||
case ifc.MessageStateFailed:
|
||||
return "Error"
|
||||
}
|
||||
switch msg.MsgType {
|
||||
case "m.emote":
|
||||
// Emotes don't show a separate sender, it's included in the buffer.
|
||||
return ""
|
||||
default:
|
||||
return msg.MsgSender
|
||||
}
|
||||
func (msg *TextMessage) SetState(state ifc.MessageState) {
|
||||
msg.BaseTextMessage.SetState(state)
|
||||
msg.cache = nil
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) RealSender() string {
|
||||
return msg.MsgSender
|
||||
func (msg *TextMessage) SetIsHighlight(isHighlight bool) {
|
||||
msg.BaseTextMessage.SetIsHighlight(isHighlight)
|
||||
msg.cache = nil
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) getStateSpecificColor() tcell.Color {
|
||||
switch msg.MsgState {
|
||||
case ifc.MessageStateSending:
|
||||
return tcell.ColorGray
|
||||
case ifc.MessageStateFailed:
|
||||
return tcell.ColorRed
|
||||
case ifc.MessageStateDefault:
|
||||
fallthrough
|
||||
default:
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
func (msg *TextMessage) SetIsService(isService bool) {
|
||||
msg.BaseTextMessage.SetIsService(isService)
|
||||
msg.cache = nil
|
||||
}
|
||||
|
||||
// SenderColor returns the color the name of the sender should be shown in.
|
||||
//
|
||||
// If the message is being sent, the color is gray.
|
||||
// If sending has failed, the color is red.
|
||||
//
|
||||
// In any other case, the color is whatever is specified in the Message struct.
|
||||
// Usually that means it is the hash-based color of the sender (see ui/widget/color.go)
|
||||
func (msg *UITextMessage) SenderColor() tcell.Color {
|
||||
stateColor := msg.getStateSpecificColor()
|
||||
switch {
|
||||
case stateColor != tcell.ColorDefault:
|
||||
return stateColor
|
||||
case msg.MsgIsService:
|
||||
return tcell.ColorGray
|
||||
default:
|
||||
return msg.MsgSenderColor
|
||||
}
|
||||
}
|
||||
|
||||
// TextColor returns the color the actual content of the message should be shown in.
|
||||
func (msg *UITextMessage) TextColor() tcell.Color {
|
||||
stateColor := msg.getStateSpecificColor()
|
||||
switch {
|
||||
case stateColor != tcell.ColorDefault:
|
||||
return stateColor
|
||||
case msg.MsgIsService:
|
||||
return tcell.ColorGray
|
||||
case msg.MsgIsHighlight:
|
||||
return tcell.ColorYellow
|
||||
case msg.MsgType == "m.room.member":
|
||||
return tcell.ColorGreen
|
||||
default:
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
}
|
||||
|
||||
// TimestampColor returns the color the timestamp should be shown in.
|
||||
//
|
||||
// As with SenderColor(), messages being sent and messages that failed to be sent are
|
||||
// gray and red respectively.
|
||||
//
|
||||
// However, other messages are the default color instead of a color stored in the struct.
|
||||
func (msg *UITextMessage) TimestampColor() tcell.Color {
|
||||
return msg.getStateSpecificColor()
|
||||
}
|
||||
|
||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
|
||||
func (msg *UITextMessage) RecalculateBuffer() {
|
||||
msg.CalculateBuffer(msg.prevBufferWidth)
|
||||
}
|
||||
|
||||
// Buffer returns the computed text buffer.
|
||||
//
|
||||
// The buffer contains the text of the message split into lines with a maximum
|
||||
// width of whatever was provided to CalculateBuffer().
|
||||
//
|
||||
// N.B. This will NOT automatically calculate the buffer if it hasn't been
|
||||
// calculated already, as that requires the target width.
|
||||
func (msg *UITextMessage) Buffer() []tstring.TString {
|
||||
return msg.buffer
|
||||
}
|
||||
|
||||
// Height returns the number of rows in the computed buffer (see Buffer()).
|
||||
func (msg *UITextMessage) Height() int {
|
||||
return len(msg.buffer)
|
||||
}
|
||||
|
||||
// Timestamp returns the full timestamp when the message was sent.
|
||||
func (msg *UITextMessage) Timestamp() time.Time {
|
||||
return msg.MsgTimestamp
|
||||
}
|
||||
|
||||
// FormatTime returns the formatted time when the message was sent.
|
||||
func (msg *UITextMessage) FormatTime() string {
|
||||
return msg.MsgTimestamp.Format(TimeFormat)
|
||||
}
|
||||
|
||||
// FormatDate returns the formatted date when the message was sent.
|
||||
func (msg *UITextMessage) FormatDate() string {
|
||||
return msg.MsgTimestamp.Format(DateFormat)
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) ID() string {
|
||||
return msg.MsgID
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) SetID(id string) {
|
||||
msg.MsgID = id
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) Type() string {
|
||||
return msg.MsgType
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) SetType(msgtype string) {
|
||||
msg.MsgType = msgtype
|
||||
msg.text = nil
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) Text() string {
|
||||
func (msg *TextMessage) NotificationContent() string {
|
||||
return msg.MsgText
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) SetText(text string) {
|
||||
msg.MsgText = text
|
||||
msg.text = nil
|
||||
func (msg *TextMessage) CalculateBuffer(width int) {
|
||||
msg.BaseTextMessage.calculateBufferWithText(msg.getCache(), width)
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) State() ifc.MessageState {
|
||||
return msg.MsgState
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) SetState(state ifc.MessageState) {
|
||||
msg.MsgState = state
|
||||
msg.text = nil
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) IsHighlight() bool {
|
||||
return msg.MsgIsHighlight
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) SetIsHighlight(isHighlight bool) {
|
||||
msg.MsgIsHighlight = isHighlight
|
||||
msg.text = nil
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) IsService() bool {
|
||||
return msg.MsgIsService
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) SetIsService(isService bool) {
|
||||
msg.MsgIsService = isService
|
||||
msg.text = nil
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) GetTStringText() tstring.TString {
|
||||
if msg.text == nil || len(msg.text) == 0 {
|
||||
msg.text = tstring.NewColorTString(msg.Text(), msg.TextColor())
|
||||
}
|
||||
return msg.text
|
||||
}
|
||||
|
||||
// Regular expressions used to split lines when calculating the buffer.
|
||||
//
|
||||
// From tview/textview.go
|
||||
var (
|
||||
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
|
||||
spacePattern = regexp.MustCompile(`\s+`)
|
||||
)
|
||||
|
||||
// CalculateBuffer generates the internal buffer for this message that consists
|
||||
// of the text of this message split into lines at most as wide as the width
|
||||
// parameter.
|
||||
func (msg *UITextMessage) CalculateBuffer(width int) {
|
||||
if width < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
msg.buffer = []tstring.TString{}
|
||||
text := msg.GetTStringText()
|
||||
if msg.MsgType == "m.emote" {
|
||||
text = tstring.NewColorTString(fmt.Sprintf("* %s %s", msg.MsgSender, text.String()), msg.TextColor())
|
||||
text.Colorize(0, len(msg.MsgSender), msg.SenderColor())
|
||||
}
|
||||
|
||||
forcedLinebreaks := text.Split('\n')
|
||||
newlines := 0
|
||||
for _, str := range forcedLinebreaks {
|
||||
if len(str) == 0 && newlines < 1 {
|
||||
msg.buffer = append(msg.buffer, tstring.TString{})
|
||||
newlines++
|
||||
} else {
|
||||
newlines = 0
|
||||
}
|
||||
// Mostly from tview/textview.go#reindexBuffer()
|
||||
for len(str) > 0 {
|
||||
extract := str.Truncate(width)
|
||||
if len(extract) < len(str) {
|
||||
if spaces := spacePattern.FindStringIndex(str[len(extract):].String()); spaces != nil && spaces[0] == 0 {
|
||||
extract = str[:len(extract)+spaces[1]]
|
||||
}
|
||||
|
||||
matches := boundaryPattern.FindAllStringIndex(extract.String(), -1)
|
||||
if len(matches) > 0 {
|
||||
extract = extract[:matches[len(matches)-1][1]]
|
||||
}
|
||||
}
|
||||
msg.buffer = append(msg.buffer, extract)
|
||||
str = str[len(extract):]
|
||||
}
|
||||
}
|
||||
msg.prevBufferWidth = width
|
||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
|
||||
func (msg *TextMessage) RecalculateBuffer() {
|
||||
msg.CalculateBuffer(msg.prevBufferWidth)
|
||||
}
|
||||
|
@ -134,12 +134,12 @@ func (view *MainView) InputSubmit(roomView *RoomView, text string) {
|
||||
|
||||
func (view *MainView) SendMessage(roomView *RoomView, text string) {
|
||||
tempMessage := roomView.NewTempMessage("m.text", text)
|
||||
go view.sendTempMessage(roomView, tempMessage)
|
||||
go view.sendTempMessage(roomView, tempMessage, text)
|
||||
}
|
||||
|
||||
func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message) {
|
||||
func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message, text string) {
|
||||
defer view.gmx.Recover()
|
||||
eventID, err := view.matrix.SendMessage(roomView.Room.ID, tempMessage.Type(), tempMessage.Text())
|
||||
eventID, err := view.matrix.SendMessage(roomView.Room.ID, tempMessage.Type(), text)
|
||||
if err != nil {
|
||||
tempMessage.SetState(ifc.MessageStateFailed)
|
||||
roomView.SetStatus(fmt.Sprintf("Failed to send message: %s", err))
|
||||
@ -153,8 +153,9 @@ func (view *MainView) HandleCommand(roomView *RoomView, command string, args []s
|
||||
debug.Print("Handling command", command, args)
|
||||
switch command {
|
||||
case "/me":
|
||||
tempMessage := roomView.NewTempMessage("m.emote", strings.Join(args, " "))
|
||||
go view.sendTempMessage(roomView, tempMessage)
|
||||
text := strings.Join(args, " ")
|
||||
tempMessage := roomView.NewTempMessage("m.emote", text)
|
||||
go view.sendTempMessage(roomView, tempMessage, text)
|
||||
view.parent.Render()
|
||||
case "/quit":
|
||||
view.gmx.Stop()
|
||||
@ -411,7 +412,7 @@ func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, shoul
|
||||
if shouldNotify && !isFocused {
|
||||
// Push rules say notify and the terminal is not focused, send desktop notification.
|
||||
shouldPlaySound := should.PlaySound && should.SoundName == "default"
|
||||
sendNotification(room, message.Sender(), message.Text(), should.Highlight, shouldPlaySound)
|
||||
sendNotification(room, message.Sender(), message.NotificationContent(), should.Highlight, shouldPlaySound)
|
||||
}
|
||||
|
||||
message.SetIsHighlight(should.Highlight)
|
||||
|
Loading…
Reference in New Issue
Block a user