mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-26 04:58:49 +03:00
Merge branch 'master' of https://github.com/Nheko-Reborn/nheko
This commit is contained in:
commit
7d72356318
29 changed files with 4193 additions and 555 deletions
|
@ -304,7 +304,6 @@ set(SRC_FILES
|
||||||
src/SideBarActions.cpp
|
src/SideBarActions.cpp
|
||||||
src/Splitter.cpp
|
src/Splitter.cpp
|
||||||
src/TextInputWidget.cpp
|
src/TextInputWidget.cpp
|
||||||
src/TopRoomBar.cpp
|
|
||||||
src/TrayIcon.cpp
|
src/TrayIcon.cpp
|
||||||
src/UserInfoWidget.cpp
|
src/UserInfoWidget.cpp
|
||||||
src/UserSettingsPage.cpp
|
src/UserSettingsPage.cpp
|
||||||
|
@ -341,7 +340,7 @@ if(USE_BUNDLED_MTXCLIENT)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
MatrixClient
|
MatrixClient
|
||||||
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
||||||
GIT_TAG d8666a3f1a5b709b78ccea2b545d540a8cb502ca
|
GIT_TAG ac680e971d437eb2135ef994dcb58c0bbb5bdf61
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(MatrixClient)
|
FetchContent_MakeAvailable(MatrixClient)
|
||||||
else()
|
else()
|
||||||
|
@ -512,7 +511,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/SideBarActions.h
|
src/SideBarActions.h
|
||||||
src/Splitter.h
|
src/Splitter.h
|
||||||
src/TextInputWidget.h
|
src/TextInputWidget.h
|
||||||
src/TopRoomBar.h
|
|
||||||
src/TrayIcon.h
|
src/TrayIcon.h
|
||||||
src/UserInfoWidget.h
|
src/UserInfoWidget.h
|
||||||
src/UserSettingsPage.h
|
src/UserSettingsPage.h
|
||||||
|
|
|
@ -132,6 +132,7 @@ Nheko can use bundled version for most of those libraries automatically, if the
|
||||||
To use them, you can enable the hunter integration by passing `-DHUNTER_ENABLED=ON`.
|
To use them, you can enable the hunter integration by passing `-DHUNTER_ENABLED=ON`.
|
||||||
It is probably wise to link those dependencies statically by passing `-DBUILD_SHARED_LIBS=OFF`
|
It is probably wise to link those dependencies statically by passing `-DBUILD_SHARED_LIBS=OFF`
|
||||||
You can select which bundled dependencies you want to use py passing various `-DUSE_BUNDLED_*` flags. By default all dependencies are bundled *if* you enable hunter.
|
You can select which bundled dependencies you want to use py passing various `-DUSE_BUNDLED_*` flags. By default all dependencies are bundled *if* you enable hunter.
|
||||||
|
If you experience build issues and you are trying to link `mtxclient` library without hunter, make sure the library version(commit) as mentioned in the `CMakeList.txt` is used. Sometimes we have to make breaking changes in `mtxclient` and for that period the master branch of both repos may not be compatible.
|
||||||
|
|
||||||
The bundle flags are currently:
|
The bundle flags are currently:
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@
|
||||||
"name": "mtxclient",
|
"name": "mtxclient",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"commit": "d8666a3f1a5b709b78ccea2b545d540a8cb502ca",
|
"commit": "ac680e971d437eb2135ef994dcb58c0bbb5bdf61",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Nheko-Reborn/mtxclient.git"
|
"url": "https://github.com/Nheko-Reborn/mtxclient.git"
|
||||||
}
|
}
|
||||||
|
|
1860
resources/langs/nheko_cs.ts
Normal file
1860
resources/langs/nheko_cs.ts
Normal file
File diff suppressed because it is too large
Load diff
1858
resources/langs/nheko_et.ts
Normal file
1858
resources/langs/nheko_et.ts
Normal file
File diff suppressed because it is too large
Load diff
|
@ -237,11 +237,6 @@ If Nheko fails to discover your homeserver, it will show you a field to enter th
|
||||||
<source>Device name</source>
|
<source>Device name</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location line="+2"/>
|
|
||||||
<source>A name for this device, which will be shown to others, when verifying your devices. If none is provided, a random string is used for privacy purposes.</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+6"/>
|
<location line="+6"/>
|
||||||
<source>The address that can be used to contact you homeservers client API.
|
<source>The address that can be used to contact you homeservers client API.
|
||||||
|
@ -486,11 +481,6 @@ Example: https://server.my:8787</source>
|
||||||
<comment>Tag name prompt title</comment>
|
<comment>Tag name prompt title</comment>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location line="+0"/>
|
|
||||||
<source>Tag:</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+173"/>
|
<location line="+173"/>
|
||||||
<source>Accept</source>
|
<source>Accept</source>
|
||||||
|
|
|
@ -14,7 +14,7 @@ Rectangle {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
text: chat.model.escapeEmoji(String.fromCodePoint(displayName.codePointAt(0)))
|
text: timelineManager.escapeEmoji(String.fromCodePoint(displayName.codePointAt(0)))
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
font.pixelSize: avatar.height/2
|
font.pixelSize: avatar.height/2
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
@ -50,6 +50,8 @@ Rectangle {
|
||||||
anchors.bottom: avatar.bottom
|
anchors.bottom: avatar.bottom
|
||||||
anchors.right: avatar.right
|
anchors.right: avatar.right
|
||||||
|
|
||||||
|
visible: !!userid
|
||||||
|
|
||||||
height: avatar.height / 6
|
height: avatar.height / 6
|
||||||
width: height
|
width: height
|
||||||
radius: settings.avatarCircles ? height / 2 : height / 4
|
radius: settings.avatarCircles ? height / 2 : height / 4
|
||||||
|
|
|
@ -115,6 +115,130 @@ Page {
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
Rectangle {
|
||||||
|
id: topBar
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: topLayout.height + 16
|
||||||
|
z: 3
|
||||||
|
|
||||||
|
color: colors.base
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: timelineManager.openRoomSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: topLayout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: 8
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
//Layout.margins: 8
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
id: backToRoomsButton
|
||||||
|
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.row: 0
|
||||||
|
Layout.rowSpan: 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
visible: timelineManager.isNarrowView
|
||||||
|
|
||||||
|
image: ":/icons/icons/ui/angle-pointing-to-left.png"
|
||||||
|
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Back to room list")
|
||||||
|
|
||||||
|
onClicked: timelineManager.backToRooms()
|
||||||
|
}
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: 0
|
||||||
|
Layout.rowSpan: 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
width: avatarSize
|
||||||
|
height: avatarSize
|
||||||
|
|
||||||
|
url: chat.model ? chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : ""
|
||||||
|
displayName: chat.model ? chat.model.roomName : qsTr("No room selected")
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: timelineManager.openRoomSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.column: 2
|
||||||
|
Layout.row: 0
|
||||||
|
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
|
|
||||||
|
text: chat.model ? chat.model.roomName : qsTr("No room selected")
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: timelineManager.openRoomSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MatrixText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.column: 2
|
||||||
|
Layout.row: 1
|
||||||
|
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
text: chat.model ? chat.model.roomTopic : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
id: roomOptionsButton
|
||||||
|
|
||||||
|
Layout.column: 3
|
||||||
|
Layout.row: 0
|
||||||
|
Layout.rowSpan: 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
image: ":/icons/icons/ui/vertical-ellipsis.png"
|
||||||
|
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Room options")
|
||||||
|
|
||||||
|
onClicked: roomOptionsMenu.popup(roomOptionsButton)
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: roomOptionsMenu
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Invite users")
|
||||||
|
onTriggered: timelineManager.openInviteUsersDialog();
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Members")
|
||||||
|
onTriggered: timelineManager.openMemberListDialog();
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Leave room")
|
||||||
|
onTriggered: timelineManager.openLeaveRoomDialog();
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Settings")
|
||||||
|
onTriggered: timelineManager.openRoomSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: chat
|
id: chat
|
||||||
|
|
||||||
|
@ -122,13 +246,8 @@ Page {
|
||||||
|
|
||||||
cacheBuffer: 400
|
cacheBuffer: 400
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
Layout.fillWidth: true
|
||||||
anchors.top: parent.top
|
Layout.fillHeight: true
|
||||||
anchors.bottom: chatFooter.top
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
anchors.leftMargin: 4
|
|
||||||
anchors.rightMargin: scrollbar.width
|
|
||||||
|
|
||||||
model: timelineManager.timeline
|
model: timelineManager.timeline
|
||||||
|
|
||||||
|
@ -167,10 +286,6 @@ Page {
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
id: scrollbar
|
id: scrollbar
|
||||||
parent: chat.parent
|
|
||||||
anchors.top: chat.top
|
|
||||||
anchors.right: chat.right
|
|
||||||
anchors.bottom: chat.bottom
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
@ -178,9 +293,9 @@ Page {
|
||||||
|
|
||||||
onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom
|
onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom
|
||||||
|
|
||||||
property int delegateMaxWidth: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > 32) ? settings.timelineMaxWidth : (parent.width - 32)
|
property int delegateMaxWidth: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > scrollbar.width*2) ? settings.timelineMaxWidth : (parent.width - scrollbar.width*2)
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Item {
|
||||||
// This would normally be previousSection, but our model's order is inverted.
|
// This would normally be previousSection, but our model's order is inverted.
|
||||||
property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
|
property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
|
||||||
|
|
||||||
|
@ -189,7 +304,6 @@ Page {
|
||||||
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||||
width: chat.delegateMaxWidth
|
width: chat.delegateMaxWidth
|
||||||
height: section ? section.height + timelinerow.height : timelinerow.height
|
height: section ? section.height + timelinerow.height : timelinerow.height
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
TimelineRow {
|
TimelineRow {
|
||||||
id: timelinerow
|
id: timelinerow
|
||||||
|
@ -276,7 +390,7 @@ Page {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: userName
|
id: userName
|
||||||
text: chat.model.escapeEmoji(modelData.userName)
|
text: timelineManager.escapeEmoji(modelData.userName)
|
||||||
color: timelineManager.userColor(modelData.userId, colors.window)
|
color: timelineManager.userColor(modelData.userId, colors.window)
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
|
||||||
|
@ -309,17 +423,13 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: chatFooter
|
id: chatFooter
|
||||||
|
|
||||||
height: Math.max(fontMetrics.height * 1.2, footerContent.height)
|
implicitHeight: Math.max(fontMetrics.height * 1.2, footerContent.height)
|
||||||
anchors.left: parent.left
|
Layout.fillWidth: true
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: footerContent
|
id: footerContent
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -397,4 +507,5 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ Item {
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.EmoteMessage
|
roleValue: MtxEvent.EmoteMessage
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
formatted: chat.model.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
|
formatted: timelineManager.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
|
||||||
color: timelineManager.userColor(modelData.userId, colors.window)
|
color: timelineManager.userColor(modelData.userId, colors.window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ Item {
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: userName
|
id: userName
|
||||||
text: chat.model ? chat.model.escapeEmoji(reply.modelData.userName) : ""
|
text: timelineManager.escapeEmoji(reply.modelData.userName)
|
||||||
color: replyComponent.userColor
|
color: replyComponent.userColor
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,12 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca
|
||||||
{
|
{
|
||||||
const auto cacheKey = QString("%1_size_%2").arg(avatarUrl).arg(size);
|
const auto cacheKey = QString("%1_size_%2").arg(avatarUrl).arg(size);
|
||||||
|
|
||||||
if (avatarUrl.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QPixmap pixmap;
|
QPixmap pixmap;
|
||||||
|
if (avatarUrl.isEmpty()) {
|
||||||
|
callback(pixmap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (avatar_cache.find(cacheKey, &pixmap)) {
|
if (avatar_cache.find(cacheKey, &pixmap)) {
|
||||||
callback(pixmap);
|
callback(pixmap);
|
||||||
return;
|
return;
|
||||||
|
@ -75,11 +77,10 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca
|
||||||
opts.mxc_url,
|
opts.mxc_url,
|
||||||
mtx::errors::to_string(err->matrix_error.errcode),
|
mtx::errors::to_string(err->matrix_error.errcode),
|
||||||
err->matrix_error.error);
|
err->matrix_error.error);
|
||||||
return;
|
} else {
|
||||||
|
cache::saveImage(cacheKey.toStdString(), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
cache::saveImage(cacheKey.toStdString(), res);
|
|
||||||
|
|
||||||
emit proxy->avatarDownloaded(QByteArray(res.data(), res.size()));
|
emit proxy->avatarDownloaded(QByteArray(res.data(), res.size()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
121
src/Cache.cpp
121
src/Cache.cpp
|
@ -94,8 +94,10 @@ namespace {
|
||||||
std::unique_ptr<Cache> instance_ = nullptr;
|
std::unique_ptr<Cache> instance_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
isHiddenEvent(mtx::events::collections::TimelineEvents e, const std::string &room_id)
|
Cache::isHiddenEvent(lmdb::txn &txn,
|
||||||
|
mtx::events::collections::TimelineEvents e,
|
||||||
|
const std::string &room_id)
|
||||||
{
|
{
|
||||||
using namespace mtx::events;
|
using namespace mtx::events;
|
||||||
if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) {
|
if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) {
|
||||||
|
@ -109,13 +111,27 @@ isHiddenEvent(mtx::events::collections::TimelineEvents e, const std::string &roo
|
||||||
e = result.event.value();
|
e = result.event.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr std::initializer_list<EventType> hiddenEvents = {
|
mtx::events::account_data::nheko_extensions::HiddenEvents hiddenEvents;
|
||||||
|
hiddenEvents.hidden_event_types = {
|
||||||
EventType::Reaction, EventType::CallCandidates, EventType::Unsupported};
|
EventType::Reaction, EventType::CallCandidates, EventType::Unsupported};
|
||||||
|
|
||||||
|
if (auto temp = getAccountData(txn, mtx::events::EventType::NhekoHiddenEvents, ""))
|
||||||
|
hiddenEvents = std::move(
|
||||||
|
std::get<
|
||||||
|
mtx::events::Event<mtx::events::account_data::nheko_extensions::HiddenEvents>>(
|
||||||
|
*temp)
|
||||||
|
.content);
|
||||||
|
if (auto temp = getAccountData(txn, mtx::events::EventType::NhekoHiddenEvents, room_id))
|
||||||
|
hiddenEvents = std::move(
|
||||||
|
std::get<
|
||||||
|
mtx::events::Event<mtx::events::account_data::nheko_extensions::HiddenEvents>>(
|
||||||
|
*temp)
|
||||||
|
.content);
|
||||||
|
|
||||||
return std::visit(
|
return std::visit(
|
||||||
[](const auto &ev) {
|
[hiddenEvents](const auto &ev) {
|
||||||
return std::any_of(hiddenEvents.begin(),
|
return std::any_of(hiddenEvents.hidden_event_types.begin(),
|
||||||
hiddenEvents.end(),
|
hiddenEvents.hidden_event_types.end(),
|
||||||
[ev](EventType type) { return type == ev.type; });
|
[ev](EventType type) { return type == ev.type; });
|
||||||
},
|
},
|
||||||
e);
|
e);
|
||||||
|
@ -624,6 +640,7 @@ Cache::removeRoom(lmdb::txn &txn, const std::string &roomid)
|
||||||
{
|
{
|
||||||
lmdb::dbi_del(txn, roomsDb_, lmdb::val(roomid), nullptr);
|
lmdb::dbi_del(txn, roomsDb_, lmdb::val(roomid), nullptr);
|
||||||
lmdb::dbi_drop(txn, getStatesDb(txn, roomid), true);
|
lmdb::dbi_drop(txn, getStatesDb(txn, roomid), true);
|
||||||
|
lmdb::dbi_drop(txn, getAccountDataDb(txn, roomid), true);
|
||||||
lmdb::dbi_drop(txn, getMembersDb(txn, roomid), true);
|
lmdb::dbi_drop(txn, getMembersDb(txn, roomid), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,6 +999,19 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
|
|
||||||
setNextBatchToken(txn, res.next_batch);
|
setNextBatchToken(txn, res.next_batch);
|
||||||
|
|
||||||
|
if (!res.account_data.events.empty()) {
|
||||||
|
auto accountDataDb = getAccountDataDb(txn, "");
|
||||||
|
for (const auto &ev : res.account_data.events)
|
||||||
|
std::visit(
|
||||||
|
[&txn, &accountDataDb](const auto &event) {
|
||||||
|
lmdb::dbi_put(txn,
|
||||||
|
accountDataDb,
|
||||||
|
lmdb::val(to_string(event.type)),
|
||||||
|
lmdb::val(json(event).dump()));
|
||||||
|
},
|
||||||
|
ev);
|
||||||
|
}
|
||||||
|
|
||||||
// Save joined rooms
|
// Save joined rooms
|
||||||
for (const auto &room : res.rooms.join) {
|
for (const auto &room : res.rooms.join) {
|
||||||
auto statesdb = getStatesDb(txn, room.first);
|
auto statesdb = getStatesDb(txn, room.first);
|
||||||
|
@ -1001,30 +1031,43 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
updatedInfo.version = getRoomVersion(txn, statesdb).toStdString();
|
updatedInfo.version = getRoomVersion(txn, statesdb).toStdString();
|
||||||
|
|
||||||
// Process the account_data associated with this room
|
// Process the account_data associated with this room
|
||||||
bool has_new_tags = false;
|
if (!room.second.account_data.events.empty()) {
|
||||||
for (const auto &evt : room.second.account_data.events) {
|
auto accountDataDb = getAccountDataDb(txn, room.first);
|
||||||
// for now only fetch tag events
|
|
||||||
if (std::holds_alternative<Event<account_data::Tags>>(evt)) {
|
bool has_new_tags = false;
|
||||||
auto tags_evt = std::get<Event<account_data::Tags>>(evt);
|
for (const auto &evt : room.second.account_data.events) {
|
||||||
has_new_tags = true;
|
std::visit(
|
||||||
for (const auto &tag : tags_evt.content.tags) {
|
[&txn, &accountDataDb](const auto &event) {
|
||||||
updatedInfo.tags.push_back(tag.first);
|
lmdb::dbi_put(txn,
|
||||||
|
accountDataDb,
|
||||||
|
lmdb::val(to_string(event.type)),
|
||||||
|
lmdb::val(json(event).dump()));
|
||||||
|
},
|
||||||
|
evt);
|
||||||
|
|
||||||
|
// for tag events
|
||||||
|
if (std::holds_alternative<Event<account_data::Tags>>(evt)) {
|
||||||
|
auto tags_evt = std::get<Event<account_data::Tags>>(evt);
|
||||||
|
has_new_tags = true;
|
||||||
|
for (const auto &tag : tags_evt.content.tags) {
|
||||||
|
updatedInfo.tags.push_back(tag.first);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!has_new_tags) {
|
||||||
if (!has_new_tags) {
|
// retrieve the old tags, they haven't changed
|
||||||
// retrieve the old tags, they haven't changed
|
lmdb::val data;
|
||||||
lmdb::val data;
|
if (lmdb::dbi_get(txn, roomsDb_, lmdb::val(room.first), data)) {
|
||||||
if (lmdb::dbi_get(txn, roomsDb_, lmdb::val(room.first), data)) {
|
try {
|
||||||
try {
|
RoomInfo tmp = json::parse(
|
||||||
RoomInfo tmp =
|
std::string_view(data.data(), data.size()));
|
||||||
json::parse(std::string_view(data.data(), data.size()));
|
updatedInfo.tags = tmp.tags;
|
||||||
updatedInfo.tags = tmp.tags;
|
} catch (const json::exception &e) {
|
||||||
} catch (const json::exception &e) {
|
nhlog::db()->warn(
|
||||||
nhlog::db()->warn(
|
"failed to parse room info: room_id ({}), {}",
|
||||||
"failed to parse room info: room_id ({}), {}",
|
room.first,
|
||||||
room.first,
|
std::string(data.data(), data.size()));
|
||||||
std::string(data.data(), data.size()));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2439,7 +2482,7 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
|
||||||
lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index)));
|
lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index)));
|
||||||
|
|
||||||
// TODO(Nico): Allow blacklisting more event types in UI
|
// TODO(Nico): Allow blacklisting more event types in UI
|
||||||
if (!isHiddenEvent(e, room_id)) {
|
if (!isHiddenEvent(txn, e, room_id)) {
|
||||||
++msgIndex;
|
++msgIndex;
|
||||||
lmdb::cursor_put(msgCursor.handle(),
|
lmdb::cursor_put(msgCursor.handle(),
|
||||||
lmdb::val(&msgIndex, sizeof(msgIndex)),
|
lmdb::val(&msgIndex, sizeof(msgIndex)),
|
||||||
|
@ -2522,7 +2565,7 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message
|
||||||
lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index)));
|
lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index)));
|
||||||
|
|
||||||
// TODO(Nico): Allow blacklisting more event types in UI
|
// TODO(Nico): Allow blacklisting more event types in UI
|
||||||
if (!isHiddenEvent(e, room_id)) {
|
if (!isHiddenEvent(txn, e, room_id)) {
|
||||||
--msgIndex;
|
--msgIndex;
|
||||||
lmdb::dbi_put(
|
lmdb::dbi_put(
|
||||||
txn, order2msgDb, lmdb::val(&msgIndex, sizeof(msgIndex)), event_id);
|
txn, order2msgDb, lmdb::val(&msgIndex, sizeof(msgIndex)), event_id);
|
||||||
|
@ -2840,6 +2883,24 @@ Cache::deleteOldData() noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<mtx::events::collections::RoomAccountDataEvents>
|
||||||
|
Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::string &room_id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
auto db = getAccountDataDb(txn, room_id);
|
||||||
|
|
||||||
|
lmdb::val data;
|
||||||
|
if (lmdb::dbi_get(txn, db, lmdb::val(to_string(type)), data)) {
|
||||||
|
mtx::responses::utils::RoomAccountDataEvents events;
|
||||||
|
mtx::responses::utils::parse_room_account_data_events(
|
||||||
|
std::string_view(data.data(), data.size()), events);
|
||||||
|
return events.front();
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
||||||
const std::string &room_id,
|
const std::string &room_id,
|
||||||
|
|
|
@ -289,6 +289,14 @@ private:
|
||||||
const std::string &room_id,
|
const std::string &room_id,
|
||||||
const mtx::responses::Timeline &res);
|
const mtx::responses::Timeline &res);
|
||||||
|
|
||||||
|
//! retrieve a specific event from account data
|
||||||
|
//! pass empty room_id for global account data
|
||||||
|
std::optional<mtx::events::collections::RoomAccountDataEvents>
|
||||||
|
getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::string &room_id);
|
||||||
|
bool isHiddenEvent(lmdb::txn &txn,
|
||||||
|
mtx::events::collections::TimelineEvents e,
|
||||||
|
const std::string &room_id);
|
||||||
|
|
||||||
//! Remove a room from the cache.
|
//! Remove a room from the cache.
|
||||||
// void removeLeftRoom(lmdb::txn &txn, const std::string &room_id);
|
// void removeLeftRoom(lmdb::txn &txn, const std::string &room_id);
|
||||||
template<class T>
|
template<class T>
|
||||||
|
@ -498,6 +506,12 @@ private:
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE);
|
return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lmdb::dbi getAccountDataDb(lmdb::txn &txn, const std::string &room_id)
|
||||||
|
{
|
||||||
|
return lmdb::dbi::open(
|
||||||
|
txn, std::string(room_id + "/account_data").c_str(), MDB_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id)
|
||||||
{
|
{
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE);
|
return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE);
|
||||||
|
|
132
src/ChatPage.cpp
132
src/ChatPage.cpp
|
@ -37,7 +37,6 @@
|
||||||
#include "SideBarActions.h"
|
#include "SideBarActions.h"
|
||||||
#include "Splitter.h"
|
#include "Splitter.h"
|
||||||
#include "TextInputWidget.h"
|
#include "TextInputWidget.h"
|
||||||
#include "TopRoomBar.h"
|
|
||||||
#include "UserInfoWidget.h"
|
#include "UserInfoWidget.h"
|
||||||
#include "UserSettingsPage.h"
|
#include "UserSettingsPage.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
@ -126,10 +125,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
contentLayout_->setSpacing(0);
|
contentLayout_->setSpacing(0);
|
||||||
contentLayout_->setMargin(0);
|
contentLayout_->setMargin(0);
|
||||||
|
|
||||||
top_bar_ = new TopRoomBar(this);
|
|
||||||
view_manager_ = new TimelineViewManager(userSettings_, &callManager_, this);
|
view_manager_ = new TimelineViewManager(userSettings_, &callManager_, this);
|
||||||
|
|
||||||
contentLayout_->addWidget(top_bar_);
|
|
||||||
contentLayout_->addWidget(view_manager_->getWidget());
|
contentLayout_->addWidget(view_manager_->getWidget());
|
||||||
|
|
||||||
activeCallBar_ = new ActiveCallBar(this);
|
activeCallBar_ = new ActiveCallBar(this);
|
||||||
|
@ -181,30 +178,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
room_list_->previousRoom();
|
room_list_->previousRoom();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(top_bar_, &TopRoomBar::mentionsClicked, this, [this](const QPoint &mentionsPos) {
|
|
||||||
if (user_mentions_popup_->isVisible()) {
|
|
||||||
user_mentions_popup_->hide();
|
|
||||||
} else {
|
|
||||||
showNotificationsDialog(mentionsPos);
|
|
||||||
http::client()->notifications(
|
|
||||||
1000,
|
|
||||||
"",
|
|
||||||
"highlight",
|
|
||||||
[this, mentionsPos](const mtx::responses::Notifications &res,
|
|
||||||
mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
nhlog::net()->warn(
|
|
||||||
"failed to retrieve notifications: {} ({})",
|
|
||||||
err->matrix_error.error,
|
|
||||||
static_cast<int>(err->status_code));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit highlightedNotifsRetrieved(std::move(res), mentionsPos);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
|
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
|
||||||
connect(&connectivityTimer_, &QTimer::timeout, this, [=]() {
|
connect(&connectivityTimer_, &QTimer::timeout, this, [=]() {
|
||||||
if (http::client()->access_token().empty()) {
|
if (http::client()->access_token().empty()) {
|
||||||
|
@ -226,8 +199,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
|
|
||||||
connect(this, &ChatPage::loggedOut, this, &ChatPage::logout);
|
connect(this, &ChatPage::loggedOut, this, &ChatPage::logout);
|
||||||
|
|
||||||
connect(top_bar_, &TopRoomBar::showRoomList, splitter, &Splitter::showFullRoomList);
|
connect(
|
||||||
connect(top_bar_, &TopRoomBar::inviteUsers, this, [this](QStringList users) {
|
view_manager_, &TimelineViewManager::showRoomList, splitter, &Splitter::showFullRoomList);
|
||||||
|
connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) {
|
||||||
const auto room_id = current_room_.toStdString();
|
const auto room_id = current_room_.toStdString();
|
||||||
|
|
||||||
for (int ii = 0; ii < users.size(); ++ii) {
|
for (int ii = 0; ii < users.size(); ++ii) {
|
||||||
|
@ -251,8 +225,10 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(room_list_, &RoomList::roomChanged, this, [this](QString room_id) {
|
||||||
|
this->current_room_ = room_id;
|
||||||
|
});
|
||||||
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping);
|
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping);
|
||||||
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo);
|
|
||||||
connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView);
|
connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView);
|
||||||
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit);
|
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit);
|
||||||
connect(
|
connect(
|
||||||
|
@ -487,8 +463,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);
|
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities);
|
this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities);
|
||||||
|
|
||||||
|
@ -588,11 +562,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
});
|
});
|
||||||
connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync);
|
connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync);
|
||||||
connect(this, &ChatPage::syncTags, communitiesList_, &CommunitiesList::syncTags);
|
connect(this, &ChatPage::syncTags, communitiesList_, &CommunitiesList::syncTags);
|
||||||
connect(
|
|
||||||
this, &ChatPage::syncTopBar, this, [this](const std::map<QString, RoomInfo> &updates) {
|
|
||||||
if (updates.find(currentRoom()) != updates.end())
|
|
||||||
changeTopRoomInfo(currentRoom());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Callbacks to update the user info (top left corner of the page).
|
// Callbacks to update the user info (top left corner of the page).
|
||||||
connect(this, &ChatPage::setUserAvatar, user_info_widget_, &UserInfoWidget::setAvatar);
|
connect(this, &ChatPage::setUserAvatar, user_info_widget_, &UserInfoWidget::setAvatar);
|
||||||
|
@ -657,7 +626,6 @@ void
|
||||||
ChatPage::resetUI()
|
ChatPage::resetUI()
|
||||||
{
|
{
|
||||||
room_list_->clear();
|
room_list_->clear();
|
||||||
top_bar_->reset();
|
|
||||||
user_info_widget_->reset();
|
user_info_widget_->reset();
|
||||||
view_manager_->clearAll();
|
view_manager_->clearAll();
|
||||||
|
|
||||||
|
@ -786,46 +754,6 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
||||||
tryInitialSync();
|
tryInitialSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::updateTopBarAvatar(const QString &roomid, const QString &img)
|
|
||||||
{
|
|
||||||
if (current_room_ != roomid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
top_bar_->updateRoomAvatar(img);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::changeTopRoomInfo(const QString &room_id)
|
|
||||||
{
|
|
||||||
if (room_id.isEmpty()) {
|
|
||||||
nhlog::ui()->warn("cannot switch to empty room_id");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto room_info = cache::getRoomInfo({room_id.toStdString()});
|
|
||||||
|
|
||||||
if (room_info.find(room_id) == room_info.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto name = QString::fromStdString(room_info[room_id].name);
|
|
||||||
const auto avatar_url = QString::fromStdString(room_info[room_id].avatar_url);
|
|
||||||
|
|
||||||
top_bar_->updateRoomName(name);
|
|
||||||
top_bar_->updateRoomTopic(QString::fromStdString(room_info[room_id].topic));
|
|
||||||
|
|
||||||
top_bar_->updateRoomAvatarFromName(name);
|
|
||||||
if (!avatar_url.isEmpty())
|
|
||||||
top_bar_->updateRoomAvatar(avatar_url);
|
|
||||||
|
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
nhlog::ui()->error("failed to change top bar room info: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
current_room_ = room_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatPage::showUnreadMessageNotification(int count)
|
ChatPage::showUnreadMessageNotification(int count)
|
||||||
{
|
{
|
||||||
|
@ -967,14 +895,22 @@ ChatPage::sendNotifications(const mtx::responses::Notifications &res)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userSettings_->hasDesktopNotifications()) {
|
if (userSettings_->hasDesktopNotifications()) {
|
||||||
notificationsManager.postNotification(
|
auto info = cache::singleRoomInfo(item.room_id);
|
||||||
room_id,
|
|
||||||
QString::fromStdString(event_id),
|
AvatarProvider::resolve(
|
||||||
QString::fromStdString(
|
QString::fromStdString(info.avatar_url),
|
||||||
cache::singleRoomInfo(item.room_id).name),
|
96,
|
||||||
cache::displayName(room_id, user_id),
|
this,
|
||||||
utils::event_body(item.event),
|
[this, room_id, event_id, item, user_id, info](
|
||||||
cache::getRoomAvatar(room_id));
|
QPixmap image) {
|
||||||
|
notificationsManager.postNotification(
|
||||||
|
room_id,
|
||||||
|
QString::fromStdString(event_id),
|
||||||
|
QString::fromStdString(info.name),
|
||||||
|
cache::displayName(room_id, user_id),
|
||||||
|
utils::event_body(item.event),
|
||||||
|
image.toImage());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
|
@ -1070,7 +1006,6 @@ ChatPage::handleSyncResponse(mtx::responses::Sync res)
|
||||||
|
|
||||||
auto updates = cache::roomUpdates(res);
|
auto updates = cache::roomUpdates(res);
|
||||||
|
|
||||||
emit syncTopBar(updates);
|
|
||||||
emit syncRoomlist(updates);
|
emit syncRoomlist(updates);
|
||||||
|
|
||||||
emit syncUI(res.rooms);
|
emit syncUI(res.rooms);
|
||||||
|
@ -1481,9 +1416,12 @@ ChatPage::getProfileInfo()
|
||||||
void
|
void
|
||||||
ChatPage::hideSideBars()
|
ChatPage::hideSideBars()
|
||||||
{
|
{
|
||||||
communitiesList_->hide();
|
// Don't hide side bar, if we are currently only showing the side bar!
|
||||||
sideBar_->hide();
|
if (view_manager_->getWidget()->isVisible()) {
|
||||||
top_bar_->enableBackButton();
|
communitiesList_->hide();
|
||||||
|
sideBar_->hide();
|
||||||
|
}
|
||||||
|
view_manager_->enableBackButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1493,23 +1431,19 @@ ChatPage::showSideBars()
|
||||||
communitiesList_->show();
|
communitiesList_->show();
|
||||||
|
|
||||||
sideBar_->show();
|
sideBar_->show();
|
||||||
top_bar_->disableBackButton();
|
view_manager_->disableBackButton();
|
||||||
|
content_->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
ChatPage::timelineWidth()
|
ChatPage::timelineWidth()
|
||||||
{
|
{
|
||||||
int sidebarWidth = sideBar_->size().width();
|
int sidebarWidth = sideBar_->minimumSize().width();
|
||||||
sidebarWidth += communitiesList_->size().width();
|
sidebarWidth += communitiesList_->minimumSize().width();
|
||||||
|
nhlog::ui()->info("timelineWidth: {}", size().width() - sidebarWidth);
|
||||||
|
|
||||||
return size().width() - sidebarWidth;
|
return size().width() - sidebarWidth;
|
||||||
}
|
}
|
||||||
bool
|
|
||||||
ChatPage::isSideBarExpanded()
|
|
||||||
{
|
|
||||||
const auto sz = splitter::calculateSidebarSizes(QFont{});
|
|
||||||
return sideBar_->size().width() > sz.normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatPage::initiateLogout()
|
ChatPage::initiateLogout()
|
||||||
|
|
|
@ -49,7 +49,6 @@ class SideBarActions;
|
||||||
class Splitter;
|
class Splitter;
|
||||||
class TextInputWidget;
|
class TextInputWidget;
|
||||||
class TimelineViewManager;
|
class TimelineViewManager;
|
||||||
class TopRoomBar;
|
|
||||||
class UserInfoWidget;
|
class UserInfoWidget;
|
||||||
class UserSettings;
|
class UserSettings;
|
||||||
|
|
||||||
|
@ -82,7 +81,6 @@ public:
|
||||||
|
|
||||||
//! Calculate the width of the message timeline.
|
//! Calculate the width of the message timeline.
|
||||||
uint64_t timelineWidth();
|
uint64_t timelineWidth();
|
||||||
bool isSideBarExpanded();
|
|
||||||
//! Hide the room & group list (if it was visible).
|
//! Hide the room & group list (if it was visible).
|
||||||
void hideSideBars();
|
void hideSideBars();
|
||||||
//! Show the room/group list (if it was visible).
|
//! Show the room/group list (if it was visible).
|
||||||
|
@ -150,7 +148,6 @@ signals:
|
||||||
void syncUI(const mtx::responses::Rooms &rooms);
|
void syncUI(const mtx::responses::Rooms &rooms);
|
||||||
void syncRoomlist(const std::map<QString, RoomInfo> &updates);
|
void syncRoomlist(const std::map<QString, RoomInfo> &updates);
|
||||||
void syncTags(const std::map<QString, RoomInfo> &updates);
|
void syncTags(const std::map<QString, RoomInfo> &updates);
|
||||||
void syncTopBar(const std::map<QString, RoomInfo> &updates);
|
|
||||||
void dropToLoginPageCb(const QString &msg);
|
void dropToLoginPageCb(const QString &msg);
|
||||||
|
|
||||||
void notifyMessage(const QString &roomid,
|
void notifyMessage(const QString &roomid,
|
||||||
|
@ -167,8 +164,6 @@ signals:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showUnreadMessageNotification(int count);
|
void showUnreadMessageNotification(int count);
|
||||||
void updateTopBarAvatar(const QString &roomid, const QString &img);
|
|
||||||
void changeTopRoomInfo(const QString &room_id);
|
|
||||||
void logout();
|
void logout();
|
||||||
void removeRoom(const QString &room_id);
|
void removeRoom(const QString &room_id);
|
||||||
void dropToLoginPage(const QString &msg);
|
void dropToLoginPage(const QString &msg);
|
||||||
|
@ -239,7 +234,6 @@ private:
|
||||||
TimelineViewManager *view_manager_;
|
TimelineViewManager *view_manager_;
|
||||||
SideBarActions *sidebarActions_;
|
SideBarActions *sidebarActions_;
|
||||||
|
|
||||||
TopRoomBar *top_bar_;
|
|
||||||
TextInputWidget *text_input_;
|
TextInputWidget *text_input_;
|
||||||
ActiveCallBar *activeCallBar_;
|
ActiveCallBar *activeCallBar_;
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,8 @@ MainWindow::adjustSideBars()
|
||||||
const uint64_t timelineWidth = chat_page_->timelineWidth();
|
const uint64_t timelineWidth = chat_page_->timelineWidth();
|
||||||
const uint64_t minAvailableWidth = sz.collapsePoint + sz.groups;
|
const uint64_t minAvailableWidth = sz.collapsePoint + sz.groups;
|
||||||
|
|
||||||
if (timelineWidth < minAvailableWidth && !chat_page_->isSideBarExpanded()) {
|
nhlog::ui()->info("timelineWidth: {}, min {}", timelineWidth, minAvailableWidth);
|
||||||
|
if (timelineWidth < minAvailableWidth) {
|
||||||
chat_page_->hideSideBars();
|
chat_page_->hideSideBars();
|
||||||
} else {
|
} else {
|
||||||
chat_page_->showSideBars();
|
chat_page_->showSideBars();
|
||||||
|
@ -339,9 +340,7 @@ MainWindow::openUserProfile(const QString &user_id, const QString &room_id)
|
||||||
void
|
void
|
||||||
MainWindow::openRoomSettings(const QString &room_id)
|
MainWindow::openRoomSettings(const QString &room_id)
|
||||||
{
|
{
|
||||||
const auto roomToSearch = room_id.isEmpty() ? chat_page_->currentRoom() : "";
|
auto dialog = new dialogs::RoomSettings(room_id, this);
|
||||||
|
|
||||||
auto dialog = new dialogs::RoomSettings(roomToSearch, this);
|
|
||||||
|
|
||||||
showDialog(dialog);
|
showDialog(dialog);
|
||||||
}
|
}
|
||||||
|
@ -349,8 +348,7 @@ MainWindow::openRoomSettings(const QString &room_id)
|
||||||
void
|
void
|
||||||
MainWindow::openMemberListDialog(const QString &room_id)
|
MainWindow::openMemberListDialog(const QString &room_id)
|
||||||
{
|
{
|
||||||
const auto roomToSearch = room_id.isEmpty() ? chat_page_->currentRoom() : "";
|
auto dialog = new dialogs::MemberList(room_id, this);
|
||||||
auto dialog = new dialogs::MemberList(roomToSearch, this);
|
|
||||||
|
|
||||||
showDialog(dialog);
|
showDialog(dialog);
|
||||||
}
|
}
|
||||||
|
@ -358,11 +356,9 @@ MainWindow::openMemberListDialog(const QString &room_id)
|
||||||
void
|
void
|
||||||
MainWindow::openLeaveRoomDialog(const QString &room_id)
|
MainWindow::openLeaveRoomDialog(const QString &room_id)
|
||||||
{
|
{
|
||||||
auto roomToLeave = room_id.isEmpty() ? chat_page_->currentRoom() : room_id;
|
|
||||||
|
|
||||||
auto dialog = new dialogs::LeaveRoom(this);
|
auto dialog = new dialogs::LeaveRoom(this);
|
||||||
connect(dialog, &dialogs::LeaveRoom::leaving, this, [this, roomToLeave]() {
|
connect(dialog, &dialogs::LeaveRoom::leaving, this, [this, room_id]() {
|
||||||
chat_page_->leaveRoom(roomToLeave);
|
chat_page_->leaveRoom(room_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
showDialog(dialog);
|
showDialog(dialog);
|
||||||
|
|
|
@ -68,14 +68,14 @@ public:
|
||||||
static MainWindow *instance() { return instance_; };
|
static MainWindow *instance() { return instance_; };
|
||||||
void saveCurrentWindowSize();
|
void saveCurrentWindowSize();
|
||||||
|
|
||||||
void openLeaveRoomDialog(const QString &room_id = "");
|
void openLeaveRoomDialog(const QString &room_id);
|
||||||
void openInviteUsersDialog(std::function<void(const QStringList &invitees)> callback);
|
void openInviteUsersDialog(std::function<void(const QStringList &invitees)> callback);
|
||||||
void openCreateRoomDialog(
|
void openCreateRoomDialog(
|
||||||
std::function<void(const mtx::requests::CreateRoom &request)> callback);
|
std::function<void(const mtx::requests::CreateRoom &request)> callback);
|
||||||
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
|
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
|
||||||
void openLogoutDialog();
|
void openLogoutDialog();
|
||||||
void openRoomSettings(const QString &room_id = "");
|
void openRoomSettings(const QString &room_id);
|
||||||
void openMemberListDialog(const QString &room_id = "");
|
void openMemberListDialog(const QString &room_id);
|
||||||
void openUserProfile(const QString &user_id, const QString &room_id);
|
void openUserProfile(const QString &user_id, const QString &room_id);
|
||||||
void openReadReceiptsDialog(const QString &event_id);
|
void openReadReceiptsDialog(const QString &event_id);
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,16 @@ MxcImageResponse::run()
|
||||||
auto data = cache::image(fileName);
|
auto data = cache::image(fileName);
|
||||||
if (!data.isNull()) {
|
if (!data.isNull()) {
|
||||||
m_image = utils::readImage(&data);
|
m_image = utils::readImage(&data);
|
||||||
m_image = m_image.scaled(
|
|
||||||
m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
m_image.setText("mxc url", "mxc://" + m_id);
|
|
||||||
|
|
||||||
if (!m_image.isNull()) {
|
if (!m_image.isNull()) {
|
||||||
emit finished();
|
m_image = m_image.scaled(
|
||||||
return;
|
m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
m_image.setText("mxc url", "mxc://" + m_id);
|
||||||
|
|
||||||
|
if (!m_image.isNull()) {
|
||||||
|
emit finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +37,7 @@ MxcImageResponse::run()
|
||||||
opts.method = "crop";
|
opts.method = "crop";
|
||||||
http::client()->get_thumbnail(
|
http::client()->get_thumbnail(
|
||||||
opts, [this, fileName](const std::string &res, mtx::http::RequestErr err) {
|
opts, [this, fileName](const std::string &res, mtx::http::RequestErr err) {
|
||||||
if (err) {
|
if (err || res.empty()) {
|
||||||
nhlog::net()->error("Failed to download image {}",
|
nhlog::net()->error("Failed to download image {}",
|
||||||
m_id.toStdString());
|
m_id.toStdString());
|
||||||
m_error = "Failed download";
|
m_error = "Failed download";
|
||||||
|
@ -46,6 +49,10 @@ MxcImageResponse::run()
|
||||||
auto data = QByteArray(res.data(), res.size());
|
auto data = QByteArray(res.data(), res.size());
|
||||||
cache::saveImage(fileName, data);
|
cache::saveImage(fileName, data);
|
||||||
m_image = utils::readImage(&data);
|
m_image = utils::readImage(&data);
|
||||||
|
if (!m_image.isNull()) {
|
||||||
|
m_image = m_image.scaled(
|
||||||
|
m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
m_image.setText("mxc url", "mxc://" + m_id);
|
m_image.setText("mxc url", "mxc://" + m_id);
|
||||||
|
|
||||||
emit finished();
|
emit finished();
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
/*
|
|
||||||
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QAction>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPen>
|
|
||||||
#include <QPoint>
|
|
||||||
#include <QStyle>
|
|
||||||
#include <QStyleOption>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "MainWindow.h"
|
|
||||||
#include "TopRoomBar.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "ui/Avatar.h"
|
|
||||||
#include "ui/FlatButton.h"
|
|
||||||
#include "ui/Menu.h"
|
|
||||||
#include "ui/OverlayModal.h"
|
|
||||||
#include "ui/TextLabel.h"
|
|
||||||
|
|
||||||
TopRoomBar::TopRoomBar(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, buttonSize_{32}
|
|
||||||
{
|
|
||||||
QFont f;
|
|
||||||
f.setPointSizeF(f.pointSizeF());
|
|
||||||
|
|
||||||
const int fontHeight = QFontMetrics(f).height();
|
|
||||||
const int widgetMargin = fontHeight / 3;
|
|
||||||
const int contentHeight = fontHeight * 3;
|
|
||||||
|
|
||||||
setFixedHeight(contentHeight + widgetMargin);
|
|
||||||
|
|
||||||
topLayout_ = new QHBoxLayout(this);
|
|
||||||
topLayout_->setSpacing(widgetMargin);
|
|
||||||
topLayout_->setContentsMargins(
|
|
||||||
2 * widgetMargin, widgetMargin, 2 * widgetMargin, widgetMargin);
|
|
||||||
|
|
||||||
avatar_ = new Avatar(this, fontHeight * 2);
|
|
||||||
avatar_->setLetter("");
|
|
||||||
|
|
||||||
textLayout_ = new QVBoxLayout();
|
|
||||||
textLayout_->setSpacing(0);
|
|
||||||
textLayout_->setMargin(0);
|
|
||||||
|
|
||||||
QFont roomFont;
|
|
||||||
roomFont.setPointSizeF(roomFont.pointSizeF() * 1.1);
|
|
||||||
roomFont.setWeight(QFont::Medium);
|
|
||||||
|
|
||||||
nameLabel_ = new QLabel(this);
|
|
||||||
nameLabel_->setFont(roomFont);
|
|
||||||
nameLabel_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
|
||||||
|
|
||||||
QFont descriptionFont;
|
|
||||||
|
|
||||||
topicLabel_ = new TextLabel(this);
|
|
||||||
topicLabel_->setLineWrapMode(QTextEdit::NoWrap);
|
|
||||||
topicLabel_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
topicLabel_->setFont(descriptionFont);
|
|
||||||
topicLabel_->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
topicLabel_->setOpenExternalLinks(true);
|
|
||||||
topicLabel_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
|
||||||
|
|
||||||
textLayout_->addWidget(nameLabel_);
|
|
||||||
textLayout_->addWidget(topicLabel_);
|
|
||||||
|
|
||||||
settingsBtn_ = new FlatButton(this);
|
|
||||||
settingsBtn_->setToolTip(tr("Room options"));
|
|
||||||
settingsBtn_->setFixedSize(buttonSize_, buttonSize_);
|
|
||||||
settingsBtn_->setCornerRadius(buttonSize_ / 2);
|
|
||||||
|
|
||||||
mentionsBtn_ = new FlatButton(this);
|
|
||||||
mentionsBtn_->setToolTip(tr("Mentions"));
|
|
||||||
mentionsBtn_->setFixedSize(buttonSize_, buttonSize_);
|
|
||||||
mentionsBtn_->setCornerRadius(buttonSize_ / 2);
|
|
||||||
|
|
||||||
QIcon settings_icon;
|
|
||||||
settings_icon.addFile(":/icons/icons/ui/vertical-ellipsis.png");
|
|
||||||
settingsBtn_->setIcon(settings_icon);
|
|
||||||
settingsBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
|
|
||||||
|
|
||||||
QIcon mentions_icon;
|
|
||||||
mentions_icon.addFile(":/icons/icons/ui/at-solid.svg");
|
|
||||||
mentionsBtn_->setIcon(mentions_icon);
|
|
||||||
mentionsBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
|
|
||||||
|
|
||||||
backBtn_ = new FlatButton(this);
|
|
||||||
backBtn_->setFixedSize(buttonSize_, buttonSize_);
|
|
||||||
backBtn_->setCornerRadius(buttonSize_ / 2);
|
|
||||||
|
|
||||||
QIcon backIcon;
|
|
||||||
backIcon.addFile(":/icons/icons/ui/angle-pointing-to-left.png");
|
|
||||||
backBtn_->setIcon(backIcon);
|
|
||||||
backBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
|
|
||||||
backBtn_->hide();
|
|
||||||
|
|
||||||
connect(backBtn_, &QPushButton::clicked, this, &TopRoomBar::showRoomList);
|
|
||||||
|
|
||||||
topLayout_->addWidget(avatar_);
|
|
||||||
topLayout_->addWidget(backBtn_);
|
|
||||||
topLayout_->addLayout(textLayout_, 1);
|
|
||||||
topLayout_->addWidget(mentionsBtn_, 0, Qt::AlignRight);
|
|
||||||
topLayout_->addWidget(settingsBtn_, 0, Qt::AlignRight);
|
|
||||||
|
|
||||||
menu_ = new Menu(this);
|
|
||||||
|
|
||||||
inviteUsers_ = new QAction(tr("Invite users"), this);
|
|
||||||
connect(inviteUsers_, &QAction::triggered, this, [this]() {
|
|
||||||
MainWindow::instance()->openInviteUsersDialog(
|
|
||||||
[this](const QStringList &invitees) { emit inviteUsers(invitees); });
|
|
||||||
});
|
|
||||||
|
|
||||||
roomMembers_ = new QAction(tr("Members"), this);
|
|
||||||
connect(roomMembers_, &QAction::triggered, this, []() {
|
|
||||||
MainWindow::instance()->openMemberListDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
leaveRoom_ = new QAction(tr("Leave room"), this);
|
|
||||||
connect(leaveRoom_, &QAction::triggered, this, []() {
|
|
||||||
MainWindow::instance()->openLeaveRoomDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
roomSettings_ = new QAction(tr("Settings"), this);
|
|
||||||
connect(roomSettings_, &QAction::triggered, this, []() {
|
|
||||||
MainWindow::instance()->openRoomSettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
menu_->addAction(inviteUsers_);
|
|
||||||
menu_->addAction(roomMembers_);
|
|
||||||
menu_->addAction(leaveRoom_);
|
|
||||||
menu_->addAction(roomSettings_);
|
|
||||||
|
|
||||||
connect(settingsBtn_, &QPushButton::clicked, this, [this]() {
|
|
||||||
auto pos = mapToGlobal(settingsBtn_->pos());
|
|
||||||
menu_->popup(
|
|
||||||
QPoint(pos.x() + buttonSize_ - menu_->sizeHint().width(), pos.y() + buttonSize_));
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(mentionsBtn_, &QPushButton::clicked, this, [this]() {
|
|
||||||
auto pos = mapToGlobal(mentionsBtn_->pos());
|
|
||||||
emit mentionsClicked(pos);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::enableBackButton()
|
|
||||||
{
|
|
||||||
avatar_->hide();
|
|
||||||
backBtn_->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::disableBackButton()
|
|
||||||
{
|
|
||||||
avatar_->show();
|
|
||||||
backBtn_->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::updateRoomAvatarFromName(const QString &name)
|
|
||||||
{
|
|
||||||
avatar_->setLetter(utils::firstChar(name));
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::reset()
|
|
||||||
{
|
|
||||||
nameLabel_->setText("");
|
|
||||||
topicLabel_->setText("");
|
|
||||||
avatar_->setLetter("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::updateRoomAvatar(const QString &avatar_image)
|
|
||||||
{
|
|
||||||
avatar_->setImage(avatar_image);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::updateRoomName(const QString &name)
|
|
||||||
{
|
|
||||||
nameLabel_->setText(name);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::updateRoomTopic(QString topic)
|
|
||||||
{
|
|
||||||
topic.replace(conf::strings::url_regex, conf::strings::url_html);
|
|
||||||
topicLabel_->clearLinks();
|
|
||||||
topicLabel_->setHtml(topic);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::mousePressEvent(QMouseEvent *)
|
|
||||||
{
|
|
||||||
if (roomSettings_ != nullptr)
|
|
||||||
roomSettings_->trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TopRoomBar::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QColor>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class Avatar;
|
|
||||||
class FlatButton;
|
|
||||||
class Menu;
|
|
||||||
class TextLabel;
|
|
||||||
class OverlayModal;
|
|
||||||
|
|
||||||
class QLabel;
|
|
||||||
class QHBoxLayout;
|
|
||||||
class QVBoxLayout;
|
|
||||||
|
|
||||||
class TopRoomBar : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor)
|
|
||||||
|
|
||||||
public:
|
|
||||||
TopRoomBar(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void updateRoomAvatar(const QString &avatar_image);
|
|
||||||
void updateRoomName(const QString &name);
|
|
||||||
void updateRoomTopic(QString topic);
|
|
||||||
void updateRoomAvatarFromName(const QString &name);
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
QColor borderColor() const { return borderColor_; }
|
|
||||||
void setBorderColor(QColor &color) { borderColor_ = color; }
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
//! Add a "back-arrow" button that can switch to roomlist only view.
|
|
||||||
void enableBackButton();
|
|
||||||
//! Replace the "back-arrow" button with the avatar of the room.
|
|
||||||
void disableBackButton();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void inviteUsers(QStringList users);
|
|
||||||
void showRoomList();
|
|
||||||
void mentionsClicked(const QPoint &pos);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *) override;
|
|
||||||
void paintEvent(QPaintEvent *) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QHBoxLayout *topLayout_ = nullptr;
|
|
||||||
QVBoxLayout *textLayout_ = nullptr;
|
|
||||||
|
|
||||||
QLabel *nameLabel_ = nullptr;
|
|
||||||
TextLabel *topicLabel_ = nullptr;
|
|
||||||
|
|
||||||
Menu *menu_;
|
|
||||||
QAction *leaveRoom_ = nullptr;
|
|
||||||
QAction *roomMembers_ = nullptr;
|
|
||||||
QAction *roomSettings_ = nullptr;
|
|
||||||
QAction *inviteUsers_ = nullptr;
|
|
||||||
|
|
||||||
FlatButton *settingsBtn_;
|
|
||||||
FlatButton *mentionsBtn_;
|
|
||||||
FlatButton *backBtn_;
|
|
||||||
|
|
||||||
Avatar *avatar_;
|
|
||||||
|
|
||||||
int buttonSize_;
|
|
||||||
|
|
||||||
QColor borderColor_;
|
|
||||||
};
|
|
|
@ -513,9 +513,6 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
||||||
callsLabel->setFont(font);
|
callsLabel->setFont(font);
|
||||||
useStunServer_ = new Toggle{this};
|
useStunServer_ = new Toggle{this};
|
||||||
|
|
||||||
defaultAudioSourceValue_ = new QLabel(this);
|
|
||||||
defaultAudioSourceValue_->setFont(font);
|
|
||||||
|
|
||||||
auto encryptionLabel_ = new QLabel{tr("ENCRYPTION"), this};
|
auto encryptionLabel_ = new QLabel{tr("ENCRYPTION"), this};
|
||||||
encryptionLabel_->setFixedHeight(encryptionLabel_->minimumHeight() + LayoutTopMargin);
|
encryptionLabel_->setFixedHeight(encryptionLabel_->minimumHeight() + LayoutTopMargin);
|
||||||
encryptionLabel_->setAlignment(Qt::AlignBottom);
|
encryptionLabel_->setAlignment(Qt::AlignBottom);
|
||||||
|
@ -652,7 +649,6 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
||||||
boxWrap(tr("Allow fallback call assist server"),
|
boxWrap(tr("Allow fallback call assist server"),
|
||||||
useStunServer_,
|
useStunServer_,
|
||||||
tr("Will use turn.matrix.org as assist when your home server does not offer one."));
|
tr("Will use turn.matrix.org as assist when your home server does not offer one."));
|
||||||
boxWrap(tr("Default audio source device"), defaultAudioSourceValue_);
|
|
||||||
|
|
||||||
formLayout_->addRow(encryptionLabel_);
|
formLayout_->addRow(encryptionLabel_);
|
||||||
formLayout_->addRow(new HorizontalLine{this});
|
formLayout_->addRow(new HorizontalLine{this});
|
||||||
|
@ -813,7 +809,6 @@ UserSettingsPage::showEvent(QShowEvent *)
|
||||||
deviceIdValue_->setText(QString::fromStdString(http::client()->device_id()));
|
deviceIdValue_->setText(QString::fromStdString(http::client()->device_id()));
|
||||||
timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth());
|
timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth());
|
||||||
useStunServer_->setState(!settings_->useStunServer());
|
useStunServer_->setState(!settings_->useStunServer());
|
||||||
defaultAudioSourceValue_->setText(settings_->defaultAudioSource());
|
|
||||||
|
|
||||||
deviceFingerprintValue_->setText(
|
deviceFingerprintValue_->setText(
|
||||||
utils::humanReadableFingerprint(olm::client()->identity_keys().ed25519));
|
utils::humanReadableFingerprint(olm::client()->identity_keys().ed25519));
|
||||||
|
|
|
@ -250,7 +250,6 @@ private:
|
||||||
Toggle *decryptSidebar_;
|
Toggle *decryptSidebar_;
|
||||||
QLabel *deviceFingerprintValue_;
|
QLabel *deviceFingerprintValue_;
|
||||||
QLabel *deviceIdValue_;
|
QLabel *deviceIdValue_;
|
||||||
QLabel *defaultAudioSourceValue_;
|
|
||||||
|
|
||||||
QComboBox *themeCombo_;
|
QComboBox *themeCombo_;
|
||||||
QComboBox *scaleFactorCombo_;
|
QComboBox *scaleFactorCombo_;
|
||||||
|
|
|
@ -426,8 +426,12 @@ WebRTCSession::acceptICECandidates(
|
||||||
for (const auto &c : candidates) {
|
for (const auto &c : candidates) {
|
||||||
nhlog::ui()->debug(
|
nhlog::ui()->debug(
|
||||||
"WebRTC: remote candidate: (m-line:{}):{}", c.sdpMLineIndex, c.candidate);
|
"WebRTC: remote candidate: (m-line:{}):{}", c.sdpMLineIndex, c.candidate);
|
||||||
g_signal_emit_by_name(
|
if (!c.candidate.empty()) {
|
||||||
webrtc_, "add-ice-candidate", c.sdpMLineIndex, c.candidate.c_str());
|
g_signal_emit_by_name(webrtc_,
|
||||||
|
"add-ice-candidate",
|
||||||
|
c.sdpMLineIndex,
|
||||||
|
c.candidate.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +495,7 @@ WebRTCSession::startPipeline(int opusPayloadType)
|
||||||
}
|
}
|
||||||
|
|
||||||
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe_));
|
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe_));
|
||||||
gst_bus_add_watch(bus, newBusMessage, this);
|
busWatchId_ = gst_bus_add_watch(bus, newBusMessage, this);
|
||||||
gst_object_unref(bus);
|
gst_object_unref(bus);
|
||||||
emit stateChanged(State::INITIATED);
|
emit stateChanged(State::INITIATED);
|
||||||
return true;
|
return true;
|
||||||
|
@ -597,6 +601,8 @@ WebRTCSession::end()
|
||||||
gst_element_set_state(pipe_, GST_STATE_NULL);
|
gst_element_set_state(pipe_, GST_STATE_NULL);
|
||||||
gst_object_unref(pipe_);
|
gst_object_unref(pipe_);
|
||||||
pipe_ = nullptr;
|
pipe_ = nullptr;
|
||||||
|
g_source_remove(busWatchId_);
|
||||||
|
busWatchId_ = 0;
|
||||||
}
|
}
|
||||||
webrtc_ = nullptr;
|
webrtc_ = nullptr;
|
||||||
if (state_ != State::DISCONNECTED)
|
if (state_ != State::DISCONNECTED)
|
||||||
|
|
|
@ -64,10 +64,11 @@ private slots:
|
||||||
private:
|
private:
|
||||||
WebRTCSession();
|
WebRTCSession();
|
||||||
|
|
||||||
bool initialised_ = false;
|
bool initialised_ = false;
|
||||||
State state_ = State::DISCONNECTED;
|
State state_ = State::DISCONNECTED;
|
||||||
GstElement *pipe_ = nullptr;
|
GstElement *pipe_ = nullptr;
|
||||||
GstElement *webrtc_ = nullptr;
|
GstElement *webrtc_ = nullptr;
|
||||||
|
unsigned int busWatchId_ = 0;
|
||||||
std::string stunServer_;
|
std::string stunServer_;
|
||||||
std::vector<std::string> turnServers_;
|
std::vector<std::string> turnServers_;
|
||||||
GList *audioSources_ = nullptr;
|
GList *audioSources_ = nullptr;
|
||||||
|
|
|
@ -517,6 +517,25 @@ TimelineModel::fetchMore(const QModelIndex &)
|
||||||
events.fetchMore();
|
events.fetchMore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineModel::syncState(const mtx::responses::State &s)
|
||||||
|
{
|
||||||
|
using namespace mtx::events;
|
||||||
|
|
||||||
|
for (const auto &e : s.events) {
|
||||||
|
if (std::holds_alternative<StateEvent<state::Avatar>>(e))
|
||||||
|
emit roomAvatarUrlChanged();
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Name>>(e))
|
||||||
|
emit roomNameChanged();
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
|
||||||
|
emit roomTopicChanged();
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
|
||||||
|
emit roomAvatarUrlChanged();
|
||||||
|
emit roomNameChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
|
TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
|
||||||
{
|
{
|
||||||
|
@ -526,6 +545,7 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
|
||||||
events.handleSync(timeline);
|
events.handleSync(timeline);
|
||||||
|
|
||||||
using namespace mtx::events;
|
using namespace mtx::events;
|
||||||
|
|
||||||
for (auto e : timeline.events) {
|
for (auto e : timeline.events) {
|
||||||
if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) {
|
if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) {
|
||||||
MegolmSessionIndex index;
|
MegolmSessionIndex index;
|
||||||
|
@ -549,6 +569,16 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
|
||||||
emit newCallEvent(event);
|
emit newCallEvent(event);
|
||||||
},
|
},
|
||||||
e);
|
e);
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Avatar>>(e))
|
||||||
|
emit roomAvatarUrlChanged();
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Name>>(e))
|
||||||
|
emit roomNameChanged();
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
|
||||||
|
emit roomTopicChanged();
|
||||||
|
else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
|
||||||
|
emit roomAvatarUrlChanged();
|
||||||
|
emit roomNameChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateLastMessage();
|
updateLastMessage();
|
||||||
}
|
}
|
||||||
|
@ -689,12 +719,6 @@ TimelineModel::formatDateSeparator(QDate date) const
|
||||||
return date.toString(fmt);
|
return date.toString(fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
|
||||||
TimelineModel::escapeEmoji(QString str) const
|
|
||||||
{
|
|
||||||
return utils::replaceEmoji(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineModel::viewRawMessage(QString id) const
|
TimelineModel::viewRawMessage(QString id) const
|
||||||
{
|
{
|
||||||
|
@ -1359,7 +1383,7 @@ TimelineModel::formatTypingUsers(const std::vector<QString> &users, QColor bg)
|
||||||
QStringList uidWithoutLast;
|
QStringList uidWithoutLast;
|
||||||
|
|
||||||
auto formatUser = [this, bg](const QString &user_id) -> QString {
|
auto formatUser = [this, bg](const QString &user_id) -> QString {
|
||||||
auto uncoloredUsername = escapeEmoji(displayName(user_id));
|
auto uncoloredUsername = utils::replaceEmoji(displayName(user_id));
|
||||||
QString prefix =
|
QString prefix =
|
||||||
QString("<font color=\"%1\">").arg(manager_->userColor(user_id, bg).name());
|
QString("<font color=\"%1\">").arg(manager_->userColor(user_id, bg).name());
|
||||||
|
|
||||||
|
@ -1409,7 +1433,7 @@ TimelineModel::formatJoinRuleEvent(QString id)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
QString user = QString::fromStdString(event->sender);
|
QString user = QString::fromStdString(event->sender);
|
||||||
QString name = escapeEmoji(displayName(user));
|
QString name = utils::replaceEmoji(displayName(user));
|
||||||
|
|
||||||
switch (event->content.join_rule) {
|
switch (event->content.join_rule) {
|
||||||
case mtx::events::state::JoinRule::Public:
|
case mtx::events::state::JoinRule::Public:
|
||||||
|
@ -1434,7 +1458,7 @@ TimelineModel::formatGuestAccessEvent(QString id)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
QString user = QString::fromStdString(event->sender);
|
QString user = QString::fromStdString(event->sender);
|
||||||
QString name = escapeEmoji(displayName(user));
|
QString name = utils::replaceEmoji(displayName(user));
|
||||||
|
|
||||||
switch (event->content.guest_access) {
|
switch (event->content.guest_access) {
|
||||||
case mtx::events::state::AccessState::CanJoin:
|
case mtx::events::state::AccessState::CanJoin:
|
||||||
|
@ -1459,7 +1483,7 @@ TimelineModel::formatHistoryVisibilityEvent(QString id)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
QString user = QString::fromStdString(event->sender);
|
QString user = QString::fromStdString(event->sender);
|
||||||
QString name = escapeEmoji(displayName(user));
|
QString name = utils::replaceEmoji(displayName(user));
|
||||||
|
|
||||||
switch (event->content.history_visibility) {
|
switch (event->content.history_visibility) {
|
||||||
case mtx::events::state::Visibility::WorldReadable:
|
case mtx::events::state::Visibility::WorldReadable:
|
||||||
|
@ -1492,7 +1516,7 @@ TimelineModel::formatPowerLevelEvent(QString id)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
QString user = QString::fromStdString(event->sender);
|
QString user = QString::fromStdString(event->sender);
|
||||||
QString name = escapeEmoji(displayName(user));
|
QString name = utils::replaceEmoji(displayName(user));
|
||||||
|
|
||||||
// TODO: power levels rendering is actually a bit complex. work on this later.
|
// TODO: power levels rendering is actually a bit complex. work on this later.
|
||||||
return tr("%1 has changed the room's permissions.").arg(name);
|
return tr("%1 has changed the room's permissions.").arg(name);
|
||||||
|
@ -1521,7 +1545,7 @@ TimelineModel::formatMemberEvent(QString id)
|
||||||
}
|
}
|
||||||
|
|
||||||
QString user = QString::fromStdString(event->state_key);
|
QString user = QString::fromStdString(event->state_key);
|
||||||
QString name = escapeEmoji(displayName(user));
|
QString name = utils::replaceEmoji(displayName(user));
|
||||||
QString rendered;
|
QString rendered;
|
||||||
|
|
||||||
// see table https://matrix.org/docs/spec/client_server/latest#m-room-member
|
// see table https://matrix.org/docs/spec/client_server/latest#m-room-member
|
||||||
|
@ -1594,3 +1618,37 @@ TimelineModel::formatMemberEvent(QString id)
|
||||||
|
|
||||||
return rendered;
|
return rendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
TimelineModel::roomName() const
|
||||||
|
{
|
||||||
|
auto info = cache::getRoomInfo({room_id_.toStdString()});
|
||||||
|
|
||||||
|
if (!info.count(room_id_))
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return QString::fromStdString(info[room_id_].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
TimelineModel::roomAvatarUrl() const
|
||||||
|
{
|
||||||
|
auto info = cache::getRoomInfo({room_id_.toStdString()});
|
||||||
|
|
||||||
|
if (!info.count(room_id_))
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return QString::fromStdString(info[room_id_].avatar_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
TimelineModel::roomTopic() const
|
||||||
|
{
|
||||||
|
auto info = cache::getRoomInfo({room_id_.toStdString()});
|
||||||
|
|
||||||
|
if (!info.count(room_id_))
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return utils::replaceEmoji(utils::linkifyMessage(
|
||||||
|
utils::escapeBlacklistedHtml(QString::fromStdString(info[room_id_].topic))));
|
||||||
|
}
|
||||||
|
|
|
@ -137,6 +137,9 @@ class TimelineModel : public QAbstractListModel
|
||||||
Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply)
|
Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged)
|
bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged)
|
||||||
|
Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
|
||||||
|
Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged)
|
||||||
|
Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TimelineModel(TimelineViewManager *manager,
|
explicit TimelineModel(TimelineViewManager *manager,
|
||||||
|
@ -194,7 +197,6 @@ public:
|
||||||
Q_INVOKABLE QString formatGuestAccessEvent(QString id);
|
Q_INVOKABLE QString formatGuestAccessEvent(QString id);
|
||||||
Q_INVOKABLE QString formatPowerLevelEvent(QString id);
|
Q_INVOKABLE QString formatPowerLevelEvent(QString id);
|
||||||
|
|
||||||
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
|
||||||
Q_INVOKABLE void viewRawMessage(QString id) const;
|
Q_INVOKABLE void viewRawMessage(QString id) const;
|
||||||
Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
|
Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
|
||||||
Q_INVOKABLE void openUserProfile(QString userid) const;
|
Q_INVOKABLE void openUserProfile(QString userid) const;
|
||||||
|
@ -217,6 +219,7 @@ public:
|
||||||
|
|
||||||
void updateLastMessage();
|
void updateLastMessage();
|
||||||
void addEvents(const mtx::responses::Timeline &events);
|
void addEvents(const mtx::responses::Timeline &events);
|
||||||
|
void syncState(const mtx::responses::State &state);
|
||||||
template<class T>
|
template<class T>
|
||||||
void sendMessageEvent(const T &content, mtx::events::EventType eventType);
|
void sendMessageEvent(const T &content, mtx::events::EventType eventType);
|
||||||
RelatedInfo relatedInfo(QString id);
|
RelatedInfo relatedInfo(QString id);
|
||||||
|
@ -253,6 +256,11 @@ public slots:
|
||||||
void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; }
|
void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; }
|
||||||
void clearTimeline() { events.clearTimeline(); }
|
void clearTimeline() { events.clearTimeline(); }
|
||||||
|
|
||||||
|
QString roomName() const;
|
||||||
|
QString roomTopic() const;
|
||||||
|
QString roomAvatarUrl() const;
|
||||||
|
QString roomId() const { return room_id_; }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void addPendingMessage(mtx::events::collections::TimelineEvents event);
|
void addPendingMessage(mtx::events::collections::TimelineEvents event);
|
||||||
|
|
||||||
|
@ -270,6 +278,10 @@ signals:
|
||||||
void newMessageToSend(mtx::events::collections::TimelineEvents event);
|
void newMessageToSend(mtx::events::collections::TimelineEvents event);
|
||||||
void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
|
void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
|
||||||
|
|
||||||
|
void roomNameChanged();
|
||||||
|
void roomTopicChanged();
|
||||||
|
void roomAvatarUrlChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendEncryptedMessageEvent(const std::string &txn_id,
|
void sendEncryptedMessageEvent(const std::string &txn_id,
|
||||||
nlohmann::json content,
|
nlohmann::json content,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "ColorImageProvider.h"
|
#include "ColorImageProvider.h"
|
||||||
#include "DelegateChooser.h"
|
#include "DelegateChooser.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
#include "MxcImageProvider.h"
|
#include "MxcImageProvider.h"
|
||||||
#include "UserSettingsPage.h"
|
#include "UserSettingsPage.h"
|
||||||
|
@ -76,7 +77,7 @@ TimelineViewManager::userStatus(QString id) const
|
||||||
|
|
||||||
TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings,
|
TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings,
|
||||||
CallManager *callManager,
|
CallManager *callManager,
|
||||||
QWidget *parent)
|
ChatPage *parent)
|
||||||
: imgProvider(new MxcImageProvider())
|
: imgProvider(new MxcImageProvider())
|
||||||
, colorImgProvider(new ColorImageProvider())
|
, colorImgProvider(new ColorImageProvider())
|
||||||
, blurhashProvider(new BlurhashProvider())
|
, blurhashProvider(new BlurhashProvider())
|
||||||
|
@ -131,15 +132,12 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin
|
||||||
view->engine()->addImageProvider("blurhash", blurhashProvider);
|
view->engine()->addImageProvider("blurhash", blurhashProvider);
|
||||||
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
|
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
|
||||||
|
|
||||||
connect(dynamic_cast<ChatPage *>(parent),
|
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
|
||||||
&ChatPage::themeChanged,
|
connect(parent,
|
||||||
this,
|
|
||||||
&TimelineViewManager::updateColorPalette);
|
|
||||||
connect(dynamic_cast<ChatPage *>(parent),
|
|
||||||
&ChatPage::decryptSidebarChanged,
|
&ChatPage::decryptSidebarChanged,
|
||||||
this,
|
this,
|
||||||
&TimelineViewManager::updateEncryptedDescriptions);
|
&TimelineViewManager::updateEncryptedDescriptions);
|
||||||
connect(dynamic_cast<ChatPage *>(parent), &ChatPage::loggedOut, this, [this]() {
|
connect(parent, &ChatPage::loggedOut, this, [this]() {
|
||||||
isInitialSync_ = true;
|
isInitialSync_ = true;
|
||||||
emit initialSyncChanged(true);
|
emit initialSyncChanged(true);
|
||||||
});
|
});
|
||||||
|
@ -157,6 +155,7 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
|
||||||
&TimelineModel::newCallEvent,
|
&TimelineModel::newCallEvent,
|
||||||
callManager_,
|
callManager_,
|
||||||
&CallManager::syncEvent);
|
&CallManager::syncEvent);
|
||||||
|
room_model->syncState(room.state);
|
||||||
room_model->addEvents(room.timeline);
|
room_model->addEvents(room.timeline);
|
||||||
if (!isInitialSync_)
|
if (!isInitialSync_)
|
||||||
disconnect(room_model.data(),
|
disconnect(room_model.data(),
|
||||||
|
@ -207,6 +206,12 @@ TimelineViewManager::setHistoryView(const QString &room_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
TimelineViewManager::escapeEmoji(QString str) const
|
||||||
|
{
|
||||||
|
return utils::replaceEmoji(str);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const
|
TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const
|
||||||
{
|
{
|
||||||
|
@ -245,6 +250,28 @@ TimelineViewManager::openLink(QString link) const
|
||||||
QDesktopServices::openUrl(link);
|
QDesktopServices::openUrl(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineViewManager::openInviteUsersDialog()
|
||||||
|
{
|
||||||
|
MainWindow::instance()->openInviteUsersDialog(
|
||||||
|
[this](const QStringList &invitees) { emit inviteUsers(invitees); });
|
||||||
|
}
|
||||||
|
void
|
||||||
|
TimelineViewManager::openMemberListDialog() const
|
||||||
|
{
|
||||||
|
MainWindow::instance()->openMemberListDialog(timeline_->roomId());
|
||||||
|
}
|
||||||
|
void
|
||||||
|
TimelineViewManager::openLeaveRoomDialog() const
|
||||||
|
{
|
||||||
|
MainWindow::instance()->openLeaveRoomDialog(timeline_->roomId());
|
||||||
|
}
|
||||||
|
void
|
||||||
|
TimelineViewManager::openRoomSettings() const
|
||||||
|
{
|
||||||
|
MainWindow::instance()->openRoomSettings(timeline_->roomId());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineViewManager::updateReadReceipts(const QString &room_id,
|
TimelineViewManager::updateReadReceipts(const QString &room_id,
|
||||||
const std::vector<QString> &event_ids)
|
const std::vector<QString> &event_ids)
|
||||||
|
|
|
@ -21,6 +21,7 @@ class BlurhashProvider;
|
||||||
class CallManager;
|
class CallManager;
|
||||||
class ColorImageProvider;
|
class ColorImageProvider;
|
||||||
class UserSettings;
|
class UserSettings;
|
||||||
|
class ChatPage;
|
||||||
|
|
||||||
class TimelineViewManager : public QObject
|
class TimelineViewManager : public QObject
|
||||||
{
|
{
|
||||||
|
@ -30,11 +31,13 @@ class TimelineViewManager : public QObject
|
||||||
TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged)
|
TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
|
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimelineViewManager(QSharedPointer<UserSettings> userSettings,
|
TimelineViewManager(QSharedPointer<UserSettings> userSettings,
|
||||||
CallManager *callManager,
|
CallManager *callManager,
|
||||||
QWidget *parent = nullptr);
|
ChatPage *parent = nullptr);
|
||||||
QWidget *getWidget() const { return container; }
|
QWidget *getWidget() const { return container; }
|
||||||
|
|
||||||
void sync(const mtx::responses::Rooms &rooms);
|
void sync(const mtx::responses::Rooms &rooms);
|
||||||
|
@ -44,14 +47,21 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
|
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
|
||||||
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
|
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
|
||||||
|
bool isNarrowView() const { return isNarrowView_; }
|
||||||
Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
|
Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
|
||||||
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
||||||
|
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
||||||
|
|
||||||
Q_INVOKABLE QString userPresence(QString id) const;
|
Q_INVOKABLE QString userPresence(QString id) const;
|
||||||
Q_INVOKABLE QString userStatus(QString id) const;
|
Q_INVOKABLE QString userStatus(QString id) const;
|
||||||
|
|
||||||
Q_INVOKABLE void openLink(QString link) const;
|
Q_INVOKABLE void openLink(QString link) const;
|
||||||
|
|
||||||
|
Q_INVOKABLE void openInviteUsersDialog();
|
||||||
|
Q_INVOKABLE void openMemberListDialog() const;
|
||||||
|
Q_INVOKABLE void openLeaveRoomDialog() const;
|
||||||
|
Q_INVOKABLE void openRoomSettings() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clearRoomMessageCount(QString roomid);
|
void clearRoomMessageCount(QString roomid);
|
||||||
void updateRoomsLastMessage(QString roomid, const DescInfo &info);
|
void updateRoomsLastMessage(QString roomid, const DescInfo &info);
|
||||||
|
@ -59,6 +69,9 @@ signals:
|
||||||
void initialSyncChanged(bool isInitialSync);
|
void initialSyncChanged(bool isInitialSync);
|
||||||
void replyingEventChanged(QString replyingEvent);
|
void replyingEventChanged(QString replyingEvent);
|
||||||
void replyClosed();
|
void replyClosed();
|
||||||
|
void inviteUsers(QStringList users);
|
||||||
|
void showRoomList();
|
||||||
|
void narrowViewChanged();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
||||||
|
@ -108,6 +121,23 @@ public slots:
|
||||||
timeline_->clearTimeline();
|
timeline_->clearTimeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableBackButton()
|
||||||
|
{
|
||||||
|
if (isNarrowView_)
|
||||||
|
return;
|
||||||
|
isNarrowView_ = true;
|
||||||
|
emit narrowViewChanged();
|
||||||
|
}
|
||||||
|
void disableBackButton()
|
||||||
|
{
|
||||||
|
if (!isNarrowView_)
|
||||||
|
return;
|
||||||
|
isNarrowView_ = false;
|
||||||
|
emit narrowViewChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void backToRooms() { emit showRoomList(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef USE_QUICK_VIEW
|
#ifdef USE_QUICK_VIEW
|
||||||
QQuickView *view;
|
QQuickView *view;
|
||||||
|
@ -125,6 +155,7 @@ private:
|
||||||
CallManager *callManager_ = nullptr;
|
CallManager *callManager_ = nullptr;
|
||||||
|
|
||||||
bool isInitialSync_ = true;
|
bool isInitialSync_ = true;
|
||||||
|
bool isNarrowView_ = false;
|
||||||
|
|
||||||
QSharedPointer<UserSettings> settings;
|
QSharedPointer<UserSettings> settings;
|
||||||
QHash<QString, QColor> userColors;
|
QHash<QString, QColor> userColors;
|
||||||
|
|
7
third_party/blurhash/blurhash.cpp
vendored
7
third_party/blurhash/blurhash.cpp
vendored
|
@ -260,6 +260,7 @@ decode(std::string_view blurhash, size_t width, size_t height, size_t bytesPerPi
|
||||||
|
|
||||||
Components components{};
|
Components components{};
|
||||||
std::vector<Color> values;
|
std::vector<Color> values;
|
||||||
|
values.reserve(blurhash.size() / 2);
|
||||||
try {
|
try {
|
||||||
components = unpackComponents(decode83(blurhash.substr(0, 1)));
|
components = unpackComponents(decode83(blurhash.substr(0, 1)));
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ decode(std::string_view blurhash, size_t width, size_t height, size_t bytesPerPi
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
i.image.reserve(height * width * 3);
|
i.image.reserve(height * width * bytesPerPixel);
|
||||||
|
|
||||||
for (size_t y = 0; y < height; y++) {
|
for (size_t y = 0; y < height; y++) {
|
||||||
for (size_t x = 0; x < width; x++) {
|
for (size_t x = 0; x < width; x++) {
|
||||||
|
@ -344,7 +345,7 @@ encode(unsigned char *image, size_t width, size_t height, int components_x, int
|
||||||
}
|
}
|
||||||
|
|
||||||
int quantisedMaximumValue = encodeMaxAC(actualMaximumValue);
|
int quantisedMaximumValue = encodeMaxAC(actualMaximumValue);
|
||||||
maximumValue = ((float)quantisedMaximumValue + 1) / 166;
|
maximumValue = ((float)quantisedMaximumValue + 1) / 166;
|
||||||
h += leftPad(encode83(quantisedMaximumValue), 1);
|
h += leftPad(encode83(quantisedMaximumValue), 1);
|
||||||
} else {
|
} else {
|
||||||
maximumValue = 1;
|
maximumValue = 1;
|
||||||
|
@ -406,7 +407,7 @@ TEST_CASE("AC")
|
||||||
{
|
{
|
||||||
auto h = "00%#MwS|WCWEM{R*bbWBbH"sv;
|
auto h = "00%#MwS|WCWEM{R*bbWBbH"sv;
|
||||||
for (size_t i = 0; i < h.size(); i += 2) {
|
for (size_t i = 0; i < h.size(); i += 2) {
|
||||||
auto s = h.substr(i, 2);
|
auto s = h.substr(i, 2);
|
||||||
const auto maxAC = 0.289157f;
|
const auto maxAC = 0.289157f;
|
||||||
CHECK(leftPad(encode83(encodeAC(decodeAC(decode83(s), maxAC), maxAC)), 2) == s);
|
CHECK(leftPad(encode83(encodeAC(decodeAC(decode83(s), maxAC), maxAC)), 2) == s);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue