From 76896e79715ca28eb3a5899b757139f912c529c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 17 Dec 2021 14:27:11 -0500 Subject: [PATCH] chatview: add the swarm details page in a stackview This allow the swarm details page to be resizable. Also adds the add members list into this view. Presence and filtering for ContactPicker is also fixed. GitLab: #670 Change-Id: I4bf4369eba1d30dff3931575cd8ebd7eb2c7aee0 --- qml.qrc | 1 + src/constant/JamiTheme.qml | 3 + src/contactadapter.cpp | 58 ++++++++---- src/contactadapter.h | 2 + src/mainview/components/AddMemberPanel.qml | 89 +++++++++++++++++++ src/mainview/components/ChatView.qml | 54 ++++++++++- src/mainview/components/ChatViewHeader.qml | 13 +-- .../components/ContactPickerItemDelegate.qml | 13 ++- src/mainview/components/SwarmDetailsPanel.qml | 2 +- 9 files changed, 204 insertions(+), 31 deletions(-) create mode 100644 src/mainview/components/AddMemberPanel.qml diff --git a/qml.qrc b/qml.qrc index 315160788..fd9a00373 100644 --- a/qml.qrc +++ b/qml.qrc @@ -112,6 +112,7 @@ <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/AddMemberPanel.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/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml index 732241cf7..2cd0c3667 100644 --- a/src/constant/JamiTheme.qml +++ b/src/constant/JamiTheme.qml @@ -368,6 +368,9 @@ Item { property real mainViewPreferredWidth: 725 property real mainViewPreferredHeight: 600 + // Details page + property real detailsPageMinWidth: 300 + function setTheme(dark) { darkTheme = dark } diff --git a/src/contactadapter.cpp b/src/contactadapter.cpp index 6ae89c87f..e9aafdf8f 100644 --- a/src/contactadapter.cpp +++ b/src/contactadapter.cpp @@ -32,6 +32,18 @@ ContactAdapter::ContactAdapter(LRCInstance* instance, QObject* parent) } } +bool +ContactAdapter::hasDifferentMembers(const VectorString& currentMembers, + const VectorString& convMembers) const +{ + for (const auto& uri : convMembers) { + if (uri != lrcInstance_->getCurrentAccountInfo().profileInfo.uri + && !currentMembers.contains(uri)) + return true; + } + return false; +} + QVariant ContactAdapter::getContactSelectableModel(int type) { @@ -54,11 +66,16 @@ ContactAdapter::getContactSelectableModel(int type) return !defaultModerators_.contains(index.data(Role::URI).toString()); }); break; - case SmartListModel::Type::ADDCONVMEMBER: - selectableProxyModel_->setPredicate([](const QModelIndex& index, const QRegularExpression&) { - return index.data(Role::IsCoreDialog).toBool(); - }); + case SmartListModel::Type::ADDCONVMEMBER: { + auto currentConvID = lrcInstance_->get_selectedConvUid(); + auto* convModel = lrcInstance_->getCurrentConversationModel(); + auto members = convModel->peersForConversation(currentConvID); + selectableProxyModel_->setPredicate( + [this, members](const QModelIndex& index, const QRegularExpression&) { + return hasDifferentMembers(members, index.data(Role::Uris).toStringList()); + }); break; + } case SmartListModel::Type::CONFERENCE: selectableProxyModel_->setPredicate([](const QModelIndex& index, const QRegularExpression&) { return index.data(Role::Presence).toBool(); @@ -107,15 +124,18 @@ ContactAdapter::setSearchFilter(const QString& filter) && index.data(Role::Title).toString().contains(filter)); }); } else if (listModeltype_ == SmartListModel::Type::ADDCONVMEMBER) { - selectableProxyModel_->setPredicate( - [this, filter](const QModelIndex& index, const QRegularExpression&) { - return (index.data(Role::Title).toString().contains(filter, Qt::CaseInsensitive) - || index.data(Role::RegisteredName) - .toString() - .contains(filter, Qt::CaseInsensitive) - || index.data(Role::URI).toString().contains(filter, Qt::CaseInsensitive)) - && index.data(Role::IsCoreDialog).toBool(); - }); + auto currentConvID = lrcInstance_->get_selectedConvUid(); + auto* convModel = lrcInstance_->getCurrentConversationModel(); + auto members = convModel->peersForConversation(currentConvID); + selectableProxyModel_->setPredicate([this, filter, members](const QModelIndex& index, + const QRegularExpression&) { + return hasDifferentMembers(members, index.data(Role::Uris).toStringList()) + && (index.data(Role::Title).toString().contains(filter, Qt::CaseInsensitive) + || index.data(Role::RegisteredName) + .toString() + .contains(filter, Qt::CaseInsensitive) + || index.data(Role::Uris).toString().contains(filter, Qt::CaseInsensitive)); + }); } selectableProxyModel_->setFilterRegularExpression( QRegularExpression(filter, QRegularExpression::CaseInsensitiveOption)); @@ -132,8 +152,16 @@ ContactAdapter::contactSelected(int index) if (contactIndex.isValid()) { switch (listModeltype_) { case SmartListModel::Type::ADDCONVMEMBER: { - const auto uri = contactIndex.data(Role::URI).value<QString>(); - convModel->addConversationMember(lrcInstance_->get_selectedConvUid(), uri); + auto members = convModel->peersForConversation(lrcInstance_->get_selectedConvUid()); + auto cntMembers = members.size(); + const auto uris = contactIndex.data(Role::Uris).toStringList(); + for (const auto& uri : uris) { + // TODO remove < 9 + if (!members.contains(uri) && cntMembers < 9) { + cntMembers++; + convModel->addConversationMember(lrcInstance_->get_selectedConvUid(), uri); + } + } break; } case SmartListModel::Type::CONFERENCE: { diff --git a/src/contactadapter.h b/src/contactadapter.h index b7a179174..c736816ff 100644 --- a/src/contactadapter.h +++ b/src/contactadapter.h @@ -104,6 +104,8 @@ private: QStringList defaultModerators_; + bool hasDifferentMembers(const VectorString& currentMembers, const VectorString& convMembers) const; + Q_SIGNALS: void defaultModeratorsUpdated(); }; diff --git a/src/mainview/components/AddMemberPanel.qml b/src/mainview/components/AddMemberPanel.qml new file mode 100644 index 000000000..95cb7db6f --- /dev/null +++ b/src/mainview/components/AddMemberPanel.qml @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 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.backgroundColor + property int type: ContactList.ADDCONVMEMBER + + width: 250 + + ColumnLayout { + id: contactPickerPopupRectColumnLayout + + anchors.top: root.top + anchors.bottom: root.bottom + anchors.margins: 16 + + ContactSearchBar { + id: contactPickerContactSearchBar + + Layout.alignment: Qt.AlignCenter + Layout.margins: 5 + Layout.fillWidth: true + Layout.preferredHeight: 35 + + placeHolderText: JamiStrings.addParticipant + + onContactSearchBarTextChanged: { + ContactAdapter.setSearchFilter(text) + } + } + + JamiListView { + id: contactPickerListView + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: root.width - 8 + Layout.preferredHeight: contactPickerPopupRectColumnLayout.height - contactPickerContactSearchBar.height + + model: ContactAdapter.getContactSelectableModel(type) + + Connections { + enabled: visible + target: CurrentConversation + + onUrisChanged: { + model = ContactAdapter.getContactSelectableModel(type) + } + } + + onVisibleChanged: { + if (visible) + model = ContactAdapter.getContactSelectableModel(type) + } + + delegate: ContactPickerItemDelegate { + id: contactPickerItemDelegate + + showPresenceIndicator: true + } + } + } +} diff --git a/src/mainview/components/ChatView.qml b/src/mainview/components/ChatView.qml index 0a4691ac9..c9b34bfb0 100644 --- a/src/mainview/components/ChatView.qml +++ b/src/mainview/components/ChatView.qml @@ -44,6 +44,7 @@ Rectangle { function focusChatView() { chatViewFooter.textInput.forceActiveFocus() swarmDetailsPanel.visible = false + addMemberPanel.visible = false } color: JamiTheme.chatviewBgColor @@ -78,9 +79,26 @@ Rectangle { } onShowDetailsClicked: { + addMemberPanel.visible = false swarmDetailsPanel.visible = !swarmDetailsPanel.visible } + Connections { + target: CurrentConversation + + onUrisChanged: { + if (CurrentConversation.uris.length >= 8 && addMemberPanel.visible) { + swarmDetailsPanel.visible = false + addMemberPanel.visible = !addMemberPanel.visible + } + } + } + + onAddToConversationClicked: { + swarmDetailsPanel.visible = false + addMemberPanel.visible = !addMemberPanel.visible + } + onPluginSelector: { // Create plugin handler picker - PLUGINS PluginHandlerPickerCreation.createPluginHandlerPickerObjects( @@ -91,14 +109,27 @@ Rectangle { } } - RowLayout { + SplitView { id: chatViewMainRow Layout.fillWidth: true Layout.fillHeight: true + handle: Rectangle { + implicitWidth: JamiTheme.splitViewHandlePreferredWidth + implicitHeight: splitView.height + color: JamiTheme.primaryBackgroundColor + Rectangle { + implicitWidth: 1 + implicitHeight: splitView.height + color: JamiTheme.tabbarBorderColor + } + } + ColumnLayout { - Layout.fillHeight: true - Layout.fillWidth: true + SplitView.maximumWidth: splitView.width + // Note, without JamiTheme.detailsPageMinWidth, sometimes the details page is hidden at the right + SplitView.preferredWidth: Math.max(0, 2 * splitView.width / 3 - JamiTheme.detailsPageMinWidth) + SplitView.fillHeight: true StackLayout { id: chatViewStack @@ -162,6 +193,23 @@ Rectangle { SwarmDetailsPanel { id: swarmDetailsPanel visible: false + + SplitView.maximumWidth: splitView.width + SplitView.preferredWidth: Math.max(JamiTheme.detailsPageMinWidth, splitView.width / 3) + SplitView.minimumWidth: JamiTheme.detailsPageMinWidth + SplitView.fillHeight: true + Layout.fillHeight: true + Layout.fillWidth: true + } + + AddMemberPanel { + id: addMemberPanel + visible: false + + SplitView.maximumWidth: splitView.width + SplitView.preferredWidth: Math.max(JamiTheme.detailsPageMinWidth, splitView.width / 3) + SplitView.minimumWidth: JamiTheme.detailsPageMinWidth + SplitView.fillHeight: true Layout.fillHeight: true Layout.fillWidth: true } diff --git a/src/mainview/components/ChatViewHeader.qml b/src/mainview/components/ChatViewHeader.qml index ff0cdcd17..8f150f711 100644 --- a/src/mainview/components/ChatViewHeader.qml +++ b/src/mainview/components/ChatViewHeader.qml @@ -25,7 +25,6 @@ import net.jami.Constants 1.1 import net.jami.Adapters 1.1 import "../../commoncomponents" -import "../js/contactpickercreation.js" as ContactPickerCreation Rectangle { id: root @@ -35,6 +34,7 @@ Rectangle { signal backClicked signal needToHideConversationInCall + signal addToConversationClicked signal pluginSelector signal showDetailsClicked @@ -61,11 +61,6 @@ Rectangle { color: JamiTheme.chatviewBgColor - function addToConversationClicked() { - ContactPickerCreation.createContactPickerObjects(ContactList.ADDCONVMEMBER, root) - ContactPickerCreation.openContactPicker() - } - RowLayout { id: messagingHeaderRectRowLayout @@ -185,15 +180,15 @@ Rectangle { PushButton { id: addParticipantsButton - visible: addMemberVisibility - source: JamiResources.add_people_24dp_svg toolTipText: JamiStrings.addParticipants normalColor: JamiTheme.chatviewBgColor imageColor: JamiTheme.chatviewButtonColor - onClicked: root.addToConversationClicked() + visible: CurrentConversation.uris.length < 8 && addMemberVisibility + + onClicked: addToConversationClicked() } PushButton { diff --git a/src/mainview/components/ContactPickerItemDelegate.qml b/src/mainview/components/ContactPickerItemDelegate.qml index 0963f6052..238046ac2 100644 --- a/src/mainview/components/ContactPickerItemDelegate.qml +++ b/src/mainview/components/ContactPickerItemDelegate.qml @@ -28,7 +28,9 @@ import "../../commoncomponents" ItemDelegate { id: contactPickerItemDelegate - property alias showPresenceIndicator: avatar.showPresenceIndicator + property var showPresenceIndicator: false + + signal contactClicked ConversationAvatar { id: avatar @@ -41,6 +43,8 @@ ItemDelegate { height: 40 imageId: UID + + showPresenceIndicator: contactPickerItemDelegate.showPresenceIndicator && Presence } Rectangle { @@ -102,7 +106,7 @@ ItemDelegate { color: JamiTheme.backgroundColor - implicitWidth: contactPickerPopupRect.width + implicitWidth: root.width implicitHeight: Math.max( contactPickerContactName.height + textMetricsContactPickerContactId.height + 10, @@ -125,7 +129,10 @@ ItemDelegate { itemSmartListBackground.color = JamiTheme.normalButtonColor ContactAdapter.contactSelected(index) - contactPickerPopup.close() + root.contactClicked() + // TODO remove from there + if (contactPickerPopup) + contactPickerPopup.close() } onEntered: { diff --git a/src/mainview/components/SwarmDetailsPanel.qml b/src/mainview/components/SwarmDetailsPanel.qml index 923f798fb..12bab5f76 100644 --- a/src/mainview/components/SwarmDetailsPanel.qml +++ b/src/mainview/components/SwarmDetailsPanel.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 by Savoir-faire Linux + * Copyright (C) 2022 by Savoir-faire Linux * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify -- GitLab