diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index 4a9a565c..ab067eee 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -49,7 +49,7 @@ Rectangle {
smooth: true
sourceSize.width: avatar.width
sourceSize.height: avatar.height
- source: avatar.url ? (avatar.url + "?radius=" + (Settings.avatarCircles ? 100.0 : 25.0) + ((avatar.crop) ? "" : "&scale")) : ""
+ source: avatar.url ? (avatar.url + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale")) : ""
MouseArea {
id: mouseArea
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index b84b4c36..a0009174 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -16,12 +16,13 @@ Page {
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
property bool collapsed: false
-Component {
+ Component {
id: roomDirectoryComponent
RoomDirectory {
}
- }
+
+ }
ListView {
id: roomlist
@@ -570,10 +571,10 @@ Component {
ToolTip.visible: hovered
ToolTip.text: qsTr("Room directory")
Layout.margins: Nheko.paddingMedium
- onClicked: {
+ onClicked: {
var win = roomDirectoryComponent.createObject(timelineRoot);
win.show();
- }
+ }
}
ImageButton {
diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml
index 491a336f..92cd431a 100644
--- a/resources/qml/RoomSettings.qml
+++ b/resources/qml/RoomSettings.qml
@@ -186,7 +186,16 @@ ApplicationWindow {
ComboBox {
enabled: roomSettings.canChangeJoinRules
- model: [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")]
+ model: {
+ let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")];
+ if (roomSettings.supportsKnocking)
+ opts.push(qsTr("By knocking"));
+
+ if (roomSettings.supportsRestricted)
+ opts.push(qsTr("Restricted by membership in other rooms"));
+
+ return opts;
+ }
currentIndex: roomSettings.accessJoinRules
onActivated: {
roomSettings.changeAccessRules(index);
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index a8bdf183..893edc77 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -3,6 +3,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.2
import im.nheko 1.0
Item {
@@ -357,11 +359,23 @@ Item {
DelegateChoice {
roleValue: MtxEvent.Member
- NoticeMessage {
- body: formatted
- isOnlyEmoji: false
- isReply: d.isReply
- formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
+ ColumnLayout {
+ width: parent ? parent.width : undefined
+
+ NoticeMessage {
+ body: formatted
+ isOnlyEmoji: false
+ isReply: d.isReply
+ formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
+ }
+
+ Button {
+ visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
+ palette: Nheko.colors
+ text: qsTr("Allow them in")
+ onClicked: room.acceptKnock(eventId)
+ }
+
}
}
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 88d575fa..1e369b46 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -1689,6 +1689,19 @@ TimelineModel::formatJoinRuleEvent(QString id)
return tr("%1 opened the room to the public.").arg(name);
case mtx::events::state::JoinRule::Invite:
return tr("%1 made this room require and invitation to join.").arg(name);
+ case mtx::events::state::JoinRule::Knock:
+ return tr("%1 allowed to join this room by knocking.").arg(name);
+ case mtx::events::state::JoinRule::Restricted: {
+ QStringList rooms;
+ for (const auto &r : event->content.allow) {
+ if (r.type == mtx::events::state::JoinAllowanceType::RoomMembership)
+ rooms.push_back(QString::fromStdString(r.room_id));
+ }
+ return tr("%1 allowed members of the following rooms to automatically join this "
+ "room: %2")
+ .arg(name)
+ .arg(rooms.join(", "));
+ }
default:
// Currently, knock and private are reserved keywords and not implemented in Matrix.
return "";
@@ -1771,6 +1784,51 @@ TimelineModel::formatPowerLevelEvent(QString id)
return tr("%1 has changed the room's permissions.").arg(name);
}
+void
+TimelineModel::acceptKnock(QString id)
+{
+ mtx::events::collections::TimelineEvents *e = events.get(id.toStdString(), "");
+ if (!e)
+ return;
+
+ auto event = std::get_if<mtx::events::StateEvent<mtx::events::state::Member>>(e);
+ if (!event)
+ return;
+
+ if (!permissions_.canInvite())
+ return;
+
+ if (cache::isRoomMember(event->state_key, room_id_.toStdString()))
+ return;
+
+ using namespace mtx::events::state;
+ if (event->content.membership != Membership::Knock)
+ return;
+
+ ChatPage::instance()->inviteUser(QString::fromStdString(event->state_key), "");
+}
+
+bool
+TimelineModel::showAcceptKnockButton(QString id)
+{
+ mtx::events::collections::TimelineEvents *e = events.get(id.toStdString(), "");
+ if (!e)
+ return false;
+
+ auto event = std::get_if<mtx::events::StateEvent<mtx::events::state::Member>>(e);
+ if (!event)
+ return false;
+
+ if (!permissions_.canInvite())
+ return false;
+
+ if (cache::isRoomMember(event->state_key, room_id_.toStdString()))
+ return false;
+
+ using namespace mtx::events::state;
+ return event->content.membership == Membership::Knock;
+}
+
QString
TimelineModel::formatMemberEvent(QString id)
{
@@ -1826,7 +1884,13 @@ TimelineModel::formatMemberEvent(QString id)
// the case of nothing changed but join follows join shouldn't happen, so
// just show it as join
} else {
- rendered = tr("%1 joined.").arg(name);
+ if (event->content.join_authorised_via_users_server.empty())
+ rendered = tr("%1 joined.").arg(name);
+ else
+ rendered = tr("%1 joined via authorisation from %2's server.")
+ .arg(name)
+ .arg(QString::fromStdString(
+ event->content.join_authorised_via_users_server));
}
break;
case Membership::Leave:
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index aa07fe01..e3ca8811 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -238,6 +238,8 @@ public:
Q_INVOKABLE QString avatarUrl(QString id) const;
Q_INVOKABLE QString formatDateSeparator(QDate date) const;
Q_INVOKABLE QString formatTypingUsers(const std::vector<QString> &users, QColor bg);
+ Q_INVOKABLE bool showAcceptKnockButton(QString id);
+ Q_INVOKABLE void acceptKnock(QString id);
Q_INVOKABLE QString formatMemberEvent(QString id);
Q_INVOKABLE QString formatJoinRuleEvent(QString id);
Q_INVOKABLE QString formatHistoryVisibilityEvent(QString id);
diff --git a/src/ui/RoomSettings.cpp b/src/ui/RoomSettings.cpp
index fcba8205..2fb93325 100644
--- a/src/ui/RoomSettings.cpp
+++ b/src/ui/RoomSettings.cpp
@@ -218,8 +218,12 @@ RoomSettings::RoomSettings(QString roomid, QObject *parent)
} else {
accessRules_ = 1;
}
- } else {
+ } else if (info_.join_rule == state::JoinRule::Invite) {
accessRules_ = 2;
+ } else if (info_.join_rule == state::JoinRule::Knock) {
+ accessRules_ = 3;
+ } else if (info_.join_rule == state::JoinRule::Restricted) {
+ accessRules_ = 4;
}
emit accessJoinRulesChanged();
}
@@ -368,6 +372,21 @@ RoomSettings::isEncryptionEnabled() const
return usesEncryption_;
}
+bool
+RoomSettings::supportsKnocking() const
+{
+ return info_.version != "" && info_.version != "1" && info_.version != "2" &&
+ info_.version != "3" && info_.version != "4" && info_.version != "5" &&
+ info_.version != "6";
+}
+bool
+RoomSettings::supportsRestricted() const
+{
+ return info_.version != "" && info_.version != "1" && info_.version != "2" &&
+ info_.version != "3" && info_.version != "4" && info_.version != "5" &&
+ info_.version != "6" && info_.version != "7";
+}
+
void
RoomSettings::openEditModal()
{
@@ -464,6 +483,15 @@ RoomSettings::changeAccessRules(int index)
case 1:
event.join_rule = state::JoinRule::Public;
break;
+ case 2:
+ event.join_rule = state::JoinRule::Invite;
+ break;
+ case 3:
+ event.join_rule = state::JoinRule::Knock;
+ break;
+ case 4:
+ event.join_rule = state::JoinRule::Restricted;
+ break;
default:
event.join_rule = state::JoinRule::Invite;
}
diff --git a/src/ui/RoomSettings.h b/src/ui/RoomSettings.h
index 1c8b47d6..ab768ffe 100644
--- a/src/ui/RoomSettings.h
+++ b/src/ui/RoomSettings.h
@@ -78,6 +78,8 @@ class RoomSettings : public QObject
Q_PROPERTY(bool canChangeJoinRules READ canChangeJoinRules CONSTANT)
Q_PROPERTY(bool canChangeNameAndTopic READ canChangeNameAndTopic CONSTANT)
Q_PROPERTY(bool isEncryptionEnabled READ isEncryptionEnabled NOTIFY encryptionChanged)
+ Q_PROPERTY(bool supportsKnocking READ supportsKnocking CONSTANT)
+ Q_PROPERTY(bool supportsRestricted READ supportsRestricted CONSTANT)
public:
RoomSettings(QString roomid, QObject *parent = nullptr);
@@ -98,6 +100,8 @@ public:
//! Whether the user has enough power level to send m.room.avatar event.
bool canChangeAvatar() const;
bool isEncryptionEnabled() const;
+ bool supportsKnocking() const;
+ bool supportsRestricted() const;
Q_INVOKABLE void enableEncryption();
Q_INVOKABLE void updateAvatar();
|