diff --git a/.ci/install.sh b/.ci/install.sh
index 0942af62..2c7c71e3 100755
--- a/.ci/install.sh
+++ b/.ci/install.sh
@@ -31,8 +31,8 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
QT_PKG="59"
fi
- wget https://cmake.org/files/v3.12/cmake-3.12.2-Linux-x86_64.sh
- sudo sh cmake-3.12.2-Linux-x86_64.sh --skip-license --prefix=/usr/local
+ wget https://cmake.org/files/v3.15/cmake-3.15.5-Linux-x86_64.sh
+ sudo sh cmake-3.15.5-Linux-x86_64.sh --skip-license --prefix=/usr/local
mkdir -p build-libsodium
( cd build-libsodium
diff --git a/.ci/script.sh b/.ci/script.sh
index ac6bfed6..06536278 100755
--- a/.ci/script.sh
+++ b/.ci/script.sh
@@ -13,6 +13,9 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo update-alternatives --set gcc "/usr/bin/${C_COMPILER}"
sudo update-alternatives --set g++ "/usr/bin/${CXX_COMPILER}"
+
+ export PATH="/usr/local/bin/:${PATH}"
+ cmake --version
fi
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
@@ -35,7 +38,8 @@ cmake --build .deps
# Build nheko
cmake -GNinja -H. -Bbuild \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
- -DCMAKE_INSTALL_PREFIX=.deps/usr
+ -DCMAKE_INSTALL_PREFIX=.deps/usr \
+ -DBUILD_SHARED_LIBS=ON # weird workaround, as the boost 1.70 cmake files seem to be broken?
cmake --build build
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e07df88d..67a1dfb0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -259,7 +259,7 @@ include(FeatureSummary)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_MULTITHREADED ON)
-find_package(Boost 1.66 REQUIRED
+find_package(Boost 1.70 REQUIRED
COMPONENTS atomic
chrono
date_time
@@ -365,6 +365,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/CommunitiesList.h
src/LoginPage.h
src/MainWindow.h
+ src/MxcImageProvider.h
src/InviteeItem.h
src/QuickSwitcher.h
src/RegisterPage.h
diff --git a/README.md b/README.md
index efa37e89..0380a90a 100644
--- a/README.md
+++ b/README.md
@@ -92,11 +92,11 @@ sudo port install nheko
- Qt5 (5.8 or greater). Qt 5.7 adds support for color font rendering with
Freetype, which is essential to properly support emoji, 5.8 adds some features
to make interopability with Qml easier.
-- CMake 3.1 or greater.
+- CMake 3.15 or greater. (Lower version may work, but may break boost linking)
- [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
- [cmark](https://github.com/commonmark/cmark)
-- Boost 1.66 or greater.
+- Boost 1.70 or greater.
- [libolm](https://git.matrix.org/git/olm)
- [libsodium](https://github.com/jedisct1/libsodium)
- [spdlog](https://github.com/gabime/spdlog)
diff --git a/appveyor.yml b/appveyor.yml
index 08251174..8572418f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -34,6 +34,7 @@ install:
lmdb:%PLATFORM%-windows
openssl:%PLATFORM%-windows
zlib:%PLATFORM%-windows
+ - vcpkg upgrade --no-dry-run
build_script:
# VERSION format: branch-master/branch-1.2
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index d0a715e0..0da4a671 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -33,23 +33,23 @@ option(USE_BUNDLED_JSON "Use the bundled version of nlohmann json." ${USE_BUNDLE
option(MTX_STATIC "Compile / link bundled mtx client statically" OFF)
if(USE_BUNDLED_BOOST)
- # bundled boost is 1.68, which requires CMake 3.12 or greater.
- cmake_minimum_required(VERSION 3.12)
+ # bundled boost is 1.70, which requires CMake 3.15 or greater.
+ cmake_minimum_required(VERSION 3.15)
endif()
include(ExternalProject)
set(BOOST_URL
- https://dl.bintray.com/boostorg/release/1.69.0/source/boost_1_69_0.tar.bz2)
+ https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.bz2)
set(BOOST_SHA256
- 8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406)
+ 430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778)
set(
MTXCLIENT_URL
- https://github.com/Nheko-Reborn/mtxclient/archive/6eee767cc25a9db9f125843e584656cde1ebb6c5.tar.gz
+ https://github.com/Nheko-Reborn/mtxclient/archive/64182a84e35378113f7d3a80f3073894416480e7.zip
)
set(MTXCLIENT_HASH
- 72fe77da4fed98b3cf069299f66092c820c900359a27ec26070175f9ad208a03)
+ c9973501920046f04c72983472451736343d00e7a40f4d4a12181191093a5fab)
set(
TWEENY_URL
https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz
diff --git a/resources/langs/nheko_de.ts b/resources/langs/nheko_de.ts
index 879551bd..59c6dffd 100644
--- a/resources/langs/nheko_de.ts
+++ b/resources/langs/nheko_de.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
- <translation>Hochladen des Bildes fehlgeschlagen. Bitte versuche es erneut.</translation>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
+ <translation>Medienupload fehlgeschlagen. Bitte versuche es erneut.</translation>
</message>
<message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation>Hochladen der Datei fehlgeschlagen. Bitte versuche es erneut.</translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation>Hochladen der Audiodatei fehlgeschlagen. Bitte versuche es erneut.</translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation>Hochladen der Videodatei fehlgeschlagen. Bitte versuche es erneut.</translation>
- </message>
- <message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation>Wiederherstellung des OLM Accounts fehlgeschlagen. Bitte logge dich erneut ein.</translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation>gelöscht</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation>Verschlüsselung aktiviert</translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation>gelöscht</translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation>Versende Datei</translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Schreibe eine Nachricht…</translation>
</message>
@@ -375,7 +365,7 @@
<translation>Emoji</translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Datei auswählen</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation>-- verschlüsselter Event (keine Schlüssel zur Entschlüsselung gefunden) --</translation>
@@ -423,10 +413,30 @@
<translation>-- verschlüsselter Event (Unbekannter Eventtyp) --</translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation>Nachricht zurückziehen fehlgeschlagen: %1</translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation>Bild speichern</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation>Video speichern</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation>Audiodatei speichern</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation>Datei speichern</translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation>Bild speichern</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation>Video speichern</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation>Audiodatei speichern</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation>Datei speichern</translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_el.ts b/resources/langs/nheko_el.ts
index e9c70da0..fe65785b 100644
--- a/resources/langs/nheko_el.ts
+++ b/resources/langs/nheko_el.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation type="unfinished"></translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Γράψε ένα μήνυμα...</translation>
</message>
@@ -375,7 +365,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Διάλεξε ένα αρχείο</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished"></translation>
@@ -423,10 +413,30 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Αποθήκευση Εικόνας</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Αποθήκευση Εικόνας</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_en.ts b/resources/langs/nheko_en.ts
index cb2ef1c7..49ea7439 100644
--- a/resources/langs/nheko_en.ts
+++ b/resources/langs/nheko_en.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
- <translation>Failed to upload image. Please try again.</translation>
- </message>
- <message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation>Failed to upload file. Please try again.</translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation>Failed to upload audio. Please try again.</translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation>Failed to upload video. Please try again.</translation>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation>Failed to restore OLM account. Please login again.</translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation>Send a file</translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Write a message…</translation>
</message>
@@ -375,7 +365,7 @@
<translation>Emoji</translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Select a file</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished">-- Encrypted Event (No keys found for decryption) --</translation>
@@ -423,10 +413,30 @@
<translation type="unfinished">-- Encrypted Event (Unknown event type) --</translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished">Message redaction failed: %1</translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Save image</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Save image</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_fi.ts b/resources/langs/nheko_fi.ts
index 76bf7064..4bb20e30 100644
--- a/resources/langs/nheko_fi.ts
+++ b/resources/langs/nheko_fi.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
- <translation>Kuvan lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.</translation>
- </message>
- <message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation>Tiedoston lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.</translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation>Äänitiedoston lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.</translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation>Videon lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.</translation>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation>OLM-tilin palauttaminen epäonnistui. Ole hyvä ja kirjaudu sisään uudelleen.</translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation>Lähetä tiedosto</translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Kirjoita viesti…</translation>
</message>
@@ -375,7 +365,7 @@
<translation>Emoji</translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Valitse tiedosto</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished">-- Salattu viesti (salauksen purkuavaimia ei löydetty) --</translation>
@@ -423,10 +413,30 @@
<translation type="unfinished">-- Salattu viesti (tuntematon viestityyppi) --</translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished">Viestin poisto epäonnistui: %1</translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Tallenna kuva</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Tallenna kuva</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_fr.ts b/resources/langs/nheko_fr.ts
index 30ff8599..8ef22268 100644
--- a/resources/langs/nheko_fr.ts
+++ b/resources/langs/nheko_fr.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation type="unfinished"></translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -355,13 +345,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Écrivez un message...</translation>
</message>
@@ -376,7 +366,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Sélectionnez un fichier</translation>
</message>
@@ -394,7 +384,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished"></translation>
@@ -424,10 +414,30 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Enregistrer l'image</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -476,29 +486,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Enregistrer l'image</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_nl.ts b/resources/langs/nheko_nl.ts
index 1c8a83c0..aaeae41c 100644
--- a/resources/langs/nheko_nl.ts
+++ b/resources/langs/nheko_nl.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation type="unfinished"></translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Typ een bericht...</translation>
</message>
@@ -375,7 +365,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Kies een bestand</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished"></translation>
@@ -423,10 +413,30 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Afbeelding opslaan</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Afbeelding opslaan</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_pl.ts b/resources/langs/nheko_pl.ts
index 6c3b2abd..b7c3878d 100644
--- a/resources/langs/nheko_pl.ts
+++ b/resources/langs/nheko_pl.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
- <translation>Nie udało się wysłać obrazu. Spróbuj ponownie.</translation>
- </message>
- <message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation>Nie udało się wysłać pliku. Spróbuj ponownie.</translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation>Nie udało się wysłać pliku dźwiękowego. Spróbuj ponownie.</translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation>Nie udało się wysłać filmu. Spróbuj ponownie.</translation>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation>Nie udało się przywrócić konta OLM. Spróbuj zalogować się ponownie.</translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation>Wyślij plik</translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Napisz wiadomość…</translation>
</message>
@@ -375,7 +365,7 @@
<translation>Emoji</translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Wybierz plik</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished"></translation>
@@ -423,10 +413,30 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished">Redagowanie wiadomości nie powiodło się: %1</translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Zapisz obraz</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Zapisz obraz</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_ru.ts b/resources/langs/nheko_ru.ts
index d5544cf8..3069cdad 100644
--- a/resources/langs/nheko_ru.ts
+++ b/resources/langs/nheko_ru.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
- <translation>Не удалось загрузить изображение. Пожалуйста, попробуйте еще раз.</translation>
- </message>
- <message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation>Не удалось загрузить файл. Пожалуйста, попробуйте еще раз.</translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation>Не удалось загрузить аудио. Пожалуйста, попробуйте еще раз.</translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation>Не удалось загрузить видео. Пожалуйста, попробуйте еще раз.</translation>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation>Не удалось восстановить учетную запись OLM. Пожалуйста, войдите снова.</translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation>Отправить файл</translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>Написать сообщение...</translation>
</message>
@@ -375,7 +365,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>Выберите файл</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished"></translation>
@@ -423,10 +413,30 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished">Ошибка редактирования сообщения: %1</translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">Сохранить изображение</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">Сохранить изображение</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/langs/nheko_zh_CN.ts b/resources/langs/nheko_zh_CN.ts
index 57f49d43..31ca068c 100644
--- a/resources/langs/nheko_zh_CN.ts
+++ b/resources/langs/nheko_zh_CN.ts
@@ -4,27 +4,12 @@
<context>
<name>ChatPage</name>
<message>
- <location filename="../../src/ChatPage.cpp" line="+330"/>
- <source>Failed to upload image. Please try again.</source>
- <translation>上传图像失败。请重试。</translation>
- </message>
- <message>
- <location line="+45"/>
- <source>Failed to upload file. Please try again.</source>
- <translation>上传文件失败,请重试。</translation>
- </message>
- <message>
- <location line="+43"/>
- <source>Failed to upload audio. Please try again.</source>
- <translation>上传音频失败。请重试。</translation>
- </message>
- <message>
- <location line="+42"/>
- <source>Failed to upload video. Please try again.</source>
- <translation>上传视频失败。请重试。</translation>
+ <location filename="../../src/ChatPage.cpp" line="+346"/>
+ <source>Failed to upload media. Please try again.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+393"/>
+ <location line="+389"/>
<source>Failed to restore OLM account. Please login again.</source>
<translation>恢复 OLM 账户失败。请重新登录。</translation>
</message>
@@ -195,6 +180,19 @@
</message>
</context>
<context>
+ <name>MessageDelegate</name>
+ <message>
+ <location filename="../qml/delegates/MessageDelegate.qml" line="+43"/>
+ <source>redacted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Encryption enabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>Placeholder</name>
<message>
<location filename="../qml/delegates/Placeholder.qml" line="+4"/>
@@ -211,14 +209,6 @@
</message>
</context>
<context>
- <name>Redacted</name>
- <message>
- <location filename="../qml/delegates/Redacted.qml" line="+5"/>
- <source>redacted</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>RegisterPage</name>
<message>
<location filename="../../src/RegisterPage.cpp" line="+77"/>
@@ -354,13 +344,13 @@
<context>
<name>TextInputWidget</name>
<message>
- <location filename="../../src/TextInputWidget.cpp" line="+507"/>
+ <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source>
<translation>发送一个文件</translation>
</message>
<message>
<location line="+13"/>
- <location filename="../../src/TextInputWidget.h" line="+164"/>
+ <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source>
<translation>写一条消息...</translation>
</message>
@@ -375,7 +365,7 @@
<translation></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+72"/>
<source>Select a file</source>
<translation>选择一个文件</translation>
</message>
@@ -393,7 +383,7 @@
<context>
<name>TimelineModel</name>
<message>
- <location filename="../../src/timeline/TimelineModel.cpp" line="+780"/>
+ <location filename="../../src/timeline/TimelineModel.cpp" line="+835"/>
<source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can't be decrypted</comment>
<translation type="unfinished"></translation>
@@ -423,10 +413,30 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+54"/>
<source>Message redaction failed: %1</source>
<translation type="unfinished">删除消息失败:%1</translation>
</message>
+ <message>
+ <location line="+453"/>
+ <source>Save image</source>
+ <translation type="unfinished">保存图像</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save video</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Save file</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>TimelineRow</name>
@@ -475,29 +485,6 @@
</message>
</context>
<context>
- <name>TimelineViewManager</name>
- <message>
- <location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
- <source>Save image</source>
- <translation type="unfinished">保存图像</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save video</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save audio</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Save file</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>TopRoomBar</name>
<message>
<location filename="../../src/TopRoomBar.cpp" line="+78"/>
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 4917e893..2c2ed02a 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -97,7 +97,7 @@ RowLayout {
MenuItem {
visible: model.type == MtxEvent.ImageMessage || model.type == MtxEvent.VideoMessage || model.type == MtxEvent.AudioMessage || model.type == MtxEvent.FileMessage || model.type == MtxEvent.Sticker
text: qsTr("Save as")
- onTriggered: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type)
+ onTriggered: timelineManager.timeline.saveMedia(model.id)
}
}
}
diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml
index f4cf3f15..2c911c5e 100644
--- a/resources/qml/delegates/FileMessage.qml
+++ b/resources/qml/delegates/FileMessage.qml
@@ -31,7 +31,7 @@ Rectangle {
}
MouseArea {
anchors.fill: parent
- onClicked: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type)
+ onClicked: timelineManager.timeline.saveMedia(model.id)
cursorShape: Qt.PointingHandCursor
}
}
diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml
index a1a06012..1b6e5729 100644
--- a/resources/qml/delegates/ImageMessage.qml
+++ b/resources/qml/delegates/ImageMessage.qml
@@ -17,7 +17,7 @@ Item {
MouseArea {
enabled: model.type == MtxEvent.ImageMessage
anchors.fill: parent
- onClicked: timelineManager.openImageOverlay(model.url, model.filename, model.mimetype, model.type)
+ onClicked: timelineManager.openImageOverlay(model.url, model.id)
}
}
}
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 3b987545..d0d4d7cb 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -97,7 +97,7 @@ Rectangle {
anchors.fill: parent
onClicked: {
switch (button.state) {
- case "": timelineManager.cacheMedia(model.url, model.mimetype); break;
+ case "": timelineManager.timeline.cacheMedia(model.id); break;
case "stopped":
media.play(); console.log("play");
button.state = "playing"
@@ -118,7 +118,7 @@ Rectangle {
}
Connections {
- target: timelineManager
+ target: timelineManager.timeline
onMediaCached: {
if (mxcUrl == model.url) {
media.source = "file://" + cacheUrl
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 091a9fa0..d6f6940b 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -54,6 +54,8 @@ constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000;
constexpr int RETRY_TIMEOUT = 5'000;
constexpr size_t MAX_ONETIME_KEYS = 50;
+Q_DECLARE_METATYPE(boost::optional<mtx::crypto::EncryptedFile>)
+
ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
: QWidget(parent)
, isConnected_(true)
@@ -62,6 +64,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
{
setObjectName("chatPage");
+ qRegisterMetaType<boost::optional<mtx::crypto::EncryptedFile>>(
+ "boost::optional<mtx::crypto::EncryptedFile>");
+
topLayout_ = new QHBoxLayout(this);
topLayout_->setSpacing(0);
topLayout_->setMargin(0);
@@ -299,9 +304,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect(
text_input_,
- &TextInputWidget::uploadImage,
+ &TextInputWidget::uploadMedia,
this,
- [this](QSharedPointer<QIODevice> dev, const QString &fn) {
+ [this](QSharedPointer<QIODevice> dev, QString mimeClass, const QString &fn) {
QMimeDatabase db;
QMimeType mime = db.mimeTypeForData(dev.data());
@@ -311,9 +316,18 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
return;
}
- auto bin = dev->peek(dev->size());
- auto payload = std::string(bin.data(), bin.size());
- auto dimensions = QImageReader(dev.data()).size();
+ auto bin = dev->peek(dev->size());
+ auto payload = std::string(bin.data(), bin.size());
+ boost::optional<mtx::crypto::EncryptedFile> encryptedFile;
+ if (cache::client()->isRoomEncrypted(current_room_.toStdString())) {
+ mtx::crypto::BinaryBuf buf;
+ std::tie(buf, encryptedFile) = mtx::crypto::encrypt_file(payload);
+ payload = mtx::crypto::to_string(buf);
+ }
+
+ QSize dimensions;
+ if (mimeClass == "image")
+ dimensions = QImageReader(dev.data()).size();
http::client()->upload(
payload,
@@ -322,193 +336,61 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
[this,
room_id = current_room_,
filename = fn,
- mime = mime.name(),
- size = payload.size(),
+ encryptedFile,
+ mimeClass,
+ mime = mime.name(),
+ size = payload.size(),
dimensions](const mtx::responses::ContentURI &res, mtx::http::RequestErr err) {
if (err) {
emit uploadFailed(
- tr("Failed to upload image. Please try again."));
- nhlog::net()->warn("failed to upload image: {} {} ({})",
+ tr("Failed to upload media. Please try again."));
+ nhlog::net()->warn("failed to upload media: {} {} ({})",
err->matrix_error.error,
to_string(err->matrix_error.errcode),
static_cast<int>(err->status_code));
return;
}
- emit imageUploaded(room_id,
+ emit mediaUploaded(room_id,
filename,
+ encryptedFile,
QString::fromStdString(res.content_uri),
+ mimeClass,
mime,
size,
dimensions);
});
});
- connect(text_input_,
- &TextInputWidget::uploadFile,
- this,
- [this](QSharedPointer<QIODevice> dev, const QString &fn) {
- QMimeDatabase db;
- QMimeType mime = db.mimeTypeForData(dev.data());
-
- if (!dev->open(QIODevice::ReadOnly)) {
- emit uploadFailed(
- QString("Error while reading media: %1").arg(dev->errorString()));
- return;
- }
-
- auto bin = dev->readAll();
- auto payload = std::string(bin.data(), bin.size());
-
- http::client()->upload(
- payload,
- mime.name().toStdString(),
- QFileInfo(fn).fileName().toStdString(),
- [this,
- room_id = current_room_,
- filename = fn,
- mime = mime.name(),
- size = payload.size()](const mtx::responses::ContentURI &res,
- mtx::http::RequestErr err) {
- if (err) {
- emit uploadFailed(
- tr("Failed to upload file. Please try again."));
- nhlog::net()->warn("failed to upload file: {} ({})",
- err->matrix_error.error,
- static_cast<int>(err->status_code));
- return;
- }
-
- emit fileUploaded(room_id,
- filename,
- QString::fromStdString(res.content_uri),
- mime,
- size);
- });
- });
-
- connect(text_input_,
- &TextInputWidget::uploadAudio,
- this,
- [this](QSharedPointer<QIODevice> dev, const QString &fn) {
- QMimeDatabase db;
- QMimeType mime = db.mimeTypeForData(dev.data());
-
- if (!dev->open(QIODevice::ReadOnly)) {
- emit uploadFailed(
- QString("Error while reading media: %1").arg(dev->errorString()));
- return;
- }
-
- auto bin = dev->readAll();
- auto payload = std::string(bin.data(), bin.size());
-
- http::client()->upload(
- payload,
- mime.name().toStdString(),
- QFileInfo(fn).fileName().toStdString(),
- [this,
- room_id = current_room_,
- filename = fn,
- mime = mime.name(),
- size = payload.size()](const mtx::responses::ContentURI &res,
- mtx::http::RequestErr err) {
- if (err) {
- emit uploadFailed(
- tr("Failed to upload audio. Please try again."));
- nhlog::net()->warn("failed to upload audio: {} ({})",
- err->matrix_error.error,
- static_cast<int>(err->status_code));
- return;
- }
-
- emit audioUploaded(room_id,
- filename,
- QString::fromStdString(res.content_uri),
- mime,
- size);
- });
- });
- connect(text_input_,
- &TextInputWidget::uploadVideo,
- this,
- [this](QSharedPointer<QIODevice> dev, const QString &fn) {
- QMimeDatabase db;
- QMimeType mime = db.mimeTypeForData(dev.data());
-
- if (!dev->open(QIODevice::ReadOnly)) {
- emit uploadFailed(
- QString("Error while reading media: %1").arg(dev->errorString()));
- return;
- }
-
- auto bin = dev->readAll();
- auto payload = std::string(bin.data(), bin.size());
-
- http::client()->upload(
- payload,
- mime.name().toStdString(),
- QFileInfo(fn).fileName().toStdString(),
- [this,
- room_id = current_room_,
- filename = fn,
- mime = mime.name(),
- size = payload.size()](const mtx::responses::ContentURI &res,
- mtx::http::RequestErr err) {
- if (err) {
- emit uploadFailed(
- tr("Failed to upload video. Please try again."));
- nhlog::net()->warn("failed to upload video: {} ({})",
- err->matrix_error.error,
- static_cast<int>(err->status_code));
- return;
- }
-
- emit videoUploaded(room_id,
- filename,
- QString::fromStdString(res.content_uri),
- mime,
- size);
- });
- });
-
connect(this, &ChatPage::uploadFailed, this, [this](const QString &msg) {
text_input_->hideUploadSpinner();
emit showNotification(msg);
});
connect(this,
- &ChatPage::imageUploaded,
+ &ChatPage::mediaUploaded,
this,
[this](QString roomid,
QString filename,
+ boost::optional<mtx::crypto::EncryptedFile> encryptedFile,
QString url,
+ QString mimeClass,
QString mime,
qint64 dsize,
QSize dimensions) {
text_input_->hideUploadSpinner();
- view_manager_->queueImageMessage(
- roomid, filename, url, mime, dsize, dimensions);
- });
- connect(this,
- &ChatPage::fileUploaded,
- this,
- [this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) {
- text_input_->hideUploadSpinner();
- view_manager_->queueFileMessage(roomid, filename, url, mime, dsize);
- });
- connect(this,
- &ChatPage::audioUploaded,
- this,
- [this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) {
- text_input_->hideUploadSpinner();
- view_manager_->queueAudioMessage(roomid, filename, url, mime, dsize);
- });
- connect(this,
- &ChatPage::videoUploaded,
- this,
- [this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) {
- text_input_->hideUploadSpinner();
- view_manager_->queueVideoMessage(roomid, filename, url, mime, dsize);
+
+ if (mimeClass == "image")
+ view_manager_->queueImageMessage(
+ roomid, filename, encryptedFile, url, mime, dsize, dimensions);
+ else if (mimeClass == "audio")
+ view_manager_->queueAudioMessage(
+ roomid, filename, encryptedFile, url, mime, dsize);
+ else if (mimeClass == "video")
+ view_manager_->queueVideoMessage(
+ roomid, filename, encryptedFile, url, mime, dsize);
+ else
+ view_manager_->queueFileMessage(
+ roomid, filename, encryptedFile, url, mime, dsize);
});
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 1898f1a7..20e156af 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -18,7 +18,9 @@
#pragma once
#include <atomic>
+#include <boost/optional.hpp>
#include <boost/variant.hpp>
+#include <mtx/common.hpp>
#include <mtx/responses.hpp>
#include <QFrame>
@@ -94,27 +96,14 @@ signals:
const QPoint widgetPos);
void uploadFailed(const QString &msg);
- void imageUploaded(const QString &roomid,
+ void mediaUploaded(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
+ const QString &mimeClass,
const QString &mime,
qint64 dsize,
const QSize &dimensions);
- void fileUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- qint64 dsize);
- void audioUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- qint64 dsize);
- void videoUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- qint64 dsize);
void contentLoaded();
void closing();
diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp
index 556b019b..edf6ceb5 100644
--- a/src/MxcImageProvider.cpp
+++ b/src/MxcImageProvider.cpp
@@ -5,7 +5,7 @@
void
MxcImageResponse::run()
{
- if (m_requestedSize.isValid()) {
+ if (m_requestedSize.isValid() && !m_encryptionInfo) {
QString fileName = QString("%1_%2x%3_crop")
.arg(m_id)
.arg(m_requestedSize.width())
@@ -65,7 +65,12 @@ MxcImageResponse::run()
return;
}
- auto data = QByteArray(res.data(), res.size());
+ auto temp = res;
+ if (m_encryptionInfo)
+ temp = mtx::crypto::to_string(
+ mtx::crypto::decrypt_file(temp, m_encryptionInfo.value()));
+
+ auto data = QByteArray(temp.data(), temp.size());
m_image.loadFromData(data);
m_image.setText("original filename",
QString::fromStdString(originalFilename));
diff --git a/src/MxcImageProvider.h b/src/MxcImageProvider.h
index 19d8a74e..2c197a13 100644
--- a/src/MxcImageProvider.h
+++ b/src/MxcImageProvider.h
@@ -6,14 +6,21 @@
#include <QImage>
#include <QThreadPool>
+#include <mtx/common.hpp>
+
+#include <boost/optional.hpp>
+
class MxcImageResponse
: public QQuickImageResponse
, public QRunnable
{
public:
- MxcImageResponse(const QString &id, const QSize &requestedSize)
+ MxcImageResponse(const QString &id,
+ const QSize &requestedSize,
+ boost::optional<mtx::crypto::EncryptedFile> encryptionInfo)
: m_id(id)
, m_requestedSize(requestedSize)
+ , m_encryptionInfo(encryptionInfo)
{
setAutoDelete(false);
}
@@ -29,19 +36,34 @@ public:
QString m_id, m_error;
QSize m_requestedSize;
QImage m_image;
+ boost::optional<mtx::crypto::EncryptedFile> m_encryptionInfo;
};
-class MxcImageProvider : public QQuickAsyncImageProvider
+class MxcImageProvider
+ : public QObject
+ , public QQuickAsyncImageProvider
{
-public:
+ Q_OBJECT
+public slots:
QQuickImageResponse *requestImageResponse(const QString &id,
const QSize &requestedSize) override
{
- MxcImageResponse *response = new MxcImageResponse(id, requestedSize);
+ boost::optional<mtx::crypto::EncryptedFile> info;
+ auto temp = infos.find("mxc://" + id);
+ if (temp != infos.end())
+ info = *temp;
+
+ MxcImageResponse *response = new MxcImageResponse(id, requestedSize, info);
pool.start(response);
return response;
}
+ void addEncryptionInfo(mtx::crypto::EncryptedFile info)
+ {
+ infos.insert(QString::fromStdString(info.url), info);
+ }
+
private:
QThreadPool pool;
+ QHash<QString, mtx::crypto::EncryptedFile> infos;
};
diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp
index f723c01a..66700dbc 100644
--- a/src/TextInputWidget.cpp
+++ b/src/TextInputWidget.cpp
@@ -458,21 +458,16 @@ FilteredTextEdit::textChanged()
}
void
-FilteredTextEdit::uploadData(const QByteArray data, const QString &media, const QString &filename)
+FilteredTextEdit::uploadData(const QByteArray data,
+ const QString &mediaType,
+ const QString &filename)
{
QSharedPointer<QBuffer> buffer{new QBuffer{this}};
buffer->setData(data);
emit startedUpload();
- if (media == "image")
- emit image(buffer, filename);
- else if (media == "audio")
- emit audio(buffer, filename);
- else if (media == "video")
- emit video(buffer, filename);
- else
- emit file(buffer, filename);
+ emit media(buffer, mediaType, filename);
}
void
@@ -580,10 +575,7 @@ TextInputWidget::TextInputWidget(QWidget *parent)
connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage);
connect(input_, &FilteredTextEdit::reply, this, &TextInputWidget::sendReplyMessage);
connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command);
- connect(input_, &FilteredTextEdit::image, this, &TextInputWidget::uploadImage);
- connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio);
- connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo);
- connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile);
+ connect(input_, &FilteredTextEdit::media, this, &TextInputWidget::uploadMedia);
connect(emojiBtn_,
SIGNAL(emojiSelected(const QString &)),
this,
@@ -642,14 +634,8 @@ TextInputWidget::openFileSelection()
const auto format = mime.name().split("/")[0];
QSharedPointer<QFile> file{new QFile{fileName, this}};
- if (format == "image")
- emit uploadImage(file, fileName);
- else if (format == "audio")
- emit uploadAudio(file, fileName);
- else if (format == "video")
- emit uploadVideo(file, fileName);
- else
- emit uploadFile(file, fileName);
+
+ emit uploadMedia(file, format, fileName);
showUploadSpinner();
}
diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h
index 71f794d1..d498be72 100644
--- a/src/TextInputWidget.h
+++ b/src/TextInputWidget.h
@@ -63,10 +63,7 @@ signals:
void message(QString);
void reply(QString, const RelatedInfo &);
void command(QString name, QString args);
- void image(QSharedPointer<QIODevice> data, const QString &filename);
- void audio(QSharedPointer<QIODevice> data, const QString &filename);
- void video(QSharedPointer<QIODevice> data, const QString &filename);
- void file(QSharedPointer<QIODevice> data, const QString &filename);
+ void media(QSharedPointer<QIODevice> data, QString mimeClass, const QString &filename);
//! Trigger the suggestion popup.
void showSuggestions(const QString &query);
@@ -179,10 +176,9 @@ signals:
void sendEmoteMessage(QString msg);
void heightChanged(int height);
- void uploadImage(const QSharedPointer<QIODevice> data, const QString &filename);
- void uploadFile(const QSharedPointer<QIODevice> data, const QString &filename);
- void uploadAudio(const QSharedPointer<QIODevice> data, const QString &filename);
- void uploadVideo(const QSharedPointer<QIODevice> data, const QString &filename);
+ void uploadMedia(const QSharedPointer<QIODevice> data,
+ QString mimeClass,
+ const QString &filename);
void sendJoinRoomRequest(const QString &room);
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index b904dfd7..2c58e2f5 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -3,11 +3,15 @@
#include <algorithm>
#include <type_traits>
+#include <QFileDialog>
+#include <QMimeDatabase>
#include <QRegularExpression>
+#include <QStandardPaths>
#include "ChatPage.h"
#include "Logging.h"
#include "MainWindow.h"
+#include "MxcImageProvider.h"
#include "Olm.h"
#include "TimelineViewManager.h"
#include "Utils.h"
@@ -89,16 +93,41 @@ eventFormattedBody(const mtx::events::RoomEvent<T> &e)
}
template<class T>
+boost::optional<mtx::crypto::EncryptedFile>
+eventEncryptionInfo(const mtx::events::Event<T> &)
+{
+ return boost::none;
+}
+
+template<class T>
+auto
+eventEncryptionInfo(const mtx::events::RoomEvent<T> &e) -> std::enable_if_t<
+ std::is_same<decltype(e.content.file), boost::optional<mtx::crypto::EncryptedFile>>::value,
+ boost::optional<mtx::crypto::EncryptedFile>>
+{
+ return e.content.file;
+}
+
+template<class T>
QString
eventUrl(const mtx::events::Event<T> &)
{
return "";
}
+
+QString
+eventUrl(const mtx::events::StateEvent<mtx::events::state::Avatar> &e)
+{
+ return QString::fromStdString(e.content.url);
+}
+
template<class T>
auto
eventUrl(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.url), std::string>::value, QString>
{
+ if (e.content.file)
+ return QString::fromStdString(e.content.file->url);
return QString::fromStdString(e.content.url);
}
@@ -644,6 +673,19 @@ TimelineModel::internalAddEvents(
continue; // don't insert redaction into timeline
}
+ if (auto event =
+ boost::get<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
+ auto temp = decryptEvent(*event).event;
+ auto encInfo = boost::apply_visitor(
+ [](const auto &ev) -> boost::optional<mtx::crypto::EncryptedFile> {
+ return eventEncryptionInfo(ev);
+ },
+ temp);
+
+ if (encInfo)
+ emit newEncryptedImage(encInfo.value());
+ }
+
this->events.insert(id, e);
ids.push_back(id);
}
@@ -1342,3 +1384,158 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
if (!isProcessingPending)
emit nextPendingMessage();
}
+
+void
+TimelineModel::saveMedia(QString eventId) const
+{
+ mtx::events::collections::TimelineEvents event = events.value(eventId);
+
+ if (auto e = boost::get<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&event)) {
+ event = decryptEvent(*e).event;
+ }
+
+ QString mxcUrl =
+ boost::apply_visitor([](const auto &e) -> QString { return eventUrl(e); }, event);
+ QString originalFilename =
+ boost::apply_visitor([](const auto &e) -> QString { return eventFilename(e); }, event);
+ QString mimeType =
+ boost::apply_visitor([](const auto &e) -> QString { return eventMimeType(e); }, event);
+
+ using EncF = boost::optional<mtx::crypto::EncryptedFile>;
+ EncF encryptionInfo =
+ boost::apply_visitor([](const auto &e) -> EncF { return eventEncryptionInfo(e); }, event);
+
+ qml_mtx_events::EventType eventType = boost::apply_visitor(
+ [](const auto &e) -> qml_mtx_events::EventType { return toRoomEventType(e); }, event);
+
+ QString dialogTitle;
+ if (eventType == qml_mtx_events::EventType::ImageMessage) {
+ dialogTitle = tr("Save image");
+ } else if (eventType == qml_mtx_events::EventType::VideoMessage) {
+ dialogTitle = tr("Save video");
+ } else if (eventType == qml_mtx_events::EventType::AudioMessage) {
+ dialogTitle = tr("Save audio");
+ } else {
+ dialogTitle = tr("Save file");
+ }
+
+ QString filterString = QMimeDatabase().mimeTypeForName(mimeType).filterString();
+
+ auto filename = QFileDialog::getSaveFileName(
+ manager_->getWidget(), dialogTitle, originalFilename, filterString);
+
+ if (filename.isEmpty())
+ return;
+
+ const auto url = mxcUrl.toStdString();
+
+ http::client()->download(
+ url,
+ [filename, url, encryptionInfo](const std::string &data,
+ const std::string &,
+ const std::string &,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn("failed to retrieve image {}: {} {}",
+ url,
+ err->matrix_error.error,
+ static_cast<int>(err->status_code));
+ return;
+ }
+
+ try {
+ auto temp = data;
+ if (encryptionInfo)
+ temp = mtx::crypto::to_string(
+ mtx::crypto::decrypt_file(temp, encryptionInfo.value()));
+
+ QFile file(filename);
+
+ if (!file.open(QIODevice::WriteOnly))
+ return;
+
+ file.write(QByteArray(temp.data(), (int)temp.size()));
+ file.close();
+ } catch (const std::exception &e) {
+ nhlog::ui()->warn("Error while saving file to: {}", e.what());
+ }
+ });
+}
+
+void
+TimelineModel::cacheMedia(QString eventId)
+{
+ mtx::events::collections::TimelineEvents event = events.value(eventId);
+
+ if (auto e = boost::get<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&event)) {
+ event = decryptEvent(*e).event;
+ }
+
+ QString mxcUrl =
+ boost::apply_visitor([](const auto &e) -> QString { return eventUrl(e); }, event);
+ QString mimeType =
+ boost::apply_visitor([](const auto &e) -> QString { return eventMimeType(e); }, event);
+
+ using EncF = boost::optional<mtx::crypto::EncryptedFile>;
+ EncF encryptionInfo =
+ boost::apply_visitor([](const auto &e) -> EncF { return eventEncryptionInfo(e); }, event);
+
+ // If the message is a link to a non mxcUrl, don't download it
+ if (!mxcUrl.startsWith("mxc://")) {
+ emit mediaCached(mxcUrl, mxcUrl);
+ return;
+ }
+
+ QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix();
+
+ const auto url = mxcUrl.toStdString();
+ QFileInfo filename(QString("%1/media_cache/%2.%3")
+ .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))
+ .arg(QString(mxcUrl).remove("mxc://"))
+ .arg(suffix));
+ if (QDir::cleanPath(filename.path()) != filename.path()) {
+ nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
+ return;
+ }
+
+ QDir().mkpath(filename.path());
+
+ if (filename.isReadable()) {
+ emit mediaCached(mxcUrl, filename.filePath());
+ return;
+ }
+
+ http::client()->download(
+ url,
+ [this, mxcUrl, filename, url, encryptionInfo](const std::string &data,
+ const std::string &,
+ const std::string &,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn("failed to retrieve image {}: {} {}",
+ url,
+ err->matrix_error.error,
+ static_cast<int>(err->status_code));
+ return;
+ }
+
+ try {
+ auto temp = data;
+ if (encryptionInfo)
+ temp = mtx::crypto::to_string(
+ mtx::crypto::decrypt_file(temp, encryptionInfo.value()));
+
+ QFile file(filename.filePath());
+
+ if (!file.open(QIODevice::WriteOnly))
+ return;
+
+ file.write(QByteArray(temp.data(), temp.size()));
+ file.close();
+ } catch (const std::exception &e) {
+ nhlog::ui()->warn("Error while saving file to: {}", e.what());
+ }
+
+ emit mediaCached(mxcUrl, filename.filePath());
+ });
+}
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index e7842b99..06c64acf 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -6,6 +6,7 @@
#include <QHash>
#include <QSet>
+#include <mtx/common.hpp>
#include <mtx/responses.hpp>
#include "Cache.h"
@@ -159,6 +160,8 @@ public:
Q_INVOKABLE void redactEvent(QString id);
Q_INVOKABLE int idToIndex(QString id) const;
Q_INVOKABLE QString indexToId(int index) const;
+ Q_INVOKABLE void cacheMedia(QString eventId);
+ Q_INVOKABLE void saveMedia(QString eventId) const;
void addEvents(const mtx::responses::Timeline &events);
template<class T>
@@ -185,6 +188,8 @@ signals:
void eventRedacted(QString id);
void nextPendingMessage();
void newMessageToSend(mtx::events::collections::TimelineEvents event);
+ void mediaCached(QString mxcUrl, QString cacheUrl);
+ void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
private:
DecryptionResult decryptEvent(
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 2a88c882..6e18d111 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -1,11 +1,8 @@
#include "TimelineViewManager.h"
-#include <QFileDialog>
#include <QMetaType>
-#include <QMimeDatabase>
#include <QPalette>
#include <QQmlContext>
-#include <QStandardPaths>
#include "ChatPage.h"
#include "ColorImageProvider.h"
@@ -105,9 +102,14 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
void
TimelineViewManager::addRoom(const QString &room_id)
{
- if (!models.contains(room_id))
- models.insert(room_id,
- QSharedPointer<TimelineModel>(new TimelineModel(this, room_id)));
+ if (!models.contains(room_id)) {
+ QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
+ connect(newRoom.data(),
+ &TimelineModel::newEncryptedImage,
+ imgProvider,
+ &MxcImageProvider::addEncryptionInfo);
+ models.insert(room_id, std::move(newRoom));
+ }
}
void
@@ -124,146 +126,24 @@ TimelineViewManager::setHistoryView(const QString &room_id)
}
void
-TimelineViewManager::openImageOverlay(QString mxcUrl,
- QString originalFilename,
- QString mimeType,
- qml_mtx_events::EventType eventType) const
+TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const
{
QQuickImageResponse *imgResponse =
imgProvider->requestImageResponse(mxcUrl.remove("mxc://"), QSize());
- connect(imgResponse,
- &QQuickImageResponse::finished,
- this,
- [this, mxcUrl, originalFilename, mimeType, eventType, imgResponse]() {
- if (!imgResponse->errorString().isEmpty()) {
- nhlog::ui()->error("Error when retrieving image for overlay: {}",
- imgResponse->errorString().toStdString());
- return;
- }
- auto pixmap = QPixmap::fromImage(imgResponse->textureFactory()->image());
+ connect(imgResponse, &QQuickImageResponse::finished, this, [this, eventId, imgResponse]() {
+ if (!imgResponse->errorString().isEmpty()) {
+ nhlog::ui()->error("Error when retrieving image for overlay: {}",
+ imgResponse->errorString().toStdString());
+ return;
+ }
+ auto pixmap = QPixmap::fromImage(imgResponse->textureFactory()->image());
- auto imgDialog = new dialogs::ImageOverlay(pixmap);
- imgDialog->show();
- connect(imgDialog,
- &dialogs::ImageOverlay::saving,
- this,
- [this, mxcUrl, originalFilename, mimeType, eventType]() {
- saveMedia(mxcUrl, originalFilename, mimeType, eventType);
- });
+ auto imgDialog = new dialogs::ImageOverlay(pixmap);
+ imgDialog->show();
+ connect(imgDialog, &dialogs::ImageOverlay::saving, timeline_, [this, eventId]() {
+ timeline_->saveMedia(eventId);
});
-}
-
-void
-TimelineViewManager::saveMedia(QString mxcUrl,
- QString originalFilename,
- QString mimeType,
- qml_mtx_events::EventType eventType) const
-{
- QString dialogTitle;
- if (eventType == qml_mtx_events::EventType::ImageMessage) {
- dialogTitle = tr("Save image");
- } else if (eventType == qml_mtx_events::EventType::VideoMessage) {
- dialogTitle = tr("Save video");
- } else if (eventType == qml_mtx_events::EventType::AudioMessage) {
- dialogTitle = tr("Save audio");
- } else {
- dialogTitle = tr("Save file");
- }
-
- QString filterString = QMimeDatabase().mimeTypeForName(mimeType).filterString();
-
- auto filename =
- QFileDialog::getSaveFileName(container, dialogTitle, originalFilename, filterString);
-
- if (filename.isEmpty())
- return;
-
- const auto url = mxcUrl.toStdString();
-
- http::client()->download(
- url,
- [filename, url](const std::string &data,
- const std::string &,
- const std::string &,
- mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn("failed to retrieve image {}: {} {}",
- url,
- err->matrix_error.error,
- static_cast<int>(err->status_code));
- return;
- }
-
- try {
- QFile file(filename);
-
- if (!file.open(QIODevice::WriteOnly))
- return;
-
- file.write(QByteArray(data.data(), (int)data.size()));
- file.close();
- } catch (const std::exception &e) {
- nhlog::ui()->warn("Error while saving file to: {}", e.what());
- }
- });
-}
-
-void
-TimelineViewManager::cacheMedia(QString mxcUrl, QString mimeType)
-{
- // If the message is a link to a non mxcUrl, don't download it
- if (!mxcUrl.startsWith("mxc://")) {
- emit mediaCached(mxcUrl, mxcUrl);
- return;
- }
-
- QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix();
-
- const auto url = mxcUrl.toStdString();
- QFileInfo filename(QString("%1/media_cache/%2.%3")
- .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))
- .arg(QString(mxcUrl).remove("mxc://"))
- .arg(suffix));
- if (QDir::cleanPath(filename.path()) != filename.path()) {
- nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
- return;
- }
-
- QDir().mkpath(filename.path());
-
- if (filename.isReadable()) {
- emit mediaCached(mxcUrl, filename.filePath());
- return;
- }
-
- http::client()->download(
- url,
- [this, mxcUrl, filename, url](const std::string &data,
- const std::string &,
- const std::string &,
- mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn("failed to retrieve image {}: {} {}",
- url,
- err->matrix_error.error,
- static_cast<int>(err->status_code));
- return;
- }
-
- try {
- QFile file(filename.filePath());
-
- if (!file.open(QIODevice::WriteOnly))
- return;
-
- file.write(QByteArray(data.data(), data.size()));
- file.close();
- } catch (const std::exception &e) {
- nhlog::ui()->warn("Error while saving file to: {}", e.what());
- }
-
- emit mediaCached(mxcUrl, filename.filePath());
- });
+ });
}
void
@@ -342,6 +222,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
void
TimelineViewManager::queueImageMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize,
@@ -354,27 +235,32 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
image.url = url.toStdString();
image.info.h = dimensions.height();
image.info.w = dimensions.width();
+ image.file = file;
models.value(roomid)->sendMessage(image);
}
void
-TimelineViewManager::queueFileMessage(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- uint64_t dsize)
+TimelineViewManager::queueFileMessage(
+ const QString &roomid,
+ const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &encryptedFile,
+ const QString &url,
+ const QString &mime,
+ uint64_t dsize)
{
mtx::events::msg::File file;
file.info.mimetype = mime.toStdString();
file.info.size = dsize;
file.body = filename.toStdString();
file.url = url.toStdString();
+ file.file = encryptedFile;
models.value(roomid)->sendMessage(file);
}
void
TimelineViewManager::queueAudioMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize)
@@ -384,12 +270,14 @@ TimelineViewManager::queueAudioMessage(const QString &roomid,
audio.info.size = dsize;
audio.body = filename.toStdString();
audio.url = url.toStdString();
+ audio.file = file;
models.value(roomid)->sendMessage(audio);
}
void
TimelineViewManager::queueVideoMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize)
@@ -399,5 +287,6 @@ TimelineViewManager::queueVideoMessage(const QString &roomid,
video.info.size = dsize;
video.body = filename.toStdString();
video.url = url.toStdString();
+ video.file = file;
models.value(roomid)->sendMessage(video);
}
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 0bc58e68..9e8de616 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -5,6 +5,7 @@
#include <QSharedPointer>
#include <QWidget>
+#include <mtx/common.hpp>
#include <mtx/responses.hpp>
#include "Cache.h"
@@ -35,38 +36,13 @@ public:
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
- void openImageOverlay(QString mxcUrl,
- QString originalFilename,
- QString mimeType,
- qml_mtx_events::EventType eventType) const;
- void saveMedia(QString mxcUrl,
- QString originalFilename,
- QString mimeType,
- qml_mtx_events::EventType eventType) const;
- Q_INVOKABLE void cacheMedia(QString mxcUrl, QString mimeType);
- // Qml can only pass enum as int
- Q_INVOKABLE void openImageOverlay(QString mxcUrl,
- QString originalFilename,
- QString mimeType,
- int eventType) const
- {
- openImageOverlay(
- mxcUrl, originalFilename, mimeType, (qml_mtx_events::EventType)eventType);
- }
- Q_INVOKABLE void saveMedia(QString mxcUrl,
- QString originalFilename,
- QString mimeType,
- int eventType) const
- {
- saveMedia(mxcUrl, originalFilename, mimeType, (qml_mtx_events::EventType)eventType);
- }
+ Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
signals:
void clearRoomMessageCount(QString roomid);
void updateRoomsLastMessage(QString roomid, const DescInfo &info);
void activeTimelineChanged(TimelineModel *timeline);
void initialSyncChanged(bool isInitialSync);
- void mediaCached(QString mxcUrl, QString cacheUrl);
public slots:
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
@@ -80,22 +56,26 @@ public slots:
void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize,
const QSize &dimensions);
void queueFileMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize);
void queueAudioMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize);
void queueVideoMessage(const QString &roomid,
const QString &filename,
+ const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
const QString &mime,
uint64_t dsize);
|