1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
// SPDX-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "user.h"
#include "avatar.h"
#include "connection.h"
#include "logging_categories_p.h"
#include "room.h"
#include "csapi/profile.h"
#include "events/roommemberevent.h"
#include <QtCore/QElapsedTimer>
#include <QtCore/QRegularExpression>
#include <QtCore/QStringBuilder>
#include <QtCore/QTimer>
using namespace Quotient;
class Q_DECL_HIDDEN User::Private {
public:
explicit Private(QString userId) : id(std::move(userId)) { }
QString id;
QString defaultName;
QUrl defaultAvatarUrl;
};
User::User(QString userId, Connection* connection)
: QObject(connection), d(makeImpl<Private>(std::move(userId)))
{
setObjectName(id());
}
Connection* User::connection() const
{
Q_ASSERT(parent());
return static_cast<Connection*>(parent());
}
void User::load()
{
connection()->callApi<GetUserProfileJob>(id()).then([this](const auto* profileJob) {
d->defaultName = profileJob->displayname();
d->defaultAvatarUrl = profileJob->avatarUrl();
emit defaultNameChanged();
emit defaultAvatarChanged();
});
}
QString User::id() const { return d->id; }
QString User::name() const { return d->defaultName; }
QString User::displayname() const
{
return d->defaultName.isEmpty() ? d->id : d->defaultName;
}
QString User::fullName() const
{
return displayname().isEmpty() ? id() : (displayname() % " ("_L1 % id() % u')');
}
bool User::isGuest() const
{
Q_ASSERT(!d->id.isEmpty() && d->id.startsWith(u'@'));
auto it = std::find_if_not(d->id.cbegin() + 1, d->id.cend(),
[](QChar c) { return c.isDigit(); });
Q_ASSERT(it != d->id.cend());
return *it == u':';
}
void User::rename(const QString& newName)
{
const auto actualNewName = sanitized(newName);
if (actualNewName == d->defaultName)
return; // Nothing to do
connection()->callApi<SetDisplayNameJob>(id(), actualNewName).then([this, actualNewName] {
// Check again, it could have changed meanwhile
if (actualNewName != d->defaultName) {
d->defaultName = actualNewName;
emit defaultNameChanged();
} else
qCWarning(MAIN) << "User" << id() << "already has profile name set to" << actualNewName;
});
}
void User::rename(const QString& newName, Room* r)
{
if (!r) {
qCWarning(MAIN) << "Passing a null room to two-argument User::rename()"
"is incorrect; client developer, please fix it";
rename(newName);
return;
}
// #481: take the current state and update it with the new name
if (const auto& maybeEvt = r->currentState().get<RoomMemberEvent>(id())) {
auto content = maybeEvt->content();
if (content.membership == Membership::Join) {
content.displayName = sanitized(newName);
r->setState<RoomMemberEvent>(id(), std::move(content));
// The state will be updated locally after it arrives with sync
return;
}
}
qCCritical(MEMBERS)
<< "Attempt to rename a non-member in a room context - ignored";
}
Avatar& User::avatarObject() { return connection()->userAvatar(avatarUrl()); }
QImage User::avatar(int width, int height, Avatar::get_callback_t callback)
{
return avatarObject().get(width, height, std::move(callback));
}
QImage User::avatar(int dimension, Avatar::get_callback_t callback)
{
return avatar(dimension, dimension, std::move(callback));
}
void User::doSetAvatar(const QUrl& contentUri)
{
connection()->callApi<SetAvatarUrlJob>(id(), contentUri).then(this, [this, contentUri] {
if (contentUri != d->defaultAvatarUrl) {
avatarObject().updateUrl(contentUri);
emit defaultAvatarChanged();
} else
qCWarning(MAIN) << "User" << id() << "already has avatar URL set to"
<< contentUri.toDisplayString();
});
}
bool User::setAvatar(const QString& fileName)
{
auto ft = avatarObject()
.upload(fileName)
.then(std::bind_front(&User::doSetAvatar, this));
// The return value only says whether the upload has started; the subsequent url setting job
// will only start after the upload completes
return !ft.isCanceled();
}
bool User::setAvatar(QIODevice* source)
{
auto ft = avatarObject()
.upload(source)
.then(std::bind_front(&User::doSetAvatar, this));
// The return value only says whether the upload has started; the subsequent url setting job
// will only start after the upload completes
return !ft.isCanceled();
}
void User::removeAvatar() const
{
connection()->callApi<SetAvatarUrlJob>(id(), QUrl());
}
void User::requestDirectChat() { connection()->requestDirectChat(d->id); }
void User::ignore() const { connection()->addToIgnoredUsers(d->id); }
void User::unmarkIgnore() const { connection()->removeFromIgnoredUsers(d->id); }
bool User::isIgnored() const { return connection()->isIgnored(d->id); }
QString User::avatarMediaId() const
{
return d->defaultAvatarUrl.authority() + d->defaultAvatarUrl.path();
}
QUrl User::avatarUrl() const
{
return d->defaultAvatarUrl;
}
|