Merge remote-tracking branch 'origin/master' into reactions
This commit is contained in:
commit
a5778bdf40
@ -137,11 +137,11 @@ before_install:
|
|||||||
# Use TRAVIS_TAG if defined, or the short commit SHA otherwise
|
# Use TRAVIS_TAG if defined, or the short commit SHA otherwise
|
||||||
- export VERSION=${TRAVIS_TAG:-$(git rev-parse --short HEAD)}
|
- export VERSION=${TRAVIS_TAG:-$(git rev-parse --short HEAD)}
|
||||||
install:
|
install:
|
||||||
- ./.ci/install.sh
|
- travis_wait ./.ci/install.sh
|
||||||
- export PATH=/usr/local/bin:${PATH}
|
- export PATH=/usr/local/bin:${PATH}
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./.ci/script.sh
|
- travis_wait ./.ci/script.sh
|
||||||
- sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true
|
- sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true
|
||||||
- cp ./.ci/bintray-release.json .
|
- cp ./.ci/bintray-release.json .
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -35,8 +35,6 @@ option(USE_BUNDLED_OPENSSL "Use the bundled version of OpenSSL."
|
|||||||
option(USE_BUNDLED_MTXCLIENT "Use the bundled version of the Matrix Client library." ${HUNTER_ENABLED})
|
option(USE_BUNDLED_MTXCLIENT "Use the bundled version of the Matrix Client library." ${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_SODIUM "Use the bundled version of libsodium."
|
option(USE_BUNDLED_SODIUM "Use the bundled version of libsodium."
|
||||||
${HUNTER_ENABLED})
|
${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_ZLIB "Use the bundled version of zlib."
|
|
||||||
${HUNTER_ENABLED})
|
|
||||||
option(USE_BUNDLED_LMDB "Use the bundled version of lmdb."
|
option(USE_BUNDLED_LMDB "Use the bundled version of lmdb."
|
||||||
${HUNTER_ENABLED})
|
${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdb++."
|
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdb++."
|
||||||
@ -323,10 +321,7 @@ find_package(Boost 1.70 REQUIRED
|
|||||||
COMPONENTS iostreams
|
COMPONENTS iostreams
|
||||||
system
|
system
|
||||||
thread)
|
thread)
|
||||||
if(USE_BUNDLED_ZLIB)
|
|
||||||
hunter_add_package(ZLIB)
|
|
||||||
endif()
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
if(USE_BUNDLED_OPENSSL)
|
if(USE_BUNDLED_OPENSSL)
|
||||||
hunter_add_package(OpenSSL)
|
hunter_add_package(OpenSSL)
|
||||||
endif()
|
endif()
|
||||||
@ -338,7 +333,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 71bd56b66cf634341ffef804f07d33f01fd57c25
|
GIT_TAG 1018c0822b80cdfc5d6b589fe94d1fd759113ef6
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(MatrixClient)
|
FetchContent_MakeAvailable(MatrixClient)
|
||||||
else()
|
else()
|
||||||
|
11
README.md
11
README.md
@ -97,13 +97,6 @@ guix install nheko
|
|||||||
|
|
||||||
#### macOS (10.14 and above)
|
#### macOS (10.14 and above)
|
||||||
|
|
||||||
|
|
||||||
with [macports](https://www.macports.org/):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo port install nheko
|
|
||||||
```
|
|
||||||
|
|
||||||
with [homebrew](https://brew.sh/):
|
with [homebrew](https://brew.sh/):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -120,7 +113,7 @@ brew cask install nheko
|
|||||||
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
|
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
|
||||||
- [cmark](https://github.com/commonmark/cmark) 0.29 or greater.
|
- [cmark](https://github.com/commonmark/cmark) 0.29 or greater.
|
||||||
- Boost 1.70 or greater.
|
- Boost 1.70 or greater.
|
||||||
- [libolm](https://git.matrix.org/git/olm)
|
- [libolm](https://gitlab.matrix.org/matrix-org/olm)
|
||||||
- [libsodium](https://github.com/jedisct1/libsodium)
|
- [libsodium](https://github.com/jedisct1/libsodium)
|
||||||
- [spdlog](https://github.com/gabime/spdlog)
|
- [spdlog](https://github.com/gabime/spdlog)
|
||||||
- A compiler that supports C++ 17:
|
- A compiler that supports C++ 17:
|
||||||
@ -210,7 +203,7 @@ guix environment nheko
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew update
|
brew update
|
||||||
brew install qt5 lmdb cmake llvm libsodium spdlog boost cmark
|
brew install qt5 lmdb cmake llvm libsodium spdlog boost cmark libolm
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Windows
|
##### Windows
|
||||||
|
@ -146,9 +146,9 @@
|
|||||||
"name": "mtxclient",
|
"name": "mtxclient",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"sha256": "7055f1459a43a12f27f949564624f13cc593ac894e445e6de0e6563ad38ebc3e",
|
"sha256": "537f4e6b280f351ad950cd6598c2407505a55f0d6c856b4ff97a9c59fc6fdb7a",
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/71bd56b66cf634341ffef804f07d33f01fd57c25.tar.gz"
|
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/1018c0822b80cdfc5d6b589fe94d1fd759113ef6.tar.gz"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
1
resources/langs/.gitignore
vendored
Normal file
1
resources/langs/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.qm
|
@ -245,7 +245,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>This message is not encrypted!</source>
|
<source>This message is not encrypted!</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Diese Nachricht ist nicht verschlüsselt!</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -673,7 +673,7 @@ Beispiel: https://mein.server:8787</translation>
|
|||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>%1 made this room require and invitation to join.</source>
|
<source>%1 made this room require and invitation to join.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>%1 hat eingestellt, dass dieser Raum eine Einladung benötigt um beizutreten.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+23"/>
|
<location line="+23"/>
|
||||||
@ -1037,7 +1037,7 @@ Beispiel: https://mein.server:8787</translation>
|
|||||||
<message>
|
<message>
|
||||||
<location line="+25"/>
|
<location line="+25"/>
|
||||||
<source>INTERFACE</source>
|
<source>INTERFACE</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>OBERFLÄCHE</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+108"/>
|
<location line="+108"/>
|
||||||
|
Binary file not shown.
@ -14,6 +14,7 @@ MouseArea {
|
|||||||
height: row.height
|
height: row.height
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@ -24,7 +25,10 @@ MouseArea {
|
|||||||
if (mouse.source === Qt.MouseEventNotSynthesized)
|
if (mouse.source === Qt.MouseEventNotSynthesized)
|
||||||
messageContextMenu.show(model.id, model.type, model.isEncrypted, row)
|
messageContextMenu.show(model.id, model.type, model.isEncrypted, row)
|
||||||
}
|
}
|
||||||
|
Rectangle {
|
||||||
|
color: (timelineSettings.message_hover_highlight && parent.containsMouse) ? colors.base : "transparent"
|
||||||
|
anchors.fill: row
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ Page {
|
|||||||
id: timelineSettings
|
id: timelineSettings
|
||||||
category: "user/timeline"
|
category: "user/timeline"
|
||||||
property bool buttons: true
|
property bool buttons: true
|
||||||
|
property bool message_hover_highlight: false
|
||||||
}
|
}
|
||||||
|
|
||||||
EmojiPicker {
|
EmojiPicker {
|
||||||
@ -113,6 +114,7 @@ Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BusyIndicator {
|
BusyIndicator {
|
||||||
|
visible: running
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
running: timelineManager.isInitialSync
|
running: timelineManager.isInitialSync
|
||||||
height: 200
|
height: 200
|
||||||
|
@ -1017,7 +1017,7 @@ ChatPage::trySync()
|
|||||||
// TODO: fine grained error handling
|
// TODO: fine grained error handling
|
||||||
try {
|
try {
|
||||||
cache::saveState(res);
|
cache::saveState(res);
|
||||||
olm::handle_to_device_messages(res.to_device);
|
olm::handle_to_device_messages(res.to_device.events);
|
||||||
|
|
||||||
auto updates = cache::roomUpdates(res);
|
auto updates = cache::roomUpdates(res);
|
||||||
|
|
||||||
@ -1240,7 +1240,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request
|
|||||||
try {
|
try {
|
||||||
cache::saveState(res);
|
cache::saveState(res);
|
||||||
|
|
||||||
olm::handle_to_device_messages(res.to_device);
|
olm::handle_to_device_messages(res.to_device.events);
|
||||||
|
|
||||||
emit initializeViews(std::move(res.rooms));
|
emit initializeViews(std::move(res.rooms));
|
||||||
emit initializeRoomList(cache::roomInfo());
|
emit initializeRoomList(cache::roomInfo());
|
||||||
|
73
src/Olm.cpp
73
src/Olm.cpp
@ -23,52 +23,55 @@ client()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_to_device_messages(const std::vector<nlohmann::json> &msgs)
|
handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEvents> &msgs)
|
||||||
{
|
{
|
||||||
if (msgs.empty())
|
if (msgs.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nhlog::crypto()->info("received {} to_device messages", msgs.size());
|
nhlog::crypto()->info("received {} to_device messages", msgs.size());
|
||||||
|
nlohmann::json j_msg;
|
||||||
|
|
||||||
for (const auto &msg : msgs) {
|
for (const auto &msg : msgs) {
|
||||||
if (msg.count("type") == 0) {
|
j_msg = std::visit([](auto &e) { return json(e); }, std::move(msg));
|
||||||
|
if (j_msg.count("type") == 0) {
|
||||||
nhlog::crypto()->warn("received message with no type field: {}",
|
nhlog::crypto()->warn("received message with no type field: {}",
|
||||||
msg.dump(2));
|
j_msg.dump(2));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string msg_type = msg.at("type");
|
std::string msg_type = j_msg.at("type");
|
||||||
|
|
||||||
if (msg_type == to_string(mtx::events::EventType::RoomEncrypted)) {
|
if (msg_type == to_string(mtx::events::EventType::RoomEncrypted)) {
|
||||||
try {
|
try {
|
||||||
OlmMessage olm_msg = msg;
|
OlmMessage olm_msg = j_msg;
|
||||||
handle_olm_message(std::move(olm_msg));
|
handle_olm_message(std::move(olm_msg));
|
||||||
} catch (const nlohmann::json::exception &e) {
|
} catch (const nlohmann::json::exception &e) {
|
||||||
nhlog::crypto()->warn(
|
nhlog::crypto()->warn(
|
||||||
"parsing error for olm message: {} {}", e.what(), msg.dump(2));
|
"parsing error for olm message: {} {}", e.what(), j_msg.dump(2));
|
||||||
} catch (const std::invalid_argument &e) {
|
} catch (const std::invalid_argument &e) {
|
||||||
nhlog::crypto()->warn(
|
nhlog::crypto()->warn("validation error for olm message: {} {}",
|
||||||
"validation error for olm message: {} {}", e.what(), msg.dump(2));
|
e.what(),
|
||||||
|
j_msg.dump(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (msg_type == to_string(mtx::events::EventType::RoomKeyRequest)) {
|
} else if (msg_type == to_string(mtx::events::EventType::RoomKeyRequest)) {
|
||||||
nhlog::crypto()->warn("handling key request event: {}", msg.dump(2));
|
nhlog::crypto()->warn("handling key request event: {}", j_msg.dump(2));
|
||||||
try {
|
try {
|
||||||
mtx::events::msg::KeyRequest req = msg;
|
mtx::events::DeviceEvent<mtx::events::msg::KeyRequest> req = j_msg;
|
||||||
if (req.action == mtx::events::msg::RequestAction::Request)
|
if (req.content.action == mtx::events::msg::RequestAction::Request)
|
||||||
handle_key_request_message(std::move(req));
|
handle_key_request_message(req);
|
||||||
else
|
else
|
||||||
nhlog::crypto()->warn(
|
nhlog::crypto()->warn(
|
||||||
"ignore key request (unhandled action): {}",
|
"ignore key request (unhandled action): {}",
|
||||||
req.request_id);
|
req.content.request_id);
|
||||||
} catch (const nlohmann::json::exception &e) {
|
} catch (const nlohmann::json::exception &e) {
|
||||||
nhlog::crypto()->warn(
|
nhlog::crypto()->warn(
|
||||||
"parsing error for key_request message: {} {}",
|
"parsing error for key_request message: {} {}",
|
||||||
e.what(),
|
e.what(),
|
||||||
msg.dump(2));
|
j_msg.dump(2));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nhlog::crypto()->warn("unhandled event: {}", msg.dump(2));
|
nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,51 +344,53 @@ send_key_request_for(const std::string &room_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_key_request_message(const mtx::events::msg::KeyRequest &req)
|
handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyRequest> &req)
|
||||||
{
|
{
|
||||||
if (req.algorithm != MEGOLM_ALGO) {
|
if (req.content.algorithm != MEGOLM_ALGO) {
|
||||||
nhlog::crypto()->debug("ignoring key request {} with invalid algorithm: {}",
|
nhlog::crypto()->debug("ignoring key request {} with invalid algorithm: {}",
|
||||||
req.request_id,
|
req.content.request_id,
|
||||||
req.algorithm);
|
req.content.algorithm);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we were the sender of the session being requested.
|
// Check if we were the sender of the session being requested.
|
||||||
if (req.sender_key != olm::client()->identity_keys().curve25519) {
|
if (req.content.sender_key != olm::client()->identity_keys().curve25519) {
|
||||||
nhlog::crypto()->debug("ignoring key request {} because we were not the sender: "
|
nhlog::crypto()->debug("ignoring key request {} because we were not the sender: "
|
||||||
"\nrequested({}) ours({})",
|
"\nrequested({}) ours({})",
|
||||||
req.request_id,
|
req.content.request_id,
|
||||||
req.sender_key,
|
req.content.sender_key,
|
||||||
olm::client()->identity_keys().curve25519);
|
olm::client()->identity_keys().curve25519);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have the keys for the requested session.
|
// Check if we have the keys for the requested session.
|
||||||
if (!cache::outboundMegolmSessionExists(req.room_id)) {
|
if (!cache::outboundMegolmSessionExists(req.content.room_id)) {
|
||||||
nhlog::crypto()->warn("requested session not found in room: {}", req.room_id);
|
nhlog::crypto()->warn("requested session not found in room: {}",
|
||||||
|
req.content.room_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the requested session_id and the one we have saved match.
|
// Check that the requested session_id and the one we have saved match.
|
||||||
const auto session = cache::getOutboundMegolmSession(req.room_id);
|
const auto session = cache::getOutboundMegolmSession(req.content.room_id);
|
||||||
if (req.session_id != session.data.session_id) {
|
if (req.content.session_id != session.data.session_id) {
|
||||||
nhlog::crypto()->warn("session id of retrieved session doesn't match the request: "
|
nhlog::crypto()->warn("session id of retrieved session doesn't match the request: "
|
||||||
"requested({}), ours({})",
|
"requested({}), ours({})",
|
||||||
req.session_id,
|
req.content.session_id,
|
||||||
session.data.session_id);
|
session.data.session_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cache::isRoomMember(req.sender, req.room_id)) {
|
if (!cache::isRoomMember(req.sender, req.content.room_id)) {
|
||||||
nhlog::crypto()->warn(
|
nhlog::crypto()->warn(
|
||||||
"user {} that requested the session key is not member of the room {}",
|
"user {} that requested the session key is not member of the room {}",
|
||||||
req.sender,
|
req.sender,
|
||||||
req.room_id);
|
req.content.room_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!utils::respondsToKeyRequests(req.room_id)) {
|
if (!utils::respondsToKeyRequests(req.content.room_id)) {
|
||||||
nhlog::crypto()->debug("ignoring all key requests for room {}", req.room_id);
|
nhlog::crypto()->debug("ignoring all key requests for room {}",
|
||||||
|
req.content.room_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,11 +398,11 @@ handle_key_request_message(const mtx::events::msg::KeyRequest &req)
|
|||||||
// Prepare the m.room_key event.
|
// Prepare the m.room_key event.
|
||||||
//
|
//
|
||||||
auto payload = json{{"algorithm", "m.megolm.v1.aes-sha2"},
|
auto payload = json{{"algorithm", "m.megolm.v1.aes-sha2"},
|
||||||
{"room_id", req.room_id},
|
{"room_id", req.content.room_id},
|
||||||
{"session_id", req.session_id},
|
{"session_id", req.content.session_id},
|
||||||
{"session_key", session.data.session_key}};
|
{"session_key", session.data.session_key}};
|
||||||
|
|
||||||
send_megolm_key_to_device(req.sender, req.requesting_device_id, payload);
|
send_megolm_key_to_device(req.sender, req.content.requesting_device_id, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -40,7 +40,7 @@ mtx::crypto::OlmClient *
|
|||||||
client();
|
client();
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_to_device_messages(const std::vector<nlohmann::json> &msgs);
|
handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEvents> &msgs);
|
||||||
|
|
||||||
nlohmann::json
|
nlohmann::json
|
||||||
try_olm_decryption(const std::string &sender_key,
|
try_olm_decryption(const std::string &sender_key,
|
||||||
@ -77,7 +77,7 @@ send_key_request_for(const std::string &room_id,
|
|||||||
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &);
|
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &);
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_key_request_message(const mtx::events::msg::KeyRequest &);
|
handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyRequest> &);
|
||||||
|
|
||||||
void
|
void
|
||||||
send_megolm_key_to_device(const std::string &user_id,
|
send_megolm_key_to_device(const std::string &user_id,
|
||||||
|
@ -51,11 +51,13 @@ void
|
|||||||
UserSettings::load()
|
UserSettings::load()
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
isTrayEnabled_ = settings.value("user/window/tray", false).toBool();
|
isTrayEnabled_ = settings.value("user/window/tray", false).toBool();
|
||||||
hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool();
|
hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool();
|
||||||
isStartInTrayEnabled_ = settings.value("user/window/start_in_tray", false).toBool();
|
isStartInTrayEnabled_ = settings.value("user/window/start_in_tray", false).toBool();
|
||||||
isGroupViewEnabled_ = settings.value("user/group_view", true).toBool();
|
isGroupViewEnabled_ = settings.value("user/group_view", true).toBool();
|
||||||
isButtonsInTimelineEnabled_ = settings.value("user/timeline/buttons", true).toBool();
|
isButtonsInTimelineEnabled_ = settings.value("user/timeline/buttons", true).toBool();
|
||||||
|
isMessageHoverHighlightEnabled_ =
|
||||||
|
settings.value("user/timeline/message_hover_highlight", false).toBool();
|
||||||
isMarkdownEnabled_ = settings.value("user/markdown_enabled", true).toBool();
|
isMarkdownEnabled_ = settings.value("user/markdown_enabled", true).toBool();
|
||||||
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
|
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
|
||||||
sortByImportance_ = settings.value("user/sort_by_unread", true).toBool();
|
sortByImportance_ = settings.value("user/sort_by_unread", true).toBool();
|
||||||
@ -165,6 +167,7 @@ UserSettings::save()
|
|||||||
|
|
||||||
settings.beginGroup("timeline");
|
settings.beginGroup("timeline");
|
||||||
settings.setValue("buttons", isButtonsInTimelineEnabled_);
|
settings.setValue("buttons", isButtonsInTimelineEnabled_);
|
||||||
|
settings.setValue("message_hover_highlight", isMessageHoverHighlightEnabled_);
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.setValue("avatar_circles", avatarCircles_);
|
settings.setValue("avatar_circles", avatarCircles_);
|
||||||
@ -235,6 +238,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||||||
groupViewToggle_ = new Toggle{this};
|
groupViewToggle_ = new Toggle{this};
|
||||||
timelineButtonsToggle_ = new Toggle{this};
|
timelineButtonsToggle_ = new Toggle{this};
|
||||||
typingNotifications_ = new Toggle{this};
|
typingNotifications_ = new Toggle{this};
|
||||||
|
messageHoverHighlight_ = new Toggle{this};
|
||||||
sortByImportance_ = new Toggle{this};
|
sortByImportance_ = new Toggle{this};
|
||||||
readReceipts_ = new Toggle{this};
|
readReceipts_ = new Toggle{this};
|
||||||
markdownEnabled_ = new Toggle{this};
|
markdownEnabled_ = new Toggle{this};
|
||||||
@ -345,6 +349,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||||||
boxWrap(tr("Read receipts"), readReceipts_);
|
boxWrap(tr("Read receipts"), readReceipts_);
|
||||||
boxWrap(tr("Send messages as Markdown"), markdownEnabled_);
|
boxWrap(tr("Send messages as Markdown"), markdownEnabled_);
|
||||||
boxWrap(tr("Desktop notifications"), desktopNotifications_);
|
boxWrap(tr("Desktop notifications"), desktopNotifications_);
|
||||||
|
boxWrap(tr("Highlight message on hover"), messageHoverHighlight_);
|
||||||
formLayout_->addRow(uiLabel_);
|
formLayout_->addRow(uiLabel_);
|
||||||
formLayout_->addRow(new HorizontalLine{this});
|
formLayout_->addRow(new HorizontalLine{this});
|
||||||
|
|
||||||
@ -463,6 +468,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||||||
settings_->setDesktopNotifications(!isDisabled);
|
settings_->setDesktopNotifications(!isDisabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(messageHoverHighlight_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||||
|
settings_->setMessageHoverHighlight(!isDisabled);
|
||||||
|
});
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys);
|
sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys);
|
||||||
|
|
||||||
@ -495,6 +504,7 @@ UserSettingsPage::showEvent(QShowEvent *)
|
|||||||
readReceipts_->setState(!settings_->isReadReceiptsEnabled());
|
readReceipts_->setState(!settings_->isReadReceiptsEnabled());
|
||||||
markdownEnabled_->setState(!settings_->isMarkdownEnabled());
|
markdownEnabled_->setState(!settings_->isMarkdownEnabled());
|
||||||
desktopNotifications_->setState(!settings_->hasDesktopNotifications());
|
desktopNotifications_->setState(!settings_->hasDesktopNotifications());
|
||||||
|
messageHoverHighlight_->setState(!settings_->isMessageHoverHighlightEnabled());
|
||||||
deviceIdValue_->setText(QString::fromStdString(http::client()->device_id()));
|
deviceIdValue_->setText(QString::fromStdString(http::client()->device_id()));
|
||||||
|
|
||||||
deviceFingerprintValue_->setText(
|
deviceFingerprintValue_->setText(
|
||||||
|
@ -44,6 +44,11 @@ public:
|
|||||||
void load();
|
void load();
|
||||||
void applyTheme();
|
void applyTheme();
|
||||||
void setTheme(QString theme);
|
void setTheme(QString theme);
|
||||||
|
void setMessageHoverHighlight(bool state)
|
||||||
|
{
|
||||||
|
isMessageHoverHighlightEnabled_ = state;
|
||||||
|
save();
|
||||||
|
}
|
||||||
void setTray(bool state)
|
void setTray(bool state)
|
||||||
{
|
{
|
||||||
isTrayEnabled_ = state;
|
isTrayEnabled_ = state;
|
||||||
@ -118,6 +123,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
|
QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
|
||||||
|
bool isMessageHoverHighlightEnabled() const { return isMessageHoverHighlightEnabled_; }
|
||||||
bool isTrayEnabled() const { return isTrayEnabled_; }
|
bool isTrayEnabled() const { return isTrayEnabled_; }
|
||||||
bool isStartInTrayEnabled() const { return isStartInTrayEnabled_; }
|
bool isStartInTrayEnabled() const { return isStartInTrayEnabled_; }
|
||||||
bool isGroupViewEnabled() const { return isGroupViewEnabled_; }
|
bool isGroupViewEnabled() const { return isGroupViewEnabled_; }
|
||||||
@ -144,6 +150,7 @@ private:
|
|||||||
? "light"
|
? "light"
|
||||||
: "system";
|
: "system";
|
||||||
QString theme_;
|
QString theme_;
|
||||||
|
bool isMessageHoverHighlightEnabled_;
|
||||||
bool isTrayEnabled_;
|
bool isTrayEnabled_;
|
||||||
bool isStartInTrayEnabled_;
|
bool isStartInTrayEnabled_;
|
||||||
bool isGroupViewEnabled_;
|
bool isGroupViewEnabled_;
|
||||||
@ -203,6 +210,7 @@ private:
|
|||||||
Toggle *groupViewToggle_;
|
Toggle *groupViewToggle_;
|
||||||
Toggle *timelineButtonsToggle_;
|
Toggle *timelineButtonsToggle_;
|
||||||
Toggle *typingNotifications_;
|
Toggle *typingNotifications_;
|
||||||
|
Toggle *messageHoverHighlight_;
|
||||||
Toggle *sortByImportance_;
|
Toggle *sortByImportance_;
|
||||||
Toggle *readReceipts_;
|
Toggle *readReceipts_;
|
||||||
Toggle *markdownEnabled_;
|
Toggle *markdownEnabled_;
|
||||||
|
@ -116,6 +116,7 @@ main(int argc, char *argv[])
|
|||||||
QCoreApplication::setApplicationName("nheko");
|
QCoreApplication::setApplicationName("nheko");
|
||||||
QCoreApplication::setApplicationVersion(nheko::version);
|
QCoreApplication::setApplicationVersion(nheko::version);
|
||||||
QCoreApplication::setOrganizationName("nheko");
|
QCoreApplication::setOrganizationName("nheko");
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
SingleApplication app(argc,
|
SingleApplication app(argc,
|
||||||
|
Loading…
Reference in New Issue
Block a user