new message model #347
|
@ -10,6 +10,7 @@ import (
|
|||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/the"
|
||||
"cwtch.im/ui/go/ui"
|
||||
"encoding/hex"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -54,19 +55,15 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
|
|||
|
||||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||
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.AboutToAddMessage()
|
||||
//time.Sleep(time.Millisecond)
|
||||
peer.StoreMessage(e.Data[event.RemotePeer], e.Data[event.Data], ts)
|
||||
uiManager.MessageJustAdded()
|
||||
|
||||
uiManager.StoreAndNotify(peer, e.Data[event.RemotePeer], e.Data[event.Data], ts, onion)
|
||||
|
||||
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
|
||||
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:
|
||||
gid, err := peer.ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
|
||||
group := peer.GetGroup(gid)
|
||||
|
|
109
go/ui/gcd.go
|
@ -2,6 +2,7 @@ package ui
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"cwtch.im/cwtch/app"
|
||||
|
@ -12,13 +13,11 @@ import (
|
|||
"cwtch.im/ui/go/constants"
|
||||
"github.com/therecipe/qt/qml"
|
||||
|
||||
"encoding/base32"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cwtch.im/ui/go/the"
|
||||
"encoding/base32"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"github.com/therecipe/qt/core"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GrandCentralDispatcher struct {
|
||||
|
@ -66,8 +65,6 @@ type GrandCentralDispatcher struct {
|
|||
_ func(handle, key, value string) `signal:"UpdateContactAttribute"`
|
||||
|
||||
// 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:"ResetMessagePane"`
|
||||
_ func(mID string) `signal:"Acknowledged"`
|
||||
|
@ -120,6 +117,8 @@ type GrandCentralDispatcher struct {
|
|||
_ func() `signal:"blockUnknownPeers,auto"`
|
||||
_ func(onion string) `signal:"storeHistoryForPeer,auto"`
|
||||
_ func(onion string) `signal:"deleteHistoryForPeer,auto"`
|
||||
// chat
|
||||
_ func(mID string) `slot:"acktest,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 {
|
||||
this.conversationLock.Lock()
|
||||
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) {
|
||||
if len(message) > 65530 {
|
||||
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.GetUiManager(this.selectedProfile()).AddMessage(this.SelectedConversation(), "me", message, true, mID, time.Now(), false)
|
||||
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
||||
_, err := the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message)
|
||||
this.TimelineInterface.RequestEIR()
|
||||
|
||||
if err != nil {
|
||||
this.InvokePopup("failed to send message " + err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
to := this.SelectedConversation()
|
||||
prenum := this.TimelineInterface.num()
|
||||
this.TimelineInterface.AddMessage(prenum)
|
||||
/*mID := */the.Peer.SendMessageToPeer(to, message)
|
||||
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
||||
the.Peer.SendMessageToPeer(this.SelectedConversation(), message)
|
||||
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.requestGroupSettings(handle)
|
||||
|
||||
tl := group.GetTimeline()
|
||||
nick := GetNick(handle)
|
||||
updateLastReadTime(group.GroupID)
|
||||
if nick == "" {
|
||||
|
@ -275,34 +293,6 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
|||
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
|
||||
} // ELSE LOAD CONTACT
|
||||
|
||||
|
@ -317,32 +307,6 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
|||
}
|
||||
updateLastReadTime(contact.Onion)
|
||||
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() {
|
||||
|
@ -760,3 +724,8 @@ func (this *GrandCentralDispatcher) deleteProfile(onion string) {
|
|||
log.Infof("deleteProfile %v\n", 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/model"
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"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
|
||||
// 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 {
|
||||
Acknowledge(mID string)
|
||||
Acknowledge(handle, mID string)
|
||||
AddContact(Handle string)
|
||||
AddSendMessageError(peer string, signature string, err string)
|
||||
AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool)
|
||||
|
@ -218,6 +219,7 @@ type Manager interface {
|
|||
|
||||
AboutToAddMessage()
|
||||
MessageJustAdded()
|
||||
StoreAndNotify(peer.CwtchPeer, string, string, time.Time, string)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (this *manager) Acknowledge(mID string) {
|
||||
func (this *manager) Acknowledge(handle, mID string) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
this.gcd.Acknowledged(mID)
|
||||
this.gcd.DoIfConversation(handle, func(){
|
||||
this.gcd.Acktest(mID)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -291,28 +295,34 @@ func (this *manager) MessageJustAdded() {
|
|||
this.gcd.TimelineInterface.RequestEIR()
|
||||
}
|
||||
|
||||
func (this *manager) StoreAndNotify(pere peer.CwtchPeer, onion string, messageTxt string, sent time.Time, profileOnion string) {
|
||||
erinn marked this conversation as resolved
Outdated
dan
commented
`pere` typo
erinn
commented
no it's the variable name version of Père aka Father CwtchPeer, preventer of naming conflicts with the peer library no it's the variable name version of Père aka Father CwtchPeer, preventer of naming conflicts with the peer library
|
||||
this.gcd.DoIfProfileElse(this.profile, func() {
|
||||
this.gcd.DoIfConversationElse(onion, func() {
|
||||
updateLastReadTime(onion)
|
||||
erinn marked this conversation as resolved
Outdated
dan
commented
Re: the comment below, since it's time based, maybe this call to updateLastReadTime() should be called after the message is added since num message new is calculated based on time? and if its updated before the message is added.. Re: the comment below, since it's time based, maybe this call to updateLastReadTime() should be called after the message is added since num message new is calculated based on time? and if its updated before the message is added..
erinn
commented
👍 :+1:
|
||||
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
|
||||
pere.StoreMessage(onion, messageTxt, sent)
|
||||
this.gcd.TimelineInterface.RequestEIR()
|
||||
}, func() {
|
||||
updateLastReadTime(onion)
|
||||
erinn marked this conversation as resolved
Outdated
dan
commented
updateLastReadTime(onion) - sets the conversation's last read time to now (shouldbe called when the conversation is opened and or open, clearing notifications. we do it at Load messages so all the old ones aren't 1000 notifications in the conversation row bubble. but this is the else? so the conversation isn't open, so we dont want to update the lastReadTime here right? updateLastReadTime(onion) - sets the conversation's last read time to now (shouldbe called when the conversation is opened and or open, clearing notifications. we do it at Load messages so all the old ones aren't 1000 notifications in the conversation row bubble. but this is the else? so the conversation isn't open, so we dont want to update the lastReadTime here right?
erinn
commented
yep you're right good catch (i was thinking of it as "updateMostRecentMessageArrivalTime" for some reason) yep you're right good catch (i was thinking of it as "updateMostRecentMessageArrivalTime" for some reason)
|
||||
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
|
||||
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() {
|
||||
|
||||
//nick := GetNick(handle)
|
||||
//image := GetProfilePic(handle)
|
||||
|
||||
// If we have this group loaded already
|
||||
this.gcd.DoIfConversation(handle, func() {
|
||||
updateLastReadTime(handle)
|
||||
// If the message is not from the user then add it, otherwise, just acknowledge.
|
||||
if !fromMe {
|
||||
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
|
||||
//this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Unix(), false, false)
|
||||
if !fromMe || !Acknowledged {
|
||||
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num()-1)
|
||||
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 {
|
||||
this.gcd.Acknowledged(messageID)
|
||||
}
|
||||
this.gcd.Acknowledged(messageID)
|
||||
}
|
||||
})
|
||||
this.gcd.IncContactUnreadCount(handle)
|
||||
|
|
|
@ -3,6 +3,7 @@ package ui
|
|||
import (
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/ui/go/the"
|
||||
"encoding/hex"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"github.com/therecipe/qt/core"
|
||||
"reflect"
|
||||
|
@ -12,16 +13,16 @@ import (
|
|||
type MessageModel struct {
|
||||
core.QAbstractTableModel
|
||||
|
||||
ackIdx int
|
||||
handle string
|
||||
//_ string `property:"handle,auto"`
|
||||
_ func(string) `signal:"setHandle,auto"`
|
||||
|
||||
_ map[int]*core.QByteArray `property:"roles"`
|
||||
_ func() `constructor:"init"`
|
||||
|
||||
_ func(int) `signal:"addMessage,auto"`
|
||||
_ func(string) `signal:"createLocalFormEntry,auto"`
|
||||
_ func() `signal:"requestEIR,auto"` // request this.EndInsertRecord() on gui thread
|
||||
_ func(int) `signal:"editMessage,auto"`
|
||||
_ func() `signal:"requestEIR,auto"`
|
||||
|
||||
_ func(string) string `slot:"getNick,auto"`
|
||||
_ func(string) string `slot:"getImage,auto"`
|
||||
|
@ -36,6 +37,8 @@ type MessageWrapper struct {
|
|||
Acknowledged bool
|
||||
RawMessage string
|
||||
Error string
|
||||
Day string
|
||||
Signature string
|
||||
_ bool `property:"ackd"`
|
||||
}
|
||||
|
||||
|
@ -49,10 +52,12 @@ func (this *MessageModel) setHandle(handle string) {
|
|||
|
||||
|
||||
func (this *MessageModel) init() {
|
||||
//mdt := reflect.TypeOf([]model.Message{}).Elem()
|
||||
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
||||
roles := make(map[int]*core.QByteArray)
|
||||
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__DisplayRole)] = core.NewQByteArray2("display", -1)
|
||||
|
@ -83,7 +88,7 @@ func (this *MessageModel) getImage(handle string) string {
|
|||
|
||||
func (this *MessageModel) num() int {
|
||||
if this.Handle() == "" || the.Peer == nil {
|
||||
log.Debugf("num: early returning 0")
|
||||
log.Debugf("MessageModel.num: early returning 0")
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -95,19 +100,17 @@ func (this *MessageModel) num() int {
|
|||
} else {
|
||||
contact := the.Peer.GetContact(this.Handle())
|
||||
if contact != nil {
|
||||
log.Debugf("num: returning %v", 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
|
||||
}
|
||||
|
||||
func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||
log.Infof("MessageModel.getMessage(%v)", idx)
|
||||
|
||||
var modelmsg model.Message
|
||||
var ackd bool
|
||||
|
||||
if this.isGroup() {
|
||||
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)]
|
||||
} else {
|
||||
modelmsg = group.Timeline.Messages[idx]
|
||||
ackd = true
|
||||
}
|
||||
} else {
|
||||
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 :/"}
|
||||
erinn marked this conversation as resolved
Outdated
dan
commented
error? error?
|
||||
} else if idx >= len(contact.Timeline.Messages) {
|
||||
log.Errorf("this shouldnt happen")
|
||||
erinn marked this conversation as resolved
Outdated
dan
commented
modelmsg = someDummy template? otherwise the return statement below will segfault? modelmsg = someDummy template? otherwise the return statement below will segfault?
|
||||
//modelmsg = contact.UnacknowledgedMessages[idx-len(contact.Timeline.Messages)]
|
||||
} else {
|
||||
modelmsg = contact.Timeline.Messages[idx]
|
||||
ackd = modelmsg.Acknowledged
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,13 +138,13 @@ func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
|||
RawMessage: modelmsg.Message,
|
||||
PeerID: modelmsg.PeerID,
|
||||
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 {
|
||||
log.Infof("MessageModel.data(%v, %v)", index.Row(), role)
|
||||
|
||||
if !index.IsValid() {
|
||||
return core.NewQVariant()
|
||||
}
|
||||
|
@ -173,7 +177,7 @@ func (this *MessageModel) headerData(section int, orientation core.Qt__Orientati
|
|||
return this.HeaderDataDefault(section, orientation, role)
|
||||
}
|
||||
|
||||
mdt := reflect.TypeOf([]model.Message{}).Elem()
|
||||
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
||||
return core.NewQVariant12(mdt.Field(section).Name)
|
||||
}
|
||||
|
||||
|
@ -185,30 +189,29 @@ func (this *MessageModel) columnCount(parent *core.QModelIndex) int {
|
|||
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) {
|
||||
log.Debugf("MessageModel.addMessage() ZOOP ZOOP %v", this.handle)
|
||||
this.BeginInsertRows(core.NewQModelIndex(), idx, idx)//this.num(), this.num())
|
||||
//this.modelData = append(this.modelData, *fe)
|
||||
//this.RequestEIR()
|
||||
this.BeginInsertRows(core.NewQModelIndex(), idx, idx)
|
||||
}
|
||||
|
||||
// perform this.EndInsertRows() on the gui thread
|
||||
// perform this.EndInsertRows() on the gui thread after an AddMessage()
|
||||
func (this *MessageModel) requestEIR() {
|
||||
log.Debugf("MessageModel.requestEIR() ZEEP ZEEP %v", this.handle)
|
||||
this.EndInsertRows()
|
||||
}
|
||||
|
||||
|
||||
func (this *MessageModel) createLocalFormEntry(name string) {
|
||||
go this.createLocalFormEntry_thread(name)
|
||||
}
|
||||
|
||||
func (this *MessageModel) createLocalFormEntry_thread(name string) {
|
||||
log.Debugf("nyi #9779729343959699492726648294050382")
|
||||
/*
|
||||
fe := &model.Message{
|
||||
Message: "hi!",
|
||||
// notify the gui that the message acknowledgement at index idx has been modified
|
||||
func (this *MessageModel) editMessage(idx int) {
|
||||
if idx < 0 || idx >= this.num() {
|
||||
log.Errorf("cant edit message %v. probably fine", idx)
|
||||
return
|
||||
}
|
||||
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
|
||||
|
||||
ListModel { // MESSAGE OBJECTS ARE STORED HERE ...
|
||||
id: messagesModel
|
||||
Connections {
|
||||
target: mm
|
||||
onRowsInserted: {
|
||||
if (messagesListView.atYEnd) thymer.running = true
|
||||
|
||||
//todo: this won't fire for non-active convos
|
||||
erinn marked this conversation as resolved
Outdated
sarah
commented
this still a todo? this still a todo?
erinn
commented
it's a bugfix i preserved. created #348 to track it it's a bugfix i preserved. created #348 to track it
erinn
commented
bug* bug*
|
||||
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 {
|
||||
id: messagesListView
|
||||
|
@ -28,22 +47,26 @@ W.Overlay {
|
|||
Layout.fillWidth: true
|
||||
|
||||
width: parent.width
|
||||
model: mm//messagesModel//
|
||||
model: mm
|
||||
spacing: 6
|
||||
|
||||
clip: true
|
||||
ScrollBar.vertical: Opaque.ScrollBar {}
|
||||
maximumFlickVelocity: 1250
|
||||
|
||||
section.delegate: sectionHeading
|
||||
section.property: "Day"
|
||||
|
||||
|
||||
delegate: W.Message {
|
||||
handle: PeerID
|
||||
from: PeerID
|
||||
displayName: mm.getNick(PeerID)
|
||||
message: JSON.parse(RawMessage).d
|
||||
message: JSON.parse(RawMessage).d+"//"+Signature
|
||||
rawMessage: RawMessage
|
||||
image: mm.getImage(PeerID)
|
||||
messageID: "-1"//_mid
|
||||
fromMe: PeerID == gcd.SelectedProfile
|
||||
messageID: Signature
|
||||
fromMe: PeerID == gcd.selectedProfile
|
||||
timestamp: parseInt(Timestamp)
|
||||
ackd: Acknowledged
|
||||
error: Error
|
||||
|
@ -53,119 +76,48 @@ W.Overlay {
|
|||
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
|
||||
erinn marked this conversation as resolved
Outdated
dan
commented
Most of the numbers in qml all tediuslyget * gcd.themeScale so the whole ui should scale appropriately Most of the numbers in qml all tediuslyget * gcd.themeScale so the whole ui should scale appropriately
|
||||
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 {
|
||||
target: gcd
|
||||
|
||||
onClearMessages: function() {
|
||||
messagesModel.clear()
|
||||
messagesListView.model = null
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
acktest?