From 4a581d0a1a147072080ac62341d1c3e4490c2680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 11 Feb 2022 11:39:16 -0500 Subject: [PATCH] swarm: pass members to swarm's creation This is the first version of the members list for swarm creation. The user is able to select 1:1 conversation, search new users and merge swarm members in a new swarm. Change-Id: Ic9ac1e9324a46f70ad5d285df890a01ca459f3fa GitLab: #670 --- src/constant/JamiStrings.qml | 5 +- src/conversationlistmodel.cpp | 4 +- src/conversationlistmodel.h | 4 + src/conversationsadapter.cpp | 9 +- src/conversationsadapter.h | 1 + src/mainview/MainView.qml | 14 ++- .../components/ConversationListView.qml | 5 +- src/mainview/components/NewSwarmPage.qml | 5 +- src/mainview/components/SidePanel.qml | 112 ++++++++++++++++-- .../components/SmartListItemDelegate.qml | 20 +++- 10 files changed, 151 insertions(+), 28 deletions(-) diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml index bb1a7917e..32a2589ef 100644 --- a/src/constant/JamiStrings.qml +++ b/src/constant/JamiStrings.qml @@ -443,7 +443,7 @@ Item { property string logsViewCopy: qsTr("Copy") property string logsViewReport: qsTr("Report Bug") property string logsViewClear: qsTr("Clear") - property string logsViewCancel: qsTr("Cancel") + property string cancel: qsTr("Cancel") property string logsViewCopied: qsTr("Copied to clipboard!") property string logsViewDisplay: qsTr("Receive Logs") @@ -621,7 +621,8 @@ Item { property string editDescription: qsTr("Edit description") // NewSwarmPage - + property string youCanAdd8: qsTr("You can add 8 people in the swarm") + property string youCanAddMore: qsTr("You can add %1 more people in the swarm") property string createTheSwarm: qsTr("Create the swarm") property string goToConversation: qsTr("Go to conversation") property string promoteAdministrator: qsTr("Promote to administrator") diff --git a/src/conversationlistmodel.cpp b/src/conversationlistmodel.cpp index 791716a30..389729380 100644 --- a/src/conversationlistmodel.cpp +++ b/src/conversationlistmodel.cpp @@ -117,7 +117,9 @@ ConversationListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s bool match {false}; // banned contacts require exact match - if (index.data(Role::IsBanned).toBool()) { + if (ignored_.contains(index.data(Role::UID).toString())) { + match = true; + } else if (index.data(Role::IsBanned).toBool()) { if (!rx.isValid()) { Q_FOREACH (const auto& filter, toFilter) { auto matchResult = rx.match(filter); diff --git a/src/conversationlistmodel.h b/src/conversationlistmodel.h index b67621168..16b0a2a1f 100644 --- a/src/conversationlistmodel.h +++ b/src/conversationlistmodel.h @@ -49,9 +49,13 @@ public: bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; Q_INVOKABLE void setFilterRequests(bool filterRequests); + Q_INVOKABLE void ignoreFiltering(const QStringList& highlighted) { + ignored_ = highlighted; + } private: // This flag can be toggled when switching tabs to show the current account's // conversation invites. bool filterRequests_ {false}; + QStringList ignored_ {}; }; diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp index 509578a2e..d1dcc4e81 100644 --- a/src/conversationsadapter.cpp +++ b/src/conversationsadapter.cpp @@ -384,6 +384,12 @@ ConversationsAdapter::setFilter(const QString& filterString) Q_EMIT textFilterChanged(filterString); } +void +ConversationsAdapter::ignoreFiltering(const QVariant& hightlighted) +{ + convModel_->ignoreFiltering(hightlighted.toStringList()); +} + QVariantMap ConversationsAdapter::getConvInfoMap(const QString& convId) { @@ -392,9 +398,9 @@ ConversationsAdapter::getConvInfoMap(const QString& convId) return {}; QString peerUri {}; QString bestId {}; + const auto& accountInfo = lrcInstance_->getAccountInfo(convInfo.accountId); if (convInfo.isCoreDialog()) { try { - const auto& accountInfo = lrcInstance_->getAccountInfo(convInfo.accountId); peerUri = accountInfo.conversationModel->peersForConversation(convId).at(0); bestId = accountInfo.contactModel->bestIdForContact(peerUri); } catch (...) { @@ -425,6 +431,7 @@ ConversationsAdapter::getConvInfoMap(const QString& convId) {"bestId", bestId}, {"title", lrcInstance_->getCurrentConversationModel()->title(convId)}, {"uri", peerUri}, + {"uris", accountInfo.conversationModel->peersForConversation(convId)}, {"isSwarm", convInfo.isSwarm()}, {"isRequest", convInfo.isRequest}, {"needsSyncing", convInfo.needsSyncing}, diff --git a/src/conversationsadapter.h b/src/conversationsadapter.h index 15189542e..de30708c3 100644 --- a/src/conversationsadapter.h +++ b/src/conversationsadapter.h @@ -53,6 +53,7 @@ public: const QString& avatar, const VectorString& participants); Q_INVOKABLE void setFilter(const QString& filterString); + Q_INVOKABLE void ignoreFiltering(const QVariant& hightlighted); Q_INVOKABLE QVariantMap getConvInfoMap(const QString& convId); Q_INVOKABLE void restartConversation(const QString& convId); Q_INVOKABLE void updateConversationTitle(const QString& convId, const QString& newTitle); diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml index 9a4a7aaba..0f8008ab9 100644 --- a/src/mainview/MainView.qml +++ b/src/mainview/MainView.qml @@ -369,7 +369,12 @@ Rectangle { } onCreateSwarmClicked: { - pushNewSwarmPage() + if (newSwarmPage.visible) { + backToMainView() + mainViewSidePanel.showSwarmListView(false) + } else { + pushNewSwarmPage() + } } } @@ -417,7 +422,12 @@ Rectangle { objectName: "newSwarmPage" visible: false - onCreateSwarmClicked: { + onVisibleChanged: { + mainViewSidePanel.showSwarmListView(newSwarmPage.visible) + } + + onCreateSwarmClicked: function(title, description, avatar) { + ConversationsAdapter.createSwarm(title, description, avatar, mainViewSidePanel.highlightedMembers) backToMainView() } } diff --git a/src/mainview/components/ConversationListView.qml b/src/mainview/components/ConversationListView.qml index 106f4b87f..e1f523ab9 100644 --- a/src/mainview/components/ConversationListView.qml +++ b/src/mainview/components/ConversationListView.qml @@ -39,10 +39,7 @@ JamiListView { // highlight selection // down and hover states are done within the delegate - highlight: Rectangle { - width: ListView.view ? ListView.view.width : 0 - color: JamiTheme.selectedColor - } + highlightMoveDuration: 60 headerPositioning: ListView.OverlayHeader diff --git a/src/mainview/components/NewSwarmPage.qml b/src/mainview/components/NewSwarmPage.qml index 109d9f33b..79dfdd5e1 100644 --- a/src/mainview/components/NewSwarmPage.qml +++ b/src/mainview/components/NewSwarmPage.qml @@ -32,7 +32,7 @@ Rectangle { color: JamiTheme.chatviewBgColor - signal createSwarmClicked + signal createSwarmClicked(string title, string description, string avatar) ColumnLayout { id: mainLayout @@ -83,8 +83,7 @@ Rectangle { text: JamiStrings.createTheSwarm onClicked: { - ConversationsAdapter.createSwarm(title.text, description.text, "", []) - createSwarmClicked() + createSwarmClicked(title.text, description.text, "") } } } diff --git a/src/mainview/components/SidePanel.qml b/src/mainview/components/SidePanel.qml index 894bb9f60..db4160977 100644 --- a/src/mainview/components/SidePanel.qml +++ b/src/mainview/components/SidePanel.qml @@ -18,6 +18,7 @@ */ import QtQuick +import QtQuick.Controls import QtQuick.Layouts import net.jami.Models 1.1 @@ -27,7 +28,7 @@ import net.jami.Constants 1.1 import "../../commoncomponents" Rectangle { - id: sidePanelRect + id: root color: JamiTheme.backgroundColor @@ -58,15 +59,39 @@ Rectangle { sidePanelTabBar.selectTab(tabIndex) } + property var highlighted: [] + property var highlightedMembers: [] + + function refreshHighlighted() { + var result = [] + for (var idx in highlighted) { + var convId = highlighted[idx] + var item = ConversationsAdapter.getConvInfoMap(convId) + for (var idx in item.uris) { + var uri = item.uris[idx] + if (!result.indexOf(uri) != -1 && uri != CurrentAccount.uri) { + result.push(uri) + } + } + } + highlightedMembers = result + ConversationsAdapter.ignoreFiltering(root.highlighted) + } + + function showSwarmListView(v) { + smartListLayout.visible = !v + swarmMemberSearchList.visible = v + } + RowLayout { id: startBar height: 40 - anchors.top: sidePanelRect.top + anchors.top: root.top anchors.topMargin: 10 - anchors.left: sidePanelRect.left + anchors.left: root.left anchors.leftMargin: 15 - anchors.right: sidePanelRect.right + anchors.right: root.right anchors.rightMargin: 15 ContactSearchBar { @@ -79,6 +104,7 @@ Rectangle { // not calling positionViewAtBeginning will cause // sort animation visual bugs conversationListView.positionViewAtBeginning() + ConversationsAdapter.ignoreFiltering(root.highlighted) ConversationsAdapter.setFilter(text) } @@ -103,12 +129,10 @@ Rectangle { preferredSize: startBar.height - source: JamiResources.create_swarm_svg - toolTipText: JamiStrings.startASwarm + source: smartListLayout.visible ? JamiResources.create_swarm_svg : JamiResources.round_close_24dp_svg + toolTipText: smartListLayout.visible ? JamiStrings.startASwarm : JamiStrings.cancel - onClicked: { - createSwarmClicked() - } + onClicked: createSwarmClicked() } } @@ -116,10 +140,10 @@ Rectangle { id: sidePanelTabBar visible: ConversationsAdapter.pendingRequestCount && - !contactSearchBar.textContent + !contactSearchBar.textContent && smartListLayout.visible anchors.top: startBar.bottom anchors.topMargin: visible ? 10 : 0 - width: sidePanelRect.width + width: root.width height: visible ? 42 : 0 contentHeight: visible ? 42 : 0 } @@ -127,7 +151,7 @@ Rectangle { Rectangle { id: searchStatusRect - visible: searchStatusText.text !== "" + visible: searchStatusText.text !== "" && smartListLayout.visible anchors.top: sidePanelTabBar.bottom anchors.topMargin: visible ? 10 : 0 @@ -214,4 +238,68 @@ Rectangle { headerVisible: searchResultsListView.visible } } + + ColumnLayout { + id: swarmMemberSearchList + + visible: false + + width: parent.width + anchors.top: searchStatusRect.bottom + anchors.topMargin: (sidePanelTabBar.visible || + searchStatusRect.visible) ? 0 : 12 + anchors.bottom: parent.bottom + + spacing: 4 + + Label { + font.bold: true + font.pointSize: JamiTheme.contactEventPointSize + + Layout.margins: 16 + Layout.maximumHeight: 24 + + text: { + if (highlightedMembers.length === 0) + return JamiStrings.youCanAdd8 + return JamiStrings.youCanAddMore.arg(8 - Math.min(highlightedMembers.length, 8)) + } + color: JamiTheme.textColor + } + + JamiListView { + id: swarmCurrentConversationList + + Layout.preferredWidth: parent.width + Layout.fillHeight: true + + model: ConversationListModel + delegate: SmartListItemDelegate { + interactive: false + + onVisibleChanged: { + if (!visible) { + highlighted = false + root.refreshHighlighted() + } + } + + onHighlightedChanged: function onHighlightedChanged() { + var currentHighlighted = root.highlighted + if (highlighted) { + root.highlighted.push(convId) + } else { + root.highlighted = Array.from(root.highlighted).filter(r => r !== convId) + } + root.refreshHighlighted() + // We can't have more than 8 participants yet. + if (root.highlightedMembers.length > 8) { + highlighted = false + root.refreshHighlighted() + } + } + } + currentIndex: model.currentFilteredRow + } + } } diff --git a/src/mainview/components/SmartListItemDelegate.qml b/src/mainview/components/SmartListItemDelegate.qml index dcf6a7754..da66c81d1 100644 --- a/src/mainview/components/SmartListItemDelegate.qml +++ b/src/mainview/components/SmartListItemDelegate.qml @@ -37,6 +37,7 @@ ItemDelegate { property string convId: "" highlighted: ListView.isCurrentItem + property bool interactive: true onVisibleChanged: { if (visible) @@ -174,7 +175,7 @@ ItemDelegate { background: Rectangle { color: { - if (root.pressed) + if (root.pressed || root.highlighted) return Qt.darker(JamiTheme.selectedColor, 1.1) else if (root.hovered) return Qt.darker(JamiTheme.selectedColor, 1.05) @@ -183,8 +184,16 @@ ItemDelegate { } } - onClicked: ListView.view.model.select(index) + onClicked: { + if (!interactive) { + highlighted = !highlighted + return; + } + ListView.view.model.select(index) + } onDoubleClicked: { + if (!interactive) + return; ListView.view.model.select(index) if (LRCInstance.currentAccountType === Profile.Type.SIP || !CurrentAccount.videoEnabled_Video) CallAdapter.placeAudioOnlyCall() @@ -194,10 +203,15 @@ ItemDelegate { } } } - onPressAndHold: ListView.view.openContextMenuAt(pressX, pressY, root) + onPressAndHold: { + if (!interactive) + return; + ListView.view.openContextMenuAt(pressX, pressY, root) + } MouseArea { anchors.fill: parent + enabled: interactive acceptedButtons: Qt.RightButton onClicked: function (mouse) { root.ListView.view.openContextMenuAt(mouse.x, mouse.y, root) -- GitLab