aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTulir Asokan <tulir@maunium.net>2020-04-08 15:30:29 +0300
committerTulir Asokan <tulir@maunium.net>2020-04-08 15:30:29 +0300
commita6f6fb3ef22658508671296a31367a198205da99 (patch)
tree448766b706b0dab25e6541fd1d9cb5e2a7439c51
parent80564b2887085a088a4f753042dbe345092e45a1 (diff)
Display thumbnail for all files and add commands to download and open files
-rw-r--r--config/config.go3
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--interface/matrix.go11
-rw-r--r--matrix/matrix.go96
-rw-r--r--ui/command-processor.go4
-rw-r--r--ui/commands.go16
-rw-r--r--ui/message-view.go4
-rw-r--r--ui/messages/base.go2
-rw-r--r--ui/messages/expandedtextmessage.go3
-rw-r--r--ui/messages/filemessage.go97
-rw-r--r--ui/messages/htmlmessage.go3
-rw-r--r--ui/messages/parser.go14
-rw-r--r--ui/messages/redactedmessage.go3
-rw-r--r--ui/messages/textmessage.go3
-rw-r--r--ui/room-view.go61
16 files changed, 224 insertions, 100 deletions
diff --git a/config/config.go b/config/config.go
index e8c7b1a..3507833 100644
--- a/config/config.go
+++ b/config/config.go
@@ -66,6 +66,7 @@ type Config struct {
HistoryPath string `yaml:"history_path"`
RoomListPath string `yaml:"room_list_path"`
MediaDir string `yaml:"media_dir"`
+ DownloadDir string `yaml:"download_dir"`
StateDir string `yaml:"state_dir"`
Preferences UserPreferences `yaml:"-"`
@@ -78,6 +79,7 @@ type Config struct {
// NewConfig creates a config that loads data from the given directory.
func NewConfig(configDir, cacheDir string) *Config {
+ home, _ := os.UserHomeDir()
return &Config{
Dir: configDir,
CacheDir: cacheDir,
@@ -85,6 +87,7 @@ func NewConfig(configDir, cacheDir string) *Config {
RoomListPath: filepath.Join(cacheDir, "rooms.gob.gz"),
StateDir: filepath.Join(cacheDir, "state"),
MediaDir: filepath.Join(cacheDir, "media"),
+ DownloadDir: home,
RoomCacheSize: 32,
RoomCacheAge: 1 * 60,
diff --git a/go.mod b/go.mod
index 137267a..6daa947 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2
gopkg.in/yaml.v2 v2.2.8
- maunium.net/go/mautrix v0.1.0-beta.2.0.20200403220206-c81fdd9fe68f
+ maunium.net/go/mautrix v0.1.0-beta.2.0.20200408115320-100bcaabf80b
maunium.net/go/mauview v0.1.0-beta.1
maunium.net/go/tcell v0.1.0
)
diff --git a/go.sum b/go.sum
index 371b37c..60af87a 100644
--- a/go.sum
+++ b/go.sum
@@ -79,6 +79,8 @@ maunium.net/go/mautrix v0.1.0-beta.1.0.20200320135656-32dd5f172592 h1:WybA4BBUyo
maunium.net/go/mautrix v0.1.0-beta.1.0.20200320135656-32dd5f172592/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM=
maunium.net/go/mautrix v0.1.0-beta.2.0.20200403220206-c81fdd9fe68f h1:Fg7t2i+AtUX4NDN5Ue9HsHO/Vog0JN9CZsOH3u+hDOc=
maunium.net/go/mautrix v0.1.0-beta.2.0.20200403220206-c81fdd9fe68f/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM=
+maunium.net/go/mautrix v0.1.0-beta.2.0.20200408115320-100bcaabf80b h1:ifTLB2qPUcZmeCeh2/CvlBZz/ddrdXcszdH7H9qFHzE=
+maunium.net/go/mautrix v0.1.0-beta.2.0.20200408115320-100bcaabf80b/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM=
maunium.net/go/mauview v0.1.0-beta.1 h1:hRprD6NTi5Mw7i97DKmgs/TzFQeNpGPytPoswNlU/Ww=
maunium.net/go/mauview v0.1.0-beta.1/go.mod h1:og9WbzmWe9SNYNyOFlCv8qa9zMcOvG2nzRJ5vYyud9U=
maunium.net/go/tcell v0.1.0 h1:XzsEoGCvOw5nac+tlkSLzQcliLYTN4PrtA7ar2ptjSM=
diff --git a/interface/matrix.go b/interface/matrix.go
index d4351d7..34d8f7f 100644
--- a/interface/matrix.go
+++ b/interface/matrix.go
@@ -17,6 +17,7 @@
package ifc
import (
+ "maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mautrix"
@@ -24,12 +25,13 @@ import (
)
type Relation struct {
- Type mautrix.RelationType
+ Type mautrix.RelationType
Event *event.Event
}
type MatrixContainer interface {
Client() *mautrix.Client
+ Preferences() *config.UserPreferences
InitClient() error
Initialized() bool
@@ -55,7 +57,8 @@ type MatrixContainer interface {
GetRoom(roomID string) *rooms.Room
GetOrCreateRoom(roomID string) *rooms.Room
- Download(mxcURL string) ([]byte, string, string, error)
- GetDownloadURL(homeserver, fileID string) string
- GetCachePath(homeserver, fileID string) string
+ Download(uri mautrix.ContentURI) ([]byte, error)
+ DownloadToDisk(uri mautrix.ContentURI, target string) (string, error)
+ GetDownloadURL(uri mautrix.ContentURI) string
+ GetCachePath(uri mautrix.ContentURI) string
}
diff --git a/matrix/matrix.go b/matrix/matrix.go
index 852a410..651f6bb 100644
--- a/matrix/matrix.go
+++ b/matrix/matrix.go
@@ -29,7 +29,6 @@ import (
"os"
"path"
"path/filepath"
- "regexp"
"runtime"
dbg "runtime/debug"
"time"
@@ -389,6 +388,10 @@ func (c *Container) HandlePreferences(source EventSource, evt *mautrix.Event) {
}
}
+func (c *Container) Preferences() *config.UserPreferences {
+ return &c.config.Preferences
+}
+
func (c *Container) SendPreferencesToMatrix() {
defer debug.Recover()
debug.Print("Sending updated preferences:", c.config.Preferences)
@@ -926,22 +929,73 @@ func (c *Container) GetRoom(roomID string) *rooms.Room {
return c.config.Rooms.Get(roomID)
}
-var mxcRegex = regexp.MustCompile("mxc://(.+)/(.+)")
+func cp(src, dst string) error {
+ in, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
-// Download fetches the given Matrix content (mxc) URL and returns the data, homeserver, file ID and potential errors.
-//
-// The file will be either read from the media cache (if found) or downloaded from the server.
-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
+ out, err := os.Create(dst)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return err
+ }
+ return out.Close()
+}
+
+func (c *Container) DownloadToDisk(uri mautrix.ContentURI, target string) (fullPath string, err error) {
+ cachePath := c.GetCachePath(uri)
+ if target == "" {
+ fullPath = cachePath
+ } else if !path.IsAbs(target) {
+ fullPath = path.Join(c.config.DownloadDir, target)
+ } else {
+ fullPath = target
+ }
+
+ if _, statErr := os.Stat(cachePath); os.IsNotExist(statErr) {
+ var file *os.File
+ file, err = os.OpenFile(cachePath, os.O_CREATE|os.O_WRONLY, 0600)
+ if err != nil {
+ return
+ }
+ defer file.Close()
+
+ var resp *http.Response
+ resp, err = c.client.Client.Get(c.GetDownloadURL(uri))
+ if err != nil {
+ return
+ }
+ defer resp.Body.Close()
+
+ _, err = io.Copy(file, resp.Body)
+ if err != nil {
+ return
+ }
}
- hs = parts[1]
- id = parts[2]
+ if fullPath != cachePath {
+ err = os.MkdirAll(path.Dir(fullPath), 0700)
+ if err != nil {
+ return
+ }
+ err = cp(cachePath, fullPath)
+ }
+
+ return
+}
- cacheFile := c.GetCachePath(hs, id)
+// Download fetches the given Matrix content (mxc) URL and returns the data, homeserver, file ID and potential errors.
+//
+// The file will be either read from the media cache (if found) or downloaded from the server.
+func (c *Container) Download(uri mautrix.ContentURI) (data []byte, err error) {
+ cacheFile := c.GetCachePath(uri)
var info os.FileInfo
if info, err = os.Stat(cacheFile); err == nil && !info.IsDir() {
data, err = ioutil.ReadFile(cacheFile)
@@ -950,22 +1004,22 @@ func (c *Container) Download(mxcURL string) (data []byte, hs, id string, err err
}
}
- data, err = c.download(hs, id, cacheFile)
+ data, err = c.download(uri, cacheFile)
return
}
-func (c *Container) GetDownloadURL(hs, id string) string {
+func (c *Container) GetDownloadURL(uri mautrix.ContentURI) string {
dlURL, _ := url.Parse(c.client.HomeserverURL.String())
if dlURL.Scheme == "" {
dlURL.Scheme = "https"
}
- dlURL.Path = path.Join(dlURL.Path, "/_matrix/media/v1/download", hs, id)
+ dlURL.Path = path.Join(dlURL.Path, "/_matrix/media/v1/download", uri.Homeserver, uri.FileID)
return dlURL.String()
}
-func (c *Container) download(hs, id, cacheFile string) (data []byte, err error) {
+func (c *Container) download(uri mautrix.ContentURI, cacheFile string) (data []byte, err error) {
var resp *http.Response
- resp, err = c.client.Client.Get(c.GetDownloadURL(hs, id))
+ resp, err = c.client.Client.Get(c.GetDownloadURL(uri))
if err != nil {
return
}
@@ -985,13 +1039,13 @@ func (c *Container) download(hs, id, cacheFile string) (data []byte, err error)
// GetCachePath gets the path to the cached version of the given homeserver:fileID combination.
// The file may or may not exist, use Download() to ensure it has been cached.
-func (c *Container) GetCachePath(homeserver, fileID string) string {
- dir := filepath.Join(c.config.MediaDir, homeserver)
+func (c *Container) GetCachePath(uri mautrix.ContentURI) string {
+ dir := filepath.Join(c.config.MediaDir, uri.Homeserver)
err := os.MkdirAll(dir, 0700)
if err != nil {
return ""
}
- return filepath.Join(dir, fileID)
+ return filepath.Join(dir, uri.FileID)
}
diff --git a/ui/command-processor.go b/ui/command-processor.go
index 3212dae..082d157 100644
--- a/ui/command-processor.go
+++ b/ui/command-processor.go
@@ -92,6 +92,8 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
"remove": {"redact"},
"rm": {"redact"},
"del": {"redact"},
+ "dl": {"download"},
+ "o": {"open"},
},
commands: map[string]CommandHandler{
"unknown-command": cmdUnknownCommand,
@@ -115,6 +117,8 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
"reply": cmdReply,
"redact": cmdRedact,
"react": cmdReact,
+ "download": cmdDownload,
+ "open": cmdOpen,
"sendevent": cmdSendEvent,
"msendevent": cmdMSendEvent,
"setstate": cmdSetState,
diff --git a/ui/commands.go b/ui/commands.go
index a57a78d..a74a164 100644
--- a/ui/commands.go
+++ b/ui/commands.go
@@ -150,9 +150,11 @@ func cmdID(cmd *Command) {
type SelectReason string
const (
- SelectReply SelectReason = "reply to"
- SelectReact = "react to"
- SelectRedact = "redact"
+ SelectReply SelectReason = "reply to"
+ SelectReact = "react to"
+ SelectRedact = "redact"
+ SelectDownload = "download"
+ SelectOpen = "open"
)
func cmdReply(cmd *Command) {
@@ -163,6 +165,14 @@ func cmdRedact(cmd *Command) {
cmd.Room.StartSelecting(SelectRedact, strings.Join(cmd.Args, " "))
}
+func cmdDownload(cmd *Command) {
+ cmd.Room.StartSelecting(SelectDownload, strings.Join(cmd.Args, " "))
+}
+
+func cmdOpen(cmd *Command) {
+ cmd.Room.StartSelecting(SelectOpen, strings.Join(cmd.Args, " "))
+}
+
func cmdReact(cmd *Command) {
if len(cmd.Args) == 0 {
cmd.Reply("Usage: /react <reaction>")
diff --git a/ui/message-view.go b/ui/message-view.go
index eab7bf6..4f406e1 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -349,8 +349,8 @@ func (view *MessageView) SetSelected(message *messages.UIMessage) {
}
func (view *MessageView) handleMessageClick(message *messages.UIMessage, mod tcell.ModMask) bool {
- if msg, ok := message.Renderer.(*messages.FileMessage); ok && mod > 0 {
- open.Open(msg.Path())
+ if msg, ok := message.Renderer.(*messages.FileMessage); ok && mod > 0 && !msg.Thumbnail.IsEmpty() {
+ open.Open(msg.ThumbnailPath())
// No need to re-render
return false
}
diff --git a/ui/messages/base.go b/ui/messages/base.go
index 58d9b6e..745bfcd 100644
--- a/ui/messages/base.go
+++ b/ui/messages/base.go
@@ -27,7 +27,6 @@ import (
"maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/ui/widget"
)
@@ -36,7 +35,6 @@ type MessageRenderer interface {
NotificationContent() string
PlainText() string
CalculateBuffer(prefs config.UserPreferences, width int, msg *UIMessage)
- RegisterMatrix(matrix ifc.MatrixContainer, prefs config.UserPreferences)
Height() int
Clone() MessageRenderer
String() string
diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go
index c666613..bd5aba9 100644
--- a/ui/messages/expandedtextmessage.go
+++ b/ui/messages/expandedtextmessage.go
@@ -20,7 +20,6 @@ import (
"fmt"
"time"
- ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -88,5 +87,3 @@ func (msg *ExpandedTextMessage) Draw(screen mauview.Screen) {
line.Draw(screen, 0, y)
}
}
-
-func (msg *ExpandedTextMessage) RegisterMatrix(matrix ifc.MatrixContainer, prefs config.UserPreferences) {}
diff --git a/ui/messages/filemessage.go b/ui/messages/filemessage.go
index 9fb3499..daf6c00 100644
--- a/ui/messages/filemessage.go
+++ b/ui/messages/filemessage.go
@@ -23,6 +23,7 @@ import (
"image/color"
"maunium.net/go/gomuks/matrix/event"
+ "maunium.net/go/mautrix"
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -34,72 +35,86 @@ import (
)
type FileMessage struct {
- Body string
- Homeserver string
- FileID string
- data []byte
- buffer []tstring.TString
+ Type mautrix.MessageType
+ Body string
+ URL mautrix.ContentURI
+ Thumbnail mautrix.ContentURI
+ imageData []byte
+ buffer []tstring.TString
matrix ifc.MatrixContainer
}
// NewFileMessage creates a new FileMessage object with the provided values and the default state.
-func NewFileMessage(matrix ifc.MatrixContainer, evt *event.Event, displayname string, body, homeserver, fileID string, data []byte) *UIMessage {
+func NewFileMessage(matrix ifc.MatrixContainer, evt *event.Event, displayname string) *UIMessage {
+ url, _ := mautrix.ParseContentURI(evt.Content.URL)
+ thumbnail, _ := mautrix.ParseContentURI(evt.Content.GetInfo().ThumbnailURL)
return newUIMessage(evt, displayname, &FileMessage{
- Body: body,
- Homeserver: homeserver,
- FileID: fileID,
- data: data,
- matrix: matrix,
+ Type: evt.Content.MsgType,
+ Body: evt.Content.Body,
+ URL: url,
+ Thumbnail: thumbnail,
+ matrix: matrix,
})
}
func (msg *FileMessage) Clone() MessageRenderer {
- data := make([]byte, len(msg.data))
- copy(data, msg.data)
+ data := make([]byte, len(msg.imageData))
+ copy(data, msg.imageData)
return &FileMessage{
- Body: msg.Body,
- Homeserver: msg.Homeserver,
- FileID: msg.FileID,
- data: data,
- matrix: msg.matrix,
- }
-}
-
-func (msg *FileMessage) RegisterMatrix(matrix ifc.MatrixContainer, prefs config.UserPreferences) {
- msg.matrix = matrix
-
- if len(msg.data) == 0 && !prefs.DisableDownloads {
- go msg.updateData()
+ Body: msg.Body,
+ URL: msg.URL,
+ Thumbnail: msg.Thumbnail,
+ imageData: data,
+ matrix: msg.matrix,
}
}
func (msg *FileMessage) NotificationContent() string {
- return "Sent a file"
+ switch msg.Type {
+ case mautrix.MsgImage:
+ return "Sent an image"
+ case mautrix.MsgAudio:
+ return "Sent an audio file"
+ case mautrix.MsgVideo:
+ return "Sent a video"
+ case mautrix.MsgFile:
+ fallthrough
+ default:
+ return "Sent a file"
+ }
}
func (msg *FileMessage) PlainText() string {
- return fmt.Sprintf("%s: %s", msg.Body, msg.matrix.GetDownloadURL(msg.Homeserver, msg.FileID))
+ return fmt.Sprintf("%s: %s", msg.Body, msg.matrix.GetDownloadURL(msg.URL))
}
func (msg *FileMessage) String() string {
- return fmt.Sprintf(`&messages.FileMessage{Body="%s", Homeserver="%s", FileID="%s"}`, msg.Body, msg.Homeserver, msg.FileID)
+ return fmt.Sprintf(`&messages.FileMessage{Body="%s", URL="%s", Thumbnail="%s"}`, msg.Body, msg.URL, msg.Thumbnail)
}
-func (msg *FileMessage) updateData() {
- defer debug.Recover()
- debug.Print("Loading file:", msg.Homeserver, msg.FileID)
- data, _, _, err := msg.matrix.Download(fmt.Sprintf("mxc://%s/%s", msg.Homeserver, msg.FileID))
+func (msg *FileMessage) DownloadPreview() {
+ url := msg.Thumbnail
+ if url.IsEmpty() {
+ if msg.Type == mautrix.MsgImage && !msg.URL.IsEmpty() {
+ msg.Thumbnail = msg.URL
+ url = msg.Thumbnail
+ } else {
+ return
+ }
+ }
+ debug.Print("Loading file:", url)
+ data, err := msg.matrix.Download(url)
if err != nil {
- debug.Printf("Failed to download file %s/%s: %v", msg.Homeserver, msg.FileID, err)
+ debug.Printf("Failed to download file %s: %v", url, err)
return
}
- debug.Print("File", msg.Homeserver, msg.FileID, "loaded.")
- msg.data = data
+ debug.Print("File", url, "loaded.")
+ msg.imageData = data
}
-func (msg *FileMessage) Path() string {
- return msg.matrix.GetCachePath(msg.Homeserver, msg.FileID)
+func (msg *FileMessage) ThumbnailPath() string {
+ return msg.matrix.GetCachePath(msg.Thumbnail)
}
func (msg *FileMessage) CalculateBuffer(prefs config.UserPreferences, width int, uiMsg *UIMessage) {
@@ -107,12 +122,12 @@ func (msg *FileMessage) CalculateBuffer(prefs config.UserPreferences, width int,
return
}
- if prefs.BareMessageView || prefs.DisableImages || uiMsg.Type != "m.image" {
+ if prefs.BareMessageView || prefs.DisableImages || len(msg.imageData) == 0 {
msg.buffer = calculateBufferWithText(prefs, tstring.NewTString(msg.PlainText()), width, uiMsg)
return
}
- img, _, err := image.DecodeConfig(bytes.NewReader(msg.data))
+ img, _, err := image.DecodeConfig(bytes.NewReader(msg.imageData))
if err != nil {
debug.Print("File could not be decoded:", err)
}
@@ -121,7 +136,7 @@ func (msg *FileMessage) CalculateBuffer(prefs config.UserPreferences, width int,
imgWidth = width / 3
}
- ansFile, err := ansimage.NewScaledFromReader(bytes.NewReader(msg.data), 0, imgWidth, color.Black)
+ ansFile, err := ansimage.NewScaledFromReader(bytes.NewReader(msg.imageData), 0, imgWidth, color.Black)
if err != nil {
msg.buffer = []tstring.TString{tstring.NewColorTString("Failed to display image", tcell.ColorRed)}
debug.Print("Failed to display image:", err)
diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go
index ff49e57..af3ce41 100644
--- a/ui/messages/htmlmessage.go
+++ b/ui/messages/htmlmessage.go
@@ -17,7 +17,6 @@
package messages
import (
- ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -39,8 +38,6 @@ func NewHTMLMessage(evt *event.Event, displayname string, root html.Entity) *UIM
})
}
-func (hw *HTMLMessage) RegisterMatrix(matrix ifc.MatrixContainer, prefs config.UserPreferences) {}
-
func (hw *HTMLMessage) Clone() MessageRenderer {
return &HTMLMessage{
Root: hw.Root.Clone(),
diff --git a/ui/messages/parser.go b/ui/messages/parser.go
index 1c145e5..d0bc6e0 100644
--- a/ui/messages/parser.go
+++ b/ui/messages/parser.go
@@ -24,7 +24,6 @@ import (
"maunium.net/go/mautrix"
"maunium.net/go/tcell"
- "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/rooms"
"maunium.net/go/gomuks/ui/messages/html"
@@ -132,18 +131,19 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *event.Event
evt.Content = *evt.Gomuks.Edits[len(evt.Gomuks.Edits)-1].Content.NewContent
}
switch evt.Content.MsgType {
- case "m.text", "m.notice", "m.emote":
+ case mautrix.MsgText, mautrix.MsgNotice, mautrix.MsgEmote:
if evt.Content.Format == mautrix.FormatHTML {
return NewHTMLMessage(evt, displayname, html.Parse(room, evt, displayname))
}
evt.Content.Body = strings.Replace(evt.Content.Body, "\t", " ", -1)
return NewTextMessage(evt, displayname, evt.Content.Body)
- case "m.file", "m.video", "m.audio", "m.image":
- data, hs, id, err := matrix.Download(evt.Content.URL)
- if err != nil {
- debug.Printf("Failed to download %s: %v", evt.Content.URL, err)
+ case mautrix.MsgImage, mautrix.MsgVideo, mautrix.MsgAudio, mautrix.MsgFile:
+ msg := NewFileMessage(matrix, evt, displayname)
+ if !matrix.Preferences().DisableDownloads {
+ renderer := msg.Renderer.(*FileMessage)
+ renderer.DownloadPreview()
}
- return NewFileMessage(matrix, evt, displayname, evt.Content.Body, hs, id, data)
+ return msg
}
return nil
}
diff --git a/ui/messages/redactedmessage.go b/ui/messages/redactedmessage.go
index 56b4822..34d880b 100644
--- a/ui/messages/redactedmessage.go
+++ b/ui/messages/redactedmessage.go
@@ -17,7 +17,6 @@
package messages
import (
- ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -64,5 +63,3 @@ func (msg *RedactedMessage) Draw(screen mauview.Screen) {
screen.SetContent(x, 0, RedactionChar, nil, RedactionStyle)
}
}
-
-func (msg *RedactedMessage) RegisterMatrix(matrix ifc.MatrixContainer, prefs config.UserPreferences) {}
diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go
index 0bfa27b..2e59c8f 100644
--- a/ui/messages/textmessage.go
+++ b/ui/messages/textmessage.go
@@ -20,7 +20,6 @@ import (
"fmt"
"time"
- ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mauview"
@@ -101,5 +100,3 @@ func (msg *TextMessage) Draw(screen mauview.Screen) {
line.Draw(screen, 0, y)
}
}
-
-func (msg *TextMessage) RegisterMatrix(matrix ifc.MatrixContainer, prefs config.UserPreferences) {}
diff --git a/ui/room-view.go b/ui/room-view.go
index ef19c9d..29c455d 100644
--- a/ui/room-view.go
+++ b/ui/room-view.go
@@ -27,6 +27,7 @@ import (
"github.com/mattn/go-runewidth"
"maunium.net/go/gomuks/debug"
+ "maunium.net/go/gomuks/lib/open"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mauview"
@@ -191,6 +192,17 @@ func (view *RoomView) OnSelect(message *messages.UIMessage) {
go view.SendReaction(message.EventID, view.selectContent)
case SelectRedact:
go view.Redact(message.EventID, view.selectContent)
+ case SelectDownload, SelectOpen:
+ msg, ok := message.Renderer.(*messages.FileMessage)
+ if ok {
+ path := ""
+ if len(view.selectContent) > 0 {
+ path = view.selectContent
+ } else if view.selectReason == SelectDownload {
+ path = msg.Body
+ }
+ go view.Download(msg.URL, path, view.selectReason == SelectOpen)
+ }
}
view.selecting = false
view.selectContent = ""
@@ -465,9 +477,22 @@ func (view *RoomView) SetEditing(evt *event.Event) {
view.input.SetCursorOffset(-1)
}
-func (view *RoomView) findMessage(current *event.Event, ownMessage, forward bool) *messages.UIMessage {
+type findFilter func(evt *event.Event) bool
+
+func (view *RoomView) filterOwnOnly(evt *event.Event) bool {
+ return evt.Sender == view.parent.matrix.Client().UserID && evt.Type == mautrix.EventMessage
+}
+
+func (view *RoomView) filterMediaOnly(evt *event.Event) bool {
+ return evt.Type == mautrix.EventMessage && (
+ evt.Content.MsgType == mautrix.MsgFile ||
+ evt.Content.MsgType == mautrix.MsgImage ||
+ evt.Content.MsgType == mautrix.MsgAudio ||
+ evt.Content.MsgType == mautrix.MsgVideo)
+}
+
+func (view *RoomView) findMessage(current *event.Event, forward bool, allow findFilter) *messages.UIMessage {
currentFound := current == nil
- self := view.parent.matrix.Client().UserID
msgs := view.MessageView().messages
for i := 0; i < len(msgs); i++ {
index := i
@@ -478,7 +503,7 @@ func (view *RoomView) findMessage(current *event.Event, ownMessage, forward bool
if evt.EventID == "" || evt.EventID == evt.TxnID || evt.IsService {
continue
} else if currentFound {
- if !ownMessage || (evt.SenderID == self && evt.Event.Type == mautrix.EventMessage) {
+ if allow == nil || allow(evt.Event) {
return evt
}
} else if evt.EventID == current.ID {
@@ -492,7 +517,7 @@ func (view *RoomView) EditNext() {
if view.editing == nil {
return
}
- foundMsg := view.findMessage(view.editing, true, true)
+ foundMsg := view.findMessage(view.editing, true, view.filterOwnOnly)
view.SetEditing(foundMsg.GetEvent())
}
@@ -500,7 +525,7 @@ func (view *RoomView) EditPrevious() {
if view.replying != nil {
return
}
- foundMsg := view.findMessage(view.editing, true, false)
+ foundMsg := view.findMessage(view.editing, false, view.filterOwnOnly)
if foundMsg != nil {
view.SetEditing(foundMsg.GetEvent())
}
@@ -511,7 +536,11 @@ func (view *RoomView) SelectNext() {
if msgView.selected == nil {
return
}
- foundMsg := view.findMessage(msgView.selected.GetEvent(), false, true)
+ var filter findFilter
+ if view.selectReason == SelectDownload || view.selectReason == SelectOpen {
+ filter = view.filterMediaOnly
+ }
+ foundMsg := view.findMessage(msgView.selected.GetEvent(), true, filter)
if foundMsg != nil {
msgView.SetSelected(foundMsg)
// TODO scroll selected message into view
@@ -520,7 +549,11 @@ func (view *RoomView) SelectNext() {
func (view *RoomView) SelectPrevious() {
msgView := view.MessageView()
- foundMsg := view.findMessage(msgView.selected.GetEvent(), false, false)
+ var filter findFilter
+ if view.selectReason == SelectDownload || view.selectReason == SelectOpen {
+ filter = view.filterMediaOnly
+ }
+ foundMsg := view.findMessage(msgView.selected.GetEvent(), false, filter)
if foundMsg != nil {
msgView.SetSelected(foundMsg)
// TODO scroll selected message into view
@@ -578,6 +611,20 @@ func (view *RoomView) InputSubmit(text string) {
view.SetInputText("")
}
+func (view *RoomView) Download(url mautrix.ContentURI, filename string, openFile bool) {
+ path, err := view.parent.matrix.DownloadToDisk(url, filename)
+ if err != nil {
+ view.AddServiceMessage(fmt.Sprintf("Failed to download media: %v", err))
+ view.parent.parent.Render()
+ return
+ }
+ view.AddServiceMessage(fmt.Sprintf("File downloaded to %s", path))
+ view.parent.parent.Render()
+ if openFile {
+ open.Open(path)
+ }
+}
+
func (view *RoomView) Redact(eventID, reason string) {
defer debug.Recover()
err := view.parent.matrix.Redact(view.Room.ID, eventID, reason)