From 756e716886036447915a10992189774991f4dd8d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 19 Oct 2017 19:26:49 +0900 Subject: Don't log renames This causes enormous traffic in the logs upon every startup when main.debug logs are on. --- user.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'user.cpp') diff --git a/user.cpp b/user.cpp index 171d6d6c..aa1aa447 100644 --- a/user.cpp +++ b/user.cpp @@ -85,8 +85,6 @@ void User::updateName(const QString& newName) const auto oldName = name(); if (d->name != newName) { - qCDebug(MAIN) << "Renaming" << id() - << "from" << oldName << "to" << newName; d->name = newName; emit nameChanged(this, oldName); } -- cgit v1.2.3 From 0ce284eeca96ac92524a390837b551bebb5431cc Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 26 Oct 2017 19:53:24 +0300 Subject: Move out the avatar code from User Avatars are also a property of rooms, and the supporting code is basically the same. The only thing different will be emitted signals, and the cleanest thing to support that (aside from making Avatar a QObject) seems to be to parameterise the thumbnail-updating logic with a continuation invoked upon completion of the thumbnail job. --- CMakeLists.txt | 1 + avatar.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ avatar.h | 57 +++++++++++++++++++++++++++++++++++ user.cpp | 93 ++++++++-------------------------------------------------- user.h | 5 ++-- 5 files changed, 152 insertions(+), 84 deletions(-) create mode 100644 avatar.cpp create mode 100644 avatar.h (limited to 'user.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0395774a..fc3276c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(libqmatrixclient_SRCS logging.cpp room.cpp user.cpp + avatar.cpp settings.cpp events/event.cpp events/eventcontent.cpp diff --git a/avatar.cpp b/avatar.cpp new file mode 100644 index 00000000..0ce9210b --- /dev/null +++ b/avatar.cpp @@ -0,0 +1,80 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include "avatar.h" + +#include "jobs/mediathumbnailjob.h" +#include "events/eventcontent.h" +#include "connection.h" + +using namespace QMatrixClient; + +QPixmap Avatar::get(int width, int height, std::function continuation) +{ + QSize size(width, height); + + // FIXME: Alternating between longer-width and longer-height requests + // is a sure way to trick the below code into constantly getting another + // image from the server because the existing one is alleged unsatisfactory. + // This is plain abuse by the client, though; so not critical for now. + if( (!_valid && _url.isValid() && !_ongoingRequest) + || width > _requestedSize.width() + || height > _requestedSize.height() ) + { + qCDebug(MAIN) << "Getting avatar from" << _url.toString(); + _requestedSize = size; + _ongoingRequest = _connection->callApi(_url, size); + _ongoingRequest->connect( _ongoingRequest, &MediaThumbnailJob::finished, + _connection, [=]() { + if (_ongoingRequest->status().good()) + { + _valid = true; + _originalPixmap = _ongoingRequest->scaledThumbnail(_requestedSize); + _scaledPixmaps.clear(); + continuation(); + } + _ongoingRequest = nullptr; + }); + } + + if( _originalPixmap.isNull() ) + { + if (_defaultIcon.isNull()) + return _originalPixmap; + + _originalPixmap = _defaultIcon.pixmap(size); + } + + auto& pixmap = _scaledPixmaps[{width, height}]; // Create if needed + if (pixmap.isNull()) + { + pixmap = _originalPixmap.scaled(size, + Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + return pixmap; +} + +bool Avatar::updateUrl(const QUrl& newUrl) +{ + if (newUrl == _url) + return false; + + _url = newUrl; + _valid = false; + return true; +} diff --git a/avatar.h b/avatar.h new file mode 100644 index 00000000..6889e839 --- /dev/null +++ b/avatar.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#pragma once + +#include +#include + +#include + +namespace QMatrixClient +{ + class MediaThumbnailJob; + class Connection; + + class Avatar + { + public: + explicit Avatar(Connection* connection, QIcon defaultIcon = {}) + : _defaultIcon(std::move(defaultIcon)), _connection(connection) + { } + + QPixmap get(int w, int h, std::function continuation); + + QUrl url() const { return _url; } + bool updateUrl(const QUrl& newUrl); + + private: + QUrl _url; + QPixmap _originalPixmap; + QIcon _defaultIcon; + + /// Map of requested size to the actual pixmap used for it + /// (it's a shame that QSize has no predefined qHash()). + QHash, QPixmap> _scaledPixmaps; + + QSize _requestedSize; + bool _valid = false; + Connection* _connection; + MediaThumbnailJob* _ongoingRequest = nullptr; + }; +} // namespace QMatrixClient diff --git a/user.cpp b/user.cpp index aa1aa447..b2c0dc1e 100644 --- a/user.cpp +++ b/user.cpp @@ -19,14 +19,12 @@ #include "user.h" #include "connection.h" +#include "avatar.h" #include "events/event.h" #include "events/roommemberevent.h" -#include "jobs/mediathumbnailjob.h" #include "jobs/generated/profile.h" #include -#include -#include #include using namespace QMatrixClient; @@ -35,35 +33,20 @@ class User::Private { public: Private(QString userId, Connection* connection) - : q(nullptr), userId(std::move(userId)), connection(connection) - , defaultIcon(QIcon::fromTheme(QStringLiteral("user-available"))) - , avatarValid(false) , avatarOngoingRequest(false) + : userId(std::move(userId)), connection(connection) + , avatar(connection, QIcon::fromTheme(QStringLiteral("user-available"))) { } - User* q; QString userId; QString name; - QUrl avatarUrl; - Connection* connection; - - QPixmap avatar; - QIcon defaultIcon; - QSize requestedSize; - bool avatarValid; - bool avatarOngoingRequest; - /// Map of requested size to the actual pixmap used for it - /// (it's a shame that QSize has no predefined qHash()). - QHash, QPixmap> scaledAvatars; QString bridged; - - void requestAvatar(); + Connection* connection; + Avatar avatar; }; User::User(QString userId, Connection* connection) - : QObject(connection), d(new Private(userId, connection)) -{ - d->q = this; // Initialization finished -} + : QObject(connection), d(new Private(std::move(userId), connection)) +{ } User::~User() { @@ -109,43 +92,12 @@ QString User::bridged() const { QPixmap User::avatar(int width, int height) { - QSize size(width, height); - - // FIXME: Alternating between longer-width and longer-height requests - // is a sure way to trick the below code into constantly getting another - // image from the server because the existing one is alleged unsatisfactory. - // This is plain abuse by the client, though; so not critical for now. - if( (!d->avatarValid && d->avatarUrl.isValid() && !d->avatarOngoingRequest) - || width > d->requestedSize.width() - || height > d->requestedSize.height() ) - { - qCDebug(MAIN) << "Getting avatar for" << id() - << "from" << d->avatarUrl.toString(); - d->requestedSize = size; - d->avatarOngoingRequest = true; - QTimer::singleShot(0, this, SLOT(requestAvatar())); - } - - if( d->avatar.isNull() ) - { - if (d->defaultIcon.isNull()) - return d->avatar; - - d->avatar = d->defaultIcon.pixmap(size); - } - - auto& pixmap = d->scaledAvatars[{width, height}]; // Create the entry if needed - if (pixmap.isNull()) - { - pixmap = d->avatar.scaled(width, height, - Qt::KeepAspectRatio, Qt::SmoothTransformation); - } - return pixmap; + return d->avatar.get(width, height, [=] { emit avatarChanged(this); }); } -const QUrl& User::avatarUrl() const +QUrl User::avatarUrl() const { - return d->avatarUrl; + return d->avatar.url(); } void User::processEvent(Event* event) @@ -165,28 +117,7 @@ void User::processEvent(Event* event) newName.truncate(match.capturedStart(0)); } updateName(newName); - if( d->avatarUrl != e->avatarUrl() ) - { - d->avatarUrl = e->avatarUrl(); - d->avatarValid = false; - } + if (d->avatar.updateUrl(e->avatarUrl())) + emit avatarChanged(this); } } - -void User::requestAvatar() -{ - d->requestAvatar(); -} - -void User::Private::requestAvatar() -{ - auto* job = connection->callApi(avatarUrl, requestedSize); - connect( job, &MediaThumbnailJob::success, [=]() { - avatarOngoingRequest = false; - avatarValid = true; - avatar = job->scaledThumbnail(requestedSize); - scaledAvatars.clear(); - emit q->avatarChanged(q); - }); -} - diff --git a/user.h b/user.h index 79a6f5db..aee6ec3e 100644 --- a/user.h +++ b/user.h @@ -30,7 +30,7 @@ namespace QMatrixClient Q_OBJECT public: User(QString userId, Connection* connection); - virtual ~User(); + ~User() override; /** * Returns the id of the user @@ -54,12 +54,11 @@ namespace QMatrixClient QPixmap avatar(int requestedWidth, int requestedHeight); - const QUrl& avatarUrl() const; + QUrl avatarUrl() const; void processEvent(Event* event); public slots: - void requestAvatar(); void rename(const QString& newName); signals: -- cgit v1.2.3 From 0fb7adfcaa5dffee4efd0a34a2a4fd655fe5c709 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 27 Oct 2017 15:19:07 +0300 Subject: Support m.room.avatar events The events are detected in /sync output, and avatars for rooms are loaded from respective URLs. Clients can use Room::avatar() method to request a pixmap of a certain size, and react to avatarChanged() in order to update the UI when new pixmaps/avatars arrive. avatarChanged() signal is overloaded with two tasks - the first firing merely indicates that a new avatar is available (without actual pixmap yet available) while the second firing means that an actual pixmap has arrived (all this is entirely transparent for clients, they just should update their pixmaps from Room::avatar() every time when Room::avatarChanged() is emitted). --- CMakeLists.txt | 1 + events/event.cpp | 2 ++ events/event.h | 3 ++- events/roomavatarevent.cpp | 23 +++++++++++++++++++ events/roomavatarevent.h | 53 ++++++++++++++++++++++++++++++++++++++++++ libqmatrixclient.pri | 2 ++ room.cpp | 57 ++++++++++++++++++++++++++++++++++++++-------- room.h | 10 +++++++- user.cpp | 5 ++++ user.h | 2 ++ 10 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 events/roomavatarevent.cpp create mode 100644 events/roomavatarevent.h (limited to 'user.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index fc3276c9..26c7bf46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ set(libqmatrixclient_SRCS events/roomcanonicalaliasevent.cpp events/roommemberevent.cpp events/roomtopicevent.cpp + events/roomavatarevent.cpp events/typingevent.cpp events/receiptevent.cpp events/encryptedevent.cpp diff --git a/events/event.cpp b/events/event.cpp index 304f2af6..9963a0ef 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -24,6 +24,7 @@ #include "roomcanonicalaliasevent.h" #include "roommemberevent.h" #include "roomtopicevent.h" +#include "roomavatarevent.h" #include "typingevent.h" #include "receiptevent.h" #include "encryptedevent.h" @@ -136,6 +137,7 @@ RoomEvent* RoomEvent::fromJson(const QJsonObject& obj) "m.room.canonical_alias", make, "m.room.member", make, "m.room.topic", make, + "m.room.avatar", make, "m.room.encryption", make, /* Insert new ROOM event types BEFORE this line */ nullptr diff --git a/events/event.h b/events/event.h index ec993522..c151ac0e 100644 --- a/events/event.h +++ b/events/event.h @@ -34,7 +34,8 @@ namespace QMatrixClient enum class Type { RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias, - RoomMember, RoomTopic, RoomEncryption, RoomEncryptedMessage, + RoomMember, RoomTopic, RoomAvatar, + RoomEncryption, RoomEncryptedMessage, Typing, Receipt, Unknown }; diff --git a/events/roomavatarevent.cpp b/events/roomavatarevent.cpp new file mode 100644 index 00000000..7a5f82a1 --- /dev/null +++ b/events/roomavatarevent.cpp @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include "roomavatarevent.h" + +using namespace QMatrixClient; + + diff --git a/events/roomavatarevent.h b/events/roomavatarevent.h new file mode 100644 index 00000000..411de4e8 --- /dev/null +++ b/events/roomavatarevent.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#pragma once + +#include "event.h" + +#include + +#include "eventcontent.h" + +namespace QMatrixClient +{ + class RoomAvatarEvent: public RoomEvent + { + public: + explicit RoomAvatarEvent(EventContent::ImageContent avatar) + : RoomEvent(Type::RoomAvatar), _avatar(std::move(avatar)) + { } + explicit RoomAvatarEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomAvatar, obj), _avatar(contentJson()) + { } + + const EventContent::ImageContent& content() const { return _avatar; } + + QJsonObject toJson() const { return _avatar.toJson(); } + + static constexpr const char* TypeId = "m.room.avatar"; + + private: + // It's a bit of an overkill to use a full-fledged ImageContent + // because in reality m.room.avatar usually only has a single URL, + // without a thumbnail. But The Spec says there be thumbnails, and + // we follow The Spec. + EventContent::ImageContent _avatar; + }; + +} // namespace QMatrixClient diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 9bcb911f..9eb6bd16 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -18,6 +18,7 @@ HEADERS += \ $$PWD/events/roomcanonicalaliasevent.h \ $$PWD/events/roommemberevent.h \ $$PWD/events/roomtopicevent.h \ + $$PWD/events/roomavatarevent.h \ $$PWD/events/typingevent.h \ $$PWD/events/receiptevent.h \ $$PWD/jobs/basejob.h \ @@ -48,6 +49,7 @@ SOURCES += \ $$PWD/events/roomcanonicalaliasevent.cpp \ $$PWD/events/roommemberevent.cpp \ $$PWD/events/roomtopicevent.cpp \ + $$PWD/events/roomavatarevent.cpp \ $$PWD/events/typingevent.cpp \ $$PWD/events/receiptevent.cpp \ $$PWD/jobs/basejob.cpp \ diff --git a/room.cpp b/room.cpp index 65cf2d2a..53a6b160 100644 --- a/room.cpp +++ b/room.cpp @@ -27,12 +27,14 @@ #include "events/roomaliasesevent.h" #include "events/roomcanonicalaliasevent.h" #include "events/roomtopicevent.h" +#include "events/roomavatarevent.h" #include "events/roommemberevent.h" #include "events/typingevent.h" #include "events/receiptevent.h" #include "jobs/sendeventjob.h" #include "jobs/roommessagesjob.h" #include "jobs/postreceiptjob.h" +#include "avatar.h" #include "connection.h" #include "user.h" @@ -53,7 +55,7 @@ class Room::Private Private(Connection* c, QString id_, JoinState initialJoinState) : q(nullptr), connection(c), id(std::move(id_)) - , joinState(initialJoinState), unreadMessages(false) + , avatar(c), joinState(initialJoinState), unreadMessages(false) , highlightCount(0), notificationCount(0), roomMessagesJob(nullptr) { } @@ -73,6 +75,7 @@ class Room::Private QString name; QString displayname; QString topic; + Avatar avatar; JoinState joinState; bool unreadMessages; int highlightCount; @@ -189,6 +192,23 @@ QString Room::topic() const return d->topic; } +QPixmap Room::avatar(int width, int height) +{ + if (!d->avatar.url().isEmpty()) + return d->avatar.get(width, height, [=] { emit avatarChanged(); }); + + // Use the other side's avatar for 1:1's + if (d->membersMap.size() == 2) + { + auto theOtherOneIt = d->membersMap.begin(); + if (theOtherOneIt.value() == localUser()) + ++theOtherOneIt; + return theOtherOneIt.value()->avatarObject() + .get(width, height, [=] { emit avatarChanged(); }); + } + return {}; +} + JoinState Room::joinState() const { return d->joinState; @@ -778,6 +798,17 @@ void Room::processStateEvents(const RoomEvents& events) emit topicChanged(); break; } + case EventType::RoomAvatar: { + const auto& avatarEventContent = + static_cast(event)->content(); + if (d->avatar.updateUrl(avatarEventContent.url)) + { + qCDebug(MAIN) << "Room avatar URL updated:" + << avatarEventContent.url.toString(); + emit avatarChanged(); + } + break; + } case EventType::RoomMember: { auto memberEvent = static_cast(event); // Can't use d->member() below because the user may be not a member (yet) @@ -939,9 +970,13 @@ void Room::Private::updateDisplayname() emit q->displaynameChanged(q); } -QJsonObject stateEventToJson(const QString& type, const QString& name, - const QJsonValue& content) +template +void appendEventJson(QJsonArray& events, const QString& type, + const QString& name, const T& content) { + if (content.isEmpty()) + return; + QJsonObject contentObj; contentObj.insert(name, content); @@ -949,7 +984,7 @@ QJsonObject stateEventToJson(const QString& type, const QString& name, eventObj.insert("type", type); eventObj.insert("content", contentObj); - return eventObj; + events.append(eventObj); } QJsonObject Room::Private::toJson() const @@ -958,12 +993,14 @@ QJsonObject Room::Private::toJson() const { QJsonArray stateEvents; - stateEvents.append(stateEventToJson("m.room.name", "name", name)); - stateEvents.append(stateEventToJson("m.room.topic", "topic", topic)); - stateEvents.append(stateEventToJson("m.room.aliases", "aliases", - QJsonArray::fromStringList(aliases))); - stateEvents.append(stateEventToJson("m.room.canonical_alias", "alias", - canonicalAlias)); + appendEventJson(stateEvents, "m.room.name", "name", name); + appendEventJson(stateEvents, "m.room.topic", "topic", topic); + appendEventJson(stateEvents, "m.room.avatar", "avatar_url", + avatar.url().toString()); + appendEventJson(stateEvents, "m.room.aliases", "aliases", + QJsonArray::fromStringList(aliases)); + appendEventJson(stateEvents, "m.room.canonical_alias", "alias", + canonicalAlias); for (const auto &i : membersMap) { diff --git a/room.h b/room.h index 2d0453bc..2fa7e7d5 100644 --- a/room.h +++ b/room.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "jobs/syncjob.h" #include "events/roommessageevent.h" @@ -79,7 +80,7 @@ namespace QMatrixClient using rev_iter_t = Timeline::const_reverse_iterator; Room(Connection* connection, QString id, JoinState initialJoinState); - virtual ~Room(); + ~Room() override; Connection* connection() const; User* localUser() const; @@ -97,6 +98,12 @@ namespace QMatrixClient Q_INVOKABLE QStringList memberNames() const; Q_INVOKABLE int memberCount() const; + /** + * Returns a room avatar and requests it from the network if needed + * @return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QPixmap avatar(int width, int height); /** * @brief Produces a disambiguated name for a given user in * the context of the room @@ -181,6 +188,7 @@ namespace QMatrixClient /** @brief The room displayname changed */ void displaynameChanged(Room* room); void topicChanged(); + void avatarChanged(); void userAdded(User* user); void userRemoved(User* user); void memberRenamed(User* user); diff --git a/user.cpp b/user.cpp index b2c0dc1e..8c8a7fdb 100644 --- a/user.cpp +++ b/user.cpp @@ -90,6 +90,11 @@ QString User::bridged() const { return d->bridged; } +Avatar& User::avatarObject() +{ + return d->avatar; +} + QPixmap User::avatar(int width, int height) { return d->avatar.get(width, height, [=] { emit avatarChanged(this); }); diff --git a/user.h b/user.h index aee6ec3e..148ed64d 100644 --- a/user.h +++ b/user.h @@ -20,6 +20,7 @@ #include #include +#include "avatar.h" namespace QMatrixClient { @@ -52,6 +53,7 @@ namespace QMatrixClient */ Q_INVOKABLE QString bridged() const; + Avatar& avatarObject(); QPixmap avatar(int requestedWidth, int requestedHeight); QUrl avatarUrl() const; -- cgit v1.2.3 From 27bec8d012b6310bf1f23c1c879232a841605f60 Mon Sep 17 00:00:00 2001 From: Roman Plášil Date: Fri, 10 Nov 2017 17:36:54 +0800 Subject: Fix (IRC) bridge detection --- user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'user.cpp') diff --git a/user.cpp b/user.cpp index 8c8a7fdb..dea75360 100644 --- a/user.cpp +++ b/user.cpp @@ -115,7 +115,7 @@ void User::processEvent(Event* event) auto newName = e->displayName(); QRegularExpression reSuffix(" \\((IRC|Gitter)\\)$"); - auto match = reSuffix.match(d->name); + auto match = reSuffix.match(newName); if (match.hasMatch()) { d->bridged = match.captured(1); -- cgit v1.2.3 From 92ede101bbde3b853a3eed20f6c097cd7a137b9c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 10 Nov 2017 19:27:40 +0900 Subject: Add Telegram to the list of bridge suffixes --- user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'user.cpp') diff --git a/user.cpp b/user.cpp index dea75360..faad6231 100644 --- a/user.cpp +++ b/user.cpp @@ -114,7 +114,7 @@ void User::processEvent(Event* event) return; auto newName = e->displayName(); - QRegularExpression reSuffix(" \\((IRC|Gitter)\\)$"); + QRegularExpression reSuffix(" \\((IRC|Gitter|Telegram)\\)$"); auto match = reSuffix.match(newName); if (match.hasMatch()) { -- cgit v1.2.3