From 3f18e6edbc9595676dbb2511ea05bd05bc27fe6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Wed, 22 Sep 2021 15:59:47 -0400 Subject: [PATCH] conversation: add swarm pages GitLab: #340 Change-Id: I4a4ebfbebb880161e93fc0a086eec13ffb1ba285 --- qml.qrc | 2 + resources/icons/swarm_details_panel.svg | 15 +++ src/constant/JamiStrings.qml | 10 ++ src/contactadapter.cpp | 4 +- src/conversationsadapter.cpp | 19 +++ src/conversationsadapter.h | 3 + src/currentconversation.cpp | 1 + src/currentconversation.h | 1 + src/mainview/MainView.qml | 29 ++++ src/mainview/components/ChatView.qml | 108 +++++++++------ src/mainview/components/ChatViewHeader.qml | 20 +++ src/mainview/components/NewSwarmPage.qml | 60 +++++++++ src/mainview/components/SidePanel.qml | 4 +- src/mainview/components/SwarmDetailsPanel.qml | 125 ++++++++++++++++++ 14 files changed, 355 insertions(+), 46 deletions(-) create mode 100644 resources/icons/swarm_details_panel.svg create mode 100644 src/mainview/components/NewSwarmPage.qml create mode 100644 src/mainview/components/SwarmDetailsPanel.qml diff --git a/qml.qrc b/qml.qrc index 9dca240a6..e3d6305b3 100644 --- a/qml.qrc +++ b/qml.qrc @@ -94,6 +94,7 @@ <file>src/mainview/components/SidePanel.qml</file> <file>src/mainview/components/WelcomePage.qml</file> <file>src/mainview/components/ChatView.qml</file> + <file>src/mainview/components/NewSwarmPage.qml</file> <file>src/mainview/components/ChatViewHeader.qml</file> <file>src/mainview/components/AccountComboBox.qml</file> <file>src/mainview/components/CallStackView.qml</file> @@ -109,6 +110,7 @@ <file>src/mainview/components/ConversationSmartListContextMenu.qml</file> <file>src/mainview/components/CallViewContextMenu.qml</file> <file>src/mainview/components/UserProfile.qml</file> + <file>src/mainview/components/SwarmDetailsPanel.qml</file> <file>src/mainview/components/SelectScreen.qml</file> <file>src/mainview/components/ScreenRubberBand.qml</file> <file>src/mainview/components/ContactPicker.qml</file> diff --git a/resources/icons/swarm_details_panel.svg b/resources/icons/swarm_details_panel.svg new file mode 100644 index 000000000..2a302f236 --- /dev/null +++ b/resources/icons/swarm_details_panel.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve"> +<g> + <path d="M3.4,5.4L3.4,5.4C2.6,5.4,2,4.8,2,4V3.9c0-0.7,0.6-1.3,1.3-1.3h0.1c0.7,0,1.3,0.6,1.3,1.3V4C4.8,4.8,4.2,5.4,3.4,5.4z"/> + <path d="M21,3H8.9c-0.5,0-1,0.4-1,1v0c0,0.5,0.4,1,1,1H21c0.5,0,1-0.4,1-1v0C22,3.4,21.6,3,21,3z"/> + <path d="M3.4,13.4L3.4,13.4C2.6,13.4,2,12.8,2,12V12c0-0.7,0.6-1.3,1.3-1.3h0.1c0.7,0,1.3,0.6,1.3,1.3V12 + C4.8,12.8,4.2,13.4,3.4,13.4z"/> + <path d="M21,13H8.9c-0.5,0-1-0.4-1-1v0c0-0.5,0.4-1,1-1H21c0.5,0,1,0.4,1,1v0C22,12.6,21.6,13,21,13z"/> + <path d="M3.4,21.4L3.4,21.4c-0.8,0-1.4-0.6-1.4-1.3V20c0-0.7,0.6-1.3,1.3-1.3h0.1c0.7,0,1.3,0.6,1.3,1.3v0.1 + C4.8,20.8,4.2,21.4,3.4,21.4z"/> + <path d="M21,21H8.9c-0.5,0-1-0.5-1-1v0c0-0.5,0.4-1,1-1H21c0.5,0,1,0.4,1,1v0C22,20.6,21.6,21,21,21z"/> +</g> +</svg> diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml index d0db3dcea..44196c6f3 100644 --- a/src/constant/JamiStrings.qml +++ b/src/constant/JamiStrings.qml @@ -222,6 +222,7 @@ Item { property string resumeVideo: qsTr("Resume video") property string addParticipant: qsTr("Add participant") property string addParticipants: qsTr("Add participants") + property string details: qsTr("Details") property string chat: qsTr("Chat") property string moreOptions: qsTr("More options") property string mosaic: qsTr("Mosaic") @@ -609,4 +610,13 @@ Item { property string invitationViewJoinConversation: qsTr("Hello,\nWould you like to join the conversation?") property string invitationViewAcceptedConversation: qsTr("You have accepted\nthe conversation request") property string invitationViewWaitingForSync: qsTr("Waiting until %1\nconnects to synchronize the conversation.") + + // SwarmDetailsPanel + property string about: qsTr("About") + property string members: qsTr("Members") + property string documents: qsTr("Documents") + + // NewSwarmPage + + property string createTheSwarm: qsTr("Create the swarm") } diff --git a/src/contactadapter.cpp b/src/contactadapter.cpp index 534ba666b..6ae89c87f 100644 --- a/src/contactadapter.cpp +++ b/src/contactadapter.cpp @@ -55,7 +55,7 @@ ContactAdapter::getContactSelectableModel(int type) }); break; case SmartListModel::Type::ADDCONVMEMBER: - selectableProxyModel_->setPredicate([](const QModelIndex& index, const QRegExp&) { + selectableProxyModel_->setPredicate([](const QModelIndex& index, const QRegularExpression&) { return index.data(Role::IsCoreDialog).toBool(); }); break; @@ -108,7 +108,7 @@ ContactAdapter::setSearchFilter(const QString& filter) }); } else if (listModeltype_ == SmartListModel::Type::ADDCONVMEMBER) { selectableProxyModel_->setPredicate( - [this, filter](const QModelIndex& index, const QRegExp&) { + [this, filter](const QModelIndex& index, const QRegularExpression&) { return (index.data(Role::Title).toString().contains(filter, Qt::CaseInsensitive) || index.data(Role::RegisteredName) .toString() diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp index 02786c587..5e87e005b 100644 --- a/src/conversationsadapter.cpp +++ b/src/conversationsadapter.cpp @@ -480,6 +480,25 @@ ConversationsAdapter::restartConversation(const QString& convId) accInfo.contactModel->removeContact(peerUri); } +void +ConversationsAdapter::updateConversationTitle(const QString& convId, const QString& newTitle) +{ + auto convModel = lrcInstance_->getCurrentConversationModel(); + QMap<QString, QString> details; + details["title"] = newTitle; + convModel->updateConversationInfo(convId, details); +} + +void +ConversationsAdapter::updateConversationDescription(const QString& convId, + const QString& newDescription) +{ + auto convModel = lrcInstance_->getCurrentConversationModel(); + QMap<QString, QString> details; + details["description"] = newDescription; + convModel->updateConversationInfo(convId, details); +} + bool ConversationsAdapter::connectConversationModel() { diff --git a/src/conversationsadapter.h b/src/conversationsadapter.h index 72da591ea..d9a7bc5d6 100644 --- a/src/conversationsadapter.h +++ b/src/conversationsadapter.h @@ -52,6 +52,9 @@ public: Q_INVOKABLE void setFilter(const QString& filterString); Q_INVOKABLE QVariantMap getConvInfoMap(const QString& convId); Q_INVOKABLE void restartConversation(const QString& convId); + Q_INVOKABLE void updateConversationTitle(const QString& convId, const QString& newTitle); + Q_INVOKABLE void updateConversationDescription(const QString& convId, + const QString& newDescription); Q_SIGNALS: void showConversation(const QString& accountId, const QString& convUid); diff --git a/src/currentconversation.cpp b/src/currentconversation.cpp index 3a94d6fd8..21ee8f43c 100644 --- a/src/currentconversation.cpp +++ b/src/currentconversation.cpp @@ -51,6 +51,7 @@ CurrentConversation::updateData() if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) { auto& convInfo = optConv->get(); set_title(accInfo.conversationModel->title(convId)); + set_description(accInfo.conversationModel->description(convId)); set_uris(accInfo.conversationModel->peersForConversation(convId).toList()); set_isSwarm(convInfo.isSwarm()); set_isLegacy(convInfo.isLegacy()); diff --git a/src/currentconversation.h b/src/currentconversation.h index d57b5737d..0ad13d40b 100644 --- a/src/currentconversation.h +++ b/src/currentconversation.h @@ -31,6 +31,7 @@ class CurrentConversation final : public QObject Q_OBJECT QML_PROPERTY(QString, id) QML_PROPERTY(QString, title) + QML_PROPERTY(QString, description) QML_PROPERTY(QStringList, uris) QML_PROPERTY(bool, isSwarm) QML_PROPERTY(bool, isLegacy) diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml index 0c1191242..353ec1a05 100644 --- a/src/mainview/MainView.qml +++ b/src/mainview/MainView.qml @@ -80,6 +80,8 @@ Rectangle { if (isPageInStack("callStackViewObject", sidePanelViewStack) || isPageInStack("chatView", sidePanelViewStack) || isPageInStack("chatView", mainViewStack) || + isPageInStack("newSwarmPage", sidePanelViewStack) || + isPageInStack("newSwarmPage", mainViewStack) || isPageInStack("callStackViewObject", mainViewStack)) { sidePanelViewStack.pop(StackView.Immediate) mainViewStack.pop(welcomePage, StackView.Immediate) @@ -107,6 +109,16 @@ Rectangle { } } + function pushNewSwarmPage() { + if (sidePanelOnly) { + sidePanelViewStack.pop(StackView.Immediate) + sidePanelViewStack.push(newSwarmPage, StackView.Immediate) + } else { + mainViewStack.pop(welcomePage, StackView.Immediate) + mainViewStack.push(newSwarmPage, StackView.Immediate) + } + } + function startWizard() { mainViewStackLayout.currentIndex = 1 } @@ -353,6 +365,11 @@ Rectangle { function onNavigateToWelcomePageRequested() { backToMainView() } + + } + + onCreateSwarmClicked: { + pushNewSwarmPage() } } @@ -394,6 +411,18 @@ Rectangle { Component.onCompleted: MessagesAdapter.setQmlObject(this) } + NewSwarmPage { + id: newSwarmPage + + objectName: "newSwarmPage" + visible: false + + onCreateSwarmClicked: { + console.warn("@@@") + backToMainView() + } + } + onWidthChanged: { // Hide unnecessary stackview when width is changed. var widthToCompare = previousWidth < mainView.width ? diff --git a/src/mainview/components/ChatView.qml b/src/mainview/components/ChatView.qml index 2077ddcf9..1ff473931 100644 --- a/src/mainview/components/ChatView.qml +++ b/src/mainview/components/ChatView.qml @@ -53,7 +53,7 @@ Rectangle { spacing: 0 ChatViewHeader { - id: messageWebViewHeader + id: chatViewHeader Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true @@ -76,6 +76,10 @@ Rectangle { root.needToHideConversationInCall() } + onShowDetailsClicked: { + swarmDetailsPanel.visible = !swarmDetailsPanel.visible + } + onPluginSelector: { // Create plugin handler picker - PLUGINS PluginHandlerPickerCreation.createPluginHandlerPickerObjects( @@ -86,61 +90,79 @@ Rectangle { } } - StackLayout { - id: chatViewStack - - Layout.alignment: Qt.AlignHCenter + RowLayout { + id: chatViewMainRow Layout.fillWidth: true - Layout.maximumWidth: JamiTheme.chatViewMaximumWidth Layout.fillHeight: true - Layout.topMargin: JamiTheme.chatViewHairLineSize - Layout.bottomMargin: JamiTheme.chatViewHairLineSize - currentIndex: CurrentConversation.isRequest || - CurrentConversation.needsSyncing + ColumnLayout { + Layout.fillHeight: true + Layout.fillWidth: true - Loader { - active: CurrentConversation.id !== "" - sourceComponent: MessageListView { - DropArea { - anchors.fill: parent - onDropped: chatViewFooter.setFilePathsToSend(drop.urls) + StackLayout { + id: chatViewStack + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + Layout.maximumWidth: JamiTheme.chatViewMaximumWidth + Layout.topMargin: JamiTheme.chatViewHairLineSize + Layout.bottomMargin: JamiTheme.chatViewHairLineSize + + currentIndex: CurrentConversation.isRequest || + CurrentConversation.needsSyncing + + Loader { + active: CurrentConversation.id !== "" + sourceComponent: MessageListView { + DropArea { + anchors.fill: parent + onDropped: chatViewFooter.setFilePathsToSend(drop.urls) + } + } + } + + InvitationView { + id: invitationView + + Layout.fillWidth: true + Layout.fillHeight: true } } - } - InvitationView { - id: invitationView + ReadOnlyFooter { + visible: CurrentConversation.readOnly + Layout.fillWidth: true + } - Layout.fillWidth: true - Layout.fillHeight: true - } - } + ChatViewFooter { + id: chatViewFooter - ReadOnlyFooter { - visible: CurrentConversation.readOnly - Layout.fillWidth: true - } + visible: { + if (CurrentConversation.needsSyncing || CurrentConversation.readOnly) + return false + else if (CurrentConversation.isSwarm && CurrentConversation.isRequest) + return false + return true + } - ChatViewFooter { - id: chatViewFooter + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + Layout.maximumHeight: JamiTheme.chatViewFooterMaximumHeight - visible: { - if (CurrentConversation.needsSyncing || CurrentConversation.readOnly) - return false - else if (CurrentConversation.isSwarm && CurrentConversation.isRequest) - return false - return true + DropArea { + anchors.fill: parent + onDropped: chatViewFooter.setFilePathsToSend(drop.urls) + } + } } - Layout.alignment: Qt.AlignHCenter - Layout.fillWidth: true - Layout.preferredHeight: implicitHeight - Layout.maximumHeight: JamiTheme.chatViewFooterMaximumHeight - - DropArea { - anchors.fill: parent - onDropped: chatViewFooter.setFilePathsToSend(drop.urls) + SwarmDetailsPanel { + id: swarmDetailsPanel + visible: false + Layout.fillHeight: true + Layout.fillWidth: true } } } diff --git a/src/mainview/components/ChatViewHeader.qml b/src/mainview/components/ChatViewHeader.qml index 50053eea6..ff0cdcd17 100644 --- a/src/mainview/components/ChatViewHeader.qml +++ b/src/mainview/components/ChatViewHeader.qml @@ -36,6 +36,7 @@ Rectangle { signal backClicked signal needToHideConversationInCall signal pluginSelector + signal showDetailsClicked property bool interactionButtonsVisibility: { if (CurrentConversation.inCall) @@ -51,6 +52,10 @@ Rectangle { } property bool addMemberVisibility: { + return swarmDetailsVisibility && !CurrentConversation.isRequest + } + + property bool swarmDetailsVisibility: { return !CurrentConversation.isCoreDialog && CurrentConversation.isSwarm } @@ -146,6 +151,7 @@ Rectangle { Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.fillWidth: true Layout.rightMargin: 8 + spacing: 16 PushButton { id: startAAudioCallButton @@ -218,6 +224,20 @@ Rectangle { onClicked: MessagesAdapter.sendConversationRequest() } + + PushButton { + id: detailsButton + + visible: swarmDetailsVisibility + + source: JamiResources.swarm_details_panel_svg + toolTipText: JamiStrings.details + + normalColor: JamiTheme.chatviewBgColor + imageColor: JamiTheme.chatviewButtonColor + + onClicked: showDetailsClicked() + } } } diff --git a/src/mainview/components/NewSwarmPage.qml b/src/mainview/components/NewSwarmPage.qml new file mode 100644 index 000000000..7400e76bf --- /dev/null +++ b/src/mainview/components/NewSwarmPage.qml @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 by Savoir-faire Linux + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import net.jami.Models 1.1 +import net.jami.Adapters 1.1 +import net.jami.Constants 1.1 + +import "../../commoncomponents" + + +Rectangle { + id: root + + color: JamiTheme.chatviewBgColor + + signal createSwarmClicked + + RowLayout { + id: mainLayout + + anchors.fill: parent + + MaterialButton { + id: btnCreateSwarm + + preferredWidth: JamiTheme.aboutButtonPreferredWidth + + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: JamiTheme.buttonTintedBlue + hoveredColor: JamiTheme.buttonTintedBlueHovered + pressedColor: JamiTheme.buttonTintedBluePressed + + text: JamiStrings.createTheSwarm + + onClicked: { + ConversationsAdapter.createSwarm() + createSwarmClicked() + } + } + } +} diff --git a/src/mainview/components/SidePanel.qml b/src/mainview/components/SidePanel.qml index f0ad5fcb7..9fbe8d0e1 100644 --- a/src/mainview/components/SidePanel.qml +++ b/src/mainview/components/SidePanel.qml @@ -31,6 +31,8 @@ Rectangle { color: JamiTheme.backgroundColor + signal createSwarmClicked + Connections { target: LRCInstance @@ -105,7 +107,7 @@ Rectangle { toolTipText: JamiStrings.startASwarm onClicked: { - ConversationsAdapter.createSwarm() + createSwarmClicked() } } } diff --git a/src/mainview/components/SwarmDetailsPanel.qml b/src/mainview/components/SwarmDetailsPanel.qml new file mode 100644 index 000000000..88468a798 --- /dev/null +++ b/src/mainview/components/SwarmDetailsPanel.qml @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 by Savoir-faire Linux + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import net.jami.Models 1.1 +import net.jami.Adapters 1.1 +import net.jami.Constants 1.1 + +import "../../commoncomponents" + +Rectangle { + id: root + + color: JamiTheme.buttonTintedBlue + + ColumnLayout { + id: swarmProfileDetails + Layout.fillWidth: true + Layout.fillHeight: true + + ConversationAvatar { + id: conversationAvatar + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: JamiTheme.avatarSizeInCall + Layout.preferredHeight: JamiTheme.avatarSizeInCall + + imageId: LRCInstance.selectedConvUid + + showPresenceIndicator: false + } + + MaterialLineEdit { + Layout.alignment: Qt.AlignCenter + Layout.topMargin: JamiTheme.preferredMarginSize + + Layout.preferredWidth: root.width + + font.pointSize: JamiTheme.titleFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: CurrentConversation.title + color: "white" + + onEditingFinished: { + ConversationsAdapter.updateConversationTitle(LRCInstance.selectedConvUid, this.text) + } + } + + MaterialLineEdit { + Layout.alignment: Qt.AlignCenter + Layout.topMargin: JamiTheme.preferredMarginSize + + Layout.preferredWidth: root.width + + font.pointSize: JamiTheme.titleFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: CurrentConversation.description + color: "white" + + onEditingFinished: { + ConversationsAdapter.updateConversationDescription(LRCInstance.selectedConvUid, this.text) + } + } + + TabBar { + id: tabBar + + currentIndex: 1 + Layout.preferredWidth: root.width + + FilterTabButton { + id: aboutTabButton + + down: tabBar.currentIndex === 0 + labelText: JamiStrings.about + } + + FilterTabButton { + id: membersTabButton + + down: tabBar.currentIndex === 1 + labelText: JamiStrings.members + } + + FilterTabButton { + id: documentsTabButton + + down: tabBar.currentIndex === 2 + labelText: JamiStrings.documents + } + } + + + Rectangle { + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: root.width + Layout.preferredHeight: root.height + color: JamiTheme.secondaryBackgroundColor + } + } +} -- GitLab