From ce0fca8a70938c68aef766ea22b36409867a2124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 17 Dec 2021 16:59:44 -0500 Subject: [PATCH] conversation: save role for each members Change-Id: Ie113db781e50d72791558dbb8261c4233a07dbc9 --- CMakeLists.txt | 25 +++++++------- src/api/conversation.h | 12 +++++-- src/api/conversationmodel.h | 8 +++++ src/api/member.h | 61 +++++++++++++++++++++++++++++++++ src/authority/storagehelper.cpp | 2 +- src/conversationmodel.cpp | 52 ++++++++++++++++++---------- 6 files changed, 127 insertions(+), 33 deletions(-) create mode 100644 src/api/member.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bfb7645..e57e9209 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,26 +327,27 @@ SET( libringclient_LIB_HDRS ) SET(libringclient_api_LIB_HDRS - src/api/interaction.h - src/api/conversation.h - src/api/contact.h - src/api/call.h src/api/account.h + src/api/avmodel.h + src/api/behaviorcontroller.h src/api/chatview.h + src/api/call.h + src/api/contact.h + src/api/conversation.h + src/api/contactmodel.h + src/api/conversationmodel.h + src/api/datatransfermodel.h + src/api/datatransfer.h + src/api/interaction.h src/api/lrc.h - src/api/avmodel.h - src/api/pluginmodel.h + src/api/member.h src/api/newaccountmodel.h - src/api/peerdiscoverymodel.h src/api/newcallmodel.h src/api/newcodecmodel.h src/api/newdevicemodel.h - src/api/contactmodel.h - src/api/conversationmodel.h + src/api/pluginmodel.h + src/api/peerdiscoverymodel.h src/api/profile.h - src/api/behaviorcontroller.h - src/api/datatransfermodel.h - src/api/datatransfer.h src/api/video.h ) diff --git a/src/api/conversation.h b/src/api/conversation.h index f1688e12..4608b201 100644 --- a/src/api/conversation.h +++ b/src/api/conversation.h @@ -20,6 +20,7 @@ #include "interaction.h" #include "messagelistmodel.h" +#include "member.h" #include "typedefs.h" #include <map> @@ -69,11 +70,10 @@ struct Info bool allMessagesLoaded = false; QString uid = ""; QString accountId; - VectorString participants; + QVector<member::Member> participants; QString callId; QString confId; std::unique_ptr<MessageListModel> interactions; - // MessageListModel interactions; QString lastMessageUid = 0; QHash<QString, QString> parentsId; // pair messageid/parentid for messages without parent loaded unsigned int unreadMessages = 0; @@ -90,6 +90,14 @@ struct Info // conversation. Where active means peer did not leave the conversation. inline bool isCoreDialog() const { return isLegacy() || mode == Mode::ONE_TO_ONE; }; + inline QStringList participantsUris() const + { + QStringList result; + for (const auto& p : participants) + result.append(p.uri); + return result; + } + Mode mode = Mode::NON_SWARM; bool needsSyncing = false; bool isRequest = false; diff --git a/src/api/conversationmodel.h b/src/api/conversationmodel.h index 721896ea..98dd8107 100644 --- a/src/api/conversationmodel.h +++ b/src/api/conversationmodel.h @@ -386,6 +386,14 @@ public: */ QString description(const QString& conversationId) const; + /** + * Get member's role in conversation + * @param conversationId + * @param memberUri + * @return role + */ + member::Role memberRole(const QString& conversationId, const QString& memberUri) const; + Q_SIGNALS: /** * Emitted when a conversation receives a new interaction diff --git a/src/api/member.h b/src/api/member.h new file mode 100644 index 00000000..91fe42e0 --- /dev/null +++ b/src/api/member.h @@ -0,0 +1,61 @@ +/**************************************************************************** + * Copyright (C) 2021 Savoir-faire Linux Inc. * + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser 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 <http://www.gnu.org/licenses/>. * + ***************************************************************************/ +#pragma once + +#include "typedefs.h" + +#include <map> +#include <memory> +#include <vector> + +namespace lrc { + +namespace api { + +namespace member { +Q_NAMESPACE +Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +enum class Role { ADMIN, MEMBER, INVITED, BANNED, LEFT }; +Q_ENUM_NS(Role) + +static inline Role +to_role(const QString& roleStr) +{ + if (roleStr == "admin") + return Role::ADMIN; + if (roleStr == "member") + return Role::MEMBER; + if (roleStr == "invited") + return Role::INVITED; + if (roleStr == "banned") + return Role::BANNED; + if (roleStr == "left") + return Role::LEFT; + return Role::MEMBER; +} + +struct Member +{ + QString uri = ""; + Role role = Role::MEMBER; +}; + +} // namespace member +} // namespace api +} // namespace lrc diff --git a/src/authority/storagehelper.cpp b/src/authority/storagehelper.cpp index dfd15af4..41ae87ce 100644 --- a/src/authority/storagehelper.cpp +++ b/src/authority/storagehelper.cpp @@ -517,7 +517,7 @@ getHistory(Database& db, api::conversation::Info& conversation) if (status != api::interaction::Status::DISPLAYED || !payloads[i + 1].isEmpty()) { continue; } - conversation.interactions->setRead(conversation.participants.front(), payloads[i]); + conversation.interactions->setRead(conversation.participants.front().uri, payloads[i]); } } } diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp index ef3ff043..72048e0d 100644 --- a/src/conversationmodel.cpp +++ b/src/conversationmodel.cpp @@ -1043,10 +1043,10 @@ ConversationModel::title(const QString& conversationId) const QString title; auto idx = 0; for (const auto& member : conversation.participants) { - if (member == owner.profileInfo.uri) { + if (member.uri == owner.profileInfo.uri) { title += owner.accountModel->bestNameForAccount(owner.id); } else { - title += owner.contactModel->bestNameForContact(member); + title += owner.contactModel->bestNameForContact(member.uri); } idx += 1; if (idx != conversation.participants.size()) { @@ -1056,6 +1056,20 @@ ConversationModel::title(const QString& conversationId) const return title; } +member::Role +ConversationModel::memberRole(const QString& conversationId, const QString& memberUri) const +{ + auto conversationOpt = getConversationForUid(conversationId); + if (!conversationOpt.has_value()) + throw std::out_of_range("Member out of range"); + auto& conversation = conversationOpt->get(); + for (const auto& p : conversation.participants) { + if (p.uri == memberUri) + return p.role; + } + throw std::out_of_range("Member out of range"); +} + QString ConversationModel::description(const QString& conversationId) const { @@ -2024,18 +2038,18 @@ ConversationModelPimpl::peersForConversation(const conversation::Info& conversat VectorString result {}; switch (conversation.mode) { case conversation::Mode::NON_SWARM: - return conversation.participants; + return {conversation.participants[0].uri}; default: break; } // Note: for one to one, we must return self if (conversation.participants.size() == 1) - return conversation.participants; + return {conversation.participants[0].uri}; for (const auto& participant : conversation.participants) { - if (participant.isNull()) + if (participant.uri.isNull()) continue; - if (participant != linked.owner.profileInfo.uri) - result.push_back(participant); + if (participant.uri != linked.owner.profileInfo.uri) + result.push_back(participant.uri); } return result; } @@ -2446,11 +2460,11 @@ ConversationModelPimpl::slotConversationReady(const QString& accountId, // remove non swarm conversation that was added from slotContactAdded const VectorMapStringString& members = ConfigurationManager::instance() .getConversationMembers(accountId, conversationId); - VectorString participants; + QVector<member::Member> participants; // it means conversation with one participant. In this case we could have non swarm conversation bool shouldRemoveNonSwarmConversation = members.size() == 2; for (const auto& member : members) { - participants.append(member["uri"]); + participants.append({member["uri"], api::member::to_role(member["role"])}); if (shouldRemoveNonSwarmConversation) { try { auto& conversation = getConversationForPeerUri(member["uri"]).get(); @@ -2575,14 +2589,14 @@ ConversationModelPimpl::slotConversationMemberEvent(const QString& accountId, auto& conversation = getConversationForUid(conversationId).get(); const VectorMapStringString& members = ConfigurationManager::instance().getConversationMembers(linked.owner.id, conversationId); - VectorString uris; + QVector<member::Member> participants; VectorString membersRemaining; for (auto& member : members) { - uris.append(member["uri"]); + participants.append(member::Member {member["uri"], member::to_role(member["role"])}); if (member["role"] != "left") membersRemaining.append(member["uri"]); } - conversation.participants = uris; + conversation.participants = participants; conversation.readOnly = membersRemaining == VectorString(1, linked.owner.profileInfo.uri); invalidateModel(); Q_EMIT linked.modelChanged(); @@ -2675,7 +2689,7 @@ ConversationModelPimpl::addContactRequest(const QString& contactUri) conversation::Info conversation; conversation.uid = contactUri; conversation.accountId = linked.owner.id; - conversation.participants = {contactUri}; + conversation.participants = {{contactUri, member::Role::INVITED}}; conversation.mode = conversation::Mode::NON_SWARM; conversation.isRequest = true; emplaceBackConversation(std::move(conversation)); @@ -2723,7 +2737,8 @@ ConversationModelPimpl::addConversationRequest(const MapStringString& convReques conversation::Info conversation; conversation.uid = convId; conversation.accountId = linked.owner.id; - conversation.participants = {linked.owner.profileInfo.uri, peerUri}; + conversation.participants = {{linked.owner.profileInfo.uri, member::Role::INVITED}, + {peerUri, member::Role::MEMBER}}; conversation.mode = mode; conversation.isRequest = true; emplaceBackConversation(std::move(conversation)); @@ -2814,7 +2829,8 @@ ConversationModelPimpl::slotContactModelUpdated(const QString& uri) for (auto& user : users) { conversation::Info conversationInfo; conversationInfo.uid = user.profileInfo.uri; - conversationInfo.participants.push_back(user.profileInfo.uri); + conversationInfo.participants.append( + member::Member {user.profileInfo.uri, member::Role::MEMBER}); conversationInfo.accountId = linked.owner.id; searchResults.emplace_front(std::move(conversationInfo)); } @@ -2824,7 +2840,7 @@ ConversationModelPimpl::slotContactModelUpdated(const QString& uri) void ConversationModelPimpl::addSwarmConversation(const QString& convId) { - VectorString participants; + QVector<member::Member> participants; const VectorMapStringString& members = ConfigurationManager::instance() .getConversationMembers(linked.owner.id, convId); auto accountURI = linked.owner.profileInfo.uri; @@ -2842,7 +2858,7 @@ ConversationModelPimpl::addSwarmConversation(const QString& convId) // this check should be removed once all usage of participants replaced by // peersForConversation. We should have ourself in participants list // Note: if members.size() == 1, it's a conv with self so we're also the peer - participants.append(member["uri"]); + participants.append(member::Member {member["uri"], member::to_role(member["role"])}); if (mode == conversation::Mode::ONE_TO_ONE && member["uri"] != accountURI) { otherMember = member["uri"]; } else if (member["uri"] == accountURI) { @@ -2906,7 +2922,7 @@ ConversationModelPimpl::addConversationWith(const QString& convId, conversation::Info conversation; conversation.uid = convId; conversation.accountId = linked.owner.id; - conversation.participants = {contactUri}; + conversation.participants = {{contactUri, member::Role::MEMBER}}; conversation.mode = conversation::Mode::NON_SWARM; conversation.needsSyncing = false; conversation.isRequest = isRequest; -- GitLab