diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index b5012818..d318d086 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -220,6 +220,17 @@ void ChatPage::syncFailed(const QString &msg)
sync_timer_->start(sync_interval_ * 5);
}
+// TODO: Should be moved in another class that manages this global list.
+void ChatPage::updateDisplayNames(const RoomState &state)
+{
+ for (const auto member : state.memberships) {
+ auto displayName = member.content().displayName();
+
+ if (!displayName.isEmpty())
+ TimelineViewManager::DISPLAY_NAMES.insert(member.stateKey(), displayName);
+ }
+}
+
void ChatPage::syncCompleted(const SyncResponse &response)
{
client_->setNextBatchToken(response.nextBatch());
@@ -234,8 +245,16 @@ void ChatPage::syncCompleted(const SyncResponse &response)
updateRoomState(room_state, it.value().state().events());
updateRoomState(room_state, it.value().timeline().events());
+ updateDisplayNames(room_state);
- state_manager_.insert(it.key(), room_state);
+ if (state_manager_.contains(it.key())) {
+ // TODO: Use pointers instead of copying.
+ auto oldState = state_manager_[it.key()];
+ oldState.update(room_state);
+ state_manager_.insert(it.key(), oldState);
+ } else {
+ qWarning() << "New rooms cannot be added after initial sync, yet.";
+ }
if (it.key() == current_room_)
changeTopRoomInfo(it.key());
@@ -260,6 +279,12 @@ void ChatPage::initialSyncCompleted(const SyncResponse &response)
updateRoomState(room_state, it.value().state().events());
updateRoomState(room_state, it.value().timeline().events());
+ room_state.removeLeaveMemberships();
+ room_state.resolveName();
+ room_state.resolveAvatar();
+
+ updateDisplayNames(room_state);
+
state_manager_.insert(it.key(), room_state);
}
@@ -298,13 +323,13 @@ void ChatPage::changeTopRoomInfo(const QString &room_id)
auto state = state_manager_[room_id];
- top_bar_->updateRoomName(state.resolveName());
- top_bar_->updateRoomTopic(state.resolveTopic());
+ top_bar_->updateRoomName(state.getName());
+ top_bar_->updateRoomTopic(state.getTopic());
if (room_avatars_.contains(room_id))
top_bar_->updateRoomAvatar(room_avatars_.value(room_id).toImage());
else
- top_bar_->updateRoomAvatarFromName(state.resolveName());
+ top_bar_->updateRoomAvatarFromName(state.getName());
current_room_ = room_id;
}
@@ -383,15 +408,7 @@ void ChatPage::updateRoomState(RoomState &room_state, const QJsonArray &events)
events::StateEvent<events::MemberEventContent> member;
member.deserialize(event);
- auto display_name = member.content().displayName();
-
- if (display_name.isEmpty())
- display_name = member.stateKey();
-
- auto current_name = TimelineViewManager::DISPLAY_NAMES.value(member.stateKey());
-
- if (current_name.isEmpty() || current_name == member.stateKey())
- TimelineViewManager::DISPLAY_NAMES.insert(member.stateKey(), display_name);
+ room_state.memberships.insert(member.stateKey(), member);
break;
}
diff --git a/src/RoomInfoListItem.cc b/src/RoomInfoListItem.cc
index 9385f927..7753536e 100644
--- a/src/RoomInfoListItem.cc
+++ b/src/RoomInfoListItem.cc
@@ -29,7 +29,7 @@ RoomInfoListItem::RoomInfoListItem(RoomState state, QString room_id, QWidget *pa
, state_(state)
, roomId_(room_id)
, isPressed_(false)
- , maxHeight_(60)
+ , maxHeight_(IconSize + 2 * Padding)
, unreadMsgCount_(0)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -71,7 +71,7 @@ void RoomInfoListItem::paintEvent(QPaintEvent *event)
QRect avatarRegion(Padding, Padding, IconSize, IconSize);
// Description line
- int bottom_y = avatarRegion.center().y() + metrics.height() / 2 + Padding / 2;
+ int bottom_y = maxHeight_ - Padding - metrics.height() / 2 + Padding / 2;
if (width() > ui::sidebar::SmallSize) {
if (isPressed_) {
@@ -79,8 +79,8 @@ void RoomInfoListItem::paintEvent(QPaintEvent *event)
p.setPen(pen);
}
- auto name = metrics.elidedText(state_.resolveName(), Qt::ElideRight, (width() - IconSize - 2 * Padding) * 0.8);
- p.drawText(QPoint(2 * Padding + IconSize, avatarRegion.center().y() - metrics.height() / 2), name);
+ auto name = metrics.elidedText(state_.getName(), Qt::ElideRight, (width() - IconSize - 2 * Padding) * 0.8);
+ p.drawText(QPoint(2 * Padding + IconSize, Padding + metrics.height()), name);
if (!isPressed_) {
QPen pen(QColor("#5d6565"));
@@ -92,7 +92,7 @@ void RoomInfoListItem::paintEvent(QPaintEvent *event)
if (unreadMsgCount_ > 0)
descPercentage = 0.8;
- auto description = metrics.elidedText(state_.resolveTopic(), Qt::ElideRight, width() * descPercentage - 2 * Padding - IconSize);
+ auto description = metrics.elidedText(state_.getTopic(), Qt::ElideRight, width() * descPercentage - 2 * Padding - IconSize);
p.drawText(QPoint(2 * Padding + IconSize, bottom_y), description);
}
@@ -113,7 +113,7 @@ void RoomInfoListItem::paintEvent(QPaintEvent *event)
p.setFont(font);
p.setPen(QColor("#333"));
p.setBrush(Qt::NoBrush);
- p.drawText(avatarRegion.translated(0, -1), Qt::AlignCenter, QChar(state_.resolveName()[0]));
+ p.drawText(avatarRegion.translated(0, -1), Qt::AlignCenter, QChar(state_.getName()[0]));
} else {
p.save();
diff --git a/src/RoomList.cc b/src/RoomList.cc
index 3e381340..6d0e185b 100644
--- a/src/RoomList.cc
+++ b/src/RoomList.cc
@@ -100,8 +100,8 @@ void RoomList::setInitialRooms(const QMap<QString, RoomState> &states)
auto room_id = it.key();
auto state = it.value();
- if (!state.avatar.content().url().toString().isEmpty())
- client_->fetchRoomAvatar(room_id, state.avatar.content().url());
+ if (!state.getAvatar().toString().isEmpty())
+ client_->fetchRoomAvatar(room_id, state.getAvatar());
RoomInfoListItem *room_item = new RoomInfoListItem(state, room_id, scrollArea_);
connect(room_item, &RoomInfoListItem::clicked, this, &RoomList::highlightSelectedRoom);
@@ -133,8 +133,8 @@ void RoomList::sync(const QMap<QString, RoomState> &states)
auto room = rooms_[room_id];
- auto current_avatar = room->state().avatar.content().url();
- auto new_avatar = state.avatar.content().url();
+ auto current_avatar = room->state().getAvatar();
+ auto new_avatar = state.getAvatar();
if (current_avatar != new_avatar && !new_avatar.toString().isEmpty())
client_->fetchRoomAvatar(room_id, new_avatar);
diff --git a/src/RoomState.cc b/src/RoomState.cc
index 98f418e3..3eaff452 100644
--- a/src/RoomState.cc
+++ b/src/RoomState.cc
@@ -15,18 +15,138 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <QDebug>
+#include <QSettings>
+
#include "RoomState.h"
-QString RoomState::resolveName() const
+namespace events = matrix::events;
+
+void RoomState::resolveName()
{
- if (!name.content().name().isEmpty())
- return name.content().name().simplified();
+ name_ = "Empty Room";
+ userAvatar_.clear();
+
+ if (!name.content().name().isEmpty()) {
+ name_ = name.content().name().simplified();
+ return;
+ }
+
+ if (!canonical_alias.content().alias().isEmpty()) {
+ name_ = canonical_alias.content().alias().simplified();
+ return;
+ }
+
+ // FIXME: Doesn't follow the spec guidelines.
+ if (aliases.content().aliases().size() != 0) {
+ name_ = aliases.content().aliases()[0].simplified();
+ return;
+ }
+
+ QSettings settings;
+ auto user_id = settings.value("auth/user_id");
+
+ // TODO: Display names should be sorted alphabetically.
+ for (const auto membership : memberships) {
+ if (membership.stateKey() == user_id)
+ continue;
+
+ if (membership.content().membershipState() == events::Membership::Join) {
+ userAvatar_ = membership.stateKey();
+
+ if (membership.content().displayName().isEmpty())
+ name_ = membership.stateKey();
+ else
+ name_ = membership.content().displayName();
+
+ break;
+ }
+ }
+
+ // TODO: pluralization
+ if (memberships.size() > 2)
+ name_ = QString("%1 and %2 others").arg(name_).arg(memberships.size());
+}
+
+void RoomState::resolveAvatar()
+{
+ if (userAvatar_.isEmpty()) {
+ avatar_ = avatar.content().url();
+ return;
+ }
+
+ if (memberships.contains(userAvatar_)) {
+ avatar_ = memberships[userAvatar_].content().avatarUrl();
+ } else {
+ qWarning() << "Setting room avatar from unknown user id" << userAvatar_;
+ }
+}
+
+// Should be used only after initial sync.
+void RoomState::removeLeaveMemberships()
+{
+ for (auto it = memberships.begin(); it != memberships.end();) {
+ if (it.value().content().membershipState() == events::Membership::Leave) {
+ it = memberships.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void RoomState::update(const RoomState &state)
+{
+ bool needsNameCalculation = false;
+ bool needsAvatarCalculation = false;
+
+ if (aliases.eventId() != state.aliases.eventId()) {
+ aliases = state.aliases;
+ }
+
+ if (avatar.eventId() != state.avatar.eventId()) {
+ avatar = state.avatar;
+ needsAvatarCalculation = true;
+ }
+
+ if (canonical_alias.eventId() != state.canonical_alias.eventId()) {
+ canonical_alias = state.canonical_alias;
+ needsNameCalculation = true;
+ }
+
+ if (create.eventId() != state.create.eventId())
+ create = state.create;
+ if (history_visibility.eventId() != state.history_visibility.eventId())
+ history_visibility = state.history_visibility;
+ if (join_rules.eventId() != state.join_rules.eventId())
+ join_rules = state.join_rules;
+
+ if (name.eventId() != state.name.eventId()) {
+ name = state.name;
+ needsNameCalculation = true;
+ }
+
+ if (power_levels.eventId() != state.power_levels.eventId())
+ power_levels = state.power_levels;
+ if (topic.eventId() != state.topic.eventId())
+ topic = state.topic;
+
+ for (auto it = state.memberships.constBegin(); it != state.memberships.constEnd(); ++it) {
+ auto membershipState = it.value().content().membershipState();
+
+ if (it.key() == userAvatar_) {
+ needsNameCalculation = true;
+ needsAvatarCalculation = true;
+ }
- if (!canonical_alias.content().alias().isEmpty())
- return canonical_alias.content().alias().simplified();
+ if (membershipState == events::Membership::Leave)
+ this->memberships.remove(it.key());
+ else
+ this->memberships.insert(it.key(), it.value());
+ }
- if (aliases.content().aliases().size() != 0)
- return aliases.content().aliases()[0].simplified();
+ if (needsNameCalculation)
+ resolveName();
- return "Unknown Room Name";
+ if (needsAvatarCalculation)
+ resolveAvatar();
}
diff --git a/src/events/MemberEventContent.cc b/src/events/MemberEventContent.cc
index 4c405f01..4dc8ad5f 100644
--- a/src/events/MemberEventContent.cc
+++ b/src/events/MemberEventContent.cc
@@ -34,15 +34,15 @@ void MemberEventContent::deserialize(const QJsonValue &data)
auto value = object.value("membership").toString();
if (value == "ban")
- membership_state_ = Membership::BanState;
+ membership_state_ = Membership::Ban;
else if (value == "invite")
- membership_state_ = Membership::InviteState;
+ membership_state_ = Membership::Invite;
else if (value == "join")
- membership_state_ = Membership::JoinState;
+ membership_state_ = Membership::Join;
else if (value == "knock")
- membership_state_ = Membership::KnockState;
+ membership_state_ = Membership::Knock;
else if (value == "leave")
- membership_state_ = Membership::LeaveState;
+ membership_state_ = Membership::Leave;
else
throw DeserializationException(QString("Unknown membership value: %1").arg(value).toUtf8().constData());
|