Compare commits
1 Commits
master
...
work/redst
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ff23239f7 |
@@ -220,6 +220,7 @@ target_link_libraries(neochat PUBLIC
|
|||||||
KF6::ItemModels
|
KF6::ItemModels
|
||||||
KF6::I18nQml
|
KF6::I18nQml
|
||||||
KirigamiApp
|
KirigamiApp
|
||||||
|
KirigamiAddonsComponents
|
||||||
QuotientQt6
|
QuotientQt6
|
||||||
Login
|
Login
|
||||||
Rooms
|
Rooms
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <KNotification>
|
#include <KNotification>
|
||||||
#include <KNotificationPermission>
|
#include <KNotificationPermission>
|
||||||
#include <KNotificationReplyAction>
|
#include <KNotificationReplyAction>
|
||||||
|
#include <KirigamiAddons/Components/NameUtils>
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <Quotient/accountregistry.h>
|
#include <Quotient/accountregistry.h>
|
||||||
@@ -144,16 +145,9 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
|||||||
body = notification["event"_L1]["content"_L1]["body"_L1].toString();
|
body = notification["event"_L1]["content"_L1]["body"_L1].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage avatar_image;
|
|
||||||
if (!sender.avatarUrl().isEmpty()) {
|
|
||||||
avatar_image = room->member(sender.id()).avatar(128, 128, {});
|
|
||||||
} else {
|
|
||||||
avatar_image = room->avatar(128);
|
|
||||||
}
|
|
||||||
postNotification(dynamic_cast<NeoChatRoom *>(room),
|
postNotification(dynamic_cast<NeoChatRoom *>(room),
|
||||||
sender.displayName(),
|
room->member(sender.id()),
|
||||||
body,
|
body,
|
||||||
avatar_image,
|
|
||||||
notification["event"_L1].toObject()["event_id"_L1].toString(),
|
notification["event"_L1].toObject()["event_id"_L1].toString(),
|
||||||
true,
|
true,
|
||||||
pair.first);
|
pair.first);
|
||||||
@@ -195,9 +189,8 @@ bool NotificationsManager::shouldPostNotification(QPointer<NeoChatConnection> co
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NotificationsManager::postNotification(NeoChatRoom *room,
|
void NotificationsManager::postNotification(NeoChatRoom *room,
|
||||||
const QString &sender,
|
const RoomMember &member,
|
||||||
const QString &text,
|
const QString &text,
|
||||||
const QImage &icon,
|
|
||||||
const QString &replyEventId,
|
const QString &replyEventId,
|
||||||
bool canReply,
|
bool canReply,
|
||||||
qint64 timestamp)
|
qint64 timestamp)
|
||||||
@@ -222,11 +215,11 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
|||||||
if (room->isDirectChat()) {
|
if (room->isDirectChat()) {
|
||||||
entry = text.toHtmlEscaped();
|
entry = text.toHtmlEscaped();
|
||||||
} else {
|
} else {
|
||||||
entry = i18n("%1: %2", sender, text.toHtmlEscaped());
|
entry = i18n("%1: %2", member.displayName(), text.toHtmlEscaped());
|
||||||
}
|
}
|
||||||
|
|
||||||
notification->setText(entry);
|
notification->setText(entry);
|
||||||
notification->setPixmap(createNotificationImage(icon, room));
|
notification->setPixmap(createNotificationImage(member, room));
|
||||||
|
|
||||||
auto defaultAction = notification->addDefaultAction(i18n("Open NeoChat in this room"));
|
auto defaultAction = notification->addDefaultAction(i18n("Open NeoChat in this room"));
|
||||||
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
||||||
@@ -294,18 +287,10 @@ void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
|
|||||||
}
|
}
|
||||||
const auto sender = room->member(roomMemberEvent->senderId());
|
const auto sender = room->member(roomMemberEvent->senderId());
|
||||||
|
|
||||||
QImage avatar_image;
|
|
||||||
if (roomMemberEvent && !room->member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
|
||||||
avatar_image = room->member(roomMemberEvent->senderId()).avatar(128, 128, {});
|
|
||||||
} else {
|
|
||||||
qWarning() << "using this room's avatar";
|
|
||||||
avatar_image = room->avatar(128);
|
|
||||||
}
|
|
||||||
|
|
||||||
KNotification *notification = new KNotification(u"invite"_s);
|
KNotification *notification = new KNotification(u"invite"_s);
|
||||||
notification->setText(i18n("%1 invited you to a room", sender.htmlSafeDisplayName()));
|
notification->setText(i18n("%1 invited you to a room", sender.htmlSafeDisplayName()));
|
||||||
notification->setTitle(room->displayName());
|
notification->setTitle(room->displayName());
|
||||||
notification->setPixmap(createNotificationImage(avatar_image, nullptr));
|
notification->setPixmap(createNotificationImage(sender, room));
|
||||||
auto defaultAction = notification->addDefaultAction(i18n("Open this invitation in NeoChat"));
|
auto defaultAction = notification->addDefaultAction(i18n("Open this invitation in NeoChat"));
|
||||||
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
||||||
if (!room) {
|
if (!room) {
|
||||||
@@ -411,41 +396,125 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap NotificationsManager::createNotificationImage(const QImage &icon, NeoChatRoom *room)
|
QPixmap NotificationsManager::createNotificationImage(const Quotient::RoomMember &member, NeoChatRoom *room)
|
||||||
|
{
|
||||||
|
QImage senderIcon = member.avatar(avatarDimension, avatarDimension, {});
|
||||||
|
bool senderIconIsPlaceholder = false;
|
||||||
|
if (senderIcon.isNull()) {
|
||||||
|
senderIcon = createPlaceholderImage(member.displayName());
|
||||||
|
senderIconIsPlaceholder = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage icon;
|
||||||
|
if (room->isDirectChat()) {
|
||||||
|
icon = senderIcon;
|
||||||
|
} else {
|
||||||
|
QImage roomIcon = room->avatar(avatarDimension, avatarDimension);
|
||||||
|
bool roomIconIsPlaceholder = false;
|
||||||
|
if (roomIcon.isNull()) {
|
||||||
|
roomIcon = createPlaceholderImage(room->displayName());
|
||||||
|
roomIconIsPlaceholder = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon = createCombinedNotificationImage(senderIcon, senderIconIsPlaceholder, roomIcon, roomIconIsPlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPixmap::fromImage(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage NotificationsManager::createCombinedNotificationImage(const QImage &senderIcon,
|
||||||
|
const bool senderIconIsPlaceholder,
|
||||||
|
const QImage &roomIcon,
|
||||||
|
const bool roomIconIsPlaceholder)
|
||||||
{
|
{
|
||||||
// Handle avatars that are lopsided in one dimension
|
// Handle avatars that are lopsided in one dimension
|
||||||
const int biggestDimension = std::max(icon.width(), icon.height());
|
const int biggestDimension = std::max(senderIcon.width(), senderIcon.height());
|
||||||
const QRect imageRect{0, 0, biggestDimension, biggestDimension};
|
const QRectF imageRect = QRect{0, 0, biggestDimension, biggestDimension}.toRectF();
|
||||||
|
|
||||||
QImage roundedImage(imageRect.size(), QImage::Format_ARGB32);
|
QImage roundedImage(imageRect.size().toSize(), QImage::Format_ARGB32);
|
||||||
roundedImage.fill(Qt::transparent);
|
roundedImage.fill(Qt::transparent);
|
||||||
|
|
||||||
QPainter painter(&roundedImage);
|
QPainter painter(&roundedImage);
|
||||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
|
|
||||||
// Fill background for transparent avatars
|
if (senderIconIsPlaceholder) {
|
||||||
painter.setBrush(Qt::white);
|
painter.drawImage(imageRect, senderIcon);
|
||||||
painter.drawRoundedRect(imageRect, imageRect.width(), imageRect.height());
|
} else {
|
||||||
|
// Fill background for transparent non-placeholder avatars
|
||||||
|
painter.setBrush(Qt::white);
|
||||||
|
painter.drawRoundedRect(imageRect, imageRect.width(), imageRect.height());
|
||||||
|
|
||||||
QBrush brush(icon.scaledToHeight(biggestDimension));
|
painter.setBrush(senderIcon.scaledToHeight(biggestDimension));
|
||||||
painter.setBrush(brush);
|
painter.drawRoundedRect(imageRect, imageRect.width(), imageRect.height());
|
||||||
painter.drawRoundedRect(imageRect, imageRect.width(), imageRect.height());
|
|
||||||
|
|
||||||
if (room != nullptr) {
|
|
||||||
const QImage roomAvatar = room->avatar(imageRect.width(), imageRect.height());
|
|
||||||
if (!roomAvatar.isNull() && icon != roomAvatar) {
|
|
||||||
const QRect lowerQuarter{imageRect.center(), imageRect.size() / 2};
|
|
||||||
|
|
||||||
painter.setBrush(Qt::white);
|
|
||||||
painter.drawRoundedRect(lowerQuarter, lowerQuarter.width(), lowerQuarter.height());
|
|
||||||
|
|
||||||
painter.setBrush(roomAvatar.scaled(lowerQuarter.size()));
|
|
||||||
painter.drawRoundedRect(lowerQuarter, lowerQuarter.width(), lowerQuarter.height());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return QPixmap::fromImage(roundedImage);
|
const QRectF lowerQuarter{imageRect.center(), imageRect.size() / 2.0};
|
||||||
|
|
||||||
|
if (roomIconIsPlaceholder) {
|
||||||
|
// Ditto for room icons, but we also want to "carve out" the transparent area for readability
|
||||||
|
painter.setCompositionMode(QPainter::CompositionMode_Clear);
|
||||||
|
painter.setBrush(Qt::transparent);
|
||||||
|
painter.drawRoundedRect(lowerQuarter, lowerQuarter.width(), lowerQuarter.height());
|
||||||
|
|
||||||
|
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||||
|
painter.drawImage(lowerQuarter, roomIcon);
|
||||||
|
} else {
|
||||||
|
painter.setBrush(Qt::white);
|
||||||
|
painter.drawRoundedRect(lowerQuarter, lowerQuarter.width(), lowerQuarter.height());
|
||||||
|
|
||||||
|
painter.setBrush(roomIcon.scaled(lowerQuarter.size().toSize()));
|
||||||
|
painter.drawRoundedRect(lowerQuarter, lowerQuarter.width(), lowerQuarter.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
return roundedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage NotificationsManager::createPlaceholderImage(const QString &name)
|
||||||
|
{
|
||||||
|
const QColor color = NameUtils().colorsFromString(name);
|
||||||
|
|
||||||
|
QImage image(avatarDimension, avatarDimension, QImage::Format_ARGB32);
|
||||||
|
image.fill(Qt::transparent);
|
||||||
|
|
||||||
|
QPainter painter(&image);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
// Draw background
|
||||||
|
QColor backgroundColor = color;
|
||||||
|
backgroundColor.setAlphaF(0.07); // Same as in Kirigami Add-ons.
|
||||||
|
|
||||||
|
painter.setBrush(backgroundColor);
|
||||||
|
painter.setPen(Qt::transparent);
|
||||||
|
painter.drawRoundedRect(image.rect(), image.width(), image.height());
|
||||||
|
|
||||||
|
constexpr float borderWidth = 3.0; // Slightly bigger than in Add-ons so it renders better with QPainter at these dimensions.
|
||||||
|
|
||||||
|
// Draw border
|
||||||
|
painter.setBrush(Qt::transparent);
|
||||||
|
painter.setPen(QPen(color, borderWidth));
|
||||||
|
painter.drawRoundedRect(image.rect().toRectF().marginsRemoved(QMarginsF(borderWidth, borderWidth, borderWidth, borderWidth)),
|
||||||
|
image.width(),
|
||||||
|
image.height());
|
||||||
|
|
||||||
|
const QString initials = NameUtils().initialsFromString(name);
|
||||||
|
|
||||||
|
QTextOption option;
|
||||||
|
option.setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
// Calculation similar to the one found in Kirigami Add-ons.
|
||||||
|
constexpr int largeSpacing = 8; // Same as what's defined in kirigami.
|
||||||
|
constexpr int padding = std::max(0, std::min(largeSpacing, avatarDimension - largeSpacing * 2));
|
||||||
|
|
||||||
|
QFont font;
|
||||||
|
font.setPixelSize((avatarDimension - padding) / 2);
|
||||||
|
|
||||||
|
painter.setBrush(color);
|
||||||
|
painter.setPen(color);
|
||||||
|
painter.setFont(font);
|
||||||
|
painter.drawText(image.rect(), initials, option);
|
||||||
|
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_notificationsmanager.cpp"
|
#include "moc_notificationsmanager.cpp"
|
||||||
|
|||||||
@@ -13,6 +13,11 @@
|
|||||||
#include <Quotient/csapi/notifications.h>
|
#include <Quotient/csapi/notifications.h>
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
|
|
||||||
|
namespace Quotient
|
||||||
|
{
|
||||||
|
class RoomMember;
|
||||||
|
}
|
||||||
|
|
||||||
class NeoChatConnection;
|
class NeoChatConnection;
|
||||||
class KNotification;
|
class KNotification;
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
@@ -67,15 +72,24 @@ private:
|
|||||||
QStringList m_connActiveJob;
|
QStringList m_connActiveJob;
|
||||||
void startNotificationJob(QPointer<NeoChatConnection> connection);
|
void startNotificationJob(QPointer<NeoChatConnection> connection);
|
||||||
|
|
||||||
QPixmap createNotificationImage(const QImage &icon, NeoChatRoom *room);
|
/**
|
||||||
|
* @return A combined image of the sender and room's avatar.
|
||||||
|
*/
|
||||||
|
static QPixmap createNotificationImage(const Quotient::RoomMember &member, NeoChatRoom *room);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The sender and room icon combined together into one image. Used internally by createNotificationImage.
|
||||||
|
*/
|
||||||
|
static QImage createCombinedNotificationImage(const QImage &senderIcon, bool senderIconIsPlaceholder, const QImage &roomIcon, bool roomIconIsPlaceholder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A placeholder avatar image, similar to the one found in Kirigami Add-ons.
|
||||||
|
*/
|
||||||
|
static QImage createPlaceholderImage(const QString &name);
|
||||||
|
|
||||||
bool shouldPostNotification(QPointer<NeoChatConnection> connection, const QJsonValue ¬ification);
|
bool shouldPostNotification(QPointer<NeoChatConnection> connection, const QJsonValue ¬ification);
|
||||||
void postNotification(NeoChatRoom *room,
|
void
|
||||||
const QString &sender,
|
postNotification(NeoChatRoom *room, const Quotient::RoomMember &member, const QString &text, const QString &replyEventId, bool canReply, qint64 timestamp);
|
||||||
const QString &text,
|
|
||||||
const QImage &icon,
|
|
||||||
const QString &replyEventId,
|
|
||||||
bool canReply,
|
|
||||||
qint64 timestamp);
|
|
||||||
|
|
||||||
void doPostInviteNotification(QPointer<NeoChatRoom> room);
|
void doPostInviteNotification(QPointer<NeoChatRoom> room);
|
||||||
|
|
||||||
@@ -84,6 +98,8 @@ private:
|
|||||||
|
|
||||||
bool permissionAsked = false;
|
bool permissionAsked = false;
|
||||||
|
|
||||||
|
static constexpr int avatarDimension = 128;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void processNotificationJob(QPointer<NeoChatConnection> connection, Quotient::GetNotificationsJob *job, bool initialization);
|
void processNotificationJob(QPointer<NeoChatConnection> connection, Quotient::GetNotificationsJob *job, bool initialization);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user