Merge pull request #416 from LorenDB/jdenticon

Add jdenticon support
This commit is contained in:
DeepBlueV7.X 2021-09-13 21:31:29 +00:00 committed by GitHub
commit 8e2f1e9dce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 399 additions and 52 deletions

View File

@ -345,6 +345,7 @@ set(SRC_FILES
src/DeviceVerificationFlow.cpp src/DeviceVerificationFlow.cpp
src/EventAccessors.cpp src/EventAccessors.cpp
src/InviteesModel.cpp src/InviteesModel.cpp
src/JdenticonProvider.cpp
src/Logging.cpp src/Logging.cpp
src/LoginPage.cpp src/LoginPage.cpp
src/MainWindow.cpp src/MainWindow.cpp
@ -557,6 +558,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/DeviceVerificationFlow.h src/DeviceVerificationFlow.h
src/ImagePackListModel.h src/ImagePackListModel.h
src/InviteesModel.h src/InviteesModel.h
src/JdenticonProvider.h
src/LoginPage.h src/LoginPage.h
src/MainWindow.h src/MainWindow.h
src/MemberList.h src/MemberList.h

View File

@ -12,6 +12,7 @@ Rectangle {
property string url property string url
property string userid property string userid
property string roomid
property string displayName property string displayName
property alias textColor: label.color property alias textColor: label.color
property bool crop: true property bool crop: true
@ -35,10 +36,28 @@ Rectangle {
font.pixelSize: avatar.height / 2 font.pixelSize: avatar.height / 2
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
visible: img.status != Image.Ready visible: img.status != Image.Ready && !Settings.useIdenticon
color: Nheko.colors.text color: Nheko.colors.text
} }
Image {
id: identicon
anchors.fill: parent
visible: Settings.useIdenticon && img.status != Image.Ready
source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
MouseArea {
anchors.fill: parent
Ripple {
rippleTarget: parent
color: Qt.rgba(Nheko.colors.alternateBase.r, Nheko.colors.alternateBase.g, Nheko.colors.alternateBase.b, 0.5)
}
}
}
Image { Image {
id: img id: img

View File

@ -130,6 +130,7 @@ Page {
else else
return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText; return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText;
} }
roomid: model.id
displayName: model.displayName displayName: model.displayName
color: communityItem.background color: communityItem.background
} }

View File

@ -139,6 +139,7 @@ Popup {
height: popup.avatarHeight height: popup.avatarHeight
width: popup.avatarWidth width: popup.avatarWidth
displayName: model.displayName displayName: model.displayName
userid: model.userid
url: model.avatarUrl.replace("mxc://", "image://MxcImage/") url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
onClicked: popup.completionClicked(completer.completionAt(model.index)) onClicked: popup.completionClicked(completer.completionAt(model.index))
} }
@ -194,6 +195,7 @@ Popup {
height: popup.avatarHeight height: popup.avatarHeight
width: popup.avatarWidth width: popup.avatarWidth
displayName: model.roomName displayName: model.roomName
roomid: model.roomid
url: model.avatarUrl.replace("mxc://", "image://MxcImage/") url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
onClicked: { onClicked: {
popup.completionClicked(completer.completionAt(model.index)); popup.completionClicked(completer.completionAt(model.index));
@ -225,6 +227,7 @@ Popup {
height: popup.avatarHeight height: popup.avatarHeight
width: popup.avatarWidth width: popup.avatarWidth
displayName: model.roomName displayName: model.roomName
roomid: model.roomid
url: model.avatarUrl.replace("mxc://", "image://MxcImage/") url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
onClicked: popup.completionClicked(completer.completionAt(model.index)) onClicked: popup.completionClicked(completer.completionAt(model.index))
} }

View File

@ -65,6 +65,7 @@ ApplicationWindow {
width: avatarSize width: avatarSize
height: avatarSize height: avatarSize
url: model.avatarUrl.replace("mxc://", "image://MxcImage/") url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
roomid: model.roomid
displayName: model.name displayName: model.name
} }

View File

@ -143,6 +143,8 @@ Page {
required property int notificationCount required property int notificationCount
required property bool hasLoudNotification required property bool hasLoudNotification
required property bool hasUnreadMessages required property bool hasUnreadMessages
required property bool isDirect
required property string directChatOtherUserId
color: background color: background
height: avatarSize + 2 * Nheko.paddingMedium height: avatarSize + 2 * Nheko.paddingMedium
@ -237,6 +239,8 @@ Page {
width: avatarSize width: avatarSize
url: avatarUrl.replace("mxc://", "image://MxcImage/") url: avatarUrl.replace("mxc://", "image://MxcImage/")
displayName: roomName displayName: roomName
userid: isDirect ? directChatOtherUserId : ""
roomid: roomId
Rectangle { Rectangle {
id: collapsedNotificationBubble id: collapsedNotificationBubble

View File

@ -39,6 +39,7 @@ ApplicationWindow {
width: 130 width: 130
height: width height: width
roomid: members.roomId
displayName: members.roomName displayName: members.roomName
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
url: members.avatarUrl.replace("mxc://", "image://MxcImage/") url: members.avatarUrl.replace("mxc://", "image://MxcImage/")

View File

@ -38,6 +38,7 @@ ApplicationWindow {
Avatar { Avatar {
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/") url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
roomid: roomSettings.roomid
displayName: roomSettings.roomName displayName: roomSettings.roomName
height: 130 height: 130
width: 130 width: 130

View File

@ -137,6 +137,7 @@ Item {
ColumnLayout { ColumnLayout {
id: preview id: preview
property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomId : "")
property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "") property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "") property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "")
property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "") property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "")
@ -153,6 +154,7 @@ Item {
Avatar { Avatar {
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/") url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
roomid: parent.roomId
displayName: parent.roomName displayName: parent.roomName
height: 130 height: 130
width: 130 width: 130

View File

@ -13,10 +13,13 @@ Rectangle {
property bool showBackButton: false property bool showBackButton: false
property string roomName: room ? room.roomName : qsTr("No room selected") property string roomName: room ? room.roomName : qsTr("No room selected")
property string roomId: room ? room.roomId : ""
property string avatarUrl: room ? room.roomAvatarUrl : "" property string avatarUrl: room ? room.roomAvatarUrl : ""
property string roomTopic: room ? room.roomTopic : "" property string roomTopic: room ? room.roomTopic : ""
property bool isEncrypted: room ? room.isEncrypted : false property bool isEncrypted: room ? room.isEncrypted : false
property int trustlevel: room ? room.trustlevel : Crypto.Unverified property int trustlevel: room ? room.trustlevel : Crypto.Unverified
property bool isDirect: room ? room.isDirect : false
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: topLayout.height + Nheko.paddingMedium * 2 implicitHeight: topLayout.height + Nheko.paddingMedium * 2
@ -65,10 +68,12 @@ Rectangle {
width: Nheko.avatarSize width: Nheko.avatarSize
height: Nheko.avatarSize height: Nheko.avatarSize
url: avatarUrl.replace("mxc://", "image://MxcImage/") url: avatarUrl.replace("mxc://", "image://MxcImage/")
roomid: roomId
userid: isDirect ? directChatOtherUserId : ""
displayName: roomName displayName: roomName
onClicked: { onClicked: {
if (room) if (room)
TimelineManager.openRoomSettings(room.roomId); TimelineManager.openRoomSettings(roomId);
} }
} }
@ -135,7 +140,7 @@ Rectangle {
Platform.MenuItem { Platform.MenuItem {
visible: room ? room.permissions.canInvite() : false visible: room ? room.permissions.canInvite() : false
text: qsTr("Invite users") text: qsTr("Invite users")
onTriggered: TimelineManager.openInviteUsers(room.roomId) onTriggered: TimelineManager.openInviteUsers(roomId)
} }
Platform.MenuItem { Platform.MenuItem {
@ -145,12 +150,12 @@ Rectangle {
Platform.MenuItem { Platform.MenuItem {
text: qsTr("Leave room") text: qsTr("Leave room")
onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId) onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
} }
Platform.MenuItem { Platform.MenuItem {
text: qsTr("Settings") text: qsTr("Settings")
onTriggered: TimelineManager.openRoomSettings(room.roomId) onTriggered: TimelineManager.openRoomSettings(roomId)
} }
} }

View File

@ -23,6 +23,8 @@ Rectangle {
required property int index required property int index
required property int selectedIndex required property int selectedIndex
property bool crop: true property bool crop: true
property alias roomid: avatar.roomid
property alias userid: avatar.userid
color: background color: background
height: avatarSize + 2 * Nheko.paddingMedium height: avatarSize + 2 * Nheko.paddingMedium

View File

@ -61,6 +61,7 @@ ApplicationWindow {
header: AvatarListTile { header: AvatarListTile {
title: imagePack.packname title: imagePack.packname
avatarUrl: imagePack.avatarUrl avatarUrl: imagePack.avatarUrl
roomid: imagePack.statekey
subtitle: imagePack.statekey subtitle: imagePack.statekey
index: -1 index: -1
selectedIndex: currentImageIndex selectedIndex: currentImageIndex
@ -142,6 +143,7 @@ ApplicationWindow {
Layout.columnSpan: 2 Layout.columnSpan: 2
url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/") url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
displayName: imagePack.packname displayName: imagePack.packname
roomid: imagePack.statekey
height: 130 height: 130
width: 130 width: 130
crop: false crop: false
@ -219,6 +221,7 @@ ApplicationWindow {
Layout.columnSpan: 2 Layout.columnSpan: 2
url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/")
displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode) displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
roomid: displayName
height: 130 height: 130
width: 130 width: 130
crop: false crop: false

View File

@ -112,6 +112,7 @@ ApplicationWindow {
return qsTr("Globally enabled pack"); return qsTr("Globally enabled pack");
} }
selectedIndex: currentPackIndex selectedIndex: currentPackIndex
roomid: currentPack.statekey
TapHandler { TapHandler {
onSingleTapped: currentPackIndex = index onSingleTapped: currentPackIndex = index
@ -135,6 +136,7 @@ ApplicationWindow {
property string packName: currentPack ? currentPack.packname : "" property string packName: currentPack ? currentPack.packname : ""
property string attribution: currentPack ? currentPack.attribution : "" property string attribution: currentPack ? currentPack.attribution : ""
property string avatarUrl: currentPack ? currentPack.avatarUrl : "" property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
property string statekey: currentPack ? currentPack.statekey : ""
anchors.fill: parent anchors.fill: parent
anchors.margins: Nheko.paddingLarge anchors.margins: Nheko.paddingLarge
@ -143,6 +145,7 @@ ApplicationWindow {
Avatar { Avatar {
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/") url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
displayName: packinfo.packName displayName: packinfo.packName
roomid: packinfo.statekey
height: 100 height: 100
width: 100 width: 100
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter

View File

@ -34,14 +34,15 @@ Rectangle {
width: Nheko.avatarSize width: Nheko.avatarSize
height: Nheko.avatarSize height: Nheko.avatarSize
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: CallManager.callParty userid: CallManager.callParty
displayName: CallManager.callPartyDisplayName
onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId)
} }
Label { Label {
Layout.leftMargin: 8 Layout.leftMargin: 8
font.pointSize: fontMetrics.font.pointSize * 1.1 font.pointSize: fontMetrics.font.pointSize * 1.1
text: CallManager.callParty text: CallManager.callPartyDisplayName
color: "#000000" color: "#000000"
} }

View File

@ -40,7 +40,7 @@ Popup {
Label { Label {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.topMargin: msgView.height / 25 Layout.topMargin: msgView.height / 25
text: CallManager.callParty text: CallManager.callPartyDisplayName
font.pointSize: fontMetrics.font.pointSize * 2 font.pointSize: fontMetrics.font.pointSize * 2
color: Nheko.colors.windowText color: Nheko.colors.windowText
} }
@ -50,7 +50,8 @@ Popup {
width: msgView.height / 5 width: msgView.height / 5
height: msgView.height / 5 height: msgView.height / 5
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: CallManager.callParty userid: CallManager.callParty
displayName: CallManager.callPartyDisplayName
} }
ColumnLayout { ColumnLayout {

View File

@ -41,14 +41,15 @@ Rectangle {
width: Nheko.avatarSize width: Nheko.avatarSize
height: Nheko.avatarSize height: Nheko.avatarSize
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: CallManager.callParty userid: CallManager.callParty
displayName: CallManager.callPartyDisplayName
onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId)
} }
Label { Label {
Layout.leftMargin: 8 Layout.leftMargin: 8
font.pointSize: fontMetrics.font.pointSize * 1.1 font.pointSize: fontMetrics.font.pointSize * 1.1
text: CallManager.callParty text: CallManager.callPartyDisplayName
color: "#000000" color: "#000000"
} }

View File

@ -79,6 +79,7 @@ Popup {
height: Nheko.avatarSize height: Nheko.avatarSize
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: room.roomName displayName: room.roomName
roomid: room.roomid
onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId)
} }

View File

@ -2776,6 +2776,46 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_
return members; return members;
} }
std::vector<RoomMember>
Cache::getMembersFromInvite(const std::string &room_id, std::size_t startIndex, std::size_t len)
{
auto txn = ro_txn(env_);
auto db = getInviteMembersDb(txn, room_id);
auto cursor = lmdb::cursor::open(txn, db);
std::size_t currentIndex = 0;
const auto endIndex = std::min(startIndex + len, db.size(txn));
std::vector<RoomMember> members;
std::string_view user_id, user_data;
while (cursor.get(user_id, user_data, MDB_NEXT)) {
if (currentIndex < startIndex) {
currentIndex += 1;
continue;
}
if (currentIndex >= endIndex)
break;
try {
MemberInfo tmp = json::parse(user_data);
members.emplace_back(
RoomMember{QString::fromStdString(std::string(user_id)),
QString::fromStdString(tmp.name)});
} catch (const json::exception &e) {
nhlog::db()->warn("{}", e.what());
}
currentIndex += 1;
}
cursor.close();
return members;
}
bool bool
Cache::isRoomMember(const std::string &user_id, const std::string &room_id) Cache::isRoomMember(const std::string &user_id, const std::string &room_id)
{ {
@ -4808,6 +4848,12 @@ getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len)
return instance_->getMembers(room_id, startIndex, len); return instance_->getMembers(room_id, startIndex, len);
} }
std::vector<RoomMember>
getMembersFromInvite(const std::string &room_id, std::size_t startIndex, std::size_t len)
{
return instance_->getMembersFromInvite(room_id, startIndex, len);
}
void void
saveState(const mtx::responses::Sync &res) saveState(const mtx::responses::Sync &res)
{ {

View File

@ -83,6 +83,9 @@ getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
//! Retrieve member info from a room. //! Retrieve member info from a room.
std::vector<RoomMember> std::vector<RoomMember>
getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30);
//! Retrive member info from an invite.
std::vector<RoomMember>
getMembersFromInvite(const std::string &room_id, std::size_t start_index = 0, std::size_t len = 30);
bool bool
isInitialized(); isInitialized();

View File

@ -93,7 +93,7 @@ to_json(nlohmann::json &j, const RoomInfo &info);
void void
from_json(const nlohmann::json &j, RoomInfo &info); from_json(const nlohmann::json &j, RoomInfo &info);
//! Basic information per member; //! Basic information per member.
struct MemberInfo struct MemberInfo
{ {
std::string name; std::string name;

View File

@ -106,6 +106,10 @@ public:
std::vector<RoomMember> getMembers(const std::string &room_id, std::vector<RoomMember> getMembers(const std::string &room_id,
std::size_t startIndex = 0, std::size_t startIndex = 0,
std::size_t len = 30); std::size_t len = 30);
std::vector<RoomMember> getMembersFromInvite(const std::string &room_id,
std::size_t startIndex = 0,
std::size_t len = 30);
size_t memberCount(const std::string &room_id); size_t memberCount(const std::string &room_id);
void saveState(const mtx::responses::Sync &res); void saveState(const mtx::responses::Sync &res);
@ -132,6 +136,9 @@ public:
//! Retrieve all the user ids from a room. //! Retrieve all the user ids from a room.
std::vector<std::string> roomMembers(const std::string &room_id); std::vector<std::string> roomMembers(const std::string &room_id);
//! Get the other user from an invite to a direct chat.
RoomMember getDirectInviteMember(const std::string &room_id);
//! Check if the given user has power leve greater than than //! Check if the given user has power leve greater than than
//! lowest power level of the given events. //! lowest power level of the given events.
bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes, bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
@ -310,7 +317,6 @@ public:
return get_skey(a).compare(get_skey(b)); return get_skey(a).compare(get_skey(b));
} }
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); void roomReadStatus(const std::map<QString, bool> &status);

View File

@ -206,7 +206,9 @@ CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int w
std::vector<RoomMember> members(cache::getMembers(roomid.toStdString())); std::vector<RoomMember> members(cache::getMembers(roomid.toStdString()));
const RoomMember &callee = const RoomMember &callee =
members.front().user_id == utils::localUser() ? members.back() : members.front(); members.front().user_id == utils::localUser() ? members.back() : members.front();
callParty_ = callee.display_name.isEmpty() ? callee.user_id : callee.display_name; callParty_ = callee.user_id;
callPartyDisplayName_ =
callee.display_name.isEmpty() ? callee.user_id : callee.display_name;
callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url);
emit newInviteState(); emit newInviteState();
playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true); playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true);
@ -308,7 +310,9 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent)
std::vector<RoomMember> members(cache::getMembers(callInviteEvent.room_id)); std::vector<RoomMember> members(cache::getMembers(callInviteEvent.room_id));
const RoomMember &caller = const RoomMember &caller =
members.front().user_id == utils::localUser() ? members.back() : members.front(); members.front().user_id == utils::localUser() ? members.back() : members.front();
callParty_ = caller.display_name.isEmpty() ? caller.user_id : caller.display_name; callParty_ = caller.user_id;
callPartyDisplayName_ =
caller.display_name.isEmpty() ? caller.user_id : caller.display_name;
callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url);
haveCallInvite_ = true; haveCallInvite_ = true;
@ -459,6 +463,7 @@ CallManager::clear()
{ {
roomid_.clear(); roomid_.clear();
callParty_.clear(); callParty_.clear();
callPartyDisplayName_.clear();
callPartyAvatarUrl_.clear(); callPartyAvatarUrl_.clear();
callid_.clear(); callid_.clear();
callType_ = CallType::VOICE; callType_ = CallType::VOICE;

View File

@ -32,6 +32,7 @@ class CallManager : public QObject
Q_PROPERTY(webrtc::CallType callType READ callType NOTIFY newInviteState) Q_PROPERTY(webrtc::CallType callType READ callType NOTIFY newInviteState)
Q_PROPERTY(webrtc::State callState READ callState NOTIFY newCallState) Q_PROPERTY(webrtc::State callState READ callState NOTIFY newCallState)
Q_PROPERTY(QString callParty READ callParty NOTIFY newInviteState) Q_PROPERTY(QString callParty READ callParty NOTIFY newInviteState)
Q_PROPERTY(QString callPartyDisplayName READ callPartyDisplayName NOTIFY newInviteState)
Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY newInviteState) Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY newInviteState)
Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged) Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged)
Q_PROPERTY(bool haveLocalPiP READ haveLocalPiP NOTIFY newCallState) Q_PROPERTY(bool haveLocalPiP READ haveLocalPiP NOTIFY newCallState)
@ -48,6 +49,7 @@ public:
webrtc::CallType callType() const { return callType_; } webrtc::CallType callType() const { return callType_; }
webrtc::State callState() const { return session_.state(); } webrtc::State callState() const { return session_.state(); }
QString callParty() const { return callParty_; } QString callParty() const { return callParty_; }
QString callPartyDisplayName() const { return callPartyDisplayName_; }
QString callPartyAvatarUrl() const { return callPartyAvatarUrl_; } QString callPartyAvatarUrl() const { return callPartyAvatarUrl_; }
bool isMicMuted() const { return session_.isMicMuted(); } bool isMicMuted() const { return session_.isMicMuted(); }
bool haveLocalPiP() const { return session_.haveLocalPiP(); } bool haveLocalPiP() const { return session_.haveLocalPiP(); }
@ -87,6 +89,7 @@ private:
WebRTCSession &session_; WebRTCSession &session_;
QString roomid_; QString roomid_;
QString callParty_; QString callParty_;
QString callPartyDisplayName_;
QString callPartyAvatarUrl_; QString callPartyAvatarUrl_;
std::string callid_; std::string callid_;
const uint32_t timeoutms_ = 120000; const uint32_t timeoutms_ = 120000;

107
src/JdenticonProvider.cpp Normal file
View File

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "JdenticonProvider.h"
#include <QApplication>
#include <QDir>
#include <QPainter>
#include <QPainterPath>
#include <QPluginLoader>
#include <QSvgRenderer>
#include <mtxclient/crypto/client.hpp>
#include "Cache.h"
#include "Logging.h"
#include "MatrixClient.h"
#include "Utils.h"
#include "jdenticoninterface.h"
static QPixmap
clipRadius(QPixmap img, double radius)
{
QPixmap out(img.size());
out.fill(Qt::transparent);
QPainter painter(&out);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
QPainterPath ppath;
ppath.addRoundedRect(img.rect(), radius, radius, Qt::SizeMode::RelativeSize);
painter.setClipPath(ppath);
painter.drawPixmap(img.rect(), img);
return out;
}
JdenticonResponse::JdenticonResponse(const QString &key,
bool crop,
double radius,
const QSize &requestedSize)
: m_key(key)
, m_crop{crop}
, m_radius{radius}
, m_requestedSize(requestedSize.isValid() ? requestedSize : QSize(100, 100))
, m_pixmap{m_requestedSize}
, jdenticonInterface_{Jdenticon::getJdenticonInterface()}
{
setAutoDelete(false);
}
void
JdenticonResponse::run()
{
m_pixmap.fill(Qt::transparent);
QPainter painter;
painter.begin(&m_pixmap);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
QSvgRenderer renderer{
jdenticonInterface_->generate(m_key, m_requestedSize.width()).toUtf8()};
renderer.render(&painter);
painter.end();
m_pixmap = clipRadius(m_pixmap, m_radius);
emit finished();
}
namespace Jdenticon {
JdenticonInterface *
getJdenticonInterface()
{
static JdenticonInterface *interface = nullptr;
static bool interfaceExists{true};
if (interface == nullptr && interfaceExists) {
QDir pluginsDir(qApp->applicationDirPath());
bool plugins = pluginsDir.cd("plugins");
if (plugins) {
for (const QString &fileName : pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if (plugin) {
interface = qobject_cast<JdenticonInterface *>(plugin);
if (interface) {
nhlog::ui()->info("Loaded jdenticon plugin.");
break;
}
}
}
} else {
nhlog::ui()->info("jdenticon plugin not found.");
interfaceExists = false;
}
}
return interface;
}
}

81
src/JdenticonProvider.h Normal file
View File

@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QImage>
#include <QQuickAsyncImageProvider>
#include <QQuickImageResponse>
#include <QThreadPool>
#include <mtx/common.hpp>
#include "jdenticoninterface.h"
namespace Jdenticon {
JdenticonInterface *
getJdenticonInterface();
}
class JdenticonResponse
: public QQuickImageResponse
, public QRunnable
{
public:
JdenticonResponse(const QString &key, bool crop, double radius, const QSize &requestedSize);
QQuickTextureFactory *textureFactory() const override
{
return QQuickTextureFactory::textureFactoryForImage(m_pixmap.toImage());
}
void run() override;
QString m_key;
bool m_crop;
double m_radius;
QSize m_requestedSize;
QPixmap m_pixmap;
JdenticonInterface *jdenticonInterface_ = nullptr;
};
class JdenticonProvider
: public QObject
, public QQuickAsyncImageProvider
{
Q_OBJECT
public:
static bool isAvailable() { return Jdenticon::getJdenticonInterface() != nullptr; }
public slots:
QQuickImageResponse *requestImageResponse(const QString &id,
const QSize &requestedSize) override
{
auto id_ = id;
bool crop = true;
double radius = 0;
auto queryStart = id.lastIndexOf('?');
if (queryStart != -1) {
id_ = id.left(queryStart);
auto query = id.midRef(queryStart + 1);
auto queryBits = query.split('&');
for (auto b : queryBits) {
if (b.startsWith("radius=")) {
radius = b.mid(7).toDouble();
}
}
}
JdenticonResponse *response =
new JdenticonResponse(id_, crop, radius, requestedSize);
pool.start(response);
return response;
}
private:
QThreadPool pool;
};

View File

@ -16,6 +16,7 @@
#include "Cache_p.h" #include "Cache_p.h"
#include "ChatPage.h" #include "ChatPage.h"
#include "Config.h" #include "Config.h"
#include "JdenticonProvider.h"
#include "Logging.h" #include "Logging.h"
#include "LoginPage.h" #include "LoginPage.h"
#include "MainWindow.h" #include "MainWindow.h"
@ -152,10 +153,6 @@ MainWindow::MainWindow(QWidget *parent)
showChatPage(); showChatPage();
} }
}); });
if (loadJdenticonPlugin()) {
nhlog::ui()->info("loaded jdenticon.");
}
} }
void void
@ -428,29 +425,6 @@ MainWindow::showDialog(QWidget *dialog)
dialog->show(); dialog->show();
} }
bool
MainWindow::loadJdenticonPlugin()
{
QDir pluginsDir(qApp->applicationDirPath());
bool plugins = pluginsDir.cd("plugins");
if (plugins) {
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if (plugin) {
jdenticonInteface_ = qobject_cast<JdenticonInterface *>(plugin);
if (jdenticonInteface_) {
nhlog::ui()->info("Found jdenticon plugin.");
return true;
}
}
}
}
nhlog::ui()->info("jdenticon plugin not found.");
return false;
}
void void
MainWindow::showWelcomePage() MainWindow::showWelcomePage()
{ {

View File

@ -106,8 +106,6 @@ signals:
void reload(); void reload();
private: private:
bool loadJdenticonPlugin();
void showDialog(QWidget *dialog); void showDialog(QWidget *dialog);
bool hasActiveUser(); bool hasActiveUser();
void restoreWindowSize(); void restoreWindowSize();
@ -137,6 +135,4 @@ private:
//! Overlay modal used to project other widgets. //! Overlay modal used to project other widgets.
OverlayModal *modal_ = nullptr; OverlayModal *modal_ = nullptr;
LoadingIndicator *spinner_ = nullptr; LoadingIndicator *spinner_ = nullptr;
JdenticonInterface *jdenticonInteface_ = nullptr;
}; };

View File

@ -86,6 +86,7 @@ UserSettings::load(std::optional<QString> profile)
theme_ = settings.value("user/theme", defaultTheme_).toString(); theme_ = settings.value("user/theme", defaultTheme_).toString();
font_ = settings.value("user/font_family", "default").toString(); font_ = settings.value("user/font_family", "default").toString();
avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
useIdenticon_ = settings.value("user/use_identicon", true).toBool();
decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool();
privacyScreen_ = settings.value("user/privacy_screen", false).toBool(); privacyScreen_ = settings.value("user/privacy_screen", false).toBool();
privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt(); privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt();
@ -596,6 +597,15 @@ UserSettings::setDisableCertificateValidation(bool disabled)
disableCertificateValidation_ = disabled; disableCertificateValidation_ = disabled;
http::client()->verify_certificates(!disabled); http::client()->verify_certificates(!disabled);
emit disableCertificateValidationChanged(disabled); emit disableCertificateValidationChanged(disabled);
}
void
UserSettings::setUseIdenticon(bool state)
{
if (state == useIdenticon_)
return;
useIdenticon_ = state;
emit useIdenticonChanged(useIdenticon_);
save(); save();
} }
@ -674,6 +684,7 @@ UserSettings::save()
settings.setValue("screen_share_hide_cursor", screenShareHideCursor_); settings.setValue("screen_share_hide_cursor", screenShareHideCursor_);
settings.setValue("use_stun_server", useStunServer_); settings.setValue("use_stun_server", useStunServer_);
settings.setValue("currentProfile", profile_); settings.setValue("currentProfile", profile_);
settings.setValue("use_identicon", useIdenticon_);
settings.endGroup(); // user settings.endGroup(); // user
@ -746,6 +757,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
trayToggle_ = new Toggle{this}; trayToggle_ = new Toggle{this};
startInTrayToggle_ = new Toggle{this}; startInTrayToggle_ = new Toggle{this};
avatarCircles_ = new Toggle{this}; avatarCircles_ = new Toggle{this};
useIdenticon_ = new Toggle{this};
decryptSidebar_ = new Toggle(this); decryptSidebar_ = new Toggle(this);
privacyScreen_ = new Toggle{this}; privacyScreen_ = new Toggle{this};
onlyShareKeysWithVerifiedUsers_ = new Toggle(this); onlyShareKeysWithVerifiedUsers_ = new Toggle(this);
@ -779,6 +791,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
trayToggle_->setChecked(settings_->tray()); trayToggle_->setChecked(settings_->tray());
startInTrayToggle_->setChecked(settings_->startInTray()); startInTrayToggle_->setChecked(settings_->startInTray());
avatarCircles_->setChecked(settings_->avatarCircles()); avatarCircles_->setChecked(settings_->avatarCircles());
useIdenticon_->setChecked(settings_->useIdenticon());
decryptSidebar_->setChecked(settings_->decryptSidebar()); decryptSidebar_->setChecked(settings_->decryptSidebar());
privacyScreen_->setChecked(settings_->privacyScreen()); privacyScreen_->setChecked(settings_->privacyScreen());
onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers()); onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers());
@ -941,6 +954,9 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
boxWrap(tr("Circular Avatars"), boxWrap(tr("Circular Avatars"),
avatarCircles_, avatarCircles_,
tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle.")); tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle."));
boxWrap(tr("Use identicons"),
useIdenticon_,
tr("Display an identicon instead of a letter when a user has not set an avatar."));
boxWrap(tr("Group's sidebar"), boxWrap(tr("Group's sidebar"),
groupViewToggle_, groupViewToggle_,
tr("Show a column containing groups and tags next to the room list.")); tr("Show a column containing groups and tags next to the room list."));
@ -1263,6 +1279,13 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
settings_->setAvatarCircles(enabled); settings_->setAvatarCircles(enabled);
}); });
if (JdenticonProvider::isAvailable())
connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) {
settings_->setUseIdenticon(enabled);
});
else
useIdenticon_->setDisabled(true);
connect(markdown_, &Toggle::toggled, this, [this](bool enabled) { connect(markdown_, &Toggle::toggled, this, [this](bool enabled) {
settings_->setMarkdown(enabled); settings_->setMarkdown(enabled);
}); });

View File

@ -12,6 +12,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QWidget> #include <QWidget>
#include "JdenticonProvider.h"
#include <optional> #include <optional>
class Toggle; class Toggle;
@ -105,6 +106,8 @@ class UserSettings : public QObject
Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)
Q_PROPERTY(bool disableCertificateValidation READ disableCertificateValidation WRITE Q_PROPERTY(bool disableCertificateValidation READ disableCertificateValidation WRITE
setDisableCertificateValidation NOTIFY disableCertificateValidationChanged) setDisableCertificateValidation NOTIFY disableCertificateValidationChanged)
Q_PROPERTY(
bool useIdenticon READ useIdenticon WRITE setUseIdenticon NOTIFY useIdenticonChanged)
UserSettings(); UserSettings();
@ -172,6 +175,7 @@ public:
void setHomeserver(QString homeserver); void setHomeserver(QString homeserver);
void setDisableCertificateValidation(bool disabled); void setDisableCertificateValidation(bool disabled);
void setHiddenTags(QStringList hiddenTags); void setHiddenTags(QStringList hiddenTags);
void setUseIdenticon(bool state);
QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; } QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
bool messageHoverHighlight() const { return messageHoverHighlight_; } bool messageHoverHighlight() const { return messageHoverHighlight_; }
@ -230,6 +234,7 @@ public:
QString homeserver() const { return homeserver_; } QString homeserver() const { return homeserver_; }
bool disableCertificateValidation() const { return disableCertificateValidation_; } bool disableCertificateValidation() const { return disableCertificateValidation_; }
QStringList hiddenTags() const { return hiddenTags_; } QStringList hiddenTags() const { return hiddenTags_; }
bool useIdenticon() const { return useIdenticon_ && JdenticonProvider::isAvailable(); }
signals: signals:
void groupViewStateChanged(bool state); void groupViewStateChanged(bool state);
@ -277,6 +282,7 @@ signals:
void deviceIdChanged(QString deviceId); void deviceIdChanged(QString deviceId);
void homeserverChanged(QString homeserver); void homeserverChanged(QString homeserver);
void disableCertificateValidationChanged(bool disabled); void disableCertificateValidationChanged(bool disabled);
void useIdenticonChanged(bool state);
private: private:
// Default to system theme if QT_QPA_PLATFORMTHEME var is set. // Default to system theme if QT_QPA_PLATFORMTHEME var is set.
@ -330,6 +336,7 @@ private:
QString deviceId_; QString deviceId_;
QString homeserver_; QString homeserver_;
QStringList hiddenTags_; QStringList hiddenTags_;
bool useIdenticon_;
QSettings settings; QSettings settings;
@ -391,6 +398,7 @@ private:
Toggle *desktopNotifications_; Toggle *desktopNotifications_;
Toggle *alertOnNotification_; Toggle *alertOnNotification_;
Toggle *avatarCircles_; Toggle *avatarCircles_;
Toggle *useIdenticon_;
Toggle *useStunServer_; Toggle *useStunServer_;
Toggle *decryptSidebar_; Toggle *decryptSidebar_;
Toggle *privacyScreen_; Toggle *privacyScreen_;

View File

@ -76,6 +76,8 @@ RoomlistModel::roleNames() const
{IsSpace, "isSpace"}, {IsSpace, "isSpace"},
{Tags, "tags"}, {Tags, "tags"},
{ParentSpaces, "parentSpaces"}, {ParentSpaces, "parentSpaces"},
{IsDirect, "isDirect"},
{DirectChatOtherUserId, "directChatOtherUserId"},
}; };
} }
@ -129,6 +131,10 @@ RoomlistModel::data(const QModelIndex &index, int role) const
list.push_back(QString::fromStdString(t)); list.push_back(QString::fromStdString(t));
return list; return list;
} }
case Roles::IsDirect:
return room->isDirect();
case Roles::DirectChatOtherUserId:
return room->directChatOtherUserId();
default: default:
return {}; return {};
} }
@ -158,6 +164,14 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return false; return false;
case Roles::Tags: case Roles::Tags:
return QStringList(); return QStringList();
case Roles::IsDirect:
// The list of users from the room doesn't contain the invited
// users, so we won't factor the invite into the count
return room.member_count == 1;
case Roles::DirectChatOtherUserId:
return cache::getMembersFromInvite(roomid.toStdString(), 0, 1)
.front()
.user_id;
default: default:
return {}; return {};
} }
@ -190,6 +204,10 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return true; return true;
case Roles::Tags: case Roles::Tags:
return QStringList(); return QStringList();
case Roles::IsDirect:
return false;
case Roles::DirectChatOtherUserId:
return QString{}; // should never be reached
default: default:
return {}; return {};
} }

View File

@ -65,6 +65,8 @@ public:
IsPreviewFetched, IsPreviewFetched,
Tags, Tags,
ParentSpaces, ParentSpaces,
IsDirect,
DirectChatOtherUserId,
}; };
RoomlistModel(TimelineViewManager *parent = nullptr); RoomlistModel(TimelineViewManager *parent = nullptr);

View File

@ -817,6 +817,11 @@ TimelineModel::syncState(const mtx::responses::State &s)
emit roomAvatarUrlChanged(); emit roomAvatarUrlChanged();
emit roomNameChanged(); emit roomNameChanged();
emit roomMemberCountChanged(); emit roomMemberCountChanged();
if (roomMemberCount() <= 2) {
emit isDirectChanged();
emit directChatOtherUserIdChanged();
}
} else if (std::holds_alternative<StateEvent<state::Encryption>>(e)) { } else if (std::holds_alternative<StateEvent<state::Encryption>>(e)) {
this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
emit encryptionChanged(); emit encryptionChanged();
@ -2073,3 +2078,16 @@ TimelineModel::roomMemberCount() const
{ {
return (int)cache::client()->memberCount(room_id_.toStdString()); return (int)cache::client()->memberCount(room_id_.toStdString());
} }
QString
TimelineModel::directChatOtherUserId() const
{
if (roomMemberCount() < 3) {
QString id;
for (auto member : cache::getMembers(room_id_.toStdString()))
if (member.user_id != UserSettings::instance()->userId())
id = member.user_id;
return id;
} else
return "";
}

View File

@ -176,6 +176,9 @@ class TimelineModel : public QAbstractListModel
Q_PROPERTY(bool isEncrypted READ isEncrypted NOTIFY encryptionChanged) Q_PROPERTY(bool isEncrypted READ isEncrypted NOTIFY encryptionChanged)
Q_PROPERTY(bool isSpace READ isSpace CONSTANT) Q_PROPERTY(bool isSpace READ isSpace CONSTANT)
Q_PROPERTY(int trustlevel READ trustlevel NOTIFY trustlevelChanged) Q_PROPERTY(int trustlevel READ trustlevel NOTIFY trustlevelChanged)
Q_PROPERTY(bool isDirect READ isDirect NOTIFY isDirectChanged)
Q_PROPERTY(QString directChatOtherUserId READ directChatOtherUserId NOTIFY
directChatOtherUserIdChanged)
Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(InputBar *input READ input CONSTANT)
Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged)
@ -292,6 +295,8 @@ public:
bool isEncrypted() const { return isEncrypted_; } bool isEncrypted() const { return isEncrypted_; }
crypto::Trust trustlevel() const; crypto::Trust trustlevel() const;
int roomMemberCount() const; int roomMemberCount() const;
bool isDirect() const { return roomMemberCount() <= 2; }
QString directChatOtherUserId() const;
std::optional<mtx::events::collections::TimelineEvents> eventById(const QString &id) std::optional<mtx::events::collections::TimelineEvents> eventById(const QString &id)
{ {
@ -391,6 +396,8 @@ signals:
void roomTopicChanged(); void roomTopicChanged();
void roomAvatarUrlChanged(); void roomAvatarUrlChanged();
void roomMemberCountChanged(); void roomMemberCountChanged();
void isDirectChanged();
void directChatOtherUserIdChanged();
void permissionsChanged(); void permissionsChanged();
void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId);

View File

@ -141,6 +141,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
, imgProvider(new MxcImageProvider()) , imgProvider(new MxcImageProvider())
, colorImgProvider(new ColorImageProvider()) , colorImgProvider(new ColorImageProvider())
, blurhashProvider(new BlurhashProvider()) , blurhashProvider(new BlurhashProvider())
, jdenticonProvider(new JdenticonProvider())
, callManager_(callManager) , callManager_(callManager)
, rooms_(new RoomlistModel(this)) , rooms_(new RoomlistModel(this))
, communities_(new CommunitiesModel(this)) , communities_(new CommunitiesModel(this))
@ -310,6 +311,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("MxcImage", imgProvider);
view->engine()->addImageProvider("colorimage", colorImgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider);
view->engine()->addImageProvider("blurhash", blurhashProvider); view->engine()->addImageProvider("blurhash", blurhashProvider);
if (JdenticonProvider::isAvailable())
view->engine()->addImageProvider("jdenticon", jdenticonProvider);
view->setSource(QUrl("qrc:///qml/Root.qml")); view->setSource(QUrl("qrc:///qml/Root.qml"));
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette); connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);

View File

@ -18,6 +18,7 @@
#include "Cache.h" #include "Cache.h"
#include "CallManager.h" #include "CallManager.h"
#include "JdenticonProvider.h"
#include "Logging.h" #include "Logging.h"
#include "TimelineModel.h" #include "TimelineModel.h"
#include "Utils.h" #include "Utils.h"
@ -141,6 +142,7 @@ private:
MxcImageProvider *imgProvider; MxcImageProvider *imgProvider;
ColorImageProvider *colorImgProvider; ColorImageProvider *colorImgProvider;
BlurhashProvider *blurhashProvider; BlurhashProvider *blurhashProvider;
JdenticonProvider *jdenticonProvider;
CallManager *callManager_ = nullptr; CallManager *callManager_ = nullptr;

View File

@ -8,12 +8,6 @@
#include <QPalette> #include <QPalette>
namespace ui { namespace ui {
enum class AvatarType
{
Image,
Letter
};
// Default font size. // Default font size.
const int FontSize = 16; const int FontSize = 16;