1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-03-28 14:56:19 +01:00

conversation: add swarm pages

GitLab: #340
Change-Id: I4a4ebfbebb880161e93fc0a086eec13ffb1ba285
This commit is contained in:
Sébastien Blin 2021-09-22 15:59:47 -04:00
parent 7b1fd8a3fe
commit 3f18e6edbc
No known key found for this signature in database
GPG key ID: C894BB01EEB2A9A9
14 changed files with 356 additions and 47 deletions

View file

@ -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>

View file

@ -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>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -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")
}

View file

@ -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()

View file

@ -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()
{

View file

@ -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);

View file

@ -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());

View file

@ -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)

View file

@ -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 ?

View file

@ -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,22 +90,67 @@ 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
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
}
}
ReadOnlyFooter {
visible: CurrentConversation.readOnly
Layout.fillWidth: true
}
ChatViewFooter {
id: chatViewFooter
visible: {
if (CurrentConversation.needsSyncing || CurrentConversation.readOnly)
return false
else if (CurrentConversation.isSwarm && CurrentConversation.isRequest)
return false
return true
}
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.maximumHeight: JamiTheme.chatViewFooterMaximumHeight
Loader {
active: CurrentConversation.id !== ""
sourceComponent: MessageListView {
DropArea {
anchors.fill: parent
onDropped: chatViewFooter.setFilePathsToSend(drop.urls)
@ -109,38 +158,11 @@ Rectangle {
}
}
InvitationView {
id: invitationView
Layout.fillWidth: true
SwarmDetailsPanel {
id: swarmDetailsPanel
visible: false
Layout.fillHeight: true
}
}
ReadOnlyFooter {
visible: CurrentConversation.readOnly
Layout.fillWidth: true
}
ChatViewFooter {
id: chatViewFooter
visible: {
if (CurrentConversation.needsSyncing || CurrentConversation.readOnly)
return false
else if (CurrentConversation.isSwarm && CurrentConversation.isRequest)
return false
return true
}
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.maximumHeight: JamiTheme.chatViewFooterMaximumHeight
DropArea {
anchors.fill: parent
onDropped: chatViewFooter.setFilePathsToSend(drop.urls)
Layout.fillWidth: true
}
}
}

View file

@ -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()
}
}
}

View file

@ -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()
}
}
}
}

View file

@ -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()
}
}
}

View file

@ -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
}
}
}