mirror of
https://github.com/muesli/telephant
synced 2024-11-22 19:33:06 +00:00
Added support for attaching images to posts
This commit is contained in:
parent
53855adda8
commit
81bc8a4840
8 changed files with 254 additions and 10 deletions
|
@ -36,6 +36,7 @@ type Follow struct {
|
||||||
|
|
||||||
// Media describes a media item.
|
// Media describes a media item.
|
||||||
type Media struct {
|
type Media struct {
|
||||||
|
ID string
|
||||||
Preview string
|
Preview string
|
||||||
URL string
|
URL string
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,22 +172,51 @@ func (mod *Account) Logo() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post posts a new status
|
// Post posts a new status
|
||||||
func (mod *Account) Post(message string) error {
|
func (mod *Account) Post(message string, attachments []string) error {
|
||||||
_, err := mod.client.PostStatus(context.Background(), &mastodon.Toot{
|
t := &mastodon.Toot{
|
||||||
Status: message,
|
Status: message,
|
||||||
})
|
}
|
||||||
|
for _, v := range attachments {
|
||||||
|
t.MediaIDs = append(t.MediaIDs, mastodon.ID(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := mod.client.PostStatus(context.Background(), t)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reply posts a new reply-status
|
// Reply posts a new reply-status
|
||||||
func (mod *Account) Reply(replyid string, message string) error {
|
func (mod *Account) Reply(replyid string, message string, attachments []string) error {
|
||||||
_, err := mod.client.PostStatus(context.Background(), &mastodon.Toot{
|
t := &mastodon.Toot{
|
||||||
Status: message,
|
Status: message,
|
||||||
InReplyToID: mastodon.ID(replyid),
|
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
|
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
|
// DeletePost deletes a post
|
||||||
func (mod *Account) DeletePost(id string) error {
|
func (mod *Account) DeletePost(id string) error {
|
||||||
err := mod.client.DeleteStatus(context.Background(), mastodon.ID(id))
|
err := mod.client.DeleteStatus(context.Background(), mastodon.ID(id))
|
||||||
|
|
122
attachmentmodel.go
Normal file
122
attachmentmodel.go
Normal 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()
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ type UIBridge struct {
|
||||||
_ func(id string) `slot:"likeButton"`
|
_ func(id string) `slot:"likeButton"`
|
||||||
_ func(id string) `slot:"unlikeButton"`
|
_ func(id string) `slot:"unlikeButton"`
|
||||||
_ func(id string, follow bool) `slot:"followButton"`
|
_ func(id string, follow bool) `slot:"followButton"`
|
||||||
|
_ func(url string) `slot:"uploadAttachment"`
|
||||||
_ func(id string) `slot:"loadConversation"`
|
_ func(id string) `slot:"loadConversation"`
|
||||||
_ func(id string) `slot:"loadAccount"`
|
_ func(id string) `slot:"loadAccount"`
|
||||||
_ func(token string) `slot:"tag"`
|
_ func(token string) `slot:"tag"`
|
||||||
|
@ -49,6 +50,7 @@ type AccountBridge struct {
|
||||||
|
|
||||||
_ *core.QAbstractListModel `property:"panes"`
|
_ *core.QAbstractListModel `property:"panes"`
|
||||||
_ *core.QAbstractListModel `property:"notifications"`
|
_ *core.QAbstractListModel `property:"notifications"`
|
||||||
|
_ *core.QAbstractListModel `property:"attachments"`
|
||||||
_ *core.QAbstractListModel `property:"conversation"`
|
_ *core.QAbstractListModel `property:"conversation"`
|
||||||
_ *core.QAbstractListModel `property:"accountMessages"`
|
_ *core.QAbstractListModel `property:"accountMessages"`
|
||||||
}
|
}
|
||||||
|
@ -111,6 +113,7 @@ func setupQmlBridges() {
|
||||||
uiBridge.ConnectLikeButton(like)
|
uiBridge.ConnectLikeButton(like)
|
||||||
uiBridge.ConnectUnlikeButton(unlike)
|
uiBridge.ConnectUnlikeButton(unlike)
|
||||||
uiBridge.ConnectFollowButton(follow)
|
uiBridge.ConnectFollowButton(follow)
|
||||||
|
uiBridge.ConnectUploadAttachment(uploadAttachment)
|
||||||
uiBridge.ConnectLoadConversation(loadConversation)
|
uiBridge.ConnectLoadConversation(loadConversation)
|
||||||
uiBridge.ConnectLoadAccount(loadAccount)
|
uiBridge.ConnectLoadAccount(loadAccount)
|
||||||
uiBridge.ConnectTag(tag)
|
uiBridge.ConnectTag(tag)
|
||||||
|
|
|
@ -79,6 +79,14 @@ func handleEvents(eventsIn chan interface{}, messages *MessageModel) {
|
||||||
log.Println("Error:", event.Message)
|
log.Println("Error:", event.Message)
|
||||||
accountBridge.SetError(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:
|
case accounts.MessageEvent:
|
||||||
{
|
{
|
||||||
// spw := &spew.ConfigState{Indent: " ", DisableCapacities: true, DisablePointerAddresses: true}
|
// spw := &spew.ConfigState{Indent: " ", DisableCapacities: true, DisablePointerAddresses: true}
|
||||||
|
|
|
@ -21,6 +21,40 @@ Popup {
|
||||||
clip: true
|
clip: true
|
||||||
contentHeight: layout.height
|
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 {
|
ColumnLayout {
|
||||||
id: layout
|
id: layout
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -47,6 +81,37 @@ Popup {
|
||||||
wrapMode: TextArea.Wrap
|
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 {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ function createMessagePopup(parent, model) {
|
||||||
"message": model
|
"message": model
|
||||||
})
|
})
|
||||||
|
|
||||||
|
accountBridge.attachments.clear()
|
||||||
|
|
||||||
if (popup == null) {
|
if (popup == null) {
|
||||||
console.log("Error creating MessagePopup")
|
console.log("Error creating MessagePopup")
|
||||||
}
|
}
|
||||||
|
|
22
telephant.go
22
telephant.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ var (
|
||||||
notificationModel = NewMessageModel(nil)
|
notificationModel = NewMessageModel(nil)
|
||||||
conversationModel = NewMessageModel(nil)
|
conversationModel = NewMessageModel(nil)
|
||||||
accountMessagesModel = NewMessageModel(nil)
|
accountMessagesModel = NewMessageModel(nil)
|
||||||
|
attachmentModel = NewAttachmentModel(nil)
|
||||||
paneModel = NewPaneModel(nil)
|
paneModel = NewPaneModel(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -84,13 +86,18 @@ func postLimitCount(body string) int {
|
||||||
// reply is used to post a new message
|
// reply is used to post a new message
|
||||||
// if replyid is > 0, it's send as a reply
|
// if replyid is > 0, it's send as a reply
|
||||||
func reply(replyid string, message string) {
|
func reply(replyid string, message string) {
|
||||||
|
var attachments []string
|
||||||
|
for _, v := range attachmentModel.Attachments() {
|
||||||
|
attachments = append(attachments, v.ID)
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if replyid != "" {
|
if replyid != "" {
|
||||||
log.Println("Sending reply to:", replyid, message)
|
log.Println("Sending reply to:", replyid, attachments, message)
|
||||||
err = tc.Reply(replyid, message)
|
err = tc.Reply(replyid, message, attachments)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Posting:", message)
|
log.Println("Posting:", attachments, message)
|
||||||
err = tc.Post(message)
|
err = tc.Post(message, attachments)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
accountBridge.SetError(err.Error())
|
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
|
// deletePost deletes a post
|
||||||
func deletePost(id string) {
|
func deletePost(id string) {
|
||||||
log.Println("Deleting:", id)
|
log.Println("Deleting:", id)
|
||||||
|
@ -266,6 +279,7 @@ func setupMastodon(config Account) {
|
||||||
|
|
||||||
accountBridge.SetUsername("Not connected...")
|
accountBridge.SetUsername("Not connected...")
|
||||||
accountBridge.SetNotifications(notificationModel)
|
accountBridge.SetNotifications(notificationModel)
|
||||||
|
accountBridge.SetAttachments(attachmentModel)
|
||||||
accountBridge.SetConversation(conversationModel)
|
accountBridge.SetConversation(conversationModel)
|
||||||
accountBridge.SetAccountMessages(accountMessagesModel)
|
accountBridge.SetAccountMessages(accountMessagesModel)
|
||||||
accountBridge.SetAvatar("qrc:/qml/images/telephant_logo.png")
|
accountBridge.SetAvatar("qrc:/qml/images/telephant_logo.png")
|
||||||
|
|
Loading…
Reference in a new issue