Skip to content
Snippets Groups Projects
Commit 10c587d6 authored by Sébastien Blin's avatar Sébastien Blin Committed by Adrien Béraud
Browse files

database: fix CALL and CONTACT messages

+ Remove the "Conversation started" message
+ Add "Contact added", "Invitation received", "Invitation accepted"
messages
+ Improve CALL messages.

Note: a behavior has changed. Now, if we clear the history, all
interactions will be removed.

Change-Id: I0bf9320c6aecaf45dbfd0fd8de738f583be7e817
parent 55aefeaf
Branches
No related tags found
No related merge requests found
......@@ -131,6 +131,7 @@ struct Info
Status status = Status::INVALID;
Type type = Type::INVALID;
std::string peer;
bool isOutoging;
bool audioMuted = false;
bool videoMuted = false;
};
......
......@@ -113,6 +113,11 @@ Q_SIGNALS:
* @param contactUri
*/
void contactAdded(const std::string& contactUri) const;
/**
* Connect this signal to know when a pending contact was accepted.
* @param contactUri
*/
void pendingContactAccepted(const std::string& contactUri) const;
/**
* Connect this signal to know when an account was removed.
* @param contactUri
......
......@@ -143,7 +143,7 @@ getConversationsBetween(Database& db, const std::string& accountProfile, const s
}
std::string
beginConversationsBetween(Database& db, const std::string& accountProfile, const std::string& contactProfile)
beginConversationsBetween(Database& db, const std::string& accountProfile, const std::string& contactProfile, const std::string& firstMessage)
{
// Add conversation between account and profile
auto newConversationsId = db.select("IFNULL(MAX(id), 0) + 1",
......@@ -156,7 +156,8 @@ beginConversationsBetween(Database& db, const std::string& accountProfile, const
db.insertInto("conversations",
{{":id", "id"}, {":participant_id", "participant_id"}},
{{":id", newConversationsId}, {":participant_id", contactProfile}});
// Add "Conversation started" interaction
// Add first interaction
if (!firstMessage.empty())
db.insertInto("interactions",
{{":account_id", "account_id"}, {":author_id", "author_id"},
{":conversation_id", "conversation_id"}, {":timestamp", "timestamp"},
......@@ -165,7 +166,7 @@ beginConversationsBetween(Database& db, const std::string& accountProfile, const
{{":account_id", accountProfile}, {":author_id", accountProfile},
{":conversation_id", newConversationsId},
{":timestamp", std::to_string(std::time(nullptr))},
{":body", "Conversation started"}, {":type", "CONTACT"},
{":body", firstMessage}, {":type", "CONTACT"},
{":status", "SUCCEED"}});
return newConversationsId;
}
......@@ -208,6 +209,41 @@ addMessageToConversation(Database& db,
{":status", to_string(msg.status)}});
}
int
addOrUpdateMessage(Database& db,
const std::string& accountProfile,
const std::string& conversationId,
const api::interaction::Info& msg,
const std::string& daemonId)
{
// Check if profile is already present.
auto msgAlreadyExists = db.select("id",
"interactions",
"daemon_id=:daemon_id",
{{":daemon_id", daemonId}});
if (msgAlreadyExists.payloads.empty()) {
return db.insertInto("interactions",
{{":account_id", "account_id"}, {":author_id", "author_id"},
{":conversation_id", "conversation_id"}, {":timestamp", "timestamp"},
{":body", "body"}, {":type", "type"}, {":daemon_id", "daemon_id"},
{":status", "status"}},
{{":account_id", accountProfile}, {":author_id", msg.authorUri},
{":conversation_id", conversationId},
{":timestamp", std::to_string(msg.timestamp)},
{":body", msg.body}, {":type", to_string(msg.type)}, {":daemon_id", daemonId},
{":status", to_string(msg.status)}});
} else {
// already exists
db.update("interactions",
"body=:body",
{{":body", msg.body}},
"daemon_id=:daemon_id",
{{":daemon_id", daemonId}});
return std::stoi(msgAlreadyExists.payloads[0]);
}
}
void addDaemonMsgId(Database& db,
const std::string& interactionId,
const std::string& daemonId)
......@@ -241,7 +277,7 @@ void updateInteractionStatus(Database& db, unsigned int id,
void clearHistory(Database& db,
const std::string& conversationId)
{
db.deleteFrom("interactions", "conversation_id=:id AND type!='CONTACT'", {{":id", conversationId}});
db.deleteFrom("interactions", "conversation_id=:id", {{":id", conversationId}});
}
void
......
......@@ -103,11 +103,13 @@ std::vector<std::string> getConversationsBetween(Database& db,
* @param db
* @param accountProfile the id of the account in the database
* @param contactProfile the id of the contact in the database
* @param firstMessage the body of the first message
* @return conversation_id of the new conversation.
*/
std::string beginConversationsBetween(Database& db,
const std::string& accountProfile,
const std::string& contactProfile);
const std::string& contactProfile,
const std::string& firstMessage = "");
/**
* Return interactions from a conversation
......@@ -129,6 +131,21 @@ int addMessageToConversation(Database& db,
const std::string& conversationId,
const api::interaction::Info& msg);
/**
* Add or update an entry into interactions linked to a conversation.
* @param db
* @param accountProfile
* @param conversationId
* @param msg
* @param daemonId
* @return the id of the inserted interaction
*/
int addOrUpdateMessage(Database& db,
const std::string& accountProfile,
const std::string& conversationId,
const api::interaction::Info& msg,
const std::string& daemonId);
/**
* Change the daemon_id column for an interaction
* @param db
......
......@@ -200,6 +200,7 @@ ContactModel::addContact(contact::Info contactInfo)
break;
case profile::Type::PENDING:
daemon::addContactFromPending(owner, profile.uri);
emit pendingContactAccepted(profile.uri);
daemon::addContact(owner, profile.uri); // BUGS?: daemon::addContactFromPending not always add the contact
break;
case profile::Type::RING:
......
......@@ -98,7 +98,7 @@ public:
* @param callId
* @param body
*/
void addCallMessage(const std::string& callId, const std::string& body);
void addOrUpdateCallMessage(const std::string& callId, const std::string& body);
/**
* Add a new message from a peer in the database
* @param from the peer uri
......@@ -136,10 +136,15 @@ public Q_SLOTS:
*/
void slotContactModelUpdated();
/**
* Listen from contactModel when aa new contact is added
* Listen from contactModel when a new contact is added
* @param uri
*/
void slotContactAdded(const std::string& uri);
/**
* Listen from contactModel when a pending contact is accepted
* @param uri
*/
void slotPendingContactAccepted(const std::string& uri);
/**
* Listen from contactModel when aa new contact is removed
* @param uri
......@@ -605,6 +610,8 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
this, &ConversationModelPimpl::slotContactModelUpdated);
connect(&*linked.owner.contactModel, &ContactModel::contactAdded,
this, &ConversationModelPimpl::slotContactAdded);
connect(&*linked.owner.contactModel, &ContactModel::pendingContactAccepted,
this, &ConversationModelPimpl::slotPendingContactAccepted);
connect(&*linked.owner.contactModel, &ContactModel::contactRemoved,
this, &ConversationModelPimpl::slotContactRemoved);
......@@ -695,11 +702,15 @@ ConversationModelPimpl::sortConversations()
conversations.begin(), conversations.end(),
[](const auto& conversationA, const auto& conversationB)
{
// A or B is a temporary contact
if (conversationA.participants.empty()) return true;
if (conversationB.participants.empty()) return false;
auto historyA = conversationA.interactions;
auto historyB = conversationB.interactions;
// A or B is a new conversation (without CONTACT interaction)
if (historyA.empty()) return true;
if (historyB.empty()) return false;
if (conversationA.uid.empty() || conversationB.uid.empty()) return conversationA.uid.empty();
if (historyA.empty()) return false;
if (historyB.empty()) return true;
// Sort by last Interaction
try
{
......@@ -710,7 +721,7 @@ ConversationModelPimpl::sortConversations()
catch (const std::exception& e)
{
qDebug() << "ConversationModel::sortConversations(), can't get lastMessage";
return true;
return false;
}
});
dirtyConversations = true;
......@@ -731,7 +742,18 @@ ConversationModelPimpl::slotContactAdded(const std::string& uri)
auto contactProfileId = database::getOrInsertProfile(db, uri);
auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
if (conv.empty()) {
conv.emplace_back(database::beginConversationsBetween(db, accountProfileId, contactProfileId));
std::string interaction = "";
try {
auto contact = linked.owner.contactModel->getContact(uri);
interaction = contact.profileInfo.type == profile::Type::PENDING ?
QObject::tr("Invitation received").toStdString() :
QObject::tr("Contact added").toStdString();
} catch (...) {}
conv.emplace_back(
database::beginConversationsBetween(db, accountProfileId,
contactProfileId, interaction
)
);
}
// Add the conversation if not already here
if (indexOf(conv[0]) == -1)
......@@ -746,6 +768,36 @@ ConversationModelPimpl::slotContactAdded(const std::string& uri)
emit linked.modelSorted();
}
void
ConversationModelPimpl::slotPendingContactAccepted(const std::string& uri)
{
auto contactProfileId = database::getOrInsertProfile(db, uri);
auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
if (conv.empty()) {
conv.emplace_back(
database::beginConversationsBetween(db, accountProfileId,
contactProfileId, QObject::tr("Invitation accepted").toStdString()
)
);
} else {
try {
auto contact = linked.owner.contactModel->getContact(uri);
auto msg = interaction::Info {accountProfileId,
QObject::tr("Invitation accepted").toStdString(),
std::time(nullptr), interaction::Type::CONTACT,
interaction::Status::SUCCEED};
auto msgId = database::addMessageToConversation(db, accountProfileId, conv[0], msg);
auto conversationIdx = indexOf(conv[0]);
conversations[conversationIdx].interactions.emplace(msgId, msg);
dirtyConversations = true;
emit linked.newUnreadMessage(conv[0], msgId, msg);
} catch (std::out_of_range& e) {
qDebug() << "ConversationModelPimpl::slotContactAdded can't find contact";
}
}
}
void
ConversationModelPimpl::slotContactRemoved(const std::string& uri)
{
......@@ -886,13 +938,35 @@ ConversationModelPimpl::slotCallStatusChanged(const std::string& callId)
void
ConversationModelPimpl::slotCallStarted(const std::string& callId)
{
addCallMessage(callId, "📞 Call started");
try {
auto call = linked.owner.callModel->getCall(callId);
if (call.isOutoging)
addOrUpdateCallMessage(callId, QObject::tr("📞 Outgoing call").toStdString());
else
addOrUpdateCallMessage(callId, QObject::tr("📞 Incoming call").toStdString());
} catch (std::out_of_range& e) {
qDebug() << "ConversationModelPimpl::slotCallEnded can't end inexistant call";
}
}
void
ConversationModelPimpl::slotCallEnded(const std::string& callId)
{
addCallMessage(callId, "🕽 Call ended");
try {
auto call = linked.owner.callModel->getCall(callId);
if (call.startTime.time_since_epoch().count() != 0) {
if (call.isOutoging)
addOrUpdateCallMessage(callId, QObject::tr("📞 Outgoing call - ").toStdString()
+ linked.owner.callModel->getFormattedCallDuration(callId));
else
addOrUpdateCallMessage(callId, QObject::tr("📞 Incoming call - ").toStdString()
+ linked.owner.callModel->getFormattedCallDuration(callId));
} else {
if (call.isOutoging)
addOrUpdateCallMessage(callId, QObject::tr("🕽 Missed outgoing call").toStdString());
else
addOrUpdateCallMessage(callId, QObject::tr("🕽 Missed incoming call").toStdString());
}
// reset the callId stored in the conversation
for (auto& conversation: conversations)
......@@ -900,10 +974,13 @@ ConversationModelPimpl::slotCallEnded(const std::string& callId)
conversation.callId = "";
dirtyConversations = true;
}
} catch (std::out_of_range& e) {
qDebug() << "ConversationModelPimpl::slotCallEnded can't end inexistant call";
}
}
void
ConversationModelPimpl::addCallMessage(const std::string& callId, const std::string& body)
ConversationModelPimpl::addOrUpdateCallMessage(const std::string& callId, const std::string& body)
{
// Get conversation
for (auto& conversation: conversations) {
......@@ -911,11 +988,19 @@ ConversationModelPimpl::addCallMessage(const std::string& callId, const std::str
auto uid = conversation.uid;
auto msg = interaction::Info {accountProfileId, body, std::time(nullptr),
interaction::Type::CALL, interaction::Status::SUCCEED};
int msgId = database::addMessageToConversation(db, accountProfileId, conversation.uid, msg);
conversation.interactions.emplace(msgId, msg);
int msgId = database::addOrUpdateMessage(db, accountProfileId, conversation.uid, msg, callId);
auto newInteraction = conversation.interactions.find(msgId) == conversation.interactions.end();
if (newInteraction) {
conversation.lastMessageUid = msgId;
conversation.interactions.emplace(msgId, msg);
} else {
conversation.interactions[msgId] = msg;
}
dirtyConversations = true;
if (newInteraction)
emit linked.newUnreadMessage(conversation.uid, msgId, msg);
else
emit linked.interactionStatusUpdated(conversation.uid, msgId, msg);
sortConversations();
emit linked.modelSorted();
}
......@@ -964,7 +1049,10 @@ ConversationModelPimpl::addIncomingMessage(const std::string& from,
auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
if (conv.empty()) {
conv.emplace_back(database::beginConversationsBetween(db, accountProfileId, contactProfileId));
conv.emplace_back(database::beginConversationsBetween(
db, accountProfileId, contactProfileId,
QObject::tr("Invitation received").toStdString()
));
}
auto authorId = authorProfileId.empty()? contactProfileId: authorProfileId;
auto msg = interaction::Info {authorId, body, std::time(nullptr),
......
......@@ -20,6 +20,7 @@
#include "database.h"
// Qt
#include <QObject>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QFile>
......@@ -533,25 +534,10 @@ Database::migrateTextHistory()
// Load interactions
auto groupsArray = loadDoc["groups"].toArray();
auto beginConversation = true;
for (const auto& groupObject: groupsArray) {
auto messagesArray = groupObject.toObject()["messages"].toArray();
for (const auto& messageRef: messagesArray) {
auto messageObject = messageRef.toObject();
if (beginConversation) {
// Add "Conversation started" message
insertInto("interactions",
{{":account_id", "account_id"}, {":author_id", "author_id"},
{":conversation_id", "conversation_id"}, {":timestamp", "timestamp"},
{":body", "body"}, {":type", "type"},
{":status", "status"}},
{{":account_id", accountId}, {":author_id", accountId},
{":conversation_id", newConversationsId},
{":timestamp", std::to_string(messageObject["timestamp"].toInt())},
{":body", "Conversation started"}, {":type", "CONTACT"},
{":status", "SUCCEED"}});
beginConversation = false;
}
auto direction = messageObject["direction"].toInt();
auto body = messageObject["payloads"].toArray()[0].toObject()["payload"].toString();
insertInto("interactions",
......
......@@ -138,6 +138,7 @@ NewCallModel::createCall(const std::string& url)
auto callInfo = std::make_shared<call::Info>();
callInfo->id = callId.toStdString();
callInfo->peer = url;
callInfo->isOutoging = true;
callInfo->status = call::Status::SEARCHING;
callInfo->type = call::Type::DIALOG;
pimpl_->calls.emplace(callId.toStdString(), std::move(callInfo));
......@@ -231,18 +232,23 @@ NewCallModel::togglePause(const std::string& callId) const
void
NewCallModel::toggleMedia(const std::string& callId, const NewCallModel::Media media, bool flag) const
{
auto it = pimpl_->calls.find(callId);
if (it == pimpl_->calls.end()) return;
auto& call = it->second;
switch(media)
{
case NewCallModel::Media::AUDIO:
CallManager::instance().muteLocalMedia(callId.c_str(),
DRing::Media::Details::MEDIA_TYPE_AUDIO,
flag);
call->audioMuted = flag;
break;
case NewCallModel::Media::VIDEO:
CallManager::instance().muteLocalMedia(callId.c_str(),
DRing::Media::Details::MEDIA_TYPE_VIDEO,
flag);
call->videoMuted = flag;
break;
case NewCallModel::Media::NONE:
......@@ -359,6 +365,7 @@ NewCallModelPimpl::slotIncomingCall(const std::string& accountId, const std::str
auto callInfo = std::make_shared<call::Info>();
callInfo->id = callId;
callInfo->peer = fromId;
callInfo->isOutoging = false;
callInfo->status = call::Status::INCOMING_RINGING;
callInfo->type = call::Type::DIALOG;
calls.emplace(callId, std::move(callInfo));
......@@ -378,9 +385,7 @@ NewCallModelPimpl::slotCallStateChanged(const std::string& callId, const std::st
} else if (state == "HUNGUP") {
calls[callId]->status = call::Status::TERMINATING;
} else if (state == "FAILURE" || state == "OVER") {
if (calls[callId]->startTime.time_since_epoch().count() != 0) {
emit linked.callEnded(callId);
}
calls[callId]->status = call::Status::ENDED;
} else if (state == "INACTIVE") {
calls[callId]->status = call::Status::INACTIVE;
......
......@@ -170,8 +170,8 @@ ConversationModelTester::testSendMessageAndClearHistory()
for (const auto& conversation: conversations) {
if (conversation.uid == firstConversation) {
conversationExists = true;
// Should contains "Conversation started" + "Hello World!"
CPPUNIT_ASSERT_EQUAL((int)conversation.interactions.size(), 2);
// Should contains "Hello World!"
CPPUNIT_ASSERT_EQUAL((int)conversation.interactions.size(), 1);
CPPUNIT_ASSERT_EQUAL((*conversation.interactions.rbegin()).second.body, std::string("Hello World!"));
break;
}
......@@ -187,8 +187,7 @@ ConversationModelTester::testSendMessageAndClearHistory()
for (const auto& conversation: conversations) {
if (conversation.uid == firstConversation) {
conversationExists = true;
// contains the "Conversation started" message.
CPPUNIT_ASSERT_EQUAL((int)conversation.interactions.size(), 1);
CPPUNIT_ASSERT_EQUAL((int)conversation.interactions.size(), 0);
break;
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment