diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml
index 2f94cf84..39365cd1 100644
--- a/resources/qml/TopBar.qml
+++ b/resources/qml/TopBar.qml
@@ -49,6 +49,13 @@ Pane {
return;
}
+ if (communityLabel.visible && eventPoint.position.y < communityAvatar.height + Nheko.paddingMedium + Nheko.paddingSmall/2) {
+ if (!Communities.trySwitchToSpace(room.parentSpace.roomid))
+ room.parentSpace.promptJoin();
+ eventPoint.accepted = true
+ return;
+ }
+
if (room) {
let p = topBar.mapToItem(roomTopicC, eventPoint.position.x, eventPoint.position.y);
let link = roomTopicC.linkAt(p.x, p.y);
@@ -80,11 +87,46 @@ Pane {
columnSpacing: Nheko.paddingSmall
rowSpacing: Nheko.paddingSmall
+
+ Avatar {
+ id: communityAvatar
+
+ visible: roomid && room.parentSpace.isLoaded && ("space:"+room.parentSpace.roomid != Communities.currentTagId)
+
+ property string avatarUrl: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomAvatarUrl) || ""
+ property string communityId: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomid) || ""
+ property string communityName: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomName) || ""
+
+ Layout.column: 1
+ Layout.row: 0
+ Layout.alignment: Qt.AlignRight
+ width: fontMetrics.lineSpacing
+ height: fontMetrics.lineSpacing
+ url: avatarUrl.replace("mxc://", "image://MxcImage/")
+ roomid: communityId
+ displayName: communityName
+ enabled: false
+ }
+
+ Label {
+ id: communityLabel
+ visible: communityAvatar.visible
+
+ Layout.column: 2
+ Layout.row: 0
+ Layout.fillWidth: true
+ color: Nheko.colors.text
+ text: qsTr("In %1").arg(communityAvatar.displayName)
+ maximumLineCount: 1
+ elide: Text.ElideRight
+ textFormat: Text.RichText
+ }
+
ImageButton {
id: backToRoomsButton
Layout.column: 0
- Layout.row: 0
+ Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
@@ -98,7 +140,7 @@ Pane {
Avatar {
Layout.column: 1
- Layout.row: 0
+ Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
width: Nheko.avatarSize
@@ -113,9 +155,10 @@ Pane {
Label {
Layout.fillWidth: true
Layout.column: 2
- Layout.row: 0
+ Layout.row: 1
color: Nheko.colors.text
font.pointSize: fontMetrics.font.pointSize * 1.1
+ font.bold: true
text: roomName
maximumLineCount: 1
elide: Text.ElideRight
@@ -126,7 +169,7 @@ Pane {
id: roomTopicC
Layout.fillWidth: true
Layout.column: 2
- Layout.row: 1
+ Layout.row: 2
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
selectByMouse: false
enabled: false
@@ -136,7 +179,7 @@ Pane {
AbstractButton {
Layout.column: 3
- Layout.row: 0
+ Layout.row: 1
Layout.rowSpan: 2
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
@@ -179,7 +222,7 @@ Pane {
visible: !!room && room.pinnedMessages.length > 0
Layout.column: 4
- Layout.row: 0
+ Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
@@ -207,7 +250,7 @@ Pane {
visible: !!room
Layout.column: 5
- Layout.row: 0
+ Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
@@ -248,7 +291,7 @@ Pane {
ScrollView {
id: pinnedMessages
- Layout.row: 2
+ Layout.row: 3
Layout.column: 2
Layout.columnSpan: 3
@@ -329,7 +372,7 @@ Pane {
ScrollView {
id: widgets
- Layout.row: 3
+ Layout.row: 4
Layout.column: 2
Layout.columnSpan: 1
diff --git a/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml b/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
index 60ac06de..80905039 100644
--- a/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
+++ b/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
@@ -122,7 +122,6 @@ ApplicationWindow {
placeholderText: qsTr("Enter additional rooms not in the list yet...")
- //font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
color: Nheko.colors.text
onTextEdited: {
roomCompleter.completer.searchString = text;
diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp
index 72d0bdfb..a0c73f84 100644
--- a/src/timeline/CommunitiesModel.cpp
+++ b/src/timeline/CommunitiesModel.cpp
@@ -605,6 +605,20 @@ CommunitiesModel::setCurrentTagId(const QString &tagId)
emit currentTagIdChanged(currentTagId_);
}
+bool
+CommunitiesModel::trySwitchToSpace(const QString &tag)
+{
+ for (const auto &t : spaceOrder_.tree) {
+ if (t.id == tag) {
+ this->currentTagId_ = "space:" + tag;
+ emit currentTagIdChanged(currentTagId_);
+ return true;
+ }
+ }
+
+ return false;
+}
+
void
CommunitiesModel::toggleTagId(QString tagId)
{
diff --git a/src/timeline/CommunitiesModel.h b/src/timeline/CommunitiesModel.h
index 89f1ed07..3a599476 100644
--- a/src/timeline/CommunitiesModel.h
+++ b/src/timeline/CommunitiesModel.h
@@ -179,6 +179,7 @@ public slots:
void clear();
QString currentTagId() const { return currentTagId_; }
void setCurrentTagId(const QString &tagId);
+ bool trySwitchToSpace(const QString &spaceId);
void resetCurrentTagId()
{
currentTagId_.clear();
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index eaf85b2a..1ddef7b7 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -915,6 +915,9 @@ TimelineModel::syncState(const mtx::responses::State &s)
} else if (std::holds_alternative<StateEvent<state::Encryption>>(e)) {
this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
emit encryptionChanged();
+ } else if (std::holds_alternative<StateEvent<state::space::Parent>>(e)) {
+ this->parentChecked = false;
+ emit parentSpaceChanged();
}
}
}
@@ -976,6 +979,9 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
} else if (std::holds_alternative<StateEvent<state::Encryption>>(e)) {
this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
emit encryptionChanged();
+ } else if (std::holds_alternative<StateEvent<state::space::Parent>>(e)) {
+ this->parentChecked = false;
+ emit parentSpaceChanged();
}
}
@@ -2904,3 +2910,22 @@ TimelineModel::directChatOtherUserId() const
} else
return {};
}
+
+RoomSummary *
+TimelineModel::parentSpace()
+{
+ if (!parentChecked) {
+ auto parents = cache::client()->getStateEventsWithType<mtx::events::state::space::Parent>(
+ this->room_id_.toStdString());
+
+ for (const auto &p : parents) {
+ if (p.content.canonical and p.content.via and not p.content.via->empty()) {
+ parentSummary.reset(new RoomSummary(p.state_key, *p.content.via, ""));
+ QQmlEngine::setObjectOwnership(parentSummary.get(), QQmlEngine::CppOwnership);
+ break;
+ }
+ }
+ }
+
+ return parentSummary.get();
+}
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 295bc69b..ea6daa34 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -22,6 +22,7 @@
#include "MemberList.h"
#include "Permissions.h"
#include "ReadReceiptsModel.h"
+#include "ui/RoomSummary.h"
namespace mtx::http {
using RequestErr = const std::optional<mtx::http::ClientError> &;
@@ -197,6 +198,7 @@ class TimelineModel : public QAbstractListModel
QString directChatOtherUserId READ directChatOtherUserId NOTIFY directChatOtherUserIdChanged)
Q_PROPERTY(InputBar *input READ input CONSTANT)
Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged)
+ Q_PROPERTY(RoomSummary *parentSpace READ parentSpace NOTIFY parentSpaceChanged)
public:
explicit TimelineModel(TimelineViewManager *manager,
@@ -397,6 +399,7 @@ public slots:
Permissions *permissions() { return &permissions_; }
QString roomAvatarUrl() const;
QString roomId() const { return room_id_; }
+ RoomSummary *parentSpace();
bool hasMentions() const { return highlight_count > 0; }
int notificationCount() const { return notification_count; }
@@ -431,6 +434,7 @@ signals:
void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
void updateFlowEventId(std::string event_id);
+ void parentSpaceChanged();
void encryptionChanged();
void fullyReadEventIdChanged();
void trustlevelChanged();
@@ -488,6 +492,9 @@ private:
bool isEncrypted_ = false;
std::string last_event_id;
std::string fullyReadEventId_;
+
+ std::unique_ptr<RoomSummary> parentSummary = nullptr;
+ bool parentChecked = false;
};
template<class T>
diff --git a/src/ui/RoomSummary.cpp b/src/ui/RoomSummary.cpp
index da2a547c..6623da10 100644
--- a/src/ui/RoomSummary.cpp
+++ b/src/ui/RoomSummary.cpp
@@ -6,6 +6,7 @@
#include <QMetaType>
+#include "Cache.h"
#include "ChatPage.h"
#include "MatrixClient.h"
@@ -18,6 +19,38 @@ RoomSummary::RoomSummary(std::string roomIdOrAlias_,
, vias(std::move(vias_))
, reason_(std::move(r_))
{
+ if (roomIdOrAlias.empty())
+ return;
+
+ if (roomIdOrAlias[0] == '!') {
+ auto temp = cache::singleRoomInfo(roomIdOrAlias);
+
+ if (temp.member_count) {
+ mtx::responses::PublicRoom newInfo{};
+ // newInfo.aliases;
+ // newInfo.canonical_alias = "";
+ newInfo.name = temp.name;
+ newInfo.room_id = roomIdOrAlias;
+ newInfo.topic = temp.topic;
+ newInfo.num_joined_members = temp.member_count;
+ // newInfo.world_readable;
+ newInfo.guest_can_join = temp.guest_access;
+ newInfo.avatar_url = temp.avatar_url;
+
+ newInfo.join_rule = temp.join_rule;
+ newInfo.room_type = temp.is_space ? mtx::events::state::room_type::space : "";
+ newInfo.room_version = temp.version;
+ newInfo.membership = mtx::events::state::Membership::Join;
+ // newInfo.encryption;
+
+ this->room = std::move(newInfo);
+ loaded_ = true;
+ return;
+ }
+
+ // newInfo.encryption;
+ }
+
auto ctx = std::make_shared<RoomSummaryProxy>();
connect(ctx.get(), &RoomSummaryProxy::failed, this, [this]() {
@@ -52,3 +85,13 @@ RoomSummary::join()
else
ChatPage::instance()->joinRoomVia(roomIdOrAlias, vias, false, reason_);
}
+
+void
+RoomSummary::promptJoin()
+{
+ if (isKnockOnly())
+ ChatPage::instance()->knockRoom(
+ QString::fromStdString(roomIdOrAlias), vias, reason_, false, true);
+ else
+ ChatPage::instance()->joinRoomVia(roomIdOrAlias, vias, true, reason_);
+}
diff --git a/src/ui/RoomSummary.h b/src/ui/RoomSummary.h
index 416a4867..f5085054 100644
--- a/src/ui/RoomSummary.h
+++ b/src/ui/RoomSummary.h
@@ -69,6 +69,7 @@ public:
bool isLoaded() const { return room.has_value() || loaded_; }
Q_INVOKABLE void join();
+ Q_INVOKABLE void promptJoin();
signals:
void loaded();
|