finishing first attempt at message model pr. mainly group acks
This commit is contained in:
parent
7bb2198879
commit
e09ad91ab5
|
@ -10,6 +10,7 @@ import (
|
||||||
"cwtch.im/ui/go/constants"
|
"cwtch.im/ui/go/constants"
|
||||||
"cwtch.im/ui/go/the"
|
"cwtch.im/ui/go/the"
|
||||||
"cwtch.im/ui/go/ui"
|
"cwtch.im/ui/go/ui"
|
||||||
|
"encoding/hex"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -54,19 +55,15 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
|
||||||
|
|
||||||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
|
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
|
||||||
//uiManager.AddMessage(e.Data[event.RemotePeer], e.Data[event.RemotePeer], e.Data[event.Data], false, e.EventID, ts, true)
|
uiManager.StoreAndNotify(peer, e.Data[event.RemotePeer], e.Data[event.Data], ts, onion)
|
||||||
uiManager.AboutToAddMessage()
|
|
||||||
//time.Sleep(time.Millisecond)
|
|
||||||
peer.StoreMessage(e.Data[event.RemotePeer], e.Data[event.Data], ts)
|
|
||||||
uiManager.MessageJustAdded()
|
|
||||||
|
|
||||||
|
|
||||||
case event.PeerAcknowledgement:
|
case event.PeerAcknowledgement:
|
||||||
uiManager.Acknowledge(e.Data[event.EventID])
|
uiManager.Acknowledge(e.Data[event.RemotePeer], e.Data[event.EventID])
|
||||||
|
|
||||||
case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer
|
case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer
|
||||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampSent])
|
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampSent])
|
||||||
uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == peer.GetOnion(), e.Data[event.Signature], ts, true)
|
uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == peer.GetOnion(), hex.EncodeToString([]byte(e.Data[event.Signature])), ts, true)
|
||||||
|
|
||||||
case event.NewGroupInvite:
|
case event.NewGroupInvite:
|
||||||
gid, err := peer.ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
|
gid, err := peer.ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
|
||||||
group := peer.GetGroup(gid)
|
group := peer.GetGroup(gid)
|
||||||
|
|
109
go/ui/gcd.go
109
go/ui/gcd.go
|
@ -2,6 +2,7 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"cwtch.im/cwtch/app"
|
"cwtch.im/cwtch/app"
|
||||||
|
@ -12,13 +13,11 @@ import (
|
||||||
"cwtch.im/ui/go/constants"
|
"cwtch.im/ui/go/constants"
|
||||||
"github.com/therecipe/qt/qml"
|
"github.com/therecipe/qt/qml"
|
||||||
|
|
||||||
"encoding/base32"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"cwtch.im/ui/go/the"
|
"cwtch.im/ui/go/the"
|
||||||
|
"encoding/base32"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"github.com/therecipe/qt/core"
|
"github.com/therecipe/qt/core"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GrandCentralDispatcher struct {
|
type GrandCentralDispatcher struct {
|
||||||
|
@ -66,8 +65,6 @@ type GrandCentralDispatcher struct {
|
||||||
_ func(handle, key, value string) `signal:"UpdateContactAttribute"`
|
_ func(handle, key, value string) `signal:"UpdateContactAttribute"`
|
||||||
|
|
||||||
// messages pane stuff
|
// messages pane stuff
|
||||||
_ func(handle, from, displayName, message, image string, mID string, fromMe bool, ts int64, ackd bool, error bool) `signal:"AppendMessage"`
|
|
||||||
_ func(handle, from, displayName, message, image string, mID string, fromMe bool, ts int64, ackd bool, error bool) `signal:"PrependMessage"`
|
|
||||||
_ func() `signal:"ClearMessages"`
|
_ func() `signal:"ClearMessages"`
|
||||||
_ func() `signal:"ResetMessagePane"`
|
_ func() `signal:"ResetMessagePane"`
|
||||||
_ func(mID string) `signal:"Acknowledged"`
|
_ func(mID string) `signal:"Acknowledged"`
|
||||||
|
@ -120,6 +117,8 @@ type GrandCentralDispatcher struct {
|
||||||
_ func() `signal:"blockUnknownPeers,auto"`
|
_ func() `signal:"blockUnknownPeers,auto"`
|
||||||
_ func(onion string) `signal:"storeHistoryForPeer,auto"`
|
_ func(onion string) `signal:"storeHistoryForPeer,auto"`
|
||||||
_ func(onion string) `signal:"deleteHistoryForPeer,auto"`
|
_ func(onion string) `signal:"deleteHistoryForPeer,auto"`
|
||||||
|
// chat
|
||||||
|
_ func(mID string) `slot:"acktest,auto"`
|
||||||
|
|
||||||
_ func(handle string) `signal:"requestServerSettings,auto"`
|
_ func(handle string) `signal:"requestServerSettings,auto"`
|
||||||
|
|
||||||
|
@ -175,6 +174,18 @@ func (this *GrandCentralDispatcher) DoIfProfile(profile string, fn func()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Like DoIfProfile() but runs elseFn() if profile isn't the currently selected one in the UI
|
||||||
|
func (this *GrandCentralDispatcher) DoIfProfileElse(profile string, fn func(), elseFn func()) {
|
||||||
|
this.profileLock.Lock()
|
||||||
|
defer this.profileLock.Unlock()
|
||||||
|
|
||||||
|
if this.m_selectedProfile == profile {
|
||||||
|
fn()
|
||||||
|
} else {
|
||||||
|
elseFn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) selectedConversation() string {
|
func (this *GrandCentralDispatcher) selectedConversation() string {
|
||||||
this.conversationLock.Lock()
|
this.conversationLock.Lock()
|
||||||
defer this.conversationLock.Unlock()
|
defer this.conversationLock.Unlock()
|
||||||
|
@ -204,6 +215,18 @@ func (this *GrandCentralDispatcher) DoIfConversation(conversation string, fn fun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// like DoIfConversation() but
|
||||||
|
func (this *GrandCentralDispatcher) DoIfConversationElse(conversation string, fn func(), elseFn func()) {
|
||||||
|
this.conversationLock.Lock()
|
||||||
|
defer this.conversationLock.Unlock()
|
||||||
|
|
||||||
|
if this.m_selectedConversation == conversation {
|
||||||
|
fn()
|
||||||
|
} else {
|
||||||
|
elseFn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) sendMessage(message string) {
|
func (this *GrandCentralDispatcher) sendMessage(message string) {
|
||||||
if len(message) > 65530 {
|
if len(message) > 65530 {
|
||||||
this.InvokePopup("message is too long")
|
this.InvokePopup("message is too long")
|
||||||
|
@ -224,22 +247,18 @@ func (this *GrandCentralDispatcher) sendMessage(message string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mID, err := the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message)
|
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
||||||
|
_, err := the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message)
|
||||||
this.GetUiManager(this.selectedProfile()).AddMessage(this.SelectedConversation(), "me", message, true, mID, time.Now(), false)
|
this.TimelineInterface.RequestEIR()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.InvokePopup("failed to send message " + err.Error())
|
this.InvokePopup("failed to send message " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
to := this.SelectedConversation()
|
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
||||||
prenum := this.TimelineInterface.num()
|
the.Peer.SendMessageToPeer(this.SelectedConversation(), message)
|
||||||
this.TimelineInterface.AddMessage(prenum)
|
|
||||||
/*mID := */the.Peer.SendMessageToPeer(to, message)
|
|
||||||
this.TimelineInterface.RequestEIR()
|
this.TimelineInterface.RequestEIR()
|
||||||
|
|
||||||
//this.GetUiManager(this.selectedProfile()).AddMessage(to, "me", message, true, mID, time.Now(), false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -266,7 +285,6 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
||||||
this.UpdateContactStatus(group.GroupID, int(state), loading)
|
this.UpdateContactStatus(group.GroupID, int(state), loading)
|
||||||
this.requestGroupSettings(handle)
|
this.requestGroupSettings(handle)
|
||||||
|
|
||||||
tl := group.GetTimeline()
|
|
||||||
nick := GetNick(handle)
|
nick := GetNick(handle)
|
||||||
updateLastReadTime(group.GroupID)
|
updateLastReadTime(group.GroupID)
|
||||||
if nick == "" {
|
if nick == "" {
|
||||||
|
@ -275,34 +293,6 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
||||||
this.SetToolbarTitle(nick)
|
this.SetToolbarTitle(nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
|
||||||
// Janky hack to let the ui/qml respond to the status updates first before freezing under a deluge of new messages
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
for i := len(tl) - 1; i >= 0; i-- {
|
|
||||||
if tl[i].PeerID == the.Peer.GetOnion() {
|
|
||||||
handle = "me"
|
|
||||||
} else {
|
|
||||||
handle = tl[i].PeerID
|
|
||||||
}
|
|
||||||
|
|
||||||
name := GetNick(tl[i].PeerID)
|
|
||||||
image := GetProfilePic(tl[i].PeerID)
|
|
||||||
|
|
||||||
this.PrependMessage(
|
|
||||||
handle,
|
|
||||||
tl[i].PeerID,
|
|
||||||
name,
|
|
||||||
tl[i].Message,
|
|
||||||
image,
|
|
||||||
string(tl[i].Signature),
|
|
||||||
tl[i].PeerID == the.Peer.GetOnion(),
|
|
||||||
tl[i].Timestamp.Unix(),
|
|
||||||
tl[i].Received.Equal(time.Unix(0, 0)) == false, // If the received timestamp is epoch, we have not yet received this message through an active server
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
} // ELSE LOAD CONTACT
|
} // ELSE LOAD CONTACT
|
||||||
|
|
||||||
|
@ -317,32 +307,6 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
||||||
}
|
}
|
||||||
updateLastReadTime(contact.Onion)
|
updateLastReadTime(contact.Onion)
|
||||||
this.SetToolbarTitle(nick)
|
this.SetToolbarTitle(nick)
|
||||||
|
|
||||||
peer := the.Peer.GetContact(handle)
|
|
||||||
messages := peer.Timeline.GetMessages()
|
|
||||||
for i := range messages {
|
|
||||||
from := messages[i].PeerID
|
|
||||||
fromMe := messages[i].PeerID == the.Peer.GetOnion()
|
|
||||||
if fromMe {
|
|
||||||
from = "me"
|
|
||||||
}
|
|
||||||
|
|
||||||
displayname := GetNick(messages[i].PeerID)
|
|
||||||
image := GetProfilePic(messages[i].PeerID)
|
|
||||||
|
|
||||||
this.AppendMessage(
|
|
||||||
from,
|
|
||||||
messages[i].PeerID,
|
|
||||||
displayname,
|
|
||||||
messages[i].Message,
|
|
||||||
image,
|
|
||||||
string(messages[i].Signature),
|
|
||||||
fromMe,
|
|
||||||
messages[i].Timestamp.Unix(),
|
|
||||||
messages[i].Acknowledged,
|
|
||||||
messages[i].Error != "",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) requestSettings() {
|
func (this *GrandCentralDispatcher) requestSettings() {
|
||||||
|
@ -760,3 +724,8 @@ func (this *GrandCentralDispatcher) deleteProfile(onion string) {
|
||||||
log.Infof("deleteProfile %v\n", onion)
|
log.Infof("deleteProfile %v\n", onion)
|
||||||
the.CwtchApp.DeletePeer(onion)
|
the.CwtchApp.DeletePeer(onion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *GrandCentralDispatcher) acktest(mID string) {
|
||||||
|
idx, _ := strconv.Atoi(mID)
|
||||||
|
this.TimelineInterface.EditMessage(idx)
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"cwtch.im/cwtch/app"
|
"cwtch.im/cwtch/app"
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"cwtch.im/cwtch/model/attr"
|
"cwtch.im/cwtch/model/attr"
|
||||||
|
"cwtch.im/cwtch/peer"
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
"cwtch.im/ui/go/constants"
|
"cwtch.im/ui/go/constants"
|
||||||
"cwtch.im/ui/go/the"
|
"cwtch.im/ui/go/the"
|
||||||
|
@ -202,7 +203,7 @@ type manager struct {
|
||||||
// manager also performs call filtering based on UI state: users of manager can safely always call it on events and not have to worry about weather the relevant ui is active
|
// manager also performs call filtering based on UI state: users of manager can safely always call it on events and not have to worry about weather the relevant ui is active
|
||||||
// ie: you can always safely call AddMessage even if in the ui a different profile is selected. manager will check with gcd, and if the correct conditions are not met, it will not call on gcd to update the ui incorrectly
|
// ie: you can always safely call AddMessage even if in the ui a different profile is selected. manager will check with gcd, and if the correct conditions are not met, it will not call on gcd to update the ui incorrectly
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
Acknowledge(mID string)
|
Acknowledge(handle, mID string)
|
||||||
AddContact(Handle string)
|
AddContact(Handle string)
|
||||||
AddSendMessageError(peer string, signature string, err string)
|
AddSendMessageError(peer string, signature string, err string)
|
||||||
AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool)
|
AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool)
|
||||||
|
@ -218,6 +219,7 @@ type Manager interface {
|
||||||
|
|
||||||
AboutToAddMessage()
|
AboutToAddMessage()
|
||||||
MessageJustAdded()
|
MessageJustAdded()
|
||||||
|
StoreAndNotify(peer.CwtchPeer, string, string, time.Time, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a new Manager interface for a profile to the gcd
|
// NewManager returns a new Manager interface for a profile to the gcd
|
||||||
|
@ -226,9 +228,11 @@ func NewManager(profile string, gcd *GrandCentralDispatcher) Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acknowledge acknowledges the given message id in the UI
|
// Acknowledge acknowledges the given message id in the UI
|
||||||
func (this *manager) Acknowledge(mID string) {
|
func (this *manager) Acknowledge(handle, mID string) {
|
||||||
this.gcd.DoIfProfile(this.profile, func() {
|
this.gcd.DoIfProfile(this.profile, func() {
|
||||||
this.gcd.Acknowledged(mID)
|
this.gcd.DoIfConversation(handle, func(){
|
||||||
|
this.gcd.Acktest(mID)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,29 +295,35 @@ func (this *manager) MessageJustAdded() {
|
||||||
this.gcd.TimelineInterface.RequestEIR()
|
this.gcd.TimelineInterface.RequestEIR()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *manager) StoreAndNotify(pere peer.CwtchPeer, onion string, messageTxt string, sent time.Time, profileOnion string) {
|
||||||
|
this.gcd.DoIfProfileElse(this.profile, func() {
|
||||||
|
this.gcd.DoIfConversationElse(onion, func() {
|
||||||
|
updateLastReadTime(onion)
|
||||||
|
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
|
||||||
|
pere.StoreMessage(onion, messageTxt, sent)
|
||||||
|
this.gcd.TimelineInterface.RequestEIR()
|
||||||
|
}, func() {
|
||||||
|
updateLastReadTime(onion)
|
||||||
|
pere.StoreMessage(onion, messageTxt, sent)
|
||||||
|
})
|
||||||
|
this.gcd.IncContactUnreadCount(onion)
|
||||||
|
}, func() {
|
||||||
|
the.CwtchApp.GetPeer(profileOnion).StoreMessage(onion, messageTxt, sent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// AddMessage adds a message to the message pane for the supplied conversation if it is active
|
// AddMessage adds a message to the message pane for the supplied conversation if it is active
|
||||||
func (this *manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) {
|
func (this *manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) {
|
||||||
log.Errorf("uiManager.AddMessage(...) uhhhh???? NO ZOOP ZEEP")
|
|
||||||
this.gcd.DoIfProfile(this.profile, func() {
|
this.gcd.DoIfProfile(this.profile, func() {
|
||||||
|
|
||||||
//nick := GetNick(handle)
|
|
||||||
//image := GetProfilePic(handle)
|
|
||||||
|
|
||||||
// If we have this group loaded already
|
|
||||||
this.gcd.DoIfConversation(handle, func() {
|
this.gcd.DoIfConversation(handle, func() {
|
||||||
updateLastReadTime(handle)
|
updateLastReadTime(handle)
|
||||||
// If the message is not from the user then add it, otherwise, just acknowledge.
|
// If the message is not from the user then add it, otherwise, just acknowledge.
|
||||||
if !fromMe {
|
if !fromMe || !Acknowledged {
|
||||||
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
|
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num()-1)
|
||||||
//this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Unix(), false, false)
|
this.gcd.TimelineInterface.RequestEIR()
|
||||||
} else {
|
|
||||||
if !Acknowledged {
|
|
||||||
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
|
|
||||||
//this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Unix(), false, false)
|
|
||||||
} else {
|
} else {
|
||||||
this.gcd.Acknowledged(messageID)
|
this.gcd.Acknowledged(messageID)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
this.gcd.IncContactUnreadCount(handle)
|
this.gcd.IncContactUnreadCount(handle)
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ui
|
||||||
import (
|
import (
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"cwtch.im/ui/go/the"
|
"cwtch.im/ui/go/the"
|
||||||
|
"encoding/hex"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"github.com/therecipe/qt/core"
|
"github.com/therecipe/qt/core"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -12,16 +13,16 @@ import (
|
||||||
type MessageModel struct {
|
type MessageModel struct {
|
||||||
core.QAbstractTableModel
|
core.QAbstractTableModel
|
||||||
|
|
||||||
|
ackIdx int
|
||||||
handle string
|
handle string
|
||||||
//_ string `property:"handle,auto"`
|
|
||||||
_ func(string) `signal:"setHandle,auto"`
|
_ func(string) `signal:"setHandle,auto"`
|
||||||
|
|
||||||
_ map[int]*core.QByteArray `property:"roles"`
|
_ map[int]*core.QByteArray `property:"roles"`
|
||||||
_ func() `constructor:"init"`
|
_ func() `constructor:"init"`
|
||||||
|
|
||||||
_ func(int) `signal:"addMessage,auto"`
|
_ func(int) `signal:"addMessage,auto"`
|
||||||
_ func(string) `signal:"createLocalFormEntry,auto"`
|
_ func(int) `signal:"editMessage,auto"`
|
||||||
_ func() `signal:"requestEIR,auto"` // request this.EndInsertRecord() on gui thread
|
_ func() `signal:"requestEIR,auto"`
|
||||||
|
|
||||||
_ func(string) string `slot:"getNick,auto"`
|
_ func(string) string `slot:"getNick,auto"`
|
||||||
_ func(string) string `slot:"getImage,auto"`
|
_ func(string) string `slot:"getImage,auto"`
|
||||||
|
@ -36,6 +37,8 @@ type MessageWrapper struct {
|
||||||
Acknowledged bool
|
Acknowledged bool
|
||||||
RawMessage string
|
RawMessage string
|
||||||
Error string
|
Error string
|
||||||
|
Day string
|
||||||
|
Signature string
|
||||||
_ bool `property:"ackd"`
|
_ bool `property:"ackd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,10 +52,12 @@ func (this *MessageModel) setHandle(handle string) {
|
||||||
|
|
||||||
|
|
||||||
func (this *MessageModel) init() {
|
func (this *MessageModel) init() {
|
||||||
//mdt := reflect.TypeOf([]model.Message{}).Elem()
|
|
||||||
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
||||||
roles := make(map[int]*core.QByteArray)
|
roles := make(map[int]*core.QByteArray)
|
||||||
for i := 0; i < mdt.NumField(); i++ {
|
for i := 0; i < mdt.NumField(); i++ {
|
||||||
|
if mdt.Field(i).Name == "Acknowledged" {
|
||||||
|
this.ackIdx = int(core.Qt__UserRole) + 1 + i
|
||||||
|
}
|
||||||
roles[int(core.Qt__UserRole) + 1 + i] = core.NewQByteArray2(mdt.Field(i).Name, -1)
|
roles[int(core.Qt__UserRole) + 1 + i] = core.NewQByteArray2(mdt.Field(i).Name, -1)
|
||||||
}
|
}
|
||||||
roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1)
|
roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1)
|
||||||
|
@ -83,7 +88,7 @@ func (this *MessageModel) getImage(handle string) string {
|
||||||
|
|
||||||
func (this *MessageModel) num() int {
|
func (this *MessageModel) num() int {
|
||||||
if this.Handle() == "" || the.Peer == nil {
|
if this.Handle() == "" || the.Peer == nil {
|
||||||
log.Debugf("num: early returning 0")
|
log.Debugf("MessageModel.num: early returning 0")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,19 +100,17 @@ func (this *MessageModel) num() int {
|
||||||
} else {
|
} else {
|
||||||
contact := the.Peer.GetContact(this.Handle())
|
contact := the.Peer.GetContact(this.Handle())
|
||||||
if contact != nil {
|
if contact != nil {
|
||||||
log.Debugf("num: returning %v", len(contact.Timeline.Messages))
|
|
||||||
return len(contact.Timeline.Messages)
|
return len(contact.Timeline.Messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warnf("group/contact was nil, returning 0")
|
log.Warnf("MessageModel.num: group/contact was nil, returning 0")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||||
log.Infof("MessageModel.getMessage(%v)", idx)
|
|
||||||
|
|
||||||
var modelmsg model.Message
|
var modelmsg model.Message
|
||||||
|
var ackd bool
|
||||||
|
|
||||||
if this.isGroup() {
|
if this.isGroup() {
|
||||||
group := the.Peer.GetGroup(this.Handle())
|
group := the.Peer.GetGroup(this.Handle())
|
||||||
|
@ -115,6 +118,7 @@ func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||||
modelmsg = group.UnacknowledgedMessages[idx - len(group.Timeline.Messages)]
|
modelmsg = group.UnacknowledgedMessages[idx - len(group.Timeline.Messages)]
|
||||||
} else {
|
} else {
|
||||||
modelmsg = group.Timeline.Messages[idx]
|
modelmsg = group.Timeline.Messages[idx]
|
||||||
|
ackd = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
contact := the.Peer.GetContact(this.Handle())
|
contact := the.Peer.GetContact(this.Handle())
|
||||||
|
@ -122,9 +126,9 @@ func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||||
modelmsg = model.Message{Message:"oops test hi uhhhhh :/"}
|
modelmsg = model.Message{Message:"oops test hi uhhhhh :/"}
|
||||||
} else if idx >= len(contact.Timeline.Messages) {
|
} else if idx >= len(contact.Timeline.Messages) {
|
||||||
log.Errorf("this shouldnt happen")
|
log.Errorf("this shouldnt happen")
|
||||||
//modelmsg = contact.UnacknowledgedMessages[idx-len(contact.Timeline.Messages)]
|
|
||||||
} else {
|
} else {
|
||||||
modelmsg = contact.Timeline.Messages[idx]
|
modelmsg = contact.Timeline.Messages[idx]
|
||||||
|
ackd = modelmsg.Acknowledged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,13 +138,13 @@ func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||||
RawMessage: modelmsg.Message,
|
RawMessage: modelmsg.Message,
|
||||||
PeerID: modelmsg.PeerID,
|
PeerID: modelmsg.PeerID,
|
||||||
Error: modelmsg.Error,
|
Error: modelmsg.Error,
|
||||||
Acknowledged: modelmsg.Acknowledged,
|
Acknowledged: ackd,
|
||||||
|
Day: modelmsg.Timestamp.Format("January 2, 2006"),
|
||||||
|
Signature: hex.EncodeToString(modelmsg.Signature),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MessageModel) data(index *core.QModelIndex, role int) *core.QVariant {
|
func (this *MessageModel) data(index *core.QModelIndex, role int) *core.QVariant {
|
||||||
log.Infof("MessageModel.data(%v, %v)", index.Row(), role)
|
|
||||||
|
|
||||||
if !index.IsValid() {
|
if !index.IsValid() {
|
||||||
return core.NewQVariant()
|
return core.NewQVariant()
|
||||||
}
|
}
|
||||||
|
@ -173,7 +177,7 @@ func (this *MessageModel) headerData(section int, orientation core.Qt__Orientati
|
||||||
return this.HeaderDataDefault(section, orientation, role)
|
return this.HeaderDataDefault(section, orientation, role)
|
||||||
}
|
}
|
||||||
|
|
||||||
mdt := reflect.TypeOf([]model.Message{}).Elem()
|
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
||||||
return core.NewQVariant12(mdt.Field(section).Name)
|
return core.NewQVariant12(mdt.Field(section).Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,30 +189,29 @@ func (this *MessageModel) columnCount(parent *core.QModelIndex) int {
|
||||||
return reflect.TypeOf(MessageWrapper{}).NumField()
|
return reflect.TypeOf(MessageWrapper{}).NumField()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perform this.BeginInsertRows() on the gui thread
|
||||||
|
// important:
|
||||||
|
// 1. idx MUST be set to this.num()'s value *before* calling addMessage()
|
||||||
|
// 2. insert the message yourself
|
||||||
|
// 3. this.RequestEIR() *must* be called afterward
|
||||||
func (this *MessageModel) addMessage(idx int) {
|
func (this *MessageModel) addMessage(idx int) {
|
||||||
log.Debugf("MessageModel.addMessage() ZOOP ZOOP %v", this.handle)
|
this.BeginInsertRows(core.NewQModelIndex(), idx, idx)
|
||||||
this.BeginInsertRows(core.NewQModelIndex(), idx, idx)//this.num(), this.num())
|
|
||||||
//this.modelData = append(this.modelData, *fe)
|
|
||||||
//this.RequestEIR()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform this.EndInsertRows() on the gui thread
|
// perform this.EndInsertRows() on the gui thread after an AddMessage()
|
||||||
func (this *MessageModel) requestEIR() {
|
func (this *MessageModel) requestEIR() {
|
||||||
log.Debugf("MessageModel.requestEIR() ZEEP ZEEP %v", this.handle)
|
|
||||||
this.EndInsertRows()
|
this.EndInsertRows()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify the gui that the message acknowledgement at index idx has been modified
|
||||||
func (this *MessageModel) createLocalFormEntry(name string) {
|
func (this *MessageModel) editMessage(idx int) {
|
||||||
go this.createLocalFormEntry_thread(name)
|
if idx < 0 || idx >= this.num() {
|
||||||
}
|
log.Errorf("cant edit message %v. probably fine", idx)
|
||||||
|
return
|
||||||
func (this *MessageModel) createLocalFormEntry_thread(name string) {
|
|
||||||
log.Debugf("nyi #9779729343959699492726648294050382")
|
|
||||||
/*
|
|
||||||
fe := &model.Message{
|
|
||||||
Message: "hi!",
|
|
||||||
}
|
}
|
||||||
this.addMessage(fe)
|
|
||||||
*/
|
log.Debugf("editMessage(%v, %v)", idx, this.ackIdx)
|
||||||
|
indexObject := this.Index(idx, 0, core.NewQModelIndex())
|
||||||
|
// replace third param with []int{} to update all attributes instead
|
||||||
|
this.DataChanged(indexObject, indexObject, []int{this.ackIdx})
|
||||||
}
|
}
|
|
@ -16,11 +16,30 @@ W.Overlay {
|
||||||
|
|
||||||
//horizontalPadding: 15 * gcd.themeScale
|
//horizontalPadding: 15 * gcd.themeScale
|
||||||
|
|
||||||
ListModel { // MESSAGE OBJECTS ARE STORED HERE ...
|
Connections {
|
||||||
id: messagesModel
|
target: mm
|
||||||
|
onRowsInserted: {
|
||||||
|
if (messagesListView.atYEnd) thymer.running = true
|
||||||
|
|
||||||
|
//todo: this won't fire for non-active convos
|
||||||
|
windowItem.alert(0)
|
||||||
|
if (gcd.os == "android" && windowItem.activeFocusItem == null) {
|
||||||
|
androidCwtchActivity.notification = "New Content"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onRowsInserted is firing after the model is updated but before the delegate is inflated
|
||||||
|
// causing positionViewAtEnd() to scroll to "just above the last message"
|
||||||
|
// so we use this timer to delay scrolling by a few milliseconds
|
||||||
|
Timer {
|
||||||
|
id: thymer
|
||||||
|
interval: 30
|
||||||
|
onTriggered: {
|
||||||
|
thymer.running = false
|
||||||
|
messagesListView.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: messagesListView
|
id: messagesListView
|
||||||
|
@ -28,22 +47,26 @@ W.Overlay {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
model: mm//messagesModel//
|
model: mm
|
||||||
spacing: 6
|
spacing: 6
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
ScrollBar.vertical: Opaque.ScrollBar {}
|
ScrollBar.vertical: Opaque.ScrollBar {}
|
||||||
maximumFlickVelocity: 1250
|
maximumFlickVelocity: 1250
|
||||||
|
|
||||||
|
section.delegate: sectionHeading
|
||||||
|
section.property: "Day"
|
||||||
|
|
||||||
|
|
||||||
delegate: W.Message {
|
delegate: W.Message {
|
||||||
handle: PeerID
|
handle: PeerID
|
||||||
from: PeerID
|
from: PeerID
|
||||||
displayName: mm.getNick(PeerID)
|
displayName: mm.getNick(PeerID)
|
||||||
message: JSON.parse(RawMessage).d
|
message: JSON.parse(RawMessage).d+"//"+Signature
|
||||||
rawMessage: RawMessage
|
rawMessage: RawMessage
|
||||||
image: mm.getImage(PeerID)
|
image: mm.getImage(PeerID)
|
||||||
messageID: "-1"//_mid
|
messageID: Signature
|
||||||
fromMe: PeerID == gcd.SelectedProfile
|
fromMe: PeerID == gcd.selectedProfile
|
||||||
timestamp: parseInt(Timestamp)
|
timestamp: parseInt(Timestamp)
|
||||||
ackd: Acknowledged
|
ackd: Acknowledged
|
||||||
error: Error
|
error: Error
|
||||||
|
@ -53,119 +76,48 @@ W.Overlay {
|
||||||
width: messagesListView.width
|
width: messagesListView.width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: sectionHeading
|
||||||
|
Rectangle {// ⟵ outer rect because anchors._Margin isnt supported here
|
||||||
|
// with qt 5.15+ this↓ can be changed to...
|
||||||
|
// required property string section
|
||||||
|
property string txt: section
|
||||||
|
color: Theme.backgroundMainColor
|
||||||
|
width: childrenRect.width
|
||||||
|
height: childrenRect.height + 12
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
opacity: 1
|
||||||
|
width: childrenRect.width + 66
|
||||||
|
height: childrenRect.height + 6
|
||||||
|
color: Theme.messageFromOtherBackgroundColor
|
||||||
|
radius: 15
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
// ... and this can be changed to
|
||||||
|
// text: parent.parent.section
|
||||||
|
text: parent.parent.txt
|
||||||
|
font.pixelSize: Theme.chatSize * gcd.themeScale
|
||||||
|
color: Theme.messageFromOtherTextColor
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: gcd
|
target: gcd
|
||||||
|
|
||||||
onClearMessages: function() {
|
onClearMessages: function() {
|
||||||
messagesModel.clear()
|
|
||||||
messagesListView.model = null
|
messagesListView.model = null
|
||||||
messagesListView.model = mm
|
messagesListView.model = mm
|
||||||
}
|
|
||||||
|
|
||||||
onAppendMessage: function(handle, from, displayName, message, image, mid, fromMe, ts, ackd, error) {
|
|
||||||
return
|
|
||||||
var msg
|
|
||||||
try {
|
|
||||||
msg = JSON.parse(message)
|
|
||||||
} catch (e) {
|
|
||||||
msg = {"o": 1, "d": "(legacy message type) " + message}
|
|
||||||
}
|
|
||||||
if (msg.o != 1) return
|
|
||||||
|
|
||||||
var date = new Date(ts * 1000);
|
|
||||||
if (messagesModel.count != 0) {
|
|
||||||
var prevDate = new Date(messagesModel.get(messagesModel.count-1)["_ts"] * 1000);
|
|
||||||
if (prevDate.getFullYear() != date.getFullYear()
|
|
||||||
|| prevDate.getMonth() != date.getMonth()
|
|
||||||
|| prevDate.getUTCDate() != date.getUTCDate()) {
|
|
||||||
// new Day detected, Add Date message divider
|
|
||||||
messagesModel.append({
|
|
||||||
"_handle": "calendar",
|
|
||||||
"_from": "calendar",
|
|
||||||
"_displayName": "calendar",
|
|
||||||
"_message": Qt.formatDateTime(date, "MMMM dd, yyyy"),
|
|
||||||
"_rawMessage": "",
|
|
||||||
"_image": "",
|
|
||||||
"_mid": "",
|
|
||||||
"_fromMe": false,
|
|
||||||
"_ts": ts,
|
|
||||||
"_ackd": true,
|
|
||||||
"_error": "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
messagesModel.append({
|
|
||||||
"_handle": handle,
|
|
||||||
"_from": from,
|
|
||||||
"_displayName": displayName,
|
|
||||||
"_message": msg.d,
|
|
||||||
"_rawMessage":msg.d,
|
|
||||||
"_image": image,
|
|
||||||
"_mid": mid,
|
|
||||||
"_fromMe": fromMe,
|
|
||||||
"_ts": ts,
|
|
||||||
"_ackd": ackd,
|
|
||||||
"_error": error == true ? "this message failed to send" : "",
|
|
||||||
})
|
|
||||||
|
|
||||||
messagesListView.positionViewAtEnd()
|
|
||||||
|
|
||||||
// If the window is out of focus, alert the user (makes taskbar light up)
|
|
||||||
windowItem.alert(0)
|
|
||||||
if (gcd.os == "android" && windowItem.activeFocusItem == null) {
|
|
||||||
androidCwtchActivity.notification = "New Content"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPrependMessage: function(handle, from, displayName, message, image, mid, fromMe, ts, ackd, error) {
|
|
||||||
return
|
|
||||||
var msg
|
|
||||||
try {
|
|
||||||
msg = JSON.parse(message)
|
|
||||||
} catch (e) {
|
|
||||||
msg = {"o": 1, "d": "(legacy message type) " + message}
|
|
||||||
}
|
|
||||||
if (msg.o != 1) return
|
|
||||||
|
|
||||||
var date = new Date(ts * 1000);
|
|
||||||
if (messagesModel.count != 0) {
|
|
||||||
var prevDate = new Date(messagesModel.get(0)["_ts"] * 1000);
|
|
||||||
|
|
||||||
if (prevDate.getFullYear() != date.getFullYear()
|
|
||||||
|| prevDate.getMonth() != date.getMonth()
|
|
||||||
|| prevDate.getUTCDate() != date.getUTCDate()) {
|
|
||||||
messagesModel.insert(0, {
|
|
||||||
"_handle": "calendar",
|
|
||||||
"_from": "calendar",
|
|
||||||
"_displayName": "calendar",
|
|
||||||
"_message": Qt.formatDateTime(prevDate, "MMMM dd, yyyy"),
|
|
||||||
"_rawMessage": "",
|
|
||||||
"_image": "",
|
|
||||||
"_mid": "",
|
|
||||||
"_fromMe": false,
|
|
||||||
"_ts": ts,
|
|
||||||
"_ackd": true,
|
|
||||||
"_error": "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
messagesModel.insert(0, {
|
|
||||||
"_handle": handle,
|
|
||||||
"_from": from,
|
|
||||||
"_displayName": displayName,
|
|
||||||
"_message": msg.d,
|
|
||||||
"_rawMessage":msg.d,
|
|
||||||
"_image": image,
|
|
||||||
"_mid": mid,
|
|
||||||
"_fromMe": fromMe,
|
|
||||||
"_ts": ts,
|
|
||||||
"_ackd": ackd,
|
|
||||||
"_error": error == true ? "this message failed to send" : "",
|
|
||||||
})
|
|
||||||
|
|
||||||
messagesListView.positionViewAtEnd()
|
messagesListView.positionViewAtEnd()
|
||||||
|
thymer.running = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue