Merge pull request #100 from Nheko-Reborn/file-encryption

Add file encryption / decryption support
This commit is contained in:
Joseph Donofry 2019-12-05 20:15:01 -05:00 committed by GitHub
commit 9d9b214e4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 734 additions and 894 deletions

View file

@ -31,8 +31,8 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
QT_PKG="59" QT_PKG="59"
fi fi
wget https://cmake.org/files/v3.12/cmake-3.12.2-Linux-x86_64.sh wget https://cmake.org/files/v3.15/cmake-3.15.5-Linux-x86_64.sh
sudo sh cmake-3.12.2-Linux-x86_64.sh --skip-license --prefix=/usr/local sudo sh cmake-3.15.5-Linux-x86_64.sh --skip-license --prefix=/usr/local
mkdir -p build-libsodium mkdir -p build-libsodium
( cd build-libsodium ( cd build-libsodium

View file

@ -13,6 +13,9 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo update-alternatives --set gcc "/usr/bin/${C_COMPILER}" sudo update-alternatives --set gcc "/usr/bin/${C_COMPILER}"
sudo update-alternatives --set g++ "/usr/bin/${CXX_COMPILER}" sudo update-alternatives --set g++ "/usr/bin/${CXX_COMPILER}"
export PATH="/usr/local/bin/:${PATH}"
cmake --version
fi fi
if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ "$TRAVIS_OS_NAME" = "linux" ]; then
@ -35,7 +38,8 @@ cmake --build .deps
# Build nheko # Build nheko
cmake -GNinja -H. -Bbuild \ cmake -GNinja -H. -Bbuild \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \ -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 cmake --build build
if [ "$TRAVIS_OS_NAME" = "osx" ]; then if [ "$TRAVIS_OS_NAME" = "osx" ]; then

View file

@ -259,7 +259,7 @@ include(FeatureSummary)
set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF) set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_MULTITHREADED ON) set(Boost_USE_MULTITHREADED ON)
find_package(Boost 1.66 REQUIRED find_package(Boost 1.70 REQUIRED
COMPONENTS atomic COMPONENTS atomic
chrono chrono
date_time date_time
@ -365,6 +365,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/CommunitiesList.h src/CommunitiesList.h
src/LoginPage.h src/LoginPage.h
src/MainWindow.h src/MainWindow.h
src/MxcImageProvider.h
src/InviteeItem.h src/InviteeItem.h
src/QuickSwitcher.h src/QuickSwitcher.h
src/RegisterPage.h src/RegisterPage.h

View file

@ -92,11 +92,11 @@ sudo port install nheko
- Qt5 (5.8 or greater). Qt 5.7 adds support for color font rendering with - 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 Freetype, which is essential to properly support emoji, 5.8 adds some features
to make interopability with Qml easier. 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) - [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
- [LMDB](https://symas.com/lightning-memory-mapped-database/) - [LMDB](https://symas.com/lightning-memory-mapped-database/)
- [cmark](https://github.com/commonmark/cmark) - [cmark](https://github.com/commonmark/cmark)
- Boost 1.66 or greater. - Boost 1.70 or greater.
- [libolm](https://git.matrix.org/git/olm) - [libolm](https://git.matrix.org/git/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)

View file

@ -34,6 +34,7 @@ install:
lmdb:%PLATFORM%-windows lmdb:%PLATFORM%-windows
openssl:%PLATFORM%-windows openssl:%PLATFORM%-windows
zlib:%PLATFORM%-windows zlib:%PLATFORM%-windows
- vcpkg upgrade --no-dry-run
build_script: build_script:
# VERSION format: branch-master/branch-1.2 # VERSION format: branch-master/branch-1.2

12
deps/CMakeLists.txt vendored
View file

@ -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) option(MTX_STATIC "Compile / link bundled mtx client statically" OFF)
if(USE_BUNDLED_BOOST) if(USE_BUNDLED_BOOST)
# bundled boost is 1.68, which requires CMake 3.12 or greater. # bundled boost is 1.70, which requires CMake 3.15 or greater.
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 3.15)
endif() endif()
include(ExternalProject) include(ExternalProject)
set(BOOST_URL 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 set(BOOST_SHA256
8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406) 430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778)
set( set(
MTXCLIENT_URL MTXCLIENT_URL
https://github.com/Nheko-Reborn/mtxclient/archive/6eee767cc25a9db9f125843e584656cde1ebb6c5.tar.gz https://github.com/Nheko-Reborn/mtxclient/archive/64182a84e35378113f7d3a80f3073894416480e7.zip
) )
set(MTXCLIENT_HASH set(MTXCLIENT_HASH
72fe77da4fed98b3cf069299f66092c820c900359a27ec26070175f9ad208a03) c9973501920046f04c72983472451736343d00e7a40f4d4a12181191093a5fab)
set( set(
TWEENY_URL TWEENY_URL
https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation>Hochladen des Bildes fehlgeschlagen. Bitte versuche es erneut.</translation> <translation>Medienupload fehlgeschlagen. Bitte versuche es erneut.</translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation>Wiederherstellung des OLM Accounts fehlgeschlagen. Bitte logge dich erneut ein.</translation> <translation>Wiederherstellung des OLM Accounts fehlgeschlagen. Bitte logge dich erneut ein.</translation>
</message> </message>
@ -194,6 +179,19 @@
<translation>OK</translation> <translation>OK</translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Raum suchen</translation> <translation>Raum suchen</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation>gelöscht</translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation>Versende Datei</translation> <translation>Versende Datei</translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Schreibe eine Nachricht</translation> <translation>Schreibe eine Nachricht</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation>Emoji</translation> <translation>Emoji</translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Datei auswählen</translation> <translation>Datei auswählen</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation>-- verschlüsselter Event (keine Schlüssel zur Entschlüsselung gefunden) --</translation> <translation>-- verschlüsselter Event (keine Schlüssel zur Entschlüsselung gefunden) --</translation>
@ -423,10 +413,30 @@
<translation>-- verschlüsselter Event (Unbekannter Eventtyp) --</translation> <translation>-- verschlüsselter Event (Unbekannter Eventtyp) --</translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation>Nachricht zurückziehen fehlgeschlagen: %1</translation> <translation>Nachricht zurückziehen fehlgeschlagen: %1</translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation>Kein Raum geöffnet</translation> <translation>Kein Raum geöffnet</translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -194,6 +179,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Αναζήτηση συνομιλίας...</translation> <translation>Αναζήτηση συνομιλίας...</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Γράψε ένα μήνυμα...</translation> <translation>Γράψε ένα μήνυμα...</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Διάλεξε ένα αρχείο</translation> <translation>Διάλεξε ένα αρχείο</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -423,10 +413,30 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation>Failed to upload image. Please try again.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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>
</message>
<message>
<location line="+393"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation>Failed to restore OLM account. Please login again.</translation> <translation>Failed to restore OLM account. Please login again.</translation>
</message> </message>
@ -194,6 +179,19 @@
<translation>OK</translation> <translation>OK</translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Search for a room</translation> <translation>Search for a room</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation>Send a file</translation> <translation>Send a file</translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Write a message</translation> <translation>Write a message</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation>Emoji</translation> <translation>Emoji</translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Select a file</translation> <translation>Select a file</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished">-- Encrypted Event (No keys found for decryption) --</translation> <translation type="unfinished">-- Encrypted Event (No keys found for decryption) --</translation>
@ -423,10 +413,30 @@
<translation type="unfinished">-- Encrypted Event (Unknown event type) --</translation> <translation type="unfinished">-- Encrypted Event (Unknown event type) --</translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished">Message redaction failed: %1</translation> <translation type="unfinished">Message redaction failed: %1</translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation>Kuvan lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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>
</message>
<message>
<location line="+393"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation>OLM-tilin palauttaminen epäonnistui. Ole hyvä ja kirjaudu sisään uudelleen.</translation> <translation>OLM-tilin palauttaminen epäonnistui. Ole hyvä ja kirjaudu sisään uudelleen.</translation>
</message> </message>
@ -194,6 +179,19 @@
<translation>OK</translation> <translation>OK</translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Etsi huonetta</translation> <translation>Etsi huonetta</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation>Lähetä tiedosto</translation> <translation>Lähetä tiedosto</translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Kirjoita viesti</translation> <translation>Kirjoita viesti</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation>Emoji</translation> <translation>Emoji</translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Valitse tiedosto</translation> <translation>Valitse tiedosto</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished">-- Salattu viesti (salauksen purkuavaimia ei löydetty) --</translation> <translation type="unfinished">-- Salattu viesti (salauksen purkuavaimia ei löydetty) --</translation>
@ -423,10 +413,30 @@
<translation type="unfinished">-- Salattu viesti (tuntematon viestityyppi) --</translation> <translation type="unfinished">-- Salattu viesti (tuntematon viestityyppi) --</translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished">Viestin poisto epäonnistui: %1</translation> <translation type="unfinished">Viestin poisto epäonnistui: %1</translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -194,6 +179,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Chercher un salon</translation> <translation>Chercher un salon</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -355,13 +345,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Écrivez un message...</translation> <translation>Écrivez un message...</translation>
</message> </message>
@ -376,7 +366,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Sélectionnez un fichier</translation> <translation>Sélectionnez un fichier</translation>
</message> </message>
@ -394,7 +384,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -424,10 +414,30 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location line="+453"/>
<source>Save image</source>
<translation type="unfinished">Enregistrer l&apos;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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -475,29 +485,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>TimelineViewManager</name>
<message>
<location filename="../../src/timeline/TimelineViewManager.cpp" line="+161"/>
<source>Save image</source>
<translation type="unfinished">Enregistrer l&apos;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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -194,6 +179,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Zoek een kamer...</translation> <translation>Zoek een kamer...</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Typ een bericht...</translation> <translation>Typ een bericht...</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Kies een bestand</translation> <translation>Kies een bestand</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -423,10 +413,30 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation>Nie udało się wysłać obrazu. Spróbuj ponownie.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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>
</message>
<message>
<location line="+393"/>
<source>Failed to restore OLM account. Please login again.</source> <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> <translation>Nie udało się przywrócić konta OLM. Spróbuj zalogować się ponownie.</translation>
</message> </message>
@ -194,6 +179,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Wyszukaj pokoju</translation> <translation>Wyszukaj pokoju</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation>Wyślij plik</translation> <translation>Wyślij plik</translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Napisz wiadomość</translation> <translation>Napisz wiadomość</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation>Emoji</translation> <translation>Emoji</translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Wybierz plik</translation> <translation>Wybierz plik</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -423,10 +413,30 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished">Redagowanie wiadomości nie powiodło się: %1</translation> <translation type="unfinished">Redagowanie wiadomości nie powiodło się: %1</translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation>Не удалось загрузить изображение. Пожалуйста, попробуйте еще раз.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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>
</message>
<message>
<location line="+393"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation>Не удалось восстановить учетную запись OLM. Пожалуйста, войдите снова.</translation> <translation>Не удалось восстановить учетную запись OLM. Пожалуйста, войдите снова.</translation>
</message> </message>
@ -194,6 +179,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>Поиск комнаты...</translation> <translation>Поиск комнаты...</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation>Отправить файл</translation> <translation>Отправить файл</translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>Написать сообщение...</translation> <translation>Написать сообщение...</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation>Выберите файл</translation> <translation>Выберите файл</translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -423,10 +413,30 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished">Ошибка редактирования сообщения: %1</translation> <translation type="unfinished">Ошибка редактирования сообщения: %1</translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -4,27 +4,12 @@
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
<message> <message>
<location filename="../../src/ChatPage.cpp" line="+330"/> <location filename="../../src/ChatPage.cpp" line="+346"/>
<source>Failed to upload image. Please try again.</source> <source>Failed to upload media. Please try again.</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+45"/> <location line="+389"/>
<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>
</message>
<message>
<location line="+393"/>
<source>Failed to restore OLM account. Please login again.</source> <source>Failed to restore OLM account. Please login again.</source>
<translation> OLM </translation> <translation> OLM </translation>
</message> </message>
@ -194,6 +179,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>Placeholder</name> <name>Placeholder</name>
<message> <message>
@ -210,14 +208,6 @@
<translation>...</translation> <translation>...</translation>
</message> </message>
</context> </context>
<context>
<name>Redacted</name>
<message>
<location filename="../qml/delegates/Redacted.qml" line="+5"/>
<source>redacted</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>RegisterPage</name> <name>RegisterPage</name>
<message> <message>
@ -354,13 +344,13 @@
<context> <context>
<name>TextInputWidget</name> <name>TextInputWidget</name>
<message> <message>
<location filename="../../src/TextInputWidget.cpp" line="+507"/> <location filename="../../src/TextInputWidget.cpp" line="+502"/>
<source>Send a file</source> <source>Send a file</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+13"/> <location line="+13"/>
<location filename="../../src/TextInputWidget.h" line="+164"/> <location filename="../../src/TextInputWidget.h" line="+161"/>
<source>Write a message...</source> <source>Write a message...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>
@ -375,7 +365,7 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+75"/> <location line="+72"/>
<source>Select a file</source> <source>Select a file</source>
<translation></translation> <translation></translation>
</message> </message>
@ -393,7 +383,7 @@
<context> <context>
<name>TimelineModel</name> <name>TimelineModel</name>
<message> <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> <source>-- Encrypted Event (No keys found for decryption) --</source>
<comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment> <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -423,10 +413,30 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+50"/> <location line="+54"/>
<source>Message redaction failed: %1</source> <source>Message redaction failed: %1</source>
<translation type="unfinished">%1</translation> <translation type="unfinished">%1</translation>
</message> </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>
<context> <context>
<name>TimelineRow</name> <name>TimelineRow</name>
@ -474,29 +484,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </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> <context>
<name>TopRoomBar</name> <name>TopRoomBar</name>
<message> <message>

View file

@ -97,7 +97,7 @@ RowLayout {
MenuItem { MenuItem {
visible: model.type == MtxEvent.ImageMessage || model.type == MtxEvent.VideoMessage || model.type == MtxEvent.AudioMessage || model.type == MtxEvent.FileMessage || model.type == MtxEvent.Sticker 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") text: qsTr("Save as")
onTriggered: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type) onTriggered: timelineManager.timeline.saveMedia(model.id)
} }
} }
} }

View file

@ -31,7 +31,7 @@ Rectangle {
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type) onClicked: timelineManager.timeline.saveMedia(model.id)
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
} }

View file

@ -17,7 +17,7 @@ Item {
MouseArea { MouseArea {
enabled: model.type == MtxEvent.ImageMessage enabled: model.type == MtxEvent.ImageMessage
anchors.fill: parent anchors.fill: parent
onClicked: timelineManager.openImageOverlay(model.url, model.filename, model.mimetype, model.type) onClicked: timelineManager.openImageOverlay(model.url, model.id)
} }
} }
} }

View file

@ -97,7 +97,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
switch (button.state) { switch (button.state) {
case "": timelineManager.cacheMedia(model.url, model.mimetype); break; case "": timelineManager.timeline.cacheMedia(model.id); break;
case "stopped": case "stopped":
media.play(); console.log("play"); media.play(); console.log("play");
button.state = "playing" button.state = "playing"
@ -118,7 +118,7 @@ Rectangle {
} }
Connections { Connections {
target: timelineManager target: timelineManager.timeline
onMediaCached: { onMediaCached: {
if (mxcUrl == model.url) { if (mxcUrl == model.url) {
media.source = "file://" + cacheUrl media.source = "file://" + cacheUrl

View file

@ -54,6 +54,8 @@ constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000;
constexpr int RETRY_TIMEOUT = 5'000; constexpr int RETRY_TIMEOUT = 5'000;
constexpr size_t MAX_ONETIME_KEYS = 50; constexpr size_t MAX_ONETIME_KEYS = 50;
Q_DECLARE_METATYPE(boost::optional<mtx::crypto::EncryptedFile>)
ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, isConnected_(true) , isConnected_(true)
@ -62,6 +64,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
{ {
setObjectName("chatPage"); setObjectName("chatPage");
qRegisterMetaType<boost::optional<mtx::crypto::EncryptedFile>>(
"boost::optional<mtx::crypto::EncryptedFile>");
topLayout_ = new QHBoxLayout(this); topLayout_ = new QHBoxLayout(this);
topLayout_->setSpacing(0); topLayout_->setSpacing(0);
topLayout_->setMargin(0); topLayout_->setMargin(0);
@ -299,9 +304,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect( connect(
text_input_, text_input_,
&TextInputWidget::uploadImage, &TextInputWidget::uploadMedia,
this, this,
[this](QSharedPointer<QIODevice> dev, const QString &fn) { [this](QSharedPointer<QIODevice> dev, QString mimeClass, const QString &fn) {
QMimeDatabase db; QMimeDatabase db;
QMimeType mime = db.mimeTypeForData(dev.data()); QMimeType mime = db.mimeTypeForData(dev.data());
@ -313,7 +318,16 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
auto bin = dev->peek(dev->size()); auto bin = dev->peek(dev->size());
auto payload = std::string(bin.data(), bin.size()); auto payload = std::string(bin.data(), bin.size());
auto dimensions = QImageReader(dev.data()).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( http::client()->upload(
payload, payload,
@ -322,193 +336,61 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
[this, [this,
room_id = current_room_, room_id = current_room_,
filename = fn, filename = fn,
encryptedFile,
mimeClass,
mime = mime.name(), mime = mime.name(),
size = payload.size(), size = payload.size(),
dimensions](const mtx::responses::ContentURI &res, mtx::http::RequestErr err) { dimensions](const mtx::responses::ContentURI &res, mtx::http::RequestErr err) {
if (err) { if (err) {
emit uploadFailed( emit uploadFailed(
tr("Failed to upload image. Please try again.")); tr("Failed to upload media. Please try again."));
nhlog::net()->warn("failed to upload image: {} {} ({})", nhlog::net()->warn("failed to upload media: {} {} ({})",
err->matrix_error.error, err->matrix_error.error,
to_string(err->matrix_error.errcode), to_string(err->matrix_error.errcode),
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
return; return;
} }
emit imageUploaded(room_id, emit mediaUploaded(room_id,
filename, filename,
encryptedFile,
QString::fromStdString(res.content_uri), QString::fromStdString(res.content_uri),
mimeClass,
mime, mime,
size, size,
dimensions); 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) { connect(this, &ChatPage::uploadFailed, this, [this](const QString &msg) {
text_input_->hideUploadSpinner(); text_input_->hideUploadSpinner();
emit showNotification(msg); emit showNotification(msg);
}); });
connect(this, connect(this,
&ChatPage::imageUploaded, &ChatPage::mediaUploaded,
this, this,
[this](QString roomid, [this](QString roomid,
QString filename, QString filename,
boost::optional<mtx::crypto::EncryptedFile> encryptedFile,
QString url, QString url,
QString mimeClass,
QString mime, QString mime,
qint64 dsize, qint64 dsize,
QSize dimensions) { QSize dimensions) {
text_input_->hideUploadSpinner(); text_input_->hideUploadSpinner();
if (mimeClass == "image")
view_manager_->queueImageMessage( view_manager_->queueImageMessage(
roomid, filename, url, mime, dsize, dimensions); roomid, filename, encryptedFile, url, mime, dsize, dimensions);
}); else if (mimeClass == "audio")
connect(this, view_manager_->queueAudioMessage(
&ChatPage::fileUploaded, roomid, filename, encryptedFile, url, mime, dsize);
this, else if (mimeClass == "video")
[this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) { view_manager_->queueVideoMessage(
text_input_->hideUploadSpinner(); roomid, filename, encryptedFile, url, mime, dsize);
view_manager_->queueFileMessage(roomid, filename, url, mime, dsize); else
}); view_manager_->queueFileMessage(
connect(this, roomid, filename, encryptedFile, url, mime, dsize);
&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);
}); });
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar); connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);

View file

@ -18,7 +18,9 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <boost/optional.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <mtx/common.hpp>
#include <mtx/responses.hpp> #include <mtx/responses.hpp>
#include <QFrame> #include <QFrame>
@ -94,27 +96,14 @@ signals:
const QPoint widgetPos); const QPoint widgetPos);
void uploadFailed(const QString &msg); void uploadFailed(const QString &msg);
void imageUploaded(const QString &roomid, void mediaUploaded(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mimeClass,
const QString &mime, const QString &mime,
qint64 dsize, qint64 dsize,
const QSize &dimensions); 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 contentLoaded();
void closing(); void closing();

View file

@ -5,7 +5,7 @@
void void
MxcImageResponse::run() MxcImageResponse::run()
{ {
if (m_requestedSize.isValid()) { if (m_requestedSize.isValid() && !m_encryptionInfo) {
QString fileName = QString("%1_%2x%3_crop") QString fileName = QString("%1_%2x%3_crop")
.arg(m_id) .arg(m_id)
.arg(m_requestedSize.width()) .arg(m_requestedSize.width())
@ -65,7 +65,12 @@ MxcImageResponse::run()
return; 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.loadFromData(data);
m_image.setText("original filename", m_image.setText("original filename",
QString::fromStdString(originalFilename)); QString::fromStdString(originalFilename));

View file

@ -6,14 +6,21 @@
#include <QImage> #include <QImage>
#include <QThreadPool> #include <QThreadPool>
#include <mtx/common.hpp>
#include <boost/optional.hpp>
class MxcImageResponse class MxcImageResponse
: public QQuickImageResponse : public QQuickImageResponse
, public QRunnable , public QRunnable
{ {
public: 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_id(id)
, m_requestedSize(requestedSize) , m_requestedSize(requestedSize)
, m_encryptionInfo(encryptionInfo)
{ {
setAutoDelete(false); setAutoDelete(false);
} }
@ -29,19 +36,34 @@ public:
QString m_id, m_error; QString m_id, m_error;
QSize m_requestedSize; QSize m_requestedSize;
QImage m_image; 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, QQuickImageResponse *requestImageResponse(const QString &id,
const QSize &requestedSize) override 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); pool.start(response);
return response; return response;
} }
void addEncryptionInfo(mtx::crypto::EncryptedFile info)
{
infos.insert(QString::fromStdString(info.url), info);
}
private: private:
QThreadPool pool; QThreadPool pool;
QHash<QString, mtx::crypto::EncryptedFile> infos;
}; };

View file

@ -458,21 +458,16 @@ FilteredTextEdit::textChanged()
} }
void 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}}; QSharedPointer<QBuffer> buffer{new QBuffer{this}};
buffer->setData(data); buffer->setData(data);
emit startedUpload(); emit startedUpload();
if (media == "image") emit media(buffer, mediaType, filename);
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);
} }
void void
@ -580,10 +575,7 @@ TextInputWidget::TextInputWidget(QWidget *parent)
connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage); connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage);
connect(input_, &FilteredTextEdit::reply, this, &TextInputWidget::sendReplyMessage); connect(input_, &FilteredTextEdit::reply, this, &TextInputWidget::sendReplyMessage);
connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command); connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command);
connect(input_, &FilteredTextEdit::image, this, &TextInputWidget::uploadImage); connect(input_, &FilteredTextEdit::media, this, &TextInputWidget::uploadMedia);
connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio);
connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo);
connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile);
connect(emojiBtn_, connect(emojiBtn_,
SIGNAL(emojiSelected(const QString &)), SIGNAL(emojiSelected(const QString &)),
this, this,
@ -642,14 +634,8 @@ TextInputWidget::openFileSelection()
const auto format = mime.name().split("/")[0]; const auto format = mime.name().split("/")[0];
QSharedPointer<QFile> file{new QFile{fileName, this}}; QSharedPointer<QFile> file{new QFile{fileName, this}};
if (format == "image")
emit uploadImage(file, fileName); emit uploadMedia(file, format, fileName);
else if (format == "audio")
emit uploadAudio(file, fileName);
else if (format == "video")
emit uploadVideo(file, fileName);
else
emit uploadFile(file, fileName);
showUploadSpinner(); showUploadSpinner();
} }

View file

@ -63,10 +63,7 @@ signals:
void message(QString); void message(QString);
void reply(QString, const RelatedInfo &); void reply(QString, const RelatedInfo &);
void command(QString name, QString args); void command(QString name, QString args);
void image(QSharedPointer<QIODevice> data, const QString &filename); void media(QSharedPointer<QIODevice> data, QString mimeClass, 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);
//! Trigger the suggestion popup. //! Trigger the suggestion popup.
void showSuggestions(const QString &query); void showSuggestions(const QString &query);
@ -179,10 +176,9 @@ signals:
void sendEmoteMessage(QString msg); void sendEmoteMessage(QString msg);
void heightChanged(int height); void heightChanged(int height);
void uploadImage(const QSharedPointer<QIODevice> data, const QString &filename); void uploadMedia(const QSharedPointer<QIODevice> data,
void uploadFile(const QSharedPointer<QIODevice> data, const QString &filename); QString mimeClass,
void uploadAudio(const QSharedPointer<QIODevice> data, const QString &filename); const QString &filename);
void uploadVideo(const QSharedPointer<QIODevice> data, const QString &filename);
void sendJoinRoomRequest(const QString &room); void sendJoinRoomRequest(const QString &room);

View file

@ -3,11 +3,15 @@
#include <algorithm> #include <algorithm>
#include <type_traits> #include <type_traits>
#include <QFileDialog>
#include <QMimeDatabase>
#include <QRegularExpression> #include <QRegularExpression>
#include <QStandardPaths>
#include "ChatPage.h" #include "ChatPage.h"
#include "Logging.h" #include "Logging.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "MxcImageProvider.h"
#include "Olm.h" #include "Olm.h"
#include "TimelineViewManager.h" #include "TimelineViewManager.h"
#include "Utils.h" #include "Utils.h"
@ -88,17 +92,42 @@ 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> template<class T>
QString QString
eventUrl(const mtx::events::Event<T> &) eventUrl(const mtx::events::Event<T> &)
{ {
return ""; return "";
} }
QString
eventUrl(const mtx::events::StateEvent<mtx::events::state::Avatar> &e)
{
return QString::fromStdString(e.content.url);
}
template<class T> template<class T>
auto auto
eventUrl(const mtx::events::RoomEvent<T> &e) eventUrl(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.url), std::string>::value, QString> -> 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); return QString::fromStdString(e.content.url);
} }
@ -644,6 +673,19 @@ TimelineModel::internalAddEvents(
continue; // don't insert redaction into timeline 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); this->events.insert(id, e);
ids.push_back(id); ids.push_back(id);
} }
@ -1342,3 +1384,158 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
if (!isProcessingPending) if (!isProcessingPending)
emit nextPendingMessage(); 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());
});
}

View file

@ -6,6 +6,7 @@
#include <QHash> #include <QHash>
#include <QSet> #include <QSet>
#include <mtx/common.hpp>
#include <mtx/responses.hpp> #include <mtx/responses.hpp>
#include "Cache.h" #include "Cache.h"
@ -159,6 +160,8 @@ public:
Q_INVOKABLE void redactEvent(QString id); Q_INVOKABLE void redactEvent(QString id);
Q_INVOKABLE int idToIndex(QString id) const; Q_INVOKABLE int idToIndex(QString id) const;
Q_INVOKABLE QString indexToId(int index) 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); void addEvents(const mtx::responses::Timeline &events);
template<class T> template<class T>
@ -185,6 +188,8 @@ signals:
void eventRedacted(QString id); void eventRedacted(QString id);
void nextPendingMessage(); void nextPendingMessage();
void newMessageToSend(mtx::events::collections::TimelineEvents event); void newMessageToSend(mtx::events::collections::TimelineEvents event);
void mediaCached(QString mxcUrl, QString cacheUrl);
void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
private: private:
DecryptionResult decryptEvent( DecryptionResult decryptEvent(

View file

@ -1,11 +1,8 @@
#include "TimelineViewManager.h" #include "TimelineViewManager.h"
#include <QFileDialog>
#include <QMetaType> #include <QMetaType>
#include <QMimeDatabase>
#include <QPalette> #include <QPalette>
#include <QQmlContext> #include <QQmlContext>
#include <QStandardPaths>
#include "ChatPage.h" #include "ChatPage.h"
#include "ColorImageProvider.h" #include "ColorImageProvider.h"
@ -105,9 +102,14 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
void void
TimelineViewManager::addRoom(const QString &room_id) TimelineViewManager::addRoom(const QString &room_id)
{ {
if (!models.contains(room_id)) if (!models.contains(room_id)) {
models.insert(room_id, QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
QSharedPointer<TimelineModel>(new TimelineModel(this, room_id))); connect(newRoom.data(),
&TimelineModel::newEncryptedImage,
imgProvider,
&MxcImageProvider::addEncryptionInfo);
models.insert(room_id, std::move(newRoom));
}
} }
void void
@ -124,17 +126,11 @@ TimelineViewManager::setHistoryView(const QString &room_id)
} }
void void
TimelineViewManager::openImageOverlay(QString mxcUrl, TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const
QString originalFilename,
QString mimeType,
qml_mtx_events::EventType eventType) const
{ {
QQuickImageResponse *imgResponse = QQuickImageResponse *imgResponse =
imgProvider->requestImageResponse(mxcUrl.remove("mxc://"), QSize()); imgProvider->requestImageResponse(mxcUrl.remove("mxc://"), QSize());
connect(imgResponse, connect(imgResponse, &QQuickImageResponse::finished, this, [this, eventId, imgResponse]() {
&QQuickImageResponse::finished,
this,
[this, mxcUrl, originalFilename, mimeType, eventType, imgResponse]() {
if (!imgResponse->errorString().isEmpty()) { if (!imgResponse->errorString().isEmpty()) {
nhlog::ui()->error("Error when retrieving image for overlay: {}", nhlog::ui()->error("Error when retrieving image for overlay: {}",
imgResponse->errorString().toStdString()); imgResponse->errorString().toStdString());
@ -144,128 +140,12 @@ TimelineViewManager::openImageOverlay(QString mxcUrl,
auto imgDialog = new dialogs::ImageOverlay(pixmap); auto imgDialog = new dialogs::ImageOverlay(pixmap);
imgDialog->show(); imgDialog->show();
connect(imgDialog, connect(imgDialog, &dialogs::ImageOverlay::saving, timeline_, [this, eventId]() {
&dialogs::ImageOverlay::saving, timeline_->saveMedia(eventId);
this,
[this, mxcUrl, originalFilename, mimeType, eventType]() {
saveMedia(mxcUrl, originalFilename, mimeType, eventType);
}); });
}); });
} }
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 void
TimelineViewManager::updateReadReceipts(const QString &room_id, TimelineViewManager::updateReadReceipts(const QString &room_id,
const std::vector<QString> &event_ids) const std::vector<QString> &event_ids)
@ -342,6 +222,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
void void
TimelineViewManager::queueImageMessage(const QString &roomid, TimelineViewManager::queueImageMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize, uint64_t dsize,
@ -354,12 +235,15 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
image.url = url.toStdString(); image.url = url.toStdString();
image.info.h = dimensions.height(); image.info.h = dimensions.height();
image.info.w = dimensions.width(); image.info.w = dimensions.width();
image.file = file;
models.value(roomid)->sendMessage(image); models.value(roomid)->sendMessage(image);
} }
void void
TimelineViewManager::queueFileMessage(const QString &roomid, TimelineViewManager::queueFileMessage(
const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &encryptedFile,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize) uint64_t dsize)
@ -369,12 +253,14 @@ TimelineViewManager::queueFileMessage(const QString &roomid,
file.info.size = dsize; file.info.size = dsize;
file.body = filename.toStdString(); file.body = filename.toStdString();
file.url = url.toStdString(); file.url = url.toStdString();
file.file = encryptedFile;
models.value(roomid)->sendMessage(file); models.value(roomid)->sendMessage(file);
} }
void void
TimelineViewManager::queueAudioMessage(const QString &roomid, TimelineViewManager::queueAudioMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize) uint64_t dsize)
@ -384,12 +270,14 @@ TimelineViewManager::queueAudioMessage(const QString &roomid,
audio.info.size = dsize; audio.info.size = dsize;
audio.body = filename.toStdString(); audio.body = filename.toStdString();
audio.url = url.toStdString(); audio.url = url.toStdString();
audio.file = file;
models.value(roomid)->sendMessage(audio); models.value(roomid)->sendMessage(audio);
} }
void void
TimelineViewManager::queueVideoMessage(const QString &roomid, TimelineViewManager::queueVideoMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize) uint64_t dsize)
@ -399,5 +287,6 @@ TimelineViewManager::queueVideoMessage(const QString &roomid,
video.info.size = dsize; video.info.size = dsize;
video.body = filename.toStdString(); video.body = filename.toStdString();
video.url = url.toStdString(); video.url = url.toStdString();
video.file = file;
models.value(roomid)->sendMessage(video); models.value(roomid)->sendMessage(video);
} }

View file

@ -5,6 +5,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QWidget> #include <QWidget>
#include <mtx/common.hpp>
#include <mtx/responses.hpp> #include <mtx/responses.hpp>
#include "Cache.h" #include "Cache.h"
@ -35,38 +36,13 @@ public:
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
void openImageOverlay(QString mxcUrl, Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
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);
}
signals: signals:
void clearRoomMessageCount(QString roomid); void clearRoomMessageCount(QString roomid);
void updateRoomsLastMessage(QString roomid, const DescInfo &info); void updateRoomsLastMessage(QString roomid, const DescInfo &info);
void activeTimelineChanged(TimelineModel *timeline); void activeTimelineChanged(TimelineModel *timeline);
void initialSyncChanged(bool isInitialSync); void initialSyncChanged(bool isInitialSync);
void mediaCached(QString mxcUrl, QString cacheUrl);
public slots: public slots:
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids); void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
@ -80,22 +56,26 @@ public slots:
void queueEmoteMessage(const QString &msg); void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid, void queueImageMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize, uint64_t dsize,
const QSize &dimensions); const QSize &dimensions);
void queueFileMessage(const QString &roomid, void queueFileMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize); uint64_t dsize);
void queueAudioMessage(const QString &roomid, void queueAudioMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize); uint64_t dsize);
void queueVideoMessage(const QString &roomid, void queueVideoMessage(const QString &roomid,
const QString &filename, const QString &filename,
const boost::optional<mtx::crypto::EncryptedFile> &file,
const QString &url, const QString &url,
const QString &mime, const QString &mime,
uint64_t dsize); uint64_t dsize);