Move event parsing to ui/messages and add image displaying
This commit is contained in:
parent
ee67c1446c
commit
8270bc0322
@ -33,6 +33,7 @@ type Config struct {
|
|||||||
|
|
||||||
Dir string `yaml:"-"`
|
Dir string `yaml:"-"`
|
||||||
HistoryDir string `yaml:"history_dir"`
|
HistoryDir string `yaml:"history_dir"`
|
||||||
|
MediaDir string `yaml:"media_dir"`
|
||||||
Session *Session `yaml:"-"`
|
Session *Session `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ func NewConfig(dir string) *Config {
|
|||||||
return &Config{
|
return &Config{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
HistoryDir: filepath.Join(dir, "history"),
|
HistoryDir: filepath.Join(dir, "history"),
|
||||||
|
MediaDir: filepath.Join(dir, "media"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,4 +34,5 @@ type MatrixContainer interface {
|
|||||||
LeaveRoom(roomID string) error
|
LeaveRoom(roomID string) error
|
||||||
GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error)
|
GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error)
|
||||||
GetRoom(roomID string) *rooms.Room
|
GetRoom(roomID string) *rooms.Room
|
||||||
|
Download(mxcURL string) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,9 @@ type MainView interface {
|
|||||||
SaveAllHistory()
|
SaveAllHistory()
|
||||||
|
|
||||||
SetTyping(roomID string, users []string)
|
SetTyping(roomID string, users []string)
|
||||||
ProcessMessageEvent(roomView RoomView, evt *gomatrix.Event) Message
|
ParseEvent(roomView RoomView, evt *gomatrix.Event) Message
|
||||||
ProcessMembershipEvent(roomView RoomView, evt *gomatrix.Event) Message
|
//ProcessMessageEvent(roomView RoomView, evt *gomatrix.Event) Message
|
||||||
|
//ProcessMembershipEvent(roomView RoomView, evt *gomatrix.Event) Message
|
||||||
NotifyMessage(room *rooms.Room, message Message, should pushrules.PushActionArrayShould)
|
NotifyMessage(room *rooms.Room, message Message, should pushrules.PushActionArrayShould)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,19 +17,25 @@
|
|||||||
package matrix
|
package matrix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
"maunium.net/go/gomuks/config"
|
"maunium.net/go/gomuks/config"
|
||||||
"maunium.net/go/gomuks/debug"
|
"maunium.net/go/gomuks/debug"
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
"maunium.net/go/gomuks/matrix/pushrules"
|
"maunium.net/go/gomuks/matrix/pushrules"
|
||||||
"maunium.net/go/gomuks/matrix/rooms"
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Container is a wrapper for a gomatrix Client and some other stuff.
|
// Container is a wrapper for a gomatrix Client and some other stuff.
|
||||||
@ -222,7 +228,7 @@ func (c *Container) HandleMessage(evt *gomatrix.Event) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message := mainView.ProcessMessageEvent(roomView, evt)
|
message := mainView.ParseEvent(roomView, evt)
|
||||||
if message != nil {
|
if message != nil {
|
||||||
if c.syncer.FirstSyncDone {
|
if c.syncer.FirstSyncDone {
|
||||||
pushRules := c.PushRules().GetActions(roomView.MxRoom(), evt).Should()
|
pushRules := c.PushRules().GetActions(roomView.MxRoom(), evt).Should()
|
||||||
@ -279,7 +285,7 @@ func (c *Container) HandleMembership(evt *gomatrix.Event) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message := mainView.ProcessMembershipEvent(roomView, evt)
|
message := mainView.ParseEvent(roomView, evt)
|
||||||
if message != nil {
|
if message != nil {
|
||||||
// TODO this shouldn't be necessary
|
// TODO this shouldn't be necessary
|
||||||
roomView.MxRoom().UpdateState(evt)
|
roomView.MxRoom().UpdateState(evt)
|
||||||
@ -403,3 +409,53 @@ func (c *Container) GetRoom(roomID string) *rooms.Room {
|
|||||||
}
|
}
|
||||||
return room
|
return room
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mxcRegex = regexp.MustCompile("mxc://(.+)/(.+)")
|
||||||
|
|
||||||
|
func (c *Container) Download(mxcURL string) ([]byte, error) {
|
||||||
|
parts := mxcRegex.FindStringSubmatch(mxcURL)
|
||||||
|
if parts == nil || len(parts) != 3 {
|
||||||
|
debug.Print(parts)
|
||||||
|
return nil, fmt.Errorf("invalid matrix content URL")
|
||||||
|
}
|
||||||
|
hs := parts[1]
|
||||||
|
id := parts[2]
|
||||||
|
|
||||||
|
cacheFile := c.getCachePath(hs, id)
|
||||||
|
if _, err := os.Stat(cacheFile); err != nil {
|
||||||
|
data, err := ioutil.ReadFile(cacheFile)
|
||||||
|
if err == nil {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dlURL, _ := url.Parse(c.client.HomeserverURL.String())
|
||||||
|
dlURL.Path = path.Join(dlURL.Path, "/_matrix/media/v1/download", hs, id)
|
||||||
|
|
||||||
|
resp, err := c.client.Client.Get(dlURL.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := buf.Bytes()
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(cacheFile, data, 0600)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
func (c *Container) getCachePath(homeserver, fileID string) string {
|
||||||
|
dir := filepath.Join(c.config.MediaDir, homeserver)
|
||||||
|
|
||||||
|
err := os.MkdirAll(dir, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(dir, fileID)
|
||||||
|
}
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"maunium.net/go/gomuks/debug"
|
"maunium.net/go/gomuks/debug"
|
||||||
@ -72,10 +71,6 @@ func NewMessageView() *MessageView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage {
|
|
||||||
return messages.NewMessage(id, sender, msgtype, text, timestamp, widget.GetHashColor(sender))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (view *MessageView) SaveHistory(path string) error {
|
func (view *MessageView) SaveHistory(path string) error {
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -102,14 +97,23 @@ func (view *MessageView) LoadHistory(path string) (int, error) {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
|
var msgs []messages.UIMessage
|
||||||
|
|
||||||
dec := gob.NewDecoder(file)
|
dec := gob.NewDecoder(file)
|
||||||
err = dec.Decode(&view.messages)
|
err = dec.Decode(&msgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, message := range view.messages {
|
view.messages = make([]messages.UIMessage, len(msgs))
|
||||||
|
indexOffset := 0
|
||||||
|
for index, message := range msgs {
|
||||||
|
if message != nil {
|
||||||
|
view.messages[index-indexOffset] = message
|
||||||
view.updateWidestSender(message.Sender())
|
view.updateWidestSender(message.Sender())
|
||||||
|
} else {
|
||||||
|
indexOffset++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return len(view.messages), nil
|
return len(view.messages), nil
|
||||||
@ -232,7 +236,11 @@ func (view *MessageView) recalculateBuffers() {
|
|||||||
view.textBuffer = []messages.UIString{}
|
view.textBuffer = []messages.UIString{}
|
||||||
view.metaBuffer = []ifc.MessageMeta{}
|
view.metaBuffer = []ifc.MessageMeta{}
|
||||||
view.prevMsgCount = 0
|
view.prevMsgCount = 0
|
||||||
for _, message := range view.messages {
|
for i, message := range view.messages {
|
||||||
|
if message == nil {
|
||||||
|
debug.Print("O.o found nil message at", i)
|
||||||
|
break
|
||||||
|
}
|
||||||
if recalculateMessageBuffers {
|
if recalculateMessageBuffers {
|
||||||
message.CalculateBuffer(width)
|
message.CalculateBuffer(width)
|
||||||
}
|
}
|
||||||
|
51
ui/messages/cell.go
Normal file
51
ui/messages/cell.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cell struct {
|
||||||
|
Char rune
|
||||||
|
Style tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStyleCell(char rune, style tcell.Style) Cell {
|
||||||
|
return Cell{char, style}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColorCell(char rune, color tcell.Color) Cell {
|
||||||
|
return Cell{char, tcell.StyleDefault.Foreground(color)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCell(char rune) Cell {
|
||||||
|
return Cell{char, tcell.StyleDefault}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cell Cell) RuneWidth() int {
|
||||||
|
return runewidth.RuneWidth(cell.Char)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cell Cell) Draw(screen tcell.Screen, x, y int) (chWidth int) {
|
||||||
|
chWidth = cell.RuneWidth()
|
||||||
|
for runeWidthOffset := 0; runeWidthOffset < chWidth; runeWidthOffset++ {
|
||||||
|
screen.SetContent(x+runeWidthOffset, y, cell.Char, nil, cell.Style)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -1,2 +1,2 @@
|
|||||||
// Package types contains common type definitions used by the UI.
|
// Package messages contains different message types and code to generate and render them.
|
||||||
package messages
|
package messages
|
||||||
|
113
ui/messages/imagemessage.go
Normal file
113
ui/messages/imagemessage.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"maunium.net/go/gomuks/debug"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
|
"maunium.net/go/pixterm/ansimage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gob.Register(&UIImageMessage{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type UIImageMessage struct {
|
||||||
|
UITextMessage
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageMessage creates a new UIImageMessage object with the provided values and the default state.
|
||||||
|
func NewImageMessage(id, sender, msgtype 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,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
fromImgMsg, ok := from.(*UIImageMessage)
|
||||||
|
if ok {
|
||||||
|
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) {
|
||||||
|
if width < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := ansimage.NewScaledFromReader(bytes.NewReader(msg.data), -1, width, color.Black, ansimage.ScaleModeResize, ansimage.NoDithering)
|
||||||
|
if err != nil {
|
||||||
|
msg.buffer = []UIString{NewColorUIString("Failed to display image", tcell.ColorRed)}
|
||||||
|
debug.Print("Failed to display image:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.buffer = make([]UIString, image.Height())
|
||||||
|
pixels := image.Pixmap()
|
||||||
|
for row, pixelRow := range pixels {
|
||||||
|
msg.buffer[row] = make(UIString, len(pixelRow))
|
||||||
|
for column, pixel := range pixelRow {
|
||||||
|
pixelColor := tcell.NewRGBColor(int32(pixel.R), int32(pixel.G), int32(pixel.B))
|
||||||
|
msg.buffer[row][column] = Cell{
|
||||||
|
Char: ' ',
|
||||||
|
Style: tcell.StyleDefault.Background(pixelColor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.prevBufferWidth = width
|
||||||
|
}
|
@ -17,10 +17,6 @@
|
|||||||
package messages
|
package messages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,148 +32,5 @@ type UIMessage interface {
|
|||||||
RealSender() string
|
RealSender() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cell struct {
|
|
||||||
Char rune
|
|
||||||
Style tcell.Style
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStyleCell(char rune, style tcell.Style) Cell {
|
|
||||||
return Cell{char, style}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewColorCell(char rune, color tcell.Color) Cell {
|
|
||||||
return Cell{char, tcell.StyleDefault.Foreground(color)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCell(char rune) Cell {
|
|
||||||
return Cell{char, tcell.StyleDefault}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cell Cell) RuneWidth() int {
|
|
||||||
return runewidth.RuneWidth(cell.Char)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cell Cell) Draw(screen tcell.Screen, x, y int) (chWidth int) {
|
|
||||||
chWidth = cell.RuneWidth()
|
|
||||||
for runeWidthOffset := 0; runeWidthOffset < chWidth; runeWidthOffset++ {
|
|
||||||
screen.SetContent(x+runeWidthOffset, y, cell.Char, nil, cell.Style)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type UIString []Cell
|
|
||||||
|
|
||||||
func NewUIString(str string) UIString {
|
|
||||||
newStr := make([]Cell, len(str))
|
|
||||||
for i, char := range str {
|
|
||||||
newStr[i] = NewCell(char)
|
|
||||||
}
|
|
||||||
return newStr
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewColorUIString(str string, color tcell.Color) UIString {
|
|
||||||
newStr := make([]Cell, len(str))
|
|
||||||
for i, char := range str {
|
|
||||||
newStr[i] = NewColorCell(char, color)
|
|
||||||
}
|
|
||||||
return newStr
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStyleUIString(str string, style tcell.Style) UIString {
|
|
||||||
newStr := make([]Cell, len(str))
|
|
||||||
for i, char := range str {
|
|
||||||
newStr[i] = NewStyleCell(char, style)
|
|
||||||
}
|
|
||||||
return newStr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) Colorize(from, to int, color tcell.Color) {
|
|
||||||
for i := from; i < to; i++ {
|
|
||||||
str[i].Style = str[i].Style.Foreground(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) Draw(screen tcell.Screen, x, y int) {
|
|
||||||
offsetX := 0
|
|
||||||
for _, cell := range str {
|
|
||||||
offsetX += cell.Draw(screen, x+offsetX, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) RuneWidth() (width int) {
|
|
||||||
for _, cell := range str {
|
|
||||||
width += runewidth.RuneWidth(cell.Char)
|
|
||||||
}
|
|
||||||
return width
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) String() string {
|
|
||||||
var buf strings.Builder
|
|
||||||
for _, cell := range str {
|
|
||||||
buf.WriteRune(cell.Char)
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Truncate return string truncated with w cells
|
|
||||||
func (str UIString) Truncate(w int) UIString {
|
|
||||||
if str.RuneWidth() <= w {
|
|
||||||
return str[:]
|
|
||||||
}
|
|
||||||
width := 0
|
|
||||||
i := 0
|
|
||||||
for ; i < len(str); i++ {
|
|
||||||
cw := runewidth.RuneWidth(str[i].Char)
|
|
||||||
if width+cw > w {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
width += cw
|
|
||||||
}
|
|
||||||
return str[0:i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) IndexFrom(r rune, from int) int {
|
|
||||||
for i := from; i < len(str); i++ {
|
|
||||||
if str[i].Char == r {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) Index(r rune) int {
|
|
||||||
return str.IndexFrom(r, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) Count(r rune) (counter int) {
|
|
||||||
index := 0
|
|
||||||
for {
|
|
||||||
index = str.IndexFrom(r, index)
|
|
||||||
if index < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
index++
|
|
||||||
counter++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (str UIString) Split(sep rune) []UIString {
|
|
||||||
a := make([]UIString, str.Count(sep)+1)
|
|
||||||
i := 0
|
|
||||||
orig := str
|
|
||||||
for {
|
|
||||||
m := orig.Index(sep)
|
|
||||||
if m < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
a[i] = orig[:m]
|
|
||||||
orig = orig[m+1:]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
a[i] = orig
|
|
||||||
return a[:i+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
const DateFormat = "January _2, 2006"
|
const DateFormat = "January _2, 2006"
|
||||||
const TimeFormat = "15:04:05"
|
const TimeFormat = "15:04:05"
|
||||||
|
120
ui/messages/parser.go
Normal file
120
ui/messages/parser.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/debug"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseEvent(mx ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) UIMessage {
|
||||||
|
member := room.GetMember(evt.Sender)
|
||||||
|
if member != nil {
|
||||||
|
evt.Sender = member.DisplayName
|
||||||
|
}
|
||||||
|
switch evt.Type {
|
||||||
|
case "m.room.message":
|
||||||
|
return ParseMessage(mx, evt)
|
||||||
|
case "m.room.member":
|
||||||
|
return ParseMembershipEvent(evt)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unixToTime(unix int64) time.Time {
|
||||||
|
timestamp := time.Now()
|
||||||
|
if unix != 0 {
|
||||||
|
timestamp = time.Unix(unix/1000, unix%1000*1000)
|
||||||
|
}
|
||||||
|
return timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMessage(mx ifc.MatrixContainer, evt *gomatrix.Event) UIMessage {
|
||||||
|
msgtype, _ := evt.Content["msgtype"].(string)
|
||||||
|
ts := unixToTime(evt.Timestamp)
|
||||||
|
switch msgtype {
|
||||||
|
case "m.text", "m.notice":
|
||||||
|
text, _ := evt.Content["body"].(string)
|
||||||
|
return NewTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
|
||||||
|
case "m.image":
|
||||||
|
url, _ := evt.Content["url"].(string)
|
||||||
|
data, err := mx.Download(url)
|
||||||
|
if err != nil {
|
||||||
|
debug.Printf("Failed to download %s: %v", url, err)
|
||||||
|
}
|
||||||
|
return NewImageMessage(evt.ID, evt.Sender, msgtype, data, ts)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMembershipEventContent(evt *gomatrix.Event) (sender string, text UIString) {
|
||||||
|
membership, _ := evt.Content["membership"].(string)
|
||||||
|
displayname, _ := evt.Content["displayname"].(string)
|
||||||
|
if len(displayname) == 0 {
|
||||||
|
displayname = *evt.StateKey
|
||||||
|
}
|
||||||
|
prevMembership := "leave"
|
||||||
|
prevDisplayname := ""
|
||||||
|
if evt.Unsigned.PrevContent != nil {
|
||||||
|
prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string)
|
||||||
|
prevDisplayname, _ = evt.Unsigned.PrevContent["displayname"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if membership != prevMembership {
|
||||||
|
switch membership {
|
||||||
|
case "invite":
|
||||||
|
sender = "---"
|
||||||
|
text = NewColorUIString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorYellow)
|
||||||
|
text.Colorize(0, len(evt.Sender), widget.GetHashColor(evt.Sender))
|
||||||
|
text.Colorize(len(evt.Sender)+len(" invited "), len(displayname), widget.GetHashColor(displayname))
|
||||||
|
case "join":
|
||||||
|
sender = "-->"
|
||||||
|
text = NewColorUIString(fmt.Sprintf("%s joined the room.", displayname), tcell.ColorGreen)
|
||||||
|
text.Colorize(0, len(displayname), widget.GetHashColor(displayname))
|
||||||
|
case "leave":
|
||||||
|
sender = "<--"
|
||||||
|
if evt.Sender != *evt.StateKey {
|
||||||
|
reason, _ := evt.Content["reason"].(string)
|
||||||
|
text = NewColorUIString(fmt.Sprintf("%s kicked %s: %s", evt.Sender, displayname, reason), tcell.ColorRed)
|
||||||
|
text.Colorize(0, len(evt.Sender), widget.GetHashColor(evt.Sender))
|
||||||
|
text.Colorize(len(evt.Sender)+len(" kicked "), len(displayname), widget.GetHashColor(displayname))
|
||||||
|
} else {
|
||||||
|
text = NewColorUIString(fmt.Sprintf("%s left the room.", displayname), tcell.ColorRed)
|
||||||
|
text.Colorize(0, len(displayname), widget.GetHashColor(displayname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if displayname != prevDisplayname {
|
||||||
|
sender = "---"
|
||||||
|
text = NewColorUIString(fmt.Sprintf("%s changed their display name to %s.", prevDisplayname, displayname), tcell.ColorYellow)
|
||||||
|
text.Colorize(0, len(prevDisplayname), widget.GetHashColor(prevDisplayname))
|
||||||
|
text.Colorize(len(prevDisplayname)+len(" changed their display name to "), len(displayname), widget.GetHashColor(displayname))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMembershipEvent(evt *gomatrix.Event) UIMessage {
|
||||||
|
sender, text := getMembershipEventContent(evt)
|
||||||
|
ts := unixToTime(evt.Timestamp)
|
||||||
|
return NewExpandedTextMessage(evt.ID, sender, "m.room.membership", text, ts)
|
||||||
|
}
|
138
ui/messages/string.go
Normal file
138
ui/messages/string.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// 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 (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIString []Cell
|
||||||
|
|
||||||
|
func NewUIString(str string) UIString {
|
||||||
|
newStr := make([]Cell, len(str))
|
||||||
|
for i, char := range str {
|
||||||
|
newStr[i] = NewCell(char)
|
||||||
|
}
|
||||||
|
return newStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColorUIString(str string, color tcell.Color) UIString {
|
||||||
|
newStr := make([]Cell, len(str))
|
||||||
|
for i, char := range str {
|
||||||
|
newStr[i] = NewColorCell(char, color)
|
||||||
|
}
|
||||||
|
return newStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStyleUIString(str string, style tcell.Style) UIString {
|
||||||
|
newStr := make([]Cell, len(str))
|
||||||
|
for i, char := range str {
|
||||||
|
newStr[i] = NewStyleCell(char, style)
|
||||||
|
}
|
||||||
|
return newStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Colorize(from, length int, color tcell.Color) {
|
||||||
|
for i := from; i < from+length; i++ {
|
||||||
|
str[i].Style = str[i].Style.Foreground(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Draw(screen tcell.Screen, x, y int) {
|
||||||
|
offsetX := 0
|
||||||
|
for _, cell := range str {
|
||||||
|
offsetX += cell.Draw(screen, x+offsetX, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) RuneWidth() (width int) {
|
||||||
|
for _, cell := range str {
|
||||||
|
width += runewidth.RuneWidth(cell.Char)
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) String() string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for _, cell := range str {
|
||||||
|
buf.WriteRune(cell.Char)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate return string truncated with w cells
|
||||||
|
func (str UIString) Truncate(w int) UIString {
|
||||||
|
if str.RuneWidth() <= w {
|
||||||
|
return str[:]
|
||||||
|
}
|
||||||
|
width := 0
|
||||||
|
i := 0
|
||||||
|
for ; i < len(str); i++ {
|
||||||
|
cw := runewidth.RuneWidth(str[i].Char)
|
||||||
|
if width+cw > w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
width += cw
|
||||||
|
}
|
||||||
|
return str[0:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) IndexFrom(r rune, from int) int {
|
||||||
|
for i := from; i < len(str); i++ {
|
||||||
|
if str[i].Char == r {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Index(r rune) int {
|
||||||
|
return str.IndexFrom(r, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Count(r rune) (counter int) {
|
||||||
|
index := 0
|
||||||
|
for {
|
||||||
|
index = str.IndexFrom(r, index)
|
||||||
|
if index < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Split(sep rune) []UIString {
|
||||||
|
a := make([]UIString, str.Count(sep)+1)
|
||||||
|
i := 0
|
||||||
|
orig := str
|
||||||
|
for {
|
||||||
|
m := orig.Index(sep)
|
||||||
|
if m < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
a[i] = orig[:m]
|
||||||
|
orig = orig[m+1:]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
a[i] = orig
|
||||||
|
return a[:i+1]
|
||||||
|
}
|
@ -24,10 +24,67 @@ import (
|
|||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
gob.Register(&UITextMessage{})
|
gob.Register(&UITextMessage{})
|
||||||
|
gob.Register(&UIExpandedTextMessage{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type UIExpandedTextMessage struct {
|
||||||
|
UITextMessage
|
||||||
|
MsgUIStringText UIString
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExpandedTextMessage creates a new UIExpandedTextMessage object with the provided values and the default state.
|
||||||
|
func NewExpandedTextMessage(id, sender, msgtype string, text UIString, 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *UIExpandedTextMessage) GetUIStringText() UIString {
|
||||||
|
return msg.MsgUIStringText
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
fromExpandedMsg, ok := from.(*UIExpandedTextMessage)
|
||||||
|
if ok {
|
||||||
|
msg.MsgUIStringText = fromExpandedMsg.MsgUIStringText
|
||||||
|
} else {
|
||||||
|
msg.MsgUIStringText = NewColorUIString(fromMsg.Text(), from.TextColor())
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.RecalculateBuffer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type UITextMessage struct {
|
type UITextMessage struct {
|
||||||
@ -44,12 +101,12 @@ type UITextMessage struct {
|
|||||||
prevBufferWidth int
|
prevBufferWidth int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessage creates a new Message object with the provided values and the default state.
|
// NewTextMessage creates a new UITextMessage object with the provided values and the default state.
|
||||||
func NewMessage(id, sender, msgtype, text string, timestamp time.Time, senderColor tcell.Color) UIMessage {
|
func NewTextMessage(id, sender, msgtype, text string, timestamp time.Time) UIMessage {
|
||||||
return &UITextMessage{
|
return &UITextMessage{
|
||||||
MsgSender: sender,
|
MsgSender: sender,
|
||||||
MsgTimestamp: timestamp,
|
MsgTimestamp: timestamp,
|
||||||
MsgSenderColor: senderColor,
|
MsgSenderColor: widget.GetHashColor(sender),
|
||||||
MsgType: msgtype,
|
MsgType: msgtype,
|
||||||
MsgText: text,
|
MsgText: text,
|
||||||
MsgID: id,
|
MsgID: id,
|
||||||
@ -250,6 +307,10 @@ func (msg *UITextMessage) SetIsService(isService bool) {
|
|||||||
msg.MsgIsService = isService
|
msg.MsgIsService = isService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *UITextMessage) GetUIStringText() UIString {
|
||||||
|
return NewColorUIString(msg.Text(), msg.TextColor())
|
||||||
|
}
|
||||||
|
|
||||||
// Regular expressions used to split lines when calculating the buffer.
|
// Regular expressions used to split lines when calculating the buffer.
|
||||||
//
|
//
|
||||||
// From tview/textview.go
|
// From tview/textview.go
|
||||||
@ -267,10 +328,10 @@ func (msg *UITextMessage) CalculateBuffer(width int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg.buffer = []UIString{}
|
msg.buffer = []UIString{}
|
||||||
text := NewColorUIString(msg.Text(), msg.TextColor())
|
text := msg.GetUIStringText()
|
||||||
if msg.MsgType == "m.emote" {
|
if msg.MsgType == "m.emote" {
|
||||||
text = NewColorUIString(fmt.Sprintf("* %s %s", msg.MsgSender, msg.MsgText), msg.TextColor())
|
text = NewColorUIString(fmt.Sprintf("* %s %s", msg.MsgSender, text.String()), msg.TextColor())
|
||||||
text.Colorize(2, 2+len(msg.MsgSender), msg.SenderColor())
|
text.Colorize(2, len(msg.MsgSender), msg.SenderColor())
|
||||||
}
|
}
|
||||||
|
|
||||||
forcedLinebreaks := text.Split('\n')
|
forcedLinebreaks := text.Split('\n')
|
||||||
|
@ -260,7 +260,7 @@ func (view *RoomView) newUIMessage(id, sender, msgtype, text string, timestamp t
|
|||||||
if member != nil {
|
if member != nil {
|
||||||
sender = member.DisplayName
|
sender = member.DisplayName
|
||||||
}
|
}
|
||||||
return view.content.NewMessage(id, sender, msgtype, text, timestamp)
|
return messages.NewTextMessage(id, sender, msgtype, text, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"maunium.net/go/gomuks/matrix/pushrules"
|
"maunium.net/go/gomuks/matrix/pushrules"
|
||||||
"maunium.net/go/gomuks/matrix/rooms"
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
"maunium.net/go/gomuks/notification"
|
"maunium.net/go/gomuks/notification"
|
||||||
|
"maunium.net/go/gomuks/ui/messages"
|
||||||
"maunium.net/go/gomuks/ui/widget"
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
@ -446,12 +447,7 @@ func (view *MainView) LoadHistory(room string, initial bool) {
|
|||||||
}
|
}
|
||||||
roomView.Room.PrevBatch = prevBatch
|
roomView.Room.PrevBatch = prevBatch
|
||||||
for _, evt := range history {
|
for _, evt := range history {
|
||||||
var message ifc.Message
|
message := view.ParseEvent(roomView, &evt)
|
||||||
if evt.Type == "m.room.message" {
|
|
||||||
message = view.ProcessMessageEvent(roomView, &evt)
|
|
||||||
} else if evt.Type == "m.room.member" {
|
|
||||||
message = view.ProcessMembershipEvent(roomView, &evt)
|
|
||||||
}
|
|
||||||
if message != nil {
|
if message != nil {
|
||||||
roomView.AddMessage(message, ifc.PrependMessage)
|
roomView.AddMessage(message, ifc.PrependMessage)
|
||||||
}
|
}
|
||||||
@ -464,61 +460,6 @@ func (view *MainView) LoadHistory(room string, initial bool) {
|
|||||||
view.parent.Render()
|
view.parent.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MainView) ProcessMessageEvent(room ifc.RoomView, evt *gomatrix.Event) ifc.Message {
|
func (view *MainView) ParseEvent(roomView ifc.RoomView, evt *gomatrix.Event) ifc.Message {
|
||||||
text, _ := evt.Content["body"].(string)
|
return messages.ParseEvent(view.matrix, roomView.MxRoom(), evt)
|
||||||
msgtype, _ := evt.Content["msgtype"].(string)
|
|
||||||
return room.NewMessage(evt.ID, evt.Sender, msgtype, text, unixToTime(evt.Timestamp))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (view *MainView) getMembershipEventContent(evt *gomatrix.Event) (sender, text string) {
|
|
||||||
membership, _ := evt.Content["membership"].(string)
|
|
||||||
displayname, _ := evt.Content["displayname"].(string)
|
|
||||||
if len(displayname) == 0 {
|
|
||||||
displayname = *evt.StateKey
|
|
||||||
}
|
|
||||||
prevMembership := "leave"
|
|
||||||
prevDisplayname := ""
|
|
||||||
if evt.Unsigned.PrevContent != nil {
|
|
||||||
prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string)
|
|
||||||
prevDisplayname, _ = evt.Unsigned.PrevContent["displayname"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if membership != prevMembership {
|
|
||||||
switch membership {
|
|
||||||
case "invite":
|
|
||||||
sender = "---"
|
|
||||||
text = fmt.Sprintf("%s invited %s.", evt.Sender, displayname)
|
|
||||||
case "join":
|
|
||||||
sender = "-->"
|
|
||||||
text = fmt.Sprintf("%s joined the room.", displayname)
|
|
||||||
case "leave":
|
|
||||||
sender = "<--"
|
|
||||||
if evt.Sender != *evt.StateKey {
|
|
||||||
reason, _ := evt.Content["reason"].(string)
|
|
||||||
text = fmt.Sprintf("%s kicked %s: %s", evt.Sender, displayname, reason)
|
|
||||||
} else {
|
|
||||||
text = fmt.Sprintf("%s left the room.", displayname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if displayname != prevDisplayname {
|
|
||||||
sender = "---"
|
|
||||||
text = fmt.Sprintf("%s changed their display name to %s.", prevDisplayname, displayname)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (view *MainView) ProcessMembershipEvent(room ifc.RoomView, evt *gomatrix.Event) ifc.Message {
|
|
||||||
sender, text := view.getMembershipEventContent(evt)
|
|
||||||
if len(text) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return room.NewMessage(evt.ID, sender, "m.room.member", text, unixToTime(evt.Timestamp))
|
|
||||||
}
|
|
||||||
|
|
||||||
func unixToTime(unix int64) time.Time {
|
|
||||||
timestamp := time.Now()
|
|
||||||
if unix != 0 {
|
|
||||||
timestamp = time.Unix(unix/1000, unix%1000*1000)
|
|
||||||
}
|
|
||||||
return timestamp
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user