diff --git a/.ci/install.sh b/.ci/install.sh
index d8dd67f2..c1f42357 100755
--- a/.ci/install.sh
+++ b/.ci/install.sh
@@ -4,6 +4,10 @@ set -ex
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update
+
+ # uninstall packages, that would get upgraded by upgrading cmake (and we don't need)
+ brew uninstall --force cgal node sfcgal postgis
+
brew install qt5 lmdb clang-format ninja libsodium cmark
brew upgrade boost cmake icu4c || true
diff --git a/.ci/linux/deploy.sh b/.ci/linux/deploy.sh
index 403fde14..2caf5e0f 100755
--- a/.ci/linux/deploy.sh
+++ b/.ci/linux/deploy.sh
@@ -25,8 +25,8 @@ for iconSize in 16 32 48 64 128 256 512; do
done
# Only download the file when not already present
-if ! [ -f linuxdeployqt-continuous-x86_64.AppImage ] ; then
- wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
+if ! [ -f linuxdeployqt-6-x86_64.AppImage ] ; then
+ wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage"
fi
chmod a+x linuxdeployqt*.AppImage
@@ -49,7 +49,10 @@ done
chmod +x nheko-*x86_64.AppImage
-if [ ! -z "$VERSION" ]; then
+mkdir artifacts
+cp nheko-*x86_64.AppImage artifacts/
+
+if [ -n "$VERSION" ]; then
# commented out for now, as AppImage file appears to already contain the version.
#mv nheko-*x86_64.AppImage nheko-${VERSION}-x86_64.AppImage
echo "nheko-${VERSION}-x86_64.AppImage"
diff --git a/.ci/macos/deploy.sh b/.ci/macos/deploy.sh
index 45ed13bc..ee4acaed 100755
--- a/.ci/macos/deploy.sh
+++ b/.ci/macos/deploy.sh
@@ -25,6 +25,8 @@ PATH=/usr/local/opt/qt/bin/:${PATH}
dmgbuild -s ./.ci/macos/settings.json "Nheko" nheko.dmg
-if [ ! -z "$VERSION" ]; then
+if [ -n "$VERSION" ]; then
mv nheko.dmg "nheko-${VERSION}.dmg"
+ mkdir artifacts
+ cp "nheko-${VERSION}.dmg" artifacts/
fi
diff --git a/.ci/script.sh b/.ci/script.sh
index cf8b524b..ac6bfed6 100755
--- a/.ci/script.sh
+++ b/.ci/script.sh
@@ -28,7 +28,8 @@ fi
cmake -GNinja -Hdeps -B.deps \
-DUSE_BUNDLED_BOOST="${USE_BUNDLED_BOOST}" \
-DUSE_BUNDLED_CMARK="${USE_BUNDLED_CMARK}" \
- -DUSE_BUNDLED_JSON="${USE_BUNDLED_JSON}"
+ -DUSE_BUNDLED_JSON="${USE_BUNDLED_JSON}" \
+ -DMTX_STATIC="${MTX_STATIC:-OFF}"
cmake --build .deps
# Build nheko
@@ -40,11 +41,11 @@ cmake --build build
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
make lint;
- if [ "$DEPLOYMENT" = 1 ] && [ ! -z "$VERSION" ] ; then
+ if [ "$DEPLOYMENT" = 1 ] && [ -n "$VERSION" ] ; then
make macos-deploy;
fi
fi
-if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$DEPLOYMENT" = 1 ] && [ ! -z "$VERSION" ]; then
+if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$DEPLOYMENT" = 1 ] && [ -n "$VERSION" ]; then
make linux-deploy;
fi
diff --git a/.gitignore b/.gitignore
index e9c854d0..43c9b7b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,14 @@
build
tags
+cscope*
.clang_complete
*wintoastlib*
+# GTAGS
+GTAGS
+GRTAGS
+GPATH
+
# C++ objects and libs
*.slo
diff --git a/.travis.yml b/.travis.yml
index fee019c2..6a699043 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,6 +21,7 @@ matrix:
- USE_BUNDLED_BOOST=0
- USE_BUNDLED_CMARK=0
- USE_BUNDLED_JSON=0
+ - MTX_STATIC=1
- os: linux
compiler: gcc
env:
@@ -79,6 +80,21 @@ script:
- sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true
- cp ./.ci/bintray-release.json .
deploy:
+- provider: s3
+ access_key_id: $ARTIFACTS_KEY
+ secret_access_key: $ARTIFACTS_SECRET
+ bucket: $ARTIFACTS_BUCKET
+ region: $AWS_DEFAULT_REGION
+ detect_encoding: true
+ cache_control: "max-age=31536000"
+ skip_cleanup: true
+ acl: public_read
+ local_dir: artifacts
+ on:
+ condition: "$DEPLOYMENT == 1"
+ repo: Nheko-Reborn/nheko
+ tags: false
+ all_branches: true
- provider: bintray
user: "redsky17"
key:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9df96e65..55e59332 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,13 +4,22 @@
### [0.7.0] -- Unreleased
+0.7.0 *requires* mtxclient 0.3.0. Make sure you compile against 0.3.0
+if you do not use the mtxclient bundled with nheko.
+
#### Features
- Make nheko session import / export format match riot. Fixes #48 (WIP)
- Implement proper replies (WIP)
+- Add .well-known support for auto-completing homeserver information
+- Add mentions viewer so you can see all the messages you have been mentioned in (WIP)
+- Add emoji font selection preference
#### Improvements
- Add dedicated reply button to Timeline items. Add button for other options so
that right click isn't always required.
+- Fix various things with regards to emoji rendering and the emoji picker (WIP)
+- Lots and lots and lots of localization updates.
+- Additional tweaks to the system theme
## [0.6.4] - 2019-05-22
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 09eea071..4d5aff7a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -179,7 +179,6 @@ set(SRC_FILES
src/dialogs/LeaveRoom.cpp
src/dialogs/Logout.cpp
src/dialogs/UserProfile.cpp
- src/dialogs/UserMentions.cpp
src/dialogs/ReadReceipts.cpp
src/dialogs/ReCaptcha.cpp
src/dialogs/RoomSettings.cpp
@@ -241,13 +240,13 @@ set(SRC_FILES
src/popups/SuggestionsPopup.cpp
src/popups/PopupItem.cpp
src/popups/ReplyPopup.cpp
+ src/popups/UserMentions.cpp
src/TextInputWidget.cpp
src/TopRoomBar.cpp
src/TrayIcon.cpp
src/TypingDisplay.cpp
src/Utils.cpp
src/UserInfoWidget.cpp
- src/UserMentionsWidget.cpp
src/UserSettingsPage.cpp
src/WelcomePage.cpp
src/main.cpp
@@ -321,7 +320,6 @@ qt5_wrap_cpp(MOC_HEADERS
src/dialogs/MemberList.h
src/dialogs/LeaveRoom.h
src/dialogs/Logout.h
- src/dialogs/UserMentions.h
src/dialogs/UserProfile.h
src/dialogs/RawMessage.h
src/dialogs/ReadReceipts.h
@@ -383,12 +381,12 @@ qt5_wrap_cpp(MOC_HEADERS
src/popups/SuggestionsPopup.h
src/popups/ReplyPopup.h
src/popups/PopupItem.h
+ src/popups/UserMentions.h
src/TextInputWidget.h
src/TopRoomBar.h
src/TrayIcon.h
src/TypingDisplay.h
src/UserInfoWidget.h
- src/UserMentionsWidget.h
src/UserSettingsPage.h
src/WelcomePage.h
)
diff --git a/README.md b/README.md
index 4cd2d319..4dd4c121 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ nheko
[](https://travis-ci.org/Nheko-Reborn/nheko)
[](https://ci.appveyor.com/project/redsky17/nheko/branch/master)
[](https://github.com/Nheko-Reborn/nheko/releases/v0.6.4)
-[](https://bintray.com/nheko-reborn/nheko/nheko)
+[](https://nheko-reborn-artifacts.s3.us-east-2.amazonaws.com/list.html)
[](https://matrix.to/#/#nheko-reborn:matrix.org)
[](https://aur.archlinux.org/packages/nheko)
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index dbc96463..d0a715e0 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -46,10 +46,10 @@ set(BOOST_SHA256
set(
MTXCLIENT_URL
- https://github.com/Nheko-Reborn/mtxclient/archive/37df82363c800b8d6b2b172a486e395e4132e061.tar.gz
+ https://github.com/Nheko-Reborn/mtxclient/archive/6eee767cc25a9db9f125843e584656cde1ebb6c5.tar.gz
)
set(MTXCLIENT_HASH
- b29dd0bc836b69bd4253499519b8396a210bba43750fb66f4ffb8453510c8dd1)
+ 72fe77da4fed98b3cf069299f66092c820c900359a27ec26070175f9ad208a03)
set(
TWEENY_URL
https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz
diff --git a/deps/cmake/Boost.cmake b/deps/cmake/Boost.cmake
index 47eb723b..5d11fd93 100644
--- a/deps/cmake/Boost.cmake
+++ b/deps/cmake/Boost.cmake
@@ -3,6 +3,10 @@ if(WIN32)
return()
endif()
+include(BoostToolsetId)
+set(BOOST_TOOLSET "gcc")
+Boost_Get_ToolsetId(BOOST_TOOLSET)
+
ExternalProject_Add(
Boost
@@ -16,6 +20,7 @@ ExternalProject_Add(
CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/boost/bootstrap.sh
--with-libraries=random,thread,system,iostreams,atomic,chrono,date_time,regex
--prefix=${DEPS_INSTALL_DIR}
+ --with-toolset=${BOOST_TOOLSET}
BUILD_COMMAND ${DEPS_BUILD_DIR}/boost/b2 -d0 cxxstd=14 variant=release link=shared runtime-link=shared threading=multi --layout=system
INSTALL_COMMAND ${DEPS_BUILD_DIR}/boost/b2 -d0 install
)
diff --git a/deps/cmake/BoostToolsetId.cmake b/deps/cmake/BoostToolsetId.cmake
new file mode 100644
index 00000000..f6c231a5
--- /dev/null
+++ b/deps/cmake/BoostToolsetId.cmake
@@ -0,0 +1,35 @@
+# - Translate CMake compilers to the Boost.Build toolset equivalents
+# To build Boost reliably when a non-system compiler may be used, we
+# need to both specify the toolset when running bootstrap.sh *and* in
+# the user-config.jam file.
+#
+# This module provides the following functions to help translate between
+# the systems:
+#
+# function Boost_Get_ToolsetId()
+# Set var equal to Boost's name for the CXX toolchain picked
+# up by CMake. Only supports GNU and Clang families at present.
+# Intel support is provisional
+#
+# downloaded from https://github.com/drbenmorgan/BoostBuilder/blob/master/BoostToolsetId.cmake
+
+function(Boost_Get_ToolsetId _var)
+ set(BOOST_TOOLSET)
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+ if(APPLE)
+ set(BOOST_TOOLSET "darwin")
+ else()
+ set(BOOST_TOOLSET "gcc")
+ endif()
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+ set(BOOST_TOOLSET "clang")
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+ set(BOOST_TOOLSET "intel")
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+ set(BOOST_TOOLSET "msvc")
+ endif()
+
+ set(${_var} ${BOOST_TOOLSET} PARENT_SCOPE)
+endfunction()
+
diff --git a/resources/langs/nheko_de.ts b/resources/langs/nheko_de.ts
index 0404eca6..e92bf966 100644
--- a/resources/langs/nheko_de.ts
+++ b/resources/langs/nheko_de.ts
@@ -4,17 +4,17 @@
AudioItem
-
+
Save File
- Datei speichern
+ In Datei speichern
ChatPage
-
+
Failed to upload image. Please try again.
- Hochladen der Bilddatei fehlgeschlagen. Bitte versuche es erneut.
+ Hochladen des Bildes fehlgeschlagen. Bitte versuche es erneut.
@@ -32,25 +32,25 @@
Hochladen der Videodatei fehlgeschlagen. Bitte versuche es erneut.
-
+
Failed to restore OLM account. Please login again.
-
+ Wiederherstellung des OLM Accounts fehlgeschlagen. Bitte logge dich erneut ein.
Failed to restore save data. Please login again.
- Nachrichten konnten nicht aus dem Cache geladen werden. Bitte melde dich erneut an.
+ Gespeicherte Nachrichten konnten nicht wiederhergestellt werden. Bitte melde Dich erneut an.
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
-
+ Fehler beim Setup der Verschlüsselungsschlüssel. Servermeldung: %1 %2. Bitte versuche es später erneut.
Please try to login again: %1
- Bitte melde dich erneut an: %1
+ Bitte melde dich erneut an: %1
@@ -68,28 +68,28 @@
All rooms
-
+ Alle Räume
Favourite rooms
-
+ Favoriten
Low priority rooms
-
+ Räume niedriger Priorität
(tag)
-
+ (tag)
(community)
-
+ (community)
@@ -97,17 +97,17 @@
Apply
-
+ Anwenden
Cancel
- Abbrechen
+ Abbrechen
Name
- Titel
+ Name
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
Datei speichern
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
Bild speichern
@@ -136,7 +136,7 @@
Remove
-
+ Löschen
@@ -159,7 +159,7 @@
Device name
-
+ Gerätename
@@ -169,27 +169,27 @@
Autodiscovery failed. Received malformed response.
-
+ Automatische Erkennung fehlgeschlagen. Antwort war fehlerhaft.
Autodiscovery failed. Unknown error when requesting .well-known.
-
+ Automatische Erkennung fehlgeschlagen. Unbekannter Fehler bei Anfrage .well-known.
The required endpoints were not found. Possibly not a Matrix server.
-
+ Benötigte Ansprechpunkte nicht auffindbar. Möglicherweise kein Matrixserver.
Received malformed response. Make sure the homeserver domain is valid.
-
+ Erhaltene Antwort war fehlerhaft. Bitte Homeserverdomain prüfen.
An unknown error occured. Make sure the homeserver domain is valid.
-
+ Ein unbekannter Fehler ist aufgetreten. Bitte Homeserverdomain prüfen.
@@ -205,9 +205,9 @@
Teilnehmerliste
-
- ESC
-
+
+ OK
+ OK
@@ -215,7 +215,7 @@
Search for a room...
- Raum suchen...
+ Raum suchen…
@@ -238,7 +238,7 @@
Home Server
- Heimserver
+ Homeserver
@@ -263,7 +263,7 @@
Invalid server name
- Ungültiger Server-Name
+ Ungültiger Servername
@@ -271,28 +271,28 @@
Logout
-
+ Abmelden
RoomInfo
-
+
no version stored
-
+ keine Version gespeichert
RoomInfoListItem
-
+
Leave room
Raum verlassen
-
+
Accept
- Akzeptieren
+ Akzeptieren
@@ -305,7 +305,7 @@
User settings
-
+ Benutzereinstellungen
@@ -320,59 +320,59 @@
Start a new chat
-
+ Neues Gespräch beginnen
Room directory
-
+ Raumverzeichnis
StatusIndicator
-
+
Encrypted
-
+ Verschlüsselt
Delivered
-
+ Erhalten
Seen
-
+ Gelesen
Sent
-
+ Gesendet
TextInputWidget
-
+
Send a file
-
+ Versende Datei
-
+
Write a message...
- Schreibe eine Nachricht...
+ Schreibe eine Nachricht…
Send a message
-
+ Versende eine Nachricht
Emoji
-
+ Emoji
@@ -387,7 +387,7 @@
Connection lost. Nheko is trying to re-connect...
-
+ Verbindung verloren. Nheko versucht sie wieder aufzunehmen…
@@ -395,17 +395,17 @@
Message redaction failed: %1
-
+ Nachricht zurückziehen fehlgeschlagen: %1
Reply
-
+ Antworten
Options
-
+ Optionen
@@ -413,7 +413,37 @@
Encryption is enabled
-
+ Verschlüsselung aktiv
+
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+ -- verschlüsselter Event (keine Schlüssel zur Entschlüsselung gefunden) --
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+ -- Entschlüsselungsfehler (Fehler bei Kommunikation mit Datenbank) --
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+ -- Entschlüsselungsfehler (Fehler bei Suche nach megolm Schlüsseln in Datenbank) --
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+ -- Entschlüsselungsfehler (%1) --
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+ -- verschlüsselter Event (Unbekannter Eventtyp) --
@@ -421,10 +451,15 @@
Room options
-
+ Raumoptionen
-
+
+ Mentions
+ Erwähnungen
+
+
+
Invite users
Benutzer einladen
@@ -459,15 +494,14 @@
TypingDisplay
-
-
- is typing
- tippt
-
-
-
- are typing
- tippen
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+ %1%2 tippt
+ %1 und %2 tippen
+
@@ -475,13 +509,13 @@
Logout
-
+ Abmelden
UserSettingsPage
-
+
Minimize to tray
Ins Benachrichtigungsfeld minimieren
@@ -508,57 +542,62 @@
Desktop notifications
-
+ Desktopbenachrichtigungen
Scale factor
-
+ Skalierungsfaktor
Font size
-
+ Schriftgröße
-
+
Font Family
-
+ Schriftart
-
+
+ Emoji Font Famly
+ Emoji Schriftart
+
+
+
Theme
Erscheinungsbild
Device ID
-
+ Geräte-ID
Device Fingerprint
-
+ Gerätefingerabdruck
Session Keys
-
+ Sitzungsschlüssel
IMPORT
-
+ IMPORTIEREN
EXPORT
-
+ EXPORTIEREN
ENCRYPTION
-
+ VERSCHLÜSSELUNG
@@ -566,9 +605,9 @@
ALLGEMEINES
-
+
Open Sessions File
-
+ Öffne Sessions Datei
@@ -582,34 +621,34 @@
Error
-
+ Feher
File Password
-
+ Password für Datei
Enter the passphrase to decrypt the file:
-
+ Bitte gib das Passwort zum Enschlüsseln der Datei ein:
The password cannot be empty
-
+ Das Passwort darf nicht leer sein
Enter passphrase to encrypt your session keys:
-
+ Bitte gib das Passwort zum Verschlüsseln der Sitzungsschlüssel ein:
File to save the exported session keys
-
+ Datei zum Speichern der zu exportierenden Sitzungsschlüssel
@@ -617,12 +656,12 @@
Welcome to nheko! The desktop client for the Matrix protocol.
- Willkommen bei nheko, dem Desktop-Client für das Matrix-Protokoll.
+ Willkommen bei nheko! Ein Desktop-Client für das Matrix-Protokoll.
Enjoy your stay!
- Genieße deinen Aufenthalt!
+ Viel Vergnügen!
@@ -635,22 +674,30 @@
ANMELDEN
+
+ descriptiveTime
+
+
+ Yesterday
+ Gestern
+
+
dialogs::CreateRoom
Create room
-
+ Raum erstellen
Cancel
- Abbrechen
+ Abbrechen
Name
- Titel
+ Raumname
@@ -660,7 +707,7 @@
Alias
- Alias
+ Raumalias
@@ -683,7 +730,7 @@
Cancel
- Abbrechen
+ Abbrechen
@@ -696,12 +743,12 @@
Join
-
+ Betreten
Cancel
- Abbrechen
+ Abbrechen
@@ -714,7 +761,7 @@
Cancel
- Abbrechen
+ Abbrechen
@@ -727,7 +774,7 @@
Cancel
- Abbrechen
+ Abbrechen
@@ -762,12 +809,12 @@ Medien-Größe: %2
Cancel
- Abbrechen
+ Abbrechen
Confirm
-
+ Bestätigen
@@ -778,19 +825,27 @@ Medien-Größe: %2
dialogs::ReadReceipts
-
+
Read receipts
Lesebestätigungen
Close
-
+ Schließen
+
+
+
+ dialogs::ReceiptItem
+
+
+ Today %1
+ Heute %1
-
- ESC
-
+
+ Yesterday %1
+ Gestern %1
@@ -798,22 +853,22 @@ Medien-Größe: %2
Settings
- Einstellungen
+ Einstellungen
Info
-
+ Informationen
Internal ID
-
+ interne ID
Room Version
-
+ Raumversion
@@ -858,73 +913,81 @@ Medien-Größe: %2
Encryption
-
+ Verschlüsselung
End-to-End Encryption
-
+ Ende-zu-Ende Verschlüsselung
Encryption is currently experimental and things might break unexpectedly. <br>Please take note that it can't be disabled afterwards.
-
+ Verschlüsselung befindet sich momentan in einem experimentellen Stadium, unerwartete Fehler können auftreten. <br>Sie kann anschließend nicht wieder deaktiviert werden.
Respond to key requests
-
+ Schlüsselnfrage beantworten
Whether or not the client should respond automatically with the session keys
upon request. Use with caution, this is a temporary measure to test the
E2E implementation until device verification is completed.
-
+ Ob nheko automatisch auf Anfragen mit Sessionschlüsseln antworten soll, oder nicht. Bitte mit Vorsicht nutzen, da dies eine temporäre Massnahme ist. Sie dient dem Test von E2E Verschlüsselung, bis die Geräteverifikation fertig gestellt ist.
%n member(s)
-
-
-
+
+ %n Teilnehmer
+ %n Teilnehmer
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
-
+ Aktivierung der Verschlüsselung fehlgeschlagen: %1
Select an avatar
-
+ Wähle einen Avatar
All Files (*)
- Alle Dateien (*)
+ Alle Dateien (*)
- The selected media is not an image
-
+ The selected file is not an image
+ Die ausgewählte Datei ist kein Bild
- Error while reading media: %1
-
+ Error while reading file: %1
+ Fehler beim Lesen der DateI: %1
Failed to upload image: %s
-
+ Hochladen der Bilddatei fehlgeschlagen: %s
+
+
+
+ dialogs::UserMentions
+
+
+ This Room
+ Dieser Raum
+
+
+
+ All Rooms
+ Alle Räume
@@ -932,32 +995,27 @@ Medien-Größe: %2
Ban the user from the room
-
+ Banne den Nutzer aus diesem Raum
Ignore messages from this user
-
+ Nachrichten von diesem Nutzer ignorieren
Kick the user from the room
-
+ Entferne diesen Nutzer aus dem Raum
Start a conversation
-
+ Gespräch beginnen
Devices
-
-
-
-
- ESC
-
+ Geräte
@@ -1003,4 +1061,94 @@ Medien-Größe: %2
Flaggen
+
+ message-description sent:
+
+
+ %1 an audio clip
+ %1 einen Audioclip
+
+
+
+ %1 an image
+ %1 ein Bild
+
+
+
+ %1 a file
+ %1 eine Datei
+
+
+
+ %1 a video clip
+ %1 einen Videoclip
+
+
+
+ %1 a sticker
+ %1 einen Sticker
+
+
+
+ %1 a notification
+ 1% eine Benachrichtigung
+
+
+
+ %1 an encrypted message
+ 1% eine verschüsselte Nachricht
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+ Du
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+ Unbekannter Nachrichtentyp
+
+
diff --git a/resources/langs/nheko_el.ts b/resources/langs/nheko_el.ts
index 5a6be4cb..700c3d57 100644
--- a/resources/langs/nheko_el.ts
+++ b/resources/langs/nheko_el.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
Αποθήκευση
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
@@ -32,7 +32,7 @@
-
+
Failed to restore OLM account. Please login again.
@@ -42,7 +42,7 @@
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
Αποθήκευση
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
Αποθήκευση Εικόνας
@@ -205,8 +205,8 @@
Μέλη
-
- ESC
+
+ OK
@@ -277,7 +277,7 @@
RoomInfo
-
+
no version stored
@@ -285,12 +285,12 @@
RoomInfoListItem
-
+
Leave room
Βγές
-
+
Accept
Αποδοχή
@@ -331,7 +331,7 @@
StatusIndicator
-
+
Encrypted
@@ -354,13 +354,13 @@
TextInputWidget
-
+
Send a file
-
+
Write a message...
Γράψε ένα μήνυμα...
@@ -415,6 +415,36 @@
Encryption is enabled
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+
+
TopRoomBar
@@ -424,7 +454,12 @@
-
+
+ Mentions
+
+
+
+
Invite users
Προσκάλεσε χρήστες
@@ -459,15 +494,14 @@
TypingDisplay
-
-
- is typing
- πληκτρολογεί
-
-
-
- are typing
- πληκτρολογούν
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+
+
+
@@ -481,7 +515,7 @@
UserSettingsPage
-
+
Minimize to tray
Ελαχιστοποίηση
@@ -521,12 +555,17 @@
-
+
Font Family
-
+
+ Emoji Font Famly
+
+
+
+
Theme
Φόντο
@@ -566,7 +605,7 @@
ΓΕΝΙΚΑ
-
+
Open Sessions File
@@ -635,6 +674,14 @@
ΕΙΣΟΔΟΣ
+
+ descriptiveTime
+
+
+ Yesterday
+
+
+
dialogs::CreateRoom
@@ -776,7 +823,7 @@ Media size: %2
dialogs::ReadReceipts
-
+
Read receipts
@@ -785,9 +832,17 @@ Media size: %2
Close
+
+
+ dialogs::ReceiptItem
-
- ESC
+
+ Today %1
+
+
+
+
+ Yesterday %1
@@ -889,12 +944,7 @@ Media size: %2
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
@@ -910,12 +960,12 @@ Media size: %2
- The selected media is not an image
+ The selected file is not an image
- Error while reading media: %1
+ Error while reading file: %1
@@ -925,6 +975,19 @@ Media size: %2
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
dialogs::UserProfile
@@ -952,11 +1015,6 @@ Media size: %2
Devices
-
-
- ESC
-
-
emoji::Panel
@@ -1001,4 +1059,94 @@ Media size: %2
Σημαίες
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
diff --git a/resources/langs/nheko_en.ts b/resources/langs/nheko_en.ts
index 170d1bf1..edb95313 100644
--- a/resources/langs/nheko_en.ts
+++ b/resources/langs/nheko_en.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
Save File
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
Failed to upload image. Please try again.
@@ -32,7 +32,7 @@
Failed to upload video. Please try again.
-
+
Failed to restore OLM account. Please login again.
Failed to restore OLM account. Please login again.
@@ -42,7 +42,7 @@
Failed to restore save data. Please login again.
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
@@ -55,12 +55,12 @@
Room creation failed: %1
-
+ Room creation failed: %1
Failed to leave room: %1
-
+ Failed to leave room: %1
@@ -68,28 +68,28 @@
All rooms
-
+ All rooms
Favourite rooms
-
+ Favourite rooms
Low priority rooms
-
+ Low priority rooms
(tag)
-
+ (tag)
(community)
-
+ (community)
@@ -97,38 +97,38 @@
Apply
-
+ Apply
Cancel
-
+ Cancel
Name
-
+ Name
Topic
-
+ Topic
FileItem
-
+
Save File
-
+ Save File
ImageItem
-
+
Save image
-
+ Save image
@@ -136,7 +136,7 @@
Remove
-
+ Remove
@@ -144,57 +144,57 @@
Matrix ID
-
+ Matrix ID
e.g @joe:matrix.org
-
+ e.g @joe:matrix.org
Password
-
+ Password
Device name
-
+ Device name
LOGIN
-
+ LOGIN
Autodiscovery failed. Received malformed response.
-
+ Autodiscovery failed. Received malformed response.
Autodiscovery failed. Unknown error when requesting .well-known.
-
+ Autodiscovery failed. Unknown error while requesting .well-known.
The required endpoints were not found. Possibly not a Matrix server.
-
+ The required endpoints were not found. Possibly not a Matrix server.
Received malformed response. Make sure the homeserver domain is valid.
-
+ Received malformed response. Make sure the homeserver domain is valid.
An unknown error occured. Make sure the homeserver domain is valid.
-
+ An unknown error occured. Make sure the homeserver domain is valid.
Empty password
-
+ Empty password
@@ -202,12 +202,12 @@
Room members
-
+ Room members
-
- ESC
-
+
+ OK
+ OK
@@ -215,7 +215,7 @@
Search for a room...
-
+ Search for a room…
@@ -223,47 +223,47 @@
Username
-
+ Username
Password
-
+ Password
Password confirmation
-
+ Password confirmation
Home Server
-
+ Home Server
REGISTER
-
+ REGISTER
Invalid username
-
+ Invalid username
Password is not long enough (min 8 chars)
-
+ Password is not long enough (min 8 chars)
Passwords don't match
-
+ Passwords don't match
Invalid server name
-
+ Invalid server name
@@ -271,33 +271,33 @@
Logout
-
+ Logout
RoomInfo
-
+
no version stored
-
+ no version stored
RoomInfoListItem
-
+
Leave room
-
+ Leave room
-
+
Accept
-
+ Accept
Decline
-
+ Decline
@@ -305,89 +305,89 @@
User settings
-
+ User settings
Create new room
-
+ Create new room
Join a room
-
+ Join a room
Start a new chat
-
+ Start a new chat
Room directory
-
+ Room directory
StatusIndicator
-
+
Encrypted
-
+ Encrypted
Delivered
-
+ Delivered
Seen
-
+ Seen
Sent
-
+ Sent
TextInputWidget
-
+
Send a file
-
+ Send a file
-
+
Write a message...
-
+ Write a message…
Send a message
-
+ Send a message
Emoji
-
+ Emoji
Select a file
-
+ Select a file
All Files (*)
-
+ All Files (*)
Connection lost. Nheko is trying to re-connect...
-
+ Connection lost. Nheko is trying to re-connect…
@@ -395,17 +395,17 @@
Message redaction failed: %1
-
+ Message redaction failed: %1
Reply
-
+ Reply
Options
-
+ Options
@@ -413,7 +413,37 @@
Encryption is enabled
-
+ Encryption is enabled
+
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+ -- Encrypted Event (No keys found for decryption) --
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+ -- Decryption Error (failed to communicate with DB) --
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+ -- Decryption Error (%1) --
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+ -- Encrypted Event (Unknown event type) --
@@ -421,27 +451,32 @@
Room options
-
+ Room options
-
+
+ Mentions
+ Mentions
+
+
+
Invite users
-
+ Invite users
Members
-
+ Members
Leave room
-
+ Leave room
Settings
-
+ Settings
@@ -449,25 +484,24 @@
Show
-
+ Show
Quit
-
+ Quit
TypingDisplay
-
-
- is typing
-
-
-
-
- are typing
-
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+ %1%2 is typing
+ %1 and %2 are typing
+
@@ -475,100 +509,105 @@
Logout
-
+ Logout
UserSettingsPage
-
+
Minimize to tray
-
+ Minimize to tray
Start in tray
-
+ Start in tray
Group's sidebar
-
+ Group's sidebar
Typing notifications
-
+ Typing notifications
Read receipts
-
+ Read receipts
Desktop notifications
-
+ Desktop notifications
Scale factor
-
+ Scale factor
Font size
-
+ Font size
-
+
Font Family
-
+ Font Family
-
+
+ Emoji Font Famly
+ Emoji Font Family
+
+
+
Theme
-
+ Theme
Device ID
-
+ Device ID
Device Fingerprint
-
+ Device Fingerprint
Session Keys
-
+ Session Keys
IMPORT
-
+ IMPORT
EXPORT
-
+ EXPORT
ENCRYPTION
-
+ ENCRYPTION
GENERAL
-
+ GENERAL
-
+
Open Sessions File
-
+ Open Sessions File
@@ -582,34 +621,34 @@
Error
-
+ Error
File Password
-
+ File Password
Enter the passphrase to decrypt the file:
-
+ Enter the passphrase to decrypt the file:
The password cannot be empty
-
+ The password cannot be empty
Enter passphrase to encrypt your session keys:
-
+ Enter passphrase to encrypt your session keys:
File to save the exported session keys
-
+ File to save the exported session keys
@@ -617,22 +656,30 @@
Welcome to nheko! The desktop client for the Matrix protocol.
-
+ Welcome to nheko! The desktop client for the Matrix protocol.
Enjoy your stay!
-
+ Enjoy your stay!
REGISTER
-
+ REGISTER
LOGIN
-
+ LOGIN
+
+
+
+ descriptiveTime
+
+
+ Yesterday
+ Yesterday
@@ -640,42 +687,42 @@
Create room
-
+ Create room
Cancel
-
+ Cancel
Name
-
+ Name
Topic
-
+ Topic
Alias
-
+ Alias
Room Visibility
-
+ Room Visibility
Room Preset
-
+ Room Preset
Direct Chat
-
+ Direct Chat
@@ -683,12 +730,12 @@
Cancel
-
+ Cancel
User ID to invite
-
+ User ID to invite
@@ -696,17 +743,17 @@
Join
-
+ Join
Cancel
-
+ Cancel
Room ID or alias
-
+ Room ID or alias
@@ -714,12 +761,12 @@
Cancel
-
+ Cancel
Are you sure you want to leave?
-
+ Are you sure you want to leave?
@@ -727,12 +774,12 @@
Cancel
-
+ Cancel
Logout. Are you sure?
-
+ Logout. Are you sure?
@@ -740,19 +787,21 @@
Upload
-
+ Upload
Cancel
-
+ Cancel
Media type: %1
Media size: %2
-
+ Media type: %1
+Media size: %2
+
@@ -760,35 +809,43 @@ Media size: %2
Cancel
-
+ Cancel
Confirm
-
+ Confirm
Solve the reCAPTCHA and press the confirm button
-
+ Solve the reCAPTCHA and press the confirm button
dialogs::ReadReceipts
-
+
Read receipts
-
+ Read receipts
Close
-
+ Close
+
+
+
+ dialogs::ReceiptItem
+
+
+ Today %1
+ Today %1
-
- ESC
-
+
+ Yesterday %1
+ Yesterday %1
@@ -796,133 +853,143 @@ Media size: %2
Settings
-
+ Settings
Info
-
+ Info
Internal ID
-
+ Internal ID
Room Version
-
+ Room Version
Notifications
-
+ Notifications
Muted
-
+ Muted
Mentions only
-
+ Mentions only
All messages
-
+ All messages
Room access
-
+ Room access
Anyone and guests
-
+ Anyone and guests
Anyone
-
+ Anyone who knows the room link (no guests)
Invited users
-
+ Invited users
Encryption
-
+ Encryption
End-to-End Encryption
-
+ End-to-End Encryption
Encryption is currently experimental and things might break unexpectedly. <br>Please take note that it can't be disabled afterwards.
-
+ Encryption is currently experimental and things might break unexpectedly. <br>Please take note that it can't be disabled afterwards.
Respond to key requests
-
+ Respond to key requests
Whether or not the client should respond automatically with the session keys
upon request. Use with caution, this is a temporary measure to test the
E2E implementation until device verification is completed.
-
+ Whether or not the client should respond automatically with the session keys
+ upon request. Use with caution, this is a temporary measure to test the
+ E2E implementation until device verification is completed.
%n member(s)
-
-
-
+
+ %n member
+ %n members
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
-
+ Failed to enable encryption: %1
Select an avatar
-
+ Select an avatar
All Files (*)
-
+ All Files (*)
- The selected media is not an image
-
+ The selected file is not an image
+ The selected file is not an image
- Error while reading media: %1
-
+ Error while reading file: %1
+ Error while reading file: %1
Failed to upload image: %s
-
+ Failed to upload image: %s
+
+
+
+ dialogs::UserMentions
+
+
+ This Room
+ This Room
+
+
+
+ All Rooms
+ All Rooms
@@ -930,32 +997,27 @@ Media size: %2
Ban the user from the room
-
+ Ban the user from the room
Ignore messages from this user
-
+ Ignore messages from this user
Kick the user from the room
-
+ Kick the user from the room
Start a conversation
-
+ Start a conversation
Devices
-
-
-
-
- ESC
-
+ Devices
@@ -963,42 +1025,132 @@ Media size: %2
Smileys & People
- Smileys & People
+ Smileys & People
Animals & Nature
- Animals & Nature
+ Animals & Nature
Food & Drink
- Food & Drink
+ Food & Drink
Activity
- Activity
+ Activity
Travel & Places
- Travel & Places
+ Travel & Places
Objects
- Objects
+ Objects
Symbols
- Symbols
+ Symbols
Flags
- Flags
+ Flags
+
+
+
+ message-description sent:
+
+
+ %1 an audio clip
+ %1 an audio clip
+
+
+
+ %1 an image
+ %1 an image
+
+
+
+ %1 a file
+ %1 a file
+
+
+
+ %1 a video clip
+ %1 a video clip
+
+
+
+ %1 a sticker
+ %1 a sticker
+
+
+
+ %1 a notification
+ %1 a notification
+
+
+
+ %1 an encrypted message
+ %1 an encrypted message
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+ sent
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+ sent
+
+
+
+ utils
+
+
+
+ You
+ You
+
+
+
+ sent a file.
+ sent a file.
+
+
+
+ sent an image.
+ sent an image.
+
+
+
+ sent an audio file.
+ sent an audio file.
+
+
+
+ sent a video
+ sent a video.
+
+
+
+ Unknown Message Type
+ Unknown Message Type
diff --git a/resources/langs/nheko_fi.ts b/resources/langs/nheko_fi.ts
new file mode 100644
index 00000000..89eb33b7
--- /dev/null
+++ b/resources/langs/nheko_fi.ts
@@ -0,0 +1,1156 @@
+
+
+
+
+ AudioItem
+
+
+ Save File
+ Tallenna tiedosto
+
+
+
+ ChatPage
+
+
+ Failed to upload image. Please try again.
+ Kuvan lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.
+
+
+
+ Failed to upload file. Please try again.
+ Tiedoston lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.
+
+
+
+ Failed to upload audio. Please try again.
+ Äänitiedoston lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.
+
+
+
+ Failed to upload video. Please try again.
+ Videon lähettäminen epäonnistui. Ole hyvä ja yritä uudelleen.
+
+
+
+ Failed to restore OLM account. Please login again.
+ OLM-tilin palauttaminen epäonnistui. Ole hyvä ja kirjaudu sisään uudelleen.
+
+
+
+ Failed to restore save data. Please login again.
+ Tallennettujen tietojen palauttaminen epäonnistui. Ole hyvä ja kirjaudu sisään uudelleen.
+
+
+
+ Failed to setup encryption keys. Server response: %1 %2. Please try again later.
+ Salausavainten lähetys epäonnistui. Palvelimen vastaus: %1 %2. Ole hyvä ja yritä uudelleen myöhemmin.
+
+
+
+
+ Please try to login again: %1
+ Ole hyvä ja yritä kirjautua sisään uudelleen: %1
+
+
+
+ Room creation failed: %1
+ Huoneen luominen epäonnistui: %1
+
+
+
+ Failed to leave room: %1
+ Huoneesta poistuminen epäonnistui: %1
+
+
+
+ CommunitiesListItem
+
+
+ All rooms
+ Kaikki huoneet
+
+
+
+ Favourite rooms
+ Suosikkihuoneet
+
+
+
+ Low priority rooms
+ Alhaisen prioriteetin huoneet
+
+
+
+
+ (tag)
+ (tag)
+
+
+
+ (community)
+ (community)
+
+
+
+ EditModal
+
+
+ Apply
+ Tallenna
+
+
+
+ Cancel
+ Peruuta
+
+
+
+ Name
+ Nimi
+
+
+
+ Topic
+ Aihe
+
+
+
+ FileItem
+
+
+ Save File
+ Tallenna tiedosto
+
+
+
+ ImageItem
+
+
+ Save image
+ Tallenna kuva
+
+
+
+ InviteeItem
+
+
+ Remove
+ Poista
+
+
+
+ LoginPage
+
+
+ Matrix ID
+ Matrix-tunnus
+
+
+
+ e.g @joe:matrix.org
+ esim. @joe:matrix.org
+
+
+
+ Password
+ Salasana
+
+
+
+ Device name
+ Laitteen nimi
+
+
+
+ LOGIN
+ KIRJAUDU
+
+
+
+ Autodiscovery failed. Received malformed response.
+ Palvelimen tietojen hakeminen epäonnistui: virheellinen vastaus.
+
+
+
+ Autodiscovery failed. Unknown error when requesting .well-known.
+ Palvelimen tietojen hakeminen epäonnistui: tuntematon virhe hakiessa .well-known -tiedostoa.
+
+
+
+ The required endpoints were not found. Possibly not a Matrix server.
+ Vaadittuja päätepisteitä ei löydetty. Mahdollisesti ei Matrix-palvelin.
+
+
+
+ Received malformed response. Make sure the homeserver domain is valid.
+ Vastaanotettiin virheellinen vastaus. Varmista, että kotipalvelimen osoite on pätevä.
+
+
+
+ An unknown error occured. Make sure the homeserver domain is valid.
+ Tapahtui tuntematon virhe. Varmista, että kotipalvelimen osoite on pätevä.
+
+
+
+ Empty password
+ Tyhjä salasana
+
+
+
+ MemberList
+
+
+ Room members
+ Huoneen jäsenet
+
+
+
+ OK
+ OK
+
+
+
+ QuickSwitcher
+
+
+ Search for a room...
+ Etsi huonetta…
+
+
+
+ RegisterPage
+
+
+ Username
+ Käyttäjänimi
+
+
+
+ Password
+ Salasana
+
+
+
+ Password confirmation
+ Salasanan varmistus
+
+
+
+ Home Server
+ Kotipalvelin
+
+
+
+ REGISTER
+ REKISTERÖIDY
+
+
+
+ Invalid username
+ Epäkelpo käyttäjänimi
+
+
+
+ Password is not long enough (min 8 chars)
+ Salasana ei ole tarpeeksi pitkä (vähintään 8 merkkiä)
+
+
+
+ Passwords don't match
+ Salasanat eivät täsmää
+
+
+
+ Invalid server name
+ Epäkelpo palvelimen nimi
+
+
+
+ ReplyPopup
+
+
+ Logout
+ Kirjaudu ulos
+
+
+
+ RoomInfo
+
+
+ no version stored
+ ei tallennettua versiota
+
+
+
+ RoomInfoListItem
+
+
+ Leave room
+ Poistu huoneesta
+
+
+
+ Accept
+ Hyväksy
+
+
+
+ Decline
+ Hylkää
+
+
+
+ SideBarActions
+
+
+ User settings
+ Käyttäjäasetukset
+
+
+
+ Create new room
+ Luo uusi huone
+
+
+
+ Join a room
+ Liity huoneeseen
+
+
+
+ Start a new chat
+ Aloita uusi keskustelu
+
+
+
+ Room directory
+ Huoneluettelo
+
+
+
+ StatusIndicator
+
+
+ Encrypted
+ Salattu
+
+
+
+ Delivered
+ Toimitettu
+
+
+
+ Seen
+ Luettu
+
+
+
+ Sent
+ Lähetetty
+
+
+
+ TextInputWidget
+
+
+ Send a file
+ Lähetä tiedosto
+
+
+
+
+ Write a message...
+ Kirjoita viesti…
+
+
+
+ Send a message
+ Lähetä viesti
+
+
+
+ Emoji
+ Emoji
+
+
+
+ Select a file
+ Valitse tiedosto
+
+
+
+ All Files (*)
+ Kaikki tiedostot (*)
+
+
+
+ Connection lost. Nheko is trying to re-connect...
+ Yhteys kadotettu. Nheko yrittää muodostaa yhteyttä uudelleen…
+
+
+
+ TimelineItem
+
+
+ Message redaction failed: %1
+ Viestin poisto epäonnistui: %1
+
+
+
+ Reply
+ Vastaa
+
+
+
+ Options
+ Asetukset
+
+
+
+ TimelineView
+
+
+ Encryption is enabled
+ Salaus on käytössä
+
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+ -- Salattu viesti (salauksen purkuavaimia ei löydetty) --
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+ -- Virhe purkaessa salausta (tietokannan kanssa kommunikointi epäonnistui) --
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+ -- Virhe purkaessa salausta (megolm-avaimien hakeminen tietokannasta epäonnistui) --
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+ -- Virhe purkaessa salausta (%1) --
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+ -- Salattu viesti (tuntematon viestityyppi) --
+
+
+
+ TopRoomBar
+
+
+ Room options
+ Huonevaihtoehdot
+
+
+
+ Mentions
+ Maininnat
+
+
+
+ Invite users
+ Kutsu käyttäjiä
+
+
+
+ Members
+ Jäsenet
+
+
+
+ Leave room
+ Poistu huoneesta
+
+
+
+ Settings
+ Asetukset
+
+
+
+ TrayIcon
+
+
+ Show
+ Näytä
+
+
+
+ Quit
+ Lopeta
+
+
+
+ TypingDisplay
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+ %1%2 kirjoittaa
+ %1 ja %2 kirjoittavat
+
+
+
+
+ UserInfoWidget
+
+
+ Logout
+ Kirjaudu ulos
+
+
+
+ UserSettingsPage
+
+
+ Minimize to tray
+ Pienennä ilmoitusalueelle
+
+
+
+ Start in tray
+ Aloita ilmoitusalueella
+
+
+
+ Group's sidebar
+ Ryhmäsivupalkki
+
+
+
+ Typing notifications
+ Kirjoitusilmoitukset
+
+
+
+ Read receipts
+ Lukukuittaukset
+
+
+
+ Desktop notifications
+ Työpöytäilmoitukset
+
+
+
+ Scale factor
+ Mittakerroin
+
+
+
+ Font size
+ Fonttikoko
+
+
+
+ Font Family
+ Fonttiperhe
+
+
+
+ Emoji Font Famly
+ Emoji-fonttiperhe
+
+
+
+ Theme
+ Teema
+
+
+
+ Device ID
+ Laitteen tunnus
+
+
+
+ Device Fingerprint
+ Laitteen sormenjälki
+
+
+
+ Session Keys
+ Istunnon avaimet
+
+
+
+ IMPORT
+ TUO
+
+
+
+ EXPORT
+ VIE
+
+
+
+ ENCRYPTION
+ SALAUS
+
+
+
+ GENERAL
+ YLEISET ASETUKSET
+
+
+
+ Open Sessions File
+ Avaa Istuntoavaintiedosto
+
+
+
+
+
+
+
+
+
+
+
+
+ Error
+ Virhe
+
+
+
+
+ File Password
+ Tiedoston Salasana
+
+
+
+ Enter the passphrase to decrypt the file:
+ Anna salasana tiedoston salauksen purkamiseksi:
+
+
+
+
+ The password cannot be empty
+ Salasana ei voi olla tyhjä
+
+
+
+ Enter passphrase to encrypt your session keys:
+ Anna salasana istuntoavaimien salaamiseksi:
+
+
+
+ File to save the exported session keys
+ Tiedosto, johon viedyt istuntoavaimet tallennetaan
+
+
+
+ WelcomePage
+
+
+ Welcome to nheko! The desktop client for the Matrix protocol.
+ Tervetuloa nhekoon! Työpöytäsovellus Matrix-protokollalle.
+
+
+
+ Enjoy your stay!
+
+
+
+
+ REGISTER
+ REKISTERÖIDY
+
+
+
+ LOGIN
+ KIRJAUDU
+
+
+
+ descriptiveTime
+
+
+ Yesterday
+ Eilen
+
+
+
+ dialogs::CreateRoom
+
+
+ Create room
+ Luo huone
+
+
+
+ Cancel
+ Peruuta
+
+
+
+ Name
+ Nimi
+
+
+
+ Topic
+ Aihe
+
+
+
+ Alias
+ Osoite
+
+
+
+ Room Visibility
+ Huoneen näkyvyys
+
+
+
+ Room Preset
+ Huoneen esiasetus
+
+
+
+ Direct Chat
+ Suora keskustelu
+
+
+
+ dialogs::InviteUsers
+
+
+ Cancel
+ Peruuta
+
+
+
+ User ID to invite
+ Käyttäjätunnus kutsuttavaksi
+
+
+
+ dialogs::JoinRoom
+
+
+ Join
+ Liity
+
+
+
+ Cancel
+ Peruuta
+
+
+
+ Room ID or alias
+ Huoneen tunnus tai osoite
+
+
+
+ dialogs::LeaveRoom
+
+
+ Cancel
+ Peruuta
+
+
+
+ Are you sure you want to leave?
+ Oletko varma, että haluat poistua?
+
+
+
+ dialogs::Logout
+
+
+ Cancel
+ Peruuta
+
+
+
+ Logout. Are you sure?
+ Kirjaudutaan ulos. Oletko varma?
+
+
+
+ dialogs::PreviewUploadOverlay
+
+
+ Upload
+ Lähetä
+
+
+
+ Cancel
+ Peruuta
+
+
+
+ Media type: %1
+Media size: %2
+
+ Median tyyppi: %1
+Median koko: %2
+
+
+
+
+ dialogs::ReCaptcha
+
+
+ Cancel
+ Peruuta
+
+
+
+ Confirm
+ Vahvista
+
+
+
+ Solve the reCAPTCHA and press the confirm button
+ Ratkaise reCAPTCHA ja paina varmista-nappia
+
+
+
+ dialogs::ReadReceipts
+
+
+ Read receipts
+ Lukukuittaukset
+
+
+
+ Close
+ Sulje
+
+
+
+ dialogs::ReceiptItem
+
+
+ Today %1
+ Tänään %1
+
+
+
+ Yesterday %1
+ Eilen %1
+
+
+
+ dialogs::RoomSettings
+
+
+ Settings
+ Asetukset
+
+
+
+ Info
+ Tiedot
+
+
+
+ Internal ID
+ Sisäinen tunnus
+
+
+
+ Room Version
+ Huoneen versio
+
+
+
+ Notifications
+ Ilmoitukset
+
+
+
+ Muted
+ Vaimennettu
+
+
+
+ Mentions only
+ Vain maininnat
+
+
+
+ All messages
+ Kaikki viestit
+
+
+
+ Room access
+ Pääsy huoneeseen
+
+
+
+ Anyone and guests
+ Kaikki (mukaanlukien vieraat)
+
+
+
+ Anyone
+ Kaikki (poislukien vieraat)
+
+
+
+ Invited users
+ Kutsutut käyttäjät
+
+
+
+ Encryption
+ Salaus
+
+
+
+ End-to-End Encryption
+ Päästä-päähän-salaus
+
+
+
+ Encryption is currently experimental and things might break unexpectedly. <br>Please take note that it can't be disabled afterwards.
+ Salaus on tällä hetkellä kokeellinen ja asiat saattavat mennä rikki odottamattomasti.<br>Huomaa, ettei sitä voi poistaa käytöstä jälkikäteen.
+
+
+
+ Respond to key requests
+ Vastaa avainpyyntöihin
+
+
+
+ Whether or not the client should respond automatically with the session keys
+ upon request. Use with caution, this is a temporary measure to test the
+ E2E implementation until device verification is completed.
+ Pitäisikö asiakasohjelman palauttaa salausavaimet automaattisesti pyydettäessä.
+ Käytä varoen, tämä on väliaikainen vaihtoehto salausjärjestelmän testausta varten,
+ kunnes laitteiden vahvistus on valmis.
+
+
+
+ %n member(s)
+
+ %n käyttäjä
+ %n käyttäjää
+
+
+
+
+ Failed to enable encryption: %1
+ Salauksen aktivointi epäonnistui: %1
+
+
+
+ Select an avatar
+ Valitse profiilikuva
+
+
+
+ All Files (*)
+ Kaikki Tiedostot (*)
+
+
+
+ The selected file is not an image
+
+
+
+
+ Error while reading file: %1
+
+
+
+
+
+ Failed to upload image: %s
+ Kuvan lähetys epäonnistui: %s
+
+
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
+
+ dialogs::UserProfile
+
+
+ Ban the user from the room
+ Anna käyttäjälle porttikielto huoneesta
+
+
+
+ Ignore messages from this user
+ Jätä tämän käyttäjän viestit huomiotta
+
+
+
+ Kick the user from the room
+ Potki käyttäjä huoneesta
+
+
+
+ Start a conversation
+ Aloita keskustelu
+
+
+
+ Devices
+ Laitteet
+
+
+
+ emoji::Panel
+
+
+ Smileys & People
+ Hymiöt ja ihmiset
+
+
+
+ Animals & Nature
+ Eläimet ja luonto
+
+
+
+ Food & Drink
+ Ruoka ja juoma
+
+
+
+ Activity
+ Aktiviteetti
+
+
+
+ Travel & Places
+ Matkailu ja paikat
+
+
+
+ Objects
+ Esineet
+
+
+
+ Symbols
+ Symbolit
+
+
+
+ Flags
+ Liput
+
+
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+ Sinä
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
+
diff --git a/resources/langs/nheko_fr.ts b/resources/langs/nheko_fr.ts
index beab8752..42f82b0f 100644
--- a/resources/langs/nheko_fr.ts
+++ b/resources/langs/nheko_fr.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
Enregistrer le fichier
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
@@ -32,7 +32,7 @@
-
+
Failed to restore OLM account. Please login again.
@@ -42,7 +42,7 @@
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
Enregistrer le fichier
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
Enregistrer l'image
@@ -205,8 +205,8 @@
Membres du salon
-
- ESC
+
+ OK
@@ -278,7 +278,7 @@
RoomInfo
-
+
no version stored
@@ -286,12 +286,12 @@
RoomInfoListItem
-
+
Leave room
Quitter le salon
-
+
Accept
Accepter
@@ -332,7 +332,7 @@
StatusIndicator
-
+
Encrypted
@@ -355,13 +355,13 @@
TextInputWidget
-
+
Send a file
-
+
Write a message...
Écrivez un message...
@@ -416,6 +416,36 @@
Encryption is enabled
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+
+
TopRoomBar
@@ -425,7 +455,12 @@
-
+
+ Mentions
+
+
+
+
Invite users
Inviter des utilisateurs
@@ -460,15 +495,14 @@
TypingDisplay
-
-
- is typing
- est en train d'écrire
-
-
-
- are typing
- sont en train d'écrire
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+
+
+
@@ -482,7 +516,7 @@
UserSettingsPage
-
+
Minimize to tray
Réduire à la barre des tâches
@@ -522,12 +556,17 @@
-
+
Font Family
-
+
+ Emoji Font Famly
+
+
+
+
Theme
Thème
@@ -567,7 +606,7 @@
GÉNÉRAL
-
+
Open Sessions File
@@ -636,6 +675,14 @@
CONNEXION
+
+ descriptiveTime
+
+
+ Yesterday
+
+
+
dialogs::CreateRoom
@@ -779,7 +826,7 @@ Taille du média : %2
dialogs::ReadReceipts
-
+
Read receipts
Accusés de lecture
@@ -788,9 +835,17 @@ Taille du média : %2
Close
+
+
+ dialogs::ReceiptItem
-
- ESC
+
+ Today %1
+
+
+
+
+ Yesterday %1
@@ -892,12 +947,7 @@ Taille du média : %2
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
@@ -913,12 +963,12 @@ Taille du média : %2
- The selected media is not an image
+ The selected file is not an image
- Error while reading media: %1
+ Error while reading file: %1
@@ -928,6 +978,19 @@ Taille du média : %2
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
dialogs::UserProfile
@@ -955,11 +1018,6 @@ Taille du média : %2
Devices
-
-
- ESC
-
-
emoji::Panel
@@ -1004,4 +1062,94 @@ Taille du média : %2
Drapeaux
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
diff --git a/resources/langs/nheko_nl.ts b/resources/langs/nheko_nl.ts
index 4c81ec76..53840f82 100644
--- a/resources/langs/nheko_nl.ts
+++ b/resources/langs/nheko_nl.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
Bestand opslaan
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
@@ -32,7 +32,7 @@
-
+
Failed to restore OLM account. Please login again.
@@ -42,7 +42,7 @@
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
Bestand opslaan
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
Afbeelding opslaan
@@ -205,8 +205,8 @@
Kamerleden
-
- ESC
+
+ OK
@@ -277,7 +277,7 @@
RoomInfo
-
+
no version stored
@@ -285,12 +285,12 @@
RoomInfoListItem
-
+
Leave room
Kamer verlaten
-
+
Accept
Accepteren
@@ -331,7 +331,7 @@
StatusIndicator
-
+
Encrypted
@@ -354,13 +354,13 @@
TextInputWidget
-
+
Send a file
-
+
Write a message...
Typ een bericht...
@@ -415,6 +415,36 @@
Encryption is enabled
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+
+
TopRoomBar
@@ -424,7 +454,12 @@
-
+
+ Mentions
+
+
+
+
Invite users
Gebruikers uitnodigen
@@ -459,15 +494,14 @@
TypingDisplay
-
-
- is typing
- is aan het typen
-
-
-
- are typing
- zijn aan het typen
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+
+
+
@@ -481,7 +515,7 @@
UserSettingsPage
-
+
Minimize to tray
Minimaliseren naar systeemvak
@@ -521,12 +555,17 @@
-
+
Font Family
-
+
+ Emoji Font Famly
+
+
+
+
Theme
Thema
@@ -566,7 +605,7 @@
ALGEMEEN
-
+
Open Sessions File
@@ -635,6 +674,14 @@
INLOGGEN
+
+ descriptiveTime
+
+
+ Yesterday
+
+
+
dialogs::CreateRoom
@@ -778,7 +825,7 @@ Mediagrootte: %2
dialogs::ReadReceipts
-
+
Read receipts
Leesbevestigingen
@@ -787,9 +834,17 @@ Mediagrootte: %2
Close
+
+
+ dialogs::ReceiptItem
-
- ESC
+
+ Today %1
+
+
+
+
+ Yesterday %1
@@ -891,12 +946,7 @@ Mediagrootte: %2
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
@@ -912,12 +962,12 @@ Mediagrootte: %2
- The selected media is not an image
+ The selected file is not an image
- Error while reading media: %1
+ Error while reading file: %1
@@ -927,6 +977,19 @@ Mediagrootte: %2
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
dialogs::UserProfile
@@ -954,11 +1017,6 @@ Mediagrootte: %2
Devices
-
-
- ESC
-
-
emoji::Panel
@@ -1003,4 +1061,94 @@ Mediagrootte: %2
Vlaggen
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
diff --git a/resources/langs/nheko_pl.ts b/resources/langs/nheko_pl.ts
index edea85b9..f4f98dbb 100644
--- a/resources/langs/nheko_pl.ts
+++ b/resources/langs/nheko_pl.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
Zapisz plik
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
Nie udało się wysłać obrazu. Spróbuj ponownie.
@@ -32,7 +32,7 @@
Nie udało się wysłać filmu. Spróbuj ponownie.
-
+
Failed to restore OLM account. Please login again.
Nie udało się przywrócić konta OLM. Spróbuj zalogować się ponownie.
@@ -42,7 +42,7 @@
Nie udało się przywrócić zapisanych danych. Spróbuj zalogować się ponownie.
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
Zapisz plik
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
Zapisz obraz
@@ -205,8 +205,8 @@
Członkowie pokoju
-
- ESC
+
+ OK
@@ -277,7 +277,7 @@
RoomInfo
-
+
no version stored
@@ -285,12 +285,12 @@
RoomInfoListItem
-
+
Leave room
Opuść pokój
-
+
Accept
Akceptuj
@@ -331,7 +331,7 @@
StatusIndicator
-
+
Encrypted
Szyfrowana
@@ -354,13 +354,13 @@
TextInputWidget
-
+
Send a file
Wyślij plik
-
+
Write a message...
Napisz wiadomość…
@@ -415,6 +415,36 @@
Encryption is enabled
Szyfrowanie jest włączone
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+
+
TopRoomBar
@@ -424,7 +454,12 @@
Ustawienia pokoju
-
+
+ Mentions
+
+
+
+
Invite users
Zaproś użytkowników
@@ -459,15 +494,15 @@
TypingDisplay
-
-
- is typing
- pisze
-
-
-
- are typing
- piszą
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+
+
+
+
@@ -481,7 +516,7 @@
UserSettingsPage
-
+
Minimize to tray
Zminimalizuj do paska zadań
@@ -521,12 +556,17 @@
-
+
Font Family
-
+
+ Emoji Font Famly
+
+
+
+
Theme
Motyw
@@ -566,7 +606,7 @@
OGÓLNE
-
+
Open Sessions File
@@ -635,6 +675,14 @@
ZALOGUJ SIĘ
+
+ descriptiveTime
+
+
+ Yesterday
+
+
+
dialogs::CreateRoom
@@ -778,7 +826,7 @@ Rozmiar multimediów: %2
dialogs::ReadReceipts
-
+
Read receipts
Potwierdzenia przeczytania
@@ -787,9 +835,17 @@ Rozmiar multimediów: %2
Close
+
+
+ dialogs::ReceiptItem
-
- ESC
+
+ Today %1
+
+
+
+
+ Yesterday %1
@@ -894,12 +950,7 @@ Rozmiar multimediów: %2
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
Nie udało się włączyć szyfrowania: %1
@@ -915,13 +966,13 @@ Rozmiar multimediów: %2
- The selected media is not an image
- Wybrany plik multimedialny nie jest obrazem
+ The selected file is not an image
+
- Error while reading media: %1
- Błąd odczytywania pliku: %1
+ Error while reading file: %1
+
@@ -930,6 +981,19 @@ Rozmiar multimediów: %2
Nie udało się wysłać obrazu: %s
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
dialogs::UserProfile
@@ -957,11 +1021,6 @@ Rozmiar multimediów: %2
Devices
Urządzenia
-
-
- ESC
-
-
emoji::Panel
@@ -1006,4 +1065,94 @@ Rozmiar multimediów: %2
Flagi
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
diff --git a/resources/langs/nheko_ru.ts b/resources/langs/nheko_ru.ts
index 4c157884..04285c72 100644
--- a/resources/langs/nheko_ru.ts
+++ b/resources/langs/nheko_ru.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
Сохранить файл
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
Не удалось загрузить изображение. Пожалуйста, попробуйте еще раз.
@@ -32,7 +32,7 @@
Не удалось загрузить видео. Пожалуйста, попробуйте еще раз.
-
+
Failed to restore OLM account. Please login again.
Не удалось восстановить учетную запись OLM. Пожалуйста, войдите снова.
@@ -42,7 +42,7 @@
Не удалось восстановить сохраненные данные. Пожалуйста, войдите снова.
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
Не удалось настроить ключи шифрования. Ответ сервера:%1 %2. Пожалуйста, попробуйте позже.
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
Сохранить файл
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
Сохранить изображение
@@ -205,9 +205,9 @@
Участники комнаты
-
- ESC
-
+
+ OK
+
@@ -277,7 +277,7 @@
RoomInfo
-
+
no version stored
@@ -285,12 +285,12 @@
RoomInfoListItem
-
+
Leave room
Покинуть комнату
-
+
Accept
Принять
@@ -331,7 +331,7 @@
StatusIndicator
-
+
Encrypted
Зашифровано
@@ -354,13 +354,13 @@
TextInputWidget
-
+
Send a file
Отправить файл
-
+
Write a message...
Написать сообщение...
@@ -415,6 +415,36 @@
Encryption is enabled
Шифрование включено
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+
+
TopRoomBar
@@ -424,7 +454,12 @@
Настройки комнаты
-
+
+ Mentions
+
+
+
+
Invite users
Пригласить пользователей
@@ -459,15 +494,15 @@
TypingDisplay
-
-
- is typing
- печатает
-
-
-
- are typing
- печатают
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+
+
+
+
@@ -481,7 +516,7 @@
UserSettingsPage
-
+
Minimize to tray
Сворачивать в системную панель
@@ -521,12 +556,17 @@
Размер шрифта
-
+
Font Family
-
+
+ Emoji Font Famly
+
+
+
+
Theme
Тема
@@ -566,7 +606,7 @@
ГЛАВНОЕ
-
+
Open Sessions File
Открыть файл сеансов
@@ -636,6 +676,14 @@
ВХОД
+
+ descriptiveTime
+
+
+ Yesterday
+
+
+
dialogs::CreateRoom
@@ -779,7 +827,7 @@ Media size: %2
dialogs::ReadReceipts
-
+
Read receipts
Подтверждать прочтение
@@ -788,10 +836,18 @@ Media size: %2
Close
Закрыть
+
+
+ dialogs::ReceiptItem
-
- ESC
-
+
+ Today %1
+
+
+
+
+ Yesterday %1
+
@@ -893,12 +949,7 @@ Media size: %2
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
Не удалось включить шифрование: %1
@@ -914,13 +965,13 @@ Media size: %2
- The selected media is not an image
- Выбранное медия не является изображением
+ The selected file is not an image
+
- Error while reading media: %1
- Ошибка при чтении медия: %1
+ Error while reading file: %1
+
@@ -929,6 +980,19 @@ Media size: %2
Не удалось загрузить изображение: %s
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
dialogs::UserProfile
@@ -956,11 +1020,6 @@ Media size: %2
Devices
Устройства
-
-
- ESC
-
-
emoji::Panel
@@ -1005,4 +1064,94 @@ Media size: %2
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
diff --git a/resources/langs/nheko_zh_CN.ts b/resources/langs/nheko_zh_CN.ts
index ca7c6e22..1e539e64 100644
--- a/resources/langs/nheko_zh_CN.ts
+++ b/resources/langs/nheko_zh_CN.ts
@@ -4,7 +4,7 @@
AudioItem
-
+
Save File
保存文件
@@ -12,7 +12,7 @@
ChatPage
-
+
Failed to upload image. Please try again.
上传图像失败。请重试。
@@ -32,7 +32,7 @@
上传视频失败。请重试。
-
+
Failed to restore OLM account. Please login again.
恢复 OLM 账户失败。请重新登录。
@@ -42,7 +42,7 @@
恢复保存的数据失败。请重新登录。
-
+
Failed to setup encryption keys. Server response: %1 %2. Please try again later.
@@ -118,7 +118,7 @@
FileItem
-
+
Save File
保存文件
@@ -126,7 +126,7 @@
ImageItem
-
+
Save image
保存图像
@@ -205,8 +205,8 @@
聊天室成员
-
- ESC
+
+ OK
@@ -277,7 +277,7 @@
RoomInfo
-
+
no version stored
@@ -285,12 +285,12 @@
RoomInfoListItem
-
+
Leave room
离开聊天室
-
+
Accept
接受
@@ -331,7 +331,7 @@
StatusIndicator
-
+
Encrypted
加密的
@@ -354,13 +354,13 @@
TextInputWidget
-
+
Send a file
发送一个文件
-
+
Write a message...
写一条消息...
@@ -415,6 +415,36 @@
Encryption is enabled
加密已启用
+
+
+ -- Encrypted Event (No keys found for decryption) --
+ Placeholder, when the message was not decrypted yet or can't be decrypted
+
+
+
+
+ -- Decryption Error (failed to communicate with DB) --
+ Placeholder, when the message can't be decrypted, because the DB access failed when trying to lookup the session.
+
+
+
+
+ -- Decryption Error (failed to retrieve megolm keys from db) --
+ Placeholder, when the message can't be decrypted, because the DB access failed.
+
+
+
+
+ -- Decryption Error (%1) --
+ Placeholder, when the message can't be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1
+
+
+
+
+ -- Encrypted Event (Unknown event type) --
+ Placeholder, when the message was decrypted, but we couldn't parse it, because Nheko/mtxclient don't support that event type yet
+
+
TopRoomBar
@@ -424,7 +454,12 @@
聊天室选项
-
+
+ Mentions
+
+
+
+
Invite users
邀请用户
@@ -459,15 +494,13 @@
TypingDisplay
-
-
- is typing
- 正在打字
-
-
-
- are typing
- 正在打字
+
+
+ %1 and %2 are typing
+ Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)
+
+
+
@@ -481,7 +514,7 @@
UserSettingsPage
-
+
Minimize to tray
最小化至托盘
@@ -521,12 +554,17 @@
-
+
Font Family
-
+
+ Emoji Font Famly
+
+
+
+
Theme
主题
@@ -566,7 +604,7 @@
通用
-
+
Open Sessions File
打开会话文件
@@ -635,6 +673,14 @@
登录
+
+ descriptiveTime
+
+
+ Yesterday
+
+
+
dialogs::CreateRoom
@@ -778,7 +824,7 @@ Media size: %2
dialogs::ReadReceipts
-
+
Read receipts
阅读回执
@@ -787,9 +833,17 @@ Media size: %2
Close
+
+
+ dialogs::ReceiptItem
-
- ESC
+
+ Today %1
+
+
+
+
+ Yesterday %1
@@ -892,12 +946,7 @@ Media size: %2
-
- ESC
-
-
-
-
+
Failed to enable encryption: %1
启用加密失败:%1
@@ -913,13 +962,13 @@ Media size: %2
- The selected media is not an image
- 选择的媒体不是一个图像
+ The selected file is not an image
+
- Error while reading media: %1
- 读取媒体时失败:%1
+ Error while reading file: %1
+
@@ -928,6 +977,19 @@ Media size: %2
上传图像失败:%s
+
+ dialogs::UserMentions
+
+
+ This Room
+
+
+
+
+ All Rooms
+
+
+
dialogs::UserProfile
@@ -955,11 +1017,6 @@ Media size: %2
Devices
设备
-
-
- ESC
-
-
emoji::Panel
@@ -1012,4 +1069,94 @@ Media size: %2
Flags
+
+ message-description sent:
+
+
+ %1 an audio clip
+
+
+
+
+ %1 an image
+
+
+
+
+ %1 a file
+
+
+
+
+ %1 a video clip
+
+
+
+
+ %1 a sticker
+
+
+
+
+ %1 a notification
+
+
+
+
+ %1 an encrypted message
+
+
+
+
+ message-description:
+
+
+ sent
+ For when someone else is the sender
+
+
+
+
+ message-description:
+
+
+ sent
+ For when you are the sender
+
+
+
+
+ utils
+
+
+
+ You
+
+
+
+
+ sent a file.
+
+
+
+
+ sent an image.
+
+
+
+
+ sent an audio file.
+
+
+
+
+ sent a video
+
+
+
+
+ Unknown Message Type
+
+
+
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
index 1e1333b2..1271e39f 100644
--- a/resources/styles/nheko-dark.qss
+++ b/resources/styles/nheko-dark.qss
@@ -24,6 +24,17 @@ TimelineView > * {
border: none;
}
+UserMentionsWidget,
+UserMentionsWidget > * {
+ background-color: #202228;
+ border: none;
+}
+
+UserMentionsWidget > TimelineItem {
+ qproperty-backgroundColor: #202228;
+ qproperty-hoverColor: rgba(45, 49, 57, 120);
+}
+
#scroll_widget {
background-color: #202228;
}
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
index a70441be..3c7f3b71 100644
--- a/resources/styles/nheko.qss
+++ b/resources/styles/nheko.qss
@@ -24,6 +24,17 @@ TimelineView > * {
border: none;
}
+UserMentionsWidget,
+UserMentionsWidget > * {
+ background-color: white;
+ border: none;
+}
+
+UserMentionsWidget > TimelineItem {
+ qproperty-backgroundColor: white;
+ qproperty-hoverColor: rgba(192, 193, 195, 120);
+}
+
#scroll_widget {
background-color: white;
}
diff --git a/resources/styles/system.qss b/resources/styles/system.qss
index dfb8ce65..0a8c4b21 100644
--- a/resources/styles/system.qss
+++ b/resources/styles/system.qss
@@ -12,6 +12,16 @@ TimelineView > * {
border: none;
}
+UserMentionsWidget,
+UserMentionsWidget > * {
+ border: none;
+}
+
+UserMentionsWidget > TimelineItem {
+ qproperty-backgroundColor: palette(window);
+ qproperty-hoverColor: palette(base);
+}
+
TextInputWidget {
border: none;
border-top: 1px solid palette(mid);
diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp
index 57b61c75..ec745c04 100644
--- a/src/AvatarProvider.cpp
+++ b/src/AvatarProvider.cpp
@@ -16,30 +16,44 @@
*/
#include
+#include
#include
+#include
#include "AvatarProvider.h"
#include "Cache.h"
#include "Logging.h"
#include "MatrixClient.h"
+static QPixmapCache avatar_cache;
+
namespace AvatarProvider {
-
void
-resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback callback)
+resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback callback)
{
- const auto key = QString("%1 %2").arg(room_id).arg(user_id);
- const auto avatarUrl = Cache::avatarUrl(room_id, user_id);
+ avatar_cache.setCacheLimit(1024 * 1024);
- if (!Cache::AvatarUrls.contains(key) || !cache::client())
+ const auto cacheKey = avatarUrl + "_size_" + size;
+
+ if (!cache::client())
return;
if (avatarUrl.isEmpty())
return;
+ QPixmap pixmap;
+ if (avatar_cache.find(cacheKey, &pixmap)) {
+ nhlog::net()->info("cached pixmap {}", avatarUrl.toStdString());
+ callback(pixmap);
+ return;
+ }
+
auto data = cache::client()->image(avatarUrl);
if (!data.isNull()) {
- callback(QImage::fromData(data));
+ pixmap.loadFromData(data);
+ avatar_cache.insert(cacheKey, pixmap);
+ nhlog::net()->info("loaded pixmap from disk cache {}", avatarUrl.toStdString());
+ callback(pixmap);
return;
}
@@ -47,7 +61,12 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
QObject::connect(proxy.get(),
&AvatarProxy::avatarDownloaded,
receiver,
- [callback](const QByteArray &data) { callback(QImage::fromData(data)); });
+ [callback, cacheKey](const QByteArray &data) {
+ QPixmap pm;
+ pm.loadFromData(data);
+ avatar_cache.insert(cacheKey, pm);
+ callback(pm);
+ });
mtx::http::ThumbOpts opts;
opts.width = 256;
@@ -67,8 +86,26 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
cache::client()->saveImage(opts.mxc_url, res);
- auto data = QByteArray(res.data(), res.size());
- emit proxy->avatarDownloaded(data);
+ nhlog::net()->info("downloaded pixmap {}", opts.mxc_url);
+
+ emit proxy->avatarDownloaded(QByteArray(res.data(), res.size()));
});
}
+
+void
+resolve(const QString &room_id,
+ const QString &user_id,
+ int size,
+ QObject *receiver,
+ AvatarCallback callback)
+{
+ const auto key = QString("%1 %2").arg(room_id).arg(user_id);
+ const auto avatarUrl = Cache::avatarUrl(room_id, user_id);
+ const auto cacheKey = avatarUrl + "_size_" + size;
+
+ if (!Cache::AvatarUrls.contains(key) || !cache::client())
+ return;
+
+ resolve(avatarUrl, size, receiver, callback);
+}
}
diff --git a/src/AvatarProvider.h b/src/AvatarProvider.h
index 4b4e15e9..47ed028e 100644
--- a/src/AvatarProvider.h
+++ b/src/AvatarProvider.h
@@ -17,7 +17,7 @@
#pragma once
-#include
+#include
#include
class AvatarProxy : public QObject
@@ -28,9 +28,15 @@ signals:
void avatarDownloaded(const QByteArray &data);
};
-using AvatarCallback = std::function;
+using AvatarCallback = std::function;
namespace AvatarProvider {
void
-resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback cb);
+resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback cb);
+void
+resolve(const QString &room_id,
+ const QString &user_id,
+ int size,
+ QObject *receiver,
+ AvatarCallback cb);
}
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 56c79678..083dbe89 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
@@ -91,6 +92,7 @@ init(const QString &user_id)
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType>();
+ qRegisterMetaType>();
qRegisterMetaType>();
qRegisterMetaType>();
@@ -1232,6 +1234,27 @@ Cache::roomMessages()
return msgs;
}
+QMap
+Cache::getTimelineMentions()
+{
+ // TODO: Should be read-only, but getMentionsDb will attempt to create a DB
+ // if it doesn't exist, throwing an error.
+ auto txn = lmdb::txn::begin(env_, nullptr);
+
+ QMap notifs;
+
+ auto room_ids = getRoomIds(txn);
+
+ for (const auto &room_id : room_ids) {
+ auto roomNotifs = getTimelineMentionsForRoom(txn, room_id);
+ notifs[QString::fromStdString(room_id)] = roomNotifs;
+ }
+
+ txn.commit();
+
+ return notifs;
+}
+
mtx::responses::Timeline
Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id)
{
@@ -1807,10 +1830,7 @@ Cache::searchRooms(const std::string &query, std::uint8_t max_items)
std::vector results;
for (auto it = items.begin(); it != end; it++) {
- results.push_back(
- RoomSearchResult{it->second.first,
- it->second.second,
- QImage::fromData(image(txn, it->second.second.avatar_url))});
+ results.push_back(RoomSearchResult{it->second.first, it->second.second});
}
txn.commit();
@@ -1935,6 +1955,88 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
}
}
+mtx::responses::Notifications
+Cache::getTimelineMentionsForRoom(lmdb::txn &txn, const std::string &room_id)
+{
+ auto db = getMentionsDb(txn, room_id);
+
+ if (db.size(txn) == 0) {
+ return mtx::responses::Notifications{};
+ }
+
+ mtx::responses::Notifications notif;
+ std::string event_id, msg;
+
+ auto cursor = lmdb::cursor::open(txn, db);
+
+ while (cursor.get(event_id, msg, MDB_NEXT)) {
+ auto obj = json::parse(msg);
+
+ if (obj.count("event") == 0)
+ continue;
+
+ mtx::responses::Notification notification;
+ mtx::responses::from_json(obj, notification);
+
+ notif.notifications.push_back(notification);
+ }
+ cursor.close();
+
+ std::reverse(notif.notifications.begin(), notif.notifications.end());
+
+ return notif;
+}
+
+//! Add all notifications containing a user mention to the db.
+void
+Cache::saveTimelineMentions(const mtx::responses::Notifications &res)
+{
+ QMap> notifsByRoom;
+
+ // Sort into room-specific 'buckets'
+ for (const auto ¬if : res.notifications) {
+ json val = notif;
+ notifsByRoom[notif.room_id].push_back(notif);
+ }
+
+ auto txn = lmdb::txn::begin(env_);
+ // Insert the entire set of mentions for each room at a time.
+ QMap>::const_iterator it =
+ notifsByRoom.constBegin();
+ auto end = notifsByRoom.constEnd();
+ while (it != end) {
+ nhlog::db()->debug("Storing notifications for " + it.key());
+ saveTimelineMentions(txn, it.key(), std::move(it.value()));
+ ++it;
+ }
+
+ txn.commit();
+}
+
+void
+Cache::saveTimelineMentions(lmdb::txn &txn,
+ const std::string &room_id,
+ const QList &res)
+{
+ auto db = getMentionsDb(txn, room_id);
+
+ using namespace mtx::events;
+ using namespace mtx::events::state;
+
+ for (const auto ¬if : res) {
+ const auto event_id = utils::event_id(notif.event);
+
+ // double check that we have the correct room_id...
+ if (room_id.compare(notif.room_id) != 0) {
+ return;
+ }
+
+ json obj = notif;
+
+ lmdb::dbi_put(txn, db, lmdb::val(event_id), lmdb::val(obj.dump()));
+ }
+}
+
void
Cache::markSentNotification(const std::string &event_id)
{
diff --git a/src/Cache.h b/src/Cache.h
index 65c5263d..0da49793 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -32,6 +32,7 @@
#include
#include "Logging.h"
+#include "MatrixClient.h"
using mtx::events::state::JoinRule;
@@ -152,7 +153,6 @@ struct RoomSearchResult
{
std::string room_id;
RoomInfo info;
- QImage img;
};
Q_DECLARE_METATYPE(RoomSearchResult)
@@ -323,6 +323,8 @@ public:
std::map roomMessages();
+ QMap getTimelineMentions();
+
//! Retrieve all the user ids from a room.
std::vector roomMembers(const std::string &room_id);
@@ -402,6 +404,9 @@ public:
//! Check if we have sent a desktop notification for the given event id.
bool isNotificationSent(const std::string &event_id);
+ //! Add all notifications containing a user mention to the db.
+ void saveTimelineMentions(const mtx::responses::Notifications &res);
+
//! Remove old unused data.
void deleteOldMessages();
void deleteOldData() noexcept;
@@ -470,6 +475,15 @@ private:
lmdb::dbi &membersdb,
const mtx::responses::InvitedRoom &room);
+ //! Add a notification containing a user mention to the db.
+ void saveTimelineMentions(lmdb::txn &txn,
+ const std::string &room_id,
+ const QList &res);
+
+ //! Get timeline items that a user was mentions in for a given room
+ mtx::responses::Notifications getTimelineMentionsForRoom(lmdb::txn &txn,
+ const std::string &room_id);
+
QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
@@ -660,6 +674,11 @@ private:
return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE);
}
+ lmdb::dbi getMentionsDb(lmdb::txn &txn, const std::string &room_id)
+ {
+ return lmdb::dbi::open(txn, std::string(room_id + "/mentions").c_str(), MDB_CREATE);
+ }
+
//! Retrieves or creates the database that stores the open OLM sessions between our device
//! and the given curve25519 key which represents another device.
//!
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 18188429..21ded4b3 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -35,7 +35,6 @@
#include "TopRoomBar.h"
#include "TypingDisplay.h"
#include "UserInfoWidget.h"
-#include "UserMentionsWidget.h"
#include "UserSettingsPage.h"
#include "Utils.h"
#include "ui/OverlayModal.h"
@@ -44,7 +43,7 @@
#include "notifications/Manager.h"
#include "dialogs/ReadReceipts.h"
-#include "dialogs/UserMentions.h"
+#include "popups/UserMentions.h"
#include "timeline/TimelineViewManager.h"
// TODO: Needs to be updated with an actual secret.
@@ -90,13 +89,12 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent)
connect(sidebarActions_, &SideBarActions::joinRoom, this, &ChatPage::joinRoom);
connect(sidebarActions_, &SideBarActions::createRoom, this, &ChatPage::createRoom);
- user_info_widget_ = new UserInfoWidget(sideBar_);
- // user_mentions_widget_ = new UserMentionsWidget(sideBar_);
- room_list_ = new RoomList(sideBar_);
+ user_info_widget_ = new UserInfoWidget(sideBar_);
+ user_mentions_popup_ = new popups::UserMentions();
+ room_list_ = new RoomList(sideBar_);
connect(room_list_, &RoomList::joinRoom, this, &ChatPage::joinRoom);
sideBarLayout_->addWidget(user_info_widget_);
- // sideBarLayout_->addWidget(user_mentions_widget_);
sideBarLayout_->addWidget(room_list_);
sideBarLayout_->addWidget(sidebarActions_);
@@ -155,21 +153,27 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent)
});
connect(top_bar_, &TopRoomBar::mentionsClicked, this, [this](const QPoint &mentionsPos) {
- http::client()->notifications(
- 1000,
- "",
- "highlight",
- [this, mentionsPos](const mtx::responses::Notifications &res,
- mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn("failed to retrieve notifications: {} ({})",
- err->matrix_error.error,
- static_cast(err->status_code));
- return;
- }
+ if (user_mentions_popup_->isVisible()) {
+ user_mentions_popup_->hide();
+ } else {
+ showNotificationsDialog(mentionsPos);
+ http::client()->notifications(
+ 1000,
+ "",
+ "highlight",
+ [this, mentionsPos](const mtx::responses::Notifications &res,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn(
+ "failed to retrieve notifications: {} ({})",
+ err->matrix_error.error,
+ static_cast(err->status_code));
+ return;
+ }
- emit highlightedNotifsRetrieved(std::move(res), mentionsPos);
- });
+ emit highlightedNotifsRetrieved(std::move(res), mentionsPos);
+ });
+ }
});
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
@@ -519,8 +523,16 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent)
connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom);
connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendDesktopNotifications);
- connect(
- this, &ChatPage::highlightedNotifsRetrieved, this, &ChatPage::showNotificationsDialog);
+ connect(this,
+ &ChatPage::highlightedNotifsRetrieved,
+ this,
+ [](const mtx::responses::Notifications ¬if) {
+ try {
+ cache::client()->saveTimelineMentions(notif);
+ } catch (const lmdb::error &e) {
+ nhlog::db()->error("failed to save mentions: {}", e.what());
+ }
+ });
connect(communitiesList_,
&CommunitiesList::communityChanged,
@@ -559,6 +571,10 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent)
&ChatPage::initializeEmptyViews,
view_manager_,
&TimelineViewManager::initWithMessages);
+ connect(this,
+ &ChatPage::initializeMentions,
+ user_mentions_popup_,
+ &popups::UserMentions::initializeMentions);
connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) {
try {
room_list_->cleanupInvites(cache::client()->invites());
@@ -758,12 +774,12 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
}
void
-ChatPage::updateTopBarAvatar(const QString &roomid, const QPixmap &img)
+ChatPage::updateTopBarAvatar(const QString &roomid, const QString &img)
{
if (current_room_ != roomid)
return;
- top_bar_->updateRoomAvatar(img.toImage());
+ top_bar_->updateRoomAvatar(img);
}
void
@@ -791,7 +807,7 @@ ChatPage::changeTopRoomInfo(const QString &room_id)
if (img.isNull())
top_bar_->updateRoomAvatarFromName(name);
else
- top_bar_->updateRoomAvatar(img);
+ top_bar_->updateRoomAvatar(avatar_url);
} catch (const lmdb::error &e) {
nhlog::ui()->error("failed to change top bar room info: {}", e.what());
@@ -831,6 +847,7 @@ ChatPage::loadStateFromCache()
emit initializeEmptyViews(cache::client()->roomMessages());
emit initializeRoomList(cache::client()->roomInfo());
+ emit initializeMentions(cache::client()->getTimelineMentions());
emit syncTags(cache::client()->roomInfo().toStdMap());
cache::client()->calculateRoomReadStatus();
@@ -987,32 +1004,15 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res)
}
void
-ChatPage::showNotificationsDialog(const mtx::responses::Notifications &res, const QPoint &widgetPos)
+ChatPage::showNotificationsDialog(const QPoint &widgetPos)
{
- // TODO: This should NOT BE A DIALOG. Make the TimelineView support
- // creating a timeline view from notifications (similarly to how it can show history views)
- auto notifDialog = new dialogs::UserMentions();
- for (const auto &item : res.notifications) {
- const auto event_id = QString::fromStdString(utils::event_id(item.event));
+ auto notifDialog = user_mentions_popup_;
- try {
- const auto room_id = QString::fromStdString(item.room_id);
- const auto user_id = utils::event_sender(item.event);
- const auto body = utils::event_body(item.event);
-
- notifDialog->pushItem(event_id, user_id, body, room_id);
-
- } catch (const lmdb::error &e) {
- nhlog::db()->warn("error while sending desktop notification: {}", e.what());
- }
- }
notifDialog->setGeometry(
widgetPos.x() - (width() / 10), widgetPos.y() + 25, width() / 5, height() / 2);
- // notifDialog->move(widgetPos.x(), widgetPos.y());
- // notifDialog->setFixedWidth(width() / 10);
- // notifDialog->setFixedHeight(height() / 2);
+
notifDialog->raise();
- notifDialog->show();
+ notifDialog->showPopup();
}
void
@@ -1243,6 +1243,8 @@ ChatPage::sendTypingNotifications()
void
ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err)
{
+ // TODO: Initial Sync should include mentions as well...
+
if (err) {
const auto error = QString::fromStdString(err->matrix_error.error);
const auto msg = tr("Please try to login again: %1").arg(error);
@@ -1280,6 +1282,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request
emit initializeViews(std::move(res.rooms));
emit initializeRoomList(cache::client()->roomInfo());
+ emit initializeMentions(cache::client()->getTimelineMentions());
cache::client()->calculateRoomReadStatus();
emit syncTags(cache::client()->roomInfo().toStdMap());
@@ -1334,37 +1337,7 @@ ChatPage::getProfileInfo()
emit setUserDisplayName(QString::fromStdString(res.display_name));
- if (cache::client()) {
- auto data = cache::client()->image(res.avatar_url);
- if (!data.isNull()) {
- emit setUserAvatar(QImage::fromData(data));
- return;
- }
- }
-
- if (res.avatar_url.empty())
- return;
-
- http::client()->download(
- res.avatar_url,
- [this, res](const std::string &data,
- const std::string &,
- const std::string &,
- mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn(
- "failed to download user avatar: {} - {}",
- mtx::errors::to_string(err->matrix_error.errcode),
- err->matrix_error.error);
- return;
- }
-
- if (cache::client())
- cache::client()->saveImage(res.avatar_url, data);
-
- emit setUserAvatar(
- QImage::fromData(QByteArray(data.data(), data.size())));
- });
+ emit setUserAvatar(QString::fromStdString(res.avatar_url));
});
http::client()->joined_groups(
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 3c97ba25..e41ae1ae 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
@@ -33,6 +34,7 @@
#include "MatrixClient.h"
#include "Utils.h"
#include "notifications/Manager.h"
+#include "popups/UserMentions.h"
class OverlayModal;
class QuickSwitcher;
@@ -44,7 +46,6 @@ class TimelineViewManager;
class TopRoomBar;
class TypingDisplay;
class UserInfoWidget;
-class UserMentionsWidget;
class UserSettings;
class NotificationsManager;
@@ -128,7 +129,7 @@ signals:
void ownProfileOk();
void setUserDisplayName(const QString &name);
- void setUserAvatar(const QImage &avatar);
+ void setUserAvatar(const QString &avatar);
void loggedOut();
void trySyncCb();
@@ -139,6 +140,7 @@ signals:
void initializeRoomList(QMap);
void initializeViews(const mtx::responses::Rooms &rooms);
void initializeEmptyViews(const std::map &msgs);
+ void initializeMentions(const QMap ¬ifs);
void syncUI(const mtx::responses::Rooms &rooms);
void syncRoomlist(const std::map &updates);
void syncTags(const std::map &updates);
@@ -157,7 +159,7 @@ signals:
private slots:
void showUnreadMessageNotification(int count);
- void updateTopBarAvatar(const QString &roomid, const QPixmap &img);
+ void updateTopBarAvatar(const QString &roomid, const QString &img);
void changeTopRoomInfo(const QString &room_id);
void logout();
void removeRoom(const QString &room_id);
@@ -208,7 +210,7 @@ private:
//! Send desktop notification for the received messages.
void sendDesktopNotifications(const mtx::responses::Notifications &);
- void showNotificationsDialog(const mtx::responses::Notifications &, const QPoint &point);
+ void showNotificationsDialog(const QPoint &point);
QStringList generateTypingUsers(const QString &room_id,
const std::vector &typing_users);
@@ -242,7 +244,7 @@ private:
UserInfoWidget *user_info_widget_;
- UserMentionsWidget *user_mentions_widget_;
+ popups::UserMentions *user_mentions_popup_;
// Keeps track of the users currently typing on each room.
std::map> typingUsers_;
diff --git a/src/Logging.cpp b/src/Logging.cpp
index 686274d8..32287582 100644
--- a/src/Logging.cpp
+++ b/src/Logging.cpp
@@ -16,6 +16,8 @@ constexpr auto MAX_LOG_FILES = 3;
}
namespace nhlog {
+bool enable_debug_log_from_commandline = false;
+
void
init(const std::string &file_path)
{
diff --git a/src/Logging.h b/src/Logging.h
index 2feae60d..e54f3c3f 100644
--- a/src/Logging.h
+++ b/src/Logging.h
@@ -18,4 +18,6 @@ db();
std::shared_ptr
crypto();
+
+extern bool enable_debug_log_from_commandline;
}
diff --git a/src/RoomInfoListItem.cpp b/src/RoomInfoListItem.cpp
index 9bcce134..8aadbea2 100644
--- a/src/RoomInfoListItem.cpp
+++ b/src/RoomInfoListItem.cpp
@@ -19,8 +19,10 @@
#include
#include
#include
+#include
#include
+#include "AvatarProvider.h"
#include "Cache.h"
#include "Config.h"
#include "RoomInfoListItem.h"
@@ -140,6 +142,8 @@ RoomInfoListItem::resizeEvent(QResizeEvent *)
void
RoomInfoListItem::paintEvent(QPaintEvent *event)
{
+ bool rounded = QSettings().value("user/avatar/circles", true).toBool();
+
Q_UNUSED(event);
QPainter p(this);
@@ -287,7 +291,8 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
p.setPen(Qt::NoPen);
p.setBrush(brush);
- p.drawEllipse(avatarRegion.center(), wm.iconSize / 2, wm.iconSize / 2);
+ rounded ? p.drawEllipse(avatarRegion.center(), wm.iconSize / 2, wm.iconSize / 2)
+ : p.drawRoundedRect(avatarRegion, 3, 3);
QFont bubbleFont;
bubbleFont.setPointSizeF(bubbleFont.pointSizeF() * 1.4);
@@ -300,7 +305,9 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
p.save();
QPainterPath path;
- path.addEllipse(wm.padding, wm.padding, wm.iconSize, wm.iconSize);
+ rounded ? path.addEllipse(wm.padding, wm.padding, wm.iconSize, wm.iconSize)
+ : path.addRoundedRect(avatarRegion, 3, 3);
+
p.setClipPath(path);
p.drawPixmap(avatarRegion, roomAvatar_);
@@ -434,10 +441,12 @@ RoomInfoListItem::mousePressEvent(QMouseEvent *event)
}
void
-RoomInfoListItem::setAvatar(const QImage &img)
+RoomInfoListItem::setAvatar(const QString &avatar_url)
{
- roomAvatar_ = utils::scaleImageToPixmap(img, IconSize);
- update();
+ AvatarProvider::resolve(avatar_url, IconSize, this, [this](const QPixmap &img) {
+ roomAvatar_ = img;
+ update();
+ });
}
void
diff --git a/src/RoomInfoListItem.h b/src/RoomInfoListItem.h
index 40c938c1..54e02a76 100644
--- a/src/RoomInfoListItem.h
+++ b/src/RoomInfoListItem.h
@@ -73,7 +73,7 @@ public:
bool isPressed() const { return isPressed_; }
int unreadMessageCount() const { return unreadMsgCount_; }
- void setAvatar(const QImage &avatar_image);
+ void setAvatar(const QString &avatar_url);
void setDescriptionMessage(const DescInfo &info);
DescInfo lastMessageInfo() const { return lastMsgInfo_; }
diff --git a/src/RoomList.cpp b/src/RoomList.cpp
index 1abf3533..c5e05621 100644
--- a/src/RoomList.cpp
+++ b/src/RoomList.cpp
@@ -89,40 +89,7 @@ RoomList::updateAvatar(const QString &room_id, const QString &url)
if (url.isEmpty())
return;
- QByteArray savedImgData;
-
- if (cache::client())
- savedImgData = cache::client()->image(url);
-
- if (savedImgData.isEmpty()) {
- mtx::http::ThumbOpts opts;
- opts.mxc_url = url.toStdString();
- http::client()->get_thumbnail(
- opts, [room_id, opts, this](const std::string &res, mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn(
- "failed to download room avatar: {} {} {}",
- opts.mxc_url,
- mtx::errors::to_string(err->matrix_error.errcode),
- err->matrix_error.error);
- return;
- }
-
- if (cache::client())
- cache::client()->saveImage(opts.mxc_url, res);
-
- auto data = QByteArray(res.data(), res.size());
- QPixmap pixmap;
- pixmap.loadFromData(data);
-
- emit updateRoomAvatarCb(room_id, pixmap);
- });
- } else {
- QPixmap img;
- img.loadFromData(savedImgData);
-
- updateRoomAvatar(room_id, img);
- }
+ emit updateRoomAvatarCb(room_id, url);
}
void
@@ -252,7 +219,7 @@ RoomList::highlightSelectedRoom(const QString &room_id)
}
void
-RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
+RoomList::updateRoomAvatar(const QString &roomid, const QString &img)
{
if (!roomExists(roomid)) {
nhlog::ui()->warn("avatar update on non-existent room_id: {}",
@@ -260,7 +227,7 @@ RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
return;
}
- rooms_[roomid]->setAvatar(img.toImage());
+ rooms_[roomid]->setAvatar(img);
// Used to inform other widgets for the new image data.
emit roomAvatarChanged(roomid, img);
diff --git a/src/RoomList.h b/src/RoomList.h
index 155a969c..95fc0d9b 100644
--- a/src/RoomList.h
+++ b/src/RoomList.h
@@ -61,12 +61,12 @@ signals:
void totalUnreadMessageCountUpdated(int count);
void acceptInvite(const QString &room_id);
void declineInvite(const QString &room_id);
- void roomAvatarChanged(const QString &room_id, const QPixmap &img);
+ void roomAvatarChanged(const QString &room_id, const QString &img);
void joinRoom(const QString &room_id);
- void updateRoomAvatarCb(const QString &room_id, const QPixmap &img);
+ void updateRoomAvatarCb(const QString &room_id, const QString &img);
public slots:
- void updateRoomAvatar(const QString &roomid, const QPixmap &img);
+ void updateRoomAvatar(const QString &roomid, const QString &img);
void highlightSelectedRoom(const QString &room_id);
void updateUnreadMessageCount(const QString &roomid, int count, int highlightedCount);
void updateRoomDescription(const QString &roomid, const DescInfo &info);
diff --git a/src/TopRoomBar.cpp b/src/TopRoomBar.cpp
index a8049e3a..712fe9aa 100644
--- a/src/TopRoomBar.cpp
+++ b/src/TopRoomBar.cpp
@@ -46,9 +46,8 @@ TopRoomBar::TopRoomBar(QWidget *parent)
topLayout_->setContentsMargins(
2 * widgetMargin, widgetMargin, 2 * widgetMargin, widgetMargin);
- avatar_ = new Avatar(this);
+ avatar_ = new Avatar(this, fontHeight * 2);
avatar_->setLetter("");
- avatar_->setSize(fontHeight * 2);
textLayout_ = new QVBoxLayout();
textLayout_->setSpacing(0);
@@ -183,7 +182,7 @@ TopRoomBar::reset()
}
void
-TopRoomBar::updateRoomAvatar(const QImage &avatar_image)
+TopRoomBar::updateRoomAvatar(const QString &avatar_image)
{
avatar_->setImage(avatar_image);
update();
diff --git a/src/TopRoomBar.h b/src/TopRoomBar.h
index 5f2c936e..3243064e 100644
--- a/src/TopRoomBar.h
+++ b/src/TopRoomBar.h
@@ -44,7 +44,7 @@ class TopRoomBar : public QWidget
public:
TopRoomBar(QWidget *parent = 0);
- void updateRoomAvatar(const QImage &avatar_image);
+ void updateRoomAvatar(const QString &avatar_image);
void updateRoomAvatar(const QIcon &icon);
void updateRoomName(const QString &name);
void updateRoomTopic(QString topic);
diff --git a/src/TypingDisplay.cpp b/src/TypingDisplay.cpp
index 6059601d..43fabcd8 100644
--- a/src/TypingDisplay.cpp
+++ b/src/TypingDisplay.cpp
@@ -33,6 +33,14 @@ TypingDisplay::setUsers(const QStringList &uid)
text_.clear();
+ QString temp = text_ +=
+ tr("%1 and %2 are typing",
+ "Multiple users are typing. First argument is a comma separated list of potentially "
+ "multiple users. Second argument is the last user of that list. (If only one user is "
+ "typing, %1 is empty. You should still use it in your string though to silence Qt "
+ "warnings.)",
+ uid.size());
+
if (uid.isEmpty()) {
hide();
update();
@@ -40,12 +48,9 @@ TypingDisplay::setUsers(const QStringList &uid)
return;
}
- text_ = uid.join(", ");
-
- if (uid.size() == 1)
- text_ += tr(" is typing");
- else if (uid.size() > 1)
- text_ += tr(" are typing");
+ QStringList uidWithoutLast = uid;
+ uidWithoutLast.pop_back();
+ text_ = temp.arg(uidWithoutLast.join(", ")).arg(uid.back());
show();
update();
diff --git a/src/UserInfoWidget.cpp b/src/UserInfoWidget.cpp
index 5345fb2a..7a910340 100644
--- a/src/UserInfoWidget.cpp
+++ b/src/UserInfoWidget.cpp
@@ -1,3 +1,4 @@
+
/*
* nheko Copyright (C) 2017 Konstantinos Sideris
*
@@ -52,10 +53,9 @@ UserInfoWidget::UserInfoWidget(QWidget *parent)
textLayout_->setSpacing(widgetMargin / 2);
textLayout_->setContentsMargins(widgetMargin * 2, widgetMargin, widgetMargin, widgetMargin);
- userAvatar_ = new Avatar(this);
+ userAvatar_ = new Avatar(this, fontHeight * 2.5);
userAvatar_->setObjectName("userAvatar");
userAvatar_->setLetter(QChar('?'));
- userAvatar_->setSize(fontHeight * 2.5);
QFont nameFont;
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
@@ -134,14 +134,6 @@ UserInfoWidget::reset()
userAvatar_->setLetter(QChar('?'));
}
-void
-UserInfoWidget::setAvatar(const QImage &img)
-{
- avatar_image_ = img;
- userAvatar_->setImage(img);
- update();
-}
-
void
UserInfoWidget::setDisplayName(const QString &name)
{
@@ -160,6 +152,14 @@ UserInfoWidget::setUserId(const QString &userid)
{
user_id_ = userid;
userIdLabel_->setText(userid);
+ update();
+}
+
+void
+UserInfoWidget::setAvatar(const QString &url)
+{
+ userAvatar_->setImage(url);
+ update();
}
void
diff --git a/src/UserInfoWidget.h b/src/UserInfoWidget.h
index 65de7be9..263dd0c2 100644
--- a/src/UserInfoWidget.h
+++ b/src/UserInfoWidget.h
@@ -33,9 +33,9 @@ class UserInfoWidget : public QWidget
public:
UserInfoWidget(QWidget *parent = 0);
- void setAvatar(const QImage &img);
void setDisplayName(const QString &name);
void setUserId(const QString &userid);
+ void setAvatar(const QString &url);
void reset();
diff --git a/src/UserMentionsWidget.cpp b/src/UserMentionsWidget.cpp
deleted file mode 100644
index a28db930..00000000
--- a/src/UserMentionsWidget.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-#include "MainWindow.h"
-#include "UserMentionsWidget.h"
-#include "Utils.h"
-#include "ui/Ripple.h"
-#include "ui/RippleOverlay.h"
-
-constexpr int MaxUnreadCountDisplayed = 99;
-
-struct WMetrics
-{
- int maxHeight;
- int iconSize;
- int padding;
- int unit;
-
- int unreadLineWidth;
- int unreadLineOffset;
-
- int inviteBtnX;
- int inviteBtnY;
-};
-
-WMetrics
-getWMetrics(const QFont &font)
-{
- WMetrics m;
-
- const int height = QFontMetrics(font).lineSpacing();
-
- m.unit = height;
- m.maxHeight = std::ceil((double)height * 3.8);
- m.iconSize = std::ceil((double)height * 2.8);
- m.padding = std::ceil((double)height / 2.0);
- m.unreadLineWidth = m.padding - m.padding / 3;
- m.unreadLineOffset = m.padding - m.padding / 4;
-
- m.inviteBtnX = m.iconSize + 2 * m.padding;
- m.inviteBtnX = m.iconSize / 2.0 + m.padding + m.padding / 3.0;
-
- return m;
-}
-
-UserMentionsWidget::UserMentionsWidget(QWidget *parent)
- : QWidget(parent)
- , isPressed_(false)
- , unreadMsgCount_(0)
-{
- init(parent);
-
- QFont f;
- f.setPointSizeF(f.pointSizeF());
-
- const int fontHeight = QFontMetrics(f).height();
- const int widgetMargin = fontHeight / 3;
- const int contentHeight = fontHeight * 3;
-
- setFixedHeight(contentHeight + widgetMargin);
-
- topLayout_ = new QHBoxLayout(this);
- topLayout_->setSpacing(0);
- topLayout_->setMargin(widgetMargin);
-}
-
-void
-UserMentionsWidget::init(QWidget *parent)
-{
- setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- setMouseTracking(true);
- setAttribute(Qt::WA_Hover);
-
- setFixedHeight(getWMetrics(QFont{}).maxHeight);
-
- QPainterPath path;
- path.addRect(0, 0, parent->width(), height());
-
- ripple_overlay_ = new RippleOverlay(this);
- ripple_overlay_->setClipPath(path);
- ripple_overlay_->setClipping(true);
-
- unreadCountFont_.setPointSizeF(unreadCountFont_.pointSizeF() * 0.8);
- unreadCountFont_.setBold(true);
-
- bubbleDiameter_ = QFontMetrics(unreadCountFont_).averageCharWidth() * 3;
-}
-
-// void
-// UserMentionsWidget::resizeEvent(QResizeEvent *event)
-// {
-// Q_UNUSED(event);
-
-// const auto sz = utils::calculateSidebarSizes(QFont{});
-
-// if (width() <= sz.small) {
-// topLayout_->setContentsMargins(0, 0, logoutButtonSize_, 0);
-
-// } else {
-// topLayout_->setMargin(5);
-// }
-
-// QWidget::resizeEvent(event);
-// }
-
-void
-UserMentionsWidget::setPressedState(bool state)
-{
- if (isPressed_ != state) {
- isPressed_ = state;
- update();
- }
-}
-
-void
-UserMentionsWidget::resizeEvent(QResizeEvent *)
-{
- // Update ripple's clipping path.
- QPainterPath path;
- path.addRect(0, 0, width(), height());
-
- const auto sidebarSizes = utils::calculateSidebarSizes(QFont{});
-
- if (width() > sidebarSizes.small)
- setToolTip("");
- else
- setToolTip("");
-
- ripple_overlay_->setClipPath(path);
- ripple_overlay_->setClipping(true);
-}
-
-void
-UserMentionsWidget::mousePressEvent(QMouseEvent *event)
-{
- if (event->buttons() == Qt::RightButton) {
- QWidget::mousePressEvent(event);
- return;
- }
-
- emit clicked();
-
- setPressedState(true);
-
- // Ripple on mouse position by default.
- QPoint pos = event->pos();
- qreal radiusEndValue = static_cast(width()) / 3;
-
- Ripple *ripple = new Ripple(pos);
-
- ripple->setRadiusEndValue(radiusEndValue);
- ripple->setOpacityStartValue(0.15);
- ripple->setColor(QColor("white"));
- ripple->radiusAnimation()->setDuration(200);
- ripple->opacityAnimation()->setDuration(400);
-
- ripple_overlay_->addRipple(ripple);
-}
-
-void
-UserMentionsWidget::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event);
-
- QPainter p(this);
- p.setRenderHint(QPainter::TextAntialiasing);
- p.setRenderHint(QPainter::SmoothPixmapTransform);
- p.setRenderHint(QPainter::Antialiasing);
-
- auto wm = getWMetrics(QFont{});
-
- QPen titlePen(titleColor_);
- QPen subtitlePen(subtitleColor_);
-
- QFontMetrics metrics(QFont{});
-
- if (isPressed_) {
- p.fillRect(rect(), highlightedBackgroundColor_);
- titlePen.setColor(highlightedTitleColor_);
- subtitlePen.setColor(highlightedSubtitleColor_);
- } else if (underMouse()) {
- p.fillRect(rect(), hoverBackgroundColor_);
- titlePen.setColor(hoverTitleColor_);
- subtitlePen.setColor(hoverSubtitleColor_);
- } else {
- p.fillRect(rect(), backgroundColor_);
- titlePen.setColor(titleColor_);
- subtitlePen.setColor(subtitleColor_);
- }
-
- // Description line with the default font.
- int bottom_y = wm.maxHeight - wm.padding - metrics.ascent() / 2;
-
- const auto sidebarSizes = utils::calculateSidebarSizes(QFont{});
-
- if (width() > sidebarSizes.small) {
- QFont headingFont;
- headingFont.setWeight(QFont::Medium);
- p.setFont(headingFont);
- p.setPen(titlePen);
-
- QFont tsFont;
- tsFont.setPointSizeF(tsFont.pointSizeF() * 0.9);
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
- const int msgStampWidth = QFontMetrics(tsFont).width("timestamp") + 4;
-#else
- const int msgStampWidth = QFontMetrics(tsFont).horizontalAdvance("timestamp") + 4;
-#endif
- // We use the full width of the widget if there is no unread msg bubble.
- // const int bottomLineWidthLimit = (unreadMsgCount_ > 0) ? msgStampWidth : 0;
-
- // Name line.
- QFontMetrics fontNameMetrics(headingFont);
- int top_y = 2 * wm.padding + fontNameMetrics.ascent() / 2;
-
- const auto name = metrics.elidedText(
- "Mentions",
- Qt::ElideRight,
- (width() - wm.iconSize - 2 * wm.padding - msgStampWidth) * 0.8);
- p.drawText(QPoint(2 * wm.padding + wm.iconSize, top_y), name);
-
- p.setFont(QFont{});
- p.setPen(subtitlePen);
-
- // The limit is the space between the end of the avatar and the start of the
- // timestamp.
- int usernameLimit =
- std::max(0, width() - 3 * wm.padding - msgStampWidth - wm.iconSize - 20);
- auto userName =
- metrics.elidedText("Show Mentioned Messages", Qt::ElideRight, usernameLimit);
-
- p.setFont(QFont{});
- p.drawText(QPoint(2 * wm.padding + wm.iconSize, bottom_y), userName);
-
- // We show the last message timestamp.
- p.save();
- if (isPressed_) {
- p.setPen(QPen(highlightedTimestampColor_));
- } else if (underMouse()) {
- p.setPen(QPen(hoverTimestampColor_));
- } else {
- p.setPen(QPen(timestampColor_));
- }
-
- // p.setFont(tsFont);
- // p.drawText(QPoint(width() - wm.padding - msgStampWidth, top_y), "timestamp");
- p.restore();
- }
-
- p.setPen(Qt::NoPen);
-
- if (unreadMsgCount_ > 0) {
- QBrush brush;
- brush.setStyle(Qt::SolidPattern);
-
- brush.setColor(mentionedColor());
-
- if (isPressed_)
- brush.setColor(bubbleFgColor());
-
- p.setBrush(brush);
- p.setPen(Qt::NoPen);
- p.setFont(unreadCountFont_);
-
- // Extra space on the x-axis to accomodate the extra character space
- // inside the bubble.
- const int x_width = unreadMsgCount_ > MaxUnreadCountDisplayed
- ? QFontMetrics(p.font()).averageCharWidth()
- : 0;
-
- QRectF r(width() - bubbleDiameter_ - wm.padding - x_width,
- bottom_y - bubbleDiameter_ / 2 - 5,
- bubbleDiameter_ + x_width,
- bubbleDiameter_);
-
- if (width() == sidebarSizes.small)
- r = QRectF(width() - bubbleDiameter_ - 5,
- height() - bubbleDiameter_ - 5,
- bubbleDiameter_ + x_width,
- bubbleDiameter_);
-
- p.setPen(Qt::NoPen);
- p.drawEllipse(r);
-
- p.setPen(QPen(bubbleFgColor()));
-
- if (isPressed_)
- p.setPen(QPen(bubbleBgColor()));
-
- auto countTxt = unreadMsgCount_ > MaxUnreadCountDisplayed
- ? QString("99+")
- : QString::number(unreadMsgCount_);
-
- p.setBrush(Qt::NoBrush);
- p.drawText(r.translated(0, -0.5), Qt::AlignCenter, countTxt);
- }
-
- if (!isPressed_ && hasUnreadMessages_) {
- QPen pen;
- pen.setWidth(wm.unreadLineWidth);
- pen.setColor(highlightedBackgroundColor_);
-
- p.setPen(pen);
- p.drawLine(0, wm.unreadLineOffset, 0, height() - wm.unreadLineOffset);
- }
-}
\ No newline at end of file
diff --git a/src/UserMentionsWidget.h b/src/UserMentionsWidget.h
deleted file mode 100644
index 179f0026..00000000
--- a/src/UserMentionsWidget.h
+++ /dev/null
@@ -1,164 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-class FlatButton;
-class RippleOverlay;
-
-class UserMentionsWidget : public QWidget
-{
- Q_OBJECT
-
- Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor)
-
- Q_PROPERTY(QColor highlightedBackgroundColor READ highlightedBackgroundColor WRITE
- setHighlightedBackgroundColor)
- Q_PROPERTY(
- QColor hoverBackgroundColor READ hoverBackgroundColor WRITE setHoverBackgroundColor)
- Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
-
- Q_PROPERTY(QColor avatarBgColor READ avatarBgColor WRITE setAvatarBgColor)
- Q_PROPERTY(QColor avatarFgColor READ avatarFgColor WRITE setAvatarFgColor)
-
- Q_PROPERTY(QColor bubbleBgColor READ bubbleBgColor WRITE setBubbleBgColor)
- Q_PROPERTY(QColor bubbleFgColor READ bubbleFgColor WRITE setBubbleFgColor)
-
- Q_PROPERTY(QColor titleColor READ titleColor WRITE setTitleColor)
- Q_PROPERTY(QColor subtitleColor READ subtitleColor WRITE setSubtitleColor)
-
- Q_PROPERTY(QColor timestampColor READ timestampColor WRITE setTimestampColor)
- Q_PROPERTY(QColor highlightedTimestampColor READ highlightedTimestampColor WRITE
- setHighlightedTimestampColor)
- Q_PROPERTY(QColor hoverTimestampColor READ hoverTimestampColor WRITE setHoverTimestampColor)
-
- Q_PROPERTY(
- QColor highlightedTitleColor READ highlightedTitleColor WRITE setHighlightedTitleColor)
- Q_PROPERTY(QColor highlightedSubtitleColor READ highlightedSubtitleColor WRITE
- setHighlightedSubtitleColor)
-
- Q_PROPERTY(QColor hoverTitleColor READ hoverTitleColor WRITE setHoverTitleColor)
- Q_PROPERTY(QColor hoverSubtitleColor READ hoverSubtitleColor WRITE setHoverSubtitleColor)
-
- Q_PROPERTY(QColor mentionedColor READ mentionedColor WRITE setMentionedColor)
- Q_PROPERTY(QColor btnColor READ btnColor WRITE setBtnColor)
- Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor)
-
-public:
- UserMentionsWidget(QWidget *parent = 0);
-
- void updateUnreadMessageCount(int count);
- void clearUnreadMessageCount() { updateUnreadMessageCount(0); };
- bool isPressed() const { return isPressed_; }
- int unreadMessageCount() const { return unreadMsgCount_; }
- QColor borderColor() const { return borderColor_; }
- void setBorderColor(QColor &color) { borderColor_ = color; }
- QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
- QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
- QColor hoverTitleColor() const { return hoverTitleColor_; }
- QColor hoverSubtitleColor() const { return hoverSubtitleColor_; }
- QColor hoverTimestampColor() const { return hoverTimestampColor_; }
- QColor backgroundColor() const { return backgroundColor_; }
- QColor avatarBgColor() const { return avatarBgColor_; }
- QColor avatarFgColor() const { return avatarFgColor_; }
-
- QColor highlightedTitleColor() const { return highlightedTitleColor_; }
- QColor highlightedSubtitleColor() const { return highlightedSubtitleColor_; }
- QColor highlightedTimestampColor() const { return highlightedTimestampColor_; }
-
- QColor titleColor() const { return titleColor_; }
- QColor subtitleColor() const { return subtitleColor_; }
- QColor timestampColor() const { return timestampColor_; }
- QColor btnColor() const { return btnColor_; }
- QColor btnTextColor() const { return btnTextColor_; }
-
- QColor bubbleFgColor() const { return bubbleFgColor_; }
- QColor bubbleBgColor() const { return bubbleBgColor_; }
- QColor mentionedColor() const { return mentionedFontColor_; }
-
- void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
- void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
- void setHoverSubtitleColor(QColor &color) { hoverSubtitleColor_ = color; }
- void setHoverTitleColor(QColor &color) { hoverTitleColor_ = color; }
- void setHoverTimestampColor(QColor &color) { hoverTimestampColor_ = color; }
- void setBackgroundColor(QColor &color) { backgroundColor_ = color; }
- void setTimestampColor(QColor &color) { timestampColor_ = color; }
- void setAvatarFgColor(QColor &color) { avatarFgColor_ = color; }
- void setAvatarBgColor(QColor &color) { avatarBgColor_ = color; }
-
- void setHighlightedTitleColor(QColor &color) { highlightedTitleColor_ = color; }
- void setHighlightedSubtitleColor(QColor &color) { highlightedSubtitleColor_ = color; }
- void setHighlightedTimestampColor(QColor &color) { highlightedTimestampColor_ = color; }
-
- void setTitleColor(QColor &color) { titleColor_ = color; }
- void setSubtitleColor(QColor &color) { subtitleColor_ = color; }
-
- void setBtnColor(QColor &color) { btnColor_ = color; }
- void setBtnTextColor(QColor &color) { btnTextColor_ = color; }
-
- void setBubbleFgColor(QColor &color) { bubbleFgColor_ = color; }
- void setBubbleBgColor(QColor &color) { bubbleBgColor_ = color; }
- void setMentionedColor(QColor &color) { mentionedFontColor_ = color; }
-
-signals:
- void clicked();
-
-public slots:
- void setPressedState(bool state);
-
-protected:
- void mousePressEvent(QMouseEvent *event) override;
- void paintEvent(QPaintEvent *event) override;
- void resizeEvent(QResizeEvent *event) override;
-
-private:
- void init(QWidget *parent);
-
- RippleOverlay *ripple_overlay_;
-
- bool isPressed_ = false;
-
- bool hasUnreadMessages_ = true;
-
- int unreadMsgCount_ = 0;
-
- QHBoxLayout *topLayout_;
-
- QColor borderColor_;
- QColor highlightedBackgroundColor_;
- QColor hoverBackgroundColor_;
- QColor backgroundColor_;
-
- QColor highlightedTitleColor_;
- QColor highlightedSubtitleColor_;
-
- QColor titleColor_;
- QColor subtitleColor_;
-
- QColor hoverTitleColor_;
- QColor hoverSubtitleColor_;
-
- QColor btnColor_;
- QColor btnTextColor_;
-
- QRectF acceptBtnRegion_;
- QRectF declineBtnRegion_;
-
- // Fonts
- QColor mentionedFontColor_;
- QFont unreadCountFont_;
- int bubbleDiameter_;
-
- QColor timestampColor_;
- QColor highlightedTimestampColor_;
- QColor hoverTimestampColor_;
-
- QColor avatarBgColor_;
- QColor avatarFgColor_;
-
- QColor bubbleBgColor_;
- QColor bubbleFgColor_;
-};
\ No newline at end of file
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 49cb2c1f..9fd033e9 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -22,9 +22,11 @@
#include
#include
#include
+#include
#include
#include
#include
+#include
#include
#include "Config.h"
@@ -49,8 +51,9 @@ UserSettings::load()
isGroupViewEnabled_ = settings.value("user/group_view", true).toBool();
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
- theme_ = settings.value("user/theme", "light").toString();
+ theme_ = settings.value("user/theme", defaultTheme_).toString();
font_ = settings.value("user/font_family", "default").toString();
+ avatarCircles_ = settings.value("user/avatar/circles", true).toBool();
emojiFont_ = settings.value("user/emoji_font_family", "default").toString();
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
@@ -116,6 +119,10 @@ UserSettings::save()
settings.setValue("start_in_tray", isStartInTrayEnabled_);
settings.endGroup();
+ settings.beginGroup("avatar");
+ settings.setValue("circles", avatarCircles_);
+ settings.endGroup();
+
settings.setValue("font_size", baseFontSize_);
settings.setValue("typing_notifications", isTypingNotificationsEnabled_);
settings.setValue("read_receipts", isReadReceiptsEnabled_);
@@ -190,6 +197,15 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge
groupViewLayout->addWidget(groupViewLabel);
groupViewLayout->addWidget(groupViewToggle_, 0, Qt::AlignRight);
+ auto avatarViewLayout = new QHBoxLayout;
+ avatarViewLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
+ auto avatarViewLabel = new QLabel(tr("Circular Avatars"), this);
+ avatarViewLabel->setFont(font);
+ avatarCircles_ = new Toggle(this);
+
+ avatarViewLayout->addWidget(avatarViewLabel);
+ avatarViewLayout->addWidget(avatarCircles_, 0, Qt::AlignRight);
+
auto typingLayout = new QHBoxLayout;
typingLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
auto typingLabel = new QLabel(tr("Typing notifications"), this);
@@ -366,6 +382,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge
mainLayout_->addLayout(startInTrayOptionLayout_);
mainLayout_->addWidget(new HorizontalLine(this));
mainLayout_->addLayout(groupViewLayout);
+ mainLayout_->addLayout(avatarViewLayout);
mainLayout_->addWidget(new HorizontalLine(this));
mainLayout_->addLayout(typingLayout);
mainLayout_->addLayout(receiptsLayout);
@@ -446,6 +463,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge
settings_->setGroupView(!isDisabled);
});
+ connect(avatarCircles_, &Toggle::toggled, this, [this](bool isDisabled) {
+ settings_->setAvatarCircles(!isDisabled);
+ });
+
connect(typingNotifications_, &Toggle::toggled, this, [this](bool isDisabled) {
settings_->setTypingNotifications(!isDisabled);
});
diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index ffff1e20..28236e83 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
@@ -85,7 +86,13 @@ public:
save();
}
- QString theme() const { return !theme_.isEmpty() ? theme_ : "light"; }
+ void setAvatarCircles(bool state)
+ {
+ avatarCircles_ = state;
+ save();
+ }
+
+ QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
bool isTrayEnabled() const { return isTrayEnabled_; }
bool isStartInTrayEnabled() const { return isStartInTrayEnabled_; }
bool isGroupViewEnabled() const { return isGroupViewEnabled_; }
@@ -100,6 +107,11 @@ signals:
void groupViewStateChanged(bool state);
private:
+ // Default to system theme if QT_QPA_PLATFORMTHEME var is set.
+ QString defaultTheme_ =
+ QProcessEnvironment::systemEnvironment().value("QT_QPA_PLATFORMTHEME", "").isEmpty()
+ ? "light"
+ : "system";
QString theme_;
bool isTrayEnabled_;
bool isStartInTrayEnabled_;
@@ -107,6 +119,7 @@ private:
bool isTypingNotificationsEnabled_;
bool isReadReceiptsEnabled_;
bool hasDesktopNotifications_;
+ bool avatarCircles_;
double baseFontSize_;
QString font_;
QString emojiFont_;
@@ -156,6 +169,7 @@ private:
Toggle *typingNotifications_;
Toggle *readReceipts_;
Toggle *desktopNotifications_;
+ Toggle *avatarCircles_;
QLabel *deviceFingerprintValue_;
QLabel *deviceIdValue_;
diff --git a/src/Utils.cpp b/src/Utils.cpp
index d6b092b1..8c02b1c2 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -99,13 +100,13 @@ utils::descriptiveTime(const QDateTime &then)
const auto days = then.daysTo(now);
if (days == 0)
- return then.toString("HH:mm");
+ return then.time().toString(Qt::DefaultLocaleShortDate);
else if (days < 2)
- return QString("Yesterday");
- else if (days < 365)
- return then.toString("dd/MM");
+ return QString(QCoreApplication::translate("descriptiveTime", "Yesterday"));
+ else if (days < 7)
+ return then.toString("dddd");
- return then.toString("dd/MM/yy");
+ return then.date().toString(Qt::DefaultLocaleShortDate);
}
DescInfo
@@ -147,7 +148,7 @@ utils::getMessageDescription(const TimelineEvent &event,
DescInfo info;
if (sender == localUser)
- info.username = "You";
+ info.username = QCoreApplication::translate("utils", "You");
else
info.username = username;
@@ -323,10 +324,25 @@ utils::linkifyMessage(const QString &body)
return doc;
}
+QByteArray escapeRawHtml(const QByteArray &data) {
+ QByteArray buffer;
+ const size_t length = data.size();
+ buffer.reserve(length);
+ for(size_t pos = 0; pos != length; ++pos) {
+ switch(data.at(pos)) {
+ case '&': buffer.append("&"); break;
+ case '<': buffer.append("<"); break;
+ case '>': buffer.append(">"); break;
+ default: buffer.append(data.at(pos)); break;
+ }
+ }
+ return buffer;
+}
+
QString
utils::markdownToHtml(const QString &text)
{
- const auto str = text.toUtf8();
+ const auto str = escapeRawHtml(text.toUtf8());
const char *tmp_buf =
cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_DEFAULT);
@@ -366,16 +382,16 @@ utils::getQuoteBody(const RelatedInfo &related)
return markdownToHtml(related.quoted_body);
}
case MsgType::File: {
- return QString("sent a file.");
+ return QString(QCoreApplication::translate("utils", "sent a file."));
}
case MsgType::Image: {
- return QString("sent an image.");
+ return QString(QCoreApplication::translate("utils", "sent an image."));
}
case MsgType::Audio: {
- return QString("sent an audio file.");
+ return QString(QCoreApplication::translate("utils", "sent an audio file."));
}
case MsgType::Video: {
- return QString("sent a video");
+ return QString(QCoreApplication::translate("utils", "sent a video"));
}
default: {
return related.quoted_body;
@@ -387,14 +403,20 @@ QString
utils::linkColor()
{
QSettings settings;
- const auto theme = settings.value("user/theme", "light").toString();
+ // Default to system theme if QT_QPA_PLATFORMTHEME var is set.
+ QString defaultTheme =
+ QProcessEnvironment::systemEnvironment().value("QT_QPA_PLATFORMTHEME", "").isEmpty()
+ ? "light"
+ : "system";
+ const auto theme = settings.value("user/theme", defaultTheme).toString();
- if (theme == "light")
+ if (theme == "light") {
return "#0077b5";
- else if (theme == "dark")
+ } else if (theme == "dark") {
return "#38A3D8";
-
- return QPalette().color(QPalette::Link).name();
+ } else {
+ return QPalette().color(QPalette::Link).name();
+ }
}
uint32_t
diff --git a/src/Utils.h b/src/Utils.h
index 756dc1e3..225754be 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -9,6 +9,7 @@
#include "timeline/widgets/ImageItem.h"
#include "timeline/widgets/VideoItem.h"
+#include
#include
#include
#include
@@ -79,7 +80,9 @@ event_body(const mtx::events::collections::TimelineEvents &event);
//! Match widgets/events with a description message.
template
QString
-messageDescription(const QString &username = "", const QString &body = "")
+messageDescription(const QString &username = "",
+ const QString &body = "",
+ const bool isLocal = false)
{
using Audio = mtx::events::RoomEvent;
using Emote = mtx::events::RoomEvent;
@@ -91,24 +94,41 @@ messageDescription(const QString &username = "", const QString &body = "")
using Video = mtx::events::RoomEvent;
using Encrypted = mtx::events::EncryptedEvent;
- if (std::is_same::value || std::is_same::value)
- return QString("sent an audio clip");
- else if (std::is_same::value || std::is_same::value)
- return QString("sent an image");
- else if (std::is_same::value || std::is_same::value)
- return QString("sent a file");
- else if (std::is_same::value || std::is_same::value)
- return QString("sent a video clip");
- else if (std::is_same::value || std::is_same::value)
- return QString("sent a sticker");
- else if (std::is_same::value)
- return QString("sent a notification");
- else if (std::is_same::value)
+ // Sometimes the verb form of sent changes in some languages depending on the actor.
+ auto remoteSent = QCoreApplication::translate(
+ "message-description: ", "sent", "For when you are the sender");
+ auto localSent = QCoreApplication::translate(
+ "message-description:", "sent", "For when someone else is the sender");
+ QString sentVerb = isLocal ? localSent : remoteSent;
+ if (std::is_same::value || std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 an audio clip")
+ .arg(sentVerb);
+ } else if (std::is_same::value || std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 an image")
+ .arg(sentVerb);
+ } else if (std::is_same::value || std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a file")
+ .arg(sentVerb);
+ } else if (std::is_same::value || std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a video clip")
+ .arg(sentVerb);
+ } else if (std::is_same::value || std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a sticker")
+ .arg(sentVerb);
+ } else if (std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a notification")
+ .arg(sentVerb);
+ } else if (std::is_same::value) {
return QString(": %1").arg(body);
- else if (std::is_same::value)
+ } else if (std::is_same::value) {
return QString("* %1 %2").arg(username).arg(body);
- else if (std::is_same::value)
- return QString("sent an encrypted message");
+ } else if (std::is_same::value) {
+ return QCoreApplication::translate("message-description sent:",
+ "%1 an encrypted message")
+ .arg(sentVerb);
+ } else {
+ return QCoreApplication::translate("utils", "Unknown Message Type");
+ }
}
template
@@ -129,10 +149,12 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin
return DescInfo{
QString::fromStdString(msg.event_id),
- isEmote ? "" : (sender == localUser ? "You" : username),
+ isEmote ? ""
+ : (sender == localUser ? QCoreApplication::translate("utils", "You") : username),
sender,
(isText || isEmote)
- ? messageDescription(username, QString::fromStdString(msg.content.body).trimmed())
+ ? messageDescription(
+ username, QString::fromStdString(msg.content.body).trimmed(), sender == localUser)
: QString(" %1").arg(messageDescription()),
utils::descriptiveTime(ts),
ts};
diff --git a/src/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp
index f4167143..9e973efa 100644
--- a/src/dialogs/MemberList.cpp
+++ b/src/dialogs/MemberList.cpp
@@ -9,7 +9,6 @@
#include "dialogs/MemberList.h"
-#include "AvatarProvider.h"
#include "Cache.h"
#include "ChatPage.h"
#include "Config.h"
@@ -28,17 +27,10 @@ MemberItem::MemberItem(const RoomMember &member, QWidget *parent)
textLayout_->setMargin(0);
textLayout_->setSpacing(0);
- avatar_ = new Avatar(this);
- avatar_->setSize(44);
+ avatar_ = new Avatar(this, 44);
avatar_->setLetter(utils::firstChar(member.display_name));
- if (!member.avatar.isNull())
- avatar_->setImage(member.avatar);
- else
- AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
- member.user_id,
- this,
- [this](const QImage &img) { avatar_->setImage(img); });
+ avatar_->setImage(ChatPage::instance()->currentRoom(), member.user_id);
QFont nameFont;
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
@@ -97,7 +89,7 @@ MemberList::MemberList(const QString &room_id, QWidget *parent)
topLabel_->setAlignment(Qt::AlignCenter);
topLabel_->setFont(font);
- auto okBtn = new QPushButton("OK", this);
+ auto okBtn = new QPushButton(tr("OK"), this);
auto buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(15);
@@ -126,7 +118,7 @@ MemberList::MemberList(const QString &room_id, QWidget *parent)
qCritical() << e.what();
}
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &MemberList::close);
connect(okBtn, &QPushButton::clicked, this, &MemberList::close);
}
diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp
index dc4145db..58ad59c3 100644
--- a/src/dialogs/ReadReceipts.cpp
+++ b/src/dialogs/ReadReceipts.cpp
@@ -37,8 +37,7 @@ ReceiptItem::ReceiptItem(QWidget *parent,
auto displayName = Cache::displayName(room_id, user_id);
- avatar_ = new Avatar(this);
- avatar_->setSize(44);
+ avatar_ = new Avatar(this, 44);
avatar_->setLetter(utils::firstChar(displayName));
// If it's a matrix id we use the second letter.
@@ -56,10 +55,7 @@ ReceiptItem::ReceiptItem(QWidget *parent,
topLayout_->addWidget(avatar_);
topLayout_->addLayout(textLayout_, 1);
- AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
- user_id,
- this,
- [this](const QImage &img) { avatar_->setImage(img); });
+ avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
}
void
@@ -78,13 +74,15 @@ ReceiptItem::dateFormat(const QDateTime &then) const
auto days = then.daysTo(now);
if (days == 0)
- return QString("Today %1").arg(then.toString("HH:mm"));
+ return tr("Today %1").arg(then.time().toString(Qt::DefaultLocaleShortDate));
else if (days < 2)
- return QString("Yesterday %1").arg(then.toString("HH:mm"));
- else if (days < 365)
- return then.toString("dd/MM HH:mm");
+ return tr("Yesterday %1").arg(then.time().toString(Qt::DefaultLocaleShortDate));
+ else if (days < 7)
+ return QString("%1 %2")
+ .arg(then.toString("dddd"))
+ .arg(then.time().toString(Qt::DefaultLocaleShortDate));
- return then.toString("dd/MM/yy");
+ return then.toString(Qt::DefaultLocaleShortDate);
}
ReadReceipts::ReadReceipts(QWidget *parent)
@@ -131,7 +129,7 @@ ReadReceipts::ReadReceipts(QWidget *parent)
layout->addWidget(userList_);
layout->addLayout(buttonLayout);
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &ReadReceipts::close);
connect(okBtn, &QPushButton::clicked, this, &ReadReceipts::close);
}
diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp
index b2344f23..00b034cc 100644
--- a/src/dialogs/RoomSettings.cpp
+++ b/src/dialogs/RoomSettings.cpp
@@ -350,12 +350,12 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
keyRequestsToggle_->hide();
}
- avatar_ = new Avatar(this);
- avatar_->setSize(128);
+ avatar_ = new Avatar(this, 128);
if (avatarImg_.isNull())
avatar_->setLetter(utils::firstChar(QString::fromStdString(info_.name)));
else
- avatar_->setImage(avatarImg_);
+ avatar_->setImage(room_id_,
+ QString::fromStdString(http::client()->user_id().to_string()));
if (canChangeAvatar(room_id_.toStdString(), utils::localUser().toStdString())) {
auto filter = new ClickableFilter(this);
@@ -438,7 +438,7 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
resetErrorLabel();
});
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &RoomSettings::close);
connect(okBtn, &QPushButton::clicked, this, &RoomSettings::close);
}
@@ -487,7 +487,7 @@ RoomSettings::retrieveRoomInfo()
try {
usesEncryption_ = cache::client()->isRoomEncrypted(room_id_.toStdString());
info_ = cache::client()->singleRoomInfo(room_id_.toStdString());
- setAvatar(QImage::fromData(cache::client()->image(info_.avatar_url)));
+ setAvatar();
} catch (const lmdb::error &e) {
nhlog::db()->warn("failed to retrieve room info from cache: {}",
room_id_.toStdString());
@@ -633,14 +633,13 @@ RoomSettings::displayErrorMessage(const QString &msg)
}
void
-RoomSettings::setAvatar(const QImage &img)
+RoomSettings::setAvatar()
{
stopLoadingSpinner();
- avatarImg_ = img;
-
if (avatar_)
- avatar_->setImage(img);
+ avatar_->setImage(room_id_,
+ QString::fromStdString(http::client()->user_id().to_string()));
}
void
@@ -668,12 +667,12 @@ RoomSettings::updateAvatar()
QFile file{fileName, this};
if (format != "image") {
- displayErrorMessage(tr("The selected media is not an image"));
+ displayErrorMessage(tr("The selected file is not an image"));
return;
}
if (!file.open(QIODevice::ReadOnly)) {
- displayErrorMessage(tr("Error while reading media: %1").arg(file.errorString()));
+ displayErrorMessage(tr("Error while reading file: %1").arg(file.errorString()));
return;
}
@@ -733,7 +732,7 @@ RoomSettings::updateAvatar()
return;
}
- emit proxy->avatarChanged(QImage::fromData(content));
+ emit proxy->avatarChanged();
});
});
}
diff --git a/src/dialogs/RoomSettings.h b/src/dialogs/RoomSettings.h
index 6667b68b..e1807ba1 100644
--- a/src/dialogs/RoomSettings.h
+++ b/src/dialogs/RoomSettings.h
@@ -52,7 +52,7 @@ class ThreadProxy : public QObject
signals:
void error(const QString &msg);
- void avatarChanged(const QImage &img);
+ void avatarChanged();
void nameEventSent(const QString &);
void topicEventSent();
};
@@ -140,7 +140,7 @@ private:
void resetErrorLabel();
void displayErrorMessage(const QString &msg);
- void setAvatar(const QImage &img);
+ void setAvatar();
void setupEditButton();
//! Retrieve the current room information from cache.
void retrieveRoomInfo();
diff --git a/src/dialogs/UserMentions.cpp b/src/dialogs/UserMentions.cpp
deleted file mode 100644
index 8f56ec93..00000000
--- a/src/dialogs/UserMentions.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include
-
-#include "UserMentions.h"
-#include "timeline/TimelineItem.h"
-
-using namespace dialogs;
-
-UserMentions::UserMentions(QWidget *parent)
- : QWidget{parent}
-{
- top_layout_ = new QVBoxLayout(this);
- top_layout_->setSpacing(0);
- top_layout_->setMargin(0);
-
- scroll_area_ = new QScrollArea(this);
- scroll_area_->setWidgetResizable(true);
- scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-
- scroll_widget_ = new QWidget(this);
- scroll_widget_->setObjectName("scroll_widget");
-
- // Height of the typing display.
- QFont f;
- f.setPointSizeF(f.pointSizeF() * 0.9);
- const int bottomMargin = QFontMetrics(f).height() + 6;
-
- scroll_layout_ = new QVBoxLayout(scroll_widget_);
- scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin);
- scroll_layout_->setSpacing(0);
- scroll_layout_->setObjectName("timelinescrollarea");
-
- scroll_area_->setWidget(scroll_widget_);
- scroll_area_->setAlignment(Qt::AlignBottom);
-
- top_layout_->addWidget(scroll_area_);
-
- setLayout(top_layout_);
-}
-
-void
-UserMentions::pushItem(const QString &event_id,
- const QString &user_id,
- const QString &body,
- const QString &room_id)
-{
- TimelineItem *view_item = new TimelineItem(
- mtx::events::MessageType::Text, user_id, body, true, room_id, scroll_widget_);
- view_item->setEventId(event_id);
- setUpdatesEnabled(false);
- view_item->hide();
-
- scroll_layout_->addWidget(view_item);
- QTimer::singleShot(0, this, [view_item, this]() {
- view_item->show();
- view_item->adjustSize();
- setUpdatesEnabled(true);
- });
-}
\ No newline at end of file
diff --git a/src/dialogs/UserMentions.h b/src/dialogs/UserMentions.h
deleted file mode 100644
index e995b207..00000000
--- a/src/dialogs/UserMentions.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-
-namespace dialogs {
-
-class UserMentions : public QWidget
-{
- Q_OBJECT
-public:
- UserMentions(QWidget *parent = nullptr);
- void pushItem(const QString &event_id,
- const QString &user_id,
- const QString &body,
- const QString &room_id);
-
-private:
- QVBoxLayout *top_layout_;
- QVBoxLayout *scroll_layout_;
-
- QScrollArea *scroll_area_;
- QWidget *scroll_widget_;
-};
-
-}
\ No newline at end of file
diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp
index b8040f9f..5ad3afa2 100644
--- a/src/dialogs/UserProfile.cpp
+++ b/src/dialogs/UserProfile.cpp
@@ -114,9 +114,8 @@ UserProfile::UserProfile(QWidget *parent)
btnLayout->setSpacing(8);
btnLayout->setMargin(0);
- avatar_ = new Avatar(this);
+ avatar_ = new Avatar(this, 128);
avatar_->setLetter("X");
- avatar_->setSize(128);
QFont font;
font.setPointSizeF(font.pointSizeF() * 2);
@@ -183,7 +182,7 @@ UserProfile::UserProfile(QWidget *parent)
qRegisterMetaType>();
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &UserProfile::close);
connect(okBtn, &QPushButton::clicked, this, &UserProfile::close);
}
@@ -210,8 +209,7 @@ UserProfile::init(const QString &userId, const QString &roomId)
displayNameLabel_->setText(displayName);
avatar_->setLetter(utils::firstChar(displayName));
- AvatarProvider::resolve(
- roomId, userId, this, [this](const QImage &img) { avatar_->setImage(img); });
+ avatar_->setImage(roomId, userId);
auto localUser = utils::localUser();
diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp
index f905983a..c4d4327f 100644
--- a/src/popups/PopupItem.cpp
+++ b/src/popups/PopupItem.cpp
@@ -11,7 +11,7 @@ constexpr int PopupItemMargin = 3;
PopupItem::PopupItem(QWidget *parent)
: QWidget(parent)
- , avatar_{new Avatar(this)}
+ , avatar_{new Avatar(this, conf::popup::avatar)}
, hovering_{false}
{
setMouseTracking(true);
@@ -40,7 +40,6 @@ UserItem::UserItem(QWidget *parent)
: PopupItem(parent)
{
userName_ = new QLabel("Placeholder", this);
- avatar_->setSize(conf::popup::avatar);
avatar_->setLetter("P");
topLayout_->addWidget(avatar_);
topLayout_->addWidget(userName_, 1);
@@ -52,7 +51,6 @@ UserItem::UserItem(QWidget *parent, const QString &user_id)
{
auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_);
- avatar_->setSize(conf::popup::avatar);
avatar_->setLetter(utils::firstChar(displayName));
// If it's a matrix id we use the second letter.
@@ -87,16 +85,7 @@ UserItem::updateItem(const QString &user_id)
void
UserItem::resolveAvatar(const QString &user_id)
{
- AvatarProvider::resolve(
- ChatPage::instance()->currentRoom(), userId_, this, [this, user_id](const QImage &img) {
- // The user on the widget when the avatar is resolved,
- // might be different from the user that made the call.
- if (user_id == userId_)
- avatar_->setImage(img);
- else
- // We try to resolve the avatar again.
- resolveAvatar(userId_);
- });
+ avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
}
void
@@ -116,7 +105,6 @@ RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
auto name = QFontMetrics(QFont()).elidedText(
QString::fromStdString(res.info.name), Qt::ElideRight, parentWidget()->width() - 10);
- avatar_->setSize(conf::popup::avatar + 6);
avatar_->setLetter(utils::firstChar(name));
roomName_ = new QLabel(name, this);
@@ -125,8 +113,7 @@ RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
topLayout_->addWidget(avatar_);
topLayout_->addWidget(roomName_, 1);
- if (!res.img.isNull())
- avatar_->setImage(res.img);
+ avatar_->setImage(QString::fromStdString(res.info.avatar_url));
}
void
@@ -141,10 +128,7 @@ RoomItem::updateItem(const RoomSearchResult &result)
roomName_->setText(name);
- if (!result.img.isNull())
- avatar_->setImage(result.img);
- else
- avatar_->setLetter(utils::firstChar(name));
+ avatar_->setImage(QString::fromStdString(result.info.avatar_url));
}
void
@@ -154,4 +138,4 @@ RoomItem::mousePressEvent(QMouseEvent *event)
emit clicked(selectedText());
QWidget::mousePressEvent(event);
-}
\ No newline at end of file
+}
diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp
new file mode 100644
index 00000000..3480959a
--- /dev/null
+++ b/src/popups/UserMentions.cpp
@@ -0,0 +1,161 @@
+#include
+#include
+#include
+#include
+
+#include "Cache.h"
+#include "ChatPage.h"
+#include "Logging.h"
+#include "UserMentions.h"
+#include "timeline/TimelineItem.h"
+
+using namespace popups;
+
+UserMentions::UserMentions(QWidget *parent)
+ : QWidget{parent}
+{
+ setAttribute(Qt::WA_ShowWithoutActivating, true);
+ setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
+
+ tab_layout_ = new QTabWidget(this);
+
+ top_layout_ = new QVBoxLayout(this);
+ top_layout_->setSpacing(0);
+ top_layout_->setMargin(0);
+
+ local_scroll_area_ = new QScrollArea(this);
+ local_scroll_area_->setWidgetResizable(true);
+ local_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ local_scroll_widget_ = new QWidget(this);
+ local_scroll_widget_->setObjectName("local_scroll_widget");
+
+ all_scroll_area_ = new QScrollArea(this);
+ all_scroll_area_->setWidgetResizable(true);
+ all_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ all_scroll_widget_ = new QWidget(this);
+ all_scroll_widget_->setObjectName("all_scroll_widget");
+
+ // Height of the typing display.
+ QFont f;
+ f.setPointSizeF(f.pointSizeF() * 0.9);
+ const int bottomMargin = QFontMetrics(f).height() + 6;
+
+ local_scroll_layout_ = new QVBoxLayout(local_scroll_widget_);
+ local_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin);
+ local_scroll_layout_->setSpacing(0);
+ local_scroll_layout_->setObjectName("localscrollarea");
+
+ all_scroll_layout_ = new QVBoxLayout(all_scroll_widget_);
+ all_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin);
+ all_scroll_layout_->setSpacing(0);
+ all_scroll_layout_->setObjectName("allscrollarea");
+
+ local_scroll_area_->setWidget(local_scroll_widget_);
+ local_scroll_area_->setAlignment(Qt::AlignBottom);
+
+ all_scroll_area_->setWidget(all_scroll_widget_);
+ all_scroll_area_->setAlignment(Qt::AlignBottom);
+
+ tab_layout_->addTab(local_scroll_area_, tr("This Room"));
+ tab_layout_->addTab(all_scroll_area_, tr("All Rooms"));
+ top_layout_->addWidget(tab_layout_);
+
+ setLayout(top_layout_);
+}
+
+void
+UserMentions::initializeMentions(const QMap ¬ifs)
+{
+ nhlog::ui()->debug("Initializing " + std::to_string(notifs.size()) + " notifications.");
+
+ for (const auto &item : notifs) {
+ for (const auto notif : item.notifications) {
+ const auto event_id = QString::fromStdString(utils::event_id(notif.event));
+
+ try {
+ const auto room_id = QString::fromStdString(notif.room_id);
+ const auto user_id = utils::event_sender(notif.event);
+ const auto body = utils::event_body(notif.event);
+
+ pushItem(event_id,
+ user_id,
+ body,
+ room_id,
+ ChatPage::instance()->currentRoom());
+
+ } catch (const lmdb::error &e) {
+ nhlog::db()->warn("error while sending desktop notification: {}",
+ e.what());
+ }
+ }
+ }
+}
+
+void
+UserMentions::showPopup()
+{
+ for (auto widget : all_scroll_layout_->findChildren()) {
+ delete widget;
+ }
+ for (auto widget : local_scroll_layout_->findChildren()) {
+ delete widget;
+ }
+
+ auto notifs = cache::client()->getTimelineMentions();
+
+ initializeMentions(notifs);
+ show();
+}
+
+void
+UserMentions::pushItem(const QString &event_id,
+ const QString &user_id,
+ const QString &body,
+ const QString &room_id,
+ const QString ¤t_room_id)
+{
+ setUpdatesEnabled(false);
+
+ // Add to the 'all' section
+ TimelineItem *view_item = new TimelineItem(
+ mtx::events::MessageType::Text, user_id, body, true, room_id, all_scroll_widget_);
+ view_item->setEventId(event_id);
+ view_item->hide();
+
+ all_scroll_layout_->addWidget(view_item);
+ QTimer::singleShot(0, this, [view_item, this]() {
+ view_item->show();
+ view_item->adjustSize();
+ setUpdatesEnabled(true);
+ });
+
+ // if it matches the current room... add it to the current room as well.
+ if (QString::compare(room_id, current_room_id, Qt::CaseInsensitive) == 0) {
+ // Add to the 'local' section
+ TimelineItem *local_view_item = new TimelineItem(mtx::events::MessageType::Text,
+ user_id,
+ body,
+ true,
+ room_id,
+ local_scroll_widget_);
+ local_view_item->setEventId(event_id);
+ local_view_item->hide();
+ local_scroll_layout_->addWidget(local_view_item);
+
+ QTimer::singleShot(0, this, [local_view_item]() {
+ local_view_item->show();
+ local_view_item->adjustSize();
+ });
+ }
+}
+
+void
+UserMentions::paintEvent(QPaintEvent *)
+{
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+}
\ No newline at end of file
diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h
new file mode 100644
index 00000000..d7dfc575
--- /dev/null
+++ b/src/popups/UserMentions.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Logging.h"
+
+namespace popups {
+
+class UserMentions : public QWidget
+{
+ Q_OBJECT
+public:
+ UserMentions(QWidget *parent = nullptr);
+
+ void initializeMentions(const QMap ¬ifs);
+ void showPopup();
+
+protected:
+ void paintEvent(QPaintEvent *) override;
+
+private:
+ void pushItem(const QString &event_id,
+ const QString &user_id,
+ const QString &body,
+ const QString &room_id,
+ const QString ¤t_room_id);
+ QTabWidget *tab_layout_;
+ QVBoxLayout *top_layout_;
+ QVBoxLayout *local_scroll_layout_;
+ QVBoxLayout *all_scroll_layout_;
+
+ QScrollArea *local_scroll_area_;
+ QWidget *local_scroll_widget_;
+
+ QScrollArea *all_scroll_area_;
+ QWidget *all_scroll_widget_;
+};
+
+}
\ No newline at end of file
diff --git a/src/timeline/.TimelineItem.cpp.swp b/src/timeline/.TimelineItem.cpp.swp
new file mode 100644
index 00000000..75e03aeb
Binary files /dev/null and b/src/timeline/.TimelineItem.cpp.swp differ
diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp
index e52dce7b..7916bd80 100644
--- a/src/timeline/TimelineItem.cpp
+++ b/src/timeline/TimelineItem.cpp
@@ -282,6 +282,7 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(ty)
, room_id_{room_id}
{
init();
@@ -325,8 +326,7 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
generateBody(userid, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(userid);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -341,6 +341,7 @@ TimelineItem::TimelineItem(ImageItem *image,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::Image)
, room_id_{room_id}
{
init();
@@ -356,6 +357,7 @@ TimelineItem::TimelineItem(FileItem *file,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::File)
, room_id_{room_id}
{
init();
@@ -369,6 +371,7 @@ TimelineItem::TimelineItem(AudioItem *audio,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::Audio)
, room_id_{room_id}
{
init();
@@ -382,6 +385,7 @@ TimelineItem::TimelineItem(VideoItem *video,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::Video)
, room_id_{room_id}
{
init();
@@ -395,6 +399,7 @@ TimelineItem::TimelineItem(ImageItem *image,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Image)
, room_id_{room_id}
{
setupWidgetLayout, ImageItem>(
@@ -426,6 +431,7 @@ TimelineItem::TimelineItem(FileItem *file,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::File)
, room_id_{room_id}
{
setupWidgetLayout, FileItem>(
@@ -440,6 +446,7 @@ TimelineItem::TimelineItem(AudioItem *audio,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Audio)
, room_id_{room_id}
{
setupWidgetLayout, AudioItem>(
@@ -454,6 +461,7 @@ TimelineItem::TimelineItem(VideoItem *video,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Video)
, room_id_{room_id}
{
setupWidgetLayout, VideoItem>(
@@ -470,6 +478,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Emote)
, room_id_{room_id}
{
init();
@@ -547,8 +556,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent
generateBody(sender, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -565,6 +573,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Text)
, room_id_{room_id}
{
init();
@@ -595,8 +604,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent
generateBody(sender, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -781,9 +789,8 @@ TimelineItem::setupAvatarLayout(const QString &userName)
QFont f;
f.setPointSizeF(f.pointSizeF());
- userAvatar_ = new Avatar(this);
+ userAvatar_ = new Avatar(this, QFontMetrics(f).height() * 2);
userAvatar_->setLetter(QChar(userName[0]).toUpper());
- userAvatar_->setSize(QFontMetrics(f).height() * 2);
// TODO: The provided user name should be a UserId class
if (userName[0] == '@' && userName.size() > 1)
@@ -810,12 +817,12 @@ TimelineItem::setupSimpleLayout()
}
void
-TimelineItem::setUserAvatar(const QImage &avatar)
+TimelineItem::setUserAvatar(const QString &userid)
{
if (userAvatar_ == nullptr)
return;
- userAvatar_->setImage(avatar);
+ userAvatar_->setImage(room_id_, userid);
}
void
@@ -899,8 +906,7 @@ TimelineItem::addAvatar()
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(userid);
}
void
@@ -951,4 +957,4 @@ TimelineItem::openRawMessageViewer() const
"failed to serialize event ({}, {})", room_id, event_id);
}
});
-}
\ No newline at end of file
+}
diff --git a/src/timeline/TimelineItem.h b/src/timeline/TimelineItem.h
index c0dab6b8..356976e5 100644
--- a/src/timeline/TimelineItem.h
+++ b/src/timeline/TimelineItem.h
@@ -215,7 +215,7 @@ public:
void setBackgroundColor(const QColor &color) { backgroundColor_ = color; }
QColor backgroundColor() const { return backgroundColor_; }
- void setUserAvatar(const QImage &pixmap);
+ void setUserAvatar(const QString &userid);
DescInfo descriptionMessage() const { return descriptionMsg_; }
QString eventId() const { return event_id_; }
void setEventId(const QString &event_id) { event_id_ = event_id; }
@@ -277,7 +277,7 @@ private:
QFutureWatcher *colorGenerating_;
QString event_id_;
- mtx::events::MessageType message_type_;
+ mtx::events::MessageType message_type_ = mtx::events::MessageType::Unknown;
QString room_id_;
DescInfo descriptionMsg_;
@@ -336,8 +336,7 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget, const QString &userid, bool
generateBody(userid, displayName, "");
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(userid);
} else {
setupSimpleLayout();
}
@@ -381,8 +380,7 @@ TimelineItem::setupWidgetLayout(Widget *widget, const Event &event, bool withSen
generateBody(sender, displayName, "");
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
setupSimpleLayout();
}
diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp
index 18b73eb0..ed783e90 100644
--- a/src/timeline/TimelineView.cpp
+++ b/src/timeline/TimelineView.cpp
@@ -306,7 +306,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEventinboundMegolmSessionExists(index)) {
@@ -319,7 +322,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEventcritical("failed to check megolm session's existence: {}", e.what());
- dummy.content.body = "-- Decryption Error (failed to communicate with DB) --";
+ dummy.content.body = tr("-- Decryption Error (failed to communicate with DB) --",
+ "Placeholder, when the message can't be decrypted, because "
+ "the DB access failed when trying to lookup the session.")
+ .toStdString();
return {dummy, false};
}
@@ -335,7 +341,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEventcritical("failed to decrypt message with index ({}, {}, {}): {}",
@@ -343,7 +352,12 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent
+#include
+#include "AvatarProvider.h"
#include "Utils.h"
#include "ui/Avatar.h"
-Avatar::Avatar(QWidget *parent)
+Avatar::Avatar(QWidget *parent, int size)
: QWidget(parent)
+ , size_(size)
{
- size_ = ui::AvatarSize;
type_ = ui::AvatarType::Letter;
letter_ = "A";
@@ -60,21 +62,6 @@ Avatar::setBackgroundColor(const QColor &color)
background_color_ = color;
}
-void
-Avatar::setSize(int size)
-{
- size_ = size;
-
- if (!image_.isNull())
- pixmap_ = utils::scaleImageToPixmap(image_, size_);
-
- QFont _font(font());
- _font.setPointSizeF(size_ * (ui::FontSize) / 40);
-
- setFont(_font);
- update();
-}
-
void
Avatar::setLetter(const QString &letter)
{
@@ -84,12 +71,23 @@ Avatar::setLetter(const QString &letter)
}
void
-Avatar::setImage(const QImage &image)
+Avatar::setImage(const QString &avatar_url)
{
- image_ = image;
- type_ = ui::AvatarType::Image;
- pixmap_ = utils::scaleImageToPixmap(image_, size_);
- update();
+ AvatarProvider::resolve(avatar_url, size_, this, [this](QPixmap pm) {
+ type_ = ui::AvatarType::Image;
+ pixmap_ = pm;
+ update();
+ });
+}
+
+void
+Avatar::setImage(const QString &room, const QString &user)
+{
+ AvatarProvider::resolve(room, user, size_, this, [this](QPixmap pm) {
+ type_ = ui::AvatarType::Image;
+ pixmap_ = pm;
+ update();
+ });
}
void
@@ -103,6 +101,8 @@ Avatar::setIcon(const QIcon &icon)
void
Avatar::paintEvent(QPaintEvent *)
{
+ bool rounded = QSettings().value("user/avatar/circles", true).toBool();
+
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
@@ -116,7 +116,8 @@ Avatar::paintEvent(QPaintEvent *)
painter.setPen(Qt::NoPen);
painter.setBrush(brush);
- painter.drawEllipse(r.center(), hs, hs);
+ rounded ? painter.drawEllipse(r.center(), hs, hs)
+ : painter.drawRoundedRect(r, 3, 3);
}
switch (type_) {
@@ -129,7 +130,10 @@ Avatar::paintEvent(QPaintEvent *)
}
case ui::AvatarType::Image: {
QPainterPath ppath;
- ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_);
+
+ rounded ? ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_)
+ : ppath.addRoundedRect(r, 3, 3);
+
painter.setClipPath(ppath);
painter.drawPixmap(QRect(width() / 2 - hs, height() / 2 - hs, size_, size_),
pixmap_);
diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h
index 41967af5..a643c8c4 100644
--- a/src/ui/Avatar.h
+++ b/src/ui/Avatar.h
@@ -15,13 +15,13 @@ class Avatar : public QWidget
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
public:
- explicit Avatar(QWidget *parent = 0);
+ explicit Avatar(QWidget *parent = 0, int size = ui::AvatarSize);
void setBackgroundColor(const QColor &color);
void setIcon(const QIcon &icon);
- void setImage(const QImage &image);
+ void setImage(const QString &avatar_url);
+ void setImage(const QString &room, const QString &user);
void setLetter(const QString &letter);
- void setSize(int size);
void setTextColor(const QColor &color);
QColor backgroundColor() const;
@@ -41,7 +41,6 @@ private:
QColor background_color_;
QColor text_color_;
QIcon icon_;
- QImage image_;
QPixmap pixmap_;
int size_;
};
diff --git a/src/ui/InfoMessage.cpp b/src/ui/InfoMessage.cpp
index fa575d76..27bc0a5f 100644
--- a/src/ui/InfoMessage.cpp
+++ b/src/ui/InfoMessage.cpp
@@ -2,6 +2,7 @@
#include "Config.h"
#include
+#include
#include
#include
#include
@@ -61,14 +62,14 @@ DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent)
{
auto now = QDateTime::currentDateTime();
- QString fmt;
+ QString fmt = QLocale::system().dateFormat(QLocale::LongFormat);
- if (now.date().year() != datetime.date().year())
- fmt = QString("ddd d MMMM yy");
- else
- fmt = QString("ddd d MMMM");
+ if (now.date().year() == datetime.date().year()) {
+ QRegularExpression rx("[^a-zA-Z]*y+[^a-zA-Z]*");
+ fmt = fmt.remove(rx);
+ }
- msg_ = datetime.toString(fmt);
+ msg_ = datetime.date().toString(fmt);
QFontMetrics fm{font()};
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)