diff --git a/go.mod b/go.mod index cd49716d..e71241b9 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.12 require ( cwtch.im/cwtch v0.3.16 - git.openprivacy.ca/openprivacy/connectivity v1.2.0 + git.openprivacy.ca/openprivacy/connectivity v1.1.4 git.openprivacy.ca/openprivacy/log v1.0.1 github.com/gopherjs/gopherjs v0.0.0-20200209183636-89e6cbcd0b6d // indirect github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 // indirect -) \ No newline at end of file +) diff --git a/go.sum b/go.sum index 12bdad00..127c46e7 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ cwtch.im/cwtch v0.3.14 h1:XL8UbCUyIosdFTD5nSlpvhbQQGFLjvFmd81/SmfBSP8= cwtch.im/cwtch v0.3.14/go.mod h1:wDmgxWBWak/xvZ5GurdYNOJ8b8eha1MwVdiWsCS/pwI= cwtch.im/cwtch v0.3.15 h1:Z7fFREwXY728q2YmmwgHL357zAobrsWJ2oPkkGwzvo0= cwtch.im/cwtch v0.3.15/go.mod h1:iI9q4C3njHFBYQkNEbzMdK6QWPS0Vbkc0FigRHZNTvM= +cwtch.im/cwtch v0.3.16 h1:4M5So2zRDjy5byzd3G8ZrA2ZWObfm/oSIRfMBIFdOuI= +cwtch.im/cwtch v0.3.16/go.mod h1:iI9q4C3njHFBYQkNEbzMdK6QWPS0Vbkc0FigRHZNTvM= cwtch.im/tapir v0.1.15 h1:XSCWOvjmNkzMT2IceFgTBXWGKtYfr3a8o+La1s10OhE= cwtch.im/tapir v0.1.15/go.mod h1:HzezugpEx+nZ3LdyDsl0w6n45IJYnOt8uqldkLWmaqs= cwtch.im/tapir v0.1.17 h1:2jVZUe1a88tMI4aJPvRTO4Id3NN3PsM62cT5lntEChk= diff --git a/go/constants/style.go b/go/constants/style.go deleted file mode 100644 index 75b5bb4c..00000000 --- a/go/constants/style.go +++ /dev/null @@ -1,3 +0,0 @@ -package constants - -var TIME_FORMAT = "Mon 3:04pm" diff --git a/go/ui/gcd.go b/go/ui/gcd.go index e6b4b229..91bca3d4 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -62,14 +62,14 @@ 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 string, ackd bool, error bool) `signal:"AppendMessage"` - _ func(handle, from, displayName, message, image string, mID string, fromMe bool, ts string, ackd bool, error bool) `signal:"PrependMessage"` - _ func() `signal:"ClearMessages"` - _ func() `signal:"ResetMessagePane"` - _ func(mID string) `signal:"Acknowledged"` - _ func(title string) `signal:"SetToolbarTitle"` - _ func(signature string, err string) `signal:"GroupSendError"` - _ func(loading bool) `signal:"SetLoadingState"` + _ 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"` + _ func(title string) `signal:"SetToolbarTitle"` + _ func(signature string, err string) `signal:"GroupSendError"` + _ func(loading bool) `signal:"SetLoadingState"` // profile-area stuff _ func(name, onion, image, tag, showBlocked string) `signal:"UpdateMyProfile"` @@ -113,8 +113,8 @@ type GrandCentralDispatcher struct { _ func(onion string) `signal:"deleteContact,auto"` _ func() `signal:"allowUnknownPeers,auto"` _ func() `signal:"blockUnknownPeers,auto"` - _ func(onion string) `signal:"storeHistoryForPeer,auto"` - _ func(onion string) `signal:"deleteHistoryForPeer,auto"` + _ func(onion string) `signal:"storeHistoryForPeer,auto"` + _ func(onion string) `signal:"deleteHistoryForPeer,auto"` _ func() `constructor:"init"` } @@ -283,7 +283,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { image, string(tl[i].Signature), tl[i].PeerID == the.Peer.GetOnion(), - tl[i].Timestamp.Format(constants.TIME_FORMAT), + 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, ) @@ -322,7 +322,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { image, string(messages[i].Signature), fromMe, - messages[i].Timestamp.Format(constants.TIME_FORMAT), + messages[i].Timestamp.Unix(), messages[i].Acknowledged, messages[i].Error != "", ) @@ -351,7 +351,7 @@ func (this *GrandCentralDispatcher) requestPeerSettings() { //blockunkownpeers, _ := the.Peer.GetAttribute(attr.GetPeerScope(constants.BlockUnknownPeersSetting)) // Whether Cwtch should save the history of the peer - saveHistory,exists := contact.GetAttribute(event.SaveHistoryKey) + saveHistory, exists := contact.GetAttribute(event.SaveHistoryKey) if !exists { saveHistory = event.DeleteHistoryDefault } diff --git a/go/ui/manager.go b/go/ui/manager.go index 87d7fd75..b88f70e1 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -285,10 +285,10 @@ func (this *manager) AddMessage(handle string, from string, message string, from updateLastReadTime(handle) // If the message is not from the user then add it, otherwise, just acknowledge. if !fromMe { - this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false) + this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Unix(), false, false) } else { if !Acknowledged { - this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false) + this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Unix(), false, false) } else { this.gcd.Acknowledged(messageID) } diff --git a/qml/main.qml b/qml/main.qml index 6b033d8d..cbf56650 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -204,8 +204,13 @@ ApplicationWindow { Item { anchors.fill: parent } // empty - OverlayPane { // messagePane - anchors.fill: parent + Rectangle { + color: Theme.backgroundMainColor + Layout.fillWidth: true + Layout.fillHeight: true + OverlayPane { // messagePane + anchors.fill: parent + } } @@ -242,13 +247,17 @@ ApplicationWindow { function updateToolbar() { - toolbar.hideTitle() - toolbar.rightMenuVisible = false + + if (currentIndex == splashPane) { + toolbar.hideTitle() + toolbar.rightMenuVisible = false toolbar.visible = false } else { toolbar.visible = true if (currentIndex == managementPane) { + toolbar.hideTitle() + toolbar.rightMenuVisible = false toolbar.color = Theme.backgroundMainColor toolbar.leftMenuVisible = true toolbar.backVisible = false @@ -257,6 +266,8 @@ ApplicationWindow { toolbar.backVisible = true if (currentIndex == profilePane && theStack.currentIndex == theStack.emptyPane) { + toolbar.hideTitle() + toolbar.rightMenuVisible = false toolbar.color = Theme.backgroundMainColor } else { toolbar.color = Theme.backgroundPaneColor diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index 4be09d70..90bf89ce 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -140,7 +140,7 @@ ColumnLayout { } Text { id: texttitle - text: '' + Utils.htmlEscaped(title) + ' by ' + from + "
" + timestamp + text: '' + Utils.htmlEscaped(title) + ' by ' + from + "
" + Qt.formatDateTime(new Date(timestamp*1000), "MMMM d, h:mm ap") leftPadding: 10 topPadding: 5 bottomPadding:5 diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index 2d0f34a2..e3114a03 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -42,9 +42,12 @@ Item { timestamp: _ts ackd: _ackd error: _error + calendarEvent: _handle == "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 } - Connections { target: gcd @@ -62,6 +65,28 @@ Item { } 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()) { + console.log("DATES DONT MATCH") + 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, @@ -95,6 +120,28 @@ Item { } 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, diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 444f9696..1393050b 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -172,7 +172,7 @@ ColumnLayout { RowLayout { Text { id: texttitle - text: '' + Utils.htmlEscaped(title) + ' by ' + displayName + "
" + timestamp + text: '' + Utils.htmlEscaped(title) + ' by ' + displayName + "
" + Qt.formatDateTime(new Date(timestamp*1000), "MMMM d, h:mm ap") leftPadding: 10 topPadding: 5 bottomPadding:5 diff --git a/qml/panes/PeerSettingsPane.qml b/qml/panes/PeerSettingsPane.qml index 9bd3e755..ad7d0494 100644 --- a/qml/panes/PeerSettingsPane.qml +++ b/qml/panes/PeerSettingsPane.qml @@ -53,8 +53,7 @@ Opaque.SettingsList { // settingsPane dropShadowColor: Theme.dropShadowPaneColor onClicked: { gcd.savePeerSettings(txtOnion.text, txtDisplayName.text) - // TODO: broken - theStack.title = txtDisplayName.text + toolbar.setTitle(txtDisplayName.text) theStack.pane = theStack.messagePane } } diff --git a/qml/panes/SettingsPane.qml b/qml/panes/SettingsPane.qml index 35342804..ac11c51f 100644 --- a/qml/panes/SettingsPane.qml +++ b/qml/panes/SettingsPane.qml @@ -71,7 +71,8 @@ Opaque.SettingsList { // settingsPane to: gcd.os == "android" ? 4.0 : 1.9 value: gcd.themeScale live: false - stepSize: 0.1 + snapMode: Slider.SnapAlways + stepSize: 0.25 onValueChanged: { gcd.themeScale = zoomSlider.value diff --git a/qml/widgets/Message.qml b/qml/widgets/Message.qml index 10ea3911..f20194e4 100644 --- a/qml/widgets/Message.qml +++ b/qml/widgets/Message.qml @@ -6,12 +6,12 @@ import QtQuick.Layouts 1.3 import "../opaque" as Opaque import "../opaque/controls" as Awesome +import "../opaque/theme" +import "../opaque/fonts" -Item { +Rectangle { id: root - anchors.left: fromMe ? undefined : parent.left - anchors.right: fromMe ? parent.right : undefined height: Math.max(imgProfile.height, rectMessageBubble.height) property string message @@ -22,9 +22,12 @@ Item { property string messageID property bool fromMe property bool ackd - property alias timestamp: ts.text + property int timestamp property alias image: imgProfile.source property string error + property bool calendarEvent + + property real logscale: 4 * Math.log10(gcd.themeScale + 1) Connections { target: gcd @@ -42,15 +45,20 @@ Item { } } - Opaque.Portrait { id: imgProfile anchors.left: parent.left - // TODO: currently unused? - //handle: root.from - visible: !fromMe - //showStatus: false - //highlight: ima.containsMouse + anchors.bottom: parent.bottom + + visible: !fromMe && !calendarEvent + + + size: fromMe || calendarEvent ? 0 : Theme.contactPortraitSize * 0.5 + badgeVisible: false + + + portraitBorderColor: Theme.portraitOnlineBorderColor + portraitColor: Theme.portraitOnlineBackgroundColor ToolTip.visible: ima.containsMouse //: Click to DM @@ -75,95 +83,112 @@ Item { Rectangle { // THIS IS JUST A PRETTY MESSAGE-HOLDING RECTANGLE id: rectMessageBubble - height: colMessageBubble.height + 8 + height: (handle.visible ? handle.height : 0) + (10 * gcd.themeScale) + colMessageBubble.height + 8 width: colMessageBubble.width + 6 - color: fromMe ? "#B09CBC" : "#4B3557" - radius: 5 + color: fromMe ? Theme.messageFromMeBackgroundColor : (calendarEvent ? Theme.messageFromOtherBackgroundColor : Theme.messageFromOtherBackgroundColor) + radius: 15 * logscale - // the console will complain constantly about me setting these anchors, but qt only allows margins if they've been set to something - // a kludge to fix this would be to have spacers before/after and set the widths according to the side they're on ^ea - anchors.left: fromMe ? undefined : imgProfile.right //parent.left - anchors.right: fromMe ? parent.right : undefined - anchors.leftMargin: 5 - anchors.rightMargin: 9 + anchors.left: fromMe ? undefined : (calendarEvent ? undefined : imgProfile.right) //parent.left + anchors.right: fromMe ? (calendarEvent ? undefined : parent.right) : undefined + anchors.horizontalCenter: calendarEvent ? parent.horizontalCenter : undefined + + anchors.leftMargin: calendarEvent ? 0 : 5 + anchors.rightMargin: calendarEvent ? 0 : 9 anchors.topMargin: 5 + // A sharp corner on the side of the "speaker" + Rectangle { + id: sharpCorner + visible: !calendarEvent + height: parent.radius + width: parent.radius - ColumnLayout { + anchors.bottom: rectMessageBubble.bottom + anchors.left: fromMe ? undefined : parent.left + anchors.right: fromMe ? parent.right : undefined + + color: parent.color + } + + Opaque.EllipsisLabel { + id: handle + visible: !fromMe && !calendarEvent + + text: displayName + + color: Theme.messageFromOtherTextColor + size: Theme.chatSize * gcd.themeScale + weight: Font.Bold + font.family: Fonts.applicationFontBold.name + font.styleName: "Bold" + leftPadding: 10 * gcd.themeScale + topPadding: 10 * gcd.themeScale + + container: lbl + } + + onWidthChanged: { handle.textResize() } + + Column { id: colMessageBubble + width: Math.max(lbl.width, ts.width + ack.width + 10) - Column { // combine these into one element or else childrenRect won't play nicely - TextEdit { // this is used as a helper to calculate the message box width - id: dummy - visible: false - padding: 6 - leftPadding: 10 - font.pixelSize: gcd.themeScale * 12 - wrapMode: TextEdit.NoWrap - text: lbl.text - textFormat: Text.RichText - } + anchors.top: fromMe ? parent.top : (calendarEvent ? parent.top : handle.bottom) + anchors.topMargin: 10 * gcd.themeScale - TextEdit { // this is the actual text display - id: lbl - text: parse(message, 12, true) - color: "#FFFFFF" - padding: 6 - leftPadding: 10 - font.pixelSize: gcd.themeScale * 12 - selectByMouse: gcd.os != "android" - readOnly: true - width: Math.min(dummy.width, root.parent.width - (imgProfile.visible ? imgProfile.width : 0) - 40) - wrapMode: TextEdit.Wrap - textFormat: Text.RichText - } + TextEdit { // this is used as a helper to calculate the message box width + id: dummy + visible: false + padding: 6 * gcd.themeScale + leftPadding: 10 * gcd.themeScale + font.pixelSize: gcd.themeScale * Theme.chatSize + wrapMode: TextEdit.NoWrap + text: lbl.text + textFormat: Text.RichText } - RowLayout { + TextEdit { // this is the actual text display + id: lbl + text: parse(message, 12, true) + color: fromMe ? Theme.messageFromMeTextColor : Theme.messageFromOtherTextColor + padding: 6 * gcd.themeScale + leftPadding: 10 * gcd.themeScale + font.pixelSize: gcd.themeScale * Theme.chatSize + selectByMouse: gcd.os != "android" + readOnly: true + width: Math.min(dummy.width, root.parent.width - (imgProfile.visible ? imgProfile.width : 0) - 40) + wrapMode: TextEdit.Wrap + textFormat: Text.RichText + } + + Row { id: rowBottom - anchors.left: parent.left anchors.right: parent.right + visible: !calendarEvent - - Label { // TIMESTAMP + Opaque.ScalingLabel { // TIMESTAMP id: ts - color: "#FFFFFF" - font.pixelSize: 10 * gcd.themeScale - anchors.left: parent.left - leftPadding: 10 - } - - Label { // DISPLAY NAME FOR GROUPS - color: "#FFFFFF" - font.pixelSize: 10 * gcd.themeScale - anchors.right: parent.right - text: displayName.length > 12 ? displayName.substr(0,12) + "..." : displayName - visible: !fromMe - ToolTip.text: from - ToolTip.visible: ma2.containsMouse - ToolTip.delay: 200 - - MouseArea { - id: ma2 - anchors.fill: parent - hoverEnabled: true - } + text: Qt.formatDateTime(new Date(root.timestamp*1000), "h:mm ap") + color: fromMe ? Theme.messageFromMeTextColor : Theme.messageFromOtherTextColor + font.pixelSize: Theme.chatMetaTextSize * gcd.themeScale + rightPadding: 10 } Image { // ACKNOWLEDGEMENT ICON id: ack - anchors.right: parent.right source: root.error != "" ? gcd.assetPath + "fontawesome/regular/window-close.svg" : (root.ackd ? gcd.assetPath + "fontawesome/regular/check-circle.svg" : gcd.assetPath + "fontawesome/regular/hourglass.svg") - height: 10 * gcd.themeScale - sourceSize.height: 10 * gcd.themeScale + height: Theme.chatMetaTextSize * gcd.themeScale + width: Theme.chatMetaTextSize * gcd.themeScale + anchors.bottom: parent.bottom + + sourceSize.height: Theme.chatMetaTextSize * gcd.themeScale visible: fromMe ToolTip.visible: ma.containsMouse ToolTip.delay: 200 //: Could not send this message ToolTip.text: root.error != "" ? qsTr("could-not-send-msg-error") + ":" + root.error : (root.ackd ? qsTr("acknowledged-label") : qsTr("pending-label")) - MouseArea { id: ma anchors.fill: parent diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index 45f6a348..04750c19 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -31,10 +31,10 @@ Item { function realignProfile() { if (dualPane) { - profile.height = 78 * logscale + profile.height = Theme.contactPortraitSize * logscale - portrait.baseWidth = 78 * logscale - portrait.height = 78 * logscale + portrait.baseWidth = Theme.contactPortraitSize * logscale + portrait.height = Theme.contactPortraitSize * logscale portrait.anchors.horizontalCenter = undefined portrait.anchors.left = profile.left @@ -49,10 +49,9 @@ Item { nameCenter.anchors.horizontalCenter = undefined nameCenter.anchors.left = nameRow.left } else { - profile.height = (150 * logscale) + profile.height = (Theme.contactPortraitSize * 2 * logscale) - portrait.baseWidth = 100 * logscale - portrait.height = 100 * logscale + portrait.size = Theme.contactPortraitSize * 1.5 portrait.anchors.left = undefined portrait.anchors.leftMargin = undefined