This repository has been archived on 2021-06-24. You can view files and clone it, but cannot push or open issues or pull requests.
ui/qml/widgets/ContactList.qml

305 lines
9.9 KiB
QML

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/theme"
import "../const"
ColumnLayout {
id: root
property alias dualPane: myprof.dualPane
property real logscale: 4 * Math.log10(gcd.themeScale + 1)
spacing: 10
MouseArea {
anchors.fill: parent
onClicked: {
forceActiveFocus()
}
}
MyProfile { // CURRENT PROFILE INFO AND CONTROL BAR
id: myprof
}
function filterContact(displayName, handle) {
return searchAddText.text == "" || displayName.toLowerCase().includes(searchAddText.text.toLowerCase()) || handle.toLowerCase().startsWith(searchAddText.text.toLowerCase())
}
function removeContact(model, handle) {
for(var i = 0; i < model.count; i++){
if(model.get(i)["_handle"] == handle) {
model.remove(i)
return true
}
}
return false
}
Opaque.IconTextField {
id: searchAddText
anchors.horizontalCenter: parent.horizontalCenter
Layout.minimumWidth: parent.width - 60
Layout.maximumWidth: parent.width - 60
//: ex: "... paste an address here to add a contact ..."
//placeholderText: qsTr("paste-address-to-add-contact")
horizontalAlignment: TextInput.AlignHCenter
icon: gcd.assetPath + "core/search-24px.webp"
onTextChanged: {
// TODO: detect peer or group address and insert a contactRow that asks to add the corresponding group or peer
/*if (text != "") {
gcd.importString(text)
text = ""
}*/
}
}
Opaque.Flickable { // THE ACTUAL CONTACT LIST
id: sv
Layout.minimumHeight: 100
Layout.fillHeight: true
Layout.minimumWidth: parent.width
Layout.maximumWidth: parent.width
contentWidth: parent.width
contentHeight: colContacts.height
ColumnLayout {
id: colContacts
width: root.width
spacing: 0
Connections { // ADD/REMOVE CONTACT ENTRIES
target: gcd
onAddContact: function(handle, displayName, image, badge, status, authorization, loading, lastMsgTs) {
var model = contactsModel
if (authorization == Const.auth_blocked) {
model = blockedContactsModel
}
// If contact already in model, skip
for (var i = 0; i < model.count; i++) {
if (model.get(i)["_handle"] == handle) {
return
}
}
// Sort order: [unknown,authed][most recent message]
var index = model.count
for (var i = 0; i < model.count; i++) {
var contact = model.get(i)
if (contact["_authorization"] == authorization) {
if (contact["_lastMsgTs"] < lastMsgTs) {
index = i
break
}
} else if (authorization == Const.auth_unknown) {
index = i
break
}
}
var newContact = {
"_handle": handle,
"_displayName": displayName,
"_image": image,
"_badge": badge,
"_status": status,
"_authorization": authorization,
"_loading": loading,
"_lastMsgTs": lastMsgTs,
}
model.insert(index, newContact)
}
onRemoveContact: function(handle) {
var removed = root.removeContact(contactsModel, handle)
if (!removed) {
root.removeContact(blockedContactsModel, handle)
}
}
/*onIncContactUnreadCount: function(handle) {
var ts = Math.round((new Date()).getTime() / 1000);
for(var i = 0; i < contactsModel.count; i++){
if(contactsModel.get(i)["_handle"] == handle) {
var contact = contactsModel.get(i)
contact["_lastMsgTs"] = ts
contactsModel.move(i, 0, 1)
}
}
}*/
onIncContactMessageCount: function(profile, handle) {
if (profile == gcd.selectedProfile) {
var ts = Math.round((new Date()).getTime() / 1000);
for(var i = 0; i < contactsModel.count; i++){
if(contactsModel.get(i)["_handle"] == handle) {
var contact = contactsModel.get(i)
contact["_lastMsgTs"] = ts
contactsModel.move(i, 0, 1)
}
}
}
}
onResetProfile: function() {
contactsModel.clear()
blockedContactsModel.clear()
}
}
ListModel { // CONTACT OBJECTS ARE STORED HERE ...
id: contactsModel
}
Repeater {
id: contactRepeater
model: contactsModel // ... AND DISPLAYED HERE
delegate: ContactRow {
handle: _handle
displayName: _displayName
image: _image
badge: _badge
status: _status
authorization: _authorization
loading: _loading
rowColor: (_authorization == Const.auth_unknown) ? Theme.backgroundHilightElementColor : Theme.backgroundMainColor
Layout.fillWidth: true
visible: filterContact(displayName, handle)
isActive: gcd.selectedConversation == _handle
}
}
Item {
id: blockItem
height: blockedToggle.height
Layout.fillWidth: true
visible: blockedContactsModel.count > 0
MouseArea {
anchors.fill: blockItem
hoverEnabled: true
onClicked: {
blockedToggle.showing = !blockedToggle.showing
blockedContacts.visible = blockedToggle.showing
if (blockedToggle.showing) {
gcd.storeSetting(Const.show_blocked, "true")
} else {
gcd.storeSetting(Const.show_blocked, "false")
}
}
onEntered: {
blockedBG.color = Theme.backgroundPaneColor
}
onExited: {
blockedBG.color = Theme.backgroundMainColor
}
}
Rectangle {
id: blockedBG
property bool isHover: false
anchors.fill: blockItem
color: Theme.backgroundMainColor
Connections {
target: Theme
onThemeChanged: {
blockedBG.color = Theme.backgroundMainColor
}
}
}
Row {
id: blockedToggle
property bool showing: true
leftPadding: 32 * logscale
topPadding: 16 * logscale
bottomPadding: 8 * logscale
spacing: 5 * logscale
Opaque.ScalingLabel {
id: blockLbl
text: qsTr("blocked")
size: Theme.chatMetaTextSize
color: Theme.portraitBlockedTextColor
}
Opaque.ScalingLabel {
id: blockBtn
text: blockedToggle.showing ? "▲" : "▼"
size: Theme.chatMetaTextSize
color: Theme.portraitBlockedTextColor
}
}
Connections {
target: gcd
onUpdateMyProfile: function(_nick, _onion, _image, _tag, _showBlocked) {
blockedToggle.showing = (_showBlocked == "true")
blockedContacts.visible = (_showBlocked == "true")
}
}
}
ListModel {
id: blockedContactsModel
}
ColumnLayout {
id: blockedContacts
Layout.fillWidth: true
spacing: 0
Repeater {
id: blockedContactsRepeater
model: blockedContactsModel // ... AND DISPLAYED HERE
delegate: ContactRow {
handle: _handle
displayName: _displayName
image: _image
badge: _badge
status: _status
authorization: _authorization
loading: _loading
Layout.fillWidth: true
visible: filterContact(displayName, handle)
}
}
}
}
}
}