Added support for attaching images to posts

This commit is contained in:
Christian Muehlhaeuser 2019-05-19 08:08:40 +02:00
parent 53855adda8
commit 81bc8a4840
No known key found for this signature in database
GPG key ID: 3CF9FA45CA1EBB7E
8 changed files with 254 additions and 10 deletions

View file

@ -36,6 +36,7 @@ type Follow struct {
// Media describes a media item.
type Media struct {
ID string
Preview string
URL string
}

View file

@ -172,22 +172,51 @@ func (mod *Account) Logo() string {
}
// Post posts a new status
func (mod *Account) Post(message string) error {
_, err := mod.client.PostStatus(context.Background(), &mastodon.Toot{
func (mod *Account) Post(message string, attachments []string) error {
t := &mastodon.Toot{
Status: message,
})
}
for _, v := range attachments {
t.MediaIDs = append(t.MediaIDs, mastodon.ID(v))
}
_, err := mod.client.PostStatus(context.Background(), t)
return err
}
// Reply posts a new reply-status
func (mod *Account) Reply(replyid string, message string) error {
_, err := mod.client.PostStatus(context.Background(), &mastodon.Toot{
func (mod *Account) Reply(replyid string, message string, attachments []string) error {
t := &mastodon.Toot{
Status: message,
InReplyToID: mastodon.ID(replyid),
})
}
for _, v := range attachments {
t.MediaIDs = append(t.MediaIDs, mastodon.ID(v))
}
_, err := mod.client.PostStatus(context.Background(), t)
return err
}
func (mod *Account) UploadAttachment(url string) {
go func() {
a, err := mod.client.UploadMedia(context.Background(), url)
if err != nil {
ev := accounts.ErrorEvent{
Message: err.Error(),
Internal: false,
}
mod.evchan <- ev
return
}
mod.evchan <- accounts.Media{
ID: string(a.ID),
Preview: a.PreviewURL,
}
}()
}
// DeletePost deletes a post
func (mod *Account) DeletePost(id string) error {
err := mod.client.DeleteStatus(context.Background(), mastodon.ID(id))

122
attachmentmodel.go Normal file
View file

@ -0,0 +1,122 @@
package main
import (
"github.com/therecipe/qt/core"
)
// Model Roles
const (
AttachmentID = int(core.Qt__UserRole) + 1<<iota
AttachmentPreview
)
// AttachmentModel holds a collection of attachments
type AttachmentModel struct {
core.QAbstractListModel
_ func() `constructor:"init"`
_ map[int]*core.QByteArray `property:"roles"`
_ []*Attachment `property:"attachments"`
_ func(*Attachment) `slot:"addAttachment"`
_ func(row int) `slot:"removeAttachment"`
_ func() `slot:"clear"`
}
// Attachment represents a single attachment
type Attachment struct {
core.QObject
ID string
Preview string
URL string
}
func (m *AttachmentModel) init() {
m.SetRoles(map[int]*core.QByteArray{
AttachmentID: core.NewQByteArray2("attachmentID", -1),
AttachmentPreview: core.NewQByteArray2("attachmentPreview", -1),
})
m.ConnectData(m.data)
m.ConnectSetData(m.setData)
m.ConnectRowCount(m.rowCount)
m.ConnectColumnCount(m.columnCount)
m.ConnectRoleNames(m.roleNames)
m.ConnectAddAttachment(m.addAttachment)
m.ConnectRemoveAttachment(m.removeAttachment)
m.ConnectClear(m.clear)
}
func (m *AttachmentModel) setData(index *core.QModelIndex, value *core.QVariant, role int) bool {
if !index.IsValid() {
return false
}
m.DataChanged(index, index, []int{Editing})
return true
}
func (m *AttachmentModel) data(index *core.QModelIndex, role int) *core.QVariant {
if !index.IsValid() {
return core.NewQVariant()
}
if index.Row() >= len(m.Attachments()) {
return core.NewQVariant()
}
var p = m.Attachments()[index.Row()]
switch role {
case AttachmentID:
{
return core.NewQVariant14(p.ID)
}
case AttachmentPreview:
{
return core.NewQVariant14(p.Preview)
}
default:
{
return core.NewQVariant()
}
}
}
func (m *AttachmentModel) rowCount(parent *core.QModelIndex) int {
return len(m.Attachments())
}
func (m *AttachmentModel) columnCount(parent *core.QModelIndex) int {
return 1
}
func (m *AttachmentModel) roleNames() map[int]*core.QByteArray {
return m.Roles()
}
func (m *AttachmentModel) clear() {
m.BeginResetModel()
m.SetAttachments([]*Attachment{})
m.EndResetModel()
}
func (m *AttachmentModel) addAttachment(p *Attachment) {
m.BeginInsertRows(core.NewQModelIndex(), len(m.Attachments()), len(m.Attachments()))
m.SetAttachments(append(m.Attachments(), p))
m.EndInsertRows()
}
func (m *AttachmentModel) removeAttachment(row int) {
m.BeginRemoveRows(core.NewQModelIndex(), row, row)
m.SetAttachments(append(m.Attachments()[:row], m.Attachments()[row+1:]...))
m.EndRemoveRows()
}
func init() {
AttachmentModel_QRegisterMetaType()
Attachment_QRegisterMetaType()
}

View file

@ -21,6 +21,7 @@ type UIBridge struct {
_ func(id string) `slot:"likeButton"`
_ func(id string) `slot:"unlikeButton"`
_ func(id string, follow bool) `slot:"followButton"`
_ func(url string) `slot:"uploadAttachment"`
_ func(id string) `slot:"loadConversation"`
_ func(id string) `slot:"loadAccount"`
_ func(token string) `slot:"tag"`
@ -49,6 +50,7 @@ type AccountBridge struct {
_ *core.QAbstractListModel `property:"panes"`
_ *core.QAbstractListModel `property:"notifications"`
_ *core.QAbstractListModel `property:"attachments"`
_ *core.QAbstractListModel `property:"conversation"`
_ *core.QAbstractListModel `property:"accountMessages"`
}
@ -111,6 +113,7 @@ func setupQmlBridges() {
uiBridge.ConnectLikeButton(like)
uiBridge.ConnectUnlikeButton(unlike)
uiBridge.ConnectFollowButton(follow)
uiBridge.ConnectUploadAttachment(uploadAttachment)
uiBridge.ConnectLoadConversation(loadConversation)
uiBridge.ConnectLoadAccount(loadAccount)
uiBridge.ConnectTag(tag)

View file

@ -79,6 +79,14 @@ func handleEvents(eventsIn chan interface{}, messages *MessageModel) {
log.Println("Error:", event.Message)
accountBridge.SetError(event.Message)
}
case accounts.Media:
{
log.Printf("Added attachment: %+v\n", event)
var p = NewAttachment(nil)
p.ID = event.ID
p.Preview = event.Preview
attachmentModel.AddAttachment(p)
}
case accounts.MessageEvent:
{
// spw := &spew.ConfigState{Indent: " ", DisableCapacities: true, DisablePointerAddresses: true}

View file

@ -21,6 +21,40 @@ Popup {
clip: true
contentHeight: layout.height
BusyIndicator {
z: 1
id: busy
running: false
anchors.centerIn: parent
}
DropArea {
id: drop
anchors.fill: parent
enabled: true
onEntered:
console.log("entered")
onExited:
console.log("exited")
onDropped: {
console.log("dropped", drop.urls.length, "urls")
for (var i = 0; i < drop.urls.length; i++) {
console.log(drop.urls[i])
busy.running = true
var media = uiBridge.uploadAttachment(drop.urls[i])
/*if (media != '') {
attachments.append({"id": media, "url": drop.urls[i]})
}*/
}
drop.acceptProposedAction()
}
}
ColumnLayout {
id: layout
width: parent.width
@ -47,6 +81,37 @@ Popup {
wrapMode: TextArea.Wrap
}
Connections {
target: accountBridge.attachments
onRowsInserted: {
busy.running = false
}
onRowsRemoved: {
}
}
Flow {
id: attachmentLayout
Layout.fillWidth: true
Repeater {
model: accountBridge.attachments
Image {
smooth: true
source: model.attachmentPreview
sourceSize.height: 64
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: function() {
accountBridge.attachments.removeAttachment(index)
}
}
}
}
}
RowLayout {
Layout.alignment: Qt.AlignRight

View file

@ -4,6 +4,8 @@ function createMessagePopup(parent, model) {
"message": model
})
accountBridge.attachments.clear()
if (popup == null) {
console.log("Error creating MessagePopup")
}

View file

@ -3,6 +3,7 @@ package main
import (
"fmt"
"log"
"net/url"
"os"
"strings"
@ -22,6 +23,7 @@ var (
notificationModel = NewMessageModel(nil)
conversationModel = NewMessageModel(nil)
accountMessagesModel = NewMessageModel(nil)
attachmentModel = NewAttachmentModel(nil)
paneModel = NewPaneModel(nil)
)
@ -84,13 +86,18 @@ func postLimitCount(body string) int {
// reply is used to post a new message
// if replyid is > 0, it's send as a reply
func reply(replyid string, message string) {
var attachments []string
for _, v := range attachmentModel.Attachments() {
attachments = append(attachments, v.ID)
}
var err error
if replyid != "" {
log.Println("Sending reply to:", replyid, message)
err = tc.Reply(replyid, message)
log.Println("Sending reply to:", replyid, attachments, message)
err = tc.Reply(replyid, message, attachments)
} else {
log.Println("Posting:", message)
err = tc.Post(message)
log.Println("Posting:", attachments, message)
err = tc.Post(message, attachments)
}
if err != nil {
accountBridge.SetError(err.Error())
@ -98,6 +105,12 @@ func reply(replyid string, message string) {
}
}
func uploadAttachment(pathurl string) {
u, _ := url.ParseRequestURI(pathurl)
log.Println("Uploding:", u.Path)
tc.UploadAttachment(u.Path)
}
// deletePost deletes a post
func deletePost(id string) {
log.Println("Deleting:", id)
@ -266,6 +279,7 @@ func setupMastodon(config Account) {
accountBridge.SetUsername("Not connected...")
accountBridge.SetNotifications(notificationModel)
accountBridge.SetAttachments(attachmentModel)
accountBridge.SetConversation(conversationModel)
accountBridge.SetAccountMessages(accountMessagesModel)
accountBridge.SetAvatar("qrc:/qml/images/telephant_logo.png")