diff --git a/qml.qrc b/qml.qrc
index 8f5835d1..d1218958 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -161,5 +161,6 @@
src/commoncomponents/BubbleLabel.qml
src/commoncomponents/BackButton.qml
src/commoncomponents/JamiSwitch.qml
+ src/mainview/components/ReadOnlyFooter.qml
diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml
index d94a7ab3..7bb9e5e9 100644
--- a/src/constant/JamiStrings.qml
+++ b/src/constant/JamiStrings.qml
@@ -40,6 +40,8 @@ Item {
property string contactSearchInvitations: qsTr("Search your invitations")
property string invitations: qsTr("Invitations")
property string description: qsTr("Jami is free software for universal communication which respects the freedoms and the privacy of its users.")
+ property string contactLeft: qsTr("You are viewing a conversation where all participants other than you have left. New interactions will not be possible.")
+ property string newConversation: qsTr("Start new conversation")
// AboutPopUp
property string version: qsTr("Version") + (UpdateManager.isCurrentVersionBeta() ? " (BETA)" : "")
@@ -222,6 +224,7 @@ Item {
property string startVideoCall: qsTr("Start video call")
property string startAudioCall: qsTr("Start audio call")
property string clearConversation: qsTr("Clear conversation")
+ property string removeConversation: qsTr("Remove conversation")
property string removeContact: qsTr("Remove contact")
property string blockContact: qsTr("Block contact")
property string contactDetails: qsTr("Contact details")
diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml
index 24812918..91de9fe2 100644
--- a/src/constant/JamiTheme.qml
+++ b/src/constant/JamiTheme.qml
@@ -311,7 +311,7 @@ Item {
property real wizardButtonWidth: 400
// Main application spec
- property real mainViewMinWidth: 300
+ property real mainViewMinWidth: 332
property real mainViewMinHeight: 500
property real wizardViewMinWidth: 500
diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp
index 18673100..34edae18 100644
--- a/src/conversationsadapter.cpp
+++ b/src/conversationsadapter.cpp
@@ -433,6 +433,53 @@ ConversationsAdapter::getConvInfoMap(const QString& convId)
{"readOnly", convInfo.readOnly}};
}
+void
+ConversationsAdapter::restartConversation(const QString& convId)
+{
+ // make sure this conversation meets the criteria of a "restartable" conv
+ // 'readOnly' implies 'isSwarm'
+ auto& accInfo = lrcInstance_->getCurrentAccountInfo();
+ const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId);
+ if (convInfo.uid.isEmpty() || !convInfo.isCoreDialog() || !convInfo.readOnly) {
+ return;
+ }
+
+ // get the ONE_TO_ONE conv's peer uri
+ auto peerUri = accInfo.conversationModel->peersForConversation(convId).at(0);
+
+ // store a copy of the original contact so we can re-add them
+ // Note: we set the profile::Type to TEMPORARY to invoke a full add
+ // when calling ContactModel::addContact
+ auto contactInfo = accInfo.contactModel->getContact(peerUri);
+ contactInfo.profileInfo.type = profile::Type::TEMPORARY;
+
+ Utils::oneShotConnect(
+ accInfo.contactModel.get(),
+ &ContactModel::contactRemoved,
+ [this, &accInfo, contactInfo](const QString& peerUri) {
+ // setup a callback to select another ONE_TO_ONE conversation for this peer
+ // once the new conversation becomes ready
+ Utils::oneShotConnect(
+ accInfo.conversationModel.get(),
+ &ConversationModel::conversationReady,
+ [this, peerUri, &accInfo](const QString& convId) {
+ const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId);
+ // 3. filter for the correct contact-conversation and select it
+ if (!convInfo.uid.isEmpty() && convInfo.isCoreDialog() && !convInfo.readOnly
+ && peerUri
+ == accInfo.conversationModel->peersForConversation(convId).at(0)) {
+ lrcInstance_->selectConversation(convId);
+ }
+ });
+
+ // 2. add the contact and await the conversationReady signal
+ accInfo.contactModel->addContact(contactInfo);
+ });
+
+ // 1. remove the contact and await the contactRemoved signal
+ accInfo.contactModel->removeContact(peerUri);
+}
+
bool
ConversationsAdapter::connectConversationModel()
{
diff --git a/src/conversationsadapter.h b/src/conversationsadapter.h
index aefc70ca..48098611 100644
--- a/src/conversationsadapter.h
+++ b/src/conversationsadapter.h
@@ -50,6 +50,7 @@ public:
Q_INVOKABLE bool connectConversationModel();
Q_INVOKABLE void setFilter(const QString& filterString);
Q_INVOKABLE QVariantMap getConvInfoMap(const QString& convId);
+ Q_INVOKABLE void restartConversation(const QString& convId);
Q_SIGNALS:
void showConversation(const QString& accountId, const QString& convUid);
diff --git a/src/mainview/components/ConversationListView.qml b/src/mainview/components/ConversationListView.qml
index bf981eb1..d7f1d4d7 100644
--- a/src/mainview/components/ConversationListView.qml
+++ b/src/mainview/components/ConversationListView.qml
@@ -175,7 +175,7 @@ ListView {
enabled: root.visible
onActivated: MessagesAdapter.clearConversationHistory(
LRCInstance.currentAccountId,
- UtilsAdapter.getCurrConvId())
+ LRCInstance.selectedConvUid)
}
Shortcut {
@@ -183,7 +183,8 @@ ListView {
context: Qt.ApplicationShortcut
enabled: root.visible
onActivated: {
- MessagesAdapter.blockConversation(UtilsAdapter.getCurrConvId())
+ MessagesAdapter.blockConversation(
+ LRCInstance.selectedConvUid)
}
}
@@ -192,9 +193,7 @@ ListView {
context: Qt.ApplicationShortcut
enabled: root.visible
onActivated: MessagesAdapter.removeConversation(
- LRCInstance.currentAccountId,
- UtilsAdapter.getCurrConvId(),
- false)
+ LRCInstance.selectedConvUid)
}
Shortcut {
diff --git a/src/mainview/components/ConversationSmartListContextMenu.qml b/src/mainview/components/ConversationSmartListContextMenu.qml
index e2f463ec..2880eb72 100644
--- a/src/mainview/components/ConversationSmartListContextMenu.qml
+++ b/src/mainview/components/ConversationSmartListContextMenu.qml
@@ -77,6 +77,15 @@ ContextMenuAutoLoader {
responsibleAccountId,
responsibleConvUid)
},
+ GeneralMenuItem {
+ id: removeConversation
+
+ canTrigger: isSwarm && !hasCall
+ itemName: JamiStrings.removeConversation
+ iconSource: JamiResources.delete_24dp_svg
+ onClicked: MessagesAdapter.removeConversation(
+ responsibleConvUid)
+ },
GeneralMenuItem {
id: removeContact
@@ -84,8 +93,7 @@ ContextMenuAutoLoader {
|| contactType === Profile.Type.SIP)
itemName: JamiStrings.removeContact
iconSource: JamiResources.ic_hangup_participant_24dp_svg
- onClicked: MessagesAdapter.removeConversation(responsibleAccountId,
- responsibleConvUid)
+ onClicked: MessagesAdapter.removeContact(responsibleConvUid)
},
GeneralMenuItem {
id: hangup
diff --git a/src/mainview/components/MessageWebView.qml b/src/mainview/components/MessageWebView.qml
index 095e7caf..d3b69439 100644
--- a/src/mainview/components/MessageWebView.qml
+++ b/src/mainview/components/MessageWebView.qml
@@ -257,6 +257,11 @@ Rectangle {
}
}
+ ReadOnlyFooter {
+ visible: CurrentConversation.readOnly
+ Layout.fillWidth: true
+ }
+
MessageWebViewFooter {
id: messageWebViewFooter
diff --git a/src/mainview/components/OngoingCallPage.qml b/src/mainview/components/OngoingCallPage.qml
index 21531002..58421abc 100644
--- a/src/mainview/components/OngoingCallPage.qml
+++ b/src/mainview/components/OngoingCallPage.qml
@@ -88,7 +88,7 @@ Rectangle {
function handleParticipantsInfo(infos) {
if (infos.length === 0) {
bestName = UtilsAdapter.getBestName(LRCInstance.currentAccountId,
- UtilsAdapter.getCurrConvId())
+ LRCInstance.selectedConvUid)
} else {
bestName = ""
}
diff --git a/src/mainview/components/PluginHandlerPicker.qml b/src/mainview/components/PluginHandlerPicker.qml
index 75a399c2..337a2111 100644
--- a/src/mainview/components/PluginHandlerPicker.qml
+++ b/src/mainview/components/PluginHandlerPicker.qml
@@ -66,7 +66,7 @@ Popup {
} else {
// Reset the model on each show.
var accountId = LRCInstance.currentAccountId
- var peerId = UtilsAdapter.getPeerUri(accountId, UtilsAdapter.getCurrConvId())
+ var peerId = UtilsAdapter.getPeerUri(accountId, LRCInstance.selectedConvUid)
pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId)
}
}
@@ -80,7 +80,7 @@ Popup {
pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(callId)
} else {
var accountId = LRCInstance.currentAccountId
- var peerId = UtilsAdapter.getPeerUri(accountId, UtilsAdapter.getCurrConvId())
+ var peerId = UtilsAdapter.getPeerUri(accountId, LRCInstance.selectedConvUid)
PluginModel.toggleChatHandler(handlerId, accountId, peerId, !isLoaded)
pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId)
}
@@ -139,7 +139,7 @@ Popup {
return PluginAdapter.getMediaHandlerSelectableModel(callId)
} else {
var accountId = LRCInstance.currentAccountId
- var peerId = UtilsAdapter.getPeerUri(accountId, UtilsAdapter.getCurrConvId())
+ var peerId = UtilsAdapter.getPeerUri(accountId, LRCInstance.selectedConvUid)
return PluginAdapter.getChatHandlerSelectableModel(accountId, peerId)
}
}
diff --git a/src/mainview/components/ReadOnlyFooter.qml b/src/mainview/components/ReadOnlyFooter.qml
new file mode 100644
index 00000000..a6ad9b58
--- /dev/null
+++ b/src/mainview/components/ReadOnlyFooter.qml
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.14
+
+import net.jami.Adapters 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
+
+Control {
+ padding: 12
+
+ background: Rectangle {
+ anchors.fill: parent
+ color: JamiTheme.primaryBackgroundColor
+
+ Rectangle {
+ anchors.top: parent.top
+ height: JamiTheme.messageWebViewHairLineSize
+ width: parent.width
+ color: JamiTheme.tabbarBorderColor
+ }
+ }
+
+ contentItem: ColumnLayout {
+ spacing: 12
+ Text {
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
+ Layout.fillWidth: true
+
+ text: JamiStrings.contactLeft
+ font.pointSize: JamiTheme.textFontSize + 2
+ color: JamiTheme.textColor
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
+ Layout.fillWidth: true
+ spacing: 12
+
+ MaterialButton {
+ text: JamiStrings.removeContact
+ font.pointSize: JamiTheme.textFontSize + 2
+ onClicked: MessagesAdapter.removeContact(
+ LRCInstance.selectedConvUid)
+ }
+
+ MaterialButton {
+ text: JamiStrings.newConversation
+ font.pointSize: JamiTheme.textFontSize + 2
+ onClicked: ConversationsAdapter.restartConversation(
+ LRCInstance.selectedConvUid)
+ }
+ }
+ }
+}
diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp
index 6f5b3d5f..93b7e6fd 100644
--- a/src/messagesadapter.cpp
+++ b/src/messagesadapter.cpp
@@ -543,20 +543,27 @@ MessagesAdapter::clearConversationHistory(const QString& accountId, const QStrin
}
void
-MessagesAdapter::removeConversation(const QString& accountId,
- const QString& convUid,
- bool banContact)
+MessagesAdapter::removeConversation(const QString& convUid)
{
- QStringList list = lrcInstance_->accountModel().getDefaultModerators(accountId);
- const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
- const auto contactURI = convInfo.participants.front();
+ auto& accInfo = lrcInstance_->getCurrentAccountInfo();
+ accInfo.conversationModel->removeConversation(convUid);
+}
- if (!contactURI.isEmpty() && list.contains(contactURI)) {
- lrcInstance_->accountModel().setDefaultModerator(accountId, contactURI, false);
+void
+MessagesAdapter::removeContact(const QString& convUid, bool banContact)
+{
+ auto& accInfo = lrcInstance_->getCurrentAccountInfo();
+
+ // remove the uri from the default moderators list
+ // TODO: seems like this should be done in libringclient
+ QStringList list = lrcInstance_->accountModel().getDefaultModerators(accInfo.id);
+ const auto contactUri = accInfo.conversationModel->peersForConversation(convUid).at(0);
+ if (!contactUri.isEmpty() && list.contains(contactUri)) {
+ lrcInstance_->accountModel().setDefaultModerator(accInfo.id, contactUri, false);
}
- lrcInstance_->getAccountInfo(accountId).conversationModel->removeConversation(convUid,
- banContact);
+ // actually remove the contact
+ accInfo.contactModel->removeContact(contactUri, banContact);
}
void
diff --git a/src/messagesadapter.h b/src/messagesadapter.h
index 0c97c931..6673651b 100644
--- a/src/messagesadapter.h
+++ b/src/messagesadapter.h
@@ -44,9 +44,8 @@ protected:
Q_INVOKABLE void setupChatView(const QVariantMap& convInfo);
Q_INVOKABLE void connectConversationModel();
Q_INVOKABLE void sendConversationRequest();
- Q_INVOKABLE void removeConversation(const QString& accountId,
- const QString& convUid,
- bool banContact = false);
+ Q_INVOKABLE void removeConversation(const QString& convUid);
+ Q_INVOKABLE void removeContact(const QString& convUid, bool banContact = false);
Q_INVOKABLE void clearConversationHistory(const QString& accountId, const QString& convUid);
Q_INVOKABLE void acceptInvitation(const QString& convId = {});
Q_INVOKABLE void refuseInvitation(const QString& convUid = "");
diff --git a/src/settingsview/components/KeyBoardShortcutTable.qml b/src/settingsview/components/KeyBoardShortcutTable.qml
index 4eb49f72..a2ef4713 100644
--- a/src/settingsview/components/KeyBoardShortcutTable.qml
+++ b/src/settingsview/components/KeyBoardShortcutTable.qml
@@ -77,35 +77,34 @@ BaseDialog {
Description: qsTr("Fullscreen")
KeyLength: 1
}
- // TODO: add the following after redesign
- // ListElement {
- // Shortcut: Qt.platform.os !== "windows" ? "Ctrl+Q" : "Alt+F4"
- // Description: Qt.platform.os !== "windows" ? qsTr("Quit") : qsTr("Exit")
- // KeyLength: 2
- // }
}
ListModel {
id: keyboardConversationShortcutsModel
ListElement {
- Shortcut: "Shift+Ctrl+C"
+ Shortcut: "Ctrl+Shift+C"
Description: qsTr("Start an audio call")
KeyLength: 3
}
ListElement {
- Shortcut: "Shift+Ctrl+X"
+ Shortcut: "Ctrl+Shift+X"
Description: qsTr("Start a video call")
KeyLength: 3
}
ListElement {
- Shortcut: "Shift+Ctrl+L"
+ Shortcut: "Ctrl+Shift+L"
Description: qsTr("Clear history")
KeyLength: 3
}
ListElement {
- Shortcut: "Shift+Ctrl+B"
+ Shortcut: "Ctrl+Shift+B"
Description: qsTr("Block contact")
KeyLength: 3
}
+ ListElement {
+ Shortcut: "Ctrl+Shift+Delete"
+ Description: qsTr("Remove conversation")
+ KeyLength: 3
+ }
ListElement {
Shortcut: "Shift+Ctrl+A"
Description: qsTr("Accept contact request")
diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp
index 95a56680..a417e17b 100644
--- a/src/utilsadapter.cpp
+++ b/src/utilsadapter.cpp
@@ -150,12 +150,6 @@ UtilsAdapter::setConversationFilter(const QString& filter)
lrcInstance_->getCurrentConversationModel()->setFilter(filter);
}
-const QString
-UtilsAdapter::getCurrConvId()
-{
- return lrcInstance_->get_selectedConvUid();
-}
-
const QStringList
UtilsAdapter::getCurrAccList()
{
diff --git a/src/utilsadapter.h b/src/utilsadapter.h
index 11a53f7c..e80fee7d 100644
--- a/src/utilsadapter.h
+++ b/src/utilsadapter.h
@@ -53,7 +53,6 @@ public:
Q_INVOKABLE const QString getPeerUri(const QString& accountId, const QString& uid);
Q_INVOKABLE QString getBestId(const QString& accountId);
Q_INVOKABLE const QString getBestId(const QString& accountId, const QString& uid);
- Q_INVOKABLE const QString getCurrConvId();
Q_INVOKABLE const QStringList getCurrAccList();
Q_INVOKABLE int getAccountListSize();
Q_INVOKABLE bool hasCall(const QString& accountId);