finishing first attempt at message model pr. mainly group acks

This commit is contained in:
erinn 2020-10-20 15:57:34 -07:00
parent 7bb2198879
commit e09ad91ab5
5 changed files with 174 additions and 243 deletions

View File

@ -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)

View File

@ -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)
}

View File

@ -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,28 +295,34 @@ 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 { } else {
if !Acknowledged { this.gcd.Acknowledged(messageID)
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.IncContactUnreadCount(handle) this.gcd.IncContactUnreadCount(handle)

View File

@ -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})
}

View File

@ -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
} messagesListView.positionViewAtEnd()
thymer.running = true
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()
} }
} }
} }