feat: Create group chats

This commit is contained in:
Richard Ramos 2020-06-17 11:56:48 -04:00 committed by Iuri Matias
parent 73520d1796
commit 4fef70fc4a
14 changed files with 350 additions and 7 deletions

View File

@ -1,6 +1,6 @@
import NimQml
import Tables
import json
import json, sequtils
import chronicles
import ../../status/status
@ -146,3 +146,7 @@ QtObject:
proc addContact*(self: ChatsView, id: string): string {.slot.} =
return self.status.contacts.addContact(id)
proc createGroup*(self: ChatsView, groupName: string, pubKeys: string) {.slot.} =
let pubKeysSeq = map(parseJson(pubKeys).getElems(), proc(x:JsonNode):string = x.getStr)
self.status.chat.createGroup(groupName, pubKeysSeq)

View File

@ -6,9 +6,10 @@ from ../../../status/ens import nil
type
ContactRoles {.pure.} = enum
Name = UserRole + 1,
Address = UserRole + 2
Identicon = UserRole + 3
PubKey = UserRole + 1
Name = UserRole + 2,
Address = UserRole + 3
Identicon = UserRole + 4
QtObject:
type ContactList* = ref object of QAbstractListModel
@ -41,6 +42,14 @@ QtObject:
return getUserName(contact)
return defaultValue
proc rowData(self: ContactList, index: int, column: string): string {.slot.} =
let contact = self.contacts[index]
case column:
of "name": result = getUserName(contact)
of "address": result = contact.address
of "identicon": result = contact.identicon
of "pubKey": result = contact.id
method data(self: ContactList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
@ -51,12 +60,14 @@ QtObject:
of ContactRoles.Name: result = newQVariant(getUserName(contact))
of ContactRoles.Address: result = newQVariant(contact.address)
of ContactRoles.Identicon: result = newQVariant(contact.identicon)
of ContactRoles.PubKey: result = newQVariant(contact.id)
method roleNames(self: ContactList): Table[int, string] =
{
ContactRoles.Name.int:"name",
ContactRoles.Address.int:"address",
ContactRoles.Identicon.int:"identicon",
ContactRoles.PubKey.int:"pubKey"
}.toTable
proc addContactToList*(self: ContactList, contact: Profile) =

View File

@ -197,4 +197,9 @@ proc getUserName*(self: ChatModel, id: string, defaultUserName: string):string =
proc updateContacts*(self: ChatModel, contacts: seq[Profile]) =
for c in contacts:
self.contacts[c.id] = c
self.events.emit("chatUpdate", ChatUpdateArgs(contacts: contacts))
self.events.emit("chatUpdate", ChatUpdateArgs(contacts: contacts))
proc createGroup*(self: ChatModel, groupName: string, pubKeys: seq[string]) =
var response = parseJson(status_chat.createGroup(groupName, pubKeys))
var (chats, messages) = formatChatUpdate(response)
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))

View File

@ -102,3 +102,6 @@ proc leaveGroupChat*(chatId: string): string =
proc renameGroup*(chatId: string, newName: string): string =
callPrivateRPC("changeGroupChatName".prefix, %* [nil, chatId, newName])
proc createGroup*(groupName: string, pubKeys: seq[string]): string =
callPrivateRPC("createGroupChatWithMembers".prefix, %* [nil, groupName, pubKeys])

View File

@ -32,6 +32,10 @@ Item {
id: publicChatPopup
}
GroupChatPopup {
id: groupChatPopup
}
PrivateChatPopup {
id: privateChatPopup
}

View File

@ -89,7 +89,7 @@ Rectangle {
text: qsTr("Start group chat")
icon.source: "../../../img/group_chat.svg"
onTriggered: {
console.log("TODO: Start group chat")
onTriggered: groupChatPopup.open()
}
}
QQC2.Action {

View File

@ -0,0 +1,73 @@
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import Qt.labs.platform 1.1
import "../../../../imports"
import "../../../../shared"
Rectangle {
property string pubKey: "0x123456"
property string name: "Jotaro Kujo"
property string address: "0x04d8c07dd137bd1b73a6f51df148b4f77ddaa11209d36e43d8344c0a7d6db1cad6085f27cfb75dd3ae21d86ceffebe4cf8a35b9ce8d26baa19dc264efe6d8f221b"
property string identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
property bool isUser: false
property bool isVisible: true
property bool showCheckbox: true
property bool isChecked: false
property var onItemChecked: (function(pubKey, itemChecked) { console.log(pubKey, itemChecked) })
visible: isVisible
height: isVisible ? 64 : 0
anchors.right: parent.right
anchors.left: parent.left
border.width: 0
radius: Theme.radius
RoundImage {
id: accountImage
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
source: identicon
}
Text {
id: usernameText
text: name
elide: Text.ElideRight
anchors.right: parent.right
anchors.rightMargin: Theme.padding
font.pixelSize: 17
anchors.top: accountImage.top
anchors.topMargin: 10
anchors.left: accountImage.right
anchors.leftMargin: Theme.padding
}
CheckBox {
id: assetCheck
visible: showCheckbox && !isUser
anchors.top: accountImage.top
anchors.topMargin: 6
anchors.right: parent.right
anchors.rightMargin: Theme.padding
checked: isChecked
onClicked: {
isChecked = !isChecked
onItemChecked(pubKey, isChecked)
}
}
Text {
visible: isUser
text: qsTr("Admin")
anchors.right: parent.right
anchors.rightMargin: Theme.padding
font.pixelSize: 15
color: Theme.darkGrey
anchors.top: accountImage.top
anchors.topMargin: 10
}
}

View File

@ -0,0 +1,226 @@
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQml.Models 2.3
import "../../../../imports"
import "../../../../shared"
import "./"
ModalPopup {
id: popup
property var pubKeys: []
property bool selectChatMembers: true
property int memberCount: 1
readonly property int maxMembers: 10
onOpened: {
groupName.text = "";
searchBox.text = "";
selectChatMembers = true;
memberCount = 1;
pubKeys = [];
btnSelectMembers.state = "inactive";
for(var i in groupMembers.contentItem.children){
if (groupMembers.contentItem.children[i].isChecked !== null) {
groupMembers.contentItem.children[i].isChecked = false
}
}
data.clear();
for(let i = 0; i < profileModel.contactList.rowCount(); i++){
data.append({
name: profileModel.contactList.rowData(i, "name"),
pubKey: profileModel.contactList.rowData(i, "pubKey"),
address: profileModel.contactList.rowData(i, "address"),
identicon: profileModel.contactList.rowData(i, "identicon"),
isUser: false
});
}
data.append({
name: profileModel.profile.username + " " + qsTr("(You)"),
pubKey: profileModel.profile.pubKey,
address: profileModel.profile.address,
identicon: profileModel.profile.identicon,
isUser: true
});
}
function doJoin(){
if(pubKeys.length === 0) return;
chatsModel.createGroup(groupName.text, JSON.stringify(pubKeys));
popup.close();
}
header: Item {
height: 30
width: parent.width
Text {
id: lblNewGroup
text: qsTr("New group chat")
anchors.left: parent.left
font.bold: true
font.pixelSize: 17
anchors.top: parent.top
anchors.topMargin: Theme.padding
}
Text {
anchors.top: lblNewGroup.bottom
text: qsTr("%1 / 10 members").arg(memberCount)
color: Theme.darkGrey
font.family: "Inter"
font.pixelSize: 15
}
}
Input {
id: searchBox
visible: selectChatMembers
placeholderText: qsTr("Search")
icon: "../../../img/search.svg"
iconWidth: 17
iconHeight: 17
onKeyReleased: function(){
}
}
Input {
id: groupName
placeholderText: qsTr("Group name")
visible: !selectChatMembers
}
ScrollView {
anchors.fill: parent
anchors.topMargin: 50
anchors.top: searchBox.bottom
Layout.fillWidth: true
Layout.fillHeight: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: groupMembers.contentHeight > groupMembers.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
ListView {
anchors.fill: parent
model: ListModel {
id: data
}
spacing: 0
clip: true
id: groupMembers
delegate: Contact {
isVisible: {
if(selectChatMembers){
return searchBox.text == "" || model.name.includes(searchBox.text)
} else {
return isChecked || isUser
}
}
showCheckbox: selectChatMembers && memberCount < maxMembers
pubKey: model.pubKey
isUser: model.isUser
name: model.name
address: model.address
identicon: model.identicon
onItemChecked: function(pubKey, itemChecked){
var idx = pubKeys.indexOf(pubKey)
if(itemChecked){
if(idx == -1){
pubKeys.push(pubKey)
}
} else {
if(idx > -1){
pubKeys.splice(idx, 1);
}
}
memberCount = pubKeys.length + 1;
btnSelectMembers.state = pubKeys.length > 0 ? "active" : "inactive"
}
}
}
}
footer: Item {
anchors.top: parent.bottom
anchors.right: parent.right
anchors.bottom: popup.bottom
anchors.left: parent.left
Button {
id: btnSelectMembers
visible: selectChatMembers
width: 44
height: 44
anchors.bottom: parent.bottom
anchors.right: parent.right
state: "inactive"
states: [
State {
name: "inactive"
PropertyChanges {
target: btnSelectMembersImg
source: "../../../img/arrow-right-btn-inactive.svg"
}
},
State {
name: "active"
PropertyChanges {
target: btnSelectMembersImg
source: "../../../img/arrow-right-btn-active.svg"
}
}
]
Image {
id: "btnSelectMembersImg"
}
background: Rectangle {
color: "transparent"
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked : {
if(pubKeys.length > 0)
selectChatMembers = false
searchBox.text = ""
groupName.forceActiveFocus(Qt.MouseFocusReason)
}
}
}
Button {
id: btnBack
visible: !selectChatMembers
width: 44
height: 44
anchors.bottom: parent.bottom
anchors.left: parent.left
Image {
source: "../../../img/arrow-left-btn-active.svg"
}
background: Rectangle {
color: "transparent"
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked : {
selectChatMembers = true
}
}
}
StyledButton {
visible: !selectChatMembers
anchors.bottom: parent.bottom
anchors.right: parent.right
label: qsTr("Create Group Chat")
disabled: groupName.text === ""
onClicked : doJoin()
}
}
}

View File

@ -4,4 +4,5 @@ PrivateChatPopup 1.0 PrivateChatPopup.qml
GroupInfoPopup 1.0 GroupInfoPopup.qml
ProfilePropup 1.0 ProfilePopup.qml
ChannelIcon 1.0 ChannelIcon.qml
RenameGroupPopup 1.0 RenameGroupPopup.qml
RenameGroupPopup 1.0 RenameGroupPopup.qml
GroupChatPopup 1.0 GroupChatPopup.qml

View File

@ -0,0 +1,4 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="22" cy="22" r="22" fill="#ECEFFC"/>
<path d="M20.2852 15.6187C20.6269 15.277 20.6269 14.723 20.2852 14.3813C19.9435 14.0396 19.3895 14.0396 19.0478 14.3813L12.0478 21.3813C11.7061 21.723 11.7061 22.277 12.0478 22.6187L19.0478 29.6187C19.3895 29.9604 19.9435 29.9604 20.2852 29.6187C20.6269 29.277 20.6269 28.723 20.2852 28.3813L15.7748 23.8708C15.4073 23.5033 15.6675 22.875 16.1872 22.875H31.3332C31.8164 22.875 32.2082 22.4832 32.2082 22C32.2082 21.5168 31.8164 21.125 31.3332 21.125H16.1872C15.6675 21.125 15.4073 20.4967 15.7748 20.1292L20.2852 15.6187Z" fill="#4360DF"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@ -0,0 +1,4 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="22" cy="22" r="22" fill="#ECEFFC"/>
<path d="M23.7145 15.6187C23.3727 15.277 23.3727 14.723 23.7145 14.3813C24.0562 14.0396 24.6102 14.0396 24.9519 14.3813L31.9519 21.3813C32.2936 21.723 32.2936 22.277 31.9519 22.6187L24.9519 29.6187C24.6102 29.9604 24.0562 29.9604 23.7145 29.6187C23.3727 29.277 23.3727 28.723 23.7145 28.3813L28.2249 23.8708C28.5924 23.5033 28.3321 22.875 27.8124 22.875H12.6665C12.1833 22.875 11.7915 22.4832 11.7915 22C11.7915 21.5168 12.1833 21.125 12.6665 21.125H27.8124C28.3321 21.125 28.5924 20.4967 28.2249 20.1292L23.7145 15.6187Z" fill="#4360DF"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@ -0,0 +1,4 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="22" cy="22" r="22" fill="#EEF2F5"/>
<path d="M23.7145 15.6187C23.3727 15.277 23.3727 14.723 23.7145 14.3813C24.0562 14.0396 24.6102 14.0396 24.9519 14.3813L31.9519 21.3813C32.2936 21.723 32.2936 22.277 31.9519 22.6187L24.9519 29.6187C24.6102 29.9604 24.0562 29.9604 23.7145 29.6187C23.3727 29.277 23.3727 28.723 23.7145 28.3813L28.2249 23.8708C28.5924 23.5033 28.3321 22.875 27.8124 22.875H12.6665C12.1833 22.875 11.7915 22.4832 11.7915 22C11.7915 21.5168 12.1833 21.125 12.6665 21.125H27.8124C28.3321 21.125 28.5924 20.4967 28.2249 20.1292L23.7145 15.6187Z" fill="#939BA1"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@ -65,6 +65,7 @@ DISTFILES += \
app/AppLayouts/Chat/ContactsColumn/ChannelList.qml \
app/AppLayouts/Chat/ContactsColumn/EmptyView.qml \
app/AppLayouts/Chat/ContactsColumn/qmldir \
app/AppLayouts/Chat/components/GroupChatPopup.qml \
app/AppLayouts/Chat/components/PublicChatPopup.qml \
app/AppLayouts/Chat/components/PrivateChatPopup.qml \
app/AppLayouts/Chat/components/RenameGroupPopup.qml \
@ -187,3 +188,4 @@ DISTFILES += \
shared/StyledTextArea.qml \
shared/TextWithLabel.qml \
shared/qmldir

View File

@ -16,6 +16,7 @@ Item {
property url icon: ""
property int iconHeight: 24
property int iconWidth: 24
property var onKeyReleased: (function(){})
readonly property bool hasIcon: icon.toString() !== ""
readonly property var forceActiveFocus: function () {
@ -70,6 +71,7 @@ Item {
background: Rectangle {
color: "#00000000"
}
Keys.onReleased: onKeyReleased()
}
Image {