diff options
Diffstat (limited to 'lib/user.cpp')
-rw-r--r-- | lib/user.cpp | 267 |
1 files changed, 114 insertions, 153 deletions
diff --git a/lib/user.cpp b/lib/user.cpp index 7f3f11f6..8adb1dae 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -13,27 +13,28 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "user.h" +#include "avatar.h" #include "connection.h" #include "room.h" -#include "avatar.h" + +#include "csapi/content-repo.h" +#include "csapi/profile.h" +#include "csapi/room_state.h" + #include "events/event.h" #include "events/roommemberevent.h" -#include "csapi/room_state.h" -#include "csapi/profile.h" -#include "csapi/content-repo.h" -#include <QtCore/QTimer> -#include <QtCore/QRegularExpression> +#include <QtCore/QCryptographicHash> +#include <QtCore/QElapsedTimer> #include <QtCore/QPointer> +#include <QtCore/QRegularExpression> #include <QtCore/QStringBuilder> -#include <QtCore/QElapsedTimer> - -#include <QtCore/QCryptographicHash> +#include <QtCore/QTimer> #include <QtCore/QtEndian> #include <functional> @@ -44,65 +45,59 @@ using std::move; class User::Private { - public: - static Avatar makeAvatar(QUrl url) - { - return Avatar(move(url)); - } - - qreal makeHueF() - { - Q_ASSERT(!userId.isEmpty()); - QByteArray hash = QCryptographicHash::hash(userId.toUtf8(), - QCryptographicHash::Sha1); - QDataStream dataStream(qToLittleEndian(hash).left(2)); - dataStream.setByteOrder(QDataStream::LittleEndian); - quint16 hashValue; - dataStream >> hashValue; - const auto hueF = - qreal(hashValue)/std::numeric_limits<quint16>::max(); - Q_ASSERT((0 <= hueF) && (hueF <= 1)); - return hueF; - } +public: + static Avatar makeAvatar(QUrl url) { return Avatar(move(url)); } - Private(QString userId, Connection* connection) - : userId(move(userId)), connection(connection), hueF(makeHueF()) - { } - - QString userId; - Connection* connection; - - QString bridged; - QString mostUsedName; - QMultiHash<QString, const Room*> otherNames; - qreal hueF; - Avatar mostUsedAvatar { makeAvatar({}) }; - std::vector<Avatar> otherAvatars; - auto otherAvatar(const QUrl& url) - { - return std::find_if(otherAvatars.begin(), otherAvatars.end(), - [&url] (const auto& av) { return av.url() == url; }); - } - QMultiHash<QUrl, const Room*> avatarsToRooms; + qreal makeHueF() + { + Q_ASSERT(!userId.isEmpty()); + QByteArray hash = QCryptographicHash::hash(userId.toUtf8(), + QCryptographicHash::Sha1); + QDataStream dataStream(qToLittleEndian(hash).left(2)); + dataStream.setByteOrder(QDataStream::LittleEndian); + quint16 hashValue; + dataStream >> hashValue; + const auto hueF = qreal(hashValue) / std::numeric_limits<quint16>::max(); + Q_ASSERT((0 <= hueF) && (hueF <= 1)); + return hueF; + } - mutable int totalRooms = 0; + Private(QString userId, Connection* connection) + : userId(move(userId)) + , connection(connection) + , hueF(makeHueF()) + {} + + QString userId; + Connection* connection; + + QString bridged; + QString mostUsedName; + QMultiHash<QString, const Room*> otherNames; + qreal hueF; + Avatar mostUsedAvatar { makeAvatar({}) }; + std::vector<Avatar> otherAvatars; + auto otherAvatar(const QUrl& url) + { + return std::find_if(otherAvatars.begin(), otherAvatars.end(), + [&url](const auto& av) { return av.url() == url; }); + } + QMultiHash<QUrl, const Room*> avatarsToRooms; - QString nameForRoom(const Room* r, const QString& hint = {}) const; - void setNameForRoom(const Room* r, QString newName, const QString& oldName); - QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const; - void setAvatarForRoom(const Room* r, const QUrl& newUrl, - const QUrl& oldUrl); + mutable int totalRooms = 0; - void setAvatarOnServer(QString contentUri, User* q); + QString nameForRoom(const Room* r, const QString& hint = {}) const; + void setNameForRoom(const Room* r, QString newName, const QString& oldName); + QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const; + void setAvatarForRoom(const Room* r, const QUrl& newUrl, const QUrl& oldUrl); + void setAvatarOnServer(QString contentUri, User* q); }; - QString User::Private::nameForRoom(const Room* r, const QString& hint) const { // If the hint is accurate, this function is O(1) instead of O(n) - if (!hint.isNull() - && (hint == mostUsedName || otherNames.contains(hint, r))) + if (!hint.isNull() && (hint == mostUsedName || otherNames.contains(hint, r))) return hint; return otherNames.key(r, mostUsedName); } @@ -114,23 +109,19 @@ void User::Private::setNameForRoom(const Room* r, QString newName, { Q_ASSERT(oldName != newName); Q_ASSERT(oldName == mostUsedName || otherNames.contains(oldName, r)); - if (totalRooms < 2) - { + if (totalRooms < 2) { Q_ASSERT_X(totalRooms > 0 && otherNames.empty(), __FUNCTION__, "Internal structures inconsistency"); mostUsedName = move(newName); return; } otherNames.remove(oldName, r); - if (newName != mostUsedName) - { + if (newName != mostUsedName) { // Check if the newName is about to become most used. - if (otherNames.count(newName) >= totalRooms - otherNames.size()) - { + if (otherNames.count(newName) >= totalRooms - otherNames.size()) { Q_ASSERT(totalRooms > 1); QElapsedTimer et; - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) - { + if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { qCDebug(MAIN) << "Switching the most used name of user" << userId << "from" << mostUsedName << "to" << newName; qCDebug(MAIN) << "The user is in" << totalRooms << "rooms"; @@ -138,7 +129,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, } const auto& roomMap = connection->roomMap(); - for (auto* r1: roomMap) + for (auto* r1 : roomMap) if (nameForRoom(r1) == mostUsedName) otherNames.insert(mostUsedName, r1); @@ -146,8 +137,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, otherNames.remove(newName); if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) qCDebug(PROFILER) << et << "to switch the most used name"; - } - else + } else otherNames.insert(newName, r); } } @@ -165,33 +155,29 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, const QUrl& oldUrl) { Q_ASSERT(oldUrl != newUrl); - Q_ASSERT(oldUrl == mostUsedAvatar.url() || - avatarsToRooms.contains(oldUrl, r)); - if (totalRooms < 2) - { + Q_ASSERT(oldUrl == mostUsedAvatar.url() + || avatarsToRooms.contains(oldUrl, r)); + if (totalRooms < 2) { Q_ASSERT_X(totalRooms > 0 && otherAvatars.empty(), __FUNCTION__, "Internal structures inconsistency"); mostUsedAvatar.updateUrl(newUrl); return; } avatarsToRooms.remove(oldUrl, r); - if (!avatarsToRooms.contains(oldUrl)) - { + if (!avatarsToRooms.contains(oldUrl)) { auto it = otherAvatar(oldUrl); if (it != otherAvatars.end()) otherAvatars.erase(it); } - if (newUrl != mostUsedAvatar.url()) - { + if (newUrl != mostUsedAvatar.url()) { // Check if the new avatar is about to become most used. - if (avatarsToRooms.count(newUrl) >= totalRooms - avatarsToRooms.size()) - { + if (avatarsToRooms.count(newUrl) >= totalRooms - avatarsToRooms.size()) { QElapsedTimer et; - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) - { - qCDebug(MAIN) << "Switching the most used avatar of user" << userId - << "from" << mostUsedAvatar.url().toDisplayString() - << "to" << newUrl.toDisplayString(); + if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { + qCDebug(MAIN) + << "Switching the most used avatar of user" << userId + << "from" << mostUsedAvatar.url().toDisplayString() << "to" + << newUrl.toDisplayString(); et.start(); } avatarsToRooms.remove(newUrl); @@ -199,7 +185,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, Q_ASSERT(nextMostUsedIt != otherAvatars.end()); std::swap(mostUsedAvatar, *nextMostUsedIt); const auto& roomMap = connection->roomMap(); - for (const auto* r1: roomMap) + for (const auto* r1 : roomMap) if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) avatarsToRooms.insert(nextMostUsedIt->url(), r1); @@ -214,7 +200,8 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, } User::User(QString userId, Connection* connection) - : QObject(connection), d(new Private(move(userId), connection)) + : QObject(connection) + , d(new Private(move(userId), connection)) { setObjectName(userId); } @@ -227,34 +214,25 @@ Connection* User::connection() const User::~User() = default; -QString User::id() const -{ - return d->userId; -} +QString User::id() const { return d->userId; } bool User::isGuest() const { Q_ASSERT(!d->userId.isEmpty() && d->userId.startsWith('@')); auto it = std::find_if_not(d->userId.begin() + 1, d->userId.end(), - [] (QChar c) { return c.isDigit(); }); + [](QChar c) { return c.isDigit(); }); Q_ASSERT(it != d->userId.end()); return *it == ':'; } -int User::hue() const -{ - return int(hueF()*359); -} +int User::hue() const { return int(hueF() * 359); } -QString User::name(const Room* room) const -{ - return d->nameForRoom(room); -} +QString User::name(const Room* room) const { return d->nameForRoom(room); } QString User::rawName(const Room* room) const { - return d->bridged.isEmpty() ? name(room) : - name(room) % " (" % d->bridged % ')'; + return d->bridged.isEmpty() ? name(room) + : name(room) % " (" % d->bridged % ')'; } void User::updateName(const QString& newName, const Room* room) @@ -265,9 +243,9 @@ void User::updateName(const QString& newName, const Room* room) void User::updateName(const QString& newName, const QString& oldName, const Room* room) { - Q_ASSERT(oldName == d->mostUsedName || d->otherNames.contains(oldName, room)); - if (newName != oldName) - { + Q_ASSERT(oldName == d->mostUsedName + || d->otherNames.contains(oldName, room)); + if (newName != oldName) { emit nameAboutToChange(newName, oldName, room); d->setNameForRoom(room, newName, oldName); setObjectName(displayname()); @@ -278,15 +256,13 @@ void User::updateName(const QString& newName, const QString& oldName, void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, const Room* room) { - Q_ASSERT(oldUrl == d->mostUsedAvatar.url() || - d->avatarsToRooms.contains(oldUrl, room)); - if (newUrl != oldUrl) - { + Q_ASSERT(oldUrl == d->mostUsedAvatar.url() + || d->avatarsToRooms.contains(oldUrl, room)); + if (newUrl != oldUrl) { d->setAvatarForRoom(room, newUrl, oldUrl); setObjectName(displayname()); emit avatarChanged(this, room); } - } void User::rename(const QString& newName) @@ -298,8 +274,7 @@ void User::rename(const QString& newName) void User::rename(const QString& newName, const Room* r) { - if (!r) - { + if (!r) { qCWarning(MAIN) << "Passing a null room to two-argument User::rename()" "is incorrect; client developer, please fix it"; rename(newName); @@ -317,34 +292,24 @@ void User::rename(const QString& newName, const Room* r) bool User::setAvatar(const QString& fileName) { return avatarObject().upload(connection(), fileName, - std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); + std::bind(&Private::setAvatarOnServer, + d.data(), _1, this)); } bool User::setAvatar(QIODevice* source) { return avatarObject().upload(connection(), source, - std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); + std::bind(&Private::setAvatarOnServer, + d.data(), _1, this)); } -void User::requestDirectChat() -{ - connection()->requestDirectChat(this); -} +void User::requestDirectChat() { connection()->requestDirectChat(this); } -void User::ignore() -{ - connection()->addToIgnoredUsers(this); -} +void User::ignore() { connection()->addToIgnoredUsers(this); } -void User::unmarkIgnore() -{ - connection()->removeFromIgnoredUsers(this); -} +void User::unmarkIgnore() { connection()->removeFromIgnoredUsers(this); } -bool User::isIgnored() const -{ - return connection()->isIgnored(this); -} +bool User::isIgnored() const { return connection()->isIgnored(this); } void User::Private::setAvatarOnServer(QString contentUri, User* q) { @@ -368,10 +333,7 @@ QString User::fullName(const Room* room) const return name.isEmpty() ? d->userId : name % " (" % d->userId % ')'; } -QString User::bridged() const -{ - return d->bridged; -} +QString User::bridged() const { return d->bridged; } const Avatar& User::avatarObject(const Room* room) const { @@ -386,14 +348,16 @@ QImage User::avatar(int dimension, const Room* room) QImage User::avatar(int width, int height, const Room* room) { - return avatar(width, height, room, []{}); + return avatar(width, height, room, [] {}); } QImage User::avatar(int width, int height, const Room* room, const Avatar::get_callback_t& callback) { - return avatarObject(room).get(d->connection, width, height, - [=] { emit avatarChanged(this, room); callback(); }); + return avatarObject(room).get(d->connection, width, height, [=] { + emit avatarChanged(this, room); + callback(); + }); } QString User::avatarMediaId(const Room* room) const @@ -414,8 +378,8 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, if (firstMention) ++d->totalRooms; - if (event.membership() != MembershipType::Invite && - event.membership() != MembershipType::Join) + if (event.membership() != MembershipType::Invite + && event.membership() != MembershipType::Join) return; auto newName = event.displayName(); @@ -425,24 +389,23 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, // exceptionally rare (the only reasonable case being that the bridge // changes the naming convention). For the same reason room-specific // bridge tags are not supported at all. - QRegularExpression reSuffix(QStringLiteral(" \\((IRC|Gitter|Telegram)\\)$")); + QRegularExpression reSuffix( + QStringLiteral(" \\((IRC|Gitter|Telegram)\\)$")); auto match = reSuffix.match(newName); - if (match.hasMatch()) - { - if (d->bridged != match.captured(1)) - { + if (match.hasMatch()) { + if (d->bridged != match.captured(1)) { if (!d->bridged.isEmpty()) - qCWarning(MAIN) << "Bridge for user" << id() << "changed:" - << d->bridged << "->" << match.captured(1); + qCWarning(MAIN) + << "Bridge for user" << id() << "changed:" << d->bridged + << "->" << match.captured(1); d->bridged = match.captured(1); } newName.truncate(match.capturedStart(0)); } - if (event.prevContent()) - { + if (event.prevContent()) { // FIXME: the hint doesn't work for bridged users - auto oldNameHint = - d->nameForRoom(room, event.prevContent()->displayName); + auto oldNameHint = d->nameForRoom(room, + event.prevContent()->displayName); updateName(newName, oldNameHint, room); updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room, event.prevContent()->avatarUrl), @@ -453,6 +416,4 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, } } -qreal User::hueF() const { - return d->hueF; -} +qreal User::hueF() const { return d->hueF; } |