Show rooms you share with someone

This commit is contained in:
Nicolas Werner 2023-02-24 02:40:14 +01:00
parent d46a67f64b
commit aae3300860
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
8 changed files with 320 additions and 139 deletions

View file

@ -0,0 +1,25 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import im.nheko 1.0
TabButton {
id: control
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? Nheko.colors.highlightedText : Nheko.colors.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
border.color: control.down ? Nheko.colors.highlight : Nheko.theme.separator
color: control.checked ? Nheko.colors.highlight : Nheko.colors.base
border.width: 1
radius: 2
}
}

View file

@ -49,30 +49,10 @@ ApplicationWindow {
width: parent.width width: parent.width
palette: Nheko.colors palette: Nheko.colors
component TabB : TabButton { NhekoTabButton {
id: control
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? Nheko.colors.highlightedText : Nheko.colors.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
border.color: control.down ? Nheko.colors.highlight : Nheko.theme.separator
color: control.checked ? Nheko.colors.highlight : Nheko.colors.base
border.width: 1
radius: 2
}
}
TabB {
text: qsTr("Roles") text: qsTr("Roles")
} }
TabB { NhekoTabButton {
text: qsTr("Users") text: qsTr("Users")
} }
} }

View file

@ -5,10 +5,12 @@
import ".." import ".."
import "../device-verification" import "../device-verification"
import "../ui" import "../ui"
import "../components"
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtQuick.Window 2.13 import QtQuick.Window 2.13
import QtQml.Models 2.2
import im.nheko 1.0 import im.nheko 1.0
ApplicationWindow { ApplicationWindow {
@ -34,12 +36,13 @@ ApplicationWindow {
ListView { ListView {
id: devicelist id: devicelist
property int selectedTab: 0
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
clip: true clip: true
spacing: 8 spacing: 8
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
model: profile.deviceList
anchors.fill: parent anchors.fill: parent
anchors.margins: 10 anchors.margins: 10
footerPositioning: ListView.OverlayFooter footerPositioning: ListView.OverlayFooter
@ -297,8 +300,31 @@ ApplicationWindow {
} }
TabBar {
id: tabbar
visible: !profile.isSelf
Layout.fillWidth: true
onCurrentIndexChanged: devicelist.selectedTab = currentIndex
palette: Nheko.colors
NhekoTabButton {
text: qsTr("Devices")
}
NhekoTabButton {
text: qsTr("Shared Rooms")
} }
Layout.bottomMargin: Nheko.paddingMedium
}
}
model: (selectedTab == 0) ? devicesModel : sharedRoomsModel
DelegateModel {
id: devicesModel
model: profile.deviceList
delegate: RowLayout { delegate: RowLayout {
required property int verificationStatus required property int verificationStatus
required property string deviceId required property string deviceId
@ -439,6 +465,50 @@ ApplicationWindow {
} }
} }
}
DelegateModel {
id: sharedRoomsModel
model: profile.sharedRooms
delegate: RowLayout {
required property string roomId
required property string roomName
required property string avatarUrl
width: devicelist.width
spacing: 4
Avatar {
id: avatar
enabled: false
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Nheko.paddingMedium
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
height: avatarSize
width: avatarSize
url: avatarUrl.replace("mxc://", "image://MxcImage/")
roomid: roomId
displayName: roomName
}
ElidedLabel {
Layout.alignment: Qt.AlignVCenter
color: Nheko.colors.text
Layout.fillWidth: true
elideWidth: width
fullText: roomName
textFormat: Text.PlainText
Layout.rightMargin: Nheko.paddingMedium
}
Item {
Layout.fillWidth: true
}
}
}
footer: DialogButtonBox { footer: DialogButtonBox {
z: 2 z: 2

View file

@ -129,6 +129,7 @@
<file>qml/components/AvatarListTile.qml</file> <file>qml/components/AvatarListTile.qml</file>
<file>qml/components/FlatButton.qml</file> <file>qml/components/FlatButton.qml</file>
<file>qml/components/MainWindowDialog.qml</file> <file>qml/components/MainWindowDialog.qml</file>
<file>qml/components/NhekoTabButton.qml</file>
<file>qml/components/NotificationBubble.qml</file> <file>qml/components/NotificationBubble.qml</file>
<file>qml/components/ReorderableListview.qml</file> <file>qml/components/ReorderableListview.qml</file>
<file>qml/components/SpaceMenuLevel.qml</file> <file>qml/components/SpaceMenuLevel.qml</file>

View file

@ -3146,6 +3146,36 @@ Cache::joinedRooms()
return room_ids; return room_ids;
} }
std::map<std::string, RoomInfo>
Cache::getCommonRooms(const std::string &user_id)
{
std::map<std::string, RoomInfo> result;
auto txn = ro_txn(env_);
std::string_view room_id;
std::string_view room_data;
std::string_view member_info;
auto roomsCursor = lmdb::cursor::open(txn, roomsDb_);
while (roomsCursor.get(room_id, room_data, MDB_NEXT)) {
try {
if (getMembersDb(txn, std::string(room_id)).get(txn, user_id, member_info)) {
RoomInfo tmp = nlohmann::json::parse(std::move(room_data)).get<RoomInfo>();
result.emplace(std::string(room_id), std::move(tmp));
}
} catch (std::exception &e) {
nhlog::db()->warn("Failed to read common room for member ({}) in room ({}): {}",
user_id,
room_id,
e.what());
}
}
roomsCursor.close();
return result;
}
std::optional<MemberInfo> std::optional<MemberInfo>
Cache::getMember(const std::string &room_id, const std::string &user_id) Cache::getMember(const std::string &room_id, const std::string &user_id)
{ {

View file

@ -64,6 +64,7 @@ public:
crypto::Trust roomVerificationStatus(const std::string &room_id); crypto::Trust roomVerificationStatus(const std::string &room_id);
std::vector<std::string> joinedRooms(); std::vector<std::string> joinedRooms();
std::map<std::string, RoomInfo> getCommonRooms(const std::string &user_id);
QMap<QString, RoomInfo> roomInfo(bool withInvites = true); QMap<QString, RoomInfo> roomInfo(bool withInvites = true);
QHash<QString, RoomInfo> invites(); QHash<QString, RoomInfo> invites();

View file

@ -58,6 +58,12 @@ UserProfile::UserProfile(const QString &roomid,
emit verificationStatiChanged(); emit verificationStatiChanged();
}); });
fetchDeviceList(this->userid_); fetchDeviceList(this->userid_);
if (userid != utils::localUser())
sharedRooms_ =
new RoomInfoModel(cache::client()->getCommonRooms(userid.toStdString()), this);
else
sharedRooms_ = new RoomInfoModel({}, this);
} }
QHash<int, QByteArray> QHash<int, QByteArray>
@ -102,12 +108,53 @@ DeviceInfoModel::reset(const std::vector<DeviceInfo> &deviceList)
endResetModel(); endResetModel();
} }
RoomInfoModel::RoomInfoModel(const std::map<std::string, RoomInfo> &raw, QObject *parent)
: QAbstractListModel(parent)
{
for (const auto &e : raw)
roomInfos_.push_back(e);
}
QHash<int, QByteArray>
RoomInfoModel::roleNames() const
{
return {
{RoomId, "roomId"},
{RoomName, "roomName"},
{AvatarUrl, "avatarUrl"},
};
}
QVariant
RoomInfoModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= (int)roomInfos_.size() || index.row() < 0)
return {};
switch (role) {
case RoomId:
return QString::fromStdString(roomInfos_[index.row()].first);
case RoomName:
return QString::fromStdString(roomInfos_[index.row()].second.name);
case AvatarUrl:
return QString::fromStdString(roomInfos_[index.row()].second.avatar_url);
default:
return {};
}
}
DeviceInfoModel * DeviceInfoModel *
UserProfile::deviceList() UserProfile::deviceList()
{ {
return &this->deviceList_; return &this->deviceList_;
} }
RoomInfoModel *
UserProfile::sharedRooms()
{
return this->sharedRooms_;
}
QString QString
UserProfile::userid() UserProfile::userid()
{ {

View file

@ -119,6 +119,30 @@ private:
friend class UserProfile; friend class UserProfile;
}; };
class RoomInfoModel final : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles
{
RoomId,
RoomName,
AvatarUrl,
};
explicit RoomInfoModel(const std::map<std::string, RoomInfo> &, QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
(void)parent;
return (int)roomInfos_.size();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
std::vector<std::pair<std::string, RoomInfo>> roomInfos_;
};
class UserProfile final : public QObject class UserProfile final : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -126,6 +150,7 @@ class UserProfile final : public QObject
Q_PROPERTY(QString userid READ userid CONSTANT) Q_PROPERTY(QString userid READ userid CONSTANT)
Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged)
Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList NOTIFY devicesChanged) Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList NOTIFY devicesChanged)
Q_PROPERTY(RoomInfoModel *sharedRooms READ sharedRooms CONSTANT)
Q_PROPERTY(bool isGlobalUserProfile READ isGlobalUserProfile CONSTANT) Q_PROPERTY(bool isGlobalUserProfile READ isGlobalUserProfile CONSTANT)
Q_PROPERTY(int userVerified READ getUserStatus NOTIFY userStatusChanged) Q_PROPERTY(int userVerified READ getUserStatus NOTIFY userStatusChanged)
Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged) Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged)
@ -139,6 +164,7 @@ public:
TimelineModel *parent = nullptr); TimelineModel *parent = nullptr);
DeviceInfoModel *deviceList(); DeviceInfoModel *deviceList();
RoomInfoModel *sharedRooms();
QString userid(); QString userid();
QString displayName(); QString displayName();
@ -198,4 +224,5 @@ private:
bool isLoading_ = false; bool isLoading_ = false;
TimelineViewManager *manager; TimelineViewManager *manager;
TimelineModel *model; TimelineModel *model;
RoomInfoModel *sharedRooms_ = nullptr;
}; };