1
0
forked from blue/squawk

primitive messages receiving

This commit is contained in:
Blue 2019-04-10 01:01:25 +03:00
parent 4775c7b700
commit 3cc53dfaf6
15 changed files with 264 additions and 22 deletions

View File

@ -333,20 +333,76 @@ void Core::Account::setResource(const QString& p_resource)
config.setResource(p_resource); config.setResource(p_resource);
} }
void Core::Account::onMessageReceived(const QXmppMessage& message) void Core::Account::onMessageReceived(const QXmppMessage& msg)
{ {
qDebug() << "Message received: "; QString from = msg.from();
qDebug() << "- from: " << message.from(); QStringList fcomps = from.split("/");
qDebug() << "- to: " << message.to(); QString fjid = fcomps.front();
qDebug() << "- body: " << message.body(); QString fresource = fcomps.back();
qDebug() << "- type: " << message.type();
qDebug() << "- state: " << message.state(); QString to = msg.to();
qDebug() << "- stamp: " << message.stamp(); QStringList tcomps = to.split("/");
qDebug() << "- id: " << message.id(); QString tjid = tcomps.front();
qDebug() << "- isAttentionRequested: " << message.isAttentionRequested(); QString tresource = tcomps.back();
qDebug() << "- isReceiptRequested: " << message.isReceiptRequested(); bool handled = false;
qDebug() << "- receiptId: " << message.receiptId(); switch (msg.type()) {
qDebug() << "- subject: " << message.subject(); case QXmppMessage::Normal:
qDebug() << "- thread: " << message.thread(); qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping";
qDebug() << "- isMarkable: " << message.isMarkable(); break;
case QXmppMessage::Chat:{
QString body(msg.body());
if (body.size() != 0) {
QString id(msg.id());
emit message({
{"body", body},
{"from", fjid},
{"to", tjid},
{"fromResource", fresource},
{"toResource", tresource},
{"id", id}
});
if (msg.isReceiptRequested() && id.size() > 0) {
QXmppMessage receipt(getFullJid(), from, "");
receipt.setReceiptId(id);
client.sendPacket(receipt);
handled = true;
} }
}
}
break;
case QXmppMessage::GroupChat:
qDebug() << "received a message with type \"GroupChat\", not sure what to do with it now, skipping";
break;
case QXmppMessage::Error:
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
break;
case QXmppMessage::Headline:
qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping";
break;
}
if (!handled) {
qDebug() << "Message wasn't handled: ";
qDebug() << "- from: " << msg.from();
qDebug() << "- to: " << msg.to();
qDebug() << "- body: " << msg.body();
qDebug() << "- type: " << msg.type();
qDebug() << "- state: " << msg.state();
qDebug() << "- stamp: " << msg.stamp();
qDebug() << "- id: " << msg.id();
qDebug() << "- isAttentionRequested: " << msg.isAttentionRequested();
qDebug() << "- isReceiptRequested: " << msg.isReceiptRequested();
qDebug() << "- receiptId: " << msg.receiptId();
qDebug() << "- subject: " << msg.subject();
qDebug() << "- thread: " << msg.thread();
qDebug() << "- isMarkable: " << msg.isMarkable();
qDebug() << "==============================";
}
}
QString Core::Account::getFullJid() const
{
return getLogin() + "@" + getServer() + "/" + getResource();
}

View File

@ -35,6 +35,7 @@ public:
void setPassword(const QString& p_password); void setPassword(const QString& p_password);
void setResource(const QString& p_resource); void setResource(const QString& p_resource);
void setAvailability(Shared::Availability avail); void setAvailability(Shared::Availability avail);
QString getFullJid() const;
signals: signals:
void connectionStateChanged(int); void connectionStateChanged(int);
@ -47,6 +48,7 @@ signals:
void changeContact(const QString& jid, const QMap<QString, QVariant>& data); void changeContact(const QString& jid, const QMap<QString, QVariant>& data);
void addPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data); void addPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& jid, const QString& name); void removePresence(const QString& jid, const QString& name);
void message(const QMap<QString, QString>& data);
private: private:
QString name; QString name;

View File

@ -86,6 +86,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
connect(acc, SIGNAL(addPresence(const QString&, const QString&, const QMap<QString, QVariant>&)), connect(acc, SIGNAL(addPresence(const QString&, const QString&, const QMap<QString, QVariant>&)),
this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap<QString, QVariant>&))); this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&))); connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&)));
connect(acc, SIGNAL(message(const QMap<QString, QString>&)), this, SLOT(onAccountMessage(const QMap<QString, QString>&)));
QMap<QString, QVariant> map = { QMap<QString, QVariant> map = {
{"login", login}, {"login", login},
@ -192,3 +193,9 @@ void Core::Squawk::onAccountAvailabilityChanged(int state)
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit accountAvailabilityChanged(acc->getName(), state); emit accountAvailabilityChanged(acc->getName(), state);
} }
void Core::Squawk::onAccountMessage(const QMap<QString, QString>& data)
{
Account* acc = static_cast<Account*>(sender());
emit accountMessage(acc->getName(), data);
}

View File

@ -35,6 +35,7 @@ signals:
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data); void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& account, const QString& jid, const QString& name); void removePresence(const QString& account, const QString& jid, const QString& name);
void stateChanged(int state); void stateChanged(int state);
void accountMessage(const QString& account, const QMap<QString, QString>& data);
public slots: public slots:
void start(); void start();
@ -66,6 +67,7 @@ private slots:
void onAccountChangeContact(const QString& jid, const QMap<QString, QVariant>& data); void onAccountChangeContact(const QString& jid, const QMap<QString, QVariant>& data);
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data); void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void onAccountRemovePresence(const QString& jid, const QString& name); void onAccountRemovePresence(const QString& jid, const QString& name);
void onAccountMessage(const QMap<QString, QString>& data);
}; };
} }

View File

@ -8,6 +8,8 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
qRegisterMetaType<QMap<QString, QString>>("QMap<QString,QString>");
QApplication app(argc, argv); QApplication app(argc, argv);
SignalCatcher sc(&app); SignalCatcher sc(&app);
@ -48,6 +50,7 @@ int main(int argc, char *argv[])
&w, SLOT(addPresence(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&))); &w, SLOT(addPresence(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)));
QObject::connect(squawk, SIGNAL(removePresence(const QString&, const QString&, const QString&)), &w, SLOT(removePresence(const QString&, const QString&, const QString&))); QObject::connect(squawk, SIGNAL(removePresence(const QString&, const QString&, const QString&)), &w, SLOT(removePresence(const QString&, const QString&, const QString&)));
QObject::connect(squawk, SIGNAL(stateChanged(int)), &w, SLOT(stateChanged(int))); QObject::connect(squawk, SIGNAL(stateChanged(int)), &w, SLOT(stateChanged(int)));
QObject::connect(squawk, SIGNAL(accountMessage(const QString&, const QMap<QString, QString>&)), &w, SLOT(accountMessage(const QString&, const QMap<QString, QString>&)));
coreThread->start(); coreThread->start();

View File

@ -23,7 +23,8 @@
Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): Conversation::Conversation(Models::Contact* p_contact, QWidget* parent):
QWidget(parent), QWidget(parent),
contact(p_contact), contact(p_contact),
m_ui(new Ui::Conversation) m_ui(new Ui::Conversation),
ker()
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->splitter->setSizes({300, 0}); m_ui->splitter->setSizes({300, 0});
@ -33,6 +34,9 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent):
setState(p_contact->getAvailability()); setState(p_contact->getAvailability());
connect(contact, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onContactChanged(Models::Item*, int, int))); connect(contact, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onContactChanged(Models::Item*, int, int)));
connect(&ker, SIGNAL(enterPressed()), this, SLOT(onEnterPressed()));
m_ui->dialogBox->installEventFilter(&ker);
} }
Conversation::~Conversation() Conversation::~Conversation()
@ -83,3 +87,29 @@ void Conversation::onContactChanged(Models::Item* item, int row, int col)
} }
} }
} }
void Conversation::addMessage(const QMap<QString, QString>& data)
{
m_ui->dialogBox->append(data.value("from") + ": " + data.value("body"));
}
bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event)
{
if (event->type()==QEvent::KeyPress) {
QKeyEvent* key = static_cast<QKeyEvent*>(event);
if ( (key->key()==Qt::Key_Enter) || (key->key()==Qt::Key_Return) ) {
emit enterPressed();
} else {
return QObject::eventFilter(obj, event);
}
return true;
} else {
return QObject::eventFilter(obj, event);
}
return false;
}
void Conversation::onEnterPressed()
{
qDebug() << "enter";
}

View File

@ -29,6 +29,16 @@ namespace Ui
class Conversation; class Conversation;
} }
class KeyEnterReceiver : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject* obj, QEvent* event);
signals:
void enterPressed();
};
class Conversation : public QWidget class Conversation : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -38,6 +48,7 @@ public:
QString getJid() const; QString getJid() const;
QString getAccount() const; QString getAccount() const;
void addMessage(const QMap<QString, QString>& data);
protected: protected:
void setState(Shared::Availability state); void setState(Shared::Availability state);
@ -46,10 +57,12 @@ protected:
protected slots: protected slots:
void onContactChanged(Models::Item* item, int row, int col); void onContactChanged(Models::Item* item, int row, int col);
void onEnterPressed();
private: private:
Models::Contact* contact; Models::Contact* contact;
QScopedPointer<Ui::Conversation> m_ui; QScopedPointer<Ui::Conversation> m_ui;
KeyEnterReceiver ker;
}; };
#endif // CONVERSATION_H #endif // CONVERSATION_H

View File

@ -6,7 +6,9 @@ Models::Contact::Contact(const QString& p_jid ,const QMap<QString, QVariant> &da
jid(p_jid), jid(p_jid),
availability(Shared::offline), availability(Shared::offline),
state(Shared::none), state(Shared::none),
presences() presences(),
messages(),
childMessages(0)
{ {
QMap<QString, QVariant>::const_iterator itr = data.find("state"); QMap<QString, QVariant>::const_iterator itr = data.find("state");
if (itr != data.end()) { if (itr != data.end()) {
@ -66,7 +68,7 @@ void Models::Contact::setAvailability(Shared::Availability p_state)
int Models::Contact::columnCount() const int Models::Contact::columnCount() const
{ {
return 4; return 5;
} }
QVariant Models::Contact::data(int column) const QVariant Models::Contact::data(int column) const
@ -84,6 +86,8 @@ QVariant Models::Contact::data(int column) const
return state; return state;
case 3: case 3:
return availability; return availability;
case 4:
return getMessagesCount();
default: default:
return QVariant(); return QVariant();
} }
@ -136,9 +140,11 @@ void Models::Contact::refresh()
{ {
QDateTime lastActivity; QDateTime lastActivity;
Presence* presence = 0; Presence* presence = 0;
unsigned int count = 0;
for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
Presence* pr = itr.value(); Presence* pr = itr.value();
QDateTime la = pr->getLastActivity(); QDateTime la = pr->getLastActivity();
count += pr->getMessagesCount();
if (la > lastActivity) { if (la > lastActivity) {
lastActivity = la; lastActivity = la;
@ -151,6 +157,11 @@ void Models::Contact::refresh()
} else { } else {
setAvailability(Shared::offline); setAvailability(Shared::offline);
} }
if (childMessages != count) {
childMessages = count;
changed(4);
}
} }
void Models::Contact::_removeChild(int index) void Models::Contact::_removeChild(int index)
@ -183,7 +194,9 @@ void Models::Contact::setState(Shared::SubscriptionState p_state)
QIcon Models::Contact::getStatusIcon() const QIcon Models::Contact::getStatusIcon() const
{ {
if (state == Shared::both) { if (getMessagesCount() > 0) {
return QIcon::fromTheme("mail-message");
} else if (state == Shared::both) {
return QIcon::fromTheme(Shared::availabilityThemeIcons[availability]); return QIcon::fromTheme(Shared::availabilityThemeIcons[availability]);
} else { } else {
return QIcon::fromTheme(Shared::subscriptionStateThemeIcons[state]); return QIcon::fromTheme(Shared::subscriptionStateThemeIcons[state]);
@ -204,3 +217,35 @@ QString Models::Contact::getAccountName() const
return p->getName(); return p->getName();
} }
void Models::Contact::addMessage(const QMap<QString, QString>& data)
{
const QString& res = data.value("fromResource");
if (res.size() > 0) {
QMap<QString, Presence*>::iterator itr = presences.find(res);
if (itr == presences.end()) {
qDebug() << "An attempt to add message to the roster to the unknown resource " << res << " of contact " << jid << " in account " << getAccountName() << ", skipping";
return;
}
itr.value()->addMessage(data);
} else {
messages.emplace_back(data);
changed(4);
}
}
unsigned int Models::Contact::getMessagesCount() const
{
return messages.size() + childMessages;
}
void Models::Contact::dropMessages()
{
if (messages.size() > 0) {
messages.clear();
changed(4);
}
for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
itr.value()->dropMessages();
}
}

View File

@ -6,6 +6,7 @@
#include "../../global.h" #include "../../global.h"
#include <QMap> #include <QMap>
#include <QIcon> #include <QIcon>
#include <deque>
namespace Models { namespace Models {
@ -32,6 +33,10 @@ public:
void appendChild(Models::Item * child) override; void appendChild(Models::Item * child) override;
QString getAccountName() const; QString getAccountName() const;
void addMessage(const QMap<QString, QString>& data);
unsigned int getMessagesCount() const;
void dropMessages();
protected: protected:
void _removeChild(int index) override; void _removeChild(int index) override;
@ -46,10 +51,13 @@ protected:
void setJid(const QString p_jid); void setJid(const QString p_jid);
private: private:
typedef std::deque<QMap<QString, QString>> Messages;
QString jid; QString jid;
Shared::Availability availability; Shared::Availability availability;
Shared::SubscriptionState state; Shared::SubscriptionState state;
QMap<QString, Presence*> presences; QMap<QString, Presence*> presences;
Messages messages;
unsigned int childMessages;
}; };
} }

View File

@ -22,7 +22,8 @@ Models::Presence::Presence(const QMap<QString, QVariant>& data, Item* parentItem
Item(Item::presence, data, parentItem), Item(Item::presence, data, parentItem),
availability(Shared::offline), availability(Shared::offline),
lastActivity(data.value("lastActivity").toDateTime()), lastActivity(data.value("lastActivity").toDateTime()),
status(data.value("status").toString()) status(data.value("status").toString()),
messages()
{ {
QMap<QString, QVariant>::const_iterator itr = data.find("availability"); QMap<QString, QVariant>::const_iterator itr = data.find("availability");
if (itr != data.end()) { if (itr != data.end()) {
@ -36,7 +37,7 @@ Models::Presence::~Presence()
int Models::Presence::columnCount() const int Models::Presence::columnCount() const
{ {
return 4; return 5;
} }
QVariant Models::Presence::data(int column) const QVariant Models::Presence::data(int column) const
@ -50,6 +51,8 @@ QVariant Models::Presence::data(int column) const
return availability; return availability;
case 3: case 3:
return status; return status;
case 4:
return getMessagesCount();
default: default:
return QVariant(); return QVariant();
} }
@ -117,3 +120,32 @@ void Models::Presence::update(const QString& key, const QVariant& value)
setLastActivity(value.toDateTime()); setLastActivity(value.toDateTime());
} }
} }
unsigned int Models::Presence::getMessagesCount() const
{
return messages.size();
}
void Models::Presence::addMessage(const QMap<QString, QString>& data)
{
messages.emplace_back(data);
changed(4);
}
void Models::Presence::dropMessages()
{
if (messages.size() > 0) {
messages.clear();
changed(4);
}
}
QIcon Models::Presence::getStatusIcon() const
{
if (getMessagesCount() > 0) {
return QIcon::fromTheme("mail-message");
} else {
return QIcon::fromTheme(Shared::availabilityThemeIcons[availability]);
}
}

View File

@ -22,6 +22,7 @@
#include "item.h" #include "item.h"
#include "../../global.h" #include "../../global.h"
#include <QDateTime> #include <QDateTime>
#include <QIcon>
namespace Models { namespace Models {
@ -44,13 +45,19 @@ public:
QString getStatus() const; QString getStatus() const;
void setStatus(const QString& p_state); void setStatus(const QString& p_state);
QIcon getStatusIcon() const;
void update(const QString& key, const QVariant& value); void update(const QString& key, const QVariant& value);
unsigned int getMessagesCount() const;
void dropMessages();
void addMessage(const QMap<QString, QString>& data);
private: private:
typedef std::deque<QMap<QString, QString>> Messages;
Shared::Availability availability; Shared::Availability availability;
QDateTime lastActivity; QDateTime lastActivity;
QString status; QString status;
Messages messages;
}; };
} }

View File

@ -69,7 +69,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
break; break;
case Item::presence:{ case Item::presence:{
Presence* presence = static_cast<Presence*>(item); Presence* presence = static_cast<Presence*>(item);
result = QIcon::fromTheme(Shared::availabilityThemeIcons[presence->getAvailability()]); result = presence->getStatusIcon();
} }
break; break;
default: default:
@ -492,3 +492,23 @@ void Models::Roster::removePresence(const QString& account, const QString& jid,
cBeg->second->removePresence(name); cBeg->second->removePresence(name);
} }
} }
void Models::Roster::addMessage(const QString& account, const QMap<QString, QString>& data)
{
ElId id(account, data.value("from"));
std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(id);
std::multimap<ElId, Contact*>::iterator cEnd = contacts.upper_bound(id);
for (;cBeg != cEnd; ++cBeg) {
cBeg->second->addMessage(data);
}
}
void Models::Roster::dropMessages(const QString& account, const QString& jid)
{
ElId id(account, jid);
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(id), cEnd = contacts.upper_bound(id) ;cBeg != cEnd; ++cBeg) {
cBeg->second->dropMessages();
}
}

View File

@ -32,6 +32,8 @@ public:
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data); void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data); void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& account, const QString& jid, const QString& name); void removePresence(const QString& account, const QString& jid, const QString& name);
void addMessage(const QString& account, const QMap<QString, QString>& data);
void dropMessages(const QString& account, const QString& jid);
QVariant data ( const QModelIndex& index, int role ) const override; QVariant data ( const QModelIndex& index, int role ) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;

View File

@ -184,6 +184,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*)));
conversations.insert(std::make_pair(id, conv)); conversations.insert(std::make_pair(id, conv));
rosterModel.dropMessages(account, jid);
conv->show(); conv->show();
} }
@ -201,3 +202,16 @@ void Squawk::onConversationClosed(QObject* parent)
} }
conversations.erase(itr); conversations.erase(itr);
} }
void Squawk::accountMessage(const QString& account, const QMap<QString, QString>& data)
{
const QString& from = data.value("from");
Conversations::iterator itr = conversations.find({account, from});
if (itr != conversations.end()) {
qDebug() << "adding message";
itr->second->addMessage(data);
} else {
qDebug() << "pending message";
rosterModel.addMessage(account, data);
}
}

View File

@ -44,6 +44,7 @@ public slots:
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data); void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& account, const QString& jid, const QString& name); void removePresence(const QString& account, const QString& jid, const QString& name);
void stateChanged(int state); void stateChanged(int state);
void accountMessage(const QString& account, const QMap<QString, QString>& data);
private: private:
typedef std::map<Models::Roster::ElId, Conversation*> Conversations; typedef std::map<Models::Roster::ElId, Conversation*> Conversations;