mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
parent
9f3de8679d
commit
8767ea181d
11 changed files with 127 additions and 18 deletions
|
@ -825,8 +825,10 @@ Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Recei
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Cache::notifyForReadReceipts(lmdb::txn &txn, const std::string &room_id)
|
Cache::notifyForReadReceipts(const std::string &room_id)
|
||||||
{
|
{
|
||||||
|
auto txn = lmdb::txn::begin(env_);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
auto local_user = settings.value("auth/user_id").toString();
|
auto local_user = settings.value("auth/user_id").toString();
|
||||||
|
|
||||||
|
@ -839,6 +841,47 @@ Cache::notifyForReadReceipts(lmdb::txn &txn, const std::string &room_id)
|
||||||
|
|
||||||
if (!matches.empty())
|
if (!matches.empty())
|
||||||
emit newReadReceipts(QString::fromStdString(room_id), matches);
|
emit newReadReceipts(QString::fromStdString(room_id), matches);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Cache::calculateRoomReadStatus()
|
||||||
|
{
|
||||||
|
const auto joined_rooms = joinedRooms();
|
||||||
|
|
||||||
|
std::map<QString, bool> readStatus;
|
||||||
|
|
||||||
|
for (const auto &room : joined_rooms)
|
||||||
|
readStatus.emplace(QString::fromStdString(room), calculateRoomReadStatus(room));
|
||||||
|
|
||||||
|
emit roomReadStatus(readStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Cache::calculateRoomReadStatus(const std::string &room_id)
|
||||||
|
{
|
||||||
|
auto txn = lmdb::txn::begin(env_);
|
||||||
|
|
||||||
|
// Get last event id on the room.
|
||||||
|
const auto last_event_id = getLastMessageInfo(txn, room_id).event_id;
|
||||||
|
const auto localUser = utils::localUser().toStdString();
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
// Retrieve all read receipts for that event.
|
||||||
|
const auto receipts = readReceipts(last_event_id, QString::fromStdString(room_id));
|
||||||
|
|
||||||
|
if (receipts.size() == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if the local user has a read receipt for it.
|
||||||
|
for (auto it = receipts.cbegin(); it != receipts.cend(); it++) {
|
||||||
|
if (it->second == localUser)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -880,11 +923,15 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
|
||||||
|
std::map<QString, bool> readStatus;
|
||||||
|
|
||||||
for (const auto &room : res.rooms.join) {
|
for (const auto &room : res.rooms.join) {
|
||||||
auto tmpTxn = lmdb::txn::begin(env_);
|
notifyForReadReceipts(room.first);
|
||||||
notifyForReadReceipts(tmpTxn, room.first);
|
readStatus.emplace(QString::fromStdString(room.first),
|
||||||
tmpTxn.commit();
|
calculateRoomReadStatus(room.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit roomReadStatus(readStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -89,6 +89,7 @@ from_json(const json &j, ReadReceiptKey &key)
|
||||||
|
|
||||||
struct DescInfo
|
struct DescInfo
|
||||||
{
|
{
|
||||||
|
QString event_id;
|
||||||
QString username;
|
QString username;
|
||||||
QString userid;
|
QString userid;
|
||||||
QString body;
|
QString body;
|
||||||
|
@ -356,7 +357,7 @@ public:
|
||||||
void removePendingReceipt(lmdb::txn &txn,
|
void removePendingReceipt(lmdb::txn &txn,
|
||||||
const std::string &room_id,
|
const std::string &room_id,
|
||||||
const std::string &event_id);
|
const std::string &event_id);
|
||||||
void notifyForReadReceipts(lmdb::txn &txn, const std::string &room_id);
|
void notifyForReadReceipts(const std::string &room_id);
|
||||||
std::vector<QString> pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
std::vector<QString> pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
||||||
|
|
||||||
QByteArray image(const QString &url) const;
|
QByteArray image(const QString &url) const;
|
||||||
|
@ -376,6 +377,11 @@ public:
|
||||||
return getRoomInfo(roomsWithStateUpdates(sync));
|
return getRoomInfo(roomsWithStateUpdates(sync));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Calculates which the read status of a room.
|
||||||
|
//! Whether all the events in the timeline have been read.
|
||||||
|
bool calculateRoomReadStatus(const std::string &room_id);
|
||||||
|
void calculateRoomReadStatus();
|
||||||
|
|
||||||
QVector<SearchResult> searchUsers(const std::string &room_id,
|
QVector<SearchResult> searchUsers(const std::string &room_id,
|
||||||
const std::string &query,
|
const std::string &query,
|
||||||
std::uint8_t max_items = 5);
|
std::uint8_t max_items = 5);
|
||||||
|
@ -444,6 +450,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
||||||
|
void roomReadStatus(const std::map<QString, bool> &status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! Save an invited room.
|
//! Save an invited room.
|
||||||
|
|
|
@ -677,6 +677,9 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
||||||
view_manager_,
|
view_manager_,
|
||||||
&TimelineViewManager::updateReadReceipts);
|
&TimelineViewManager::updateReadReceipts);
|
||||||
|
|
||||||
|
connect(
|
||||||
|
cache::client(), &Cache::roomReadStatus, room_list_, &RoomList::updateReadStatus);
|
||||||
|
|
||||||
const bool isInitialized = cache::client()->isInitialized();
|
const bool isInitialized = cache::client()->isInitialized();
|
||||||
const bool isValid = cache::client()->isFormatValid();
|
const bool isValid = cache::client()->isFormatValid();
|
||||||
|
|
||||||
|
@ -794,6 +797,8 @@ ChatPage::loadStateFromCache()
|
||||||
emit initializeEmptyViews(cache::client()->roomMessages());
|
emit initializeEmptyViews(cache::client()->roomMessages());
|
||||||
emit initializeRoomList(cache::client()->roomInfo());
|
emit initializeRoomList(cache::client()->roomInfo());
|
||||||
|
|
||||||
|
cache::client()->calculateRoomReadStatus();
|
||||||
|
|
||||||
} catch (const mtx::crypto::olm_exception &e) {
|
} catch (const mtx::crypto::olm_exception &e) {
|
||||||
nhlog::crypto()->critical("failed to restore olm account: {}", e.what());
|
nhlog::crypto()->critical("failed to restore olm account: {}", e.what());
|
||||||
emit dropToLoginPageCb(
|
emit dropToLoginPageCb(
|
||||||
|
|
|
@ -31,9 +31,11 @@
|
||||||
|
|
||||||
constexpr int MaxUnreadCountDisplayed = 99;
|
constexpr int MaxUnreadCountDisplayed = 99;
|
||||||
|
|
||||||
constexpr int Padding = 9;
|
constexpr int Padding = 9;
|
||||||
constexpr int IconSize = 44;
|
constexpr int UnreadLineWidth = 4;
|
||||||
constexpr int MaxHeight = IconSize + 2 * Padding;
|
constexpr int UnreadLineOffset = 4;
|
||||||
|
constexpr int IconSize = 44;
|
||||||
|
constexpr int MaxHeight = IconSize + 2 * Padding;
|
||||||
|
|
||||||
constexpr int InviteBtnX = IconSize + 2 * Padding;
|
constexpr int InviteBtnX = IconSize + 2 * Padding;
|
||||||
constexpr int InviteBtnY = IconSize / 2 + Padding + Padding / 3;
|
constexpr int InviteBtnY = IconSize / 2 + Padding + Padding / 3;
|
||||||
|
@ -89,6 +91,8 @@ RoomInfoListItem::RoomInfoListItem(QString room_id, RoomInfo info, QWidget *pare
|
||||||
{
|
{
|
||||||
init(parent);
|
init(parent);
|
||||||
|
|
||||||
|
QString emptyEventId;
|
||||||
|
|
||||||
// HACK
|
// HACK
|
||||||
// We use fake message info with an old date to pin
|
// We use fake message info with an old date to pin
|
||||||
// the invite events to the top.
|
// the invite events to the top.
|
||||||
|
@ -96,7 +100,8 @@ RoomInfoListItem::RoomInfoListItem(QString room_id, RoomInfo info, QWidget *pare
|
||||||
// State events in invited rooms don't contain timestamp info,
|
// State events in invited rooms don't contain timestamp info,
|
||||||
// so we can't use them for sorting.
|
// so we can't use them for sorting.
|
||||||
if (roomType_ == RoomType::Invited)
|
if (roomType_ == RoomType::Invited)
|
||||||
lastMsgInfo_ = {"-", "-", "-", "-", QDateTime::currentDateTime().addYears(10)};
|
lastMsgInfo_ = {
|
||||||
|
emptyEventId, "-", "-", "-", "-", QDateTime::currentDateTime().addYears(10)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -305,6 +310,15 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
|
||||||
p.setBrush(Qt::NoBrush);
|
p.setBrush(Qt::NoBrush);
|
||||||
p.drawText(r.translated(0, -0.5), Qt::AlignCenter, countTxt);
|
p.drawText(r.translated(0, -0.5), Qt::AlignCenter, countTxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isPressed_ && hasUnreadMessages_) {
|
||||||
|
QPen pen;
|
||||||
|
pen.setWidth(UnreadLineWidth);
|
||||||
|
pen.setColor(highlightedBackgroundColor_);
|
||||||
|
|
||||||
|
p.setPen(pen);
|
||||||
|
p.drawLine(0, UnreadLineOffset, 0, height() - UnreadLineOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -121,6 +121,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInvite() { return roomType_ == RoomType::Invited; }
|
bool isInvite() { return roomType_ == RoomType::Invited; }
|
||||||
|
void setReadState(bool hasUnreadMessages)
|
||||||
|
{
|
||||||
|
if (hasUnreadMessages_ != hasUnreadMessages) {
|
||||||
|
hasUnreadMessages_ = hasUnreadMessages;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clicked(const QString &room_id);
|
void clicked(const QString &room_id);
|
||||||
|
@ -164,7 +171,8 @@ private:
|
||||||
Menu *menu_;
|
Menu *menu_;
|
||||||
QAction *leaveRoom_;
|
QAction *leaveRoom_;
|
||||||
|
|
||||||
bool isPressed_ = false;
|
bool isPressed_ = false;
|
||||||
|
bool hasUnreadMessages_ = true;
|
||||||
|
|
||||||
int unreadMsgCount_ = 0;
|
int unreadMsgCount_ = 0;
|
||||||
|
|
||||||
|
|
|
@ -455,3 +455,16 @@ RoomList::firstRoom() const
|
||||||
return std::pair<QString, QSharedPointer<RoomInfoListItem>>(firstRoom->first,
|
return std::pair<QString, QSharedPointer<RoomInfoListItem>>(firstRoom->first,
|
||||||
firstRoom->second);
|
firstRoom->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RoomList::updateReadStatus(const std::map<QString, bool> &status)
|
||||||
|
{
|
||||||
|
for (const auto &room : status) {
|
||||||
|
if (roomExists(room.first)) {
|
||||||
|
auto item = rooms_.at(room.first);
|
||||||
|
|
||||||
|
if (item)
|
||||||
|
item->setReadState(room.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ public slots:
|
||||||
void updateUnreadMessageCount(const QString &roomid, int count);
|
void updateUnreadMessageCount(const QString &roomid, int count);
|
||||||
void updateRoomDescription(const QString &roomid, const DescInfo &info);
|
void updateRoomDescription(const QString &roomid, const DescInfo &info);
|
||||||
void closeJoinRoomDialog(bool isJoining, QString roomAlias);
|
void closeJoinRoomDialog(bool isJoining, QString roomAlias);
|
||||||
|
void updateReadStatus(const std::map<QString, bool> &status);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
|
@ -126,6 +126,7 @@ utils::getMessageDescription(const TimelineEvent &event,
|
||||||
info.userid = sender;
|
info.userid = sender;
|
||||||
info.body = QString(" %1").arg(messageDescription<Encrypted>());
|
info.body = QString(" %1").arg(messageDescription<Encrypted>());
|
||||||
info.timestamp = utils::descriptiveTime(ts);
|
info.timestamp = utils::descriptiveTime(ts);
|
||||||
|
info.event_id = QString::fromStdString(msg.event_id);
|
||||||
info.datetime = ts;
|
info.datetime = ts;
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
|
|
@ -109,6 +109,7 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin
|
||||||
bool isEmote = std::is_same<T, Emote>::value;
|
bool isEmote = std::is_same<T, Emote>::value;
|
||||||
|
|
||||||
return DescInfo{
|
return DescInfo{
|
||||||
|
QString::fromStdString(msg.event_id),
|
||||||
isEmote ? "" : (sender == localUser ? "You" : username),
|
isEmote ? "" : (sender == localUser ? "You" : username),
|
||||||
sender,
|
sender,
|
||||||
(isText || isEmote)
|
(isText || isEmote)
|
||||||
|
|
|
@ -317,16 +317,23 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
|
||||||
if (formatted_body == body.trimmed().toHtmlEscaped())
|
if (formatted_body == body.trimmed().toHtmlEscaped())
|
||||||
formatted_body = body.toHtmlEscaped();
|
formatted_body = body.toHtmlEscaped();
|
||||||
|
|
||||||
|
QString emptyEventId;
|
||||||
|
|
||||||
if (ty == mtx::events::MessageType::Emote) {
|
if (ty == mtx::events::MessageType::Emote) {
|
||||||
formatted_body = QString("<em>%1</em>").arg(formatted_body);
|
formatted_body = QString("<em>%1</em>").arg(formatted_body);
|
||||||
descriptionMsg_ = {"",
|
descriptionMsg_ = {emptyEventId,
|
||||||
|
"",
|
||||||
userid,
|
userid,
|
||||||
QString("* %1 %2").arg(displayName).arg(body),
|
QString("* %1 %2").arg(displayName).arg(body),
|
||||||
utils::descriptiveTime(timestamp),
|
utils::descriptiveTime(timestamp),
|
||||||
timestamp};
|
timestamp};
|
||||||
} else {
|
} else {
|
||||||
descriptionMsg_ = {
|
descriptionMsg_ = {emptyEventId,
|
||||||
"You: ", userid, body, utils::descriptiveTime(timestamp), timestamp};
|
"You: ",
|
||||||
|
userid,
|
||||||
|
body,
|
||||||
|
utils::descriptiveTime(timestamp),
|
||||||
|
timestamp};
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted_body = utils::linkifyMessage(formatted_body);
|
formatted_body = utils::linkifyMessage(formatted_body);
|
||||||
|
@ -496,7 +503,8 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
|
||||||
auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());
|
auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());
|
||||||
auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();
|
auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();
|
||||||
|
|
||||||
descriptionMsg_ = {Cache::displayName(room_id_, sender),
|
descriptionMsg_ = {event_id_,
|
||||||
|
Cache::displayName(room_id_, sender),
|
||||||
sender,
|
sender,
|
||||||
" sent a notification",
|
" sent a notification",
|
||||||
utils::descriptiveTime(timestamp),
|
utils::descriptiveTime(timestamp),
|
||||||
|
@ -545,7 +553,8 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
|
||||||
auto displayName = Cache::displayName(room_id_, sender);
|
auto displayName = Cache::displayName(room_id_, sender);
|
||||||
formatted_body = QString("<em>%1</em>").arg(formatted_body);
|
formatted_body = QString("<em>%1</em>").arg(formatted_body);
|
||||||
|
|
||||||
descriptionMsg_ = {"",
|
descriptionMsg_ = {event_id_,
|
||||||
|
"",
|
||||||
sender,
|
sender,
|
||||||
QString("* %1 %2").arg(displayName).arg(body),
|
QString("* %1 %2").arg(displayName).arg(body),
|
||||||
utils::descriptiveTime(timestamp),
|
utils::descriptiveTime(timestamp),
|
||||||
|
@ -592,7 +601,8 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
|
||||||
auto displayName = Cache::displayName(room_id_, sender);
|
auto displayName = Cache::displayName(room_id_, sender);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
|
descriptionMsg_ = {event_id_,
|
||||||
|
sender == settings.value("auth/user_id") ? "You" : displayName,
|
||||||
sender,
|
sender,
|
||||||
QString(": %1").arg(body),
|
QString(": %1").arg(body),
|
||||||
utils::descriptiveTime(timestamp),
|
utils::descriptiveTime(timestamp),
|
||||||
|
|
|
@ -319,7 +319,8 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget, const QString &userid, bool
|
||||||
auto displayName = Cache::displayName(room_id_, userid);
|
auto displayName = Cache::displayName(room_id_, userid);
|
||||||
auto timestamp = QDateTime::currentDateTime();
|
auto timestamp = QDateTime::currentDateTime();
|
||||||
|
|
||||||
descriptionMsg_ = {"You",
|
descriptionMsg_ = {"", // No event_id up until this point.
|
||||||
|
"You",
|
||||||
userid,
|
userid,
|
||||||
QString(" %1").arg(utils::messageDescription<Widget>()),
|
QString(" %1").arg(utils::messageDescription<Widget>()),
|
||||||
utils::descriptiveTime(timestamp),
|
utils::descriptiveTime(timestamp),
|
||||||
|
@ -358,7 +359,8 @@ TimelineItem::setupWidgetLayout(Widget *widget, const Event &event, bool withSen
|
||||||
auto displayName = Cache::displayName(room_id_, sender);
|
auto displayName = Cache::displayName(room_id_, sender);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
|
descriptionMsg_ = {event_id_,
|
||||||
|
sender == settings.value("auth/user_id") ? "You" : displayName,
|
||||||
sender,
|
sender,
|
||||||
QString(" %1").arg(utils::messageDescription<Widget>()),
|
QString(" %1").arg(utils::messageDescription<Widget>()),
|
||||||
utils::descriptiveTime(timestamp),
|
utils::descriptiveTime(timestamp),
|
||||||
|
|
Loading…
Reference in a new issue