diff --git a/qml.qrc b/qml.qrc
index 8e731998..36590f64 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -98,6 +98,7 @@
src/app/mainview/components/SidePanel.qml
src/app/mainview/components/WelcomePage.qml
src/app/mainview/components/ChatView.qml
+ src/app/mainview/components/ConversationErrorsRow.qml
src/app/mainview/components/NewSwarmPage.qml
src/app/mainview/components/ChatViewHeader.qml
src/app/mainview/components/AccountComboBox.qml
diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index 9d6ca31b..0c64a1e5 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -301,6 +301,7 @@ Item {
property string placeVideoCall: qsTr("Place video call")
property string showPlugins: qsTr("Show available plugins")
property string addToConversations: qsTr("Add to conversations")
+ property string backendError: qsTr("This is the error from the backend: %0")
// Chatview footer
property string jumpToLatest: qsTr("Jump to latest")
diff --git a/src/app/conversationsadapter.cpp b/src/app/conversationsadapter.cpp
index 5a50608d..cd1bf5de 100644
--- a/src/app/conversationsadapter.cpp
+++ b/src/app/conversationsadapter.cpp
@@ -495,6 +495,13 @@ ConversationsAdapter::updateConversationTitle(const QString& convId, const QStri
convModel->updateConversationInfos(convId, details);
}
+void
+ConversationsAdapter::popFrontError(const QString& convId)
+{
+ auto convModel = lrcInstance_->getCurrentConversationModel();
+ convModel->popFrontError(convId);
+}
+
void
ConversationsAdapter::updateConversationDescription(const QString& convId,
const QString& newDescription)
diff --git a/src/app/conversationsadapter.h b/src/app/conversationsadapter.h
index de30708c..0319b398 100644
--- a/src/app/conversationsadapter.h
+++ b/src/app/conversationsadapter.h
@@ -57,6 +57,7 @@ public:
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 popFrontError(const QString& convId);
Q_INVOKABLE void updateConversationDescription(const QString& convId,
const QString& newDescription);
diff --git a/src/app/currentconversation.cpp b/src/app/currentconversation.cpp
index 1ccda9c0..0038bc9c 100644
--- a/src/app/currentconversation.cpp
+++ b/src/app/currentconversation.cpp
@@ -104,6 +104,7 @@ CurrentConversation::updateData()
} catch (...) {
qWarning() << "Can't update current conversation data for" << convId;
}
+ updateErrors(convId);
}
void
@@ -142,6 +143,11 @@ CurrentConversation::connectModel()
this,
&CurrentConversation::onProfileUpdated,
Qt::UniqueConnection);
+ connect(lrcInstance_->getCurrentConversationModel(),
+ &ConversationModel::onConversationErrorsUpdated,
+ this,
+ &CurrentConversation::updateErrors,
+ Qt::UniqueConnection);
}
void
@@ -149,3 +155,36 @@ CurrentConversation::showSwarmDetails() const
{
Q_EMIT showDetails();
}
+
+void
+CurrentConversation::updateErrors(const QString& convId)
+{
+ if (convId != id_)
+ return;
+ try {
+ const auto& convModel = lrcInstance_->getCurrentConversationModel();
+ if (auto optConv = convModel->getConversationForUid(convId)) {
+ auto& convInfo = optConv->get();
+ QStringList newErrors;
+ QStringList newBackendErr;
+ for (const auto& [code, error]: convInfo.errors) {
+ if (code == 1) {
+ newErrors.append(tr("An error occurred while fetching this repository"));
+ } else if (code == 2) {
+ newErrors.append(tr("The conversation's mode is un-recognized"));
+ } else if (code == 3) {
+ newErrors.append(tr("An invalid message was detected"));
+ } else if (code == 4) {
+ newErrors.append(tr("Not enough authorization for updating conversation's infos"));
+ } else {
+ continue;
+ }
+ newBackendErr.push_back(error);
+ }
+ set_backendErrors(newBackendErr);
+ set_errors(newErrors);
+ }
+ } catch (...) {
+
+ }
+}
diff --git a/src/app/currentconversation.h b/src/app/currentconversation.h
index 72de66ed..399f19dd 100644
--- a/src/app/currentconversation.h
+++ b/src/app/currentconversation.h
@@ -48,6 +48,8 @@ class CurrentConversation final : public QObject
QML_PROPERTY(bool, isContact)
QML_PROPERTY(bool, allMessagesLoaded)
QML_PROPERTY(QString, modeString)
+ QML_PROPERTY(QStringList, errors)
+ QML_PROPERTY(QStringList, backendErrors)
public:
explicit CurrentConversation(LRCInstance* lrcInstance, QObject* parent = nullptr);
@@ -62,6 +64,7 @@ private Q_SLOTS:
void updateData();
void onConversationUpdated(const QString& convId);
void onProfileUpdated(const QString& convId);
+ void updateErrors(const QString& convId);
private:
LRCInstance* lrcInstance_;
diff --git a/src/app/mainview/components/ChatView.qml b/src/app/mainview/components/ChatView.qml
index 084f0019..ee90d925 100644
--- a/src/app/mainview/components/ChatView.qml
+++ b/src/app/mainview/components/ChatView.qml
@@ -21,6 +21,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Adapters 1.1
@@ -104,6 +105,14 @@ Rectangle {
}
}
+ ConversationErrorsRow {
+ id: errorRect
+ color: JamiTheme.filterBadgeColor
+ Layout.fillWidth: true
+ Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
+ visible: false
+ }
+
SplitView {
id: chatViewMainRow
Layout.fillWidth: true
diff --git a/src/app/mainview/components/ConversationErrorsRow.qml b/src/app/mainview/components/ConversationErrorsRow.qml
new file mode 100644
index 00000000..7a79fc06
--- /dev/null
+++ b/src/app/mainview/components/ConversationErrorsRow.qml
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * 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
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
+
+import net.jami.Models 1.1
+import net.jami.Adapters 1.1
+import net.jami.Constants 1.1
+
+import "../../commoncomponents"
+
+Rectangle {
+ id: root
+
+ opacity: visible
+
+ Connections {
+ target: CurrentConversation
+ enabled: true
+
+ onErrorsChanged: {
+ if (CurrentConversation.errors.length > 0) {
+ errorLabel.text = CurrentConversation.errors[0]
+ backendErrorToolTip.text = JamiStrings.backendError.arg(CurrentConversation.backendErrors[0])
+ }
+ errorRect.visible = CurrentConversation.errors.length > 0
+ }
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: JamiTheme.preferredMarginSize
+
+ Text {
+ id: errorLabel
+ Layout.alignment: Qt.AlignVCenter
+ text: CurrentConversation.errors.count > 0 ? CurrentConversation.errors[0][0] : ""
+ color: JamiTheme.filterBadgeTextColor
+ font.pixelSize: JamiTheme.headerFontSize
+ elide: Text.ElideRight
+ }
+
+ ResponsiveImage {
+ id: backEndError
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ width: 30
+ height: 30
+
+ source: JamiResources.outline_info_24dp_svg
+ layer {
+ enabled: true
+ effect: ColorOverlay {
+ color: JamiTheme.filterBadgeTextColor
+ }
+ }
+
+
+ MaterialToolTip {
+ id: backendErrorToolTip
+ text: ""
+ visible: parent.hovered && text !== ""
+ delay: Qt.styleHints.mousePressAndHoldInterval
+ }
+ }
+
+ PushButton {
+ id: btnClose
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+
+ imageColor: JamiTheme.filterBadgeTextColor
+ normalColor: JamiTheme.transparentColor
+
+ source: JamiResources.round_close_24dp_svg
+
+ onClicked: ConversationsAdapter.popFrontError(CurrentConversation.id)
+ }
+ }
+
+ Behavior on opacity {
+ NumberAnimation {
+ from: 0
+ duration: JamiTheme.shortFadeDuration
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/libclient/api/conversation.h b/src/libclient/api/conversation.h
index 086fe5ff..26743e61 100644
--- a/src/libclient/api/conversation.h
+++ b/src/libclient/api/conversation.h
@@ -77,6 +77,7 @@ struct Info
QString lastMessageUid = 0;
QHash parentsId; // pair messageid/parentid for messages without parent loaded
unsigned int unreadMessages = 0;
+ QVector> errors;
QSet typers;
diff --git a/src/libclient/api/conversationmodel.h b/src/libclient/api/conversationmodel.h
index 634891de..7da8d5aa 100644
--- a/src/libclient/api/conversationmodel.h
+++ b/src/libclient/api/conversationmodel.h
@@ -348,6 +348,11 @@ public:
* @param info
*/
void updateConversationInfos(const QString& conversationId, MapStringString info);
+ /**
+ * Remove first error
+ * @param conversationId
+ */
+ void popFrontError(const QString& conversationId);
/**
* @return if conversations requests exists.
@@ -427,6 +432,11 @@ Q_SIGNALS:
* @param uid
*/
void conversationUpdated(const QString& uid) const;
+ /**
+ * Emitted when a conversation detects an error
+ * @param uid
+ */
+ void onConversationErrorsUpdated(const QString& uid) const;
/**
* Emitted when conversation's profile has been updated
* @param uid
diff --git a/src/libclient/callbackshandler.cpp b/src/libclient/callbackshandler.cpp
index dc49c848..a542b718 100644
--- a/src/libclient/callbackshandler.cpp
+++ b/src/libclient/callbackshandler.cpp
@@ -340,6 +340,11 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
this,
&CallbacksHandler::slotConversationMemberEvent,
Qt::QueuedConnection);
+ connect(&ConfigurationManager::instance(),
+ &ConfigurationManagerInterface::conversationError,
+ this,
+ &CallbacksHandler::slotOnConversationError,
+ Qt::QueuedConnection);
}
CallbacksHandler::~CallbacksHandler() {}
@@ -791,4 +796,13 @@ CallbacksHandler::slotConversationMemberEvent(const QString& accountId,
Q_EMIT conversationMemberEvent(accountId, conversationId, memberId, event);
}
+void
+CallbacksHandler::slotOnConversationError(const QString& accountId,
+ const QString& conversationId,
+ int code,
+ const QString& what)
+{
+ Q_EMIT conversationError(accountId, conversationId, code, what);
+}
+
} // namespace lrc
diff --git a/src/libclient/callbackshandler.h b/src/libclient/callbackshandler.h
index 133d4ca7..b39b6334 100644
--- a/src/libclient/callbackshandler.h
+++ b/src/libclient/callbackshandler.h
@@ -366,6 +366,10 @@ Q_SIGNALS:
const QString& conversationId,
const QString& memberId,
int event);
+ void conversationError(const QString& accountId,
+ const QString& conversationId,
+ int code,
+ const QString& what);
private Q_SLOTS:
/**
@@ -677,6 +681,10 @@ private Q_SLOTS:
const QString& conversationId,
const QString& memberId,
int event);
+ void slotOnConversationError(const QString& accountId,
+ const QString& conversationId,
+ int code,
+ const QString& what);
private:
const api::Lrc& parent;
diff --git a/src/libclient/conversationmodel.cpp b/src/libclient/conversationmodel.cpp
index 85cfa5d2..78ed4090 100644
--- a/src/libclient/conversationmodel.cpp
+++ b/src/libclient/conversationmodel.cpp
@@ -369,6 +369,10 @@ public Q_SLOTS:
const QString& conversationId,
const QString& memberUri,
int event);
+ void slotOnConversationError(const QString& accountId,
+ const QString& conversationId,
+ int code,
+ const QString& what);
void slotConversationReady(const QString& accountId, const QString& conversationId);
void slotConversationRemoved(const QString& accountId, const QString& conversationId);
};
@@ -988,6 +992,18 @@ ConversationModel::updateConversationInfos(const QString& conversationId, const
ConfigurationManager::instance().updateConversationInfos(owner.id, conversationId, newInfos);
}
+void
+ConversationModel::popFrontError(const QString& conversationId)
+{
+ auto conversationOpt = getConversationForUid(conversationId);
+ if (!conversationOpt.has_value())
+ return;
+
+ auto& conversation = conversationOpt->get();
+ conversation.errors.pop_front();
+ Q_EMIT onConversationErrorsUpdated(conversationId);
+}
+
bool
ConversationModel::hasPendingRequests() const
{
@@ -1830,6 +1846,10 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
&CallbacksHandler::conversationMemberEvent,
this,
&ConversationModelPimpl::slotConversationMemberEvent);
+ connect(&callbacksHandler,
+ &CallbacksHandler::conversationError,
+ this,
+ &ConversationModelPimpl::slotOnConversationError);
}
ConversationModelPimpl::~ConversationModelPimpl()
@@ -1970,6 +1990,10 @@ ConversationModelPimpl::~ConversationModelPimpl()
&CallbacksHandler::conversationMemberEvent,
this,
&ConversationModelPimpl::slotConversationMemberEvent);
+ disconnect(&callbacksHandler,
+ &CallbacksHandler::conversationError,
+ this,
+ &ConversationModelPimpl::slotOnConversationError);
}
void
@@ -2661,6 +2685,22 @@ ConversationModelPimpl::slotConversationMemberEvent(const QString& accountId,
Q_EMIT linked.dataChanged(indexOf(conversationId));
}
+void
+ConversationModelPimpl::slotOnConversationError(const QString& accountId,
+ const QString& conversationId,
+ int code,
+ const QString& what)
+{
+ if (accountId != linked.owner.id || indexOf(conversationId) < 0) {
+ return;
+ }
+ try {
+ auto& conversation = getConversationForUid(conversationId).get();
+ conversation.errors.push_back({code, what});
+ Q_EMIT linked.onConversationErrorsUpdated(conversationId);
+ } catch (...) {}
+}
+
void
ConversationModelPimpl::slotIncomingContactRequest(const QString& contactUri)
{
diff --git a/src/libclient/qtwrapper/configurationmanager_wrap.h b/src/libclient/qtwrapper/configurationmanager_wrap.h
index c586c6e8..fc63cb07 100644
--- a/src/libclient/qtwrapper/configurationmanager_wrap.h
+++ b/src/libclient/qtwrapper/configurationmanager_wrap.h
@@ -328,6 +328,16 @@ public:
QString(conversationId.c_str()),
QString(memberId.c_str()),
event);
+ }),
+ exportable_callback(
+ [this](const std::string& accountId,
+ const std::string& conversationId,
+ int code,
+ const std::string& what) {
+ Q_EMIT conversationError(QString(accountId.c_str()),
+ QString(conversationId.c_str()),
+ code,
+ QString(what.c_str()));
})};
}
@@ -1190,6 +1200,10 @@ Q_SIGNALS: // SIGNALS
const QString& conversationId,
const QString& memberId,
int event);
+ void conversationError(const QString& accountId,
+ const QString& conversationId,
+ int code,
+ const QString& what);
};
namespace org {