Carbon copies basic support

This commit is contained in:
Blue 2019-04-12 18:22:10 +03:00
parent 6e9e100188
commit 48e735b0e9
10 changed files with 215 additions and 52 deletions

View File

@ -25,6 +25,7 @@ add_subdirectory(core)
target_link_libraries(squawk squawkUI) target_link_libraries(squawk squawkUI)
target_link_libraries(squawk squawkCORE) target_link_libraries(squawk squawkCORE)
target_link_libraries(squawk uuid)
# Install the executable # Install the executable
install(TARGETS squawk DESTINATION bin) install(TARGETS squawk DESTINATION bin)

View File

@ -1,5 +1,4 @@
#include "account.h" #include "account.h"
#include <qxmpp/QXmppRosterManager.h>
#include <qxmpp/QXmppMessage.h> #include <qxmpp/QXmppMessage.h>
#include <QDateTime> #include <QDateTime>
@ -12,7 +11,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
config(), config(),
presence(), presence(),
state(Shared::disconnected), state(Shared::disconnected),
groups() groups(),
cm(new QXmppCarbonManager())
{ {
config.setUser(p_login); config.setUser(p_login);
config.setDomain(p_server); config.setDomain(p_server);
@ -30,6 +30,11 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&))); QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&)));
QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&))); QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&)));
//QObject::connect(&rm, SIGNAL(presenceChanged(const QString&, const QString&)), this, SLOT(onRosterPresenceChanged(const QString&, const QString&))); //QObject::connect(&rm, SIGNAL(presenceChanged(const QString&, const QString&)), this, SLOT(onRosterPresenceChanged(const QString&, const QString&)));
client.addExtension(cm);
QObject::connect(cm, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onCarbonMessageReceived(const QXmppMessage&)));
QObject::connect(cm, SIGNAL(messageSent(const QXmppMessage&)), this, SLOT(onCarbonMessageSent(const QXmppMessage&)));
} }
Account::~Account() Account::~Account()
@ -65,6 +70,7 @@ void Core::Account::onClientConnected()
{ {
if (state == Shared::connecting) { if (state == Shared::connecting) {
state = Shared::connected; state = Shared::connected;
cm->setCarbonsEnabled(true);
emit connectionStateChanged(state); emit connectionStateChanged(state);
} else { } else {
qDebug("Something weird had happened - xmpp client reported about successful connection but account wasn't in connecting state"); qDebug("Something weird had happened - xmpp client reported about successful connection but account wasn't in connecting state");
@ -335,36 +341,13 @@ void Core::Account::setResource(const QString& p_resource)
void Core::Account::onMessageReceived(const QXmppMessage& msg) void Core::Account::onMessageReceived(const QXmppMessage& msg)
{ {
QString from = msg.from();
QString to = msg.to();
bool handled = false; bool handled = false;
switch (msg.type()) { switch (msg.type()) {
case QXmppMessage::Normal: case QXmppMessage::Normal:
qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping";
break; break;
case QXmppMessage::Chat:{ case QXmppMessage::Chat:
QString body(msg.body()); handled = handleChatMessage(msg);
if (body.size() != 0) {
QString id(msg.id());
QDateTime time(msg.stamp());
Shared::Message sMsg(Shared::Message::chat);
sMsg.setId(id);
sMsg.setFrom(from);
sMsg.setTo(to);
sMsg.setBody(body);
if (time.isValid()) {
sMsg.setTime(time);
}
emit message(sMsg);
if (msg.isReceiptRequested() && id.size() > 0) {
QXmppMessage receipt(getFullJid(), from, "");
receipt.setReceiptId(id);
client.sendPacket(receipt);
handled = true;
}
}
}
break; break;
case QXmppMessage::GroupChat: case QXmppMessage::GroupChat:
qDebug() << "received a message with type \"GroupChat\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"GroupChat\", not sure what to do with it now, skipping";
@ -377,10 +360,9 @@ void Core::Account::onMessageReceived(const QXmppMessage& msg)
break; break;
} }
if (!handled) { if (!handled) {
qDebug() << "Message wasn't handled: "; qDebug() << "Message wasn't handled: ";
qDebug() << "- from: " << from; qDebug() << "- from: " << msg.from();
qDebug() << "- to: " << to; qDebug() << "- to: " << msg.to();
qDebug() << "- body: " << msg.body(); qDebug() << "- body: " << msg.body();
qDebug() << "- type: " << msg.type(); qDebug() << "- type: " << msg.type();
qDebug() << "- state: " << msg.state(); qDebug() << "- state: " << msg.state();
@ -404,9 +386,52 @@ QString Core::Account::getFullJid() const
void Core::Account::sendMessage(const Shared::Message& data) void Core::Account::sendMessage(const Shared::Message& data)
{ {
if (state == Shared::connected) { if (state == Shared::connected) {
client.sendMessage(data.getTo(), data.getBody()); QXmppMessage msg(data.getFrom(), data.getTo(), data.getBody(), data.getThread());
msg.setId(data.getId());
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
client.sendPacket(msg);
} else { } else {
qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; qDebug() << "An attempt to send message with not connected account " << name << ", skipping";
} }
} }
void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg)
{
handleChatMessage(msg, false, true);
}
void Core::Account::onCarbonMessageSent(const QXmppMessage& msg)
{
handleChatMessage(msg, true, true);
}
bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded)
{
QString body(msg.body());
if (body.size() != 0) {
QString id(msg.id());
QDateTime time(msg.stamp());
Shared::Message sMsg(Shared::Message::chat);
sMsg.setId(id);
sMsg.setFrom(msg.from());
sMsg.setTo(msg.to());
sMsg.setBody(body);
sMsg.setForwarded(forwarded);
sMsg.setOutgoing(outgoing);
if (time.isValid()) {
sMsg.setTime(time);
}
emit message(sMsg);
if (!forwarded && !outgoing) {
if (msg.isReceiptRequested() && id.size() > 0) {
QXmppMessage receipt(getFullJid(), msg.from(), "");
receipt.setReceiptId(id);
client.sendPacket(receipt);
}
}
return true;
}
return false;
}

View File

@ -5,6 +5,8 @@
#include <map> #include <map>
#include <set> #include <set>
#include <qxmpp/QXmppRosterManager.h>
#include <qxmpp/QXmppCarbonManager.h>
#include <qxmpp/QXmppClient.h> #include <qxmpp/QXmppClient.h>
#include "../global.h" #include "../global.h"
@ -58,6 +60,7 @@ private:
QXmppPresence presence; QXmppPresence presence;
Shared::ConnectionState state; Shared::ConnectionState state;
std::map<QString, std::set<QString>> groups; std::map<QString, std::set<QString>> groups;
QXmppCarbonManager* cm;
private slots: private slots:
void onClientConnected(); void onClientConnected();
@ -69,9 +72,12 @@ private slots:
void onRosterPresenceChanged(const QString& bareJid, const QString& resource); void onRosterPresenceChanged(const QString& bareJid, const QString& resource);
void onPresenceReceived(const QXmppPresence& presence); void onPresenceReceived(const QXmppPresence& presence);
void onMessageReceived(const QXmppMessage& message); void onMessageReceived(const QXmppMessage& message);
void onCarbonMessageReceived(const QXmppMessage& message);
void onCarbonMessageSent(const QXmppMessage& message);
private: private:
void addedAccount(const QString &bareJid); void addedAccount(const QString &bareJid);
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false);
}; };
} }

View File

@ -1,4 +1,5 @@
#include "global.h" #include "global.h"
#include <uuid/uuid.h>
Shared::Message::Message(Shared::Message::Type p_type): Shared::Message::Message(Shared::Message::Type p_type):
jFrom(), jFrom(),
@ -8,8 +9,10 @@ Shared::Message::Message(Shared::Message::Type p_type):
id(), id(),
body(), body(),
time(), time(),
thread(),
type(p_type), type(p_type),
outgoing(false) outgoing(false),
forwarded(false)
{ {
} }
@ -21,8 +24,10 @@ Shared::Message::Message():
id(), id(),
body(), body(),
time(), time(),
thread(),
type(Message::normal), type(Message::normal),
outgoing(false) outgoing(false),
forwarded(false)
{ {
} }
@ -164,3 +169,42 @@ void Shared::Message::setOutgoing(bool og)
outgoing = og; outgoing = og;
} }
bool Shared::Message::getForwarded() const
{
return forwarded;
}
void Shared::Message::generateRandomId()
{
uuid_t uuid;
uuid_generate(uuid);
char uuid_str[37];
uuid_unparse_lower(uuid, uuid_str);
id = uuid_str;
}
QString Shared::Message::getThread() const
{
return thread;
}
void Shared::Message::setForwarded(bool fwd)
{
forwarded = fwd;
}
void Shared::Message::setThread(const QString& p_body)
{
thread = p_body;
}
Shared::Message::Type Shared::Message::getType() const
{
return type;
}
void Shared::Message::setType(Shared::Message::Type t)
{
type = t;
}

View File

@ -73,7 +73,10 @@ public:
void setTime(const QDateTime& p_time); void setTime(const QDateTime& p_time);
void setId(const QString& p_id); void setId(const QString& p_id);
void setBody(const QString& p_body); void setBody(const QString& p_body);
void setThread(const QString& p_body);
void setOutgoing(bool og); void setOutgoing(bool og);
void setForwarded(bool fwd);
void setType(Type t);
QString getFrom() const; QString getFrom() const;
QString getFromJid() const; QString getFromJid() const;
@ -84,10 +87,14 @@ public:
QDateTime getTime() const; QDateTime getTime() const;
QString getId() const; QString getId() const;
QString getBody() const; QString getBody() const;
QString getThread() const;
bool getOutgoing() const; bool getOutgoing() const;
bool getForwarded() const;
Type getType() const;
QString getPenPalJid() const; QString getPenPalJid() const;
QString getPenPalResource() const; QString getPenPalResource() const;
void generateRandomId();
private: private:
QString jFrom; QString jFrom;
@ -97,8 +104,10 @@ private:
QString id; QString id;
QString body; QString body;
QDateTime time; QDateTime time;
QString thread;
Type type; Type type;
bool outgoing; bool outgoing;
bool forwarded;
}; };
}; };

View File

@ -19,13 +19,16 @@
#include "conversation.h" #include "conversation.h"
#include "ui_conversation.h" #include "ui_conversation.h"
#include <QDebug> #include <QDebug>
#include <QScrollBar>
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),
line(new MessageLine()), line(new MessageLine()),
ker() ker(),
activePalResource(),
thread()
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->splitter->setSizes({300, 0}); m_ui->splitter->setSizes({300, 0});
@ -47,7 +50,10 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent):
addMessage(*itr); addMessage(*itr);
} }
line->setMyName(p_contact->getAccountName());
m_ui->scrollArea->setWidget(line); m_ui->scrollArea->setWidget(line);
m_ui->scrollArea->verticalScrollBar()->setBackgroundRole(QPalette::Base);
} }
Conversation::~Conversation() Conversation::~Conversation()
@ -61,6 +67,7 @@ void Conversation::setName(const QString& name)
m_ui->nameLabel->setText(getJid()); m_ui->nameLabel->setText(getJid());
} else { } else {
m_ui->nameLabel->setText(name); m_ui->nameLabel->setText(name);
line->setPalName(getJid(), name);
} }
} }
@ -101,7 +108,19 @@ void Conversation::onContactChanged(Models::Item* item, int row, int col)
void Conversation::addMessage(const Shared::Message& data) void Conversation::addMessage(const Shared::Message& data)
{ {
int pos = m_ui->scrollArea->verticalScrollBar()->sliderPosition();
int max = m_ui->scrollArea->verticalScrollBar()->maximum();
line->message(data); line->message(data);
if (pos == max) {
m_ui->scrollArea->verticalScrollBar()->setSliderPosition(m_ui->scrollArea->verticalScrollBar()->maximum());
}
if (!data.getOutgoing()) {
const QString& res = data.getPenPalResource();
if (res.size() > 0) {
setPalResource(res);
}
}
} }
KeyEnterReceiver::KeyEnterReceiver(QObject* parent): QObject(parent), ownEvent(false) {} KeyEnterReceiver::KeyEnterReceiver(QObject* parent): QObject(parent), ownEvent(false) {}
@ -133,17 +152,33 @@ bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event)
return QObject::eventFilter(obj, event); return QObject::eventFilter(obj, event);
} }
QString Conversation::getPalResource() const
{
return activePalResource;
}
void Conversation::setPalResource(const QString& res)
{
activePalResource = res;
}
void Conversation::onEnterPressed() void Conversation::onEnterPressed()
{ {
QString body(m_ui->messageEditor->toPlainText()); QString body(m_ui->messageEditor->toPlainText());
const QString& aJid = contact->getAccountJid();
m_ui->messageEditor->clear(); if (body.size() > 0) {
Shared::Message msg(Shared::Message::chat); const QString& aJid = contact->getAccountJid();
msg.setFromJid(aJid); m_ui->messageEditor->clear();
msg.setFromResource(contact->getAccountResource()); Shared::Message msg(Shared::Message::chat);
msg.setTo(contact->getJid()); msg.setFromJid(aJid);
msg.setBody(body); msg.setFromResource(contact->getAccountResource());
msg.setOutgoing(true); qDebug() << "sending message from " << contact->getAccountResource();
line->message(msg); msg.setToJid(contact->getJid());
emit sendMessage(msg); msg.setToResource(activePalResource);
msg.setBody(body);
msg.setOutgoing(true);
msg.generateRandomId();
line->message(msg);
emit sendMessage(msg);
}
} }

View File

@ -52,8 +52,11 @@ public:
QString getJid() const; QString getJid() const;
QString getAccount() const; QString getAccount() const;
QString getPalResource() const;
void addMessage(const Shared::Message& data); void addMessage(const Shared::Message& data);
void setPalResource(const QString& res);
signals: signals:
void sendMessage(const Shared::Message& message); void sendMessage(const Shared::Message& message);
@ -71,6 +74,8 @@ private:
MessageLine* line; MessageLine* line;
QScopedPointer<Ui::Conversation> m_ui; QScopedPointer<Ui::Conversation> m_ui;
KeyEnterReceiver ker; KeyEnterReceiver ker;
QString activePalResource;
QString thread;
}; };
#endif // CONVERSATION_H #endif // CONVERSATION_H

View File

@ -1,6 +1,6 @@
/* /*
* <one line to give the program's name and a brief idea of what it does.> * <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 Юрий Губич <y.gubich@initi.ru> * Copyright (C) 2019 Yury Gubich <blue@macaw.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,7 +22,9 @@ MessageLine::MessageLine(QWidget* parent):
QWidget(parent), QWidget(parent),
messageIndex(), messageIndex(),
messageOrder(), messageOrder(),
layout(new QVBoxLayout()) layout(new QVBoxLayout()),
myName(),
palNames()
{ {
setLayout(layout); setLayout(layout);
setBackgroundRole(QPalette::Base); setBackgroundRole(QPalette::Base);
@ -39,12 +41,11 @@ void MessageLine::message(const Shared::Message& msg)
QHBoxLayout* hBox = new QHBoxLayout(); QHBoxLayout* hBox = new QHBoxLayout();
QWidget* message = new QWidget(); QWidget* message = new QWidget();
message->setLayout(vBox); message->setLayout(vBox);
//message->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
message->setBackgroundRole(QPalette::AlternateBase); message->setBackgroundRole(QPalette::AlternateBase);
message->setAutoFillBackground(true);; message->setAutoFillBackground(true);;
QLabel* body = new QLabel(msg.getBody()); QLabel* body = new QLabel(msg.getBody());
QLabel* sender = new QLabel(msg.getFrom()); QLabel* sender = new QLabel();
QFont f; QFont f;
f.setBold(true); f.setBold(true);
sender->setFont(f); sender->setFont(f);
@ -57,9 +58,17 @@ void MessageLine::message(const Shared::Message& msg)
if (msg.getOutgoing()) { if (msg.getOutgoing()) {
body->setAlignment(Qt::AlignRight); body->setAlignment(Qt::AlignRight);
sender->setAlignment(Qt::AlignRight); sender->setAlignment(Qt::AlignRight);
sender->setText(myName);
hBox->addStretch(); hBox->addStretch();
hBox->addWidget(message); hBox->addWidget(message);
} else { } else {
QString jid = msg.getFromJid();
std::map<QString, QString>::iterator itr = palNames.find(jid);
if (itr != palNames.end()) {
sender->setText(itr->second);
} else {
sender->setText(jid);
}
hBox->addWidget(message); hBox->addWidget(message);
hBox->addStretch(); hBox->addStretch();
} }
@ -67,3 +76,17 @@ void MessageLine::message(const Shared::Message& msg)
layout->addLayout(hBox); layout->addLayout(hBox);
} }
void MessageLine::setMyName(const QString& name)
{
myName = name;
}
void MessageLine::setPalName(const QString& jid, const QString& name)
{
std::map<QString, QString>::iterator itr = palNames.find(jid);
if (itr == palNames.end()) {
palNames.insert(std::make_pair(jid, name));
} else {
itr->second = name;
}
}

View File

@ -1,6 +1,6 @@
/* /*
* <one line to give the program's name and a brief idea of what it does.> * <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 Юрий Губич <y.gubich@initi.ru> * Copyright (C) 2019 Yury Gubich <blue@macaw.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -34,12 +34,17 @@ public:
~MessageLine(); ~MessageLine();
void message(const Shared::Message& msg); void message(const Shared::Message& msg);
void setMyName(const QString& name);
void setPalName(const QString& jid, const QString& name);
private: private:
typedef W::Order<Shared::Message*> Order; typedef W::Order<Shared::Message*> Order;
std::map<QString, Shared::Message*> messageIndex; std::map<QString, Shared::Message*> messageIndex;
Order messageOrder; Order messageOrder;
QVBoxLayout* layout; QVBoxLayout* layout;
QString myName;
std::map<QString, QString> palNames;
}; };
#endif // MESSAGELINE_H #endif // MESSAGELINE_H

View File

@ -156,12 +156,14 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
if (item.isValid()) { if (item.isValid()) {
Models::Item* node = static_cast<Models::Item*>(item.internalPointer()); Models::Item* node = static_cast<Models::Item*>(item.internalPointer());
Models::Contact* contact = 0; Models::Contact* contact = 0;
QString res;
switch (node->type) { switch (node->type) {
case Models::Item::contact: case Models::Item::contact:
contact = static_cast<Models::Contact*>(node); contact = static_cast<Models::Contact*>(node);
break; break;
case Models::Item::presence: case Models::Item::presence:
contact = static_cast<Models::Contact*>(node->parentItem()); contact = static_cast<Models::Contact*>(node->parentItem());
res = node->getName();
break; break;
default: default:
m_ui->roster->expand(item); m_ui->roster->expand(item);
@ -177,6 +179,10 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
itr->second->show(); itr->second->show();
itr->second->raise(); itr->second->raise();
itr->second->activateWindow(); itr->second->activateWindow();
if (res.size() > 0) {
itr->second->setPalResource(res);
}
} else { } else {
Conversation* conv = new Conversation(contact); Conversation* conv = new Conversation(contact);
@ -188,6 +194,10 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
rosterModel.dropMessages(account, jid); rosterModel.dropMessages(account, jid);
conv->show(); conv->show();
if (res.size() > 0) {
itr->second->setPalResource(res);
}
} }
} }
} }
@ -209,11 +219,11 @@ void Squawk::accountMessage(const QString& account, const Shared::Message& data)
const QString& from = data.getPenPalJid(); const QString& from = data.getPenPalJid();
Conversations::iterator itr = conversations.find({account, from}); Conversations::iterator itr = conversations.find({account, from});
if (itr != conversations.end()) { if (itr != conversations.end()) {
qDebug() << "adding message";
itr->second->addMessage(data); itr->second->addMessage(data);
} else { } else {
qDebug() << "pending message"; if (!data.getForwarded()) {
rosterModel.addMessage(account, data); rosterModel.addMessage(account, data);
}
} }
} }