Commit 77f906e1 authored by Sébastien Blin's avatar Sébastien Blin Committed by Adrien Béraud

conversationmodel: add composingStatus indicator support

Change-Id: I06daf46ed6f59f43c4d7f5ca653e7162a27ed7c9
Gitlab: #425
parent 9ce238b7
......@@ -221,6 +221,12 @@ public:
* @return the number of unread messages for the conversation
*/
int getNumberOfUnreadMessagesFor(const QString& convUid);
/**
* Send a composing status
* @param uid conversation's id
* @param isComposing if is composing
*/
void setIsComposing(const QString& uid, bool isComposing);
Q_SIGNALS:
/**
......@@ -285,6 +291,13 @@ Q_SIGNALS:
* @param uid
*/
void conversationReady(QString uid) const;
/**
* Emitted when a contact in a conversation is composing a message
* @param uid conversation's id
* @param contactUri contact's uri
* @param isComposing if contact is composing a message
*/
void composingStatusChanged(const QString& uid, const QString& contactUri, bool isComposing) const;
private:
std::unique_ptr<ConversationModelPimpl> pimpl_;
......
......@@ -244,6 +244,13 @@ public Q_SLOTS:
* @param confId
*/
void slotConferenceRemoved(const QString& confId);
/**
* Listen for when a contact is composing
* @param accountId
* @param contactUri
* @param isComposing
*/
void slotComposingStatusChanged(const QString& accountId, const QString& contactUri, bool isComposing);
void slotTransferStatusCreated(long long dringId, api::datatransfer::Info info);
void slotTransferStatusCanceled(long long dringId, api::datatransfer::Info info);
......@@ -1204,6 +1211,10 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
&CallbacksHandler::conferenceRemoved,
this,
&ConversationModelPimpl::slotConferenceRemoved);
connect(&ConfigurationManager::instance(),
&ConfigurationManagerInterface::composingStatusChanged,
this,
&ConversationModelPimpl::slotComposingStatusChanged);
// data transfer
connect(&*linked.owner.contactModel, &ContactModel::newAccountTransfer,
......@@ -1278,6 +1289,8 @@ ConversationModelPimpl::~ConversationModelPimpl()
this, &ConversationModelPimpl::slotCallAddedToConference);
disconnect(&callbacksHandler, &CallbacksHandler::conferenceRemoved,
this, &ConversationModelPimpl::slotConferenceRemoved);
disconnect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::composingStatusChanged,
this, &ConversationModelPimpl::slotComposingStatusChanged);
// data transfer
disconnect(&*linked.owner.contactModel, &ContactModel::newAccountTransfer,
......@@ -1927,12 +1940,33 @@ ConversationModelPimpl::slotConferenceRemoved(const QString& confId)
}
}
void
ConversationModelPimpl::slotComposingStatusChanged(const QString& accountId, const QString& contactUri, bool isComposing)
{
if (accountId != linked.owner.id) return;
// Check conversation's validity
auto convIds = storage::getConversationsWithPeer(db, contactUri);
if (convIds.empty()) return;
emit linked.composingStatusChanged(convIds.front(), contactUri, isComposing);
}
int
ConversationModelPimpl::getNumberOfUnreadMessagesFor(const QString& uid)
{
return storage::countUnreadFromInteractions(db, uid);
}
void
ConversationModel::setIsComposing(const QString& uid, bool isComposing)
{
auto conversationIdx = pimpl_->indexOf(uid);
if (conversationIdx == -1 || !owner.enabled)
return;
const auto peerUri = pimpl_->conversations[conversationIdx].participants.front();
ConfigurationManager::instance().setIsComposing(owner.id, peerUri, isComposing);
}
void
ConversationModel::sendFile(const QString& convUid,
const QString& path,
......
......@@ -172,6 +172,10 @@ public:
[this](const std::string& message) {
Q_EMIT this->debugMessageReceived(QString(message.c_str()));
}),
exportable_callback<ConfigurationSignal::ComposingStatusChanged>(
[this](const std::string& account_id, const std::string& from, int status) {
Q_EMIT this->composingStatusChanged(QString(account_id.c_str()), QString(from.c_str()), status > 0 ? true : false);
}),
};
dataXferHandlers = {
......@@ -705,6 +709,10 @@ public Q_SLOTS: // METHODS
DRing::pushNotificationReceived(from.toStdString(), convertMap(data));
}
void setIsComposing(const QString& accountId, const QString& contactId, bool isComposing) {
DRing::setIsComposing(accountId.toStdString(), contactId.toStdString(), isComposing);
}
Q_SIGNALS: // SIGNALS
void volumeChanged(const QString& device, double value);
void accountsChanged();
......@@ -735,6 +743,7 @@ Q_SIGNALS: // SIGNALS
void dataTransferEvent(qulonglong transfer_id, uint code);
void deviceRevocationEnded(const QString& accountId, const QString& deviceId, int status);
void debugMessageReceived(const QString& message);
void composingStatusChanged(const QString& accountId, const QString& contactId, bool isComposing);
};
namespace org { namespace ring { namespace Ring {
......
......@@ -1206,4 +1206,74 @@ video {
.oneEntry #nav-contactid-bestId {
display: none;
}
.typing_message {
display: flex;
justify-content: flex-start;
}
.typing_message .message_wrapper {
border-top-left-radius: 0;
transform-origin: top left;
background-color: var(--message-out-bg);
color: var(--message-out-txt);
margin-top: auto;
margin-bottom: auto;
}
.typing-indicator {
width: auto;
border-radius: 50px;
padding: 0px;
display: table;
position: relative;
-webkit-animation: 2s bulge infinite ease-out;
animation: 2s bulge infinite ease-out;
}
.typing-indicator span {
height: 8px;
width: 8px;
float: left;
margin: 0 1px;
background-color: #003b4e;
display: block;
border-radius: 50%;
opacity: 0.4;
}
.typing-indicator span:nth-of-type(1) {
-webkit-animation: 1s blink infinite 0.3333s;
animation: 1s blink infinite 0.3333s;
}
.typing-indicator span:nth-of-type(2) {
-webkit-animation: 1s blink infinite 0.6666s;
animation: 1s blink infinite 0.6666s;
}
.typing-indicator span:nth-of-type(3) {
-webkit-animation: 1s blink infinite 0.9999s;
animation: 1s blink infinite 0.9999s;
}
@-webkit-keyframes blink {
50% {
opacity: 1;
}
}
@keyframes blink {
50% {
opacity: 1;
}
}
@-webkit-keyframes bulge {
50% {
-webkit-transform: scale(1.05);
transform: scale(1.05);
}
}
@keyframes bulge {
50% {
-webkit-transform: scale(1.05);
transform: scale(1.05);
}
}
\ No newline at end of file
......@@ -86,6 +86,7 @@
</div>
<div id="container">
<div id="messages" onscroll="onScrolled()"></div>
<div id="back_to_bottom_button_container">
<div id="back_to_bottom_button" onclick="back_to_bottom()">Jump to latest &#9660;</div>
</div>
......
......@@ -376,6 +376,12 @@ function grow_text_area() {
var total_size = parseInt(msgbar_size) + parseInt(new_height) - parseInt(old_height)
document.body.style.setProperty("--messagebar-size", total_size.toString() + "px")
if (use_qt) {
window.jsbridge.onComposing(messageBarInput.value.length !== 0)
} else {
window.prompt(`ON_COMPOSING:${messageBarInput.value.length !== 0}`)
}
}, [])
}
......@@ -389,6 +395,7 @@ function grow_text_area() {
function process_messagebar_keydown(key) {
key = key || event
var map = {}
map[key.keyCode] = key.type == "keydown"
if (key.ctrlKey && map[13]) {
messageBarInput.value += "\n"
......@@ -1728,8 +1735,15 @@ function addOrUpdateMessage(message_object, new_message, insert_after = true, me
computeSequencing(previousMessage, message_div, null, insert_after)
if (previousMessage) {
previousMessage.classList.remove("last_message")
console.log(previousMessage)
if (previousMessage.id === "message_typing") {
previousMessage.parentNode.removeChild(previousMessage)
console.log(previousMessage)
message_div.parentNode.appendChild(previousMessage)
} else {
message_div.classList.add("last_message")
}
}
message_div.classList.add("last_message")
/* When inserting at the bottom we should also check that the
previously sent message does not have the same timestamp.
......@@ -2135,7 +2149,7 @@ function setSenderImage(set_sender_image_object)
if (use_qt) {
var sender_contact_method = set_sender_image_object["sender_contact_method"].replace(/@/g, "_").replace(/\./g, "_"),
sender_image = set_sender_image_object["sender_image"],
sender_image_id = "sender_image_" + sender_contact_method,
contactUri = "sender_image_" + sender_contact_method,
invite_sender_image_id = "invite_sender_image_" + sender_contact_method,
currentSenderImage = document.getElementById(sender_image_id), // Remove the currently set sender image
style, invite_style
......@@ -2158,17 +2172,59 @@ function setSenderImage(set_sender_image_object)
style.type = "text/css"
style.id = sender_image_id
style.innerHTML = "." + sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 2.25em;width: 2.25em;}"
style.innerHTML = "." + sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 32px; width: 32px;}"
document.head.appendChild(style)
invite_style = document.createElement("style")
invite_style.type = "text/css"
invite_style.id = invite_sender_image_id
invite_style.innerHTML = "." + invite_sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 48px;width: 48px;}"
invite_style.innerHTML = "." + invite_sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 48px; width: 48px;}"
document.head.appendChild(invite_style)
}
/**
* Show Typing indicator
*/
/* exported showTypingIndicator */
function showTypingIndicator(contactUri, isTyping) {
var message_div = messages.lastChild.querySelector("#message_typing")
if (!isTyping) {
if (message_div) {
message_div.style.display = 'none'
}
} else {
if (message_div) {
message_div.parentNode.removeChild(message_div)
}
message_div = buildNewMessage({
"id":"typing",
"type":"text",
"text":"",
"direction":"in",
"delivery_status":"",
"sender_contact_method": contactUri
})
var previousMessage = messages.lastChild.lastChild
messages.lastChild.appendChild(message_div)
computeSequencing(previousMessage, message_div, null, true)
if (previousMessage) {
previousMessage.classList.remove("last_message")
}
message_div.classList.add("last_message")
let msg_text = message_div.querySelector(".message_text")
msg_text.innerHTML = " \
<div class=\"typing-indicator\"> \
<span></span> \
<span></span> \
<span></span> \
</div>"
}
updateMesPos()
}
/**
* Copy Mouse Selected Text and return it
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment