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
-
+
@@ -140,7 +140,7 @@
-
+
@@ -157,28 +157,43 @@
Message
-
+
Click to DM
Klicken, um DM zu senden
-
+
Could not send this message
Nachricht konnte nicht gesendet werden
-
+
bestätigt
-
+
Bestätigung ausstehend
+
+ MessageEditor
+
+
+
+ Peer is blockced
+
+
+
+
+
+ Peer is offline, messages can't be delivered right now
+
+
+
MyProfile
@@ -205,35 +220,32 @@
OverlayPane
-
Do you want to accept the invitation to $GROUP
- Möchtest Du die Einladung annehmen
+ Möchtest Du die Einladung annehmen
-
Accept group invite button
- Annehmen
+ Annehmen
-
Reject Group invite button
- Ablehnen
+ Ablehnen
-
+
Chat
-
+
Listen
-
+
Meldungen
@@ -271,34 +283,29 @@
speichern
-
-
+
+
Save Peer History
-
+
-
-
-
-
-
-
+
-
+
löschen
-
+
@@ -411,7 +418,7 @@
ProfileList
-
+
@@ -487,34 +494,34 @@
-
+
Groß
-
+
Theme
-
+
-
+
-
+
Version %1
-
+
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
@@ -212,17 +212,17 @@ Right-click to reset.
ex: "... paste an address here to add a contact ..."
- Search List
+ Search List
- Peer is Offline. Applications cannot be used right now.
+ Peer is Offline. Applications cannot be used right now.
-
+
- Add Item
+ Add Item
@@ -237,28 +237,43 @@ Right-click to reset.
Message
-
+
Click to DM
Click to DM
-
+
Could not send this message
Could not send this message
-
+
Acknowledged
-
+
Pending
+
+ MessageEditor
+
+
+
+ Peer is blockced
+ Peer is blocked
+
+
+
+
+ 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
-
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 invite button
- Accept
+ Accept
-
Reject Group invite button
- Reject
+ Reject
-
+
Chat
-
+
Lists
-
+
Bulletins
@@ -351,38 +363,33 @@ Right-click to reset.
Save
-
+
Block Peer
-
-
+
+
Save Peer History
- Save Peer History
+ Save Peer History
-
+
- Determines whether or not to delete any history associated with the peer.
+ Determines whether or not to delete any history associated with the peer.
-
-
-
-
-
-
+
- Delete Peer History
+ Delete Peer History
Unblock Peer
-
+
Delete
@@ -503,7 +510,7 @@ Right-click to reset.
ProfileList
-
+
Add new profile
@@ -588,34 +595,34 @@ Right-click to reset.
Zoom level
-
+
Large
-
+
Theme
Theme
-
+
Light
-
+
Dark
-
+
Version %1
Version %1
-
+
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...
-
+
@@ -140,7 +140,7 @@
-
+
@@ -157,28 +157,43 @@
Message
-
+
Click to DM
Envoyer un message privé
-
+
Could not send this message
Impossible d'envoyer ce message
-
+
Confirmé
-
+
En attente
+
+ MessageEditor
+
+
+
+ Peer is blockced
+
+
+
+
+
+ Peer is offline, messages can't be delivered right now
+
+
+
MyProfile
@@ -205,35 +220,32 @@
OverlayPane
-
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 invite button
- Accepter
+ Accepter
-
Reject Group invite button
- Refuser
+ Refuser
-
+
Discuter
-
+
Listes
-
+
Bulletins
@@ -271,34 +283,29 @@
Sauvegarder
-
-
+
+
Save Peer History
-
+
-
-
-
-
-
-
+
-
+
Effacer
-
+
@@ -411,7 +418,7 @@
ProfileList
-
+
@@ -487,34 +494,34 @@
-
+
Large
-
+
Theme
-
+
-
+
-
+
Version %1
-
+
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…
-
+
@@ -140,7 +140,7 @@
-
+
@@ -157,28 +157,43 @@
Message
-
+
Click to DM
Clique para DM
-
+
Could not send this message
Não deu para enviar esta mensagem
-
+
Confirmada
-
+
Pendente
+
+ MessageEditor
+
+
+
+ Peer is blockced
+
+
+
+
+
+ Peer is offline, messages can't be delivered right now
+
+
+
MyProfile
@@ -205,35 +220,32 @@
OverlayPane
-
Do you want to accept the invitation to $GROUP
- Você quer aceitar o convite para
+ Você quer aceitar o convite para
-
Accept group invite button
- Aceitar
+ Aceitar
-
Reject Group invite button
- Recusar
+ Recusar
-
+
Chat
-
+
Listas
-
+
Boletins
@@ -271,34 +283,29 @@
Salvar
-
-
+
+
Save Peer History
-
+
-
-
-
-
-
-
+
-
+
Deletar
-
+
@@ -411,7 +418,7 @@
ProfileList
-
+
@@ -487,34 +494,34 @@
-
+
Grande
-
+
Theme
-
+
-
+
-
+
Version %1
-
+
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