#include "xxcc.h" #include "client.h" #include "direction.h" #include "accounts.h" #include "contacts.h" #include "conversation.h" #include "message.h" #include #include #include #include #include #include #include #include xxcc::xxcc(QWidget *const parent) : QWidget(parent), selected(nullptr) { const auto pairs = creds.load(); ui.setupUi(this); QScroller::grabGesture(ui.conversations_list, QScroller::TouchGesture); QScroller::grabGesture(ui.messages, QScroller::TouchGesture); connectAccounts(pairs); retrieveConversations(); connect(ui.accounts, &QPushButton::released, this, [this] { Accounts a(clients, this); a.connect(&a, &Accounts::new_account, this, &xxcc::addAccount); a.connect(&a, &Accounts::new_account, &creds, &Credentials::store); a.exec(); }); connect(ui.contacts, &QPushButton::released, this, [this] { Contacts c(clients, this); connect(&c, &Contacts::startChat, this, &xxcc::startChat); for (const auto cl : clients) { const auto db = &cl->database(); c.connect(db, &JidDb::addedToRoster, &c, [&c, db] (const QString jid) { c.add(db->jid, jid); }); } c.exec(); }); connect(ui.send, &QPushButton::released, this, &xxcc::send); connect(ui.back, &QPushButton::released, this, [this] { ui.sw->setCurrentIndex(Tab::Conversations); ui.messages->clear(); selected = nullptr; }); connect(ui.conversations_list, &QListWidget::itemActivated, this, [this] (QListWidgetItem *const it) { const auto conv = static_cast(it); for (const auto c : clients) if (c->jidBare() == conv->from) { selected = c; break; } if (selected) { const auto &db = selected->database(); static const auto n_messages = 20; const auto messages = db.getMessages(conv->to, n_messages); for (auto it = messages.rbegin(); it != messages.rend(); it++) new Message(it->body, it->dt, it->direction, ui.messages); } ui.sw->setCurrentIndex(Tab::Chat); ui.jid->setText(conv->to); ui.messages->scrollToBottom(); }); connect(ui.messages->model(), &QAbstractItemModel::rowsInserted, ui.messages, &QListWidget::scrollToBottom); } xxcc::~xxcc() { for (const auto c : clients) delete c; } void xxcc::connectAccounts(const QList &pairs) { for (const auto &p : pairs) { QXmppConfiguration cfg; cfg.setStreamSecurityMode(QXmppConfiguration::TLSRequired); cfg.setJid(p.first); cfg.setPassword(p.second); cfg.setAutoReconnectionEnabled(true); const auto client = new Client(p.first); addAccount(client); client->connectToServer(cfg); } } void xxcc::startChat(const QString from, const QString to) { bool found = false; for (int i = 0; i < ui.conversations_list->count(); i++) { const auto it = static_cast(ui.conversations_list->item(i)); if (it->from == from && it->to == to) { found = true; break; } } if (!found) new Conversation(from, to, ui.conversations_list); for (const auto c : clients) if (c->jidBare() == from) { selected = c; break; } ui.sw->setCurrentIndex(Tab::Chat); ui.jid->setText(to); ui.messages->scrollToBottom(); } void xxcc::addInMessage(const QXmppMessage &msg) { new Message(msg.body(), msg.stamp().toLocalTime(), Direction::In, ui.messages); } void xxcc::addOutMessage(const QXmppMessage &msg) { new Message(msg.body(), msg.stamp().toLocalTime(), Direction::Out, ui.messages); } void xxcc::addAccount(Client *const c) { c->configuration().setAutoReconnectionEnabled(true); clients.append(c); c->connect(c, &Client::messageReceived, this, [this] (QXmppMessage msg) { if (msg.body().isEmpty()) return; else if (msg.stamp().isNull()) msg.setStamp(QDateTime::currentDateTimeUtc()); storeMessage(msg, Direction::In); if (selected) addInMessage(msg); }); const auto roster = c->findExtension(); if (roster) roster->connect(roster, &QXmppRosterManager::rosterReceived, c, [this, c, roster] { c->database().addToRoster(roster->getRosterBareJids()); }); else throw std::runtime_error("Expected non-null QXmppRosterManager"); } void xxcc::send(void) { if (!selected) throw std::runtime_error("Expected non-null selected client"); const auto from = selected->jidBare(), to = ui.jid->text(), msg = ui.chatinput->toPlainText(); QXmppMessage out(from, to, msg); out.setStamp(QDateTime::currentDateTimeUtc()); selected->sendPacket(out); addOutMessage(out); storeMessage(out, Direction::Out); ui.chatinput->clear(); } void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const { QString jid, contact; switch (dir) { case Direction::In: jid = QXmppUtils::jidToBareJid(msg.to()); contact = QXmppUtils::jidToBareJid(msg.from()); break; case Direction::Out: jid = msg.from(); contact = msg.to(); break; } for (const auto c : clients) { if (c->jidBare() == jid) { const auto &db = c->database(); JidDb::Message m; m.body = msg.body(); m.dt = msg.stamp(); m.direction = dir; m.contact = contact; db.storeMessage(m); } } } void xxcc::retrieveConversations() { for (const auto c : clients) { const auto &db = c->database(); for (const auto &conv : db.getConversations()) new Conversation(db.jid, conv.to, ui.conversations_list, conv.last_msg, conv.dt); } }