Allow clicking images and load images from cache
This commit is contained in:
parent
ff7ee333a1
commit
92a2428865
@ -34,5 +34,6 @@ type MatrixContainer interface {
|
||||
LeaveRoom(roomID string) error
|
||||
GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error)
|
||||
GetRoom(roomID string) *rooms.Room
|
||||
Download(mxcURL string) ([]byte, string, error)
|
||||
Download(mxcURL string) ([]byte, string, string, error)
|
||||
GetCachePath(homeserver, fileID string) string
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ const (
|
||||
type RoomView interface {
|
||||
MxRoom() *rooms.Room
|
||||
SaveHistory(dir string) error
|
||||
LoadHistory(dir string) (int, error)
|
||||
LoadHistory(gmx Gomuks, dir string) (int, error)
|
||||
|
||||
SetStatus(status string)
|
||||
SetTyping(users []string)
|
||||
|
@ -413,17 +413,16 @@ func (c *Container) GetRoom(roomID string) *rooms.Room {
|
||||
|
||||
var mxcRegex = regexp.MustCompile("mxc://(.+)/(.+)")
|
||||
|
||||
func (c *Container) Download(mxcURL string) (data []byte, fullID string, err error) {
|
||||
func (c *Container) Download(mxcURL string) (data []byte, hs, id string, err error) {
|
||||
parts := mxcRegex.FindStringSubmatch(mxcURL)
|
||||
if parts == nil || len(parts) != 3 {
|
||||
err = fmt.Errorf("invalid matrix content URL")
|
||||
return
|
||||
}
|
||||
hs := parts[1]
|
||||
id := parts[2]
|
||||
fullID = fmt.Sprintf("%s/%s", hs, id)
|
||||
hs = parts[1]
|
||||
id = parts[2]
|
||||
|
||||
cacheFile := c.getCachePath(hs, id)
|
||||
cacheFile := c.GetCachePath(hs, id)
|
||||
if _, err = os.Stat(cacheFile); err != nil {
|
||||
data, err = ioutil.ReadFile(cacheFile)
|
||||
if err == nil {
|
||||
@ -452,7 +451,8 @@ func (c *Container) Download(mxcURL string) (data []byte, fullID string, err err
|
||||
err = ioutil.WriteFile(cacheFile, data, 0600)
|
||||
return
|
||||
}
|
||||
func (c *Container) getCachePath(homeserver, fileID string) string {
|
||||
|
||||
func (c *Container) GetCachePath(homeserver, fileID string) string {
|
||||
dir := filepath.Join(c.config.MediaDir, homeserver)
|
||||
|
||||
err := os.MkdirAll(dir, 0700)
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"maunium.net/go/gomuks/lib/open"
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
"maunium.net/go/tcell"
|
||||
"maunium.net/go/gomuks/debug"
|
||||
@ -88,7 +89,7 @@ func (view *MessageView) SaveHistory(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (view *MessageView) LoadHistory(path string) (int, error) {
|
||||
func (view *MessageView) LoadHistory(gmx ifc.Gomuks, path string) (int, error) {
|
||||
file, err := os.OpenFile(path, os.O_RDONLY, 0600)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@ -112,6 +113,7 @@ func (view *MessageView) LoadHistory(path string) (int, error) {
|
||||
if message != nil {
|
||||
view.messages[index-indexOffset] = message
|
||||
view.updateWidestSender(message.Sender())
|
||||
message.RegisterGomuks(gmx)
|
||||
} else {
|
||||
indexOffset++
|
||||
}
|
||||
@ -251,6 +253,30 @@ func (view *MessageView) recalculateBuffers() {
|
||||
}
|
||||
}
|
||||
|
||||
func (view *MessageView) HandleClick(x, y int, button tcell.ButtonMask) {
|
||||
if button != tcell.Button1 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, _, height := view.GetRect()
|
||||
line := view.TotalHeight() - view.ScrollOffset - height + y
|
||||
if line < 0 || line >= view.TotalHeight() {
|
||||
return
|
||||
}
|
||||
|
||||
message := view.metaBuffer[line]
|
||||
imageMessage, ok := message.(*messages.UIImageMessage)
|
||||
if !ok {
|
||||
uiMessage, ok := message.(messages.UIMessage)
|
||||
if ok {
|
||||
debug.Print("Message clicked:", uiMessage.Text())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
open.Open(imageMessage.Path())
|
||||
}
|
||||
|
||||
const PaddingAtTop = 5
|
||||
|
||||
func (view *MessageView) AddScrollOffset(diff int) {
|
||||
|
86
ui/messages/expandedtextmessage.go
Normal file
86
ui/messages/expandedtextmessage.go
Normal file
@ -0,0 +1,86 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&UITextMessage{})
|
||||
gob.Register(&UIExpandedTextMessage{})
|
||||
}
|
||||
|
||||
type UIExpandedTextMessage struct {
|
||||
UITextMessage
|
||||
MsgTStringText tstring.TString
|
||||
}
|
||||
|
||||
// NewExpandedTextMessage creates a new UIExpandedTextMessage 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,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIExpandedTextMessage) GetTStringText() tstring.TString {
|
||||
return msg.MsgTStringText
|
||||
}
|
||||
|
||||
// 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.MsgTStringText = fromExpandedMsg.MsgTStringText
|
||||
} else {
|
||||
msg.MsgTStringText = tstring.NewColorTString(fromMsg.Text(), from.TextColor())
|
||||
}
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
}
|
||||
}
|
@ -19,15 +19,16 @@ package messages
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"image/color"
|
||||
|
||||
"maunium.net/go/gomuks/debug"
|
||||
"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/gomuks/lib/ansimage"
|
||||
"maunium.net/go/tcell"
|
||||
)
|
||||
|
||||
@ -37,12 +38,15 @@ func init() {
|
||||
|
||||
type UIImageMessage struct {
|
||||
UITextMessage
|
||||
Path string
|
||||
data []byte
|
||||
Homeserver string
|
||||
FileID string
|
||||
data []byte
|
||||
|
||||
gmx ifc.Gomuks
|
||||
}
|
||||
|
||||
// NewImageMessage creates a new UIImageMessage object with the provided values and the default state.
|
||||
func NewImageMessage(id, sender, msgtype string, path string, data []byte, timestamp time.Time) UIMessage {
|
||||
func NewImageMessage(gmx ifc.Gomuks, id, sender, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
|
||||
return &UIImageMessage{
|
||||
UITextMessage{
|
||||
MsgSender: sender,
|
||||
@ -55,11 +59,39 @@ func NewImageMessage(id, sender, msgtype string, path string, data []byte, times
|
||||
MsgIsHighlight: false,
|
||||
MsgIsService: false,
|
||||
},
|
||||
path,
|
||||
homeserver,
|
||||
fileID,
|
||||
data,
|
||||
gmx,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
|
||||
msg.gmx = gmx
|
||||
|
||||
debug.Print(len(msg.data), msg.data)
|
||||
if len(msg.data) == 0 {
|
||||
go func() {
|
||||
defer gmx.Recover()
|
||||
msg.updateData()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIImageMessage) 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 {
|
||||
debug.Print("Failed to download image %s/%s: %v", msg.Homeserver, msg.FileID, err)
|
||||
return
|
||||
}
|
||||
msg.data = data
|
||||
}
|
||||
|
||||
func (msg *UIImageMessage) 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()
|
||||
|
@ -31,6 +31,7 @@ type UIMessage interface {
|
||||
Height() int
|
||||
|
||||
RealSender() string
|
||||
RegisterGomuks(gmx ifc.Gomuks)
|
||||
}
|
||||
|
||||
const DateFormat = "January _2, 2006"
|
||||
|
@ -29,14 +29,14 @@ import (
|
||||
"maunium.net/go/tcell"
|
||||
)
|
||||
|
||||
func ParseEvent(mx ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) UIMessage {
|
||||
func ParseEvent(gmx ifc.Gomuks, 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)
|
||||
return ParseMessage(gmx, evt)
|
||||
case "m.room.member":
|
||||
return ParseMembershipEvent(evt)
|
||||
}
|
||||
@ -51,7 +51,7 @@ func unixToTime(unix int64) time.Time {
|
||||
return timestamp
|
||||
}
|
||||
|
||||
func ParseMessage(mx ifc.MatrixContainer, evt *gomatrix.Event) UIMessage {
|
||||
func ParseMessage(gmx ifc.Gomuks, evt *gomatrix.Event) UIMessage {
|
||||
msgtype, _ := evt.Content["msgtype"].(string)
|
||||
ts := unixToTime(evt.Timestamp)
|
||||
switch msgtype {
|
||||
@ -60,11 +60,11 @@ func ParseMessage(mx ifc.MatrixContainer, evt *gomatrix.Event) UIMessage {
|
||||
return NewTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
|
||||
case "m.image":
|
||||
url, _ := evt.Content["url"].(string)
|
||||
data, path, err := mx.Download(url)
|
||||
data, hs, id, err := gmx.Matrix().Download(url)
|
||||
if err != nil {
|
||||
debug.Printf("Failed to download %s: %v", url, err)
|
||||
}
|
||||
return NewImageMessage(evt.ID, evt.Sender, msgtype, path, data, ts)
|
||||
return NewImageMessage(gmx, evt.ID, evt.Sender, msgtype, hs, id, data, ts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -22,70 +22,14 @@ import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||
"maunium.net/go/tcell"
|
||||
"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(&UIExpandedTextMessage{})
|
||||
}
|
||||
|
||||
type UIExpandedTextMessage struct {
|
||||
UITextMessage
|
||||
MsgTStringText tstring.TString
|
||||
}
|
||||
|
||||
// NewExpandedTextMessage creates a new UIExpandedTextMessage 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,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UIExpandedTextMessage) GetTStringText() tstring.TString {
|
||||
return msg.MsgTStringText
|
||||
}
|
||||
|
||||
// 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.MsgTStringText = fromExpandedMsg.MsgTStringText
|
||||
} else {
|
||||
msg.MsgTStringText = tstring.NewColorTString(fromMsg.Text(), from.TextColor())
|
||||
}
|
||||
|
||||
msg.RecalculateBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
type UITextMessage struct {
|
||||
@ -118,6 +62,8 @@ func NewTextMessage(id, sender, msgtype, text string, timestamp time.Time) UIMes
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *UITextMessage) RegisterGomuks(gmx ifc.Gomuks) {}
|
||||
|
||||
// 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()
|
||||
|
@ -81,8 +81,8 @@ func (view *RoomView) SaveHistory(dir string) error {
|
||||
return view.MessageView().SaveHistory(view.logPath(dir))
|
||||
}
|
||||
|
||||
func (view *RoomView) LoadHistory(dir string) (int, error) {
|
||||
return view.MessageView().LoadHistory(view.logPath(dir))
|
||||
func (view *RoomView) LoadHistory(gmx ifc.Gomuks, dir string) (int, error) {
|
||||
return view.MessageView().LoadHistory(gmx, view.logPath(dir))
|
||||
}
|
||||
|
||||
func (view *RoomView) SetTabCompleteFunc(fn func(room *RoomView, text string, cursorOffset int) string) *RoomView {
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"maunium.net/go/tcell"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"maunium.net/go/gomatrix"
|
||||
"maunium.net/go/gomuks/config"
|
||||
@ -34,6 +33,7 @@ import (
|
||||
"maunium.net/go/gomuks/matrix/rooms"
|
||||
"maunium.net/go/gomuks/ui/messages"
|
||||
"maunium.net/go/gomuks/ui/widget"
|
||||
"maunium.net/go/tcell"
|
||||
"maunium.net/go/tview"
|
||||
)
|
||||
|
||||
@ -221,7 +221,7 @@ func (view *MainView) KeyEventHandler(roomView *RoomView, key *tcell.EventKey) *
|
||||
const WheelScrollOffsetDiff = 3
|
||||
|
||||
func (view *MainView) MouseEventHandler(roomView *RoomView, event *tcell.EventMouse) *tcell.EventMouse {
|
||||
if event.Buttons() == tcell.ButtonNone {
|
||||
if event.Buttons() == tcell.ButtonNone || event.HasMotion() {
|
||||
return event
|
||||
}
|
||||
view.BumpFocus()
|
||||
@ -230,11 +230,6 @@ func (view *MainView) MouseEventHandler(roomView *RoomView, event *tcell.EventMo
|
||||
x, y := event.Position()
|
||||
|
||||
switch event.Buttons() {
|
||||
case tcell.Button1:
|
||||
mx, my, mw, mh := msgView.GetRect()
|
||||
if x >= mx && y >= my && x < mx+mw && y < my+mh {
|
||||
debug.Print("Message view clicked")
|
||||
}
|
||||
case tcell.WheelUp:
|
||||
if msgView.IsAtTop() {
|
||||
go view.LoadHistory(roomView.Room.ID, false)
|
||||
@ -252,7 +247,12 @@ func (view *MainView) MouseEventHandler(roomView *RoomView, event *tcell.EventMo
|
||||
roomView.Room.MarkRead()
|
||||
}
|
||||
default:
|
||||
debug.Print("Mouse event received:", event.Buttons(), event.Modifiers(), x, y)
|
||||
mx, my, mw, mh := msgView.GetRect()
|
||||
if x >= mx && y >= my && x < mx+mw && y < my+mh {
|
||||
msgView.HandleClick(x-mx, y-my, event.Buttons())
|
||||
} else {
|
||||
debug.Print("Mouse event received:", event.Buttons(), event.Modifiers(), x, y)
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
@ -315,7 +315,7 @@ func (view *MainView) addRoom(index int, room string) {
|
||||
view.roomView.AddPage(room, roomView, true, false)
|
||||
roomView.UpdateUserList()
|
||||
|
||||
count, err := roomView.LoadHistory(view.config.HistoryDir)
|
||||
count, err := roomView.LoadHistory(view.gmx, view.config.HistoryDir)
|
||||
if err != nil {
|
||||
debug.Printf("Failed to load history of %s: %v", roomView.Room.GetTitle(), err)
|
||||
} else if count <= 0 {
|
||||
@ -466,5 +466,5 @@ func (view *MainView) LoadHistory(room string, initial bool) {
|
||||
}
|
||||
|
||||
func (view *MainView) ParseEvent(roomView ifc.RoomView, evt *gomatrix.Event) ifc.Message {
|
||||
return messages.ParseEvent(view.matrix, roomView.MxRoom(), evt)
|
||||
return messages.ParseEvent(view.gmx, roomView.MxRoom(), evt)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user