From 1a556d05ce86856d3705573168506e1f010b1e50 Mon Sep 17 00:00:00 2001 From: erinn Date: Tue, 8 Dec 2020 16:37:08 -0800 Subject: [PATCH] history warning and overlay model fixes --- go/ui/gcd.go | 80 ++++++++++++++++++++------------ go/ui/messagemodel.go | 59 ++++++++++++++--------- main.go | 1 + qml/opaque | 2 +- qml/overlays/BulletinOverlay.qml | 18 ++++++- qml/overlays/ChatOverlay.qml | 57 ++++++++++++++++------- qml/overlays/ListOverlay.qml | 14 +++++- qml/panes/PeerSettingsPane.qml | 3 -- qml/widgets/Message.qml | 3 +- qml/widgets/MessageEditor.qml | 4 ++ qml/widgets/Overlay.qml | 10 ++-- 11 files changed, 170 insertions(+), 81 deletions(-) diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 71c20670..b2e35901 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -9,6 +9,7 @@ import ( "cwtch.im/ui/go/constants" "cwtch.im/ui/go/features/groups" "cwtch.im/ui/go/ui/android" + "encoding/json" "github.com/therecipe/qt/qml" "strconv" "sync" @@ -143,7 +144,7 @@ type GrandCentralDispatcher struct { _ func() `constructor:"init"` // legacy overlay model support - _ func(onion string, overlay int) `signal:"legacyLoadOverlay,auto"` + _ func(onion string) `signal:"legacyLoadOverlay,auto"` _ 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"` } @@ -334,20 +335,31 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { updateLastReadTime(contact.Onion) this.SetToolbarTitle(nick) - this.legacyLoadOverlay(handle, 4) + this.legacyLoadOverlay(handle) } -func (this *GrandCentralDispatcher) legacyLoadOverlay(handle string, overlay int) { - go this.legacyLoadOverlay_helper(handle, overlay) +func (this *GrandCentralDispatcher) legacyLoadOverlay(handle string) { + // only do this for overlays 2 (bulletin) and 4 (lists) + go this.legacyLoadOverlay_helper(handle, []int{2,4}) } -func (this *GrandCentralDispatcher) legacyLoadOverlay_helper(handle string, overlay int) { +func contains(arr []int, x int) bool { + for _, v := range arr { + if v == x { + return true + } + } + + return false +} + +func (this *GrandCentralDispatcher) legacyLoadOverlay_helper(handle string, overlays []int) { if isGroup(handle) { group := the.CwtchApp.GetPeer(this.selectedProfile()).GetGroup(handle) tl := group.GetTimeline() for i := len(tl) - 1; i >= 0; i-- { - if tl[i].PeerID == the.Peer.GetOnion() { + if tl[i].PeerID == this.selectedProfile() { handle = "me" } else { handle = tl[i].PeerID @@ -356,18 +368,22 @@ func (this *GrandCentralDispatcher) legacyLoadOverlay_helper(handle string, over 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, - ) + obj := &OverlayJSONObject{} + err := json.Unmarshal([]byte(tl[i].Message), obj) + if err == nil && contains(overlays, obj.Overlay) { + this.PrependMessage( + handle, + tl[i].PeerID, + name, + tl[i].Message, + image, + string(tl[i].Signature), + tl[i].PeerID == this.selectedProfile(), + 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, + ) + } } } else {// !isGroup @@ -382,18 +398,22 @@ func (this *GrandCentralDispatcher) legacyLoadOverlay_helper(handle string, over displayname := GetNick(messages[i].PeerID) image := GetProfilePic(messages[i].PeerID) - this.PrependMessage( - from, - messages[i].PeerID, - displayname, - messages[i].Message, - image, - string(messages[i].Signature), - fromMe, - messages[i].Timestamp.Unix(), - messages[i].Acknowledged, - messages[i].Error != "", - ) + obj := &OverlayJSONObject{} + err := json.Unmarshal([]byte(messages[i].Message), obj) + if err == nil && contains(overlays, obj.Overlay) { + this.PrependMessage( + from, + messages[i].PeerID, + displayname, + messages[i].Message, + image, + string(messages[i].Signature), + fromMe, + messages[i].Timestamp.Unix(), + messages[i].Acknowledged, + messages[i].Error != "", + ) + } } } diff --git a/go/ui/messagemodel.go b/go/ui/messagemodel.go index 6b360726..ebdb3e1d 100644 --- a/go/ui/messagemodel.go +++ b/go/ui/messagemodel.go @@ -4,6 +4,8 @@ import ( "cwtch.im/cwtch/model" "cwtch.im/ui/go/the" "encoding/hex" + + //"encoding/hex" "git.openprivacy.ca/openprivacy/log" "github.com/therecipe/qt/core" "reflect" @@ -20,6 +22,7 @@ type MessageModel struct { _ map[int]*core.QByteArray `property:"roles"` _ func() `constructor:"init"` + _ func(int) *MessageWrapper `slot:"getMessage,auto"` _ func(int) `signal:"addMessage,auto"` _ func(int) `signal:"editMessage,auto"` _ func() `signal:"requestEIR,auto"` @@ -29,17 +32,22 @@ type MessageModel struct { } type MessageWrapper struct { - model.Message core.QObject + model.Message - Timestamp int64 - PeerID string - Acknowledged bool - RawMessage string - Error string - Day string - Signature string - _ bool `property:"ackd"` + _ int64 `property:"timestamp"` + _ string `property:"peerID"` + _ bool `property:"acknowledged"` + _ string `property:"rawMessage"` + _ string `property:"error"` + _ string `property:"day"` + _ string `property:"signature"` + _ bool `property:"ackd"` +} + +type OverlayJSONObject struct { + Overlay int `json:"o"` + Data string `json:"d"` } func (this *MessageModel) Handle() string { @@ -51,13 +59,19 @@ func (this *MessageModel) setHandle(handle string) { } func (this *MessageModel) init() { - mdt := reflect.TypeOf([]MessageWrapper{}).Elem() + sacrificialObject := NewMessageWrapper(nil) + mdt := reflect.TypeOf(*sacrificialObject) roles := make(map[int]*core.QByteArray) for i := 0; i < mdt.NumField(); i++ { - if mdt.Field(i).Name == "Acknowledged" { + fieldName := mdt.Field(i).Name + if fieldName == "_" { + fieldName = mdt.Field(i).Tag.Get("property") + } + + if fieldName == "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(fieldName, -1) } roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1) this.SetRoles(roles) @@ -133,16 +147,17 @@ func (this *MessageModel) getMessage(idx int) *MessageWrapper { } } - return &MessageWrapper{ - Message: modelmsg, - Timestamp: modelmsg.Timestamp.Unix(), - RawMessage: modelmsg.Message, - PeerID: modelmsg.PeerID, - Error: modelmsg.Error, - Acknowledged: ackd, - Day: modelmsg.Timestamp.Format("January 2, 2006"), - Signature: hex.EncodeToString(modelmsg.Signature), - } + mw := NewMessageWrapper(nil) + mw.Message = modelmsg + mw.SetTimestamp(modelmsg.Timestamp.Unix()) + mw.SetPeerID(modelmsg.PeerID) + mw.SetError(modelmsg.Error) + mw.SetAcknowledged(ackd) + mw.SetAckd(ackd)//??why both?? + mw.SetDay(modelmsg.Timestamp.Format("January 2, 2006")) + mw.SetSignature(hex.EncodeToString(modelmsg.Signature)) + mw.SetRawMessage(modelmsg.Message) + return mw } func (this *MessageModel) data(index *core.QModelIndex, role int) *core.QVariant { diff --git a/main.go b/main.go index 3f6d22a4..b6e37529 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ var ( func init() { // make go-defined types available in qml ui.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher") + ui.MessageWrapper_QmlRegisterType2("CustomQmlTypes", 1, 0, "MessageWrapper") } func main() { diff --git a/qml/opaque b/qml/opaque index 365eedbd..dd8dde1f 160000 --- a/qml/opaque +++ b/qml/opaque @@ -1 +1 @@ -Subproject commit 365eedbd90eb936043e42f632355862d2a34c77a +Subproject commit dd8dde1fb38e296530570d31e95780ff707e2895 diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index 20737d6c..a1230854 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -8,6 +8,7 @@ import QtQuick.Layouts 1.3 import "../opaque" as Opaque import "../opaque/styles" +import "../opaque/theme" import "../utils.js" as Utils ColumnLayout { @@ -50,7 +51,18 @@ ColumnLayout { maximumFlickVelocity: 800 + Connections { + id: cnxns1 + target: mm + onRowsInserted: { + var msg = mm.getMessage(first); + var name = msg.peerID == gcd.selectedProfile ? "me" : mm.getNick(msg.peerID); + cnxns2.handler(msg.peerID, msg.peerID, name, msg.rawMessage, mm.getImage(msg.peerID), msg.signature, msg.peerID == gcd.selectedProfile, msg.timestamp, msg.ack, msg.error) + } + } + Connections { + id: cnxns2 target: gcd onClearMessages: function() { @@ -139,11 +151,11 @@ ColumnLayout { } Text { id: texttitle - text: '' + Utils.htmlEscaped(title) + ' by ' + from + "
" + Qt.formatDateTime(new Date(timestamp*1000), "MMMM d, h:mm ap") + text: '' + Utils.htmlEscaped(title) + ' by ' + displayName + "
" + Qt.formatDateTime(new Date(timestamp*1000), "MMMM d, h:mm ap") leftPadding: 10 topPadding: 5 bottomPadding:5 - color: windowItem.cwtch_dark_color + color: Theme.mainTextColor } MouseArea { anchors.fill: parent @@ -172,6 +184,7 @@ ColumnLayout { leftPadding: 10 topPadding: 10 width: parent.width - 50 + color: Theme.mainTextColor } Opaque.Button { @@ -212,6 +225,7 @@ ColumnLayout { Text { //: Post a new Bulletin Post text: qsTr("post-new-bulletin-label") + color: Theme.mainTextColor } TextField { diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index c5c49711..7cdc90e2 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -3,6 +3,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.4 import QtQuick.Controls.Material 2.0 import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 import "../opaque" as Opaque import "../opaque/controls" as Awesome @@ -11,7 +12,9 @@ import "../widgets" as W import "../opaque/theme" W.Overlay { + id: overlayRoot property bool loading + property string historyState: "DefaultDeleteHistory" //horizontalPadding: 15 * gcd.themeScale @@ -59,8 +62,17 @@ W.Overlay { header: Component { Column { width: messagesListView.width + Label { + width: messagesListView.width + font.pointSize: Theme.textSmallPt + font.weight: Font.Bold + color: Theme.chatOverlayWarningTextColor + horizontalAlignment: Text.AlignHCenter + text: overlayRoot.historyState + } Label { + id: historyWarning wrapMode: Text.WordWrap width: messagesListView.width font.pointSize: Theme.textSmallPt @@ -68,9 +80,12 @@ W.Overlay { color: Theme.chatOverlayWarningTextColor horizontalAlignment: Text.AlignHCenter //: This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right. - text: true ? qsTr("chat-history-disabled") : - //: Message history is enabled. - qsTr("chat-history-enabled") + text: overlayRoot.historyState == "DefaultDeleteHistory" ? qsTr("chat-history-default") : ( + //: Message history is disabled. + overlayRoot.historyState == "DeleteHistoryConfirmed" ? qsTr("chat-history-disabled") : + //: Message history is enabled. + qsTr("chat-history-enabled") + ) } Opaque.Icon { @@ -89,20 +104,26 @@ W.Overlay { } } - delegate: W.Message { - handle: PeerID - from: PeerID - displayName: mm.getNick(PeerID) - message: JSON.parse(RawMessage).d - rawMessage: RawMessage - image: mm.getImage(PeerID) - messageID: Signature - fromMe: PeerID == gcd.selectedProfile - timestamp: parseInt(Timestamp) - ackd: Acknowledged - error: Error - calendarEvent: PeerID == "calendar" + // unusual msg... syntax is due to qt stack weirdnesses + // model injection doesn't work properly because MessageWrapper uses tagged QML properties + // but reverting to struct properties prevents us from using mm.getMessage() in onRowsInserted sooooo + property variant msg: mm.getMessage(index) + property variant obj: JSON.parse(msg.rawMessage) + visible: obj.o == 1 + height: visible ? implicitHeight : -messagesListView.spacing + handle: msg.peerID + from: msg.peerID + displayName: mm.getNick(msg.peerID) + message: obj.o == 1 ? obj.d : "" + //rawMessage: msg.rawMessage + image: mm.getImage(msg.peerID) + messageID: msg.signature + fromMe: msg.peerID == gcd.selectedProfile + timestamp: parseInt(msg.timestamp) + //ackd: acknowledged + //error: msg.error + calendarEvent: msg.peerID == "calendar" // listview doesnt do anchors right // https://stackoverflow.com/questions/31381997/why-does-anchors-fill-does-not-work-in-a-qml-listviews-delegates-subviews-and/31382307 width: messagesListView.width - scrollbar.width @@ -159,6 +180,10 @@ W.Overlay { messagesListView.positionViewAtEnd() thymer.running = true } + + onSupplyPeerSettings: function(onion, nick, authorization, saveHistory) { + overlayRoot.historyState = saveHistory + } } } diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 875051ce..47334736 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -4,6 +4,7 @@ import QtQuick.Controls 2.4 import QtQuick.Controls.Material 2.0 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 import "../opaque" as Opaque import "../opaque/controls" as Awesome @@ -28,7 +29,7 @@ ColumnLayout { anchors.right: parent.right anchors.margins: 10 - //: ex: "... paste an address here to add a contact ..." + //: ex: "Find..." placeholderText: qsTr("search-list") horizontalAlignment: TextInput.AlignHCenter icon: gcd.assetPath + "core/search-24px.webp" @@ -78,7 +79,18 @@ ColumnLayout { maximumFlickVelocity: 800 + Connections { + id: cnxns1 + target: mm + onRowsInserted: { + var msg = mm.getMessage(first); + var name = msg.peerID == gcd.selectedProfile ? "me" : mm.getNick(msg.peerID); + cnxns2.handler(msg.peerID, msg.peerID, name, msg.rawMessage, mm.getImage(msg.peerID), msg.signature, msg.peerID == gcd.selectedProfile, msg.timestamp, msg.ack, msg.error) + } + } + Connections { + id: cnxns2 target: gcd onClearMessages: function() { diff --git a/qml/panes/PeerSettingsPane.qml b/qml/panes/PeerSettingsPane.qml index d83260d5..a2aaaf34 100644 --- a/qml/panes/PeerSettingsPane.qml +++ b/qml/panes/PeerSettingsPane.qml @@ -82,14 +82,11 @@ Opaque.SettingsList { // settingsPane isToggled: root.authorization == Const.auth_blocked onToggled: function() { - console.log("peer block toddle for " + txtOnion.text + " currently: " + root.authorization) if (root.authorization == Const.auth_blocked) { root.authorization = Const.auth_unknown - console.log("setPeerAuthorization to " + Const.auth_unknown + " for " + txtOnion.text) gcd.setPeerAuthorization(txtOnion.text, Const.auth_unknown) } else { root.authorization = Const.auth_blocked - console.log("setPeerAuthorization to " + Const.auth_blocked + " for " + txtOnion.text) gcd.setPeerAuthorization(txtOnion.text, Const.auth_blocked) } isToggled = root.authorization == Const.auth_blocked diff --git a/qml/widgets/Message.qml b/qml/widgets/Message.qml index 5b1ebe87..030fb13c 100644 --- a/qml/widgets/Message.qml +++ b/qml/widgets/Message.qml @@ -12,7 +12,8 @@ import "../opaque/fonts" Rectangle { id: root - height: Math.max(imgProfile.height, rectMessageBubble.height) + implicitHeight: Math.max(imgProfile.height, rectMessageBubble.height) + height: implicitHeight color: Theme.backgroundMainColor property string message diff --git a/qml/widgets/MessageEditor.qml b/qml/widgets/MessageEditor.qml index e2ccd536..305b30be 100644 --- a/qml/widgets/MessageEditor.qml +++ b/qml/widgets/MessageEditor.qml @@ -79,6 +79,10 @@ ColumnLayout { id: statusText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter + width: parent.width - 2 * Theme.paddingMinimal + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + wrapMode: Text.NoWrap text: "" visible: text != "" size: Theme.chatMetaTextSize diff --git a/qml/widgets/Overlay.qml b/qml/widgets/Overlay.qml index b3c811a4..43567a0f 100644 --- a/qml/widgets/Overlay.qml +++ b/qml/widgets/Overlay.qml @@ -28,12 +28,12 @@ Item { id: control width: parent.width anchors.top: parent.top - anchors.topMargin: 10 * gcd.themeScale + anchors.topMargin: 10 anchors.bottom: msgEd.top - anchors.bottomMargin: 10 * gcd.themeScale + anchors.bottomMargin: 10 - horizontalPadding: 15 * gcd.themeScale + horizontalPadding: 15 } @@ -44,8 +44,8 @@ Item { anchors.bottom: parent.bottom anchors.right: parent.right anchors.left: parent.left - anchors.rightMargin: 15 * gcd.themeScale - anchors.leftMargin: 15 * gcd.themeScale + anchors.rightMargin: 15 + anchors.leftMargin: 15 onSendClicked: function(messageText) { root.sendClicked(messageText)