mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
Correctly handle IMDN for group chat.
This commit is contained in:
parent
87921f7376
commit
91bce6e695
14 changed files with 264 additions and 65 deletions
|
|
@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
#include "c-wrapper/c-wrapper.h"
|
||||
#include "call/call-p.h"
|
||||
#include "chat/chat-message/chat-message-p.h"
|
||||
#include "chat/chat-room/chat-room.h"
|
||||
#include "chat/chat-room/server-group-chat-room-p.h"
|
||||
#include "conference/participant.h"
|
||||
|
|
@ -565,7 +566,7 @@ static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status)
|
|||
|
||||
// Check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks
|
||||
if (msg->getChatRoom())
|
||||
msg->updateState((LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(status));
|
||||
L_GET_PRIVATE(msg)->setState((LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(status));
|
||||
}
|
||||
|
||||
static void info_received(SalOp *op, SalBodyHandler *body_handler) {
|
||||
|
|
|
|||
|
|
@ -355,7 +355,6 @@ LinphoneChatMessageStateChangedCb linphone_chat_message_get_message_state_change
|
|||
void linphone_chat_message_set_message_state_changed_cb(LinphoneChatMessage* msg, LinphoneChatMessageStateChangedCb cb);
|
||||
void linphone_chat_message_set_message_state_changed_cb_user_data(LinphoneChatMessage* msg, void *user_data);
|
||||
void * linphone_chat_message_get_message_state_changed_cb_user_data(LinphoneChatMessage* msg);
|
||||
void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state);
|
||||
LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call);
|
||||
|
||||
void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID id);
|
||||
|
|
|
|||
|
|
@ -209,10 +209,6 @@ void linphone_chat_message_resend_2(LinphoneChatMessage *msg) {
|
|||
L_GET_CPP_PTR_FROM_C_OBJECT(msg)->send();
|
||||
}
|
||||
|
||||
void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state) {
|
||||
L_GET_CPP_PTR_FROM_C_OBJECT(msg)->updateState((LinphonePrivate::ChatMessage::State) new_state);
|
||||
}
|
||||
|
||||
LinphoneStatus linphone_chat_message_put_char(LinphoneChatMessage *msg, uint32_t character) {
|
||||
return ((LinphoneStatus)L_GET_CPP_PTR_FROM_C_OBJECT(msg)->putCharacter(character));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ public:
|
|||
|
||||
void setDirection (ChatMessage::Direction dir);
|
||||
|
||||
void setState (ChatMessage::State state, bool force = false);
|
||||
void setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState);
|
||||
void setState (ChatMessage::State newState, bool force = false);
|
||||
|
||||
void setTime (time_t time);
|
||||
|
||||
|
|
@ -134,6 +135,8 @@ public:
|
|||
void updateInDb ();
|
||||
|
||||
private:
|
||||
static bool validStateTransition (ChatMessage::State currentState, ChatMessage::State newState);
|
||||
|
||||
// TODO: Clean attributes.
|
||||
time_t time = ::ms_time(0); // TODO: Change me in all files.
|
||||
std::string imdnId;
|
||||
|
|
|
|||
|
|
@ -63,28 +63,65 @@ void ChatMessagePrivate::setIsReadOnly (bool readOnly) {
|
|||
isReadOnly = readOnly;
|
||||
}
|
||||
|
||||
void ChatMessagePrivate::setState (ChatMessage::State s, bool force) {
|
||||
void ChatMessagePrivate::setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState) {
|
||||
L_Q();
|
||||
|
||||
if (!(q->getChatRoom()->getCapabilities() & AbstractChatRoom::Capabilities::Conference)) {
|
||||
setState(newState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dbKey.isValid())
|
||||
return;
|
||||
|
||||
unique_ptr<MainDb> &mainDb = q->getChatRoom()->getCore()->getPrivate()->mainDb;
|
||||
shared_ptr<EventLog> eventLog = mainDb->getEventFromKey(dbKey);
|
||||
ChatMessage::State currentState = mainDb->getChatMessageParticipantState(eventLog, participantAddress);
|
||||
if (!validStateTransition(currentState, newState))
|
||||
return;
|
||||
|
||||
mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState);
|
||||
|
||||
list<ChatMessage::State> states = mainDb->getChatMessageParticipantStates(eventLog);
|
||||
size_t nbDisplayedStates = 0;
|
||||
size_t nbDeliveredToUserStates = 0;
|
||||
size_t nbNotDeliveredStates = 0;
|
||||
for (const auto &state : states) {
|
||||
switch (state) {
|
||||
case ChatMessage::State::Displayed:
|
||||
nbDisplayedStates++;
|
||||
break;
|
||||
case ChatMessage::State::DeliveredToUser:
|
||||
nbDeliveredToUserStates++;
|
||||
break;
|
||||
case ChatMessage::State::NotDelivered:
|
||||
nbNotDeliveredStates++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nbNotDeliveredStates > 0)
|
||||
setState(ChatMessage::State::NotDelivered);
|
||||
else if (nbDisplayedStates == states.size())
|
||||
setState(ChatMessage::State::Displayed);
|
||||
else if ((nbDisplayedStates + nbDeliveredToUserStates) == states.size())
|
||||
setState(ChatMessage::State::DeliveredToUser);
|
||||
}
|
||||
|
||||
void ChatMessagePrivate::setState (ChatMessage::State newState, bool force) {
|
||||
L_Q();
|
||||
|
||||
if (force)
|
||||
state = s;
|
||||
state = newState;
|
||||
|
||||
if (s == state)
|
||||
return;
|
||||
|
||||
if (
|
||||
(state == ChatMessage::State::Displayed || state == ChatMessage::State::DeliveredToUser) &&
|
||||
(
|
||||
s == ChatMessage::State::DeliveredToUser ||
|
||||
s == ChatMessage::State::Delivered ||
|
||||
s == ChatMessage::State::NotDelivered
|
||||
)
|
||||
)
|
||||
if (!validStateTransition(state, newState))
|
||||
return;
|
||||
|
||||
lInfo() << "Chat message " << this << ": moving from " << Utils::toString(state) <<
|
||||
" to " << Utils::toString(s);
|
||||
state = s;
|
||||
" to " << Utils::toString(newState);
|
||||
state = newState;
|
||||
|
||||
LinphoneChatMessage *msg = L_GET_C_BACK_PTR(q);
|
||||
if (linphone_chat_message_get_message_state_changed_cb(msg))
|
||||
|
|
@ -756,6 +793,24 @@ void ChatMessagePrivate::updateInDb () {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool ChatMessagePrivate::validStateTransition (ChatMessage::State currentState, ChatMessage::State newState) {
|
||||
if (newState == currentState)
|
||||
return false;
|
||||
|
||||
if (
|
||||
(currentState == ChatMessage::State::Displayed || currentState == ChatMessage::State::DeliveredToUser) &&
|
||||
(
|
||||
newState == ChatMessage::State::DeliveredToUser ||
|
||||
newState == ChatMessage::State::Delivered ||
|
||||
newState == ChatMessage::State::NotDelivered
|
||||
)
|
||||
)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ChatMessage::ChatMessage (const shared_ptr<AbstractChatRoom> &chatRoom, ChatMessage::Direction direction) :
|
||||
Object(*new ChatMessagePrivate), CoreAccessor(chatRoom->getCore()) {
|
||||
L_D();
|
||||
|
|
@ -931,12 +986,6 @@ void ChatMessage::removeCustomHeader (const string &headerName) {
|
|||
d->customHeaders.erase(headerName);
|
||||
}
|
||||
|
||||
void ChatMessage::updateState (State state) {
|
||||
L_D();
|
||||
|
||||
d->setState(state);
|
||||
}
|
||||
|
||||
void ChatMessage::send () {
|
||||
L_D();
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class LINPHONE_PUBLIC ChatMessage : public Object, public CoreAccessor {
|
|||
friend class ChatRoomPrivate;
|
||||
friend class CpimChatMessageModifier;
|
||||
friend class FileTransferChatMessageModifier;
|
||||
friend class Imdn;
|
||||
friend class MainDb;
|
||||
friend class MainDbPrivate;
|
||||
friend class RealTimeTextChatRoomPrivate;
|
||||
|
|
@ -63,7 +64,6 @@ public:
|
|||
// ----- TODO: Remove me.
|
||||
void cancelFileTransfer ();
|
||||
int putCharacter (uint32_t character);
|
||||
void updateState (State state);
|
||||
void sendDeliveryNotification (LinphoneReason reason);
|
||||
void sendDisplayNotification ();
|
||||
void setIsSecured (bool isSecured);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public:
|
|||
|
||||
LinphoneReason onSipMessageReceived (SalOp *op, const SalMessage *message) override;
|
||||
void onChatMessageReceived (const std::shared_ptr<ChatMessage> &chatMessage) override;
|
||||
void onImdnReceived (const std::string &text);
|
||||
void onImdnReceived (const std::shared_ptr<ChatMessage> &chatMessage);
|
||||
void onIsComposingReceived (const Address &remoteAddress, const std::string &text);
|
||||
void onIsComposingRefreshNeeded () override;
|
||||
void onIsComposingStateChanged (bool isComposing) override;
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ LinphoneReason ChatRoomPrivate::onSipMessageReceived (SalOp *op, const SalMessag
|
|||
goto end;
|
||||
}
|
||||
} else if (msg->getPrivate()->getContentType() == ContentType::Imdn) {
|
||||
onImdnReceived(msg->getPrivate()->getText());
|
||||
onImdnReceived(msg);
|
||||
if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) {
|
||||
goto end;
|
||||
}
|
||||
|
|
@ -246,9 +246,8 @@ void ChatRoomPrivate::onChatMessageReceived (const shared_ptr<ChatMessage> &chat
|
|||
chatMessage->getPrivate()->notifyReceiving();
|
||||
}
|
||||
|
||||
void ChatRoomPrivate::onImdnReceived (const string &text) {
|
||||
L_Q();
|
||||
Imdn::parse(*q, text);
|
||||
void ChatRoomPrivate::onImdnReceived (const shared_ptr<ChatMessage> &chatMessage) {
|
||||
Imdn::parse(chatMessage);
|
||||
}
|
||||
|
||||
void ChatRoomPrivate::onIsComposingReceived (const Address &remoteAddress, const string &text) {
|
||||
|
|
|
|||
|
|
@ -364,24 +364,24 @@ void FileTransferChatMessageModifier::processResponseFromPostFile (const belle_h
|
|||
message->removeContent(*fileContent);
|
||||
message->addContent(*fileTransferContent);
|
||||
|
||||
message->updateState(ChatMessage::State::FileTransferDone);
|
||||
message->getPrivate()->setState(ChatMessage::State::FileTransferDone);
|
||||
releaseHttpRequest();
|
||||
message->getPrivate()->send();
|
||||
fileUploadEndBackgroundTask();
|
||||
} else {
|
||||
lWarning() << "Received empty response from server, file transfer failed";
|
||||
message->updateState(ChatMessage::State::NotDelivered);
|
||||
message->getPrivate()->setState(ChatMessage::State::NotDelivered);
|
||||
releaseHttpRequest();
|
||||
fileUploadEndBackgroundTask();
|
||||
}
|
||||
} else if (code == 400) {
|
||||
lWarning() << "Received HTTP code response " << code << " for file transfer, probably meaning file is too large";
|
||||
message->updateState(ChatMessage::State::FileTransferError);
|
||||
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
|
||||
releaseHttpRequest();
|
||||
fileUploadEndBackgroundTask();
|
||||
} else {
|
||||
lWarning() << "Unhandled HTTP code response " << code << " for file transfer";
|
||||
message->updateState(ChatMessage::State::NotDelivered);
|
||||
message->getPrivate()->setState(ChatMessage::State::NotDelivered);
|
||||
releaseHttpRequest();
|
||||
fileUploadEndBackgroundTask();
|
||||
}
|
||||
|
|
@ -398,7 +398,7 @@ void FileTransferChatMessageModifier::processIoErrorUpload (const belle_sip_io_e
|
|||
shared_ptr<ChatMessage> message = chatMessage.lock();
|
||||
if (!message)
|
||||
return;
|
||||
message->updateState(ChatMessage::State::NotDelivered);
|
||||
message->getPrivate()->setState(ChatMessage::State::NotDelivered);
|
||||
releaseHttpRequest();
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +412,7 @@ void FileTransferChatMessageModifier::processAuthRequestedUpload (const belle_si
|
|||
shared_ptr<ChatMessage> message = chatMessage.lock();
|
||||
if (!message)
|
||||
return;
|
||||
message->updateState(ChatMessage::State::NotDelivered);
|
||||
message->getPrivate()->setState(ChatMessage::State::NotDelivered);
|
||||
releaseHttpRequest();
|
||||
}
|
||||
|
||||
|
|
@ -844,7 +844,7 @@ void FileTransferChatMessageModifier::processAuthRequestedDownload (const belle_
|
|||
shared_ptr<ChatMessage> message = chatMessage.lock();
|
||||
if (!message)
|
||||
return;
|
||||
message->updateState(ChatMessage::State::FileTransferError);
|
||||
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
|
||||
releaseHttpRequest();
|
||||
}
|
||||
|
||||
|
|
@ -858,7 +858,7 @@ void FileTransferChatMessageModifier::processIoErrorDownload (const belle_sip_io
|
|||
shared_ptr<ChatMessage> message = chatMessage.lock();
|
||||
if (!message)
|
||||
return;
|
||||
message->updateState(ChatMessage::State::FileTransferError);
|
||||
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
|
||||
releaseHttpRequest();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "chat/chat-message/chat-message.h"
|
||||
#include "chat/chat-message/chat-message-p.h"
|
||||
#include "chat/chat-room/chat-room.h"
|
||||
#include "core/core.h"
|
||||
#include "logger/logger.h"
|
||||
|
|
@ -133,18 +133,18 @@ string Imdn::createXml (const string &id, time_t time, Imdn::Type imdnType, Linp
|
|||
return content;
|
||||
}
|
||||
|
||||
void Imdn::parse (ChatRoom &cr, const string &text) {
|
||||
void Imdn::parse (const shared_ptr<ChatMessage> &chatMessage) {
|
||||
xmlparsing_context_t *xmlCtx = linphone_xmlparsing_context_new();
|
||||
xmlSetGenericErrorFunc(xmlCtx, linphone_xmlparsing_genericxml_error);
|
||||
xmlCtx->doc = xmlReadDoc((const unsigned char *)text.c_str(), 0, nullptr, 0);
|
||||
xmlCtx->doc = xmlReadDoc((const unsigned char *)chatMessage->getPrivate()->getText().c_str(), 0, nullptr, 0);
|
||||
if (xmlCtx->doc)
|
||||
parse(cr, xmlCtx);
|
||||
parse(chatMessage, xmlCtx);
|
||||
else
|
||||
lWarning() << "Wrongly formatted IMDN XML: " << xmlCtx->errorBuffer;
|
||||
linphone_xmlparsing_context_destroy(xmlCtx);
|
||||
}
|
||||
|
||||
void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) {
|
||||
void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context_t *xmlCtx) {
|
||||
char xpathStr[MAX_XPATH_LENGTH];
|
||||
char *messageIdStr = nullptr;
|
||||
char *datetimeStr = nullptr;
|
||||
|
|
@ -164,11 +164,13 @@ void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) {
|
|||
}
|
||||
|
||||
if (messageIdStr && datetimeStr) {
|
||||
shared_ptr<ChatMessage> cm = cr.findChatMessage(messageIdStr, ChatMessage::Direction::Outgoing);
|
||||
shared_ptr<AbstractChatRoom> cr = imdnMessage->getChatRoom();
|
||||
shared_ptr<ChatMessage> cm = cr->findChatMessage(messageIdStr, ChatMessage::Direction::Outgoing);
|
||||
const IdentityAddress &participantAddress = imdnMessage->getFromAddress().getAddressWithoutGruu();
|
||||
if (!cm) {
|
||||
lWarning() << "Received IMDN for unknown message " << messageIdStr;
|
||||
} else {
|
||||
LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr.getCore()->getCCore());
|
||||
LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr->getCore()->getCCore());
|
||||
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:delivery-notification/imdn:status", imdnPrefix.c_str());
|
||||
xmlXPathObjectPtr deliveryStatusObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, xpathStr);
|
||||
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:display-notification/imdn:status", imdnPrefix.c_str());
|
||||
|
|
@ -178,9 +180,9 @@ void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) {
|
|||
xmlNodePtr node = deliveryStatusObject->nodesetval->nodeTab[0];
|
||||
if (node->children && node->children->name) {
|
||||
if (strcmp((const char *)node->children->name, "delivered") == 0) {
|
||||
cm->updateState(ChatMessage::State::DeliveredToUser);
|
||||
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::DeliveredToUser);
|
||||
} else if (strcmp((const char *)node->children->name, "error") == 0) {
|
||||
cm->updateState(ChatMessage::State::NotDelivered);
|
||||
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::NotDelivered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -191,7 +193,7 @@ void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) {
|
|||
xmlNodePtr node = displayStatusObject->nodesetval->nodeTab[0];
|
||||
if (node->children && node->children->name) {
|
||||
if (strcmp((const char *)node->children->name, "displayed") == 0) {
|
||||
cm->updateState(ChatMessage::State::Displayed);
|
||||
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::Displayed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ public:
|
|||
};
|
||||
|
||||
static std::string createXml (const std::string &id, time_t time, Imdn::Type imdnType, LinphoneReason reason);
|
||||
static void parse (ChatRoom &cr, const std::string &content);
|
||||
static void parse (const std::shared_ptr<ChatMessage> &chatMessage);
|
||||
|
||||
private:
|
||||
static void parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx);
|
||||
static void parse (const std::shared_ptr<ChatMessage> &chatMessage, xmlparsing_context_t *xmlCtx);
|
||||
|
||||
private:
|
||||
static const std::string imdnPrefix;
|
||||
|
|
|
|||
|
|
@ -516,19 +516,12 @@ void MainDbPrivate::insertChatRoomParticipantDevice (
|
|||
}
|
||||
|
||||
void MainDbPrivate::insertChatMessageParticipant (long long eventId, long long sipAddressId, int state) {
|
||||
// TODO: Deal with read messages.
|
||||
// Remove if displayed? Think a good alorithm for mark as read.
|
||||
soci::session *session = dbSession.getBackendSession();
|
||||
soci::statement statement = (
|
||||
session->prepare << "UPDATE chat_message_participant SET state = :state"
|
||||
" WHERE event_id = :eventId AND participant_sip_address_id = :sipAddressId",
|
||||
soci::use(state), soci::use(eventId), soci::use(sipAddressId)
|
||||
);
|
||||
statement.execute();
|
||||
if (statement.get_affected_rows() == 0 && state != int(ChatMessage::State::Displayed))
|
||||
if (state != static_cast<int>(ChatMessage::State::Displayed)) {
|
||||
soci::session *session = dbSession.getBackendSession();
|
||||
*session << "INSERT INTO chat_message_participant (event_id, participant_sip_address_id, state)"
|
||||
" VALUES (:eventId, :sipAddressId, :state)",
|
||||
soci::use(eventId), soci::use(sipAddressId), soci::use(state);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -992,6 +985,11 @@ long long MainDbPrivate::insertConferenceChatMessageEvent (const shared_ptr<Even
|
|||
for (const Content *content : chatMessage->getContents())
|
||||
insertContent(eventId, *content);
|
||||
|
||||
for (const auto &participant : chatRoom->getParticipants()) {
|
||||
const long long &participantSipAddressId = selectSipAddressId(participant->getAddress().asString());
|
||||
insertChatMessageParticipant(eventId, participantSipAddressId, state);
|
||||
}
|
||||
|
||||
return eventId;
|
||||
}
|
||||
|
||||
|
|
@ -2216,6 +2214,75 @@ list<shared_ptr<ChatMessage>> MainDb::getUnreadChatMessages (const ChatRoomId &c
|
|||
};
|
||||
}
|
||||
|
||||
list<ChatMessage::State> MainDb::getChatMessageParticipantStates (const shared_ptr<EventLog> &eventLog) const {
|
||||
return L_SAFE_TRANSACTION {
|
||||
L_D();
|
||||
|
||||
soci::session *session = d->dbSession.getBackendSession();
|
||||
|
||||
const EventLogPrivate *dEventLog = eventLog->getPrivate();
|
||||
MainDbKeyPrivate *dEventKey = static_cast<MainDbKey &>(dEventLog->dbKey).getPrivate();
|
||||
const long long &eventId = dEventKey->storageId;
|
||||
list<ChatMessage::State> states;
|
||||
unsigned int state;
|
||||
|
||||
soci::statement statement = (session->prepare
|
||||
<< "SELECT state FROM chat_message_participant WHERE event_id = :eventId",
|
||||
soci::into(state), soci::use(eventId)
|
||||
);
|
||||
statement.execute();
|
||||
while (statement.fetch())
|
||||
states.push_back(static_cast<ChatMessage::State>(state));
|
||||
|
||||
return states;
|
||||
};
|
||||
}
|
||||
|
||||
ChatMessage::State MainDb::getChatMessageParticipantState (
|
||||
const shared_ptr<EventLog> &eventLog,
|
||||
const IdentityAddress &participantAddress
|
||||
) const {
|
||||
return L_SAFE_TRANSACTION {
|
||||
L_D();
|
||||
|
||||
soci::session *session = d->dbSession.getBackendSession();
|
||||
|
||||
const EventLogPrivate *dEventLog = eventLog->getPrivate();
|
||||
MainDbKeyPrivate *dEventKey = static_cast<MainDbKey &>(dEventLog->dbKey).getPrivate();
|
||||
const long long &eventId = dEventKey->storageId;
|
||||
const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString());
|
||||
unsigned int state;
|
||||
|
||||
*session << "SELECT state FROM chat_message_participant"
|
||||
" WHERE event_id = :eventId AND participant_sip_address_id = :participantSipAddressId",
|
||||
soci::into(state), soci::use(eventId), soci::use(participantSipAddressId);
|
||||
|
||||
return static_cast<ChatMessage::State>(state);
|
||||
};
|
||||
}
|
||||
|
||||
void MainDb::setChatMessageParticipantState (
|
||||
const shared_ptr<EventLog> &eventLog,
|
||||
const IdentityAddress &participantAddress,
|
||||
ChatMessage::State state
|
||||
) {
|
||||
L_SAFE_TRANSACTION {
|
||||
L_D();
|
||||
|
||||
soci::session *session = d->dbSession.getBackendSession();
|
||||
|
||||
const EventLogPrivate *dEventLog = eventLog->getPrivate();
|
||||
MainDbKeyPrivate *dEventKey = static_cast<MainDbKey &>(dEventLog->dbKey).getPrivate();
|
||||
const long long &eventId = dEventKey->storageId;
|
||||
const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString());
|
||||
int stateInt = static_cast<int>(state);
|
||||
|
||||
*session << "UPDATE chat_message_participant SET state = :state"
|
||||
" WHERE event_id = :eventId AND participant_sip_address_id = :participantSipAddressId",
|
||||
soci::use(stateInt), soci::use(eventId), soci::use(participantSipAddressId);
|
||||
};
|
||||
}
|
||||
|
||||
shared_ptr<ChatMessage> MainDb::getLastChatMessage (const ChatRoomId &chatRoomId) const {
|
||||
list<shared_ptr<EventLog>> chatList = getHistory(chatRoomId, 1, Filter::ConferenceChatMessageFilter);
|
||||
if (chatList.empty())
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "linphone/utils/enum-mask.h"
|
||||
|
||||
#include "abstract/abstract-db.h"
|
||||
#include "chat/chat-message/chat-message.h"
|
||||
#include "chat/chat-room/chat-room-id.h"
|
||||
#include "core/core-accessor.h"
|
||||
|
||||
|
|
@ -85,6 +86,17 @@ public:
|
|||
void markChatMessagesAsRead (const ChatRoomId &chatRoomId = ChatRoomId()) const;
|
||||
std::list<std::shared_ptr<ChatMessage>> getUnreadChatMessages (const ChatRoomId &chatRoomId = ChatRoomId()) const;
|
||||
|
||||
std::list<ChatMessage::State> getChatMessageParticipantStates (const std::shared_ptr<EventLog> &eventLog) const;
|
||||
ChatMessage::State getChatMessageParticipantState (
|
||||
const std::shared_ptr<EventLog> &eventLog,
|
||||
const IdentityAddress &participantAddress
|
||||
) const;
|
||||
void setChatMessageParticipantState (
|
||||
const std::shared_ptr<EventLog> &eventLog,
|
||||
const IdentityAddress &participantAddress,
|
||||
ChatMessage::State state
|
||||
);
|
||||
|
||||
std::shared_ptr<ChatMessage> getLastChatMessage (const ChatRoomId &chatRoomId) const;
|
||||
|
||||
std::list<std::shared_ptr<ChatMessage>> findChatMessages (
|
||||
|
|
|
|||
|
|
@ -2614,6 +2614,76 @@ static void group_chat_room_new_unique_one_to_one_chat_room_after_both_participa
|
|||
linphone_core_manager_destroy(pauline);
|
||||
}
|
||||
|
||||
static void imdn_for_group_chat_room (void) {
|
||||
LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc");
|
||||
LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc");
|
||||
LinphoneCoreManager *chloe = linphone_core_manager_create("chloe_rc");
|
||||
bctbx_list_t *coresManagerList = NULL;
|
||||
bctbx_list_t *participantsAddresses = NULL;
|
||||
coresManagerList = bctbx_list_append(coresManagerList, marie);
|
||||
coresManagerList = bctbx_list_append(coresManagerList, pauline);
|
||||
coresManagerList = bctbx_list_append(coresManagerList, chloe);
|
||||
bctbx_list_t *coresList = init_core_for_conference(coresManagerList);
|
||||
start_core_for_conference(coresManagerList);
|
||||
participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(pauline->lc)));
|
||||
participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(chloe->lc)));
|
||||
stats initialMarieStats = marie->stat;
|
||||
stats initialPaulineStats = pauline->stat;
|
||||
stats initialChloeStats = chloe->stat;
|
||||
|
||||
// Enable IMDN
|
||||
linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie->lc));
|
||||
linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline->lc));
|
||||
linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(chloe->lc));
|
||||
|
||||
// Marie creates a new group chat room
|
||||
const char *initialSubject = "Colleagues";
|
||||
LinphoneChatRoom *marieCr = create_chat_room_client_side(coresList, marie, &initialMarieStats, participantsAddresses, initialSubject, -1);
|
||||
const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr);
|
||||
|
||||
// Check that the chat room is correctly created on Pauline's side and that the participants are added
|
||||
LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline, &initialPaulineStats, confAddr, initialSubject, 2, 0);
|
||||
|
||||
// Check that the chat room is correctly created on Chloe's side and that the participants are added
|
||||
LinphoneChatRoom *chloeCr = check_creation_chat_room_client_side(coresList, chloe, &initialChloeStats, confAddr, initialSubject, 2, 0);
|
||||
|
||||
// Chloe begins composing a message
|
||||
const char *chloeMessage = "Hello";
|
||||
_send_message(chloeCr, chloeMessage);
|
||||
BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageReceived, initialMarieStats.number_of_LinphoneMessageReceived + 1, 10000));
|
||||
BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, initialPaulineStats.number_of_LinphoneMessageReceived + 1, 10000));
|
||||
LinphoneChatMessage *marieLastMsg = marie->stat.last_received_chat_message;
|
||||
if (!BC_ASSERT_PTR_NOT_NULL(marieLastMsg))
|
||||
goto end;
|
||||
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marieLastMsg), chloeMessage);
|
||||
LinphoneAddress *chloeAddr = linphone_address_new(linphone_core_get_identity(chloe->lc));
|
||||
BC_ASSERT_TRUE(linphone_address_weak_equal(chloeAddr, linphone_chat_message_get_from_address(marieLastMsg)));
|
||||
linphone_address_unref(chloeAddr);
|
||||
|
||||
// Check that the message has been delivered to Marie and Pauline
|
||||
BC_ASSERT_TRUE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDeliveredToUser, initialChloeStats.number_of_LinphoneMessageDeliveredToUser + 1, 10000));
|
||||
|
||||
// Marie marks the message as read, check that the state is not yet displayed on Chloe's side
|
||||
linphone_chat_room_mark_as_read(marieCr);
|
||||
BC_ASSERT_FALSE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDisplayed, initialChloeStats.number_of_LinphoneMessageDisplayed + 1, 1000));
|
||||
|
||||
// Pauline also marks the message as read, check that the state is now displayed on Chloe's side
|
||||
linphone_chat_room_mark_as_read(paulineCr);
|
||||
BC_ASSERT_TRUE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDisplayed, initialChloeStats.number_of_LinphoneMessageDisplayed + 1, 1000));
|
||||
|
||||
end:
|
||||
// Clean db from chat room
|
||||
linphone_core_manager_delete_chat_room(marie, marieCr, coresList);
|
||||
linphone_core_manager_delete_chat_room(chloe, chloeCr, coresList);
|
||||
linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList);
|
||||
|
||||
bctbx_list_free(coresList);
|
||||
bctbx_list_free(coresManagerList);
|
||||
linphone_core_manager_destroy(marie);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
linphone_core_manager_destroy(chloe);
|
||||
}
|
||||
|
||||
test_t group_chat_tests[] = {
|
||||
TEST_TWO_TAGS("Group chat room creation server", group_chat_room_creation_server, "Server", "LeaksMemory"),
|
||||
TEST_TWO_TAGS("Send message", group_chat_room_send_message, "Server", "LeaksMemory"),
|
||||
|
|
@ -2651,7 +2721,8 @@ test_t group_chat_tests[] = {
|
|||
TEST_TWO_TAGS("Unique one-to-one chatroom", group_chat_room_unique_one_to_one_chat_room, "Server", "LeaksMemory"),
|
||||
TEST_TWO_TAGS("Unique one-to-one chatroom recreated from message", group_chat_room_unique_one_to_one_chat_room_recreated_from_message, "Server", "LeaksMemory"),
|
||||
TEST_TWO_TAGS("Unique one-to-one chatroom recreated from message with app restart", group_chat_room_unique_one_to_one_chat_room_recreated_from_message_with_app_restart, "Server", "LeaksMemory"),
|
||||
TEST_TWO_TAGS("New unique one-to-one chatroom after both participants left", group_chat_room_new_unique_one_to_one_chat_room_after_both_participants_left, "Server", "LeaksMemory")
|
||||
TEST_TWO_TAGS("New unique one-to-one chatroom after both participants left", group_chat_room_new_unique_one_to_one_chat_room_after_both_participants_left, "Server", "LeaksMemory"),
|
||||
TEST_TWO_TAGS("IMDN for group chat room", imdn_for_group_chat_room, "Server", "LeaksMemory")
|
||||
};
|
||||
|
||||
test_suite_t group_chat_test_suite = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue