feat: Create group chats
This commit is contained in:
parent
73520d1796
commit
4fef70fc4a
|
@ -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)
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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: @[]))
|
|
@ -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])
|
|
@ -32,6 +32,10 @@ Item {
|
|||
id: publicChatPopup
|
||||
}
|
||||
|
||||
GroupChatPopup {
|
||||
id: groupChatPopup
|
||||
}
|
||||
|
||||
PrivateChatPopup {
|
||||
id: privateChatPopup
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue