Skip to content

Commit

Permalink
More porting to lib 0.9
Browse files Browse the repository at this point in the history
  • Loading branch information
KitsuneRal committed Oct 27, 2024
1 parent b7d0902 commit 0d814d9
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 92 deletions.
110 changes: 27 additions & 83 deletions client/thumbnailprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ class AbstractThumbnailResponse : public QQuickImageResponse {
QQuickTextureFactory* textureFactory() const override
{
QReadLocker _(&lock);
return QQuickTextureFactory::textureFactoryForImage(image);
return QQuickTextureFactory::textureFactoryForImage(result.value_or(QImage()));
}

QString errorString() const override
{
QReadLocker _(&lock);
return errorStr;
return result.has_value() ? QString() : result.error();
}

void cancel() override
Expand Down Expand Up @@ -119,93 +119,42 @@ private slots:

const auto* currentRoom = timeline->currentRoom();
if (!currentRoom) {
finish({}, NoConnectionError);
finish(NoConnectionError);
return;
}

job = currentRoom->connection()->getThumbnail(mediaId, requestedSize);

// Connect to any possible outcome including abandonment
// to make sure the QML thread is not left stuck forever.
connect(job, &BaseJob::finished, this, [this] {
Q_ASSERT(job->error() != BaseJob::Pending);
if (job->error() == BaseJob::Success) {
qCDebug(THUMBNAILS).noquote()
<< "Thumbnail for" << mediaId
<< "ready, actual size:" << job->thumbnail().size();
finish(job->thumbnail());
} else if (job->error() == BaseJob::Abandoned) {
// Save the future so that we could cancel it
futureResult =
Quotient::JobHandle(currentRoom->connection()->getThumbnail(mediaId, requestedSize))
.then(this,
[this](const QImage& thumbnail) {
qCDebug(THUMBNAILS).noquote()
<< "Thumbnail for" << mediaId
<< "ready, actual size:" << thumbnail.size();
return result_type { thumbnail };
},
[this](const Quotient::MediaThumbnailJob* job) {
qCWarning(THUMBNAILS).nospace()
<< "No valid thumbnail for" << mediaId << ": " << job->errorString();
return result_type { job->errorString() };
});
// NB: Make sure to connect to any possible outcome including cancellation so that
// the QML thread is not left stuck forever.
futureResult
.onCanceled([this] {
qCDebug(THUMBNAILS) << "Request cancelled for" << mediaId;
finish({}, tr("Image request has been cancelled"));
} else {
qCWarning(THUMBNAILS).nospace()
<< "No valid thumbnail for" << mediaId << ": "
<< job->errorString();
finish({}, job->errorString());
}
job = nullptr;
});
return tr("Image request has been cancelled"); // Turn it to an error
})
.then([this] (const result_type& r) { finish(r); });
}

void doCancel() override
{
if (job) {
Q_ASSERT(QThread::currentThread() == job->thread());
job->abandon();
}
futureResult.cancel();
}

private:
QPointer<Quotient::MediaThumbnailJob> job = nullptr;
};

class AvatarResponse : public AbstractThumbnailResponse {
Q_OBJECT
public:
using AbstractThumbnailResponse::AbstractThumbnailResponse;

private:
void startRequest() override
{
Q_ASSERT(QThread::currentThread() == qApp->thread());

Quotient::Room* currentRoom = timeline->currentRoom();
if (!currentRoom) {
finish({}, NoConnectionError);
return;
}

// NB: both Room:avatar() and User::avatar() invocations return an image
// available right now and, if needed, request one with the better
// resolution asynchronously. To get this better resolution image,
// Avatar elements in QML should call Avatar.reload() in response to
// Room::avatarChanged() and Room::memberAvatarChanged() (sic!)
// respectively.
const auto& w = requestedSize.width();
const auto& h = requestedSize.height();
if (mediaId.startsWith(u'!')) {
if (mediaId != currentRoom->id()) {
currentRoom = currentRoom->connection()->room(mediaId);
Q_ASSERT(currentRoom != nullptr);
}
// As of libQuotient 0.8, Room::avatar() is the only call in the
// Room::avatar*() family that substitutes the counterpart's
// avatar for a direct chat avatar.
prepareResult(currentRoom->avatar(w, h));
return;
}

auto* user = currentRoom->user(mediaId);
Q_ASSERT(user != nullptr);
prepareResult(user->avatar(w, h, currentRoom));
}

void prepareResult(const QImage& avatar)
{
qCDebug(THUMBNAILS).noquote() << "Returning avatar for" << mediaId
<< "with size:" << avatar.size();
finish(avatar);
}
QFuture<Quotient::Expected<QImage, QString>> futureResult;
};

#include "thumbnailprovider.moc" // Because we define a Q_OBJECT in the cpp file
Expand All @@ -226,11 +175,6 @@ class ImageProviderTemplate : public QQuickAsyncImageProvider {
Q_DISABLE_COPY(ImageProviderTemplate)
};

QQuickAsyncImageProvider* makeAvatarProvider(TimelineWidget* parent)
{
return new ImageProviderTemplate<AvatarResponse>(parent);
}

QQuickAsyncImageProvider* makeThumbnailProvider(TimelineWidget* parent)
{
return new ImageProviderTemplate<ThumbnailResponse>(parent);
Expand Down
1 change: 0 additions & 1 deletion client/thumbnailprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@

class TimelineWidget;

QQuickAsyncImageProvider* makeAvatarProvider(TimelineWidget* parent);
QQuickAsyncImageProvider* makeThumbnailProvider(TimelineWidget* parent);
9 changes: 1 addition & 8 deletions client/timelinewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ TimelineWidget::TimelineWidget(ChatRoomWidget* chatRoomWidget)

setResizeMode(SizeRootObjectToView);

engine()->addImageProvider("avatar"_ls, makeAvatarProvider(this));
engine()->addImageProvider("thumbnail"_ls, makeThumbnailProvider(this));

auto* ctxt = rootContext();
Expand Down Expand Up @@ -179,13 +178,7 @@ void TimelineWidget::showMenu(int index, const QString& hoveredLink,
auto menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);

const auto* plEvt =
currentRoom()->currentState().get<Quotient::RoomPowerLevelsEvent>();
const auto localUserId = currentRoom()->localUser()->id();
const int userPl = plEvt ? plEvt->powerLevelForUser(localUserId) : 0;
const auto* modelUser =
modelIndex.data(MessageEventModel::AuthorRole).value<Quotient::User*>();
if (!plEvt || userPl >= plEvt->redact() || localUserId == modelUser->id())
if (currentRoom()->canRedact(eventId))
menu->addAction(QIcon::fromTheme("edit-delete"), tr("Redact"), this,
[this, eventId] { currentRoom()->redactEvent(eventId); });

Expand Down

0 comments on commit 0d814d9

Please sign in to comment.