diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 91bca3d4..3a855d0a 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -1,6 +1,8 @@ package ui import ( + "sync" + "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" "cwtch.im/cwtch/model" @@ -8,14 +10,14 @@ import ( "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" "github.com/therecipe/qt/qml" - "sync" - "cwtch.im/ui/go/the" "encoding/base32" - "git.openprivacy.ca/openprivacy/log" - "github.com/therecipe/qt/core" "strings" "time" + + "cwtch.im/ui/go/the" + "git.openprivacy.ca/openprivacy/log" + "github.com/therecipe/qt/core" ) type GrandCentralDispatcher struct { @@ -94,27 +96,27 @@ type GrandCentralDispatcher struct { _ func(onion, currentPassword, newPassword string, defaultPass bool) `signal:"changePassword,auto""` _ func(key, val string) `signal:"storeSetting,auto"` // operating a profile - _ func(message string, mid string) `signal:"sendMessage,auto"` - _ func(onion string, auth string) `signal:"setPeerAuthorization,auto"` - _ func(onion string) `signal:"loadMessagesPane,auto"` - _ func(signal string) `signal:"broadcast,auto"` // convenience relay signal - _ func(str string) `signal:"importString,auto"` - _ func(str string) `signal:"createContact,auto"` - _ func(str string) `signal:"popup,auto"` - _ func(server, groupName string) `signal:"createGroup,auto"` - _ func(groupID string) `signal:"leaveGroup,auto"` - _ func(groupID string) `signal:"acceptGroup,auto"` - _ func() `signal:"requestSettings,auto"` - _ func(groupID string) `signal:"requestGroupSettings,auto"` - _ func(groupID, nick string) `signal:"saveGroupSettings,auto"` - _ func() `signal:"requestPeerSettings,auto"` - _ func(onion, nick string) `signal:"savePeerSettings,auto"` - _ func(onion, groupID string) `signal:"inviteToGroup,auto"` - _ 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(message string) `signal:"sendMessage,auto"` + _ func(onion string, auth string) `signal:"setPeerAuthorization,auto"` + _ func(onion string) `signal:"loadMessagesPane,auto"` + _ func(signal string) `signal:"broadcast,auto"` // convenience relay signal + _ func(str string) `signal:"importString,auto"` + _ func(str string) `signal:"createContact,auto"` + _ func(str string) `signal:"popup,auto"` + _ func(server, groupName string) `signal:"createGroup,auto"` + _ func(groupID string) `signal:"leaveGroup,auto"` + _ func(groupID string) `signal:"acceptGroup,auto"` + _ func() `signal:"requestSettings,auto"` + _ func(groupID string) `signal:"requestGroupSettings,auto"` + _ func(groupID, nick string) `signal:"saveGroupSettings,auto"` + _ func(handle string) `signal:"requestPeerSettings,auto"` + _ func(onion, nick string) `signal:"savePeerSettings,auto"` + _ func(onion, groupID string) `signal:"inviteToGroup,auto"` + _ 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() `constructor:"init"` } @@ -197,7 +199,7 @@ func (this *GrandCentralDispatcher) DoIfConversation(conversation string, fn fun } } -func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { +func (this *GrandCentralDispatcher) sendMessage(message string) { if len(message) > 65530 { this.InvokePopup("message is too long") return @@ -217,8 +219,7 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { } } - var err error - mID, err = the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message) + mID, err := the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message) this.GetUiManager(this.selectedProfile()).AddMessage(this.SelectedConversation(), "me", message, true, mID, time.Now(), false) @@ -228,7 +229,7 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { } } else { to := this.SelectedConversation() - mID = the.Peer.SendMessageToPeer(to, message) + mID := the.Peer.SendMessageToPeer(to, message) this.GetUiManager(this.selectedProfile()).AddMessage(to, "me", message, true, mID, time.Now(), false) } @@ -255,6 +256,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { loading = true } this.UpdateContactStatus(group.GroupID, int(state), loading) + this.requestGroupSettings(handle) tl := group.GetTimeline() nick := getNick(handle) @@ -265,35 +267,41 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { this.SetToolbarTitle(nick) } - for i := len(tl) - 1; i >= 0; i-- { - if tl[i].PeerID == the.Peer.GetOnion() { - handle = "me" - } else { - handle = tl[i].PeerID + 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, + ) } + }() - 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 contact := the.Peer.GetContact(handle) this.UpdateContactStatus(handle, int(connections.ConnectionStateToType[contact.State]), false) + this.requestPeerSettings(handle) var nick string if contact != nil { @@ -337,11 +345,11 @@ func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) { } -func (this *GrandCentralDispatcher) requestPeerSettings() { - contact := the.Peer.GetContact(this.SelectedConversation()) +func (this *GrandCentralDispatcher) requestPeerSettings(handle string) { + contact := the.Peer.GetContact(handle) if contact == nil { - log.Errorf("error: requested settings for unknown contact %v?", this.SelectedConversation()) - this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), string(contact.Authorization), "") + log.Errorf("error: requested settings for unknown contact %v?", handle) + this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), string(model.AuthUnknown), "") return } @@ -387,9 +395,7 @@ func (this *GrandCentralDispatcher) requestGroupSettings(groupID string) { contactnames := make([]string, len(contactaddrs)) for i, contact := range contactaddrs { contactnames[i] = getNick(contact) - } - this.SupplyGroupSettings(group.GroupID, nick, group.GroupServer, invite, group.Accepted, contactnames, contactaddrs) } diff --git a/i18n/translation_de.qm b/i18n/translation_de.qm index c40eed58..d3f984f6 100644 Binary files a/i18n/translation_de.qm and b/i18n/translation_de.qm differ diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index 42e5eb7e..d978b4a6 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -61,7 +61,7 @@ Adresse hier hinzufügen, um einen Kontakt aufzunehmen - + blocked @@ -140,7 +140,7 @@ - + add-list-item-btn @@ -157,28 +157,43 @@ Message - + dm-tooltip Click to DM Klicken, um DM zu senden - + could-not-send-msg-error Could not send this message Nachricht konnte nicht gesendet werden - + acknowledged-label bestätigt - + pending-label Bestätigung ausstehend + + MessageEditor + + + peer-blocked-message + Peer is blockced + + + + + peer-offline-message + Peer is offline, messages can't be delivered right now + + + MyProfile @@ -205,35 +220,32 @@ OverlayPane - accept-group-invite-label Do you want to accept the invitation to $GROUP - Möchtest Du die Einladung annehmen + Möchtest Du die Einladung annehmen - accept-group-btn Accept group invite button - Annehmen + Annehmen - reject-group-btn Reject Group invite button - Ablehnen + Ablehnen - + chat-btn Chat - + lists-btn Listen - + bulletins-btn Meldungen @@ -271,34 +283,29 @@ speichern - - + + save-peer-history Save Peer History - + save-peer-history-description - - dont-save-peer-history-default - - - - + dont-save-peer-history - + delete-btn löschen - + block-btn @@ -411,7 +418,7 @@ ProfileList - + add-new-profile-btn @@ -487,34 +494,34 @@ - + large-text-label Groß - + setting-theme Theme - + theme-light - + theme-dark - + version %1 Version %1 - + builddate %2 Built on: %2 diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index 8d749503..e1871aa5 100644 Binary files a/i18n/translation_en.qm and b/i18n/translation_en.qm differ diff --git a/i18n/translation_en.ts b/i18n/translation_en.ts index 0f29ecc2..a62f2a31 100644 --- a/i18n/translation_en.ts +++ b/i18n/translation_en.ts @@ -61,7 +61,7 @@ ... paste an address here to add a contact... - + blocked Blocked @@ -212,17 +212,17 @@ Right-click to reset. search-list ex: "... paste an address here to add a contact ..." - Search List + Search List peer-not-online - Peer is Offline. Applications cannot be used right now. + Peer is Offline. Applications cannot be used right now. - + add-list-item-btn - Add Item + Add Item @@ -237,28 +237,43 @@ Right-click to reset. Message - + dm-tooltip Click to DM Click to DM - + could-not-send-msg-error Could not send this message Could not send this message - + acknowledged-label Acknowledged - + pending-label Pending + + MessageEditor + + + peer-blocked-message + Peer is blockced + Peer is blocked + + + + peer-offline-message + Peer is offline, messages can't be delivered right now + Peer is offline, messages can't be delivered right now + + MyProfile @@ -285,35 +300,32 @@ Right-click to reset. OverlayPane - accept-group-invite-label Do you want to accept the invitation to $GROUP - Do you want to accept the invitation to + Do you want to accept the invitation to - accept-group-btn Accept group invite button - Accept + Accept - reject-group-btn Reject Group invite button - Reject + Reject - + chat-btn Chat - + lists-btn Lists - + bulletins-btn Bulletins @@ -351,38 +363,33 @@ Right-click to reset. Save - + block-btn Block Peer - - + + save-peer-history Save Peer History - Save Peer History + Save Peer History - + save-peer-history-description - Determines whether or not to delete any history associated with the peer. + Determines whether or not to delete any history associated with the peer. - - dont-save-peer-history-default - - - - + dont-save-peer-history - Delete Peer History + Delete Peer History unblock-btn Unblock Peer - + delete-btn Delete @@ -503,7 +510,7 @@ Right-click to reset. ProfileList - + add-new-profile-btn Add new profile @@ -588,34 +595,34 @@ Right-click to reset. Zoom level - + large-text-label Large - + setting-theme Theme Theme - + theme-light Light - + theme-dark Dark - + version %1 Version %1 Version %1 - + builddate %2 Built on: %2 Built on: %2 diff --git a/i18n/translation_fr.qm b/i18n/translation_fr.qm index 3b814608..d20f24f0 100644 Binary files a/i18n/translation_fr.qm and b/i18n/translation_fr.qm differ diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index 7b4b9716..10aa6cd7 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -61,7 +61,7 @@ ... coller une adresse ici pour ajouter un contact... - + blocked @@ -140,7 +140,7 @@ - + add-list-item-btn @@ -157,28 +157,43 @@ Message - + dm-tooltip Click to DM Envoyer un message privé - + could-not-send-msg-error Could not send this message Impossible d'envoyer ce message - + acknowledged-label Confirmé - + pending-label En attente + + MessageEditor + + + peer-blocked-message + Peer is blockced + + + + + peer-offline-message + Peer is offline, messages can't be delivered right now + + + MyProfile @@ -205,35 +220,32 @@ OverlayPane - accept-group-invite-label Do you want to accept the invitation to $GROUP - Voulez-vous accepter l'invitation au groupe + Voulez-vous accepter l'invitation au groupe - accept-group-btn Accept group invite button - Accepter + Accepter - reject-group-btn Reject Group invite button - Refuser + Refuser - + chat-btn Discuter - + lists-btn Listes - + bulletins-btn Bulletins @@ -271,34 +283,29 @@ Sauvegarder - - + + save-peer-history Save Peer History - + save-peer-history-description - - dont-save-peer-history-default - - - - + dont-save-peer-history - + delete-btn Effacer - + block-btn @@ -411,7 +418,7 @@ ProfileList - + add-new-profile-btn @@ -487,34 +494,34 @@ - + large-text-label Large - + setting-theme Theme - + theme-light - + theme-dark - + version %1 Version %1 - + builddate %2 Built on: %2 diff --git a/i18n/translation_pt.qm b/i18n/translation_pt.qm index b6c2805f..3480551a 100644 Binary files a/i18n/translation_pt.qm and b/i18n/translation_pt.qm differ diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index 7f3910f1..36119bb0 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -61,7 +61,7 @@ … cole um endereço aqui para adicionar um contato… - + blocked @@ -140,7 +140,7 @@ - + add-list-item-btn @@ -157,28 +157,43 @@ Message - + dm-tooltip Click to DM Clique para DM - + could-not-send-msg-error Could not send this message Não deu para enviar esta mensagem - + acknowledged-label Confirmada - + pending-label Pendente + + MessageEditor + + + peer-blocked-message + Peer is blockced + + + + + peer-offline-message + Peer is offline, messages can't be delivered right now + + + MyProfile @@ -205,35 +220,32 @@ OverlayPane - accept-group-invite-label Do you want to accept the invitation to $GROUP - Você quer aceitar o convite para + Você quer aceitar o convite para - accept-group-btn Accept group invite button - Aceitar + Aceitar - reject-group-btn Reject Group invite button - Recusar + Recusar - + chat-btn Chat - + lists-btn Listas - + bulletins-btn Boletins @@ -271,34 +283,29 @@ Salvar - - + + save-peer-history Save Peer History - + save-peer-history-description - - dont-save-peer-history-default - - - - + dont-save-peer-history - + delete-btn Deletar - + block-btn @@ -411,7 +418,7 @@ ProfileList - + add-new-profile-btn @@ -487,34 +494,34 @@ - + large-text-label Grande - + setting-theme Theme - + theme-light - + theme-dark - + version %1 Version %1 - + builddate %2 Built on: %2 diff --git a/qml.qrc b/qml.qrc index 794ee803..8dd66745 100644 --- a/qml.qrc +++ b/qml.qrc @@ -16,6 +16,7 @@ qml/widgets/ContactList.qml qml/widgets/ContactRow.qml qml/widgets/Message.qml + qml/widgets/MessageEditor.qml qml/widgets/MyProfile.qml qml/widgets/ProfileList.qml qml/widgets/ProfileRow.qml diff --git a/qml/const/Const.qml b/qml/const/Const.qml index 3f4b8986..d7e09430 100644 --- a/qml/const/Const.qml +++ b/qml/const/Const.qml @@ -11,4 +11,16 @@ Item { // defined in cwtch.im/ui/go/constants/attributes.go readonly property string show_blocked: "show-blocked" + + // defined in cwtch.im/cwtch/protocol/connection/state.go + //0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Synced,5:Failed,6:Killed + readonly property int state_disconnected: 0 + readonly property int state_connecting: 1 + readonly property int state_connected: 2 + readonly property int state_authenticated: 3 + readonly property int state_synced: 4 + readonly property int state_failed: 5 + readonly property int state_killed: 6 + + } diff --git a/qml/main.qml b/qml/main.qml index cbf56650..d14f9f85 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -93,7 +93,7 @@ ApplicationWindow { gcd.requestGroupSettings(gcd.selectedConversation) } else { theStack.pane = theStack.userProfilePane - gcd.requestPeerSettings() + gcd.requestPeerSettings(gcd.selectedConversation) } } } @@ -165,7 +165,7 @@ ApplicationWindow { } - RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR + RowLayout { // Profile Pane (contact list + overlays) Layout.fillHeight: true Layout.fillWidth: true spacing: 0 @@ -179,9 +179,24 @@ ApplicationWindow { ContactList { - anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.right: (divider.visible ? divider.left : parent.right) + //anchors.topMargin: 10 * gcd.themeScale dualPane: theStack.pane != theStack.emptyPane || theStack.pane == undefined } + + Rectangle { + id: divider + width: 2 + anchors.right: parent.right + height: parent.height - (20 * gcd.themeScale) + anchors.verticalCenter: parent.verticalCenter + visible: theStack.pane != theStack.emptyPane + //Layout.fillHeight: true + color: Theme.dividerColor + } } Rectangle { // THE RIGHT PANE WHERE THE MESSAGES AND STUFF GO @@ -189,7 +204,6 @@ ApplicationWindow { Layout.fillWidth: true Layout.fillHeight: true - StackLayout { id: theStack anchors.fill: parent @@ -204,12 +218,13 @@ ApplicationWindow { Item { anchors.fill: parent } // empty - Rectangle { + Rectangle { color: Theme.backgroundMainColor Layout.fillWidth: true Layout.fillHeight: true OverlayPane { // messagePane anchors.fill: parent + anchors.topMargin: 10 * gcd.themeScale } } diff --git a/qml/opaque b/qml/opaque index 05059341..60c6a8d9 160000 --- a/qml/opaque +++ b/qml/opaque @@ -1 +1 @@ -Subproject commit 0505934172c42fc589f8c0e095e1e8f0efeda245 +Subproject commit 60c6a8d9838df0a83b7e0faf06a4d6d01b4eb0a0 diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index e3114a03..cb1bce3c 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -9,25 +9,30 @@ import "../opaque/controls" as Awesome import "../opaque/fonts/Twemoji.js" as T import "../utils.js" as Utils import "../widgets" +import "../opaque/theme" -Item { - width: parent.width +Overlay { property bool loading + //horizontalPadding: 15 * gcd.themeScale + ListModel { // MESSAGE OBJECTS ARE STORED HERE ... id: messagesModel } - ListView { + + + contentItem: ListView { id: messagesListView - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: emojiDrawer.visible ? emojiDrawer.top : rowDrawer.top + + Layout.fillWidth: true + + width: parent.width model: messagesModel spacing: 6 + clip: true - ScrollBar.vertical: ScrollBar {} + ScrollBar.vertical: Opaque.ScrollBar {} maximumFlickVelocity: 1250 delegate: Message { @@ -53,7 +58,6 @@ Item { onClearMessages: function() { messagesModel.clear() - txtMessage.text = "" } onAppendMessage: function(handle, from, displayName, message, image, mid, fromMe, ts, ackd, error) { @@ -69,9 +73,9 @@ Item { 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") + || prevDate.getMonth() != date.getMonth() + || prevDate.getUTCDate() != date.getUTCDate()) { + // new Day detected, Add Date message divider messagesModel.append({ "_handle": "calendar", "_from": "calendar", @@ -125,8 +129,8 @@ Item { var prevDate = new Date(messagesModel.get(0)["_ts"] * 1000); if (prevDate.getFullYear() != date.getFullYear() - || prevDate.getMonth() != date.getMonth() - || prevDate.getUTCDate() != date.getUTCDate()) { + || prevDate.getMonth() != date.getMonth() + || prevDate.getUTCDate() != date.getUTCDate()) { messagesModel.insert(0, { "_handle": "calendar", "_from": "calendar", @@ -159,216 +163,16 @@ Item { messagesListView.positionViewAtEnd() } - - onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.selectedConversation == _handle) { - // Group is Synced OR p2p is Authenticated - if ( (_handle.length == 32 && _status == 4) || (_handle.length == 56 && _status == 3) ) { - txtMessage.enabled = true - btnSend.enabled = true - } else { - txtMessage.enabled = false - btnSend.enabled = false - } - - } - } } } - Opaque.EmojiDrawer { - id: emojiDrawer - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: rowDrawer.top - size: 24 * gcd.themeScale - onPicked: function(shortcode) { - if (!txtMessage.enabled) return - txtMessage.insert(txtMessage.cursorPosition, ":" + shortcode + ":") - } + onSendClicked: function(messageText) { + var msg = JSON.stringify({"o":1, "d":messageText.replace(/\[\:newline\:\]/g,"\n")}) + gcd.sendMessage(msg) } - RowLayout { // THE BOTTOM DRAWER - id: rowDrawer - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.right: parent.right - Rectangle { // MESSAGE ENTRY TEXTFIELD - id: rectMessage - Layout.fillWidth: true - Layout.minimumHeight: 40 * gcd.themeScale - Layout.maximumHeight: 40 * gcd.themeScale - color: txtMessage.isEnabled ? "#EDEDED" : "#CCCCCC" - border.color: "#AAAAAA" - radius: 10 - - - MouseArea { - anchors.fill: parent - onClicked: txtMessage.focus = true - } - - Flickable { - id: flkMessage - anchors.fill: parent//this does nothing! bug in qt - Layout.minimumWidth: parent.width - Layout.maximumWidth: parent.width - Layout.minimumHeight: rectMessage.height - Layout.maximumHeight: rectMessage.height - contentWidth: txtMessage.width - contentHeight: txtMessage.height - boundsBehavior: Flickable.StopAtBounds - clip:true - maximumFlickVelocity: 300 - - - ScrollBar.vertical: ScrollBar{} - - TextEdit { - id: txtMessage - font.pixelSize: 10 * gcd.themeScale - text: "" - padding: 6 - wrapMode: TextEdit.Wrap - textFormat: Text.RichText - width: rectMessage.width - - property bool skipOneUpdate: false - property int previousCursor - - Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK - if ((event.modifiers & Qt.ControlModifier) && gcd.os != "android") { - txtMessage.insert(txtMessage.cursorPosition, "
") - } else if (event.modifiers == Qt.NoModifier) { - btnSend.clicked() - } - } - - // welcome to the emoji parser! it is horrifying code that needs to leave in - // while also stripping any other tag, including other images. - // TODO: this probably breaks if people actually do want to paste html - onTextChanged: { - if (gcd.os == "android") { - return - } - - // we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally - // passing text into it to be filtered. this prevents recursive calls putting us into an - // infinite loop - if (skipOneUpdate) { - skipOneUpdate = false - return - } - - previousCursor = cursorPosition - //console.log("onTextChanged() at position " + previousCursor) - - //console.log("1: " + txtMessage.getText(0, txtMessage.text.length)) - - // convert tags back to their emoji form - // Then parse out the rest of the HTML - var nt = restoreEmoji(txtMessage.text) - if (nt != txtMessage.text) { - skipOneUpdate = true - txtMessage.text = nt - } - - //console.log("2: " + txtMessage.getText(0, txtMessage.text.length)) - var preserveSpaces = txtMessage.text.replace(/
/g,"[:newline:]"); - if (preserveSpaces != txtMessage.text) { - skipOneUpdate = true - txtMessage.text = preserveSpaces - } - // strip all HTML tags - var theText = Utils.htmlEscaped(txtMessage.getText(0, txtMessage.text.length)) - //console.log("3: " + theText) - - // convert emoji back to tags - nt = parse(theText, 10) - //console.log("4: " + nt) - - // preserve double spacing - nt = nt.replace(/\s\s/g, "  "); - nt = nt.replace(/\[\:newline\:\]/g, "
"); - - // then we actually put the updated text in - skipOneUpdate = true - txtMessage.text = nt - - txtMessage.cursorPosition = previousCursor - - // autoscroll down only when the scrollbar is already all the way down - if (flkMessage.contentY + flkMessage.height >= flkMessage.contentHeight - txtMessage.height && flkMessage.contentHeight > flkMessage.height) { - flkMessage.contentY = flkMessage.contentHeight - flkMessage.height - } - } - } - } - } - - ColumnLayout { - id: colRight - spacing: 1 - - - Opaque.Button { // SEND MESSAGE BUTTON - id: btnSend - icon: "regular/paper-plane" - text: "send" - Layout.minimumWidth: btnEmoji.width + btnAttach.width + 2 - Layout.maximumWidth: btnEmoji.width + btnAttach.width + 2 - anchors.right: parent.right - anchors.rightMargin: 2 - - property int nextMessageID: 1 - - TextEdit { - id: txtHidden - visible: false - textFormat: Text.RichText - } - - onClicked: { - if (txtMessage.text != "") { - txtHidden.text = restoreEmoji(txtMessage.text) - txtHidden.text = txtHidden.text.replace(/
/g,"[:newline:]"); - var txt = txtHidden.text.trim() - if (txt.length > 0) { - var rawText = txtHidden.getText(0, txtHidden.text.length) - var msg = JSON.stringify({"o":1, "d":rawText.replace(/\[\:newline\:\]/g,"\n")}) - gcd.sendMessage(msg, nextMessageID++) - } - } - txtMessage.text = "" - } - } - - RowLayout { - spacing: 1 - - - Opaque.Button { // EMOJI DRAWER BUTTON - id: btnEmoji - icon: "regular/smile" - anchors.right: btnAttach.left - anchors.rightMargin: 2 - - onClicked: emojiDrawer.visible ? emojiDrawer.slideclosed() : emojiDrawer.slideopen() - } - - Opaque.Button { - id: btnAttach - icon: "solid/paperclip" - anchors.right: parent.right - anchors.rightMargin: 2 - - onClicked: { - gcd.popup("attachments not yet implemented, sorry") - } - } - } - } - } } + + diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 1393050b..52b8150a 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -181,10 +181,7 @@ ColumnLayout { } } - Opaque.HLine{} - - } } @@ -197,23 +194,23 @@ ColumnLayout { } Opaque.ButtonTextField { - id: newlistitem - visible:listpanel.online - readOnly: false - button_text: qsTr("add-list-item-btn") - dropShadowColor: Theme.dropShadowPaneColor - property int nextMessageID: 1 - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 10 + id: newlistitem + visible:listpanel.online + readOnly: false + button_text: qsTr("add-list-item-btn") + dropShadowColor: Theme.dropShadowPaneColor + property int nextMessageID: 1 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 10 - onClicked: { - if (newlistitem.text != "") { - var msg = JSON.stringify({"o":4, "t":newlistitem.text}) - gcd.sendMessage(msg, nextMessageID++) - } - newlistitem.text = "" + onClicked: { + if (newlistitem.text != "") { + var msg = JSON.stringify({"o":4, "t":newlistitem.text}) + gcd.sendMessage(msg, nextMessageID++) } + newlistitem.text = "" + } } diff --git a/qml/panes/OverlayPane.qml b/qml/panes/OverlayPane.qml index 453899cb..293ebd96 100644 --- a/qml/panes/OverlayPane.qml +++ b/qml/panes/OverlayPane.qml @@ -7,77 +7,102 @@ import QtQuick.Layouts 1.3 import "../opaque" as Opaque import "../opaque/styles" import "../overlays" +import "../opaque/fonts" +import "../opaque/theme" + ColumnLayout { Layout.fillWidth: true id: overlay + property string name property bool accepted property bool inGroup - RowLayout { - visible:!overlay.accepted && (gcd.selectedConversation.length == 32) + // TODO: this isn't needed now right? the peer approval flow can be addapted for groups too? + /* RowLayout { + visible:!overlay.accepted && (gcd.selectedConversation.length == 32) - Text { - //: Do you want to accept the invitation to $GROUP - text: qsTr("accept-group-invite-label") + " " + overlay.name + "?" - } - - Opaque.Button { - //: Accept group invite button - text: qsTr("accept-group-btn") - icon: "regular/heart" - onClicked: { - gcd.acceptGroup(gcd.selectedConversation) - gcd.requestGroupSettings(gcd.selectedConversation) - } - } - - Opaque.Button { - //: Reject Group invite button - text: qsTr("reject-group-btn") - icon: "regular/trash-alt" - onClicked: { - gcd.leaveGroup(gcd.selectedConversation) - theStack.pane = theStack.emptyPane - } - } + Text { + //: Do you want to accept the invitation to $GROUP + text: qsTr("accept-group-invite-label") + " " + overlay.name + "?" } + Opaque.Button { + //: Accept group invite button + text: qsTr("accept-group-btn") + icon: "regular/heart" + onClicked: { + gcd.acceptGroup(gcd.selectedConversation) + gcd.requestGroupSettings(gcd.selectedConversation) + } + } + + Opaque.Button { + //: Reject Group invite button + text: qsTr("reject-group-btn") + icon: "regular/trash-alt" + onClicked: { + gcd.leaveGroup(gcd.selectedConversation) + theStack.pane = theStack.emptyPane + } + } + }*/ + RowLayout { id: switcher + height: chatTab.height + implicitHeight: height + Layout.fillWidth: true - Opaque.Button { + property string selectedTab: "chat" + + Opaque.Tab { + id: chatTab + Layout.fillWidth: true + active: switcher.selectedTab == "chat" text: qsTr("chat-btn") - - onClicked: overlayStack.overlay = overlayStack.chatOverlay + onClicked: { switcher.selectedTab = "chat"; overlayStack.overlay = overlayStack.chatOverlay } } - Opaque.Button { + Rectangle { + width: 2 + height: parent.height + color: Theme.dividerColor + } + + Opaque.Tab { + Layout.fillWidth: true + active: switcher.selectedTab == "lists" text: qsTr("lists-btn") - - onClicked: overlayStack.overlay = overlayStack.listOverlay + onClicked: { switcher.selectedTab = "lists"; overlayStack.overlay = overlayStack.listOverlay } } - Opaque.Button { + Rectangle { + width: 2 + height: parent.height + color: Theme.dividerColor + } + + Opaque.Tab { + Layout.fillWidth: true + active: switcher.selectedTab == "bullitin" text: qsTr("bulletins-btn") - - onClicked: overlayStack.overlay = overlayStack.bulletinOverlay + onClicked: { switcher.selectedTab = "bullitin"; overlayStack.overlay = overlayStack.bulletinOverlay } } } StackLayout { id: overlayStack - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.top: switcher.bottom + Layout.fillWidth: true + + implicitHeight: height currentIndex: 0 diff --git a/qml/panes/PeerSettingsPane.qml b/qml/panes/PeerSettingsPane.qml index ad7d0494..5d8bbd64 100644 --- a/qml/panes/PeerSettingsPane.qml +++ b/qml/panes/PeerSettingsPane.qml @@ -104,7 +104,7 @@ Opaque.SettingsList { // settingsPane if (item["value"] == "SaveHistory") { gcd.storeHistoryForPeer(txtOnion.text) } else { - gcd.deleteHistoryForPeer(txtOnion.text) + gcd.deleteHistoryForPeer(txtOnion.text) } } diff --git a/qml/utils.js b/qml/utils.js index 1a200699..63fe4f4d 100644 --- a/qml/utils.js +++ b/qml/utils.js @@ -44,4 +44,12 @@ function isGridOccupied(x, y, points) { } } return inPoints +} + +function isGroup(id) { + return id.length == 32 +} + +function isPeer(id) { + return id.length == 56 } \ No newline at end of file diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index 157f9b56..e0165564 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -64,34 +64,15 @@ ColumnLayout { } - Flickable { // THE ACTUAL CONTACT LIST + Opaque.Flickable { // THE ACTUAL CONTACT LIST id: sv - //Layout.alignment: Qt.AlignLeft | Qt.AlignTop - clip: true + Layout.minimumHeight: 100 - //Layout.maximumHeight: parent.height - 30 Layout.fillHeight: true Layout.minimumWidth: parent.width Layout.maximumWidth: parent.width contentWidth: parent.width contentHeight: colContacts.height - boundsBehavior: Flickable.StopAtBounds - - ScrollBar.vertical: ScrollBar { - policy: ScrollBar.AsNeeded - background: Rectangle { - implicitWidth: 6 - - color: Theme.backgroundMainColor - } - contentItem: Rectangle { - implicitWidth: 6 - implicitHeight:1 - color: Theme.backgroundPaneColor - } - } - - ColumnLayout { id: colContacts @@ -227,13 +208,13 @@ ColumnLayout { anchors.fill: blockItem color: Theme.backgroundMainColor - Connections { + Connections { target: Theme onThemeChanged: { blockedBG.color = Theme.backgroundMainColor } - } + } } Row { @@ -263,12 +244,12 @@ ColumnLayout { } Connections { - target: gcd + target: gcd - onUpdateMyProfile: function(_nick, _onion, _image, _tag, _showBlocked) { - blockedToggle.showing = (_showBlocked == "true") - blockedContacts.visible = (_showBlocked == "true") - } + onUpdateMyProfile: function(_nick, _onion, _image, _tag, _showBlocked) { + blockedToggle.showing = (_showBlocked == "true") + blockedContacts.visible = (_showBlocked == "true") + } } } @@ -277,7 +258,7 @@ ColumnLayout { id: blockedContactsModel } - ColumnLayout { + ColumnLayout { id: blockedContacts Layout.fillWidth: true spacing: 0 diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 4be5090b..cc6aeaa6 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -82,15 +82,12 @@ Opaque.PortraitRow { } } - onClicked: function(handle) { + onClicked: function() { gcd.broadcast("ResetMessagePane") isActive = true theStack.pane = theStack.messagePane gcd.loadMessagesPane(handle) badge = 0 - if (handle.length == 32) { - gcd.requestGroupSettings(handle) - } } Component.onCompleted: { setColors(status) } @@ -105,12 +102,12 @@ Opaque.PortraitRow { portraitColor = Theme.portraitBlockedBackgroundColor nameColor = Theme.portraitBlockedTextColor onionColor = Theme.portraitBlockedTextColor - } else if (status == 4 || status == 3) { + } else if (status == Const.state_synced || status == Const.state_authenticated) { portraitBorderColor = Theme.portraitOnlineBorderColor portraitColor = Theme.portraitOnlineBackgroundColor nameColor = Theme.portraitOnlineTextColor onionColor = Theme.portraitOnlineTextColor - } else if (status == 2 || status == 1) { + } else if (status == Const.state_connected || status == Const.state_connecting) { portraitBorderColor = Theme.portraitConnectingBorderColor portraitColor = Theme.portraitConnectingBackgroundColor nameColor = Theme.portraitConnectingTextColor diff --git a/qml/widgets/Message.qml b/qml/widgets/Message.qml index f20194e4..e8d2788d 100644 --- a/qml/widgets/Message.qml +++ b/qml/widgets/Message.qml @@ -13,6 +13,7 @@ Rectangle { id: root height: Math.max(imgProfile.height, rectMessageBubble.height) + color: Theme.backgroundMainColor property string message property string rawMessage @@ -92,8 +93,6 @@ Rectangle { 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" diff --git a/qml/widgets/MessageEditor.qml b/qml/widgets/MessageEditor.qml new file mode 100644 index 00000000..4e499f7a --- /dev/null +++ b/qml/widgets/MessageEditor.qml @@ -0,0 +1,291 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 + +import "../opaque" as Opaque +import "../opaque/controls" as Awesome +import "../opaque/fonts/Twemoji.js" as T +import "../utils.js" as Utils +import "../widgets" +import "../opaque/theme" +import "../const" +import "../opaque/fonts" + + +ColumnLayout { + id: root + + property int state: Const.state_disconnected + property string authorization: "" + + signal sendClicked(string messageText) + + + function updateState() { + + // TODO unapproved + if (root.authorization == Const.auth_blocked) { + statusSeperator.color = Theme.messageStatusBlockedColor + //: Peer is blocked + statusText.text = qsTr("peer-blocked-message") + statusText.color = Theme.messageStatusBlockedTextColor + txtMessage.enabled = false + btnSend.enabled = false + btnAttach.enabled = false + btnEmoji.enabled = false + } else if ( (Utils.isGroup(gcd.selectedConversation) && root.state == Const.state_synced) || (Utils.isPeer(gcd.selectedConversation) && root.state == Const.state_authenticated) ) { + statusSeperator.color = Theme.messageStatusNormalColor + statusText.text = "" + txtMessage.enabled = true + btnSend.enabled = true + btnSend.enabled = true + btnAttach.enabled = true + btnEmoji.enabled = true + } else { + statusSeperator.color = Theme.messageStatusAlertColor + //: Peer is offline, messages can't be delivered right now + statusText.text = qsTr("peer-offline-message") + statusText.color = Theme.messageStatusAlertTextColor + txtMessage.enabled = false + btnSend.enabled = false + btnSend.enabled = false + btnAttach.enabled = false + btnEmoji.enabled = false + } + } + + Opaque.EmojiDrawer { + id: emojiDrawer + Layout.fillWidth: true + size: 24 * gcd.themeScale + + onPicked: function(shortcode) { + if (!txtMessage.enabled) return + txtMessage.insert(txtMessage.cursorPosition, ":" + shortcode + ":") + } + } + + Rectangle { + id: statusSeperator + + Layout.fillWidth: true + + height: statusText.visible ? statusText.height + (4 * gcd.themeScale) : 3 * gcd.themeScale + implicitHeight: height + color: Theme.dividerColor + + Opaque.ScalingLabel { + id: statusText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: "" + visible: text != "" + size: Theme.chatMetaTextSize + font.family: Fonts.applicationFontRegular.name + font.styleName: "Bold" + } + } + + RowLayout { // THE BOTTOM DRAWER + id: rowDrawer + Layout.fillWidth: true + + Rectangle { // MESSAGE ENTRY TEXTFIELD + id: rectMessage + Layout.fillWidth: true + Layout.minimumHeight: 120 * gcd.themeScale + Layout.maximumHeight: 120 * gcd.themeScale + color: Theme.backgroundMainColor + + Opaque.Flickable { + id: flkMessage + anchors.fill: parent //this does nothing! bug in qt + contentWidth: txtMessage.width + contentHeight: txtMessage.height + txtma.height + + TextArea { + id: txtMessage + font.pixelSize: Theme.chatSize * gcd.themeScale + text: "" + padding: 6 * gcd.themeScale + wrapMode: TextEdit.Wrap + textFormat: Text.RichText + width: rectMessage.width + color: Theme.mainTextColor + + property bool skipOneUpdate: false + property int previousCursor + + Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK // TODO: Broken + if ((event.modifiers & Qt.ControlModifier) && gcd.os != "android") { + txtMessage.insert(txtMessage.cursorPosition, "
") + } else if (event.modifiers == Qt.NoModifier) { + btnSend.clicked() + } + } + + // welcome to the emoji parser! it is horrifying code that needs to leave in + // while also stripping any other tag, including other images. + // TODO: this probably breaks if people actually do want to paste html + onTextChanged: { + if (gcd.os == "android") { + return + } + + // we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally + // passing text into it to be filtered. this prevents recursive calls putting us into an + // infinite loop + if (skipOneUpdate) { + skipOneUpdate = false + return + } + + previousCursor = cursorPosition + //console.log("onTextChanged() at position " + previousCursor) + + //console.log("1: " + txtMessage.getText(0, txtMessage.text.length)) + + // convert tags back to their emoji form + // Then parse out the rest of the HTML + var nt = restoreEmoji(txtMessage.text) + if (nt != txtMessage.text) { + skipOneUpdate = true + txtMessage.text = nt + } + + //console.log("2: " + txtMessage.getText(0, txtMessage.text.length)) + var preserveSpaces = txtMessage.text.replace(/
/g,"[:newline:]"); + if (preserveSpaces != txtMessage.text) { + skipOneUpdate = true + txtMessage.text = preserveSpaces + } + // strip all HTML tags + var theText = Utils.htmlEscaped(txtMessage.getText(0, txtMessage.text.length)) + //console.log("3: " + theText) + + // convert emoji back to tags + nt = parse(theText, 10) + //console.log("4: " + nt) + + // preserve double spacing + nt = nt.replace(/\s\s/g, "  "); + nt = nt.replace(/\[\:newline\:\]/g, "
"); + + // then we actually put the updated text in + skipOneUpdate = true + txtMessage.text = nt + + txtMessage.cursorPosition = previousCursor + + // autoscroll down only when the scrollbar is already all the way down + if (flkMessage.contentY + flkMessage.height >= flkMessage.contentHeight - txtMessage.height && flkMessage.contentHeight > flkMessage.height) { + flkMessage.contentY = flkMessage.contentHeight - flkMessage.height + } + } + } + + MouseArea { + id: txtma + anchors.top: txtMessage.bottom + width: flkMessage.width + height: Math.max(rectMessage.height - txtMessage.height, 0) + + onClicked: { txtMessage.focus = true } + } + } + } + + ColumnLayout { + id: colRight + spacing: 0 + width: 100 * gcd.themeScale + + Layout.minimumWidth: width + Layout.preferredWidth: width + Layout.maximumWidth: width + + Opaque.Icon { // SEND MESSAGE BUTTON + id: btnSend + source: gcd.assetPath + "core/send-24px.svg" + width: colRight.width + height: 50 * gcd.themeScale + size: 36 * gcd.themeScale + + backgroundColor: enabled ? Theme.defaultButtonColor : Theme.defaultButtonDisabledColor + hilightBackgroundColor: Theme.defaultButtonActiveColor + iconColor: Theme.defaultButtonTextColor + + property int nextMessageID: 1 + + TextEdit { + id: txtHidden + visible: false + textFormat: Text.RichText + } + + onClicked: { + // Cannot use .text b/c in rich text mode it is always full of html + if (txtMessage.length != 0) { + txtHidden.text = restoreEmoji(txtMessage.text) + txtHidden.text = txtHidden.text.replace(/
/g,"[:newline:]"); + + var txt = txtHidden.text.trim() + if (txt.length > 0) { + var rawText = txtHidden.getText(0, txtHidden.text.length) + + root.sendClicked(rawText) + } + } + txtMessage.text = "" + } + } + + RowLayout { + Layout.alignment: Qt.AlignHCenter + spacing: 0 + + Opaque.Icon { // EMOJI DRAWER BUTTON + id: btnEmoji + source: gcd.assetPath + "core/mood-24px.svg" + + size: 25 + height: 36 * gcd.themeScale + width: 48 * gcd.themeScale + + backgroundColor: enabled ? Theme.backgroundMainColor : Theme.backgroundPaneColor + hilightBackgroundColor: Theme.backgroundPaneColor + iconColor: Theme.dividerColor + + onClicked: emojiDrawer.visible ? emojiDrawer.slideclosed() : emojiDrawer.slideopen() + } + + Opaque.Icon { + id: btnAttach + source: gcd.assetPath + "core/attach_file-24px.svg" + + size: 25 + height: 36 * gcd.themeScale + width: 48 * gcd.themeScale + + backgroundColor: enabled ? Theme.backgroundMainColor : Theme.backgroundPaneColor + hilightBackgroundColor: Theme.backgroundPaneColor + iconColor: Theme.dividerColor + + onClicked: { + gcd.popup("attachments not yet implemented, sorry") + } + } + } + } + } + + Connections { + target: gcd + + onClearMessages: function() { + txtMessage.text = "" + } + } +} diff --git a/qml/widgets/Overlay.qml b/qml/widgets/Overlay.qml new file mode 100644 index 00000000..3334258b --- /dev/null +++ b/qml/widgets/Overlay.qml @@ -0,0 +1,103 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 + +import "../opaque" as Opaque +import "../opaque/controls" as Awesome +import "../opaque/fonts/Twemoji.js" as T +import "../utils.js" as Utils +import "../widgets" +import "../opaque/theme" +import "../const" + + +Item { + id: root + Layout.fillWidth: true + property bool online: false + + property int state: Const.state_disconnected + property string authorization: "" + + + signal sendClicked(string messateText) + property alias contentItem: control.contentItem + + Control { + id: control + width: parent.width + anchors.top: parent.top + anchors.topMargin: 10 * gcd.themeScale + + anchors.bottom: msgEd.top + anchors.bottomMargin: 10 * gcd.themeScale + + horizontalPadding: 15 * gcd.themeScale + + } + + + MessageEditor { + id: msgEd + + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left + anchors.rightMargin: 15 * gcd.themeScale + anchors.leftMargin: 15 * gcd.themeScale + + onSendClicked: function(messageText) { + root.sendClicked(messageText) + } + } + + function updateState() { + if (root.authorization == Const.auth_blocked) { + // Blocked + + } else if ( (Utils.isGroup(gcd.selectedConversation) && root.state == Const.state_synced) || (Utils.isPeer(gcd.selectedConversation) && root.state == Const.state_authenticated) ) { + // Online + + } else { + // Not Online + + } + } + + Connections { + target: gcd + + onUpdateContactStatus: function(_handle, _status, _loading) { + if (gcd.selectedConversation == _handle) { + root.state = _status + msgEd.state = _status + updateState() + msgEd.updateState() + } + } + + onSupplyPeerSettings: function(onion, nick, authorization, storage) { + // TODO unknown + root.authorization = authorization + msgEd.authorization = authorization + updateState() + msgEd.updateState() + } + + onSupplyGroupSettings: function(groupID, nick, groupServer, invite, accepted, contactnames, contactaddrs) { + if (accepted) { + root.authorization = Const.auth_approved + msgEd.authorization = Const.auth_approved + } else { + root.authorization = Const.auth_unknown + msgEd.authorization = Const.auth_unknown + } + updateState() + msgEd.updateState() + } + } +} + + diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index 1931872d..624a53e9 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -18,23 +18,16 @@ ColumnLayout { } } - Flickable { // Profile List + Opaque.Flickable { // Profile List id: sv - clip: true Layout.minimumHeight: 100 Layout.fillHeight: true Layout.minimumWidth: parent.width Layout.maximumWidth: parent.width contentWidth: colContacts.width contentHeight: colContacts.height - boundsBehavior: Flickable.StopAtBounds - maximumFlickVelocity: 400 - ScrollBar.vertical: ScrollBar { - policy: ScrollBar.AlwaysOn - } - ColumnLayout { id: colContacts width: sv.width