Compare commits
	
		
			209 Commits
		
	
	
		
			nightly/20
			...
			beta/20231
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6fc30b51d6 | |||
| 2719f303d9 | |||
| 100756b02d | |||
| b963a0bdf2 | |||
| d765fc9297 | |||
| f8f1301fa0 | |||
| b9d24298f7 | |||
| 6b9ce14ca9 | |||
| f88b8584af | |||
| ad35d108f2 | |||
| 63c3d0bf2e | |||
| 6f8f96edfd | |||
| 1bd7d75d29 | |||
| ad29993e17 | |||
| 70ce0c091e | |||
| ea5b2d6589 | |||
| eaababc817 | |||
| aa96b723bc | |||
| 1616261bef | |||
| 926ab72334 | |||
| a6d16ba1f8 | |||
| b0c205159e | |||
| 0c440660c4 | |||
| c8cc61520d | |||
| ab25276e27 | |||
| 7b41c6faff | |||
| 7a84518f2c | |||
| 1a12cb3b8c | |||
| 75a154f4fe | |||
| b95abbc891 | |||
| bc51b74692 | |||
| 899ff6120c | |||
| b77012baae | |||
| bc34abc8f4 | |||
| 69b59ad2dd | |||
| 1f2401bc7e | |||
| 06b0f1d39c | |||
| d5b36e7a6d | |||
| 4e2ae6cde0 | |||
| 0895a9f183 | |||
| e8075a412e | |||
| 4d55a1430d | |||
| 8de099e38d | |||
| 306c428019 | |||
| c0a5ced044 | |||
| 9f570a3cee | |||
| 2371f0c09f | |||
| f605cfce81 | |||
| 2aa3212b8f | |||
| 031d4348bc | |||
| e7436dbc05 | |||
| b291728472 | |||
| 5820052a53 | |||
| 65cc7a36ed | |||
| 46e2354274 | |||
| 1bbd8e31af | |||
| 0bd24bf8c5 | |||
| a028696e57 | |||
| c8b371e77a | |||
| b26259dac8 | |||
| 423290a09a | |||
| 3195fa9b22 | |||
| 4619f04f7b | |||
| ae2380c71b | |||
| 2bdf8e088c | |||
| 9947021394 | |||
| 80fe376e3c | |||
| d1eb1f5885 | |||
| b1ca6cf861 | |||
| 8c728374a7 | |||
| 3a693536e4 | |||
| ffcd3e59a4 | |||
| 77935de893 | |||
| 386ef224d1 | |||
| d62c2ff98e | |||
| 3b00b42213 | |||
| 21f3479a96 | |||
| 8307089900 | |||
| 2916b4c523 | |||
| 5530649f07 | |||
| 7581f9397a | |||
| 7f2c98a594 | |||
| 6341f32618 | |||
| a652a3d20f | |||
| 46da989a59 | |||
| 1a463ec662 | |||
| aee632c967 | |||
| e3a73ac932 | |||
| 416417d15a | |||
| 36a36dadcf | |||
| 38735b052a | |||
| 5b984396cf | |||
| 22cd3d4d4e | |||
| 53811f93b2 | |||
| 5e874f4f4b | |||
| a5bf258476 | |||
| 3b85425aa3 | |||
| e8e9fd30c9 | |||
| e914f795bc | |||
| 007b0c1132 | |||
| 11f67c73c8 | |||
| 01cbcbbb2c | |||
| 76fcd5f910 | |||
| 38a3da38c5 | |||
| f0b78036e3 | |||
| 2f7acbd31b | |||
| 8c1b214619 | |||
| e3e4de0fe7 | |||
| 452d49a439 | |||
| f53c2be978 | |||
| 36e5bdb839 | |||
| b54cb31d75 | |||
| 5134160539 | |||
| a9ad7d0bde | |||
| 6a1a9b60aa | |||
| 61126cfa64 | |||
| ec0feef74d | |||
| eff76eddc7 | |||
| 83039abc1b | |||
| bbdd68fb1d | |||
| afc0423bf6 | |||
| 0b18f3d145 | |||
| 593ecc9910 | |||
| 8fd7c70d1f | |||
| e71c1d1729 | |||
| 207872244b | |||
| 03b3530d3d | |||
| a7bd860e2b | |||
| df9c4b0653 | |||
| d2eed3af64 | |||
| f0eb826b64 | |||
| 2ed89fec3c | |||
| 281516823e | |||
| a2d110740d | |||
| ad14302ac5 | |||
| f903c635a7 | |||
| 5582d39a3f | |||
| 4f1e04a9b9 | |||
| 7514d75242 | |||
| fe2f3258b2 | |||
| c8ec980a3b | |||
| cb0e45c3fa | |||
| 532bf6c4ad | |||
| d7200cc8a3 | |||
| 3383f43688 | |||
| 26212b21b2 | |||
| 136f365fe2 | |||
| 2145ee6229 | |||
| 0f66152d72 | |||
| 24a0a384ff | |||
| 97297eacf7 | |||
| 5a70c7e7e8 | |||
| 7931d66b81 | |||
| 9fd48580bb | |||
| 1789402949 | |||
| 2546b69343 | |||
| fb420b2ff7 | |||
| 8a7547aaba | |||
| 9fe34c5282 | |||
| b948646cbd | |||
| 21c4afa564 | |||
| 41b9e541bb | |||
| e867b578bf | |||
| 944a20b2f3 | |||
| 7f4e3c4739 | |||
| 2ef5dcae71 | |||
| ad4d7aecc3 | |||
| 388ad92d96 | |||
| c37ec740e2 | |||
| cb31ea3575 | |||
| a27c344cb7 | |||
| a65c4f28e1 | |||
| ecd291c1be | |||
| 498dfed98b | |||
| da7366f23d | |||
| 8990162f99 | |||
| 1f0e2e92ad | |||
| 7743c14598 | |||
| c47cfe446d | |||
| 948e2cc837 | |||
| b611685653 | |||
| 26d16c38b8 | |||
| 5508f28c63 | |||
| c59ac1dd7f | |||
| 51cfef2fdc | |||
| 7611cb6147 | |||
| e04e3db69f | |||
| 6e7278c87a | |||
| 30e8bf56fb | |||
| d94c30833f | |||
| ed6e76aa0a | |||
| 1bf4d232b4 | |||
| 886074dc1e | |||
| 316750ad93 | |||
| f7bc95d47e | |||
| dc53aa887d | |||
| a179a9ae2d | |||
| 193b125c48 | |||
| 6d8adc9453 | |||
| e8b6914517 | |||
| da5d041227 | |||
| 5a48bccd25 | |||
| d4b3048e8e | |||
| 087135cccf | |||
| 8520ddaa83 | |||
| 7d856899c3 | |||
| 63687acc71 | |||
| 860f59893b | |||
| 39264e59c2 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -30,6 +30,9 @@ install/
 | 
			
		||||
*.log
 | 
			
		||||
*.pid
 | 
			
		||||
 | 
			
		||||
# tests
 | 
			
		||||
Testing/
 | 
			
		||||
 | 
			
		||||
# auto-gen files
 | 
			
		||||
src/app/resources.qrc
 | 
			
		||||
src/app/qml.qrc
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,5 @@ host = https://www.transifex.com
 | 
			
		||||
 | 
			
		||||
[o:savoirfairelinux:p:jami:r:jami_client_qt]
 | 
			
		||||
file_filter = translations/jami_client_qt_<lang>.ts
 | 
			
		||||
source_file = translations/jami_client_qt.ts
 | 
			
		||||
source_lang = en
 | 
			
		||||
source_file = translations/jami_client_qt_en.ts
 | 
			
		||||
type        = TS
 | 
			
		||||
 | 
			
		||||
@ -74,16 +74,19 @@ set(TESTS_DIR ${PROJECT_SOURCE_DIR}/tests)
 | 
			
		||||
# Here we let find_package(<PackageName>...) try to find Qt 6,
 | 
			
		||||
# If it is found, find_package will succeed, and the CMake variable
 | 
			
		||||
# QT_VERSION_MAJOR will be defined 6.
 | 
			
		||||
set(QT6_MINVER_MINOR 4)
 | 
			
		||||
if(QT6_VER AND QT6_PATH)
 | 
			
		||||
  find_package(QT NAMES Qt6 REQUIRED
 | 
			
		||||
    PATHS ${QT6_PATH} NO_DEFAULT_PATH)
 | 
			
		||||
else()
 | 
			
		||||
  message(STATUS "Looking for Qt 6" ${CMAKE_PREFIX_PATH})
 | 
			
		||||
  find_package(QT NAMES Qt6 REQUIRED)
 | 
			
		||||
endif()
 | 
			
		||||
if (${QT_VERSION_MAJOR} STRLESS 6)
 | 
			
		||||
  if (${QT_VERSION_MINOR} STRLESS 2)
 | 
			
		||||
    message(FATAL_ERROR "Qt 6.2 or higher is required.")
 | 
			
		||||
  endif()
 | 
			
		||||
if (${QT_VERSION_MINOR} GREATER_EQUAL ${QT6_MINVER_MINOR})
 | 
			
		||||
  # Qt version is 6.4 or higher
 | 
			
		||||
  message(STATUS "Found a suitable Qt version ${QT_VERSION}")
 | 
			
		||||
else()
 | 
			
		||||
  message(FATAL_ERROR "Qt 6.4 or higher is required. Found ${QT_VERSION}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(MSVC)
 | 
			
		||||
@ -206,6 +209,7 @@ set(COMMON_SOURCES
 | 
			
		||||
  ${APP_SRC_DIR}/pluginadapter.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/deviceitemlistmodel.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/pluginlistmodel.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/pluginstorelistmodel.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/pluginhandlerlistmodel.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/preferenceitemlistmodel.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/mediacodeclistmodel.cpp
 | 
			
		||||
@ -238,13 +242,15 @@ set(COMMON_SOURCES
 | 
			
		||||
  ${APP_SRC_DIR}/positioning.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/currentcall.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/messageparser.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/previewengine.cpp)
 | 
			
		||||
  ${APP_SRC_DIR}/previewengine.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/imagedownloader.cpp
 | 
			
		||||
  ${APP_SRC_DIR}/pluginversionmanager.cpp)
 | 
			
		||||
 | 
			
		||||
set(COMMON_HEADERS
 | 
			
		||||
  ${APP_SRC_DIR}/avatarimageprovider.h
 | 
			
		||||
  ${APP_SRC_DIR}/networkmanager.h
 | 
			
		||||
  ${APP_SRC_DIR}/smartlistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/updatemanager.h
 | 
			
		||||
  ${APP_SRC_DIR}/appversionmanager.h
 | 
			
		||||
  ${APP_SRC_DIR}/utils.h
 | 
			
		||||
  ${APP_SRC_DIR}/bannedlistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/version.h
 | 
			
		||||
@ -266,6 +272,7 @@ set(COMMON_HEADERS
 | 
			
		||||
  ${APP_SRC_DIR}/pluginadapter.h
 | 
			
		||||
  ${APP_SRC_DIR}/deviceitemlistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/pluginlistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/pluginstorelistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/pluginhandlerlistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/preferenceitemlistmodel.h
 | 
			
		||||
  ${APP_SRC_DIR}/mediacodeclistmodel.h
 | 
			
		||||
@ -301,7 +308,10 @@ set(COMMON_HEADERS
 | 
			
		||||
  ${APP_SRC_DIR}/positioning.h
 | 
			
		||||
  ${APP_SRC_DIR}/currentcall.h
 | 
			
		||||
  ${APP_SRC_DIR}/messageparser.h
 | 
			
		||||
  ${APP_SRC_DIR}/htmlparser.h)
 | 
			
		||||
  ${APP_SRC_DIR}/htmlparser.h
 | 
			
		||||
  ${APP_SRC_DIR}/imagedownloader.h
 | 
			
		||||
  ${APP_SRC_DIR}/pluginversionmanager.h)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# For libavutil/avframe.
 | 
			
		||||
set(LIBJAMI_CONTRIB_DIR "${DAEMON_DIR}/contrib")
 | 
			
		||||
@ -333,7 +343,7 @@ if(MSVC)
 | 
			
		||||
 | 
			
		||||
  list(APPEND COMMON_SOURCES
 | 
			
		||||
    ${APP_SRC_DIR}/connectivitymonitor.cpp
 | 
			
		||||
    ${APP_SRC_DIR}/updatemanager.cpp)
 | 
			
		||||
    ${APP_SRC_DIR}/appversionmanager.cpp)
 | 
			
		||||
  # preprocessor defines
 | 
			
		||||
  add_definitions(-DUNICODE -DQT_NO_DEBUG -DNDEBUG)
 | 
			
		||||
 | 
			
		||||
@ -363,7 +373,7 @@ if(MSVC)
 | 
			
		||||
 | 
			
		||||
  # daemon
 | 
			
		||||
  set(JAMID_SRC_PATH ${DAEMON_DIR}/contrib/msvc/include)
 | 
			
		||||
  set(JAMID_LIB ${DAEMON_DIR}/build/x64/ReleaseLib_win32/bin/jami.lib)
 | 
			
		||||
  set(JAMID_LIB ${DAEMON_DIR}/build/x64/ReleaseLib_win32/bin/libjami.lib)
 | 
			
		||||
  set(GNUTLS_LIB ${DAEMON_DIR}/contrib/msvc/lib/x64/libgnutls.lib)
 | 
			
		||||
 | 
			
		||||
  # Beta config
 | 
			
		||||
@ -385,7 +395,7 @@ elseif (NOT APPLE)
 | 
			
		||||
    ${APP_SRC_DIR}/xrectsel.c
 | 
			
		||||
    ${APP_SRC_DIR}/connectivitymonitor.cpp
 | 
			
		||||
    ${APP_SRC_DIR}/dbuserrorhandler.cpp
 | 
			
		||||
    ${APP_SRC_DIR}/updatemanager.cpp)
 | 
			
		||||
    ${APP_SRC_DIR}/appversionmanager.cpp)
 | 
			
		||||
  list(APPEND COMMON_HEADERS
 | 
			
		||||
    ${APP_SRC_DIR}/xrectsel.h
 | 
			
		||||
    ${APP_SRC_DIR}/dbuserrorhandler.h)
 | 
			
		||||
@ -437,7 +447,7 @@ elseif (NOT APPLE)
 | 
			
		||||
  find_library(X11 X11)
 | 
			
		||||
else() # APPLE
 | 
			
		||||
  list(APPEND COMMON_SOURCES
 | 
			
		||||
    ${APP_SRC_DIR}/os/macos/updatemanager.mm
 | 
			
		||||
    ${APP_SRC_DIR}/os/macos/appversionmanager.mm
 | 
			
		||||
    ${APP_SRC_DIR}/os/macos/connectivitymonitor.mm
 | 
			
		||||
    ${APP_SRC_DIR}/os/macos/macutils.mm)
 | 
			
		||||
  list(APPEND COMMON_HEADERS
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										65
									
								
								INSTALL.md
									
									
									
									
									
								
							
							
						
						@ -7,49 +7,55 @@ There are essentially two ways to build `client-qt`:
 | 
			
		||||
 | 
			
		||||
## Disclaimer
 | 
			
		||||
 | 
			
		||||
Because the client-qt is multi-platforms and supporting macOS, we need a recent version of Qt to do rendering with Metal. So, Qt 6.2 is necessary.
 | 
			
		||||
Because the client-qt is multi-platforms and supporting macOS, we need a recent version of Qt to do rendering with Metal. So, Qt 6.4 is necessary.
 | 
			
		||||
This version is generally not packaged on a lot of platforms, and to control available plugins and such, we have our own Qt packaged (available on https://jami.net on the distributions we support).
 | 
			
		||||
So, you will need to get Qt 6.2 first. For this, there is 3 methods:
 | 
			
		||||
So, you will need to get Qt 6.4 first. For this, there is 3 methods:
 | 
			
		||||
 | 
			
		||||
### Qt from https://jami.net (recommended)
 | 
			
		||||
### Qt from our repo (recommended)
 | 
			
		||||
 | 
			
		||||
If your distribution is supported, we provide a Qt package (libqt-jami) on our repo.
 | 
			
		||||
The files will be installed in `/usr/lib/libqt-jami`.
 | 
			
		||||
 | 
			
		||||
#### Install libqt-jami, Debian based
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
sudo apt install gnupg dirmngr ca-certificates curl --no-install-recommends
 | 
			
		||||
curl -s https://dl.jami.net/public-key.gpg | sudo tee /usr/share/keyrings/jami-archive-keyring.gpg > /dev/null
 | 
			
		||||
sudo sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/nightly/debian_<VERSION>/ jami main' > /etc/apt/sources.list.d/jami.list"
 | 
			
		||||
sudo apt-get update && sudo apt-get install jami
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Install libqt-jami, Ubuntu based
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
To install libqt-jami on Ubuntu, execute these commands replacing `ubuntu_<VERSION>` by your distribution version. For example "ubuntu_22.04"
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sudo apt install gnupg dirmngr ca-certificates curl --no-install-recommends
 | 
			
		||||
curl -s https://dl.jami.net/public-key.gpg | sudo tee /usr/share/keyrings/jami-archive-keyring.gpg > /dev/null
 | 
			
		||||
sudo sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/nightly/ubuntu_<VERSION>/ jami main' > /etc/apt/sources.list.d/jami.list"
 | 
			
		||||
sudo apt-get update && sudo apt-get install libqt-jami
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Install libqt-jami, Debian based
 | 
			
		||||
 | 
			
		||||
To install libqt-jami on Debian, execute these commands replacing `debian_<VERSION>` by your distribution version. For example "debian_11"
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sudo apt install gnupg dirmngr ca-certificates curl --no-install-recommends
 | 
			
		||||
curl -s https://dl.jami.net/public-key.gpg | sudo tee /usr/share/keyrings/jami-archive-keyring.gpg > /dev/null
 | 
			
		||||
sudo sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/nightly/debian_<VERSION>/ jami main' > /etc/apt/sources.list.d/jami.list"
 | 
			
		||||
sudo apt-get update && sudo apt-get install jami
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Install jami-libqt, Fedora based
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
To install libqt-jami on Fedora, execute these commands replacing `fedora_<VERSION>` by your distribution version. For example "fedora_38"
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sudo dnf config-manager --add-repo https://dl.jami.net/nightly/fedora_<VERSION>/jami-nightly.repo
 | 
			
		||||
sudo dnf update && sudo dnf install jami-libqt
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Qt from your distribution
 | 
			
		||||
 | 
			
		||||
If Qt 6.2 is available, you can use the packages from your distribution:
 | 
			
		||||
If Qt 6.4 is available, you can use the packages from your distribution:
 | 
			
		||||
 | 
			
		||||
It should be (For now qt5 only is packaged by distributions, so names can change).
 | 
			
		||||
 | 
			
		||||
#### Dependencies, Debian based
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
sudo apt-get install cmake make doxygen g++ gettext libnotify-dev pandoc nasm libqrencode-dev \
 | 
			
		||||
                     libnotify-dev libnm-dev \
 | 
			
		||||
                     qt6-base-dev \
 | 
			
		||||
@ -66,7 +72,7 @@ sudo apt-get install cmake make doxygen g++ gettext libnotify-dev pandoc nasm li
 | 
			
		||||
 | 
			
		||||
#### Dependencies, Fedora based
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
sudo dnf install qt6-qtsvg-devel qt6-qtwebengine-devel qt6-qtmultimedia-devel qt6-qtdeclarative-devel qt6-qtquickcontrols2-devel qt6-qtquickcontrols qrencode-devel NetworkManager-libnm-devel
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -87,6 +93,11 @@ for getting the latest development versions; otherwise, you can use
 | 
			
		||||
`git submodule update --init` then checkout specific commits for each
 | 
			
		||||
submodule).
 | 
			
		||||
 | 
			
		||||
If you're a developer you need to install clang-format separately before initializing with the command
 | 
			
		||||
```bash
 | 
			
		||||
sudo apt install clang-format
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
./build.py --init [--qt=<path/to/qt> (this is required for qmlformatting to work)]
 | 
			
		||||
```
 | 
			
		||||
@ -96,7 +107,7 @@ Then you will need to install dependencies:
 | 
			
		||||
- For GNU/Linux
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
./build.py --dependencies # needs sudo
 | 
			
		||||
sudo ./build.py --dependencies
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then, you can build daemon and the client using:
 | 
			
		||||
@ -107,9 +118,9 @@ Then, you can build daemon and the client using:
 | 
			
		||||
 | 
			
		||||
If you use a Qt version that is not system-wide installed, you need to
 | 
			
		||||
specify its path using the `--qt` flag, e.g.
 | 
			
		||||
`./build.py --install --qt=/home/<username>/Qt/6.2.1/gcc_64`.
 | 
			
		||||
`./build.py --install --qt=/home/<username>/Qt/6.4.1/gcc_64`.
 | 
			
		||||
 | 
			
		||||
Now you will have the daemon in `daemon/bin/jamid` and the client in
 | 
			
		||||
Now you will have the daemon in `daemon/bin/dbus/jamid` and the client in
 | 
			
		||||
`build/jami`. You can now run Jami using:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
@ -123,7 +134,7 @@ Notes:
 | 
			
		||||
 | 
			
		||||
## Build only the client
 | 
			
		||||
 | 
			
		||||
In order to use the Qt Client it is necessary to have the Qt version 6.2 or higher. If your system does not have it you can install it [from sources or download the binary installer](https://www.qt.io/download).
 | 
			
		||||
In order to use the Qt Client it is necessary to have the Qt version 6.4 or higher. If your system does not have it you can install it [from sources or download the binary installer](https://www.qt.io/download).
 | 
			
		||||
 | 
			
		||||
## Build only this repository
 | 
			
		||||
 | 
			
		||||
@ -173,7 +184,7 @@ Only 64-bit MSVC build can be compiled.
 | 
			
		||||
 | 
			
		||||
- Download [Qt (Open Source)](https://www.qt.io/download-open-source?hsCtaTracking=9f6a2170-a938-42df-a8e2-a9f0b1d6cdce%7C6cb0de4f-9bb5-4778-ab02-bfb62735f3e5)
 | 
			
		||||
 | 
			
		||||
- Using the online installer, install the following Qt 6.2.3 components:
 | 
			
		||||
- Using the online installer, install the following Qt 6.4.3 components:
 | 
			
		||||
 | 
			
		||||
  - Git 2.10.2
 | 
			
		||||
  - MSVC 2019 64-bit
 | 
			
		||||
@ -196,7 +207,7 @@ Only 64-bit MSVC build can be compiled.
 | 
			
		||||
 | 
			
		||||
  |                      | Qt Version |
 | 
			
		||||
  | -------------------- | ---------- |
 | 
			
		||||
  | Minimum requirement: | 6.2.3      |
 | 
			
		||||
  | Minimum requirement: | 6.4.3      |
 | 
			
		||||
 | 
			
		||||
- Install [Python3](https://www.python.org/downloads/) for Windows
 | 
			
		||||
 | 
			
		||||
@ -222,7 +233,7 @@ Only 64-bit MSVC build can be compiled.
 | 
			
		||||
- Using a new **Non-Elevated Command Prompt**
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
    python build.py --install --qt <path-to-qt-bin-folder> (e.g. C:/Qt/6.2.3/msvc2019_64)
 | 
			
		||||
    python build.py --install --qt <path-to-qt-bin-folder> (e.g. C:/Qt/6.4.3/msvc2019_64)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> **SDK** Note:
 | 
			
		||||
@ -265,7 +276,7 @@ Once the build has finished, you should then be able to use the Visual Studio So
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
    python extras\scripts\build-windows.py --init
 | 
			
		||||
    python extras\scripts\build-windows.py --qt <path-to-qt-bin-folder> (e.g. C:/Qt/6.2.3/msvc2019_64)
 | 
			
		||||
    python extras\scripts\build-windows.py --qt <path-to-qt-bin-folder> (e.g. C:/Qt/6.4.3/msvc2019_64)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Building On MacOS
 | 
			
		||||
@ -275,9 +286,9 @@ Once the build has finished, you should then be able to use the Visual Studio So
 | 
			
		||||
- macOS minimum version 10.15
 | 
			
		||||
- install python3
 | 
			
		||||
- download xcode
 | 
			
		||||
- install Qt 6.2
 | 
			
		||||
- install Qt 6.4
 | 
			
		||||
 | 
			
		||||
Qt 6.2 can be installed via brew
 | 
			
		||||
Qt 6.4 can be installed via brew
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
brew install qt
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								build.py
									
									
									
									
									
								
							
							
						
						@ -92,7 +92,7 @@ ZYPPER_INSTALL_SCRIPT = [
 | 
			
		||||
ZYPPER_DEPENDENCIES = [
 | 
			
		||||
    # build system
 | 
			
		||||
    'autoconf', 'autoconf-archive', 'automake', 'cmake', 'make', 'patch', 'gcc-c++',
 | 
			
		||||
    'libtool', 'which', 'pandoc', 'nasm', 'doxygen', 'graphviz',
 | 
			
		||||
    'libtool', 'which', 'pandoc', 'nasm', 'doxygen', 'graphviz', 'systemd-devel',
 | 
			
		||||
    # contrib dependencies
 | 
			
		||||
    'curl', 'gzip', 'bzip2',
 | 
			
		||||
    # daemon
 | 
			
		||||
@ -152,7 +152,7 @@ APT_DEPENDENCIES = [
 | 
			
		||||
    'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
 | 
			
		||||
    'libudev-dev', 'libyaml-cpp-dev', 'sip-tester', 'swig',
 | 
			
		||||
    'uuid-dev', 'yasm', 'libjsoncpp-dev', 'libva-dev', 'libvdpau-dev', 'libmsgpack-dev',
 | 
			
		||||
    'pandoc', 'nasm', 'dpkg-dev'
 | 
			
		||||
    'pandoc', 'nasm', 'dpkg-dev', 'libsystemd-dev'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
APT_CLIENT_DEPENDENCIES = [
 | 
			
		||||
@ -181,7 +181,7 @@ PACMAN_DEPENDENCIES = [
 | 
			
		||||
    'gcc', 'ffmpeg', 'boost', 'cppunit', 'libdbus', 'dbus-c++', 'libe-book', 'expat',
 | 
			
		||||
    'jack', 'opus', 'pcre', 'libpulse', 'speex', 'speexdsp', 'libtool', 'yaml-cpp',
 | 
			
		||||
    'swig', 'yasm', 'make', 'patch', 'pkg-config',
 | 
			
		||||
    'automake', 'libva', 'libvdpau', 'openssl', 'pandoc', 'nasm'
 | 
			
		||||
    'automake', 'libva', 'libvdpau', 'openssl', 'pandoc', 'nasm', 'systemd-libs'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
PACMAN_CLIENT_DEPENDENCIES = [
 | 
			
		||||
@ -329,7 +329,7 @@ def run_init(args):
 | 
			
		||||
    # The client submodule has QML files, so we need to run qmlformat on it,
 | 
			
		||||
    # and thus need to supply the Qt path.
 | 
			
		||||
    execute_script([f'{format_script} --install {client_hooks_dir}'
 | 
			
		||||
                    f' --qt {args.qt}' if args.qt else ''],
 | 
			
		||||
                    f' --qt {args.qt}'],
 | 
			
		||||
                   {"path": client_hooks_dir})
 | 
			
		||||
 | 
			
		||||
    # The daemon submodule has no QML files, so we don't need to run
 | 
			
		||||
@ -416,6 +416,11 @@ def run_install(args):
 | 
			
		||||
 | 
			
		||||
    command = ['extras/scripts/install.sh'] + install_args
 | 
			
		||||
 | 
			
		||||
    if 'TARBALLS' not in os.environ:
 | 
			
		||||
        print('info: consider setting the TARBALLS environment variable '
 | 
			
		||||
              'to a stable writable location to avoid loosing '
 | 
			
		||||
              'cached tarballs')
 | 
			
		||||
 | 
			
		||||
    if args.distribution == 'guix':
 | 
			
		||||
        if args.global_install:
 | 
			
		||||
            print('error: global install is not supported when using Guix.')
 | 
			
		||||
@ -425,10 +430,6 @@ def run_install(args):
 | 
			
		||||
        if 'TARBALLS' in os.environ:
 | 
			
		||||
            share_tarballs_args = ['--preserve=TARBALLS',
 | 
			
		||||
                                   f'--share={os.environ["TARBALLS"]}']
 | 
			
		||||
        else:
 | 
			
		||||
            print('info: consider setting the TARBALLS environment variable '
 | 
			
		||||
                  'to a stable writable location to avoid loosing '
 | 
			
		||||
                  'cached tarballs')
 | 
			
		||||
        command = ['guix', 'shell', f'--manifest={GUIX_MANIFEST}',
 | 
			
		||||
                   '--symlink=/usr/bin/env=bin/env',
 | 
			
		||||
                   '--symlink=/etc/ssl/certs=etc/ssl/certs',
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								daemon
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						@ -53,6 +53,11 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES_orig ${CMAKE_FIND_LIBRARY_SUFFIXES})
 | 
			
		||||
 | 
			
		||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib;.so;.dll")
 | 
			
		||||
 | 
			
		||||
# Add the lib prefix for Windows checks.
 | 
			
		||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
 | 
			
		||||
  set(CMAKE_FIND_LIBRARY_PREFIXES "lib;")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(WITH_DAEMON_SUBMODULE)
 | 
			
		||||
  find_library(LIBJAMI_LIB NAMES jami ring
 | 
			
		||||
    PATHS ${DAEMON_DIR}/src/.libs
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ RUN apt-get install -y -o Acquire::Retries=10 \
 | 
			
		||||
        cmake \
 | 
			
		||||
        libtool \
 | 
			
		||||
        libdbus-1-dev \
 | 
			
		||||
        libdbus-c++-dev \
 | 
			
		||||
        libsystemd-dev \
 | 
			
		||||
        libargon2-0-dev \
 | 
			
		||||
        libcanberra-gtk3-dev \
 | 
			
		||||
        libclutter-gtk-1.0-dev \
 | 
			
		||||
@ -66,4 +66,4 @@ RUN apt-get install -y -o Acquire::Retries=10 \
 | 
			
		||||
        libssl-dev
 | 
			
		||||
RUN apt-get install -y pandoc \
 | 
			
		||||
        googletest \
 | 
			
		||||
        libgtest-dev
 | 
			
		||||
        libgtest-dev
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								extras/packaging/gnu-linux/Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -215,7 +215,7 @@ make -f ${PACKAGING_DIR}/Makefile portable-release-tarball .tarball-version
 | 
			
		||||
        }
 | 
			
		||||
        stage('Sign & deploy packages') {
 | 
			
		||||
            agent {
 | 
			
		||||
                label 'ring-buildmachine-02.mtl.sfl'
 | 
			
		||||
                label 'jami-buildmachine-02.mtl.sfl'
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            when {
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ QT_MAJOR := 6
 | 
			
		||||
QT_MINOR := 4
 | 
			
		||||
QT_PATCH := 3
 | 
			
		||||
QT_TARBALL_CHECKSUM := 29a7eebdbba0ea57978dea6083709c93593a60f0f3133a3de08b9571ee8eaab4
 | 
			
		||||
DEBIAN_QT_VERSION := $(QT_MAJOR).$(QT_MINOR).$(QT_PATCH)-1
 | 
			
		||||
DEBIAN_QT_VERSION := $(QT_MAJOR).$(QT_MINOR).$(QT_PATCH)-2
 | 
			
		||||
DEBIAN_QT_DSC_FILENAME := libqt-jami_$(DEBIAN_QT_VERSION).dsc
 | 
			
		||||
QT_JAMI_PREFIX := /usr/lib/libqt-jami
 | 
			
		||||
 | 
			
		||||
@ -161,13 +161,12 @@ endif
 | 
			
		||||
#
 | 
			
		||||
DISTRIBUTIONS := \
 | 
			
		||||
	debian_11 \
 | 
			
		||||
	debian_12 \
 | 
			
		||||
	debian_testing \
 | 
			
		||||
	debian_unstable \
 | 
			
		||||
	ubuntu_20.04 \
 | 
			
		||||
	ubuntu_22.04 \
 | 
			
		||||
	ubuntu_22.10 \
 | 
			
		||||
	ubuntu_23.04 \
 | 
			
		||||
	fedora_36 \
 | 
			
		||||
	fedora_37 \
 | 
			
		||||
	fedora_38 \
 | 
			
		||||
	opensuse-leap_15.4 \
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
FROM ubuntu:22.10
 | 
			
		||||
FROM debian:bookworm
 | 
			
		||||
 | 
			
		||||
ENV DEBIAN_FRONTEND=noninteractive
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ RUN apt-get update && \
 | 
			
		||||
        equivs \
 | 
			
		||||
        python-is-python3 \
 | 
			
		||||
        libdbus-1-dev \
 | 
			
		||||
        libdbus-c++-dev \
 | 
			
		||||
        wget
 | 
			
		||||
 | 
			
		||||
ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
 | 
			
		||||
 | 
			
		||||
@ -1,106 +0,0 @@
 | 
			
		||||
FROM fedora:36
 | 
			
		||||
 | 
			
		||||
RUN dnf clean all
 | 
			
		||||
 | 
			
		||||
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
 | 
			
		||||
    dnf install -y mock
 | 
			
		||||
 | 
			
		||||
RUN dnf groupinstall -y "X Software Development"
 | 
			
		||||
 | 
			
		||||
RUN dnf install -y \
 | 
			
		||||
        git \
 | 
			
		||||
        rpm-build \
 | 
			
		||||
        tar \
 | 
			
		||||
        make \
 | 
			
		||||
        autoconf \
 | 
			
		||||
        automake \
 | 
			
		||||
        nasm \
 | 
			
		||||
        speexdsp-devel \
 | 
			
		||||
        pulseaudio-libs-devel \
 | 
			
		||||
        libcanberra-devel \
 | 
			
		||||
        libcurl-devel \
 | 
			
		||||
        libtool \
 | 
			
		||||
        mesa-libgbm-devel \
 | 
			
		||||
        mesa-dri-drivers \
 | 
			
		||||
        dbus-devel \
 | 
			
		||||
        expat-devel \
 | 
			
		||||
        pcre-devel \
 | 
			
		||||
        yaml-cpp-devel \
 | 
			
		||||
        dbus-c++-devel \
 | 
			
		||||
        dbus-devel \
 | 
			
		||||
        libXext-devel \
 | 
			
		||||
        libXfixes-devel \
 | 
			
		||||
        yasm \
 | 
			
		||||
        python2.7 \
 | 
			
		||||
        python3-html5lib \
 | 
			
		||||
        speex-devel \
 | 
			
		||||
        gsm-devel \
 | 
			
		||||
        chrpath \
 | 
			
		||||
        check \
 | 
			
		||||
        astyle \
 | 
			
		||||
        uuid-c++-devel \
 | 
			
		||||
        gettext-devel \
 | 
			
		||||
        gcc-c++ \
 | 
			
		||||
        which \
 | 
			
		||||
        alsa-lib-devel \
 | 
			
		||||
        systemd-devel \
 | 
			
		||||
        libuuid-devel \
 | 
			
		||||
        uuid-devel \
 | 
			
		||||
        gnutls-devel \
 | 
			
		||||
        nettle-devel \
 | 
			
		||||
        opus-devel \
 | 
			
		||||
        patch \
 | 
			
		||||
        jsoncpp-devel \
 | 
			
		||||
        libnatpmp-devel \
 | 
			
		||||
        webkitgtk4-devel \
 | 
			
		||||
        cryptopp-devel \
 | 
			
		||||
        libva-devel \
 | 
			
		||||
        libvdpau-devel \
 | 
			
		||||
        msgpack-devel \
 | 
			
		||||
        NetworkManager-libnm-devel \
 | 
			
		||||
        openssl-devel \
 | 
			
		||||
        clutter-devel \
 | 
			
		||||
        clutter-gtk-devel \
 | 
			
		||||
        libappindicator-gtk3-devel \
 | 
			
		||||
        libnotify-devel \
 | 
			
		||||
        libupnp-devel \
 | 
			
		||||
        qrencode-devel \
 | 
			
		||||
        libargon2-devel \
 | 
			
		||||
        libsndfile-devel \
 | 
			
		||||
        libdrm \
 | 
			
		||||
        gperf \
 | 
			
		||||
        bison \
 | 
			
		||||
        clang \
 | 
			
		||||
        clang-devel \
 | 
			
		||||
        llvm-devel \
 | 
			
		||||
        nodejs \
 | 
			
		||||
        flex \
 | 
			
		||||
        gstreamer1 gstreamer1-devel \
 | 
			
		||||
        gstreamer1-plugins-base-devel \
 | 
			
		||||
        gstreamer1-plugins-good \
 | 
			
		||||
        gstreamer1-plugins-bad-free-devel \
 | 
			
		||||
        nss-devel \
 | 
			
		||||
        libxcb* \
 | 
			
		||||
        libxkb* \
 | 
			
		||||
        libX11-devel \
 | 
			
		||||
        vulkan-devel \
 | 
			
		||||
        libXrender-devel \
 | 
			
		||||
        xcb-util-* \
 | 
			
		||||
        xz \
 | 
			
		||||
        xkeyboard-config \
 | 
			
		||||
        libnotify \
 | 
			
		||||
        wget \
 | 
			
		||||
        libstdc++-static \
 | 
			
		||||
        sqlite-devel \
 | 
			
		||||
        perl-generators \
 | 
			
		||||
        perl-English \
 | 
			
		||||
        libxshmfence-devel \
 | 
			
		||||
        ninja-build \
 | 
			
		||||
        clang \
 | 
			
		||||
        cmake \
 | 
			
		||||
        fmt-devel \
 | 
			
		||||
        cups-devel #Chromium for Qt
 | 
			
		||||
 | 
			
		||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
 | 
			
		||||
 | 
			
		||||
CMD ["/opt/build-package-rpm.sh"]
 | 
			
		||||
@ -27,8 +27,6 @@ RUN dnf install -y \
 | 
			
		||||
        expat-devel \
 | 
			
		||||
        pcre-devel \
 | 
			
		||||
        yaml-cpp-devel \
 | 
			
		||||
        dbus-c++-devel \
 | 
			
		||||
        dbus-devel \
 | 
			
		||||
        libXext-devel \
 | 
			
		||||
        libXfixes-devel \
 | 
			
		||||
        yasm \
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,6 @@ RUN dnf install -y \
 | 
			
		||||
        expat-devel \
 | 
			
		||||
        pcre-devel \
 | 
			
		||||
        yaml-cpp-devel \
 | 
			
		||||
        dbus-c++-devel \
 | 
			
		||||
        dbus-devel \
 | 
			
		||||
        libXext-devel \
 | 
			
		||||
        libXfixes-devel \
 | 
			
		||||
        yasm \
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ RUN zypper --non-interactive install -y \
 | 
			
		||||
        libtool \
 | 
			
		||||
        pcre-devel \
 | 
			
		||||
        yaml-cpp-devel \
 | 
			
		||||
        libdbus-c++-devel \
 | 
			
		||||
        libXext-devel \
 | 
			
		||||
        libXfixes-devel \
 | 
			
		||||
        yasm \
 | 
			
		||||
@ -51,7 +50,6 @@ RUN zypper --non-interactive install -y \
 | 
			
		||||
        clutter-gtk-devel \
 | 
			
		||||
        libnma-devel \
 | 
			
		||||
        libcryptopp-devel \
 | 
			
		||||
        libdbus-c++-devel \
 | 
			
		||||
        libexpat-devel \
 | 
			
		||||
        gnome-icon-theme-symbolic \
 | 
			
		||||
        libgsm-devel \
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
From f4410fcbb093f259eaff4a20fc4266a535479235 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Sebastien Blin <sebastien.blin@savoirfairelinux.com>
 | 
			
		||||
Date: Mon, 29 May 2023 13:09:53 -0400
 | 
			
		||||
Subject: [PATCH] OpenFile portal: do not use O_PATH fds
 | 
			
		||||
 | 
			
		||||
Using O_PATH requires correctly specifying whether the fd is writable or
 | 
			
		||||
not. Stating that the fd is writable without it actually being writable
 | 
			
		||||
results into rejection on xdg-desktop-portal side. Other implementations
 | 
			
		||||
like xdg-open or gtk have also moved away from O_PATH fds so this will
 | 
			
		||||
make a matching implementation and avoid possible rejections from xdp.
 | 
			
		||||
 | 
			
		||||
Fixes: QTBUG-113143
 | 
			
		||||
Original: https://codereview.qt-project.org/c/qt/qtbase/+/475425
 | 
			
		||||
---
 | 
			
		||||
 qtbase/src/gui/platform/unix/qgenericunixservices.cpp | 8 ++------
 | 
			
		||||
 1 file changed, 2 insertions(+), 6 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/qtbase/src/gui/platform/unix/qgenericunixservices.cpp b/qtbase/src/gui/platform/unix/qgenericunixservices.cpp
 | 
			
		||||
index a0e5466c58..fe0fdaa625 100644
 | 
			
		||||
--- a/qtbase/src/gui/platform/unix/qgenericunixservices.cpp
 | 
			
		||||
+++ b/qtbase/src/gui/platform/unix/qgenericunixservices.cpp
 | 
			
		||||
@@ -163,8 +163,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
 | 
			
		||||
     // handle_token (s) -  A string that will be used as the last element of the @handle.
 | 
			
		||||
     // writable (b) - Whether to allow the chosen application to write to the file.
 | 
			
		||||
 
 | 
			
		||||
-#ifdef O_PATH
 | 
			
		||||
-    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH);
 | 
			
		||||
+    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY);
 | 
			
		||||
     if (fd != -1) {
 | 
			
		||||
         QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1,
 | 
			
		||||
                                                               "/org/freedesktop/portal/desktop"_L1,
 | 
			
		||||
@@ -174,16 +173,13 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
 | 
			
		||||
         QDBusUnixFileDescriptor descriptor;
 | 
			
		||||
         descriptor.giveFileDescriptor(fd);
 | 
			
		||||
 
 | 
			
		||||
-        const QVariantMap options = {{"writable"_L1, true}};
 | 
			
		||||
+        const QVariantMap options = {};
 | 
			
		||||
 
 | 
			
		||||
         // FIXME parent_window_id
 | 
			
		||||
         message << QString() << QVariant::fromValue(descriptor) << options;
 | 
			
		||||
 
 | 
			
		||||
         return QDBusConnection::sessionBus().call(message);
 | 
			
		||||
     }
 | 
			
		||||
-#else
 | 
			
		||||
-    Q_UNUSED(url);
 | 
			
		||||
-#endif
 | 
			
		||||
 
 | 
			
		||||
     return QDBusMessage::createError(QDBusError::InternalError, qt_error_string());
 | 
			
		||||
 }
 | 
			
		||||
-- 
 | 
			
		||||
2.40.1
 | 
			
		||||
 | 
			
		||||
@ -1 +1,2 @@
 | 
			
		||||
0001-fix-gcc13.patch
 | 
			
		||||
0001-fix-gcc13.patch
 | 
			
		||||
0002-OpenFile-portal-do-not-use-O_PATH-fds.patch
 | 
			
		||||
@ -16,7 +16,7 @@ Build-Depends: debhelper (>= 9),
 | 
			
		||||
               libayatana-appindicator3-dev | libappindicator3-dev,
 | 
			
		||||
# daemon
 | 
			
		||||
               libdbus-1-dev,
 | 
			
		||||
               libdbus-c++-dev,
 | 
			
		||||
               libsystemd-dev,
 | 
			
		||||
               libupnp-dev,
 | 
			
		||||
               libgnutls28-dev,
 | 
			
		||||
               libpulse-dev,
 | 
			
		||||
@ -68,7 +68,7 @@ Depends: gnupg,
 | 
			
		||||
Replaces: jami,
 | 
			
		||||
          jami-libclient,
 | 
			
		||||
          jami-daemon,
 | 
			
		||||
          libqt-jami (>= 6.2.3),
 | 
			
		||||
          libqt-jami (>= 6.4.3),
 | 
			
		||||
Conflicts: jami,
 | 
			
		||||
           jami-libclient,
 | 
			
		||||
           jami-libclient-gnome,
 | 
			
		||||
@ -84,7 +84,7 @@ Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends},
 | 
			
		||||
         ${misc:Depends},
 | 
			
		||||
         jami-daemon (=${binary:Version}),
 | 
			
		||||
         libqt-jami (>= 6.2.3)
 | 
			
		||||
         libqt-jami (>= 6.4.3)
 | 
			
		||||
Provides: jami-qt
 | 
			
		||||
Replaces: jami-all,
 | 
			
		||||
          jami-libclient (<= 20220516.0214.9b42ad3~dfsg1-1)
 | 
			
		||||
 | 
			
		||||
@ -93,12 +93,12 @@ if [ -f /etc/os-release ]; then
 | 
			
		||||
    # Set-up Jami repository end tag
 | 
			
		||||
    if [ "${VERSION_CODENAME}" = "bullseye" ] || [ "${ID}_${VERSION_ID}" = "debian_11" ]; then
 | 
			
		||||
        ENDTAG="debian_11"
 | 
			
		||||
    elif [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${ID}_${VERSION_ID}" = "debian_12" ]; then
 | 
			
		||||
        ENDTAG="debian_12"
 | 
			
		||||
    elif [ "${UBUNTU_CODENAME}" = "focal" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_20.04" ]; then
 | 
			
		||||
        ENDTAG="ubuntu_20.04"
 | 
			
		||||
    elif [ "${UBUNTU_CODENAME}" = "jammy" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_22.04" ]; then
 | 
			
		||||
        ENDTAG="ubuntu_22.04"
 | 
			
		||||
    elif [ "${UBUNTU_CODENAME}" = "kinetic" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_22.10" ]; then
 | 
			
		||||
        ENDTAG="ubuntu_22.10"
 | 
			
		||||
    elif [ "${UBUNTU_CODENAME}" = "lunar" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_23.04" ]; then
 | 
			
		||||
        ENDTAG="ubuntu_23.04"
 | 
			
		||||
    elif [ "${ID}" = "debian" ] && \
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ BuildRequires: opus-devel
 | 
			
		||||
BuildRequires: pulseaudio-libs-devel
 | 
			
		||||
%endif
 | 
			
		||||
%if %{defined suse_version}
 | 
			
		||||
BuildRequires: libdbus-c++-devel
 | 
			
		||||
BuildRequires: systemd-devel
 | 
			
		||||
BuildRequires: libexpat-devel
 | 
			
		||||
BuildRequires: libopus-devel
 | 
			
		||||
BuildRequires: libpulse-devel
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ Vendor:        Savoir-faire Linux Inc.
 | 
			
		||||
URL:           https://jami.net/
 | 
			
		||||
Source:        jami-libqt-%{version}.tar.xz
 | 
			
		||||
Patch0:        0001-fix-gcc13.patch
 | 
			
		||||
Patch1:        0002-OpenFile-portal-do-not-use-O_PATH-fds.patch
 | 
			
		||||
 | 
			
		||||
%global gst 0.10
 | 
			
		||||
%if 0%{?fedora} || 0%{?rhel} > 7
 | 
			
		||||
@ -62,10 +63,11 @@ This package contains Qt libraries for Jami.
 | 
			
		||||
%prep
 | 
			
		||||
%setup -n qt-everywhere-src-%{version}
 | 
			
		||||
%patch0 -p1
 | 
			
		||||
%patch1 -p1
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
echo "Building Qt using %{job_count} parallel jobs"
 | 
			
		||||
# Qt 6.2 (https://wiki.linuxfromscratch.org/blfs/ticket/14729)
 | 
			
		||||
# Qt 6.4 (https://wiki.linuxfromscratch.org/blfs/ticket/14729)
 | 
			
		||||
sed -i 's,default=False,default=True,g' qtwebengine/src/3rdparty/chromium/third_party/catapult/tracing/tracing_build/generate_about_tracing_contents.py
 | 
			
		||||
# Gcc 13
 | 
			
		||||
sed -i 's,std::uint32_t,uint32_t,g' qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
From f4410fcbb093f259eaff4a20fc4266a535479235 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Sebastien Blin <sebastien.blin@savoirfairelinux.com>
 | 
			
		||||
Date: Mon, 29 May 2023 13:09:53 -0400
 | 
			
		||||
Subject: [PATCH] OpenFile portal: do not use O_PATH fds
 | 
			
		||||
 | 
			
		||||
Using O_PATH requires correctly specifying whether the fd is writable or
 | 
			
		||||
not. Stating that the fd is writable without it actually being writable
 | 
			
		||||
results into rejection on xdg-desktop-portal side. Other implementations
 | 
			
		||||
like xdg-open or gtk have also moved away from O_PATH fds so this will
 | 
			
		||||
make a matching implementation and avoid possible rejections from xdp.
 | 
			
		||||
 | 
			
		||||
Fixes: QTBUG-113143
 | 
			
		||||
Original: https://codereview.qt-project.org/c/qt/qtbase/+/475425
 | 
			
		||||
---
 | 
			
		||||
 qtbase/src/gui/platform/unix/qgenericunixservices.cpp | 8 ++------
 | 
			
		||||
 1 file changed, 2 insertions(+), 6 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/qtbase/src/gui/platform/unix/qgenericunixservices.cpp b/qtbase/src/gui/platform/unix/qgenericunixservices.cpp
 | 
			
		||||
index a0e5466c58..fe0fdaa625 100644
 | 
			
		||||
--- a/qtbase/src/gui/platform/unix/qgenericunixservices.cpp
 | 
			
		||||
+++ b/qtbase/src/gui/platform/unix/qgenericunixservices.cpp
 | 
			
		||||
@@ -163,8 +163,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
 | 
			
		||||
     // handle_token (s) -  A string that will be used as the last element of the @handle.
 | 
			
		||||
     // writable (b) - Whether to allow the chosen application to write to the file.
 | 
			
		||||
 
 | 
			
		||||
-#ifdef O_PATH
 | 
			
		||||
-    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH);
 | 
			
		||||
+    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY);
 | 
			
		||||
     if (fd != -1) {
 | 
			
		||||
         QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1,
 | 
			
		||||
                                                               "/org/freedesktop/portal/desktop"_L1,
 | 
			
		||||
@@ -174,16 +173,13 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
 | 
			
		||||
         QDBusUnixFileDescriptor descriptor;
 | 
			
		||||
         descriptor.giveFileDescriptor(fd);
 | 
			
		||||
 
 | 
			
		||||
-        const QVariantMap options = {{"writable"_L1, true}};
 | 
			
		||||
+        const QVariantMap options = {};
 | 
			
		||||
 
 | 
			
		||||
         // FIXME parent_window_id
 | 
			
		||||
         message << QString() << QVariant::fromValue(descriptor) << options;
 | 
			
		||||
 
 | 
			
		||||
         return QDBusConnection::sessionBus().call(message);
 | 
			
		||||
     }
 | 
			
		||||
-#else
 | 
			
		||||
-    Q_UNUSED(url);
 | 
			
		||||
-#endif
 | 
			
		||||
 
 | 
			
		||||
     return QDBusMessage::createError(QDBusError::InternalError, qt_error_string());
 | 
			
		||||
 }
 | 
			
		||||
-- 
 | 
			
		||||
2.40.1
 | 
			
		||||
 | 
			
		||||
@ -308,8 +308,7 @@ parts:
 | 
			
		||||
    - libexpat1-dev
 | 
			
		||||
    - libjsoncpp-dev
 | 
			
		||||
    - libnm-dev # connectivityChanged()
 | 
			
		||||
    - libdbus-1-dev # dbus
 | 
			
		||||
    - libdbus-c++-dev
 | 
			
		||||
    - libsystemd-dev # sdbus-cpp
 | 
			
		||||
    - libpulse-dev # pulse
 | 
			
		||||
    - libudev-dev
 | 
			
		||||
    - libopus-dev # Avoid to build opus from contrib
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ CMAKE_PREFIX_PATH="${QT_JAMI_PREFIX}/lib/cmake:${CMAKE_PREFIX_PATH}"
 | 
			
		||||
QT_MAJOR=6
 | 
			
		||||
QT_MINOR=4
 | 
			
		||||
QT_PATCH=3
 | 
			
		||||
QT_RELEASE_PATCH=1
 | 
			
		||||
QT_RELEASE_PATCH=2
 | 
			
		||||
 | 
			
		||||
QT_MAJOR_MINOR=${QT_MAJOR}.${QT_MINOR}
 | 
			
		||||
QT_MAJOR_MINOR_PATCH=${QT_MAJOR}.${QT_MINOR}.${QT_PATCH}
 | 
			
		||||
 | 
			
		||||
@ -272,7 +272,7 @@ function package()
 | 
			
		||||
{
 | 
			
		||||
    if [[ $DISTRIBUTION =~ debian|ubuntu|raspbian|guix-deb-pack ]]; then
 | 
			
		||||
        package_deb
 | 
			
		||||
    elif [[ $DISTRIBUTION =~ fedora|rhel|opensuse|guix-rpm-pack ]]; then
 | 
			
		||||
    elif [[ $DISTRIBUTION =~ alma|fedora|rhel|opensuse|guix-rpm-pack ]]; then
 | 
			
		||||
        package_rpm
 | 
			
		||||
    elif [[ $DISTRIBUTION =~ snap ]]; then
 | 
			
		||||
        package_snap
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Build, test, and package the project.
 | 
			
		||||
 | 
			
		||||
@ -27,7 +28,7 @@ mutually exclusive required arguments:
 | 
			
		||||
  -z, --zip         Build portable archive
 | 
			
		||||
 | 
			
		||||
examples:
 | 
			
		||||
1.  build.py --qt=C:/Qt/6.2.3/msvc2019_64  # Build the app using a specific Qt
 | 
			
		||||
1.  build.py --qt=C:/Qt/6.4.3/msvc2019_64  # Build the app using a specific Qt
 | 
			
		||||
2.  build.py --init pack --msi             # Build the app and an MSI installer
 | 
			
		||||
3.  build.py --init --tests                # Build the app and run tests
 | 
			
		||||
    build.py pack --zip --skip-build       # Generate a 7z archive of the app
 | 
			
		||||
@ -293,11 +294,11 @@ def build(config_str, qt_dir, tests):
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def deploy_runtimes(qt_dir):
 | 
			
		||||
def deploy_runtimes(config_str, qt_dir):
 | 
			
		||||
    """Deploy the dependencies to the runtime directory."""
 | 
			
		||||
    print("Deploying runtime dependencies")
 | 
			
		||||
 | 
			
		||||
    runtime_dir = os.path.join(repo_root_dir, "x64", "Release")
 | 
			
		||||
    runtime_dir = os.path.join(repo_root_dir, "x64", config_str)
 | 
			
		||||
    stamp_file = os.path.join(runtime_dir, ".deploy.stamp")
 | 
			
		||||
    if os.path.exists(stamp_file):
 | 
			
		||||
        return
 | 
			
		||||
@ -340,8 +341,12 @@ def deploy_runtimes(qt_dir):
 | 
			
		||||
    print("Running windeployqt (this may take a while)...")
 | 
			
		||||
    win_deploy_qt = os.path.join(qt_dir, "bin", "windeployqt.exe")
 | 
			
		||||
    qml_src_dir = os.path.join(repo_root_dir, "src", "app")
 | 
			
		||||
    os.environ["VCINSTALLDIR"] = os.path.join(
 | 
			
		||||
        get_vs_prop("installationPath"), "VC")
 | 
			
		||||
    installation_dir = get_vs_prop("installationPath")
 | 
			
		||||
    if not installation_dir:
 | 
			
		||||
        print("Visual Studio not found. Please install Visual Studio 2017 or "
 | 
			
		||||
              "later.")
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
    os.environ["VCINSTALLDIR"] = os.path.join(installation_dir, "VC")
 | 
			
		||||
    executable = os.path.join(runtime_dir, "Jami.exe")
 | 
			
		||||
    execute_cmd([win_deploy_qt, "--verbose", "1", "--no-compiler-runtime",
 | 
			
		||||
                 "--qmldir", qml_src_dir, "--release", executable],
 | 
			
		||||
@ -371,11 +376,19 @@ def run_tests(config_str, qt_dir):
 | 
			
		||||
        qt_dir, 'bin', 'QtWebEngineProcess.exe')
 | 
			
		||||
    os.environ["QML2_IMPORT_PATH"] = os.path.join(qt_dir, "qml")
 | 
			
		||||
 | 
			
		||||
    cmd = ["ctest", "-V", "-C", config_str]
 | 
			
		||||
    # On Windows, when running on a jenkins slave, the QML tests don't output
 | 
			
		||||
    # anything to stdout/stderr. Workaround by outputting to a file and then
 | 
			
		||||
    # printing the contents of the file.
 | 
			
		||||
    if os.environ.get("JENKINS_URL"):
 | 
			
		||||
        cmd += ["--output-log", "test.log", "--quiet"]
 | 
			
		||||
    tests_dir = os.path.join(build_dir, "tests")
 | 
			
		||||
    if execute_cmd(["ctest", "-V", "-C", config_str],
 | 
			
		||||
                   False, None, tests_dir):
 | 
			
		||||
        print("Tests failed.")
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
    exit_code = execute_cmd(cmd, False, None, tests_dir)
 | 
			
		||||
    # Print the contents of the log file.
 | 
			
		||||
    if os.environ.get("JENKINS_URL"):
 | 
			
		||||
        with open(os.path.join(tests_dir, "test.log"), "r") as file:
 | 
			
		||||
            print(file.read())
 | 
			
		||||
    sys.exit(exit_code)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_msi(version):
 | 
			
		||||
@ -483,7 +496,11 @@ def main():
 | 
			
		||||
        print("These scripts will only run on a 64-bit system for now.")
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
    if sys.platform == "win32":
 | 
			
		||||
        vs_version = get_vs_prop("installationVersion").split(".")[0]
 | 
			
		||||
        vs_version = get_vs_prop("installationVersion")
 | 
			
		||||
        if vs_version is None:
 | 
			
		||||
            print("Visual Studio version not found.")
 | 
			
		||||
            sys.exit(1)
 | 
			
		||||
        vs_version = vs_version.split(".")[0]
 | 
			
		||||
        if vs_version is None or int(vs_version) < 15:
 | 
			
		||||
            print("Visual Studio 2017 or later is required.")
 | 
			
		||||
            sys.exit(1)
 | 
			
		||||
@ -516,7 +533,7 @@ def main():
 | 
			
		||||
        if not parsed_args.skip_build:
 | 
			
		||||
            build(config_str, parsed_args.qt, do_tests)
 | 
			
		||||
        if not parsed_args.skip_deploy:
 | 
			
		||||
            deploy_runtimes(parsed_args.qt)
 | 
			
		||||
            deploy_runtimes(config_str, parsed_args.qt)
 | 
			
		||||
 | 
			
		||||
    if parsed_args.subcommand == "pack":
 | 
			
		||||
        do_build(False)
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@ for ARCH in "${ARCHS[@]}"; do
 | 
			
		||||
    # force to build every contrib
 | 
			
		||||
    for dir in "$DAEMON"/contrib/src/*/; do
 | 
			
		||||
      PKG=$(basename -- "$dir")
 | 
			
		||||
      if [ "$PKG" != "dbus-cpp" ] && [ "$PKG" != "natpmp" ] &&
 | 
			
		||||
      if [ "$PKG" != "sdbus-cpp" ] && [ "$PKG" != "natpmp" ] &&
 | 
			
		||||
        [ "$PKG" != "portaudio" ] && [ "$PKG" != "pthreads" ] &&
 | 
			
		||||
        [ "$PKG" != "lttng-ust" ] && [ "$PKG" != "openssl" ] &&
 | 
			
		||||
        [ "$PKG" != "media-sdk" ] && [ "$PKG" != "jack" ] &&
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ import shutil
 | 
			
		||||
from platform import uname
 | 
			
		||||
 | 
			
		||||
CFVERSION = "9"
 | 
			
		||||
CLANGFORMAT = ""
 | 
			
		||||
CLANGFORMAT = None
 | 
			
		||||
 | 
			
		||||
QMLFORMAT = None
 | 
			
		||||
 | 
			
		||||
@ -138,27 +138,29 @@ def main():
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    if args.type in ["cpp", "both"]:
 | 
			
		||||
        if not command_exists("clang-format-" + CFVERSION):
 | 
			
		||||
            if not command_exists("clang-format"):
 | 
			
		||||
                print("Required version of clang-format not found")
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
            else:
 | 
			
		||||
                CLANGFORMAT = "clang-format"
 | 
			
		||||
        else:
 | 
			
		||||
            CLANGFORMAT = "clang-format-" + CFVERSION
 | 
			
		||||
        if command_exists("clang-format-" + CFVERSION):
 | 
			
		||||
            CLANGFORMAT = "clang-format-" + CFVERSION   
 | 
			
		||||
        elif command_exists("clang-format"):
 | 
			
		||||
            CLANGFORMAT = "clang-format"     
 | 
			
		||||
 | 
			
		||||
    if CLANGFORMAT is not None:
 | 
			
		||||
        print("Using source formatter: " + CLANGFORMAT)
 | 
			
		||||
    else:
 | 
			
		||||
        print("clang-format not found. can't format source files")
 | 
			
		||||
 | 
			
		||||
    if args.qt is not None and args.type in ["qml", "both"]:
 | 
			
		||||
        global QMLFORMAT  # pylint: disable=global-statement
 | 
			
		||||
        QMLFORMAT = find_qmlformat(args.qt)
 | 
			
		||||
 | 
			
		||||
    if QMLFORMAT is not None:
 | 
			
		||||
        print("Using qmlformatter: " + QMLFORMAT)
 | 
			
		||||
    else:
 | 
			
		||||
        print("No qmlformat found, can't format QML files")
 | 
			
		||||
        if QMLFORMAT is not None:
 | 
			
		||||
            print("Using qmlformatter: " + QMLFORMAT)
 | 
			
		||||
        else:
 | 
			
		||||
            print("qmlformat not found, can't format QML files")
 | 
			
		||||
 | 
			
		||||
    if args.install:
 | 
			
		||||
        install_hook(args.install, args.qt)
 | 
			
		||||
        if CLANGFORMAT is not None or QMLFORMAT is not None:
 | 
			
		||||
            install_hook(args.install, args.qt)
 | 
			
		||||
        else:
 | 
			
		||||
            print("No formatters found, skipping hook install")
 | 
			
		||||
        sys.exit(0)
 | 
			
		||||
 | 
			
		||||
    src_files = get_files([".cpp", ".cxx", ".cc", ".h", ".hpp"],
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ export OSTYPE
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
# Qt_MIN_VER required for client-qt
 | 
			
		||||
QT_MIN_VER="6.2"
 | 
			
		||||
QT_MIN_VER="6.4"
 | 
			
		||||
 | 
			
		||||
debug=
 | 
			
		||||
global=false
 | 
			
		||||
@ -125,6 +125,8 @@ else
 | 
			
		||||
      if [[ "$OSTYPE" != "darwin"* ]]; then
 | 
			
		||||
        CONFIGURE_FLAGS+=" --disable-shared"
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      CONFIGURE_FLAGS+="--without-dbus"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    BUILD_TYPE="Release"
 | 
			
		||||
 | 
			
		||||
@ -10,4 +10,4 @@ if (-not(Test-Path -Path $QtDir)) {
 | 
			
		||||
$lupdate = "$QtDir\bin\lupdate.exe"
 | 
			
		||||
 | 
			
		||||
$tsFileNames = Get-ChildItem -Path "$clientDir\translations" -Recurse -Include *.ts
 | 
			
		||||
Invoke-Expression("$lupdate $clientDir\src -ts $tsFileNames -no-obsolete")
 | 
			
		||||
Invoke-Expression("$lupdate -extensions cpp,h,qml $clientDir\src -ts $tsFileNames -no-obsolete")
 | 
			
		||||
 | 
			
		||||
@ -34,8 +34,6 @@
 | 
			
		||||
	<string>Jami requires to access your camera to make calls and record video</string>
 | 
			
		||||
	<key>NSMicrophoneUsageDescription</key>
 | 
			
		||||
	<string>Jami requires to access your microphone to make calls and record audio</string>
 | 
			
		||||
	<key>NSPhotoLibraryUsageDescription</key>
 | 
			
		||||
	<string>Jami requires to access your photo library to show image on profile and send via chat</string>
 | 
			
		||||
	<key>ITSAppUsesNonExemptEncryption</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								resources/icons/Icon_Donate.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="43" height="52.655" viewBox="0 0 43 52.655">
 | 
			
		||||
  <defs>
 | 
			
		||||
    <clipPath id="clip-path">
 | 
			
		||||
      <rect id="Rectangle_268" data-name="Rectangle 268" width="38" height="24" transform="translate(-0.407 0.083)" fill="#fff" stroke="#707070" stroke-width="1"/>
 | 
			
		||||
    </clipPath>
 | 
			
		||||
  </defs>
 | 
			
		||||
  <g id="Icon_Donate" transform="translate(-22 -189.345)">
 | 
			
		||||
    <rect id="Rectangle_267" data-name="Rectangle 267" width="43" height="10" rx="5" transform="translate(22 232)" fill="#9eb3c3"/>
 | 
			
		||||
    <path id="Path_459" data-name="Path 459" d="M9.674,17.083,8.562,16.07C4.609,12.486,2,10.122,2,7.221A4.18,4.18,0,0,1,6.221,3,4.6,4.6,0,0,1,9.674,4.6,4.6,4.6,0,0,1,13.128,3a4.18,4.18,0,0,1,4.221,4.221c0,2.9-2.609,5.265-6.562,8.856Z" transform="translate(22.407 199.828)" fill="#ff0045" opacity="0.3"/>
 | 
			
		||||
    <path id="Path_460" data-name="Path 460" d="M6.953,12.088l-.718-.654C3.684,9.122,2,7.6,2,5.724A2.7,2.7,0,0,1,4.724,3,2.966,2.966,0,0,1,6.953,4.035,2.966,2.966,0,0,1,9.182,3a2.7,2.7,0,0,1,2.724,2.724c0,1.872-1.684,3.4-4.235,5.716Z" transform="translate(45.571 186.345)" fill="#ff0045" opacity="0.16"/>
 | 
			
		||||
    <g id="Mask_Group_38" data-name="Mask Group 38" transform="translate(24.407 213.918)" clip-path="url(#clip-path)">
 | 
			
		||||
      <path id="Path_270" data-name="Path 270" d="M12.649,22.542l-1.544-1.406C5.621,16.163,2,12.883,2,8.857A5.8,5.8,0,0,1,7.857,3a6.377,6.377,0,0,1,4.792,2.226A6.377,6.377,0,0,1,17.442,3,5.8,5.8,0,0,1,23.3,8.857c0,4.025-3.621,7.306-9.105,12.289Z" transform="translate(5.992 5.54)" fill="#ff0045"/>
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
							
								
								
									
										14
									
								
								resources/icons/Preview_Black_24dp.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,14 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
 | 
			
		||||
<g id="noun-overview-4287788" transform="translate(-112.49 -112.51)">
 | 
			
		||||
	<path id="Path_272" d="M134.5,133.4l-3.5-3.5c3.3-3.9,2.8-9.8-1.1-13.2c-3.9-3.3-9.8-2.8-13.2,1.1c-3.3,3.9-2.8,9.8,1.1,13.2
 | 
			
		||||
		c3.5,2.9,8.6,2.9,12.1,0l3.5,3.5L134.5,133.4z M123.8,131.6c-4.3,0-7.8-3.5-7.8-7.8s3.5-7.8,7.8-7.8c4.3,0,7.8,3.5,7.8,7.8l0,0
 | 
			
		||||
		C131.6,128.2,128.1,131.6,123.8,131.6L123.8,131.6z"/>
 | 
			
		||||
	<path id="Path_273" d="M123.8,119.7c-3.5,0-6.2,1.8-6.2,4.1s2.7,4.1,6.2,4.1c3.5,0,6.2-1.8,6.2-4.1S127.3,119.7,123.8,119.7z
 | 
			
		||||
		 M123.8,126.6c-2.6,0-4.8-1.3-4.8-2.8s2.2-2.8,4.8-2.8c2.6,0,4.8,1.3,4.8,2.8S126.5,126.6,123.8,126.6z"/>
 | 
			
		||||
	<path id="Path_274" d="M126.1,123.9c0,1.3-1,2.3-2.3,2.3c-1.3,0-2.3-1-2.3-2.3c0-1.3,1-2.3,2.3-2.3
 | 
			
		||||
		C125.1,121.6,126.1,122.6,126.1,123.9L126.1,123.9"/>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										7
									
								
								resources/icons/Shift-Enter_Black_24dp.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
 | 
			
		||||
<path d="M5.8,4.7h12.5c0.7,0,1.2,0.6,1.2,1.2v7.5c0,0.7-0.6,1.2-1.2,1.2h-12l2.9-2.9l-1.8-1.8l-5,5c-0.1,0.1-0.2,0.3-0.3,0.4
 | 
			
		||||
	c-0.2,0.5-0.1,1,0.3,1.4l5,5L9.1,20l-2.9-2.9h12c2.1,0,3.7-1.7,3.7-3.7V5.9c0-2.1-1.7-3.7-3.7-3.7H5.8V4.7z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 582 B  | 
							
								
								
									
										9
									
								
								resources/icons/Trash_Black_24dp.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
 | 
			
		||||
<path d="M20.9,4.6H15V4.2C15,3,14,2,12.8,2h-1.6C10,2,9,3,9,4.2v0.3H3.1c-0.4,0-0.7,0.3-0.7,0.7s0.3,0.7,0.7,0.7h1.4l0.7,13.8
 | 
			
		||||
	C5.2,21,6.3,22,7.6,22h8.9c1.3,0,2.4-1,2.4-2.3l0.7-13.8h1.4c0.4,0,0.7-0.3,0.7-0.7S21.3,4.6,20.9,4.6z M10.3,4.6V4.2
 | 
			
		||||
	c0-0.5,0.4-0.8,0.8-0.8h1.6c0.5,0,0.8,0.4,0.8,0.8v0.3H10.3z M18.1,5.9l-0.7,13.7c0,0.6-0.5,1-1,1H7.6c-0.6,0-1-0.4-1-1L5.8,5.9
 | 
			
		||||
	H18.1z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 726 B  | 
							
								
								
									
										15
									
								
								resources/icons/backup.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
<svg xmlns="http://www.w3.org/2000/svg" width="26.503" height="26.5" viewBox="0 0 26.503 26.5">
 | 
			
		||||
  <g id="noun-backup-2912773" transform="translate(-51.27 -51.32)">
 | 
			
		||||
    <path id="Path_259" data-name="Path 259" d="M76.04,60.533H53a1.483,1.483,0,0,1-1.482-1.482v-6A1.483,1.483,0,0,1,53,51.57H76.04a1.483,1.483,0,0,1,1.482,1.482v6A1.483,1.483,0,0,1,76.04,60.533ZM53,52.014a1.038,1.038,0,0,0-1.037,1.037v6A1.038,1.038,0,0,0,53,60.089H76.04a1.038,1.038,0,0,0,1.037-1.037v-6a1.038,1.038,0,0,0-1.037-1.037Z" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_260" data-name="Path 260" d="M72.468,779.243H53.042a1.483,1.483,0,0,1-1.482-1.482v-6a1.483,1.483,0,0,1,1.482-1.482h16.2a.222.222,0,1,1,0,.444h-16.2A1.038,1.038,0,0,0,52,771.762v6a1.038,1.038,0,0,0,1.037,1.037H72.468a.222.222,0,1,1,0,.444Z" transform="translate(-0.039 -701.673)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_261" data-name="Path 261" d="M723.843,728.9a5.093,5.093,0,1,1,5.093-5.093,5.1,5.1,0,0,1-5.093,5.093Zm0-9.741a4.649,4.649,0,1,0,3.079,1.166A4.642,4.642,0,0,0,723.843,719.156Z" transform="translate(-651.414 -651.328)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_262" data-name="Path 262" d="M77.382,423.416a.222.222,0,0,1-.222-.222V412.138a.76.76,0,0,0-.759-.759H52.807a.76.76,0,0,0-.759.759v6.556a.76.76,0,0,0,.759.759H69.285a.222.222,0,1,1,0,.444H52.807a1.205,1.205,0,0,1-1.2-1.2v-6.556a1.205,1.205,0,0,1,1.2-1.2H76.4a1.205,1.205,0,0,1,1.2,1.2v11.056A.222.222,0,0,1,77.382,423.416Z" transform="translate(-0.081 -350.845)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_263" data-name="Path 263" d="M168.967,149.643a.222.222,0,0,1-.222-.222v-3.889a.222.222,0,1,1,.444,0v3.889A.222.222,0,0,1,168.967,149.643Z" transform="translate(-114.446 -91.518)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_264" data-name="Path 264" d="M168.967,524.643a.222.222,0,0,1-.222-.222v-3.889a.222.222,0,1,1,.444,0v3.889A.222.222,0,0,1,168.967,524.643Z" transform="translate(-114.446 -457.629)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_265" data-name="Path 265" d="M168.967,899.623a.222.222,0,0,1-.222-.222v-3.889a.222.222,0,1,1,.444,0V899.4A.222.222,0,0,1,168.967,899.623Z" transform="translate(-114.446 -823.72)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_266" data-name="Path 266" d="M498.2,171.417a1.333,1.333,0,1,1,1.333-1.333A1.335,1.335,0,0,1,498.2,171.417Zm0-2.222a.889.889,0,1,0,.889.889A.89.89,0,0,0,498.2,169.194Z" transform="translate(-434.793 -114.402)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_267" data-name="Path 267" d="M709.143,171.417a1.333,1.333,0,1,1,1.333-1.333A1.335,1.335,0,0,1,709.143,171.417Zm0-2.222a.889.889,0,1,0,.889.889A.89.89,0,0,0,709.143,169.194Z" transform="translate(-640.733 -114.402)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_268" data-name="Path 268" d="M920.083,171.417a1.333,1.333,0,1,1,1.333-1.333A1.335,1.335,0,0,1,920.083,171.417Zm0-2.222a.889.889,0,1,0,.889.889A.89.89,0,0,0,920.083,169.194Z" transform="translate(-846.673 -114.402)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
    <path id="Path_269" data-name="Path 269" d="M826.331,853.344h-.015a.222.222,0,0,1-.17-.1l-1.111-1.667a.222.222,0,1,1,.37-.247l.95,1.425,3.7-4.226a.222.222,0,0,1,.335.293l-3.889,4.445a.223.223,0,0,1-.167.076Z" transform="translate(-755.142 -777.995)" fill="#005699" stroke="#005699" stroke-width="0.5"/>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										358
									
								
								resources/icons/jami-logo-icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,358 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 116.3 116.3" style="enable-background:new 0 0 116.3 116.3;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill:url(#SVGID_1_);}
 | 
			
		||||
	.st1{fill:url(#SVGID_2_);}
 | 
			
		||||
	.st2{fill:url(#SVGID_3_);}
 | 
			
		||||
	.st3{fill:url(#SVGID_4_);}
 | 
			
		||||
	.st4{fill:url(#SVGID_5_);}
 | 
			
		||||
	.st5{fill:url(#SVGID_6_);}
 | 
			
		||||
	.st6{opacity:0.4;fill:url(#SVGID_7_);enable-background:new    ;}
 | 
			
		||||
	.st7{fill:url(#SVGID_8_);}
 | 
			
		||||
	.st8{fill:url(#SVGID_9_);}
 | 
			
		||||
	.st9{fill:url(#SVGID_10_);}
 | 
			
		||||
	.st10{fill:url(#SVGID_11_);}
 | 
			
		||||
	.st11{fill:url(#SVGID_12_);}
 | 
			
		||||
	.st12{fill:url(#SVGID_13_);}
 | 
			
		||||
	.st13{fill:url(#SVGID_14_);}
 | 
			
		||||
	.st14{fill:url(#SVGID_15_);}
 | 
			
		||||
	.st15{fill:url(#SVGID_16_);}
 | 
			
		||||
	.st16{opacity:0.2;fill:url(#SVGID_17_);enable-background:new    ;}
 | 
			
		||||
	.st17{fill:url(#SVGID_18_);}
 | 
			
		||||
	.st18{fill:url(#SVGID_19_);}
 | 
			
		||||
	.st19{opacity:0.25;fill:url(#SVGID_20_);enable-background:new    ;}
 | 
			
		||||
	.st20{fill:url(#SVGID_21_);}
 | 
			
		||||
	.st21{fill:url(#SVGID_22_);}
 | 
			
		||||
	.st22{opacity:0.2;fill:url(#SVGID_23_);enable-background:new    ;}
 | 
			
		||||
	.st23{fill:url(#SVGID_24_);}
 | 
			
		||||
</style>
 | 
			
		||||
<g>
 | 
			
		||||
	<g>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="69.0328" y1="454.9245" x2="64.4435" y2="449.5703" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<polygon class="st0" points="54.9,55.8 62,55.6 58.4,62.2 		"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="108.534" y1="457.2333" x2="108.534" y2="494.0327" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="7.040000e-02" style="stop-color:#2D4576"/>
 | 
			
		||||
			<stop  offset="0.2165" style="stop-color:#2E5589"/>
 | 
			
		||||
			<stop  offset="0.3608" style="stop-color:#2B5E97"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#29629C"/>
 | 
			
		||||
			<stop  offset="0.6392" style="stop-color:#2B5E97"/>
 | 
			
		||||
			<stop  offset="0.7835" style="stop-color:#2E5589"/>
 | 
			
		||||
			<stop  offset="0.9296" style="stop-color:#2D4576"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st1" d="M85.5,57.5c26.1,4.2,29.8,10.7,29.8,10.7c0,0.4,0,0.8,0,1.2c0,0.2-0.1,0.4-0.2,0.7c-0.1,0.3-2.7,5.7-19.6,9.3
 | 
			
		||||
			L85.5,57.5z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="26.7686" y1="496.8278" x2="38.7091" y2="475.5678" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#252B59"/>
 | 
			
		||||
			<stop  offset="8.590000e-02" style="stop-color:#2B3A69"/>
 | 
			
		||||
			<stop  offset="0.2267" style="stop-color:#2E4A7C"/>
 | 
			
		||||
			<stop  offset="0.3658" style="stop-color:#2E5487"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#2D578C"/>
 | 
			
		||||
			<stop  offset="0.6342" style="stop-color:#2E5487"/>
 | 
			
		||||
			<stop  offset="0.7733" style="stop-color:#2E4A7C"/>
 | 
			
		||||
			<stop  offset="0.9141" style="stop-color:#2B3A69"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#252B59"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st2" d="M19.8,78.5c-5.9,15.7-2.9,20.6-2.7,21c0.1,0.2,0.2,0.4,0.4,0.5c0.4,0,0.8-0.1,1.2-0.2c0,0,10.7-0.5,23.2-18.3
 | 
			
		||||
			L19.8,78.5z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="60.8512" y1="424.6486" x2="41.0872" y2="401.5905" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#181844"/>
 | 
			
		||||
			<stop  offset="2.840000e-02" style="stop-color:#1E1E4C"/>
 | 
			
		||||
			<stop  offset="0.1353" style="stop-color:#283261"/>
 | 
			
		||||
			<stop  offset="0.2468" style="stop-color:#2D4172"/>
 | 
			
		||||
			<stop  offset="0.3647" style="stop-color:#2E4A7C"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#2E4D7F"/>
 | 
			
		||||
			<stop  offset="0.6177" style="stop-color:#2D4576"/>
 | 
			
		||||
			<stop  offset="0.8125" style="stop-color:#273160"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#181844"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st3" d="M58.8,18.6C44.5,2.9,37.5,3.8,37.1,3.9c-0.2,0-0.5,0.1-0.7,0.2c-0.5,0.2-1,1.8-1,1.8s-3,8.7,9.1,31.9
 | 
			
		||||
			L58.8,18.6z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="99.9721" y1="499.3526" x2="84.7036" y2="450.1664" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#181844"/>
 | 
			
		||||
			<stop  offset="2.840000e-02" style="stop-color:#1E1E4C"/>
 | 
			
		||||
			<stop  offset="0.1353" style="stop-color:#283261"/>
 | 
			
		||||
			<stop  offset="0.2468" style="stop-color:#2D4172"/>
 | 
			
		||||
			<stop  offset="0.3647" style="stop-color:#2E4A7C"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#2E4D7F"/>
 | 
			
		||||
			<stop  offset="0.6177" style="stop-color:#2D4576"/>
 | 
			
		||||
			<stop  offset="0.8125" style="stop-color:#273160"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#181844"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st4" d="M71.2,78.8C90.6,101,97.4,99.8,97.4,99.8c0.4,0.1,0.9,0.2,1.3,0.2c0.2-0.1,0.3-0.3,0.4-0.5
 | 
			
		||||
			c0.3-0.4,5.8-9.4-12.8-41.8L71.2,78.8z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="31.6447" y1="457.2331" x2="31.6447" y2="494.0317" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="7.040000e-02" style="stop-color:#2D4576"/>
 | 
			
		||||
			<stop  offset="0.2165" style="stop-color:#2E5589"/>
 | 
			
		||||
			<stop  offset="0.3608" style="stop-color:#2B5E97"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#29629C"/>
 | 
			
		||||
			<stop  offset="0.6392" style="stop-color:#2B5E97"/>
 | 
			
		||||
			<stop  offset="0.7835" style="stop-color:#2E5589"/>
 | 
			
		||||
			<stop  offset="0.9296" style="stop-color:#2D4576"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st5" d="M46.1,82.2c-40-1.7-44.8-11.6-45-12.1C1.1,69.9,1,69.7,1,69.5c0.1-0.7,1.5-2,1.5-2s10.6-6.5,28.3-10
 | 
			
		||||
			L46.1,82.2z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="51.8906" y1="421.1236" x2="66.337" y2="421.1236" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st6" d="M57.6,17.4c-5,5.4-9.6,11.2-13.8,17.2h1c3.1-3.9,10.8-13.7,13.4-16.5L57.6,17.4z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="70.9709" y1="436.4003" x2="70.9709" y2="401.6908" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#252B59"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#2D578C"/>
 | 
			
		||||
			<stop  offset="0.6934" style="stop-color:#2D5588"/>
 | 
			
		||||
			<stop  offset="0.8038" style="stop-color:#2E4F80"/>
 | 
			
		||||
			<stop  offset="0.8932" style="stop-color:#2D4474"/>
 | 
			
		||||
			<stop  offset="0.971" style="stop-color:#283463"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#252B59"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st7" d="M73.8,35.4C84.2,10.7,80.2,5,80.2,5c-0.1-0.3-0.2-0.7-0.3-1c-0.2-0.1-0.4-0.1-0.7-0.2
 | 
			
		||||
			c-0.5-0.1-11.4-1.5-34.4,30.7L73.8,35.4z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="39.2669" y1="503.7654" x2="102.6764" y2="407.9172" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="8.239999e-02" style="stop-color:#177EBC"/>
 | 
			
		||||
			<stop  offset="0.1675" style="stop-color:#0E91D1"/>
 | 
			
		||||
			<stop  offset="0.2527" style="stop-color:#10A2E1"/>
 | 
			
		||||
			<stop  offset="0.337" style="stop-color:#18ACEA"/>
 | 
			
		||||
			<stop  offset="0.42" style="stop-color:#24B1ED"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#28B1ED"/>
 | 
			
		||||
			<stop  offset="0.58" style="stop-color:#24B1ED"/>
 | 
			
		||||
			<stop  offset="0.663" style="stop-color:#18ACEA"/>
 | 
			
		||||
			<stop  offset="0.7473" style="stop-color:#10A2E1"/>
 | 
			
		||||
			<stop  offset="0.8325" style="stop-color:#0E91D1"/>
 | 
			
		||||
			<stop  offset="0.9176" style="stop-color:#177EBC"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st8" d="M58,97.4c-14.2,15.4-21,14.5-21.4,14.5c-0.6-0.2-1.2-0.5-1.7-0.8l-17.3-11c7.3-1.6,16.3-10.2,25.7-21.3
 | 
			
		||||
			L58,97.4z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="33.568" y1="505.6873" x2="58.683" y2="483.2007" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2C5D95"/>
 | 
			
		||||
			<stop  offset="8.220001e-02" style="stop-color:#246EAB"/>
 | 
			
		||||
			<stop  offset="0.1808" style="stop-color:#1B79B9"/>
 | 
			
		||||
			<stop  offset="0.3007" style="stop-color:#1080C2"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#0E81C5"/>
 | 
			
		||||
			<stop  offset="0.6993" style="stop-color:#1080C2"/>
 | 
			
		||||
			<stop  offset="0.8192" style="stop-color:#1B79B9"/>
 | 
			
		||||
			<stop  offset="0.9178" style="stop-color:#246EAB"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2C5D95"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st9" d="M58.6,98c-14.2,15.4-21,14.5-21.4,14.5c-0.6-0.2-1.2-0.5-1.7-0.8L17.6,100c7.3-1.6,16.9-9.6,26.3-20.7
 | 
			
		||||
			L58.6,98z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="31.3212" y1="407.2591" x2="89.6399" y2="504.4568" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2C5D95"/>
 | 
			
		||||
			<stop  offset="1.760000e-02" style="stop-color:#2A6199"/>
 | 
			
		||||
			<stop  offset="0.1407" style="stop-color:#1C79B7"/>
 | 
			
		||||
			<stop  offset="0.2631" style="stop-color:#0A8CCA"/>
 | 
			
		||||
			<stop  offset="0.3837" style="stop-color:#0B97D4"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#0E9AD8"/>
 | 
			
		||||
			<stop  offset="0.6163" style="stop-color:#0B97D4"/>
 | 
			
		||||
			<stop  offset="0.7369" style="stop-color:#0A8CCA"/>
 | 
			
		||||
			<stop  offset="0.8593" style="stop-color:#1C79B7"/>
 | 
			
		||||
			<stop  offset="0.9824" style="stop-color:#2A6199"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2C5D95"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st10" d="M20.7,38c-6.3-16.2-3.2-21.3-3-21.6c0.4-0.5,0.9-0.9,1.4-1.2l17.3-11c-1.5,7.1,2.3,18,8.2,30.7L20.7,38z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="94.4028" y1="503.8655" x2="56.9038" y2="456.6339" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2C5D95"/>
 | 
			
		||||
			<stop  offset="2.570000e-02" style="stop-color:#2A6199"/>
 | 
			
		||||
			<stop  offset="0.2053" style="stop-color:#1C79B7"/>
 | 
			
		||||
			<stop  offset="0.384" style="stop-color:#0A8CCA"/>
 | 
			
		||||
			<stop  offset="0.5599" style="stop-color:#0B97D4"/>
 | 
			
		||||
			<stop  offset="0.7296" style="stop-color:#0E9AD8"/>
 | 
			
		||||
			<stop  offset="0.7925" style="stop-color:#0B97D4"/>
 | 
			
		||||
			<stop  offset="0.8577" style="stop-color:#0A8CCA"/>
 | 
			
		||||
			<stop  offset="0.9239" style="stop-color:#1C79B7"/>
 | 
			
		||||
			<stop  offset="0.9905" style="stop-color:#2A6199"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2C5D95"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st11" d="M57.2,57.2c-8.7,0-17.4,0.7-26,1.9c0.3,0.5,4.3,7.2,7.2,11.7c27.8,42.8,40.8,41.1,41.4,41
 | 
			
		||||
			c0.6-0.2,1.2-0.5,1.7-0.8l17.3-11C87,97.5,70.6,78.2,57.2,57.2z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="40.2175" y1="431.0724" x2="32.9164" y2="404.7886" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2C5D95"/>
 | 
			
		||||
			<stop  offset="8.220001e-02" style="stop-color:#246EAB"/>
 | 
			
		||||
			<stop  offset="0.1808" style="stop-color:#1B79B9"/>
 | 
			
		||||
			<stop  offset="0.3007" style="stop-color:#1080C2"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#0E81C5"/>
 | 
			
		||||
			<stop  offset="0.6993" style="stop-color:#1080C2"/>
 | 
			
		||||
			<stop  offset="0.8192" style="stop-color:#1B79B9"/>
 | 
			
		||||
			<stop  offset="0.9178" style="stop-color:#246EAB"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2C5D95"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st12" d="M20.1,38.5c-6.3-16.2-3.2-21.3-3-21.6c0.4-0.5,0.9-0.9,1.4-1.2L36.5,4.1c-1.5,7.1,1.7,18.6,7.6,31.2
 | 
			
		||||
			L20.1,38.5z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="122.9025" y1="448.7971" x2="9.1479" y2="448.7971" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="1.280000e-02" style="stop-color:#256BA7"/>
 | 
			
		||||
			<stop  offset="0.1121" style="stop-color:#0B83C3"/>
 | 
			
		||||
			<stop  offset="0.2115" style="stop-color:#1797D8"/>
 | 
			
		||||
			<stop  offset="0.3099" style="stop-color:#25A3E2"/>
 | 
			
		||||
			<stop  offset="0.4066" style="stop-color:#27ABE7"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#2AAEEA"/>
 | 
			
		||||
			<stop  offset="0.5934" style="stop-color:#27ABE7"/>
 | 
			
		||||
			<stop  offset="0.6901" style="stop-color:#25A3E2"/>
 | 
			
		||||
			<stop  offset="0.7885" style="stop-color:#1797D8"/>
 | 
			
		||||
			<stop  offset="0.8879" style="stop-color:#0B83C3"/>
 | 
			
		||||
			<stop  offset="0.9872" style="stop-color:#256BA7"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st13" d="M85.5,59.6c13.5,1.9,24.7,4.7,29.8,9.8V48.9c0-0.6-0.1-1.3-0.2-1.9c-0.1-0.3-2.7-5.6-19-9.1L85.5,59.6z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="90.2941" y1="447.1362" x2="122.8074" y2="448.8884" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2E5284"/>
 | 
			
		||||
			<stop  offset="6.100000e-03" style="stop-color:#2E5486"/>
 | 
			
		||||
			<stop  offset="9.120000e-02" style="stop-color:#29649D"/>
 | 
			
		||||
			<stop  offset="0.1876" style="stop-color:#236FAD"/>
 | 
			
		||||
			<stop  offset="0.3049" style="stop-color:#1D77B6"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#1C78B9"/>
 | 
			
		||||
			<stop  offset="0.6951" style="stop-color:#1D77B6"/>
 | 
			
		||||
			<stop  offset="0.8124" style="stop-color:#236FAD"/>
 | 
			
		||||
			<stop  offset="0.9088" style="stop-color:#29649D"/>
 | 
			
		||||
			<stop  offset="0.9939" style="stop-color:#2E5486"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2E5284"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st14" d="M85.5,58.8c13.5,1.9,24.7,5.5,29.8,10.6V48.1c0-0.6-0.1-1.3-0.2-1.9c-0.1-0.3-2.7-5.6-19-9.1L85.5,58.8z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="9.0577" y1="447.1912" x2="67.7186" y2="447.1912" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="2.250000e-02" style="stop-color:#256BA7"/>
 | 
			
		||||
			<stop  offset="0.1971" style="stop-color:#0B83C3"/>
 | 
			
		||||
			<stop  offset="0.3718" style="stop-color:#1797D8"/>
 | 
			
		||||
			<stop  offset="0.5447" style="stop-color:#25A3E2"/>
 | 
			
		||||
			<stop  offset="0.7147" style="stop-color:#27ABE7"/>
 | 
			
		||||
			<stop  offset="0.8788" style="stop-color:#2AAEEA"/>
 | 
			
		||||
			<stop  offset="0.9015" style="stop-color:#27ABE7"/>
 | 
			
		||||
			<stop  offset="0.9249" style="stop-color:#25A3E2"/>
 | 
			
		||||
			<stop  offset="0.9487" style="stop-color:#1797D8"/>
 | 
			
		||||
			<stop  offset="0.9728" style="stop-color:#0B83C3"/>
 | 
			
		||||
			<stop  offset="0.9969" style="stop-color:#256BA7"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st15" d="M73.2,35.1c-3-0.1-9.5-0.4-15.1-0.4C7.1,34.7,1.4,46.5,1.2,47C1,47.6,1,48.3,1,48.9v20.6
 | 
			
		||||
			c5-5,15.9-7.7,29.2-9.7c8.6-1.2,17.2-1.9,25.9-2c1.6,0,3.2,0,4.8,0C60.8,57.8,76.3,35.2,73.2,35.1z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_17_" gradientUnits="userSpaceOnUse" x1="50.3126" y1="432.0108" x2="28.2664" y2="428.9106" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st16" d="M44.1,33.5c-10.7,0.5-18.4,1.3-24.8,3.1l0.3,0.8c0.6-0.1,1.3-0.3,1.9-0.4c7.7-1.2,15.4-2,23.1-2.3L44.1,33.5
 | 
			
		||||
			z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_18_" gradientUnits="userSpaceOnUse" x1="72.7451" y1="450.8669" x2="10.4435" y2="442.1056" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="3.820000e-02" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="0.1177" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.2081" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.3177" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#109EDE"/>
 | 
			
		||||
			<stop  offset="0.6823" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.7919" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.8823" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.9618" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st17" d="M73.2,34.3c-3-0.1-9.5-0.4-15.1-0.4c-51.1,0-56.7,11.8-57,12.4C1,46.8,1,47.5,1,48.1v21.4
 | 
			
		||||
			c5-5,15.9-8.5,29.2-10.5c8.6-1.2,17.2-1.9,25.9-2c1.6,0,3.2,0,4.8,0C63.2,57.1,76.3,34.4,73.2,34.3z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_19_" gradientUnits="userSpaceOnUse" x1="101.6202" y1="407.733" x2="71.9933" y2="458.7571" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="0.1238" style="stop-color:#177EBC"/>
 | 
			
		||||
			<stop  offset="0.2516" style="stop-color:#0E91D1"/>
 | 
			
		||||
			<stop  offset="0.3797" style="stop-color:#10A2E1"/>
 | 
			
		||||
			<stop  offset="0.5064" style="stop-color:#18ACEA"/>
 | 
			
		||||
			<stop  offset="0.631" style="stop-color:#24B1ED"/>
 | 
			
		||||
			<stop  offset="0.7513" style="stop-color:#28B1ED"/>
 | 
			
		||||
			<stop  offset="0.7911" style="stop-color:#24B1ED"/>
 | 
			
		||||
			<stop  offset="0.8324" style="stop-color:#18ACEA"/>
 | 
			
		||||
			<stop  offset="0.8743" style="stop-color:#10A2E1"/>
 | 
			
		||||
			<stop  offset="0.9167" style="stop-color:#0E91D1"/>
 | 
			
		||||
			<stop  offset="0.959" style="stop-color:#177EBC"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st18" d="M58.2,58.8C72.1,37.9,82.4,16,79.8,4.1l17.3,11c0.5,0.3,1,0.7,1.4,1.2c0.3,0.5,7.1,11.7-20.7,54.5
 | 
			
		||||
			c-2,3.2-3.9,6.9-4.6,7.2c-4.1-4-12.6-15.6-14.7-18.5C58.3,59.1,58.2,58.8,58.2,58.8z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_20_" gradientUnits="userSpaceOnUse" x1="105.3445" y1="433.3988" x2="94.5575" y2="453.6416" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st19" d="M96.6,37.2c-3.1,7.8-6.8,15.3-11.1,22.4l1.3,0.2c5.5-9.5,8.7-16.5,10.9-22.4L96.6,37.2z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_21_" gradientUnits="userSpaceOnUse" x1="100.6479" y1="408.8533" x2="70.927" y2="464.6275" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="3.820000e-02" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="0.1177" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.2081" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.3177" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#109EDE"/>
 | 
			
		||||
			<stop  offset="0.6823" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.7919" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.8823" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.9618" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st20" d="M78.5,71.4c27.8-42.8,21-54,20.7-54.5c-0.4-0.5-0.9-0.9-1.4-1.2L79.8,4.1c2.5,11.8-7.4,34.6-21.2,55.5
 | 
			
		||||
			c4.6,7.1,9.4,14,14.5,19.9C74.1,78.3,76.7,74.3,78.5,71.4z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_22_" gradientUnits="userSpaceOnUse" x1="102.9539" y1="410.1097" x2="72.9714" y2="466.375" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2867A2"/>
 | 
			
		||||
			<stop  offset="3.820000e-02" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="0.1177" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.2081" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.3177" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#109EDE"/>
 | 
			
		||||
			<stop  offset="0.6823" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.7919" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.8823" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.9618" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2867A2"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st21" d="M72.6,80.2c0.1-0.1,0.2-0.1,0.2-0.2c-5.3-6.2-10.2-12.8-14.7-19.7c-0.1,0.1-0.2,0.2-0.2,0.4
 | 
			
		||||
			C57.9,60.6,67.6,75.9,72.6,80.2z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_23_" gradientUnits="userSpaceOnUse" x1="54.5637" y1="474.0854" x2="62.254" y2="494.9197" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2B3B6A"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2B3B6A"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st22" d="M58.2,98.4C57.9,98.1,47.7,86.1,43.1,79l-0.8,0.9C46.9,86.6,52,93,57.6,99L58.2,98.4z"/>
 | 
			
		||||
		
 | 
			
		||||
			<linearGradient id="SVGID_24_" gradientUnits="userSpaceOnUse" x1="69.1674" y1="452.6575" x2="78.2209" y2="509.0225" gradientTransform="matrix(1 0 0 1 -8.0854 -395.1333)">
 | 
			
		||||
			<stop  offset="0" style="stop-color:#2E5284"/>
 | 
			
		||||
			<stop  offset="2.440000e-02" style="stop-color:#2C5C92"/>
 | 
			
		||||
			<stop  offset="8.880000e-02" style="stop-color:#2075B1"/>
 | 
			
		||||
			<stop  offset="0.1599" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.2403" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.3378" style="stop-color:#0E9CDA"/>
 | 
			
		||||
			<stop  offset="0.5" style="stop-color:#109EDE"/>
 | 
			
		||||
			<stop  offset="0.6718" style="stop-color:#0C9BDB"/>
 | 
			
		||||
			<stop  offset="0.7751" style="stop-color:#0A94D2"/>
 | 
			
		||||
			<stop  offset="0.8603" style="stop-color:#0B86C4"/>
 | 
			
		||||
			<stop  offset="0.9351" style="stop-color:#2174B1"/>
 | 
			
		||||
			<stop  offset="1" style="stop-color:#2C5D95"/>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<path class="st23" d="M72.9,79.9c-5.3-6.2-10.2-12.8-14.7-19.7c-0.5-0.8-1.1-1.6-1.6-2.4c-8.7,0-17.4,0.7-26,1.9
 | 
			
		||||
			c0.3,0.5,4.3,7.2,7.2,11.7c27.8,42.8,40.8,41.1,41.4,41c0.6-0.2,1.2-0.5,1.7-0.8L98.8,100C91.4,98.4,82.1,90.6,72.9,79.9z"/>
 | 
			
		||||
	</g>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										1
									
								
								resources/icons/jami_id_logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="24" viewBox="0 0 30 24"><defs><style>.a{fill:#005699;}.b,.d{fill:none;}.b{stroke:#005699;stroke-width:1.5px;}.c{stroke:none;}</style></defs><g transform="translate(7.328 6.159)"><g transform="translate(0 0)"><path class="a" d="M-2124.191-3452.351h.007a1.335,1.335,0,0,0,.972-.341,1.106,1.106,0,0,0,.351-.847.958.958,0,0,0-.34-.76,1.407,1.407,0,0,0-.983-.337h-.012a1.365,1.365,0,0,0-.977.342,1.1,1.1,0,0,0-.053,1.549l.055.056a1.364,1.364,0,0,0,.973.341Z" transform="translate(2125.521 3454.638)"/></g><g transform="translate(0.358 0.062)"><path class="a" d="M-2123.846-3442.183h9.042a8,8,0,0,0,3.567-.767,5.651,5.651,0,0,0,2.353-2.118,6.555,6.555,0,0,0,0-6.4,5.651,5.651,0,0,0-2.351-2.114,7.975,7.975,0,0,0-3.562-.766h-3.427v8.653h2.057v-7.142h1.216a4.586,4.586,0,0,1,4.953,4.187,4.586,4.586,0,0,1-4.187,4.954,4.585,4.585,0,0,1-.772,0h-6.942v-6.035h-1.946v6.035h0Z" transform="translate(2123.846 3454.35)"/></g></g><g class="b"><path class="c" d="M5,0H25a5,5,0,0,1,5,5V24a0,0,0,0,1,0,0H5a5,5,0,0,1-5-5V5A5,5,0,0,1,5,0Z"/><path class="d" d="M5,.75H25A4.25,4.25,0,0,1,29.25,5V22.5a.75.75,0,0,1-.75.75H5A4.25,4.25,0,0,1,.75,19V5A4.25,4.25,0,0,1,5,.75Z"/></g></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										13
									
								
								resources/icons/more_Menu_Black_24dp.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,13 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
 | 
			
		||||
<g id="noun-add-929469" transform="translate(-102.6 -102.6)">
 | 
			
		||||
	<path id="Path_270" d="M107.5,107.5c-3.9,3.9-3.9,10.2,0,14.1c3.9,3.9,10.2,3.9,14.1,0c3.9-3.9,3.9-10.2,0-14.1
 | 
			
		||||
		C117.8,103.6,111.4,103.6,107.5,107.5C107.5,107.5,107.5,107.5,107.5,107.5z M120.5,120.5c-3.3,3.3-8.5,3.3-11.8,0
 | 
			
		||||
		c-3.3-3.3-3.3-8.5,0-11.8c3.3-3.3,8.5-3.3,11.8,0C123.7,112,123.7,117.2,120.5,120.5z"/>
 | 
			
		||||
	<path id="Path_271" d="M114.6,109.8c-0.4,0-0.8,0.4-0.8,0.8c0,0,0,0,0,0v3.1h-3.1c-0.5,0-0.8,0.4-0.8,0.8c0,0.5,0.4,0.8,0.8,0.8
 | 
			
		||||
		h3.1v3.1c0,0.4,0.4,0.8,0.8,0.8c0,0,0,0,0,0c0.5,0,0.8-0.4,0.8-0.8v-3.1h3.1c0.5,0,0.8-0.4,0.8-0.8c0-0.4-0.4-0.8-0.8-0.8
 | 
			
		||||
		c0,0,0,0,0,0h-3.1v-3.1C115.4,110.2,115.1,109.8,114.6,109.8C114.6,109.8,114.6,109.8,114.6,109.8z"/>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										41
									
								
								resources/icons/plugins_default_icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,41 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 428 428" style="enable-background:new 0 0 428 428;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);}
 | 
			
		||||
	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_2_);}
 | 
			
		||||
	.st2{fill:url(#SVGID_3_);}
 | 
			
		||||
	.st3{fill:url(#SVGID_4_);}
 | 
			
		||||
</style>
 | 
			
		||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-12.625" y1="262.475" x2="165.425" y2="84.425" gradientTransform="matrix(1 0 0 -1 0 428)">
 | 
			
		||||
	<stop  offset="0" style="stop-color:#7E7E7E"/>
 | 
			
		||||
	<stop  offset="1" style="stop-color:#E9FFFF"/>
 | 
			
		||||
</linearGradient>
 | 
			
		||||
<path class="st0" d="M1.6,154v21.5v1.4c0.2,0,0.4,0,0.6,0c21.8,0,39.5,17.7,39.5,39.5S24,255.9,2.2,255.9c-0.2,0-0.4,0-0.6,0V408
 | 
			
		||||
	H101V152c0.2,0.1,0.3,0.1,0.5,0.2V51.4L1.6,154z"/>
 | 
			
		||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="227.375" y1="343.175" x2="405.475" y2="165.075" gradientTransform="matrix(1 0 0 -1 0 428)">
 | 
			
		||||
	<stop  offset="0" style="stop-color:#999999"/>
 | 
			
		||||
	<stop  offset="0.3476" style="stop-color:#9B9B9B"/>
 | 
			
		||||
	<stop  offset="0.525" style="stop-color:#A1A3A3"/>
 | 
			
		||||
	<stop  offset="0.6639" style="stop-color:#ABB0B0"/>
 | 
			
		||||
	<stop  offset="0.7828" style="stop-color:#BAC3C3"/>
 | 
			
		||||
	<stop  offset="0.8889" style="stop-color:#CDDCDC"/>
 | 
			
		||||
	<stop  offset="0.9845" style="stop-color:#E5F9F9"/>
 | 
			
		||||
	<stop  offset="1" style="stop-color:#E9FFFF"/>
 | 
			
		||||
</linearGradient>
 | 
			
		||||
<path class="st1" d="M386.9,176.8L386.9,176.8V19h-93.7v256c-0.1-0.1-0.3-0.1-0.4-0.2v100.8l94.2-96.7v-23.2l0,0
 | 
			
		||||
	c21.8,0,39.5-17.7,39.5-39.5C426.4,194.5,408.7,176.8,386.9,176.8z"/>
 | 
			
		||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1.6" y1="341.5" x2="395.2748" y2="341.5" gradientTransform="matrix(1 0 0 -1 0 428)">
 | 
			
		||||
	<stop  offset="0" style="stop-color:#999999"/>
 | 
			
		||||
	<stop  offset="0.9889" style="stop-color:#E9FFFF"/>
 | 
			
		||||
</linearGradient>
 | 
			
		||||
<path class="st2" d="M181.3,19C126.4,19,1.6,31.5,1.6,154c0,0,49.3-42.7,136.9-25s160.9-0.7,207.6-49.1
 | 
			
		||||
	c18.6-19.3,32.4-39.6,40.2-60.9H181.3z"/>
 | 
			
		||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-723.2346" y1="1360.2583" x2="-329.5598" y2="1360.2583" gradientTransform="matrix(-1 0 0 1 -336.3346 -1016.8083)">
 | 
			
		||||
	<stop  offset="0" style="stop-color:#E9FFFF"/>
 | 
			
		||||
	<stop  offset="1" style="stop-color:#999999"/>
 | 
			
		||||
</linearGradient>
 | 
			
		||||
<path class="st3" d="M207.2,408c54.9,0,179.7-11.9,179.7-129.1c0,0-49.3,40.8-136.9,23.9s-160.9,0.7-207.6,47
 | 
			
		||||
	C23.8,368.3,10.1,387.6,2.2,408H207.2z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/BG-DarkMode-ID_Jami.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 514 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/BG-LightMode-ID_Jami.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 622 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/default_plugin_background.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 145 KiB  | 
@ -35,9 +35,6 @@ BaseView {
 | 
			
		||||
 | 
			
		||||
    property bool successState: true
 | 
			
		||||
 | 
			
		||||
    // signal to redirect the page to main view
 | 
			
		||||
    signal loaderSourceChangeRequested(int sourceToLoad)
 | 
			
		||||
 | 
			
		||||
    function slotMigrationButtonClicked() {
 | 
			
		||||
        stackedWidget.currentIndex = AccountMigrationView.AccountMigrationStep.Synching;
 | 
			
		||||
        AccountAdapter.setArchivePasswordAsync(CurrentAccountToMigrate.accountId, passwordInputLineEdit.text);
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,6 @@ ApplicationWindow {
 | 
			
		||||
    LayoutMirroring.childrenInherit: isRTL
 | 
			
		||||
 | 
			
		||||
    enum LoadedSource {
 | 
			
		||||
        WizardView,
 | 
			
		||||
        MainView,
 | 
			
		||||
        AccountMigrationView,
 | 
			
		||||
        None
 | 
			
		||||
@ -66,6 +65,15 @@ ApplicationWindow {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    header: Loader {
 | 
			
		||||
        active: true
 | 
			
		||||
        sourceComponent: GenericErrorsRow {
 | 
			
		||||
            id: genericError
 | 
			
		||||
            text: CurrentAccount.enabled ? JamiStrings.noNetworkConnectivity : JamiStrings.disabledAccount
 | 
			
		||||
            height: visible? JamiTheme.chatViewHeaderPreferredHeight : 0
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Rectangle {
 | 
			
		||||
        id: focusOverlay
 | 
			
		||||
        objectName: "focusOverlay"
 | 
			
		||||
@ -95,19 +103,13 @@ ApplicationWindow {
 | 
			
		||||
 | 
			
		||||
    function checkLoadedSource() {
 | 
			
		||||
        var sourceString = mainApplicationLoader.source.toString();
 | 
			
		||||
        if (sourceString === JamiQmlUtils.wizardViewLoadPath)
 | 
			
		||||
            return MainApplicationWindow.LoadedSource.WizardView;
 | 
			
		||||
        else if (sourceString === JamiQmlUtils.mainViewLoadPath)
 | 
			
		||||
        if (sourceString === JamiQmlUtils.mainViewLoadPath)
 | 
			
		||||
            return MainApplicationWindow.LoadedSource.MainView;
 | 
			
		||||
        return MainApplicationWindow.LoadedSource.None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function startClient() {
 | 
			
		||||
        if (UtilsAdapter.getAccountListSize() !== 0) {
 | 
			
		||||
            setMainLoaderSource(JamiQmlUtils.mainViewLoadPath);
 | 
			
		||||
        } else {
 | 
			
		||||
            setMainLoaderSource(JamiQmlUtils.wizardViewLoadPath);
 | 
			
		||||
        }
 | 
			
		||||
        setMainLoaderSource(JamiQmlUtils.mainViewLoadPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setMainLoaderSource(source) {
 | 
			
		||||
@ -129,9 +131,6 @@ ApplicationWindow {
 | 
			
		||||
        // If we're in the onboarding wizard or 'MinimizeOnClose'
 | 
			
		||||
        // is set, then we can quit
 | 
			
		||||
        if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) || !UtilsAdapter.getAccountListSize()) {
 | 
			
		||||
            if (checkLoadedSource() === MainApplicationWindow.LoadedSource.MainView) {
 | 
			
		||||
                cleanupMainView();
 | 
			
		||||
            }
 | 
			
		||||
            Qt.quit();
 | 
			
		||||
        } else {
 | 
			
		||||
            layoutManager.closeToTray();
 | 
			
		||||
@ -159,14 +158,6 @@ ApplicationWindow {
 | 
			
		||||
        asynchronous: true
 | 
			
		||||
        visible: status == Loader.Ready
 | 
			
		||||
 | 
			
		||||
        Connections {
 | 
			
		||||
            target: viewCoordinator
 | 
			
		||||
 | 
			
		||||
            function onRequestAppWindowWizardView() {
 | 
			
		||||
                setMainLoaderSource(JamiQmlUtils.wizardViewLoadPath);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Connections {
 | 
			
		||||
            id: connectionMigrationEnded
 | 
			
		||||
 | 
			
		||||
@ -182,32 +173,16 @@ ApplicationWindow {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Connections {
 | 
			
		||||
            target: mainApplicationLoader.item
 | 
			
		||||
 | 
			
		||||
            function onLoaderSourceChangeRequested(sourceToLoad) {
 | 
			
		||||
                if (sourceToLoad === MainApplicationWindow.LoadedSource.WizardView)
 | 
			
		||||
                    setMainLoaderSource(JamiQmlUtils.wizardViewLoadPath);
 | 
			
		||||
                else if (sourceToLoad === MainApplicationWindow.LoadedSource.AccountMigrationView)
 | 
			
		||||
                    setMainLoaderSource(JamiQmlUtils.accountMigrationViewLoadPath);
 | 
			
		||||
                else
 | 
			
		||||
                    setMainLoaderSource(JamiQmlUtils.mainViewLoadPath);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set `visible = false` when loading a new QML file.
 | 
			
		||||
        onSourceChanged: windowSettingsLoaded = false
 | 
			
		||||
 | 
			
		||||
        onLoaded: {
 | 
			
		||||
            if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView) {
 | 
			
		||||
                // Onboarding wizard window, these settings are fixed.
 | 
			
		||||
                // - window screen will default to the primary
 | 
			
		||||
                // - the window will showNormal once windowSettingsLoaded is
 | 
			
		||||
                //   set to true(then forcing visible to true)
 | 
			
		||||
                appWindow.width = JamiTheme.wizardViewMinWidth;
 | 
			
		||||
                appWindow.height = JamiTheme.wizardViewMinHeight;
 | 
			
		||||
                appWindow.minimumWidth = JamiTheme.wizardViewMinWidth;
 | 
			
		||||
                appWindow.minimumHeight = JamiTheme.wizardViewMinHeight;
 | 
			
		||||
            if (UtilsAdapter.getAccountListSize() === 0) {
 | 
			
		||||
                layoutManager.restoreWindowSettings();
 | 
			
		||||
                if (!viewCoordinator.rootView)
 | 
			
		||||
                    // Set the viewCoordinator's root item.
 | 
			
		||||
                    viewCoordinator.init(item);
 | 
			
		||||
                viewCoordinator.present("WizardView");
 | 
			
		||||
            } else {
 | 
			
		||||
                // Main window, load any valid app settings, and allow the
 | 
			
		||||
                // layoutManager to handle as much as possible.
 | 
			
		||||
@ -220,8 +195,9 @@ ApplicationWindow {
 | 
			
		||||
                        viewCoordinator.present("WelcomePage");
 | 
			
		||||
                        viewCoordinator.preload("ConversationView");
 | 
			
		||||
                    });
 | 
			
		||||
                // Set the viewCoordinator's root item.
 | 
			
		||||
                viewCoordinator.init(item);
 | 
			
		||||
                if (!viewCoordinator.rootView)
 | 
			
		||||
                    // Set the viewCoordinator's root item.
 | 
			
		||||
                    viewCoordinator.init(item);
 | 
			
		||||
                if (CurrentAccountToMigrate.accountToMigrateListSize > 0)
 | 
			
		||||
                    viewCoordinator.present("AccountMigrationView");
 | 
			
		||||
            }
 | 
			
		||||
@ -233,10 +209,10 @@ ApplicationWindow {
 | 
			
		||||
            windowSettingsLoaded = true;
 | 
			
		||||
 | 
			
		||||
            // Quiet check for updates on start if set to.
 | 
			
		||||
            if (Qt.platform.os.toString() !== "osx") {
 | 
			
		||||
            if (Qt.platform.os.toString() === "windows") {
 | 
			
		||||
                if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
 | 
			
		||||
                    UpdateManager.checkForUpdates(true);
 | 
			
		||||
                    UpdateManager.setAutoUpdateCheck(true);
 | 
			
		||||
                    AppVersionManager.checkForUpdates(true);
 | 
			
		||||
                    AppVersionManager.setAutoUpdateCheck(true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -263,6 +239,10 @@ ApplicationWindow {
 | 
			
		||||
    Connections {
 | 
			
		||||
        target: MainApplication
 | 
			
		||||
 | 
			
		||||
        function onAboutToQuit() {
 | 
			
		||||
            cleanupMainView()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function onCloseRequested() {
 | 
			
		||||
            close(true);
 | 
			
		||||
        }
 | 
			
		||||
@ -288,7 +268,7 @@ ApplicationWindow {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function presentUpdateInfoDialog(infoText) {
 | 
			
		||||
        viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
 | 
			
		||||
        return viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
 | 
			
		||||
                "title": JamiStrings.updateDialogTitle,
 | 
			
		||||
                "infoText": infoText,
 | 
			
		||||
                "buttonTitles": [JamiStrings.optionOk],
 | 
			
		||||
@ -297,10 +277,40 @@ ApplicationWindow {
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Connections {
 | 
			
		||||
        target: UpdateManager
 | 
			
		||||
    function presentUpdateConfirmInstallDialog(switchToBeta=false) {
 | 
			
		||||
        return viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
 | 
			
		||||
                "title": JamiStrings.updateDialogTitle,
 | 
			
		||||
                "infoText": switchToBeta ? JamiStrings.confirmBeta : JamiStrings.updateFound,
 | 
			
		||||
                "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
 | 
			
		||||
                "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
 | 
			
		||||
                "buttonCallBacks": [function () {
 | 
			
		||||
                        AppVersionManager.applyUpdates(switchToBeta);
 | 
			
		||||
                    }]
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        function onUpdateDownloadStarted() {
 | 
			
		||||
    function translateErrorToString(error) {
 | 
			
		||||
        switch (error) {
 | 
			
		||||
        case NetworkManager.DISCONNECTED:
 | 
			
		||||
            return JamiStrings.networkDisconnected;
 | 
			
		||||
        case NetworkManager.CONTENT_NOT_FOUND:
 | 
			
		||||
            return JamiStrings.contentNotFoundError;
 | 
			
		||||
        case NetworkManager.ACCESS_DENIED:
 | 
			
		||||
            return JamiStrings.accessError;
 | 
			
		||||
        case NetworkManager.SSL_ERROR:
 | 
			
		||||
            return JamiStrings.updateSSLError;
 | 
			
		||||
        case NetworkManager.CANCELED:
 | 
			
		||||
            return JamiStrings.updateDownloadCanceled;
 | 
			
		||||
        case NetworkManager.NETWORK_ERROR:
 | 
			
		||||
        default:
 | 
			
		||||
            return JamiStrings.updateNetworkError;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Connections {
 | 
			
		||||
        target: AppVersionManager
 | 
			
		||||
 | 
			
		||||
        function onDownloadStarted() {
 | 
			
		||||
            viewCoordinator.presentDialog(appWindow, "settingsview/components/UpdateDownloadDialog.qml", {
 | 
			
		||||
                    "title": JamiStrings.updateDialogTitle
 | 
			
		||||
                });
 | 
			
		||||
@ -308,41 +318,26 @@ ApplicationWindow {
 | 
			
		||||
 | 
			
		||||
        function onUpdateCheckReplyReceived(ok, found) {
 | 
			
		||||
            if (!ok) {
 | 
			
		||||
                // Show an error dialog describing that we could not successfully check for an update.
 | 
			
		||||
                presentUpdateInfoDialog(JamiStrings.updateCheckError);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (!found) {
 | 
			
		||||
                // Show a dialog describing that no update was found.
 | 
			
		||||
                presentUpdateInfoDialog(JamiStrings.updateNotFound);
 | 
			
		||||
            } else {
 | 
			
		||||
                viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
 | 
			
		||||
                        "title": JamiStrings.updateDialogTitle,
 | 
			
		||||
                        "infoText": JamiStrings.updateFound,
 | 
			
		||||
                        "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
 | 
			
		||||
                        "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
 | 
			
		||||
                        "buttonCallBacks": [function () {
 | 
			
		||||
                                UpdateManager.applyUpdates();
 | 
			
		||||
                            }]
 | 
			
		||||
                    });
 | 
			
		||||
                // Show a dialog describing that an update were found, and offering to install it.
 | 
			
		||||
                presentUpdateConfirmInstallDialog()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function onUpdateErrorOccurred(error) {
 | 
			
		||||
            presentUpdateInfoDialog((function () {
 | 
			
		||||
                        switch (error) {
 | 
			
		||||
                        case NetworkManager.ACCESS_DENIED:
 | 
			
		||||
                            return JamiStrings.genericError;
 | 
			
		||||
                        case NetworkManager.DISCONNECTED:
 | 
			
		||||
                            return JamiStrings.networkDisconnected;
 | 
			
		||||
                        case NetworkManager.NETWORK_ERROR:
 | 
			
		||||
                            return JamiStrings.updateNetworkError;
 | 
			
		||||
                        case NetworkManager.SSL_ERROR:
 | 
			
		||||
                            return JamiStrings.updateSSLError;
 | 
			
		||||
                        case NetworkManager.CANCELED:
 | 
			
		||||
                            return JamiStrings.updateDownloadCanceled;
 | 
			
		||||
                        default:
 | 
			
		||||
                            return {};
 | 
			
		||||
                        }
 | 
			
		||||
                    })());
 | 
			
		||||
        function onNetworkErrorOccurred(error) {
 | 
			
		||||
            var errorStr = translateErrorToString(error);
 | 
			
		||||
            presentUpdateInfoDialog(errorStr);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function onInstallErrorOccurred(errorMsg) {
 | 
			
		||||
            presentUpdateInfoDialog(errorMsg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,10 @@ QtObject {
 | 
			
		||||
    required property QtObject viewManager
 | 
			
		||||
 | 
			
		||||
    signal initialized
 | 
			
		||||
    signal requestAppWindowWizardView
 | 
			
		||||
 | 
			
		||||
    function requestAppWindowWizardView() {
 | 
			
		||||
        viewCoordinator.present("WizardView");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // A map of view names to file paths for QML files that define each view.
 | 
			
		||||
    property variant resources: {
 | 
			
		||||
@ -71,7 +74,7 @@ QtObject {
 | 
			
		||||
    // Create, present, and return a dialog object.
 | 
			
		||||
    function presentDialog(parent, path, props = {}) {
 | 
			
		||||
        // Open the dialog once the object is created
 | 
			
		||||
        return viewManager.createView(path, parent, function (obj) {
 | 
			
		||||
        return viewManager.createUniqueView(path, parent, function (obj) {
 | 
			
		||||
                const doneCb = function () {
 | 
			
		||||
                    viewManager.destroyView(path);
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
@ -36,36 +36,70 @@ QtObject {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create a view from a path only if it doesn't already exist. This is used
 | 
			
		||||
    // by the view coordinator to create views that are not self-destructing
 | 
			
		||||
    // (main views) and only exist once per instance of the app.
 | 
			
		||||
    function createView(path, parent = null, cb = null, props = {}) {
 | 
			
		||||
        if (views.hasOwnProperty(path)) {
 | 
			
		||||
            // an instance of <path> already exists
 | 
			
		||||
        const component = Qt.createComponent(Qt.resolvedUrl(path));
 | 
			
		||||
        return createViewFromComponent(component, path, parent, cb, props);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create a new view. Useful when we want to create multiple views that are
 | 
			
		||||
    // self-destructing (dialogs).
 | 
			
		||||
    function createUniqueView(path, parent = null, cb = null, props = {}) {
 | 
			
		||||
        const component = Qt.createComponent(Qt.resolvedUrl(path));
 | 
			
		||||
        return createViewFromComponent(component, getViewName(path), parent, cb,
 | 
			
		||||
                                       props);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create a new view from a component. If a view with the same path already
 | 
			
		||||
    // exists, it is returned instead.
 | 
			
		||||
    function createViewFromComponent(component, viewName, parent = null,
 | 
			
		||||
                                     cb = null, props = {}) {
 | 
			
		||||
        if (views.hasOwnProperty(viewName)) {
 | 
			
		||||
            // an instance of the view already exists
 | 
			
		||||
            if (cb !== null) {
 | 
			
		||||
                cb(views[path])
 | 
			
		||||
                cb(views[viewName])
 | 
			
		||||
            }
 | 
			
		||||
            return views[path]
 | 
			
		||||
            return views[viewName]
 | 
			
		||||
        }
 | 
			
		||||
        const component = Qt.createComponent(Qt.resolvedUrl(path))
 | 
			
		||||
        if (component.status === Component.Ready) {
 | 
			
		||||
            const obj = component.createObject(parent, props)
 | 
			
		||||
            if (obj === null) {
 | 
			
		||||
                print("error creating object")
 | 
			
		||||
                console.error("error creating object")
 | 
			
		||||
                return null
 | 
			
		||||
            }
 | 
			
		||||
            views[path] = obj
 | 
			
		||||
            views[viewName] = obj
 | 
			
		||||
            // Set the view name to the object name if it has one.
 | 
			
		||||
            const viewName = obj.objectName.toString() !== '' ? obj.objectName : path.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "")
 | 
			
		||||
            viewPaths[viewName] = path
 | 
			
		||||
            const friendlyName = obj.objectName.toString() !== '' ?
 | 
			
		||||
                obj.objectName :
 | 
			
		||||
                viewName.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "")
 | 
			
		||||
            viewPaths[friendlyName] = viewName
 | 
			
		||||
            if (cb !== null) {
 | 
			
		||||
                cb(obj)
 | 
			
		||||
            }
 | 
			
		||||
            return views[path]
 | 
			
		||||
            return views[viewName]
 | 
			
		||||
        }
 | 
			
		||||
        print("error creating component", path)
 | 
			
		||||
        console.error("error creating component", component.url)
 | 
			
		||||
        console.error(component.errorString())
 | 
			
		||||
        Qt.exit(1)
 | 
			
		||||
        return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Finds a unique view name for a given path by appending a number to the
 | 
			
		||||
    // base name. For example, if a view named "MyView" already exists, the next
 | 
			
		||||
    // view will be named "MyView_1".
 | 
			
		||||
    function getViewName(path) {
 | 
			
		||||
        const baseName = path.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "")
 | 
			
		||||
        let viewName = baseName
 | 
			
		||||
        let suffix = 1
 | 
			
		||||
        while (views.hasOwnProperty(viewName)) {
 | 
			
		||||
            viewName = `${baseName}_${suffix}`
 | 
			
		||||
            suffix++
 | 
			
		||||
        }
 | 
			
		||||
        return viewName
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function destroyView(path) {
 | 
			
		||||
        // The view may already have been destroyed.
 | 
			
		||||
        if (!views.hasOwnProperty(path)) {
 | 
			
		||||
 | 
			
		||||
@ -34,10 +34,12 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager,
 | 
			
		||||
    , settingsManager_(settingsManager)
 | 
			
		||||
    , systemTray_(systemTray)
 | 
			
		||||
    , accountListModel_(new AccountListModel(instance))
 | 
			
		||||
    , deviceItemListModel_(new DeviceItemListModel(instance))
 | 
			
		||||
    , deviceItemListModel_(new DeviceItemListModel(instance, parent))
 | 
			
		||||
    , moderatorListModel_(new ModeratorListModel(instance, parent))
 | 
			
		||||
{
 | 
			
		||||
    QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accountListModel_.get(), "AccountListModel");
 | 
			
		||||
    QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, deviceItemListModel_.get(), "DeviceItemListModel");
 | 
			
		||||
    QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, moderatorListModel_.get(), "ModeratorListModel");
 | 
			
		||||
 | 
			
		||||
    connect(&lrcInstance_->accountModel(),
 | 
			
		||||
            &AccountModel::accountStatusChanged,
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@
 | 
			
		||||
 | 
			
		||||
#include "accountlistmodel.h"
 | 
			
		||||
#include "deviceitemlistmodel.h"
 | 
			
		||||
#include "moderatorlistmodel.h"
 | 
			
		||||
#include "systemtray.h"
 | 
			
		||||
#include "lrcinstance.h"
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
@ -102,5 +103,6 @@ private:
 | 
			
		||||
 | 
			
		||||
    QScopedPointer<AccountListModel> accountListModel_;
 | 
			
		||||
    QScopedPointer<DeviceItemListModel> deviceItemListModel_;
 | 
			
		||||
    QScopedPointer<ModeratorListModel> moderatorListModel_;
 | 
			
		||||
};
 | 
			
		||||
Q_DECLARE_METATYPE(AccountAdapter*)
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ extern const QString defaultDownloadPath;
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
#define KEYS \
 | 
			
		||||
    X(MinimizeOnClose, true) \
 | 
			
		||||
    X(MinimizeOnClose, false) \
 | 
			
		||||
    X(DownloadPath, defaultDownloadPath) \
 | 
			
		||||
    X(ScreenshotPath, {}) \
 | 
			
		||||
    X(EnableNotifications, true) \
 | 
			
		||||
@ -46,9 +46,10 @@ extern const QString defaultDownloadPath;
 | 
			
		||||
    X(AppTheme, "System") \
 | 
			
		||||
    X(BaseZoom, 1.0) \
 | 
			
		||||
    X(ParticipantsSide, false) \
 | 
			
		||||
    X(HideSelf, false) \
 | 
			
		||||
    X(HideSelf, true) \
 | 
			
		||||
    X(HideSpectators, false) \
 | 
			
		||||
    X(AutoUpdate, true) \
 | 
			
		||||
    X(PluginAutoUpdate, false) \
 | 
			
		||||
    X(StartMinimized, false) \
 | 
			
		||||
    X(ShowChatviewHorizontally, true) \
 | 
			
		||||
    X(NeverShowMeAgain, false) \
 | 
			
		||||
@ -56,12 +57,14 @@ extern const QString defaultDownloadPath;
 | 
			
		||||
    X(WindowState, QWindow::AutomaticVisibility) \
 | 
			
		||||
    X(EnableExperimentalSwarm, false) \
 | 
			
		||||
    X(LANG, "SYSTEM") \
 | 
			
		||||
    X(PluginStoreEndpoint, "https://plugins.jami.net") \
 | 
			
		||||
    X(PositionShareDuration, 15) \
 | 
			
		||||
    X(PositionShareLimit, true) \
 | 
			
		||||
    X(FlipSelf, true) \
 | 
			
		||||
    X(ShowMardownOption, false) \
 | 
			
		||||
    X(ShowSendOption, false)
 | 
			
		||||
 | 
			
		||||
    X(ChatViewEnterIsNewLine, false) \
 | 
			
		||||
    X(ShowSendOption, false) \
 | 
			
		||||
    X(DonateVisibleDate,  "2999-02-01 05:00")
 | 
			
		||||
/*
 | 
			
		||||
 * A class to expose settings keys in both c++ and QML.
 | 
			
		||||
 * Note: this is using a non-constructable class instead of a
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										243
									
								
								src/app/appversionmanager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,243 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020-2023 Savoir-faire Linux Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "appversionmanager.h"
 | 
			
		||||
 | 
			
		||||
#include "lrcinstance.h"
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
#include <QProcess>
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
 | 
			
		||||
#ifdef BETA
 | 
			
		||||
static constexpr bool isBeta = true;
 | 
			
		||||
#else
 | 
			
		||||
static constexpr bool isBeta = false;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static constexpr int updatePeriod = 1000 * 60 * 60 * 24; // one day in millis
 | 
			
		||||
static constexpr char downloadUrl[] = "https://dl.jami.net/windows";
 | 
			
		||||
static constexpr char versionSubUrl[] = "/version";
 | 
			
		||||
static constexpr char betaVersionSubUrl[] = "/beta/version";
 | 
			
		||||
static constexpr char msiSubUrl[] = "/jami.release.x64.msi";
 | 
			
		||||
static constexpr char betaMsiSubUrl[] = "/beta/jami.beta.x64.msi";
 | 
			
		||||
 | 
			
		||||
struct AppVersionManager::Impl : public QObject
 | 
			
		||||
{
 | 
			
		||||
    Impl(const QString& url, LRCInstance* instance, AppVersionManager& parent)
 | 
			
		||||
        : QObject(nullptr)
 | 
			
		||||
        , parent_(parent)
 | 
			
		||||
        , lrcInstance_(instance)
 | 
			
		||||
        , baseUrlString_(url.isEmpty() ? downloadUrl : url)
 | 
			
		||||
        , tempPath_(QDir::tempPath())
 | 
			
		||||
        , updateTimer_(new QTimer(this))
 | 
			
		||||
    {
 | 
			
		||||
        connect(updateTimer_, &QTimer::timeout, this, [this] {
 | 
			
		||||
            // Quiet period update check.
 | 
			
		||||
            parent_.checkForUpdates(true);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    ~Impl() = default;
 | 
			
		||||
 | 
			
		||||
    void checkForUpdates(bool quiet)
 | 
			
		||||
    {
 | 
			
		||||
        parent_.disconnect();
 | 
			
		||||
        // Fail without UI if this is a programmatic check.
 | 
			
		||||
        if (!quiet)
 | 
			
		||||
            connect(&parent_,
 | 
			
		||||
                    &NetworkManager::errorOccurred,
 | 
			
		||||
                    &parent_,
 | 
			
		||||
                    &AppVersionManager::networkErrorOccurred);
 | 
			
		||||
 | 
			
		||||
        cleanUpdateFiles();
 | 
			
		||||
        const QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl)
 | 
			
		||||
                                      : QUrl::fromUserInput(baseUrlString_ + versionSubUrl)};
 | 
			
		||||
        parent_.sendGetRequest(versionUrl, [this, quiet](const QByteArray& latestVersionString) {
 | 
			
		||||
            if (latestVersionString.isEmpty()) {
 | 
			
		||||
                qWarning() << "Error checking version";
 | 
			
		||||
                if (!quiet)
 | 
			
		||||
                    Q_EMIT parent_.updateCheckReplyReceived(false);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            auto currentVersion = QString(VERSION_STRING).toULongLong();
 | 
			
		||||
            auto latestVersion = latestVersionString.toULongLong();
 | 
			
		||||
            const QString channelStr = isBeta ? "beta" : "stable";
 | 
			
		||||
            const auto newVersionFound = latestVersion > currentVersion;
 | 
			
		||||
            qInfo().noquote() << "--------- Version info ------------"
 | 
			
		||||
                              << QString("\n - Current: %1 (%2)").arg(currentVersion).arg(channelStr);
 | 
			
		||||
            if (newVersionFound) {
 | 
			
		||||
                qDebug() << " - Latest: " << latestVersion;
 | 
			
		||||
                Q_EMIT parent_.updateCheckReplyReceived(true, true);
 | 
			
		||||
            } else if (!quiet) {
 | 
			
		||||
                Q_EMIT parent_.updateCheckReplyReceived(true, false);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void applyUpdates(bool beta = false)
 | 
			
		||||
    {
 | 
			
		||||
        parent_.disconnect();
 | 
			
		||||
        connect(&parent_,
 | 
			
		||||
                &NetworkManager::errorOccurred,
 | 
			
		||||
                &parent_,
 | 
			
		||||
                &AppVersionManager::networkErrorOccurred);
 | 
			
		||||
 | 
			
		||||
        const QUrl downloadUrl {(beta || isBeta)
 | 
			
		||||
                                    ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl)
 | 
			
		||||
                                    : QUrl::fromUserInput(baseUrlString_ + msiSubUrl)};
 | 
			
		||||
 | 
			
		||||
        const auto lastDownloadReplyId = parent_.replyId_;
 | 
			
		||||
        parent_.replyId_ = parent_.downloadFile(
 | 
			
		||||
            downloadUrl,
 | 
			
		||||
            lastDownloadReplyId,
 | 
			
		||||
            [this, downloadUrl](bool success, const QString& errorMessage) {
 | 
			
		||||
                Q_UNUSED(success)
 | 
			
		||||
                Q_UNUSED(errorMessage)
 | 
			
		||||
                QProcess process;
 | 
			
		||||
                auto basePath = tempPath_ + QDir::separator();
 | 
			
		||||
                auto msiPath = QDir::toNativeSeparators(basePath + downloadUrl.fileName());
 | 
			
		||||
                auto logPath = QDir::toNativeSeparators(basePath + "jami_x64_install.log");
 | 
			
		||||
                connect(&process, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
 | 
			
		||||
                    QString errorMsg;
 | 
			
		||||
                    if (error == QProcess::ProcessError::Timedout) {
 | 
			
		||||
                        errorMsg = tr("The installer process has timed out.");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        errorMsg = process.readAllStandardError();
 | 
			
		||||
                        if (errorMsg.isEmpty())
 | 
			
		||||
                            errorMsg = tr("The installer process has failed.");
 | 
			
		||||
                    }
 | 
			
		||||
                    Q_EMIT parent_.installErrorOccurred(errorMsg);
 | 
			
		||||
                });
 | 
			
		||||
                connect(&process,
 | 
			
		||||
                        &QProcess::finished,
 | 
			
		||||
                        this,
 | 
			
		||||
                        [&](int exitCode, QProcess::ExitStatus exitStatus) {
 | 
			
		||||
                            if (exitStatus != QProcess::ExitStatus::NormalExit || exitCode != 0) {
 | 
			
		||||
                                auto errorMsg = process.readAllStandardOutput();
 | 
			
		||||
                                Q_EMIT parent_.installErrorOccurred(errorMsg);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                process.start("msiexec",
 | 
			
		||||
                              QStringList() << "/i" << msiPath << "/passive"
 | 
			
		||||
                                            << "/norestart"
 | 
			
		||||
                                            << "WIXNONUILAUNCH=1"
 | 
			
		||||
                                            << "/L*V" << logPath);
 | 
			
		||||
                process.waitForFinished();
 | 
			
		||||
            },
 | 
			
		||||
            tempPath_);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void cancelUpdate()
 | 
			
		||||
    {
 | 
			
		||||
        parent_.cancelDownload(parent_.replyId_);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void setAutoUpdateCheck(bool state)
 | 
			
		||||
    {
 | 
			
		||||
        // Quiet check for updates periodically, if set to.
 | 
			
		||||
        if (!state) {
 | 
			
		||||
            updateTimer_->stop();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        updateTimer_->start(updatePeriod);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void cleanUpdateFiles()
 | 
			
		||||
    {
 | 
			
		||||
        // Delete all logs and msi in the temporary directory before launching.
 | 
			
		||||
        const QString dir = QDir::tempPath();
 | 
			
		||||
        QDir log_dir(dir, {"jami*.log"});
 | 
			
		||||
        for (const QString& filename : log_dir.entryList()) {
 | 
			
		||||
            log_dir.remove(filename);
 | 
			
		||||
        }
 | 
			
		||||
        QDir msi_dir(dir, {"jami*.msi"});
 | 
			
		||||
        for (const QString& filename : msi_dir.entryList()) {
 | 
			
		||||
            msi_dir.remove(filename);
 | 
			
		||||
        }
 | 
			
		||||
        QDir version_dir(dir, {"version"});
 | 
			
		||||
        for (const QString& filename : version_dir.entryList()) {
 | 
			
		||||
            version_dir.remove(filename);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    AppVersionManager& parent_;
 | 
			
		||||
 | 
			
		||||
    LRCInstance* lrcInstance_ {nullptr};
 | 
			
		||||
    QString baseUrlString_;
 | 
			
		||||
    QString tempPath_;
 | 
			
		||||
    QTimer* updateTimer_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AppVersionManager::AppVersionManager(const QString& url,
 | 
			
		||||
                                     ConnectivityMonitor* cm,
 | 
			
		||||
                                     LRCInstance* instance,
 | 
			
		||||
                                     QObject* parent)
 | 
			
		||||
    : NetworkManager(cm, parent)
 | 
			
		||||
    , replyId_(0)
 | 
			
		||||
    , pimpl_(std::make_unique<Impl>(url, instance, *this))
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
AppVersionManager::~AppVersionManager()
 | 
			
		||||
{
 | 
			
		||||
    cancelDownload(replyId_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
AppVersionManager::checkForUpdates(bool quiet)
 | 
			
		||||
{
 | 
			
		||||
    pimpl_->checkForUpdates(quiet);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
AppVersionManager::applyUpdates(bool beta)
 | 
			
		||||
{
 | 
			
		||||
    pimpl_->applyUpdates(beta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
AppVersionManager::cancelUpdate()
 | 
			
		||||
{
 | 
			
		||||
    pimpl_->cancelUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
AppVersionManager::setAutoUpdateCheck(bool state)
 | 
			
		||||
{
 | 
			
		||||
    pimpl_->setAutoUpdateCheck(state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
AppVersionManager::isCurrentVersionBeta()
 | 
			
		||||
{
 | 
			
		||||
    return isBeta;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
AppVersionManager::isUpdaterEnabled()
 | 
			
		||||
{
 | 
			
		||||
#ifdef Q_OS_WIN
 | 
			
		||||
    return true;
 | 
			
		||||
#endif
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
AppVersionManager::isAutoUpdaterEnabled()
 | 
			
		||||
{
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020-2023 Savoir-faire Linux Inc.
 | 
			
		||||
 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
@ -25,51 +24,36 @@
 | 
			
		||||
class LRCInstance;
 | 
			
		||||
class ConnectivityMonitor;
 | 
			
		||||
 | 
			
		||||
class UpdateManager final : public NetworkManager
 | 
			
		||||
class AppVersionManager final : public NetworkManager
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
    Q_DISABLE_COPY(UpdateManager)
 | 
			
		||||
    Q_DISABLE_COPY(AppVersionManager)
 | 
			
		||||
public:
 | 
			
		||||
    explicit UpdateManager(const QString& url,
 | 
			
		||||
                           ConnectivityMonitor* cm,
 | 
			
		||||
                           LRCInstance* instance = nullptr,
 | 
			
		||||
                           QObject* parent = nullptr);
 | 
			
		||||
    ~UpdateManager();
 | 
			
		||||
 | 
			
		||||
    enum GetStatus { STARTED, FINISHED };
 | 
			
		||||
    Q_ENUM(GetStatus)
 | 
			
		||||
    explicit AppVersionManager(const QString& url,
 | 
			
		||||
                               ConnectivityMonitor* cm,
 | 
			
		||||
                               LRCInstance* instance = nullptr,
 | 
			
		||||
                               QObject* parent = nullptr);
 | 
			
		||||
    ~AppVersionManager();
 | 
			
		||||
 | 
			
		||||
    Q_INVOKABLE void checkForUpdates(bool quiet = false);
 | 
			
		||||
    Q_INVOKABLE void applyUpdates(bool beta = false);
 | 
			
		||||
    Q_INVOKABLE void cancelUpdate();
 | 
			
		||||
    Q_INVOKABLE void setAutoUpdateCheck(bool state);
 | 
			
		||||
    Q_INVOKABLE bool isCurrentVersionBeta();
 | 
			
		||||
    Q_INVOKABLE bool isUpdaterEnabled();
 | 
			
		||||
    Q_INVOKABLE bool isAutoUpdaterEnabled();
 | 
			
		||||
    Q_INVOKABLE void cancelDownload();
 | 
			
		||||
 | 
			
		||||
    void downloadFile(const QUrl& url,
 | 
			
		||||
                      std::function<void(bool, const QString&)> onDoneCallback,
 | 
			
		||||
                      const QString& filePath);
 | 
			
		||||
    Q_INVOKABLE void setAutoUpdateCheck(bool state);
 | 
			
		||||
    Q_INVOKABLE void cancelUpdate();
 | 
			
		||||
    Q_INVOKABLE bool isCurrentVersionBeta();
 | 
			
		||||
 | 
			
		||||
Q_SIGNALS:
 | 
			
		||||
    void statusChanged(GetStatus status);
 | 
			
		||||
    void downloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
 | 
			
		||||
 | 
			
		||||
    void updateCheckReplyReceived(bool ok, bool found = false);
 | 
			
		||||
    void updateErrorOccurred(const NetworkManager::GetError& error);
 | 
			
		||||
    void updateDownloadStarted();
 | 
			
		||||
    void updateDownloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
 | 
			
		||||
    void updateDownloadFinished();
 | 
			
		||||
    void appCloseRequested();
 | 
			
		||||
    void updateCheckReplyReceived(bool ok, bool found = false);
 | 
			
		||||
    void updateDownloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
 | 
			
		||||
    void networkErrorOccurred(const NetworkManager::GetError& error);
 | 
			
		||||
    void installErrorOccurred(const QString& errorMsg);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void resetDownload();
 | 
			
		||||
    QNetworkReply* downloadReply_ {nullptr};
 | 
			
		||||
    QScopedPointer<QFile> file_;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    int replyId_;
 | 
			
		||||
    struct Impl;
 | 
			
		||||
    friend struct Impl;
 | 
			
		||||
    std::unique_ptr<Impl> pimpl_;
 | 
			
		||||
};
 | 
			
		||||
Q_DECLARE_METATYPE(UpdateManager*)
 | 
			
		||||
Q_DECLARE_METATYPE(AppVersionManager*)
 | 
			
		||||
@ -53,21 +53,21 @@ public:
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto imageId = idInfo.at(1);
 | 
			
		||||
        const auto& imageId = idInfo.at(1);
 | 
			
		||||
        if (!imageId.size()) {
 | 
			
		||||
            qWarning() << Q_FUNC_INFO << "Missing id in the image url";
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto type = idInfo.at(0);
 | 
			
		||||
        const auto& type = idInfo.at(0);
 | 
			
		||||
        if (type == "conversation") {
 | 
			
		||||
            if (imageId == "temp")
 | 
			
		||||
                return Utils::tempConversationAvatar(requestedSize);
 | 
			
		||||
 | 
			
		||||
            return Utils::conversationAvatar(lrcInstance_, imageId, requestedSize);
 | 
			
		||||
        } else if (type == "account")
 | 
			
		||||
        }
 | 
			
		||||
        if (type == "account")
 | 
			
		||||
            return Utils::accountPhoto(lrcInstance_, imageId, requestedSize);
 | 
			
		||||
        else if (type == "contact")
 | 
			
		||||
        if (type == "contact")
 | 
			
		||||
            return Utils::contactPhoto(lrcInstance_, imageId, requestedSize);
 | 
			
		||||
 | 
			
		||||
        qWarning() << Q_FUNC_INFO << "Missing valid prefix in the image url";
 | 
			
		||||
 | 
			
		||||
@ -77,6 +77,7 @@ void
 | 
			
		||||
AvatarRegistry::onProfileUpdated(const QString& uri)
 | 
			
		||||
{
 | 
			
		||||
    auto& convInfo = lrcInstance_->getConversationFromPeerUri(uri);
 | 
			
		||||
    addOrUpdateImage(uri);
 | 
			
		||||
    if (convInfo.uid.isEmpty())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,10 +35,11 @@ IndexRangeFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
 | 
			
		||||
{
 | 
			
		||||
    auto index = sourceModel()->index(sourceRow, 0, sourceParent);
 | 
			
		||||
    bool predicate = true;
 | 
			
		||||
    bool enabled = sourceModel()->data(index, CallControl::Role::Enabled).toBool();
 | 
			
		||||
    if (filterRole() != Qt::DisplayRole) {
 | 
			
		||||
        predicate = sourceModel()->data(index, filterRole()).toInt() != 0;
 | 
			
		||||
    }
 | 
			
		||||
    return sourceRow <= max_ && sourceRow >= min_ && predicate;
 | 
			
		||||
    return sourceRow <= max_ && sourceRow >= min_ && predicate && enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@ -197,10 +198,12 @@ CallControlListModel::data(const QModelIndex& index, int role) const
 | 
			
		||||
    auto item = data_.at(index.row());
 | 
			
		||||
 | 
			
		||||
    switch (role) {
 | 
			
		||||
    case Role::ItemAction:
 | 
			
		||||
    case CallControl::Role::ItemAction:
 | 
			
		||||
        return QVariant::fromValue(item.itemAction);
 | 
			
		||||
    case Role::UrgentCount:
 | 
			
		||||
    case CallControl::Role::UrgentCount:
 | 
			
		||||
        return QVariant::fromValue(item.urgentCount);
 | 
			
		||||
    case CallControl::Role::Enabled:
 | 
			
		||||
        return QVariant::fromValue(item.enabled);
 | 
			
		||||
    }
 | 
			
		||||
    return QVariant();
 | 
			
		||||
}
 | 
			
		||||
@ -212,6 +215,7 @@ CallControlListModel::roleNames() const
 | 
			
		||||
    QHash<int, QByteArray> roles;
 | 
			
		||||
    roles[ItemAction] = "ItemAction";
 | 
			
		||||
    roles[UrgentCount] = "UrgentCount";
 | 
			
		||||
    roles[Enabled] = "Enabled";
 | 
			
		||||
    return roles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -232,6 +236,24 @@ CallControlListModel::setUrgentCount(QVariant item, int count)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CallControlListModel::setEnabled(QObject* obj, bool enabled)
 | 
			
		||||
{
 | 
			
		||||
    beginResetModel();
 | 
			
		||||
    auto it = std::find_if(data_.cbegin(), data_.cend(), [obj](const auto& item) {
 | 
			
		||||
        return item.itemAction == obj;
 | 
			
		||||
    });
 | 
			
		||||
    if (it != data_.cend()) {
 | 
			
		||||
        auto row = std::distance(data_.cbegin(), it);
 | 
			
		||||
        if (row >= rowCount())
 | 
			
		||||
            return;
 | 
			
		||||
        data_[row].enabled = enabled;
 | 
			
		||||
        auto idx = index(row, 0);
 | 
			
		||||
        Q_EMIT dataChanged(idx, idx);
 | 
			
		||||
    }
 | 
			
		||||
    endResetModel();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CallControlListModel::addItem(const CallControl::Item& item)
 | 
			
		||||
{
 | 
			
		||||
@ -264,15 +286,15 @@ CallOverlayModel::CallOverlayModel(LRCInstance* instance, QObject* parent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CallOverlayModel::addPrimaryControl(const QVariant& action)
 | 
			
		||||
CallOverlayModel::addPrimaryControl(const QVariant& action, bool enabled)
 | 
			
		||||
{
 | 
			
		||||
    primaryModel_->addItem(CallControl::Item {action.value<QObject*>()});
 | 
			
		||||
    primaryModel_->addItem(CallControl::Item {action.value<QObject*>(), enabled});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CallOverlayModel::addSecondaryControl(const QVariant& action)
 | 
			
		||||
CallOverlayModel::addSecondaryControl(const QVariant& action, bool enabled)
 | 
			
		||||
{
 | 
			
		||||
    secondaryModel_->addItem(CallControl::Item {action.value<QObject*>()});
 | 
			
		||||
    secondaryModel_->addItem(CallControl::Item {action.value<QObject*>(), enabled});
 | 
			
		||||
    setControlRanges();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -282,6 +304,13 @@ CallOverlayModel::setUrgentCount(QVariant row, int count)
 | 
			
		||||
    secondaryModel_->setUrgentCount(row, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CallOverlayModel::setEnabled(QObject* obj, bool enabled)
 | 
			
		||||
{
 | 
			
		||||
    primaryModel_->setEnabled(obj, enabled);
 | 
			
		||||
    secondaryModel_->setEnabled(obj, enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QVariant
 | 
			
		||||
CallOverlayModel::primaryModel()
 | 
			
		||||
{
 | 
			
		||||
@ -363,7 +392,8 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
 | 
			
		||||
void
 | 
			
		||||
CallOverlayModel::setControlRanges()
 | 
			
		||||
{
 | 
			
		||||
    auto count = secondaryModel_->rowCount();
 | 
			
		||||
    overflowModel_->setRange(0, overflowIndex_);
 | 
			
		||||
    overflowVisibleModel_->setRange(overflowIndex_, secondaryModel_->rowCount());
 | 
			
		||||
    overflowHiddenModel_->setRange(overflowIndex_ + 1, secondaryModel_->rowCount());
 | 
			
		||||
    overflowVisibleModel_->setRange(overflowIndex_, count);
 | 
			
		||||
    overflowHiddenModel_->setRange(overflowIndex_ + 1, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -36,12 +36,13 @@
 | 
			
		||||
 | 
			
		||||
namespace CallControl {
 | 
			
		||||
Q_NAMESPACE
 | 
			
		||||
enum Role { ItemAction = Qt::UserRole + 1, UrgentCount };
 | 
			
		||||
enum Role { ItemAction = Qt::UserRole + 1, UrgentCount, Enabled};
 | 
			
		||||
Q_ENUM_NS(Role)
 | 
			
		||||
 | 
			
		||||
struct Item
 | 
			
		||||
{
 | 
			
		||||
    QObject* itemAction;
 | 
			
		||||
    bool enabled {true};
 | 
			
		||||
    int urgentCount {0};
 | 
			
		||||
};
 | 
			
		||||
} // namespace CallControl
 | 
			
		||||
@ -106,6 +107,7 @@ public:
 | 
			
		||||
    QHash<int, QByteArray> roleNames() const override;
 | 
			
		||||
 | 
			
		||||
    void setUrgentCount(QVariant item, int count);
 | 
			
		||||
    void setEnabled(QObject* obj, bool enabled);
 | 
			
		||||
    void addItem(const CallControl::Item& item);
 | 
			
		||||
    void clearData();
 | 
			
		||||
 | 
			
		||||
@ -121,9 +123,10 @@ class CallOverlayModel : public QObject
 | 
			
		||||
public:
 | 
			
		||||
    CallOverlayModel(LRCInstance* instance, QObject* parent = nullptr);
 | 
			
		||||
 | 
			
		||||
    Q_INVOKABLE void addPrimaryControl(const QVariant& action);
 | 
			
		||||
    Q_INVOKABLE void addSecondaryControl(const QVariant& action);
 | 
			
		||||
    Q_INVOKABLE void addPrimaryControl(const QVariant& action, bool enabled);
 | 
			
		||||
    Q_INVOKABLE void addSecondaryControl(const QVariant& action, bool enabled);
 | 
			
		||||
    Q_INVOKABLE void setUrgentCount(QVariant item, int count);
 | 
			
		||||
    Q_INVOKABLE void setEnabled(QObject* obj, bool enabled);
 | 
			
		||||
    Q_INVOKABLE void clearControls();
 | 
			
		||||
 | 
			
		||||
    Q_INVOKABLE QVariant primaryModel();
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ Item {
 | 
			
		||||
    property string imageId
 | 
			
		||||
 | 
			
		||||
    readonly property string divider: '_'
 | 
			
		||||
    readonly property string baseProviderPrefix: 'image://avatarImage'
 | 
			
		||||
    readonly property string baseProviderPrefix: 'image://avatarimage'
 | 
			
		||||
    property string typePrefix: {
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case Avatar.Mode.Account:
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ import QtQuick.Controls
 | 
			
		||||
import QtQuick.Layouts
 | 
			
		||||
import Qt5Compat.GraphicalEffects
 | 
			
		||||
import net.jami.Constants 1.1
 | 
			
		||||
import "../mainview/components"
 | 
			
		||||
 | 
			
		||||
Popup {
 | 
			
		||||
    id: root
 | 
			
		||||
@ -28,64 +29,80 @@ Popup {
 | 
			
		||||
    property bool autoClose: true
 | 
			
		||||
    property alias backgroundColor: container.color
 | 
			
		||||
    property alias title: titleText.text
 | 
			
		||||
    property var popupContentLoader: containerSubContentLoader
 | 
			
		||||
    property var popupcontainerSubContentLoader: containerSubContentLoader
 | 
			
		||||
    property bool topLayoutVisible: true
 | 
			
		||||
    property alias popupContentLoadStatus: containerSubContentLoader.status
 | 
			
		||||
    property alias popupContent: containerSubContentLoader.sourceComponent
 | 
			
		||||
    property int popupContentPreferredHeight: 0
 | 
			
		||||
    property int popupContentPreferredWidth: 0
 | 
			
		||||
    property int popupContentMargins: 0
 | 
			
		||||
    property int popupContentMargins: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
    parent: Overlay.overlay
 | 
			
		||||
 | 
			
		||||
    // center in parent
 | 
			
		||||
    x: Math.round((parent.width - width) / 2)
 | 
			
		||||
    y: Math.round((parent.height - height) / 2)
 | 
			
		||||
 | 
			
		||||
    anchors.centerIn: parent
 | 
			
		||||
    modal: true
 | 
			
		||||
    padding: popupContentMargins
 | 
			
		||||
 | 
			
		||||
    padding: 0
 | 
			
		||||
 | 
			
		||||
    // A popup is invisible until opened.
 | 
			
		||||
    visible: false
 | 
			
		||||
    focus: true
 | 
			
		||||
    closePolicy: autoClose ? (Popup.CloseOnEscape | Popup.CloseOnPressOutside) : Popup.NoAutoClose
 | 
			
		||||
 | 
			
		||||
    Rectangle {
 | 
			
		||||
    contentItem: Control {
 | 
			
		||||
        id: container
 | 
			
		||||
 | 
			
		||||
        anchors.fill: parent
 | 
			
		||||
        property color color: JamiTheme.secondaryBackgroundColor
 | 
			
		||||
        padding: popupContentMargins
 | 
			
		||||
        anchors.margins: popupContentMargins
 | 
			
		||||
        anchors.centerIn: parent
 | 
			
		||||
 | 
			
		||||
        ColumnLayout {
 | 
			
		||||
            anchors.fill: parent
 | 
			
		||||
        background: Rectangle {
 | 
			
		||||
            id: bgRect
 | 
			
		||||
            radius: JamiTheme.modalPopupRadius
 | 
			
		||||
            color: container.color
 | 
			
		||||
            layer.enabled: true
 | 
			
		||||
            layer.effect: DropShadow {
 | 
			
		||||
                horizontalOffset: 3.0
 | 
			
		||||
                verticalOffset: 3.0
 | 
			
		||||
                radius: bgRect.radius * 4
 | 
			
		||||
                color: JamiTheme.shadowColor
 | 
			
		||||
                source: bgRect
 | 
			
		||||
                transparentBorder: true
 | 
			
		||||
                samples: radius + 1
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            spacing: 0
 | 
			
		||||
        contentItem: ColumnLayout {
 | 
			
		||||
            id: contentLayout
 | 
			
		||||
 | 
			
		||||
            Text {
 | 
			
		||||
                id: titleText
 | 
			
		||||
            RowLayout {
 | 
			
		||||
                Layout.preferredWidth: parent.width
 | 
			
		||||
                Layout.bottomMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
                visible: topLayoutVisible
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignTop | Qt.AlignLeft
 | 
			
		||||
                Layout.margins: text.length === 0 ? 0 : 10
 | 
			
		||||
                Label {
 | 
			
		||||
                    id: titleText
 | 
			
		||||
 | 
			
		||||
                Layout.preferredHeight: text.length === 0 ? 0 : contentHeight
 | 
			
		||||
                    Layout.alignment: Qt.AlignTop | Qt.AlignLeft
 | 
			
		||||
                    font.pointSize: JamiTheme.menuFontSize
 | 
			
		||||
                    color: JamiTheme.textColor
 | 
			
		||||
 | 
			
		||||
                font.pointSize: 12
 | 
			
		||||
                color: JamiTheme.textColor
 | 
			
		||||
 | 
			
		||||
                    visible: text.length > 0
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                JamiPushButton {
 | 
			
		||||
                    id: closeButton
 | 
			
		||||
                    Layout.alignment: Qt.AlignRight
 | 
			
		||||
                    imageColor: "grey"
 | 
			
		||||
                    normalColor: "transparent"
 | 
			
		||||
 | 
			
		||||
                    source: JamiResources.round_close_24dp_svg
 | 
			
		||||
                    onClicked: close()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Loader {
 | 
			
		||||
                id: containerSubContentLoader
 | 
			
		||||
 | 
			
		||||
                Layout.topMargin: popupContentMargins
 | 
			
		||||
                Layout.bottomMargin: popupContentMargins
 | 
			
		||||
                Layout.alignment: Qt.AlignCenter
 | 
			
		||||
                Layout.fillWidth: popupContentPreferredWidth === 0
 | 
			
		||||
                Layout.fillHeight: popupContentPreferredHeight === 0
 | 
			
		||||
                Layout.preferredHeight: popupContentPreferredHeight
 | 
			
		||||
                Layout.preferredWidth: popupContentPreferredWidth
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        radius: JamiTheme.modalPopupRadius
 | 
			
		||||
        color: JamiTheme.secondaryBackgroundColor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    background: Rectangle {
 | 
			
		||||
@ -102,19 +119,6 @@ Popup {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DropShadow {
 | 
			
		||||
        z: -1
 | 
			
		||||
        width: root.width
 | 
			
		||||
        height: root.height
 | 
			
		||||
        horizontalOffset: 3.0
 | 
			
		||||
        verticalOffset: 3.0
 | 
			
		||||
        radius: container.radius * 4
 | 
			
		||||
        color: JamiTheme.shadowColor
 | 
			
		||||
        source: container
 | 
			
		||||
        transparentBorder: true
 | 
			
		||||
        samples: radius + 1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enter: Transition {
 | 
			
		||||
        NumberAnimation {
 | 
			
		||||
            properties: "opacity"
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ SBSMessageBase {
 | 
			
		||||
    component JoinCallButton: PushButton {
 | 
			
		||||
        visible: root.isActive
 | 
			
		||||
        toolTipText: JamiStrings.joinCall
 | 
			
		||||
        preferredSize: 40
 | 
			
		||||
        preferredSize: visible ? 40 : 0
 | 
			
		||||
        imageColor: callLabel.color
 | 
			
		||||
        normalColor: "transparent"
 | 
			
		||||
        hoveredColor: Qt.rgba(255, 255, 255, 0.2)
 | 
			
		||||
@ -84,7 +84,7 @@ SBSMessageBase {
 | 
			
		||||
                }
 | 
			
		||||
                horizontalAlignment: Qt.AlignHCenter
 | 
			
		||||
 | 
			
		||||
                font.pixelSize: JamiTheme.emojiBubbleSize
 | 
			
		||||
                font.pointSize: JamiTheme.mediumFontSize
 | 
			
		||||
                font.hintingPreference: Font.PreferNoHinting
 | 
			
		||||
                font.bold: true
 | 
			
		||||
                renderType: Text.NativeRendering
 | 
			
		||||
 | 
			
		||||
@ -27,9 +27,6 @@ BaseModalDialog {
 | 
			
		||||
 | 
			
		||||
    signal accepted
 | 
			
		||||
 | 
			
		||||
    width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth)
 | 
			
		||||
    height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight)
 | 
			
		||||
 | 
			
		||||
    property string confirmLabel: ""
 | 
			
		||||
    property string textLabel: ""
 | 
			
		||||
 | 
			
		||||
@ -40,7 +37,7 @@ BaseModalDialog {
 | 
			
		||||
            id: labelAction
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: column.width - JamiTheme.preferredMarginSize * 2
 | 
			
		||||
            Layout.maximumWidth: root.width - JamiTheme.preferredMarginSize * 4
 | 
			
		||||
 | 
			
		||||
            color: JamiTheme.textColor
 | 
			
		||||
            text: root.textLabel
 | 
			
		||||
@ -55,8 +52,8 @@ BaseModalDialog {
 | 
			
		||||
 | 
			
		||||
        RowLayout {
 | 
			
		||||
            spacing: 16
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
            Layout.alignment: Qt.AlignCenter
 | 
			
		||||
            Layout.topMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            MaterialButton {
 | 
			
		||||
                id: primaryBtn
 | 
			
		||||
 | 
			
		||||
@ -30,17 +30,16 @@ Column {
 | 
			
		||||
    property int timestamp: Timestamp
 | 
			
		||||
    property string formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
 | 
			
		||||
    property string formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
 | 
			
		||||
    property int seq: MsgSeq.single//a changer par textlabel
 | 
			
		||||
    property int seq: MsgSeq.single
 | 
			
		||||
    property alias messageToSend: textLabel.text
 | 
			
		||||
 | 
			
		||||
    width: ListView.view ? ListView.view.width : 0
 | 
			
		||||
    spacing: 2
 | 
			
		||||
    topPadding: 12
 | 
			
		||||
    bottomPadding: 12
 | 
			
		||||
    height: timestampItem.height + textLabel.height
 | 
			
		||||
    spacing: 0
 | 
			
		||||
 | 
			
		||||
    ColumnLayout {
 | 
			
		||||
    Item {
 | 
			
		||||
        anchors.horizontalCenter: parent.horizontalCenter
 | 
			
		||||
        width: parent.width
 | 
			
		||||
        height: timestampItem.height + textLabel.height
 | 
			
		||||
 | 
			
		||||
        TimestampInfo {
 | 
			
		||||
            id: timestampItem
 | 
			
		||||
@ -49,46 +48,23 @@ Column {
 | 
			
		||||
            showTime: root.showTime
 | 
			
		||||
            formattedTime: root.formattedTime
 | 
			
		||||
            formattedDay: root.formattedDay
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
 | 
			
		||||
            anchors.horizontalCenter: parent.horizontalCenter
 | 
			
		||||
            anchors.top: parent.top
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Rectangle {
 | 
			
		||||
            id: msg
 | 
			
		||||
        Label {
 | 
			
		||||
            id: textLabel
 | 
			
		||||
 | 
			
		||||
            width: childrenRect.width
 | 
			
		||||
            height: JamiTheme.contactMessageAvatarSize + 12
 | 
			
		||||
            radius: JamiTheme.contactMessageAvatarSize / 2 + 6
 | 
			
		||||
            Layout.alignment: Qt.AlignCenter
 | 
			
		||||
            color: "transparent"
 | 
			
		||||
            border.width: 1
 | 
			
		||||
            border.color: CurrentConversation.isCoreDialog ? JamiTheme.messageInBgColor : CurrentConversation.color
 | 
			
		||||
            anchors.horizontalCenter: parent.horizontalCenter
 | 
			
		||||
            anchors.top: timestampItem.bottom
 | 
			
		||||
 | 
			
		||||
            RowLayout {
 | 
			
		||||
                anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
 | 
			
		||||
                Avatar {
 | 
			
		||||
                    Layout.leftMargin: 6
 | 
			
		||||
                    width: JamiTheme.contactMessageAvatarSize
 | 
			
		||||
                    height: JamiTheme.contactMessageAvatarSize
 | 
			
		||||
                    visible: ActionUri !== ""
 | 
			
		||||
                    imageId: ActionUri !== CurrentAccount.uri ? ActionUri : CurrentAccount.id
 | 
			
		||||
                    showPresenceIndicator: false
 | 
			
		||||
                    mode: ActionUri !== CurrentAccount.uri ? Avatar.Mode.Contact : Avatar.Mode.Account
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Label {
 | 
			
		||||
                    id: textLabel
 | 
			
		||||
 | 
			
		||||
                    Layout.rightMargin: 6
 | 
			
		||||
                    width: parent.width
 | 
			
		||||
                    text: Body
 | 
			
		||||
                    horizontalAlignment: Qt.AlignHCenter
 | 
			
		||||
                    font.pointSize: JamiTheme.contactEventPointSize
 | 
			
		||||
                    font.bold: true
 | 
			
		||||
                    color: JamiTheme.chatviewTextColor
 | 
			
		||||
                    textFormat: TextEdit.PlainText
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            text: Body
 | 
			
		||||
            horizontalAlignment: Qt.AlignHCenter
 | 
			
		||||
            font.pointSize: JamiTheme.smallFontSize
 | 
			
		||||
            color: JamiTheme.chatviewSecondaryInformationColor
 | 
			
		||||
            textFormat: TextEdit.PlainText
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    opacity: 0
 | 
			
		||||
 | 
			
		||||
@ -68,7 +68,8 @@ BaseModalDialog {
 | 
			
		||||
            id: daemonReconnectPopupTextLabel
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
 | 
			
		||||
            Layout.topMargin: preferredMargin
 | 
			
		||||
            Layout.maximumWidth: root.parent.width - 4 * JamiTheme.preferredMarginSize
 | 
			
		||||
            wrapMode: Text.Wrap
 | 
			
		||||
 | 
			
		||||
            text: connectionFailed ? JamiStrings.reconnectionFailed : JamiStrings.reconnectDaemon
 | 
			
		||||
            font.pointSize: JamiTheme.textFontSize + 2
 | 
			
		||||
 | 
			
		||||
@ -88,8 +88,11 @@ Loader {
 | 
			
		||||
                        target: parent
 | 
			
		||||
                        enabled: canOpen
 | 
			
		||||
                        onHoveredChanged: {
 | 
			
		||||
                            dataTransferItem.hoveredLink = enabled && hovered ?
 | 
			
		||||
                                        ("file:///" + Body) : ""
 | 
			
		||||
                            if (enabled && hovered) {
 | 
			
		||||
                                dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(Body)
 | 
			
		||||
                            } else {
 | 
			
		||||
                                dataTransferItem.hoveredLink = ""
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        cursorShape: enabled ?
 | 
			
		||||
                                         Qt.PointingHandCursor :
 | 
			
		||||
@ -199,10 +202,12 @@ Loader {
 | 
			
		||||
                                                 Qt.PointingHandCursor :
 | 
			
		||||
                                                 Qt.ArrowCursor
 | 
			
		||||
                                onClicked: function (mouse) {
 | 
			
		||||
                                    dataTransferItem.hoveredLink = canOpen ?
 | 
			
		||||
                                                ("file:///" + Body) : ""
 | 
			
		||||
                                    if (dataTransferItem.hoveredLink)
 | 
			
		||||
                                    if (canOpen) {
 | 
			
		||||
                                        dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(Body)
 | 
			
		||||
                                        Qt.openUrlExternally(new Url(dataTransferItem.hoveredLink))
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        dataTransferItem.hoveredLink = ""
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
@ -303,7 +308,7 @@ Loader {
 | 
			
		||||
                            antialiasing: true
 | 
			
		||||
                            autoTransform: true
 | 
			
		||||
                            asynchronous: true
 | 
			
		||||
                            source: "file:///" + Body
 | 
			
		||||
                            source: UtilsAdapter.urlFromLocalPath(Body)
 | 
			
		||||
                            property real aspectRatio: implicitWidth / implicitHeight
 | 
			
		||||
                            property real adjustedWidth: Math.min(maxSize,
 | 
			
		||||
                                                                  Math.max(minSize,
 | 
			
		||||
@ -348,7 +353,7 @@ Loader {
 | 
			
		||||
                            antialiasing: true
 | 
			
		||||
                            autoTransform: true
 | 
			
		||||
                            asynchronous: true
 | 
			
		||||
                            source: Body !== undefined ? "file:///" + Body : ''
 | 
			
		||||
                            source: Body !== undefined ? UtilsAdapter.urlFromLocalPath(Body) : ''
 | 
			
		||||
 | 
			
		||||
                            // The sourceSize represents the maximum source dimensions.
 | 
			
		||||
                            // This should not be a dynamic binding, as property changes
 | 
			
		||||
 | 
			
		||||
@ -33,17 +33,15 @@ BaseModalDialog {
 | 
			
		||||
 | 
			
		||||
    title: JamiStrings.deleteAccount
 | 
			
		||||
 | 
			
		||||
    width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth)
 | 
			
		||||
    height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight)
 | 
			
		||||
 | 
			
		||||
    popupContent: ColumnLayout {
 | 
			
		||||
        id: deleteAccountContentColumnLayout
 | 
			
		||||
        anchors.centerIn: parent
 | 
			
		||||
 | 
			
		||||
        Label {
 | 
			
		||||
            id: labelDeletion
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: deleteAccountContentColumnLayout.width - JamiTheme.preferredMarginSize * 2
 | 
			
		||||
            Layout.maximumWidth: root.width - 4*JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            color: JamiTheme.textColor
 | 
			
		||||
            text: JamiStrings.confirmDeleteQuestion
 | 
			
		||||
@ -51,8 +49,6 @@ BaseModalDialog {
 | 
			
		||||
            font.pointSize: JamiTheme.textFontSize
 | 
			
		||||
            font.kerning: true
 | 
			
		||||
 | 
			
		||||
            horizontalAlignment: Text.AlignHCenter
 | 
			
		||||
            verticalAlignment: Text.AlignVCenter
 | 
			
		||||
            wrapMode: Text.Wrap
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -60,7 +56,6 @@ BaseModalDialog {
 | 
			
		||||
            id: labelBestId
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: deleteAccountContentColumnLayout.width - JamiTheme.preferredMarginSize * 2
 | 
			
		||||
 | 
			
		||||
            color: JamiTheme.textColor
 | 
			
		||||
            text: bestName
 | 
			
		||||
@ -68,9 +63,6 @@ BaseModalDialog {
 | 
			
		||||
            font.pointSize: JamiTheme.textFontSize
 | 
			
		||||
            font.kerning: true
 | 
			
		||||
            font.bold: true
 | 
			
		||||
 | 
			
		||||
            horizontalAlignment: Text.AlignHCenter
 | 
			
		||||
            verticalAlignment: Text.AlignVCenter
 | 
			
		||||
            wrapMode: Text.Wrap
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -78,7 +70,7 @@ BaseModalDialog {
 | 
			
		||||
            id: labelAccountHash
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: deleteAccountContentColumnLayout.width - JamiTheme.preferredMarginSize * 2
 | 
			
		||||
            Layout.preferredWidth: root.width - 4*JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            color: JamiTheme.textColor
 | 
			
		||||
            text: accountId
 | 
			
		||||
@ -87,7 +79,6 @@ BaseModalDialog {
 | 
			
		||||
            font.kerning: true
 | 
			
		||||
 | 
			
		||||
            horizontalAlignment: Text.AlignHCenter
 | 
			
		||||
            verticalAlignment: Text.AlignVCenter
 | 
			
		||||
            wrapMode: Text.Wrap
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -97,7 +88,7 @@ BaseModalDialog {
 | 
			
		||||
            visible: !isSIP
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: deleteAccountContentColumnLayout.width - JamiTheme.preferredMarginSize * 2
 | 
			
		||||
            Layout.preferredWidth: root.width - 4*JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            text: JamiStrings.deleteAccountInfos
 | 
			
		||||
 | 
			
		||||
@ -120,6 +111,7 @@ BaseModalDialog {
 | 
			
		||||
                id: btnDelete
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
                Layout.topMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
                preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
 | 
			
		||||
                buttontextHeightMargin: JamiTheme.buttontextHeightMargin
 | 
			
		||||
@ -164,6 +156,7 @@ BaseModalDialog {
 | 
			
		||||
                id: btnCancel
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
                Layout.topMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
                preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
 | 
			
		||||
                buttontextHeightMargin: JamiTheme.buttontextHeightMargin
 | 
			
		||||
 | 
			
		||||
@ -85,10 +85,12 @@ BaseView {
 | 
			
		||||
        clip: true
 | 
			
		||||
        required property bool isMinorPane
 | 
			
		||||
        onWidthChanged: {
 | 
			
		||||
            if (!isSinglePane && ((isRTL && !isMinorPane) || (!isRTL && isMinorPane)))
 | 
			
		||||
            if (!isSinglePane && isMinorPane)
 | 
			
		||||
                previousMinorPaneWidth = width
 | 
			
		||||
            if (!isSinglePane && ((isRTL && isMinorPane) || (!isRTL && !isMinorPane)))
 | 
			
		||||
            if (!isSinglePane && !isMinorPane)
 | 
			
		||||
                previousMajorPaneWidth = width
 | 
			
		||||
            if (isMinorPane)
 | 
			
		||||
                JamiTheme.currentLeftPaneWidth = width
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Connections {
 | 
			
		||||
@ -101,8 +103,9 @@ BaseView {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SplitView.minimumWidth: isSinglePane ? viewNode.width : (isMinorPane && !isRTL ? minorPaneMinWidth : majorPaneMinWidth)
 | 
			
		||||
        SplitView.maximumWidth: isSinglePane ? viewNode.width : viewNode.width - (isMinorPane && !isRTL ? majorPaneMinWidth : minorPaneMinWidth)
 | 
			
		||||
        SplitView.preferredWidth: isMinorPane && !isRTL ? minorPaneMinWidth : majorPaneMinWidth
 | 
			
		||||
        SplitView.minimumWidth: isSinglePane ? undefined : (isMinorPane ? minorPaneMinWidth : majorPaneMinWidth)
 | 
			
		||||
        SplitView.maximumWidth: isSinglePane || !isMinorPane ? undefined : Math.abs(viewNode.width - majorPaneMinWidth)
 | 
			
		||||
        SplitView.preferredWidth: isSinglePane || !isMinorPane ? undefined : JamiTheme.currentLeftPaneWidth
 | 
			
		||||
        SplitView.fillWidth: !isMinorPane || isSinglePane
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -25,19 +25,13 @@ import net.jami.Constants 1.1
 | 
			
		||||
BaseModalDialog {
 | 
			
		||||
    id: root
 | 
			
		||||
 | 
			
		||||
    width: 488
 | 
			
		||||
    height: 256
 | 
			
		||||
    width: JamiTheme.secondaryDialogDimension
 | 
			
		||||
 | 
			
		||||
    property var previousBodies: undefined
 | 
			
		||||
 | 
			
		||||
    popupContent: Item {
 | 
			
		||||
        id: rect
 | 
			
		||||
 | 
			
		||||
        width: root.width
 | 
			
		||||
 | 
			
		||||
        JamiListView {
 | 
			
		||||
            anchors.fill: parent
 | 
			
		||||
            anchors.margins: JamiTheme.preferredMarginSize
 | 
			
		||||
    popupContent: JamiListView {
 | 
			
		||||
            width: root.width - 4 * JamiTheme.preferredMarginSize
 | 
			
		||||
            height: Math.min(count * 50, 150)
 | 
			
		||||
 | 
			
		||||
            model: root.previousBodies
 | 
			
		||||
 | 
			
		||||
@ -79,17 +73,4 @@ BaseModalDialog {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        PushButton {
 | 
			
		||||
            id: btnCancel
 | 
			
		||||
            imageColor: "grey"
 | 
			
		||||
            normalColor: "transparent"
 | 
			
		||||
            anchors.right: parent.right
 | 
			
		||||
            anchors.top: parent.top
 | 
			
		||||
            anchors.topMargin: 10
 | 
			
		||||
            anchors.rightMargin: 10
 | 
			
		||||
            source: JamiResources.round_close_24dp_svg
 | 
			
		||||
            onClicked: close()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ Popup {
 | 
			
		||||
    background.visible: false
 | 
			
		||||
    parent: Overlay.overlay
 | 
			
		||||
 | 
			
		||||
    property var emojiReaction
 | 
			
		||||
    property var reactions
 | 
			
		||||
    property string msgId
 | 
			
		||||
 | 
			
		||||
    // center in parent
 | 
			
		||||
@ -42,6 +42,7 @@ Popup {
 | 
			
		||||
    padding: 0
 | 
			
		||||
 | 
			
		||||
    visible: false
 | 
			
		||||
    focus: true
 | 
			
		||||
    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
 | 
			
		||||
 | 
			
		||||
    Rectangle {
 | 
			
		||||
@ -88,9 +89,9 @@ Popup {
 | 
			
		||||
                spacing: 15
 | 
			
		||||
                Layout.preferredWidth: 400
 | 
			
		||||
                Layout.preferredHeight: childrenRect.height + 30 < 700 ? childrenRect.height + 30 : 700
 | 
			
		||||
                model: Object.entries(emojiReaction)
 | 
			
		||||
                model: Object.entries(reactions)
 | 
			
		||||
                clip: true
 | 
			
		||||
                property int modelCount: Object.entries(emojiReaction).length
 | 
			
		||||
                property int modelCount: Object.entries(reactions).length
 | 
			
		||||
 | 
			
		||||
                delegate: RowLayout {
 | 
			
		||||
                    width: parent.width
 | 
			
		||||
 | 
			
		||||
@ -24,19 +24,24 @@ import net.jami.Constants 1.1
 | 
			
		||||
Item {
 | 
			
		||||
    id: root
 | 
			
		||||
 | 
			
		||||
    property var emojiReaction
 | 
			
		||||
    property real contentHeight: bubble.height
 | 
			
		||||
    property real contentWidth: bubble.width
 | 
			
		||||
    property var emojiTexts: ownEmojiList
 | 
			
		||||
    width: reactionBubble.width
 | 
			
		||||
 | 
			
		||||
    property var reactions
 | 
			
		||||
    property real contentHeight: reactionBubble.height
 | 
			
		||||
    property real contentWidth: reactionBubble.width
 | 
			
		||||
    property color borderColor: undefined
 | 
			
		||||
    property int maxWidth: JamiTheme.defaulMaxWidthReaction
 | 
			
		||||
 | 
			
		||||
    visible: emojis.length && Body !== ""
 | 
			
		||||
 | 
			
		||||
    property string emojis: {
 | 
			
		||||
        if (reactions === undefined)
 | 
			
		||||
            return [];
 | 
			
		||||
        var space = "";
 | 
			
		||||
        var emojiList = [];
 | 
			
		||||
        var emojiNumberList = [];
 | 
			
		||||
        for (const reactions of Object.entries(emojiReaction)) {
 | 
			
		||||
            var authorEmojiList = reactions[1];
 | 
			
		||||
        for (const reaction of Object.entries(reactions)) {
 | 
			
		||||
            var authorEmojiList = reaction[1];
 | 
			
		||||
            for (var emojiIndex in authorEmojiList) {
 | 
			
		||||
                var emoji = authorEmojiList[emojiIndex];
 | 
			
		||||
                if (emojiList.includes(emoji)) {
 | 
			
		||||
@ -60,12 +65,14 @@ Item {
 | 
			
		||||
        return cur;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    property var ownEmojiList: {
 | 
			
		||||
    property var ownEmojis: {
 | 
			
		||||
        if (reactions === undefined)
 | 
			
		||||
            return [];
 | 
			
		||||
        var list = [];
 | 
			
		||||
        var index = 0;
 | 
			
		||||
        for (const reactions of Object.entries(emojiReaction)) {
 | 
			
		||||
            var authorUri = reactions[0];
 | 
			
		||||
            var authorEmojiList = reactions[1];
 | 
			
		||||
        for (const reaction of Object.entries(reactions)) {
 | 
			
		||||
            var authorUri = reaction[0];
 | 
			
		||||
            var authorEmojiList = reaction[1];
 | 
			
		||||
            if (CurrentAccount.uri === authorUri) {
 | 
			
		||||
                for (var emojiIndex in authorEmojiList) {
 | 
			
		||||
                    list[index] = authorEmojiList[emojiIndex];
 | 
			
		||||
@ -77,36 +84,30 @@ Item {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO:
 | 
			
		||||
 | 
			
		||||
    // -order emojis based on the timestamp of the reaction and/or the quantity of emojis
 | 
			
		||||
    Rectangle {
 | 
			
		||||
        id: bubble
 | 
			
		||||
        id: reactionBubble
 | 
			
		||||
 | 
			
		||||
        color: JamiTheme.emojiReactBubbleBgColor
 | 
			
		||||
        width: textEmojis.width + 6
 | 
			
		||||
        height: textEmojis.height + 6
 | 
			
		||||
        radius: 10
 | 
			
		||||
 | 
			
		||||
        Text {
 | 
			
		||||
            id: textEmojis
 | 
			
		||||
 | 
			
		||||
            anchors.margins: 10
 | 
			
		||||
            anchors.centerIn: bubble
 | 
			
		||||
            font.pointSize: JamiTheme.emojiReactSize
 | 
			
		||||
            color: JamiTheme.chatviewTextColor
 | 
			
		||||
            text: root.emojis
 | 
			
		||||
        }
 | 
			
		||||
        width: textEmojis.width + 10
 | 
			
		||||
        height: textEmojis.height + 10
 | 
			
		||||
        anchors.centerIn: textEmojis
 | 
			
		||||
        radius: 5
 | 
			
		||||
        border.color: root.borderColor
 | 
			
		||||
        border.width: 1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DropShadow {
 | 
			
		||||
        z: -1
 | 
			
		||||
    Text {
 | 
			
		||||
        id: textEmojis
 | 
			
		||||
        anchors.margins: 10
 | 
			
		||||
        anchors.centerIn: root
 | 
			
		||||
 | 
			
		||||
        width: bubble.width
 | 
			
		||||
        height: bubble.height
 | 
			
		||||
        horizontalOffset: 3.0
 | 
			
		||||
        verticalOffset: 3.0
 | 
			
		||||
        radius: bubble.radius * 4
 | 
			
		||||
        color: JamiTheme.shadowColor
 | 
			
		||||
        source: bubble
 | 
			
		||||
        transparentBorder: true
 | 
			
		||||
        samples: radius + 1
 | 
			
		||||
        font.pointSize: JamiTheme.emojiReactSize
 | 
			
		||||
        color: JamiTheme.chatviewTextColor
 | 
			
		||||
        text: root.emojis
 | 
			
		||||
        width: Math.min(implicitWidth,root.maxWidth)
 | 
			
		||||
        wrapMode: Text.Wrap
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,7 @@ Column {
 | 
			
		||||
    ColumnLayout {
 | 
			
		||||
 | 
			
		||||
        width: parent.width
 | 
			
		||||
        spacing: 0
 | 
			
		||||
 | 
			
		||||
        TimestampInfo {
 | 
			
		||||
            id: timestampItem
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										75
									
								
								src/app/commoncomponents/HeaderToggleSwitch.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,75 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2022-2023 Savoir-faire Linux Inc.
 | 
			
		||||
 * Author: Xavier Jouslin <xavier.jouslindenoray@savoirfairelinux.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
import QtQuick
 | 
			
		||||
import QtQuick.Controls
 | 
			
		||||
import QtQuick.Layouts
 | 
			
		||||
import Qt.labs.platform
 | 
			
		||||
import net.jami.Models 1.1
 | 
			
		||||
import net.jami.Adapters 1.1
 | 
			
		||||
import net.jami.Constants 1.1
 | 
			
		||||
 | 
			
		||||
RowLayout {
 | 
			
		||||
    id: root
 | 
			
		||||
    property string labelText: ""
 | 
			
		||||
    property int widthOfSwitch: 50
 | 
			
		||||
    property int heightOfSwitch: 10
 | 
			
		||||
 | 
			
		||||
    property string tooltipText: ""
 | 
			
		||||
 | 
			
		||||
    property alias toggleSwitch: autoupdate
 | 
			
		||||
    property alias checked: autoupdate.checked
 | 
			
		||||
 | 
			
		||||
    signal switchToggled
 | 
			
		||||
    Layout.alignment: Qt.AlignRight
 | 
			
		||||
    JamiSwitch {
 | 
			
		||||
        id: autoupdate
 | 
			
		||||
        Layout.alignment: Qt.AlignLeft
 | 
			
		||||
 | 
			
		||||
        Layout.preferredWidth: widthOfSwitch
 | 
			
		||||
 | 
			
		||||
        hoverEnabled: true
 | 
			
		||||
        toolTipText: tooltipText
 | 
			
		||||
 | 
			
		||||
        Accessible.role: Accessible.Button
 | 
			
		||||
        Accessible.name: JamiStrings.autoUpdate
 | 
			
		||||
        Accessible.description: root.tooltipText
 | 
			
		||||
 | 
			
		||||
        onToggled: switchToggled()
 | 
			
		||||
    }
 | 
			
		||||
    Text {
 | 
			
		||||
        id: description
 | 
			
		||||
        Layout.rightMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
        text: JamiStrings.autoUpdate
 | 
			
		||||
        font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
 | 
			
		||||
        visible: labelText !== ""
 | 
			
		||||
        font.kerning: true
 | 
			
		||||
        wrapMode: Text.WordWrap
 | 
			
		||||
        verticalAlignment: Text.AlignVCenter
 | 
			
		||||
 | 
			
		||||
        color: JamiTheme.textColor
 | 
			
		||||
    }
 | 
			
		||||
    TapHandler {
 | 
			
		||||
        target: parent
 | 
			
		||||
        enabled: parent.visible
 | 
			
		||||
        onTapped: function onTapped(eventPoint) {
 | 
			
		||||
            // switchToggled should be emitted as onToggled is not called (because it's only called if the user click on the switch)
 | 
			
		||||
            autoupdate.toggle();
 | 
			
		||||
            switchToggled();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -23,151 +23,188 @@ import net.jami.Adapters 1.1
 | 
			
		||||
import net.jami.Constants 1.1
 | 
			
		||||
 | 
			
		||||
Item {
 | 
			
		||||
    id: root
 | 
			
		||||
    id: jamiId
 | 
			
		||||
 | 
			
		||||
    property alias backgroundColor: outerRect.color
 | 
			
		||||
    property bool slimDisplay: true
 | 
			
		||||
    property color backgroundColor: JamiTheme.welcomeBlockColor
 | 
			
		||||
    property color contentColor: JamiTheme.tintedBlue
 | 
			
		||||
    property bool centered: true
 | 
			
		||||
    height: getHeight()
 | 
			
		||||
 | 
			
		||||
    width: childrenRect.width
 | 
			
		||||
    height: controlsLayout.height + usernameTextEdit.height + 2 * JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
    // Background rounded rectangle.
 | 
			
		||||
    Rectangle {
 | 
			
		||||
        id: outerRect
 | 
			
		||||
        anchors.fill: parent
 | 
			
		||||
        radius: 20
 | 
			
		||||
        color: JamiTheme.secondaryBackgroundColor
 | 
			
		||||
    function getHeight() {
 | 
			
		||||
        return outerRow.height;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Logo masked by outerRect.
 | 
			
		||||
    Item {
 | 
			
		||||
        anchors.fill: outerRect
 | 
			
		||||
        layer.enabled: true
 | 
			
		||||
        layer.effect: OpacityMask {
 | 
			
		||||
            maskSource: outerRect
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Rectangle {
 | 
			
		||||
            id: logoRect
 | 
			
		||||
            width: 97 + radius
 | 
			
		||||
            height: 40
 | 
			
		||||
            color: JamiTheme.mainColor
 | 
			
		||||
            radius: 20
 | 
			
		||||
            anchors.top: parent.top
 | 
			
		||||
            anchors.left: parent.left
 | 
			
		||||
            anchors.leftMargin: -radius
 | 
			
		||||
 | 
			
		||||
            ResponsiveImage {
 | 
			
		||||
                id: jamiIdLogo
 | 
			
		||||
                anchors.horizontalCenter: parent.horizontalCenter
 | 
			
		||||
                // Adjust offset for parent masking margin.
 | 
			
		||||
                anchors.horizontalCenterOffset: parent.radius / 2
 | 
			
		||||
                anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
                width: JamiTheme.jamiIdLogoWidth
 | 
			
		||||
                height: JamiTheme.jamiIdLogoHeight
 | 
			
		||||
                source: JamiResources.jamiid_svg
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ColumnLayout {
 | 
			
		||||
        id: columnLayout
 | 
			
		||||
 | 
			
		||||
        spacing: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
        RowLayout {
 | 
			
		||||
            id: controlsLayout
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
 | 
			
		||||
            Layout.topMargin: JamiTheme.pushButtonMargin / 2
 | 
			
		||||
            Layout.rightMargin: JamiTheme.pushButtonMargin
 | 
			
		||||
            Layout.preferredHeight: childrenRect.height
 | 
			
		||||
 | 
			
		||||
            JamiIdControlButton {
 | 
			
		||||
                id: btnEdit
 | 
			
		||||
                visible: CurrentAccount.registeredName === ""
 | 
			
		||||
                border.color: enabled ? JamiTheme.buttonTintedBlue : JamiTheme.buttonTintedBlack
 | 
			
		||||
                imageColor: enabled ? JamiTheme.buttonTintedBlue : JamiTheme.buttonTintedBlack
 | 
			
		||||
                enabled: {
 | 
			
		||||
                    if (!usernameTextEdit.editMode)
 | 
			
		||||
                        return true;
 | 
			
		||||
                    switch (usernameTextEdit.nameRegistrationState) {
 | 
			
		||||
                    case UsernameTextEdit.NameRegistrationState.BLANK:
 | 
			
		||||
                    case UsernameTextEdit.NameRegistrationState.FREE:
 | 
			
		||||
                        return true;
 | 
			
		||||
                    case UsernameTextEdit.NameRegistrationState.SEARCHING:
 | 
			
		||||
                    case UsernameTextEdit.NameRegistrationState.INVALID:
 | 
			
		||||
                    case UsernameTextEdit.NameRegistrationState.TAKEN:
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                source: usernameTextEdit.editMode ? JamiResources.check_black_24dp_svg : JamiResources.round_edit_24dp_svg
 | 
			
		||||
                toolTipText: JamiStrings.chooseUsername
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    if (usernameTextEdit.readOnly) {
 | 
			
		||||
                        usernameTextEdit.startEditing();
 | 
			
		||||
                        usernameTextEdit.readOnly = false;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        usernameTextEdit.accepted();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            JamiIdControlButton {
 | 
			
		||||
                id: btnCopy
 | 
			
		||||
                source: JamiResources.content_copy_24dp_svg
 | 
			
		||||
                toolTipText: JamiStrings.copy
 | 
			
		||||
                onClicked: UtilsAdapter.setClipboardText(usernameTextEdit.staticText)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            JamiIdControlButton {
 | 
			
		||||
                id: btnShare
 | 
			
		||||
                source: JamiResources.share_24dp_svg
 | 
			
		||||
                toolTipText: JamiStrings.share
 | 
			
		||||
                onClicked: viewCoordinator.presentDialog(appWindow, "mainview/components/WelcomePageQrDialog.qml")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            JamiIdControlButton {
 | 
			
		||||
                id: btnId
 | 
			
		||||
                source: JamiResources.key_black_24dp_svg
 | 
			
		||||
                visible: CurrentAccount.registeredName !== ""
 | 
			
		||||
                toolTipText: JamiStrings.identifierURI
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    if (clicked) {
 | 
			
		||||
                        usernameTextEdit.staticText = CurrentAccount.uri;
 | 
			
		||||
                        btnId.toolTipText = JamiStrings.identifierRegisterName;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        usernameTextEdit.staticText = CurrentAccount.registeredName;
 | 
			
		||||
                        btnId.toolTipText = JamiStrings.identifierURI;
 | 
			
		||||
                    }
 | 
			
		||||
                    clicked = !clicked;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        UsernameTextEdit {
 | 
			
		||||
            id: usernameTextEdit
 | 
			
		||||
 | 
			
		||||
            Layout.preferredWidth: 330
 | 
			
		||||
            Layout.preferredHeight: implicitHeight + JamiTheme.preferredMarginSize
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
 | 
			
		||||
            Layout.leftMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
            Layout.rightMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
            fontPixelSize: JamiTheme.jamiIdFontSize
 | 
			
		||||
            editMode: false
 | 
			
		||||
            isPersistent: false
 | 
			
		||||
            readOnly: true
 | 
			
		||||
 | 
			
		||||
            onAccepted: {
 | 
			
		||||
    Connections {
 | 
			
		||||
        target: CurrentAccount
 | 
			
		||||
        function onIdChanged(id) {
 | 
			
		||||
            if (!usernameTextEdit.readOnly) {
 | 
			
		||||
                usernameTextEdit.readOnly = true;
 | 
			
		||||
                if (dynamicText === '') {
 | 
			
		||||
                    return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RowLayout {
 | 
			
		||||
        id: outerRow
 | 
			
		||||
        anchors.horizontalCenter: jamiId.centered ? parent.horizontalCenter : undefined
 | 
			
		||||
        anchors.left: jamiId.centered ? undefined : parent.left
 | 
			
		||||
        spacing: 2
 | 
			
		||||
 | 
			
		||||
        RoundedBorderRectangle {
 | 
			
		||||
            id: leftRect
 | 
			
		||||
            fillColor: jamiId.backgroundColor
 | 
			
		||||
            Layout.preferredWidth: childrenRect.width
 | 
			
		||||
            Layout.maximumWidth: jamiId.width - rightRect.width
 | 
			
		||||
            Layout.preferredHeight: childrenRect.height
 | 
			
		||||
            radius: {
 | 
			
		||||
                "tl": 5,
 | 
			
		||||
                "tr": 0,
 | 
			
		||||
                "br": 0,
 | 
			
		||||
                "bl": 5
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            RowLayout {
 | 
			
		||||
                anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
 | 
			
		||||
                ResponsiveImage {
 | 
			
		||||
                    id: jamiIdLogoImage
 | 
			
		||||
                    Layout.preferredHeight: 40
 | 
			
		||||
                    containerHeight: 40
 | 
			
		||||
                    containerWidth: 40
 | 
			
		||||
                    Layout.leftMargin: JamiTheme.pushButtonMargins
 | 
			
		||||
                    source: JamiResources.jami_id_logo_svg
 | 
			
		||||
                    color: jamiId.contentColor
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                UsernameTextEdit {
 | 
			
		||||
                    id: usernameTextEdit
 | 
			
		||||
                    visible: !readOnly
 | 
			
		||||
                    Layout.preferredHeight: 40
 | 
			
		||||
                    Layout.alignment: Qt.AlignVCenter
 | 
			
		||||
                    textColor: jamiId.contentColor
 | 
			
		||||
                    fontPixelSize: staticText.length > 16 || dynamicText.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.bigFontSize
 | 
			
		||||
                    editMode: false
 | 
			
		||||
                    isPersistent: false
 | 
			
		||||
                    readOnly: true
 | 
			
		||||
 | 
			
		||||
                    onAccepted: {
 | 
			
		||||
                        usernameTextEdit.readOnly = true;
 | 
			
		||||
                        if (dynamicText === '') {
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        var dlg = viewCoordinator.presentDialog(appWindow, "settingsview/components/NameRegistrationDialog.qml", {
 | 
			
		||||
                                "registeredName": dynamicText
 | 
			
		||||
                            });
 | 
			
		||||
                        dlg.accepted.connect(function () {
 | 
			
		||||
                                usernameTextEdit.nameRegistrationState = UsernameTextEdit.NameRegistrationState.BLANK;
 | 
			
		||||
                            });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Label{
 | 
			
		||||
                    id: usernameLabel
 | 
			
		||||
                    visible: usernameTextEdit.readOnly
 | 
			
		||||
                    Layout.alignment: Qt.AlignVCenter
 | 
			
		||||
                    Layout.rightMargin: JamiTheme.pushButtonMargins
 | 
			
		||||
                    Layout.maximumWidth: leftRect.width - 50
 | 
			
		||||
                    elide: Text.ElideRight
 | 
			
		||||
                    color: jamiId.contentColor
 | 
			
		||||
                    font.pixelSize : text.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.bigFontSize
 | 
			
		||||
                    property string registeredName: CurrentAccount.registeredName
 | 
			
		||||
                    property string infohash: CurrentAccount.uri
 | 
			
		||||
                    text: registeredName ? registeredName : infohash
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RoundedBorderRectangle {
 | 
			
		||||
            id: rightRect
 | 
			
		||||
            fillColor: jamiId.backgroundColor
 | 
			
		||||
            Layout.preferredWidth: childrenRect.width + 2 * JamiTheme.pushButtonMargins
 | 
			
		||||
 | 
			
		||||
            Layout.preferredHeight: leftRect.height
 | 
			
		||||
            radius: {
 | 
			
		||||
                "tl": 0,
 | 
			
		||||
                "tr": 5,
 | 
			
		||||
                "br": 5,
 | 
			
		||||
                "bl": 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            RowLayout {
 | 
			
		||||
                id: controlsLayout
 | 
			
		||||
 | 
			
		||||
                height: childrenRect.height
 | 
			
		||||
                anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
                anchors.rightMargin: JamiTheme.pushButtonMargins
 | 
			
		||||
                anchors.leftMargin: JamiTheme.pushButtonMargins
 | 
			
		||||
                anchors.horizontalCenter: parent.horizontalCenter
 | 
			
		||||
 | 
			
		||||
                JamiIdControlButton {
 | 
			
		||||
                    id: btnEdit
 | 
			
		||||
                    anchors.leftMargin: JamiTheme.pushButtonMargins
 | 
			
		||||
                    visible: CurrentAccount.registeredName === ""
 | 
			
		||||
                    border.color: enabled ? jamiId.contentColor : JamiTheme.buttonTintedBlack
 | 
			
		||||
                    imageColor: enabled ? jamiId.contentColor : JamiTheme.buttonTintedBlack
 | 
			
		||||
                    enabled: {
 | 
			
		||||
                        if (!usernameTextEdit.editMode)
 | 
			
		||||
                            return true;
 | 
			
		||||
                        switch (usernameTextEdit.nameRegistrationState) {
 | 
			
		||||
                        case UsernameTextEdit.NameRegistrationState.BLANK:
 | 
			
		||||
                        case UsernameTextEdit.NameRegistrationState.FREE:
 | 
			
		||||
                            return true;
 | 
			
		||||
                        case UsernameTextEdit.NameRegistrationState.SEARCHING:
 | 
			
		||||
                        case UsernameTextEdit.NameRegistrationState.INVALID:
 | 
			
		||||
                        case UsernameTextEdit.NameRegistrationState.TAKEN:
 | 
			
		||||
                            return false;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    source: usernameTextEdit.editMode ? JamiResources.check_black_24dp_svg : JamiResources.round_edit_24dp_svg
 | 
			
		||||
                    toolTipText: JamiStrings.chooseUsername
 | 
			
		||||
                    onClicked: {
 | 
			
		||||
                        if (usernameTextEdit.readOnly) {
 | 
			
		||||
                            usernameTextEdit.startEditing();
 | 
			
		||||
                            usernameTextEdit.readOnly = false;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            usernameTextEdit.accepted();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                JamiIdControlButton {
 | 
			
		||||
                    id: btnCopy
 | 
			
		||||
                    anchors.leftMargin: JamiTheme.pushButtonMargins
 | 
			
		||||
                    source: JamiResources.content_copy_24dp_svg
 | 
			
		||||
                    border.color: "transparent"
 | 
			
		||||
                    toolTipText: JamiStrings.copy
 | 
			
		||||
                    onClicked: UtilsAdapter.setClipboardText(usernameTextEdit.staticText)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                JamiIdControlButton {
 | 
			
		||||
                    id: btnShare
 | 
			
		||||
                    source: JamiResources.share_24dp_svg
 | 
			
		||||
                    border.color: "transparent"
 | 
			
		||||
                    toolTipText: JamiStrings.share
 | 
			
		||||
                    onClicked: viewCoordinator.presentDialog(appWindow, "mainview/components/WelcomePageQrDialog.qml")
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                JamiIdControlButton {
 | 
			
		||||
                    id: btnId
 | 
			
		||||
                    source: JamiResources.key_black_24dp_svg
 | 
			
		||||
                    visible: CurrentAccount.registeredName !== ""
 | 
			
		||||
                    border.color: "transparent"
 | 
			
		||||
                    toolTipText: JamiStrings.identifierURI
 | 
			
		||||
                    onClicked: {
 | 
			
		||||
                        if (clicked) {
 | 
			
		||||
                            usernameLabel.text = Qt.binding(function() {return CurrentAccount.uri} );
 | 
			
		||||
                            usernameTextEdit.staticText = Qt.binding(function() {return CurrentAccount.uri} );
 | 
			
		||||
                            btnId.toolTipText = JamiStrings.identifierRegisterName;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            usernameLabel.text = Qt.binding(function() {return CurrentAccount.registeredName} );
 | 
			
		||||
                            usernameTextEdit.staticText = Qt.binding(function() {return CurrentAccount.registeredName} );
 | 
			
		||||
                            btnId.toolTipText = JamiStrings.identifierURI;
 | 
			
		||||
                        }
 | 
			
		||||
                        clicked = !clicked;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var dlg = viewCoordinator.presentDialog(appWindow, "settingsview/components/NameRegistrationDialog.qml", {
 | 
			
		||||
                        "registeredName": dynamicText
 | 
			
		||||
                    });
 | 
			
		||||
                dlg.accepted.connect(function () {
 | 
			
		||||
                        usernameTextEdit.nameRegistrationState = UsernameTextEdit.NameRegistrationState.BLANK;
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -175,11 +212,12 @@ Item {
 | 
			
		||||
    component JamiIdControlButton: PushButton {
 | 
			
		||||
        property bool clicked: true
 | 
			
		||||
        preferredSize: 30
 | 
			
		||||
        radius: 5
 | 
			
		||||
        normalColor: JamiTheme.transparentColor
 | 
			
		||||
        hoveredColor: JamiTheme.hoveredButtonColorWizard
 | 
			
		||||
        imageContainerWidth: JamiTheme.pushButtonSize
 | 
			
		||||
        imageContainerHeight: JamiTheme.pushButtonSize
 | 
			
		||||
        border.color: JamiTheme.tintedBlue
 | 
			
		||||
        imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
        border.color: jamiId.contentColor
 | 
			
		||||
        imageColor: jamiId.contentColor
 | 
			
		||||
        duration: 0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,14 +30,18 @@ Popup {
 | 
			
		||||
    padding: 0
 | 
			
		||||
    property list<Action> menuTypoActionsSecond
 | 
			
		||||
 | 
			
		||||
    focus: true
 | 
			
		||||
    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
 | 
			
		||||
 | 
			
		||||
    contentItem: ListView {
 | 
			
		||||
        id: listViewTypoSecond
 | 
			
		||||
 | 
			
		||||
        width: contentWidth + leftMargin
 | 
			
		||||
        width: contentWidth + leftMargin + rightMargin
 | 
			
		||||
        height: JamiTheme.chatViewFooterButtonSize
 | 
			
		||||
        orientation: ListView.Horizontal
 | 
			
		||||
        interactive: false
 | 
			
		||||
        leftMargin: 10
 | 
			
		||||
        rightMargin: 10
 | 
			
		||||
        spacing: 10
 | 
			
		||||
 | 
			
		||||
        Rectangle {
 | 
			
		||||
@ -50,7 +54,7 @@ Popup {
 | 
			
		||||
        model: menuTypoActionsSecond
 | 
			
		||||
 | 
			
		||||
        delegate: PushButton {
 | 
			
		||||
            anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
            anchors.verticalCenter: parent ? parent.verticalCenter : undefined
 | 
			
		||||
 | 
			
		||||
            preferredSize: JamiTheme.chatViewFooterRealButtonSize
 | 
			
		||||
            imageContainerWidth: 20
 | 
			
		||||
@ -64,7 +68,7 @@ Popup {
 | 
			
		||||
            focusPolicy: Qt.TabFocus
 | 
			
		||||
 | 
			
		||||
            normalColor: JamiTheme.chatViewFooterListColor
 | 
			
		||||
            imageColor: JamiTheme.chatViewFooterImgColor
 | 
			
		||||
            imageColor: JamiTheme.chatViewFooterImgHoverColor
 | 
			
		||||
            hoveredColor: JamiTheme.showMoreButtonOpenColor
 | 
			
		||||
            pressedColor: hoveredColor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,6 @@ AbstractButton {
 | 
			
		||||
    id: root
 | 
			
		||||
 | 
			
		||||
    property bool autoAccelerator: false
 | 
			
		||||
    property bool boldFont: false
 | 
			
		||||
    property bool primary: false
 | 
			
		||||
    property bool secondary: false
 | 
			
		||||
    property bool tertiary: false
 | 
			
		||||
@ -51,12 +50,12 @@ AbstractButton {
 | 
			
		||||
    height: buttontextHeightMargin + textButton.height
 | 
			
		||||
    Layout.preferredHeight: height
 | 
			
		||||
 | 
			
		||||
    Binding on width  {
 | 
			
		||||
    Binding on width {
 | 
			
		||||
        when: root.preferredWidth !== undefined || root.Layout.fillWidth
 | 
			
		||||
        value: root.preferredWidth
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Binding on Layout.preferredWidth  {
 | 
			
		||||
    Binding on Layout.preferredWidth {
 | 
			
		||||
        when: root.preferredWidth !== undefined || root.Layout.fillWidth
 | 
			
		||||
        value: width
 | 
			
		||||
    }
 | 
			
		||||
@ -66,7 +65,6 @@ AbstractButton {
 | 
			
		||||
        value: height
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hoverEnabled: true
 | 
			
		||||
    focusPolicy: Qt.StrongFocus
 | 
			
		||||
 | 
			
		||||
    Accessible.role: Accessible.Button
 | 
			
		||||
@ -96,7 +94,7 @@ AbstractButton {
 | 
			
		||||
    contentItem: Item {
 | 
			
		||||
        id: item
 | 
			
		||||
 | 
			
		||||
        Binding on implicitWidth  {
 | 
			
		||||
        Binding on implicitWidth {
 | 
			
		||||
            when: root.preferredWidth === undefined || !root.Layout.fillWidth
 | 
			
		||||
            value: item.childrenRect.width
 | 
			
		||||
        }
 | 
			
		||||
@ -105,7 +103,7 @@ AbstractButton {
 | 
			
		||||
 | 
			
		||||
        RowLayout {
 | 
			
		||||
            anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
            Binding on width  {
 | 
			
		||||
            Binding on width {
 | 
			
		||||
                when: root.preferredWidth !== undefined || root.Layout.fillWidth
 | 
			
		||||
                value: root.availableWidth
 | 
			
		||||
            }
 | 
			
		||||
@ -168,7 +166,6 @@ AbstractButton {
 | 
			
		||||
                leftPadding: root.primary ? JamiTheme.buttontextWizzardPadding : textLeftPadding
 | 
			
		||||
                rightPadding: root.primary ? JamiTheme.buttontextWizzardPadding : textRightPadding
 | 
			
		||||
                text: root.text
 | 
			
		||||
                font.weight: (root.hovered && root.hoverEnabled) || boldFont ? Font.Bold : Font.Medium
 | 
			
		||||
                elide: Text.ElideRight
 | 
			
		||||
                verticalAlignment: Text.AlignVCenter
 | 
			
		||||
                horizontalAlignment: root.textAlignment
 | 
			
		||||
@ -183,18 +180,18 @@ AbstractButton {
 | 
			
		||||
        color: {
 | 
			
		||||
            var baseColor = root.color;
 | 
			
		||||
            if (root.primary) {
 | 
			
		||||
                if (root.hovered && root.hoverEnabled)
 | 
			
		||||
                if (root.hovered && root.enabled)
 | 
			
		||||
                    return root.hoveredColor;
 | 
			
		||||
                return baseColor;
 | 
			
		||||
            }
 | 
			
		||||
            if (root.secondary || root.tertiary) {
 | 
			
		||||
                if (root.hovered && root.hoverEnabled)
 | 
			
		||||
                if (root.hovered && root.enabled)
 | 
			
		||||
                    return root.secHoveredColor;
 | 
			
		||||
                return JamiTheme.transparentColor;
 | 
			
		||||
            }
 | 
			
		||||
            if (root.down)
 | 
			
		||||
                return root.pressedColor;
 | 
			
		||||
            if (root.hovered && root.hoverEnabled)
 | 
			
		||||
            if (root.hovered && root.enabled)
 | 
			
		||||
                return root.hoveredColor;
 | 
			
		||||
            return baseColor;
 | 
			
		||||
        }
 | 
			
		||||
@ -202,7 +199,7 @@ AbstractButton {
 | 
			
		||||
        border.color: {
 | 
			
		||||
            if (root.primary || root.tertiary)
 | 
			
		||||
                return JamiTheme.transparentColor;
 | 
			
		||||
            if (root.secondary && root.hovered && root.hoverEnabled)
 | 
			
		||||
            if (root.secondary && root.hovered && root.enabled)
 | 
			
		||||
                return JamiTheme.secondaryButtonHoveredBorderColor;
 | 
			
		||||
            if (root.secondary)
 | 
			
		||||
                return JamiTheme.secondaryButtonBorderColor;
 | 
			
		||||
@ -226,7 +223,7 @@ AbstractButton {
 | 
			
		||||
 | 
			
		||||
        // We don't want to eat clicks on the Text.
 | 
			
		||||
        acceptedButtons: Qt.NoButton
 | 
			
		||||
        cursorShape: (root.hovered && root.hoverEnabled) ? Qt.PointingHandCursor : Qt.ArrowCursor
 | 
			
		||||
        cursorShape: (root.hovered && root.enabled) ? Qt.PointingHandCursor : Qt.ArrowCursor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Shortcut {
 | 
			
		||||
 | 
			
		||||
@ -74,7 +74,7 @@ RadioButton {
 | 
			
		||||
        spacing: 10
 | 
			
		||||
 | 
			
		||||
        anchors.left: root.indicator.right
 | 
			
		||||
        anchors.leftMargin: root.spacing
 | 
			
		||||
        anchors.leftMargin: 10
 | 
			
		||||
 | 
			
		||||
        ResponsiveImage {
 | 
			
		||||
            color: borderColor
 | 
			
		||||
@ -89,6 +89,8 @@ RadioButton {
 | 
			
		||||
            font.pixelSize: JamiTheme.settingsDescriptionPixelSize
 | 
			
		||||
            text: root.text
 | 
			
		||||
            wrapMode: Text.WordWrap
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
            Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -100,7 +102,7 @@ RadioButton {
 | 
			
		||||
        color: "transparent"
 | 
			
		||||
        implicitHeight: 20
 | 
			
		||||
        implicitWidth: 20
 | 
			
		||||
        radius: JamiTheme.settingsBoxRadius
 | 
			
		||||
        radius: 10
 | 
			
		||||
        z: 1
 | 
			
		||||
 | 
			
		||||
        border {
 | 
			
		||||
@ -114,7 +116,7 @@ RadioButton {
 | 
			
		||||
            anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
            color: checkedColor
 | 
			
		||||
            height: 12
 | 
			
		||||
            radius: JamiTheme.settingsBoxRadius
 | 
			
		||||
            radius: 10
 | 
			
		||||
            visible: checked || hovered
 | 
			
		||||
            width: 12
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,9 +33,11 @@ Popup {
 | 
			
		||||
    padding: 0
 | 
			
		||||
    background.visible: false
 | 
			
		||||
 | 
			
		||||
    required property var emojiReactions
 | 
			
		||||
    property var emojiReplied: emojiReactions.ownEmojis
 | 
			
		||||
 | 
			
		||||
    required property string msgId
 | 
			
		||||
    required property string msgBody
 | 
			
		||||
    required property var emojiReplied
 | 
			
		||||
    required property bool isOutgoing
 | 
			
		||||
    required property int type
 | 
			
		||||
    required property string transferName
 | 
			
		||||
@ -47,6 +49,9 @@ Popup {
 | 
			
		||||
    property bool closeWithoutAnimation: false
 | 
			
		||||
    property var emojiPicker
 | 
			
		||||
 | 
			
		||||
    focus: true
 | 
			
		||||
    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
 | 
			
		||||
 | 
			
		||||
    function xPositionProvider(width) {
 | 
			
		||||
        // Use the width at function scope to retrigger property evaluation.
 | 
			
		||||
        const listViewWidth = listView.width
 | 
			
		||||
@ -107,27 +112,11 @@ Popup {
 | 
			
		||||
    onClosed: if (emojiPicker) emojiPicker.closeEmojiPicker()
 | 
			
		||||
 | 
			
		||||
    function getModel() {
 | 
			
		||||
        var model = ["👍", "👎", "😂"]
 | 
			
		||||
        var cur = []
 | 
			
		||||
        //Add emoji reacted
 | 
			
		||||
        var index = 0
 | 
			
		||||
        for (let emoji of emojiReplied) {
 | 
			
		||||
            if (index < model.length) {
 | 
			
		||||
                cur[index] = emoji
 | 
			
		||||
                index ++
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //complete with default model
 | 
			
		||||
        var modelIndex = cur.length
 | 
			
		||||
        for (let j = 0; j < model.length; j++) {
 | 
			
		||||
            if (cur.length < model.length) {
 | 
			
		||||
                if (!cur.includes(model[j]) ) {
 | 
			
		||||
                    cur[modelIndex] = model[j]
 | 
			
		||||
                    modelIndex ++
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return cur
 | 
			
		||||
        const defaultModel = ["👍", "👎", "😂"]
 | 
			
		||||
        const reactedEmojis = Array.isArray(emojiReplied) ? emojiReplied.slice(0, defaultModel.length) : []
 | 
			
		||||
        const uniqueEmojis = Array.from(new Set(reactedEmojis))
 | 
			
		||||
        const missingEmojis = defaultModel.filter(emoji => !uniqueEmojis.includes(emoji))
 | 
			
		||||
        return uniqueEmojis.concat(missingEmojis)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Rectangle {
 | 
			
		||||
@ -167,7 +156,7 @@ Popup {
 | 
			
		||||
 | 
			
		||||
                        background: Rectangle {
 | 
			
		||||
                            anchors.fill: parent
 | 
			
		||||
                            opacity: emojiReplied.includes(modelData) ? 1 : 0
 | 
			
		||||
                            opacity: emojiReplied ? (emojiReplied.includes(modelData) ? 1 : 0) : 0
 | 
			
		||||
                            color: JamiTheme.emojiReactPushButtonColor
 | 
			
		||||
                            radius: 10
 | 
			
		||||
                        }
 | 
			
		||||
@ -239,6 +228,18 @@ Popup {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MessageOptionButton {
 | 
			
		||||
                visible: type === Interaction.Type.DATA_TRANSFER && Status === Interaction.Status.TRANSFER_FINISHED
 | 
			
		||||
                textButton: JamiStrings.removeLocally
 | 
			
		||||
                iconSource: JamiResources.trash_black_24dp_svg
 | 
			
		||||
                Layout.fillWidth: true
 | 
			
		||||
                Layout.margins: 5
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    MessagesAdapter.removeFile(msgId, root.location)
 | 
			
		||||
                    close()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MessageOptionButton {
 | 
			
		||||
                id: buttonEdit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@ Loader {
 | 
			
		||||
 | 
			
		||||
    // Needed to give proper focus to loaded item
 | 
			
		||||
    onFocusChanged: {
 | 
			
		||||
        if (root.focus && root.isPersistent) {
 | 
			
		||||
        if (item && root.focus && root.isPersistent) {
 | 
			
		||||
            item.forceActiveFocus();
 | 
			
		||||
        }
 | 
			
		||||
        isEditing = !isEditing;
 | 
			
		||||
 | 
			
		||||
@ -35,9 +35,6 @@ BaseModalDialog {
 | 
			
		||||
 | 
			
		||||
    signal done(bool success, int purpose)
 | 
			
		||||
 | 
			
		||||
    width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth)
 | 
			
		||||
    height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight)
 | 
			
		||||
 | 
			
		||||
    title: {
 | 
			
		||||
        switch (purpose) {
 | 
			
		||||
        case PasswordDialog.ExportAccount:
 | 
			
		||||
@ -75,7 +72,7 @@ BaseModalDialog {
 | 
			
		||||
    popupContent: ColumnLayout {
 | 
			
		||||
        id: popupContentColumnLayout
 | 
			
		||||
 | 
			
		||||
        spacing: 0
 | 
			
		||||
        spacing: 16
 | 
			
		||||
 | 
			
		||||
        function validatePassword() {
 | 
			
		||||
            switch (purpose) {
 | 
			
		||||
@ -128,6 +125,8 @@ BaseModalDialog {
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: JamiTheme.preferredFieldWidth
 | 
			
		||||
            Layout.preferredHeight: visible ? 48 : 0
 | 
			
		||||
            Layout.leftMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
            Layout.rightMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            visible: purpose === PasswordDialog.ChangePassword || purpose === PasswordDialog.ExportAccount
 | 
			
		||||
            placeholderText: JamiStrings.enterCurrentPassword
 | 
			
		||||
@ -141,6 +140,8 @@ BaseModalDialog {
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: JamiTheme.preferredFieldWidth
 | 
			
		||||
            Layout.preferredHeight: visible ? 48 : 0
 | 
			
		||||
            Layout.leftMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
            Layout.rightMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            visible: purpose === PasswordDialog.ChangePassword || purpose === PasswordDialog.SetPassword
 | 
			
		||||
 | 
			
		||||
@ -155,6 +156,8 @@ BaseModalDialog {
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            Layout.preferredWidth: JamiTheme.preferredFieldWidth
 | 
			
		||||
            Layout.preferredHeight: visible ? 48 : 0
 | 
			
		||||
            Layout.leftMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
            Layout.rightMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            visible: purpose === PasswordDialog.ChangePassword || purpose === PasswordDialog.SetPassword
 | 
			
		||||
 | 
			
		||||
@ -163,49 +166,24 @@ BaseModalDialog {
 | 
			
		||||
            onDynamicTextChanged: popupContentColumnLayout.validatePassword()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RowLayout {
 | 
			
		||||
            spacing: 16
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
            Layout.alignment: Qt.AlignCenter
 | 
			
		||||
        MaterialButton {
 | 
			
		||||
            id: btnConfirm
 | 
			
		||||
 | 
			
		||||
            MaterialButton {
 | 
			
		||||
                id: btnConfirm
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            color: enabled ? JamiTheme.buttonTintedBlack : JamiTheme.buttonTintedGrey
 | 
			
		||||
            hoveredColor: JamiTheme.buttonTintedBlackHovered
 | 
			
		||||
            pressedColor: JamiTheme.buttonTintedBlackPressed
 | 
			
		||||
            secondary: true
 | 
			
		||||
            autoAccelerator: true
 | 
			
		||||
            enabled: purpose === PasswordDialog.SetPassword
 | 
			
		||||
 | 
			
		||||
                preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
 | 
			
		||||
            text: (purpose === PasswordDialog.ExportAccount) ? JamiStrings.exportAccount : JamiStrings.change
 | 
			
		||||
 | 
			
		||||
                color: enabled ? JamiTheme.buttonTintedBlack : JamiTheme.buttonTintedGrey
 | 
			
		||||
                hoveredColor: JamiTheme.buttonTintedBlackHovered
 | 
			
		||||
                pressedColor: JamiTheme.buttonTintedBlackPressed
 | 
			
		||||
                secondary: true
 | 
			
		||||
                autoAccelerator: true
 | 
			
		||||
                enabled: purpose === PasswordDialog.SetPassword
 | 
			
		||||
 | 
			
		||||
                text: (purpose === PasswordDialog.ExportAccount) ? JamiStrings.exportAccount : JamiStrings.change
 | 
			
		||||
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    btnConfirm.enabled = false;
 | 
			
		||||
                    timerToOperate.restart();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MaterialButton {
 | 
			
		||||
                id: btnCancel
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
 | 
			
		||||
                preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
 | 
			
		||||
 | 
			
		||||
                color: JamiTheme.buttonTintedBlack
 | 
			
		||||
                hoveredColor: JamiTheme.buttonTintedBlackHovered
 | 
			
		||||
                pressedColor: JamiTheme.buttonTintedBlackPressed
 | 
			
		||||
                secondary: true
 | 
			
		||||
                autoAccelerator: true
 | 
			
		||||
 | 
			
		||||
                text: JamiStrings.optionCancel
 | 
			
		||||
 | 
			
		||||
                onClicked: close()
 | 
			
		||||
            onClicked: {
 | 
			
		||||
                btnConfirm.enabled = false;
 | 
			
		||||
                timerToOperate.restart();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -32,15 +32,12 @@ import "../mainview/components"
 | 
			
		||||
BaseModalDialog {
 | 
			
		||||
    id: root
 | 
			
		||||
 | 
			
		||||
    height: 157
 | 
			
		||||
    x: - width / 2
 | 
			
		||||
    y: - height / 5
 | 
			
		||||
 | 
			
		||||
    property string imageId
 | 
			
		||||
    property bool newItem
 | 
			
		||||
    property real buttonSize: JamiTheme.smartListAvatarSize
 | 
			
		||||
    property real imageSize: 25
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    signal focusOnPreviousItem
 | 
			
		||||
    signal focusOnNextItem
 | 
			
		||||
 | 
			
		||||
@ -60,9 +57,15 @@ BaseModalDialog {
 | 
			
		||||
        importButton.forceActiveFocus()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    width: JamiTheme.preferredDialogWidth
 | 
			
		||||
 | 
			
		||||
    title: JamiStrings.chooseAvatarPicture
 | 
			
		||||
 | 
			
		||||
    RecordBox {
 | 
			
		||||
        id: recordBox
 | 
			
		||||
 | 
			
		||||
        anchors.centerIn: parent
 | 
			
		||||
 | 
			
		||||
        isPhoto: true
 | 
			
		||||
        visible: false
 | 
			
		||||
 | 
			
		||||
@ -77,223 +80,176 @@ BaseModalDialog {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    popupContent: Item {
 | 
			
		||||
    popupContent: RowLayout {
 | 
			
		||||
            id: buttonsRowLayout
 | 
			
		||||
 | 
			
		||||
        Component.onCompleted: {
 | 
			
		||||
            root.width = Qt.binding(() => clearButton.visible ? 283 : 210)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Rectangle {
 | 
			
		||||
            id: container
 | 
			
		||||
 | 
			
		||||
            anchors.fill: parent
 | 
			
		||||
            radius: JamiTheme.photoPopupRadius
 | 
			
		||||
            color: JamiTheme.inviteHoverColor
 | 
			
		||||
            spacing: 10
 | 
			
		||||
 | 
			
		||||
            PushButton {
 | 
			
		||||
                id: btnCancel
 | 
			
		||||
                imageColor: "grey"
 | 
			
		||||
                id: takePhotoButton
 | 
			
		||||
 | 
			
		||||
                objectName: "takePhotoButton"
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
 | 
			
		||||
                height: buttonSize
 | 
			
		||||
                width: buttonSize
 | 
			
		||||
                imageContainerWidth: imageSize
 | 
			
		||||
                imageContainerHeight: imageSize
 | 
			
		||||
                radius: height / 2
 | 
			
		||||
                border.color: JamiTheme.buttonTintedBlue
 | 
			
		||||
                normalColor: "transparent"
 | 
			
		||||
                anchors.right: parent.right
 | 
			
		||||
                anchors.top: parent.top
 | 
			
		||||
                anchors.topMargin: 10
 | 
			
		||||
                anchors.rightMargin: 10
 | 
			
		||||
                source: JamiResources.round_close_24dp_svg
 | 
			
		||||
                onClicked: { close();}
 | 
			
		||||
            }
 | 
			
		||||
                imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
                toolTipText: JamiStrings.takePhoto
 | 
			
		||||
                source: JamiResources.baseline_camera_alt_24dp_svg
 | 
			
		||||
                hoveredColor: JamiTheme.smartListHoveredColor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            ColumnLayout {
 | 
			
		||||
                id:  mainLayout
 | 
			
		||||
                anchors.fill: parent
 | 
			
		||||
                anchors.margins: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
                Text {
 | 
			
		||||
                    id: informativeLabel
 | 
			
		||||
 | 
			
		||||
                    Layout.alignment: Qt.AlignCenter
 | 
			
		||||
                    Layout.fillWidth: true
 | 
			
		||||
                    Layout.topMargin: 26
 | 
			
		||||
                    horizontalAlignment: Text.AlignHCenter
 | 
			
		||||
                    verticalAlignment: Text.AlignVCenter
 | 
			
		||||
                    text: JamiStrings.chooseAvatarPicture
 | 
			
		||||
                    color: JamiTheme.primaryForegroundColor
 | 
			
		||||
                    font.pixelSize: JamiTheme.popupPhotoTextSize
 | 
			
		||||
                    elide: Text.ElideRight
 | 
			
		||||
                Keys.onPressed: function (keyEvent) {
 | 
			
		||||
                    if (keyEvent.key === Qt.Key_Enter ||
 | 
			
		||||
                            keyEvent.key === Qt.Key_Return) {
 | 
			
		||||
                        clicked()
 | 
			
		||||
                        keyEvent.accepted = true
 | 
			
		||||
                    } else if (keyEvent.key === Qt.Key_Up) {
 | 
			
		||||
                        root.focusOnPreviousItem()
 | 
			
		||||
                        keyEvent.accepted = true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                RowLayout {
 | 
			
		||||
                    id: buttonsRowLayout
 | 
			
		||||
                    Layout.preferredHeight: childrenRect.height
 | 
			
		||||
                    Layout.alignment: Qt.AlignCenter
 | 
			
		||||
                    spacing: 10
 | 
			
		||||
                KeyNavigation.tab: {
 | 
			
		||||
                    if (clearButton.visible)
 | 
			
		||||
                        return clearButton
 | 
			
		||||
                    return importButton
 | 
			
		||||
                }
 | 
			
		||||
                KeyNavigation.down: KeyNavigation.tab
 | 
			
		||||
 | 
			
		||||
                    PushButton {
 | 
			
		||||
                        id: takePhotoButton
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    recordBox.parent = buttonsRowLayout
 | 
			
		||||
 | 
			
		||||
                        objectName: "takePhotoButton"
 | 
			
		||||
                    recordBox.x = Qt.binding(function() {
 | 
			
		||||
                        var buttonCenterX = buttonsRowLayout.width / 2
 | 
			
		||||
                        return buttonCenterX - recordBox.width / 2
 | 
			
		||||
                    })
 | 
			
		||||
                    recordBox.y = Qt.binding(function() {
 | 
			
		||||
                        return - recordBox.height / 2
 | 
			
		||||
                    })
 | 
			
		||||
                    startBooth()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                        Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
            PushButton {
 | 
			
		||||
                id: importButton
 | 
			
		||||
 | 
			
		||||
                        height: buttonSize
 | 
			
		||||
                        width: buttonSize
 | 
			
		||||
                        imageContainerWidth: imageSize
 | 
			
		||||
                        imageContainerHeight: imageSize
 | 
			
		||||
                        radius: height / 2
 | 
			
		||||
                        border.color: JamiTheme.buttonTintedBlue
 | 
			
		||||
                        normalColor: "transparent"
 | 
			
		||||
                        imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
                        toolTipText: JamiStrings.takePhoto
 | 
			
		||||
                        source: JamiResources.baseline_camera_alt_24dp_svg
 | 
			
		||||
                        hoveredColor: JamiTheme.smartListHoveredColor
 | 
			
		||||
                objectName: "photoboothViewImportButton"
 | 
			
		||||
 | 
			
		||||
                        Keys.onPressed: function (keyEvent) {
 | 
			
		||||
                            if (keyEvent.key === Qt.Key_Enter ||
 | 
			
		||||
                                    keyEvent.key === Qt.Key_Return) {
 | 
			
		||||
                                clicked()
 | 
			
		||||
                                keyEvent.accepted = true
 | 
			
		||||
                            } else if (keyEvent.key === Qt.Key_Up) {
 | 
			
		||||
                                root.focusOnPreviousItem()
 | 
			
		||||
                                keyEvent.accepted = true
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
                visible: parent.visible
 | 
			
		||||
 | 
			
		||||
                        KeyNavigation.tab: {
 | 
			
		||||
                            if (clearButton.visible)
 | 
			
		||||
                                return clearButton
 | 
			
		||||
                            return importButton
 | 
			
		||||
                        }
 | 
			
		||||
                        KeyNavigation.down: KeyNavigation.tab
 | 
			
		||||
                height: buttonSize
 | 
			
		||||
                width: buttonSize
 | 
			
		||||
                imageContainerWidth: imageSize
 | 
			
		||||
                imageContainerHeight: imageSize
 | 
			
		||||
                radius: height / 2
 | 
			
		||||
                border.color: JamiTheme.buttonTintedBlue
 | 
			
		||||
                normalColor: "transparent"
 | 
			
		||||
                source: JamiResources.round_folder_24dp_svg
 | 
			
		||||
                toolTipText: JamiStrings.importFromFile
 | 
			
		||||
                imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
                hoveredColor: JamiTheme.smartListHoveredColor
 | 
			
		||||
 | 
			
		||||
                        onClicked: {
 | 
			
		||||
                            recordBox.parent = buttonsRowLayout
 | 
			
		||||
 | 
			
		||||
                            recordBox.x = Qt.binding(function() {
 | 
			
		||||
                                var buttonCenterX = buttonsRowLayout.width / 2
 | 
			
		||||
                                return buttonCenterX - recordBox.width / 2
 | 
			
		||||
                            })
 | 
			
		||||
                            recordBox.y = Qt.binding(function() {
 | 
			
		||||
                                return - recordBox.height / 2
 | 
			
		||||
                            })
 | 
			
		||||
                            startBooth()
 | 
			
		||||
                        }
 | 
			
		||||
                Keys.onPressed: function (keyEvent) {
 | 
			
		||||
                    if (keyEvent.key === Qt.Key_Enter ||
 | 
			
		||||
                            keyEvent.key === Qt.Key_Return) {
 | 
			
		||||
                        clicked()
 | 
			
		||||
                        keyEvent.accepted = true
 | 
			
		||||
                    } else if (keyEvent.key === Qt.Key_Down ||
 | 
			
		||||
                               keyEvent.key === Qt.Key_Tab) {
 | 
			
		||||
                        clearButton.forceActiveFocus()
 | 
			
		||||
                        keyEvent.accepted = true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                    PushButton {
 | 
			
		||||
                        id: importButton
 | 
			
		||||
                KeyNavigation.up: takePhotoButton
 | 
			
		||||
 | 
			
		||||
                        objectName: "photoboothViewImportButton"
 | 
			
		||||
 | 
			
		||||
                        Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
                        visible: parent.visible
 | 
			
		||||
 | 
			
		||||
                        height: buttonSize
 | 
			
		||||
                        width: buttonSize
 | 
			
		||||
                        imageContainerWidth: imageSize
 | 
			
		||||
                        imageContainerHeight: imageSize
 | 
			
		||||
                        radius: height / 2
 | 
			
		||||
                        border.color: JamiTheme.buttonTintedBlue
 | 
			
		||||
                        normalColor: "transparent"
 | 
			
		||||
                        source: JamiResources.round_folder_24dp_svg
 | 
			
		||||
                        toolTipText: JamiStrings.importFromFile
 | 
			
		||||
                        imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
                        hoveredColor: JamiTheme.smartListHoveredColor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        Keys.onPressed: function (keyEvent) {
 | 
			
		||||
                            if (keyEvent.key === Qt.Key_Enter ||
 | 
			
		||||
                                    keyEvent.key === Qt.Key_Return) {
 | 
			
		||||
                                clicked()
 | 
			
		||||
                                keyEvent.accepted = true
 | 
			
		||||
                            } else if (keyEvent.key === Qt.Key_Down ||
 | 
			
		||||
                                       keyEvent.key === Qt.Key_Tab) {
 | 
			
		||||
                                clearButton.forceActiveFocus()
 | 
			
		||||
                                keyEvent.accepted = true
 | 
			
		||||
                            }
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    stopBooth()
 | 
			
		||||
                    var dlg = viewCoordinator.presentDialog(
 | 
			
		||||
                                appWindow,
 | 
			
		||||
                                "commoncomponents/JamiFileDialog.qml",
 | 
			
		||||
                                {
 | 
			
		||||
                                    title: JamiStrings.chooseAvatarImage,
 | 
			
		||||
                                    fileMode: JamiFileDialog.OpenFile,
 | 
			
		||||
                                    folder: StandardPaths.writableLocation(
 | 
			
		||||
                                                StandardPaths.PicturesLocation),
 | 
			
		||||
                                    nameFilters: [JamiStrings.imageFiles,
 | 
			
		||||
                                        JamiStrings.allFiles]
 | 
			
		||||
                                })
 | 
			
		||||
                    dlg.fileAccepted.connect(function(file) {
 | 
			
		||||
                        var filePath = UtilsAdapter.getAbsPath(file)
 | 
			
		||||
                        if (!root.newItem) {
 | 
			
		||||
                            AccountAdapter.setCurrentAccountAvatarFile(filePath)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            UtilsAdapter.setTempCreationImageFromFile(filePath, root.imageId)
 | 
			
		||||
                        }
 | 
			
		||||
                        root.close()
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                        KeyNavigation.up: takePhotoButton
 | 
			
		||||
            PushButton {
 | 
			
		||||
                id: clearButton
 | 
			
		||||
 | 
			
		||||
                        onClicked: {
 | 
			
		||||
                            stopBooth()
 | 
			
		||||
                            var dlg = viewCoordinator.presentDialog(
 | 
			
		||||
                                        appWindow,
 | 
			
		||||
                                        "commoncomponents/JamiFileDialog.qml",
 | 
			
		||||
                                        {
 | 
			
		||||
                                            title: JamiStrings.chooseAvatarImage,
 | 
			
		||||
                                            fileMode: JamiFileDialog.OpenFile,
 | 
			
		||||
                                            folder: StandardPaths.writableLocation(
 | 
			
		||||
                                                        StandardPaths.PicturesLocation),
 | 
			
		||||
                                            nameFilters: [JamiStrings.imageFiles,
 | 
			
		||||
                                                JamiStrings.allFiles]
 | 
			
		||||
                                        })
 | 
			
		||||
                            dlg.fileAccepted.connect(function(file) {
 | 
			
		||||
                                var filePath = UtilsAdapter.getAbsPath(file)
 | 
			
		||||
                                if (!root.newItem) {
 | 
			
		||||
                                    AccountAdapter.setCurrentAccountAvatarFile(filePath)
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    UtilsAdapter.setTempCreationImageFromFile(filePath, root.imageId)
 | 
			
		||||
                                }
 | 
			
		||||
                                root.close()
 | 
			
		||||
                            })
 | 
			
		||||
                        }
 | 
			
		||||
                objectName: "photoboothViewClearButton"
 | 
			
		||||
 | 
			
		||||
                Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
 | 
			
		||||
                height: buttonSize
 | 
			
		||||
                width: buttonSize
 | 
			
		||||
                imageContainerWidth: imageSize
 | 
			
		||||
                imageContainerHeight: imageSize
 | 
			
		||||
                radius: height / 2
 | 
			
		||||
                border.color: JamiTheme.buttonTintedBlue
 | 
			
		||||
                normalColor: "transparent"
 | 
			
		||||
                source: JamiResources.ic_hangup_participant_24dp_svg
 | 
			
		||||
                toolTipText: JamiStrings.clearAvatar
 | 
			
		||||
                imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
                hoveredColor: JamiTheme.smartListHoveredColor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                visible: {
 | 
			
		||||
                    if (!newItem && LRCInstance.currentAccountAvatarSet)
 | 
			
		||||
                        return true
 | 
			
		||||
                    if (newItem && UtilsAdapter.tempCreationImage(imageId).length !== 0)
 | 
			
		||||
                        return true
 | 
			
		||||
                    return false
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                KeyNavigation.up: importButton
 | 
			
		||||
 | 
			
		||||
                Keys.onPressed: function (keyEvent) {
 | 
			
		||||
                    if (keyEvent.key === Qt.Key_Enter ||
 | 
			
		||||
                            keyEvent.key === Qt.Key_Return) {
 | 
			
		||||
                        clicked()
 | 
			
		||||
                        importButton.forceActiveFocus()
 | 
			
		||||
                        keyEvent.accepted = true
 | 
			
		||||
                    } else if (keyEvent.key === Qt.Key_Down ||
 | 
			
		||||
                               keyEvent.key === Qt.Key_Tab) {
 | 
			
		||||
                        btnCancel.forceActiveFocus()
 | 
			
		||||
                        keyEvent.accepted = true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                    PushButton {
 | 
			
		||||
                        id: clearButton
 | 
			
		||||
 | 
			
		||||
                        objectName: "photoboothViewClearButton"
 | 
			
		||||
 | 
			
		||||
                        Layout.alignment: Qt.AlignHCenter
 | 
			
		||||
 | 
			
		||||
                        height: buttonSize
 | 
			
		||||
                        width: buttonSize
 | 
			
		||||
                        imageContainerWidth: imageSize
 | 
			
		||||
                        imageContainerHeight: imageSize
 | 
			
		||||
                        radius: height / 2
 | 
			
		||||
                        border.color: JamiTheme.buttonTintedBlue
 | 
			
		||||
                        normalColor: "transparent"
 | 
			
		||||
                        source: JamiResources.ic_hangup_participant_24dp_svg
 | 
			
		||||
                        toolTipText: JamiStrings.clearAvatar
 | 
			
		||||
                        imageColor: JamiTheme.buttonTintedBlue
 | 
			
		||||
                        hoveredColor: JamiTheme.smartListHoveredColor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        visible: {
 | 
			
		||||
                            if (!newItem && LRCInstance.currentAccountAvatarSet)
 | 
			
		||||
                                return true
 | 
			
		||||
                            if (newItem && UtilsAdapter.tempCreationImage(imageId).length !== 0)
 | 
			
		||||
                                return true
 | 
			
		||||
                            return false
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        KeyNavigation.up: importButton
 | 
			
		||||
 | 
			
		||||
                        Keys.onPressed: function (keyEvent) {
 | 
			
		||||
                            if (keyEvent.key === Qt.Key_Enter ||
 | 
			
		||||
                                    keyEvent.key === Qt.Key_Return) {
 | 
			
		||||
                                clicked()
 | 
			
		||||
                                importButton.forceActiveFocus()
 | 
			
		||||
                                keyEvent.accepted = true
 | 
			
		||||
                            } else if (keyEvent.key === Qt.Key_Down ||
 | 
			
		||||
                                       keyEvent.key === Qt.Key_Tab) {
 | 
			
		||||
                                btnCancel.forceActiveFocus()
 | 
			
		||||
                                keyEvent.accepted = true
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        onClicked: {
 | 
			
		||||
                            if (!root.newItem)
 | 
			
		||||
                                AccountAdapter.setCurrentAccountAvatarBase64()
 | 
			
		||||
                            else
 | 
			
		||||
                                UtilsAdapter.setTempCreationImageFromString("", imageId)
 | 
			
		||||
                            visible = false
 | 
			
		||||
                            stopBooth()
 | 
			
		||||
                            root.close()
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    if (!root.newItem)
 | 
			
		||||
                        AccountAdapter.setCurrentAccountAvatarBase64()
 | 
			
		||||
                    else
 | 
			
		||||
                        UtilsAdapter.setTempCreationImageFromString("", imageId)
 | 
			
		||||
                    visible = false
 | 
			
		||||
                    stopBooth()
 | 
			
		||||
                    root.close()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -85,7 +85,7 @@ ItemDelegate {
 | 
			
		||||
            id: prefLlabel
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
            Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
 | 
			
		||||
            Layout.leftMargin: 8
 | 
			
		||||
            Layout.leftMargin: 20
 | 
			
		||||
 | 
			
		||||
            text: preferenceName
 | 
			
		||||
            color: JamiTheme.textColor
 | 
			
		||||
@ -103,7 +103,7 @@ ItemDelegate {
 | 
			
		||||
            normalColor: JamiTheme.primaryBackgroundColor
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignRight | Qt.AlingVCenter
 | 
			
		||||
            Layout.rightMargin: 8
 | 
			
		||||
            Layout.rightMargin: 20
 | 
			
		||||
            Layout.preferredWidth: preferredSize
 | 
			
		||||
            Layout.preferredHeight: preferredSize
 | 
			
		||||
            imageColor: JamiTheme.textColor
 | 
			
		||||
@ -119,7 +119,7 @@ ItemDelegate {
 | 
			
		||||
 | 
			
		||||
            visible: preferenceType === PreferenceItemListModel.SWITCH
 | 
			
		||||
            Layout.alignment: Qt.AlignRight | Qt.AlingVCenter
 | 
			
		||||
            Layout.rightMargin: 16
 | 
			
		||||
            Layout.rightMargin: 20
 | 
			
		||||
            Layout.preferredHeight: 30
 | 
			
		||||
            Layout.preferredWidth: 30
 | 
			
		||||
            checked: preferenceCurrentValue === "1"
 | 
			
		||||
@ -134,7 +134,7 @@ ItemDelegate {
 | 
			
		||||
            visible: preferenceType === PreferenceItemListModel.LIST
 | 
			
		||||
            Layout.preferredWidth: root.width / 2 - 8
 | 
			
		||||
            Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
 | 
			
		||||
            Layout.rightMargin: 4
 | 
			
		||||
            Layout.rightMargin: 20
 | 
			
		||||
 | 
			
		||||
            font.pointSize: JamiTheme.settingsFontSize
 | 
			
		||||
            font.kerning: true
 | 
			
		||||
@ -157,7 +157,7 @@ ItemDelegate {
 | 
			
		||||
            buttontextHeightMargin: JamiTheme.buttontextHeightMargin
 | 
			
		||||
 | 
			
		||||
            Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
 | 
			
		||||
            Layout.rightMargin: 4
 | 
			
		||||
            Layout.rightMargin: 20
 | 
			
		||||
 | 
			
		||||
            text: UtilsAdapter.fileName(preferenceCurrentValue)
 | 
			
		||||
            toolTipText: JamiStrings.chooseImageFile
 | 
			
		||||
@ -176,7 +176,7 @@ ItemDelegate {
 | 
			
		||||
            Layout.preferredWidth: root.width / 2 - 8
 | 
			
		||||
            Layout.preferredHeight: 30
 | 
			
		||||
            Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
 | 
			
		||||
            Layout.rightMargin: 4
 | 
			
		||||
            Layout.rightMargin: 20
 | 
			
		||||
 | 
			
		||||
            visible: preferenceType === PreferenceItemListModel.EDITTEXT
 | 
			
		||||
            width: root.width / 2 - 8
 | 
			
		||||
 | 
			
		||||
@ -64,7 +64,7 @@ Item {
 | 
			
		||||
 | 
			
		||||
        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
 | 
			
		||||
        selectByMouse: true
 | 
			
		||||
        font.pixelSize: IsEmojiOnly ? JamiTheme.chatviewEmojiSize : JamiTheme.emojiBubbleSize
 | 
			
		||||
        font.pixelSize: IsEmojiOnly ? JamiTheme.chatviewEmojiSize : JamiTheme.mediumFontSize
 | 
			
		||||
        font.hintingPreference: Font.PreferNoHinting
 | 
			
		||||
        renderType: Text.NativeRendering
 | 
			
		||||
        textFormat: Text.MarkdownText
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,9 @@ Item {
 | 
			
		||||
 | 
			
		||||
    property alias source: image.source
 | 
			
		||||
    property alias status: image.status
 | 
			
		||||
    property alias fillMode: image.fillMode
 | 
			
		||||
    property alias cache: image.cache
 | 
			
		||||
    property alias image: image
 | 
			
		||||
    property alias mirror: image.mirror
 | 
			
		||||
    property string color: "transparent"
 | 
			
		||||
    property bool hovered: false
 | 
			
		||||
@ -68,6 +70,7 @@ Item {
 | 
			
		||||
        antialiasing: true
 | 
			
		||||
        asynchronous: true
 | 
			
		||||
        visible: false
 | 
			
		||||
        mipmap: true
 | 
			
		||||
 | 
			
		||||
        function setSourceSize() {
 | 
			
		||||
            sourceSize = undefined;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										77
									
								
								src/app/commoncomponents/RoundedBorderRectangle.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,77 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2023 Savoir-faire Linux Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import QtQuick
 | 
			
		||||
import QtQuick.Controls
 | 
			
		||||
import QtQuick.Layouts
 | 
			
		||||
import Qt5Compat.GraphicalEffects
 | 
			
		||||
import net.jami.Models 1.1
 | 
			
		||||
import net.jami.Adapters 1.1
 | 
			
		||||
import net.jami.Constants 1.1
 | 
			
		||||
 | 
			
		||||
Canvas {
 | 
			
		||||
 | 
			
		||||
    property var radius
 | 
			
		||||
    property string fillColor: Style.colorBGPrimary
 | 
			
		||||
 | 
			
		||||
    onRadiusChanged: requestPaint()
 | 
			
		||||
    onFillColorChanged: requestPaint()
 | 
			
		||||
 | 
			
		||||
    //Draw rounded rectangle.
 | 
			
		||||
    onPaint: {
 | 
			
		||||
        var ctx = getContext("2d");
 | 
			
		||||
        var r = {};
 | 
			
		||||
        Object.assign(r, radius);
 | 
			
		||||
        if (typeof r === 'undefined')
 | 
			
		||||
            r = 0;
 | 
			
		||||
        if (typeof r === 'number')
 | 
			
		||||
            r = {
 | 
			
		||||
                "tl": r,
 | 
			
		||||
                "tr": r,
 | 
			
		||||
                "br": r,
 | 
			
		||||
                "bl": r
 | 
			
		||||
            };
 | 
			
		||||
        else {
 | 
			
		||||
            var defaultRadius = {
 | 
			
		||||
                "tl": 0,
 | 
			
		||||
                "tr": 0,
 | 
			
		||||
                "br": 0,
 | 
			
		||||
                "bl": 0
 | 
			
		||||
            };
 | 
			
		||||
            for (var side in defaultRadius)
 | 
			
		||||
                r[side] = r[side] || defaultRadius[side];
 | 
			
		||||
        }
 | 
			
		||||
        var x0 = 0;
 | 
			
		||||
        var y0 = x0;
 | 
			
		||||
        var x1 = width;
 | 
			
		||||
        var y1 = height;
 | 
			
		||||
        ctx.reset();
 | 
			
		||||
        ctx.beginPath();
 | 
			
		||||
        ctx.moveTo(x0 + r.tl, y0);
 | 
			
		||||
        ctx.lineTo(x1 - r.tr, y0);
 | 
			
		||||
        ctx.quadraticCurveTo(x1, y0, x1, y0 + r.tr);
 | 
			
		||||
        ctx.lineTo(x1, y1 - r.br);
 | 
			
		||||
        ctx.quadraticCurveTo(x1, y1, x1 - r.br, y1);
 | 
			
		||||
        ctx.lineTo(x0 + r.bl, y1);
 | 
			
		||||
        ctx.quadraticCurveTo(x0, y1, x0, y1 - r.bl);
 | 
			
		||||
        ctx.lineTo(x0, y0 + r.tl);
 | 
			
		||||
        ctx.quadraticCurveTo(x0, y0, x0 + r.tl, y0);
 | 
			
		||||
        ctx.closePath();
 | 
			
		||||
        ctx.fillStyle = fillColor;
 | 
			
		||||
        ctx.fill();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -15,12 +15,10 @@
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import QtQuick
 | 
			
		||||
import QtQuick.Controls
 | 
			
		||||
import QtQuick.Layouts
 | 
			
		||||
import Qt5Compat.GraphicalEffects
 | 
			
		||||
 | 
			
		||||
import net.jami.Models 1.1
 | 
			
		||||
import net.jami.Adapters 1.1
 | 
			
		||||
import net.jami.Constants 1.1
 | 
			
		||||
@ -62,11 +60,20 @@ Control {
 | 
			
		||||
    property real textContentHeight
 | 
			
		||||
    property bool isReply: ReplyTo !== ""
 | 
			
		||||
 | 
			
		||||
    property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth
 | 
			
		||||
 | 
			
		||||
    // If the ListView attached properties are not available,
 | 
			
		||||
    // then the root delegate is likely a Loader.
 | 
			
		||||
    readonly property ListView listView: ListView.view ?
 | 
			
		||||
                                             ListView.view :
 | 
			
		||||
                                             parent.ListView.view
 | 
			
		||||
    readonly property ListView listView: ListView.view ? ListView.view : parent.ListView.view
 | 
			
		||||
 | 
			
		||||
    function getBaseColor() {
 | 
			
		||||
        var baseColor = isOutgoing ? CurrentConversation.color : JamiTheme.messageInBgColor;
 | 
			
		||||
        if (Id === MessagesAdapter.replyToId || Id === MessagesAdapter.editId) {
 | 
			
		||||
            // If we are replying to or editing the message
 | 
			
		||||
            return Qt.darker(baseColor, 1.5);
 | 
			
		||||
        }
 | 
			
		||||
        return baseColor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rightPadding: hPadding
 | 
			
		||||
    leftPadding: hPadding
 | 
			
		||||
@ -94,21 +101,21 @@ Control {
 | 
			
		||||
            id: usernameblock
 | 
			
		||||
            Layout.preferredHeight: (seq === MsgSeq.first || seq === MsgSeq.single) ? 10 : 0
 | 
			
		||||
            visible: !isReply
 | 
			
		||||
            Layout.topMargin: (seq === MsgSeq.first || seq === MsgSeq.single) && !isOutgoing && !root.showTime ? 20 : 0
 | 
			
		||||
 | 
			
		||||
            Label {
 | 
			
		||||
                id: username
 | 
			
		||||
                text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author)
 | 
			
		||||
                font.bold: true
 | 
			
		||||
                visible:(seq === MsgSeq.first || seq === MsgSeq.single) && !isOutgoing
 | 
			
		||||
                font.pixelSize: JamiTheme.usernameBlockFontSize
 | 
			
		||||
                color: JamiTheme.chatviewUsernameColor
 | 
			
		||||
                visible: (seq === MsgSeq.first || seq === MsgSeq.single) && !isOutgoing
 | 
			
		||||
                font.pointSize: JamiTheme.smallFontSize
 | 
			
		||||
                color: JamiTheme.chatviewSecondaryInformationColor
 | 
			
		||||
                lineHeight: JamiTheme.usernameBlockLineHeight
 | 
			
		||||
                leftPadding: JamiTheme.usernameBlockPadding
 | 
			
		||||
                textFormat: TextEdit.PlainText
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        Item {
 | 
			
		||||
            id: replyItem
 | 
			
		||||
            property bool isSelf: ReplyToAuthor === CurrentAccount.uri
 | 
			
		||||
@ -119,18 +126,19 @@ Control {
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
            Layout.preferredHeight: childrenRect.height
 | 
			
		||||
 | 
			
		||||
            Layout.topMargin: JamiTheme.sbsMessageBaseReplyTopMargin
 | 
			
		||||
            Layout.topMargin: visible ? JamiTheme.sbsMessageBaseReplyTopMargin : 0
 | 
			
		||||
            Layout.leftMargin: isOutgoing ? undefined : JamiTheme.sbsMessageBaseReplyMargin
 | 
			
		||||
            Layout.rightMargin: !isOutgoing ? undefined : JamiTheme.sbsMessageBaseReplyMargin
 | 
			
		||||
 | 
			
		||||
            transform: Translate { y: JamiTheme.sbsMessageBaseReplyBottomMargin }
 | 
			
		||||
 | 
			
		||||
            transform: Translate {
 | 
			
		||||
                y: JamiTheme.sbsMessageBaseReplyBottomMargin
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ColumnLayout {
 | 
			
		||||
                width: parent.width
 | 
			
		||||
                spacing: 2
 | 
			
		||||
 | 
			
		||||
                RowLayout{
 | 
			
		||||
                RowLayout {
 | 
			
		||||
                    id: replyToLayout
 | 
			
		||||
 | 
			
		||||
                    Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
 | 
			
		||||
@ -155,8 +163,8 @@ Control {
 | 
			
		||||
                        showPresenceIndicator: false
 | 
			
		||||
                        imageId: {
 | 
			
		||||
                            if (replyItem.isSelf)
 | 
			
		||||
                                return CurrentAccount.id
 | 
			
		||||
                            return ReplyToAuthor
 | 
			
		||||
                                return CurrentAccount.id;
 | 
			
		||||
                            return ReplyToAuthor;
 | 
			
		||||
                        }
 | 
			
		||||
                        mode: replyItem.isSelf ? Avatar.Mode.Account : Avatar.Mode.Contact
 | 
			
		||||
                    }
 | 
			
		||||
@ -179,11 +187,10 @@ Control {
 | 
			
		||||
                    color: replyItem.isSelf ? CurrentConversation.color : JamiTheme.messageInBgColor
 | 
			
		||||
                    radius: msgRadius
 | 
			
		||||
 | 
			
		||||
                    Layout.preferredWidth: replyToRow.width + 2*JamiTheme.preferredMarginSize
 | 
			
		||||
                    Layout.preferredHeight: replyToRow.height + 2*JamiTheme.preferredMarginSize
 | 
			
		||||
                    Layout.preferredWidth: replyToRow.width + 2 * JamiTheme.preferredMarginSize
 | 
			
		||||
                    Layout.preferredHeight: replyToRow.height + 2 * JamiTheme.preferredMarginSize
 | 
			
		||||
                    Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    // place actual content here
 | 
			
		||||
                    ReplyToRow {
 | 
			
		||||
                        id: replyToRow
 | 
			
		||||
@ -194,8 +201,8 @@ Control {
 | 
			
		||||
                    MouseArea {
 | 
			
		||||
                        z: 2
 | 
			
		||||
                        anchors.fill: parent
 | 
			
		||||
                        onClicked: function(mouse) {
 | 
			
		||||
                            CurrentConversation.scrollToMsg(ReplyTo)
 | 
			
		||||
                        onClicked: function (mouse) {
 | 
			
		||||
                            CurrentConversation.scrollToMsg(ReplyTo);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@ -205,13 +212,13 @@ Control {
 | 
			
		||||
        RowLayout {
 | 
			
		||||
            id: msgRowlayout
 | 
			
		||||
 | 
			
		||||
            Layout.preferredHeight: innerContent.height + root.extraHeight
 | 
			
		||||
            Layout.preferredHeight: innerContent.height + root.extraHeight + (emojiReactions.emojis === "" ? 0 : emojiReactions.height - 8)
 | 
			
		||||
            Layout.topMargin: ((seq === MsgSeq.first || seq === MsgSeq.single) && !root.isReply) ? 6 : 0
 | 
			
		||||
 | 
			
		||||
            Item {
 | 
			
		||||
                id: avatarBlock
 | 
			
		||||
 | 
			
		||||
                Layout.preferredWidth: isOutgoing ? 0 : avatar.width + hPadding/3
 | 
			
		||||
                Layout.preferredWidth: isOutgoing ? 0 : avatar.width + hPadding / 3
 | 
			
		||||
                Layout.preferredHeight: isOutgoing ? 0 : bubble.height
 | 
			
		||||
                Avatar {
 | 
			
		||||
                    id: avatar
 | 
			
		||||
@ -238,7 +245,7 @@ Control {
 | 
			
		||||
                    hoverEnabled: true
 | 
			
		||||
                    onClicked: function (mouse) {
 | 
			
		||||
                        if (root.hoveredLink) {
 | 
			
		||||
                            MessagesAdapter.openUrl(root.hoveredLink)
 | 
			
		||||
                            MessagesAdapter.openUrl(root.hoveredLink);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    property bool bubbleHovered: containsMouse || textHovered
 | 
			
		||||
@ -276,30 +283,24 @@ Control {
 | 
			
		||||
                        anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
                        anchors.right: isOutgoing ? optionButtonItem.right : undefined
 | 
			
		||||
                        anchors.left: !isOutgoing ? optionButtonItem.left : undefined
 | 
			
		||||
                        visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" &&
 | 
			
		||||
                                    (
 | 
			
		||||
                                        bubbleArea.bubbleHovered
 | 
			
		||||
                                        || hovered
 | 
			
		||||
                                        || reply.hovered
 | 
			
		||||
                                        || bgHandler.hovered
 | 
			
		||||
                                    )
 | 
			
		||||
                        visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" && (bubbleArea.bubbleHovered || hovered || reply.hovered || bgHandler.hovered)
 | 
			
		||||
                        source: JamiResources.more_vert_24dp_svg
 | 
			
		||||
                        width: optionButtonItem.width / 2
 | 
			
		||||
                        height: optionButtonItem.height
 | 
			
		||||
 | 
			
		||||
                        onClicked: {
 | 
			
		||||
                            var component = Qt.createComponent("qrc:/commoncomponents/MessageOptionsPopup.qml")
 | 
			
		||||
                            var component = Qt.createComponent("qrc:/commoncomponents/MessageOptionsPopup.qml");
 | 
			
		||||
                            var obj = component.createObject(bubble, {
 | 
			
		||||
                                                                 "emojiReplied": Qt.binding(() => emojiReaction.emojiTexts),
 | 
			
		||||
                                                                 "isOutgoing": isOutgoing,
 | 
			
		||||
                                                                 "msgId": Id,
 | 
			
		||||
                                                                 "msgBody": Body,
 | 
			
		||||
                                                                 "type": Type,
 | 
			
		||||
                                                                 "transferName": TransferName,
 | 
			
		||||
                                                                 "msgBubble": bubble,
 | 
			
		||||
                                                                 "listView": listView
 | 
			
		||||
                                                             })
 | 
			
		||||
                            obj.open()
 | 
			
		||||
                                    "emojiReactions": emojiReactions,
 | 
			
		||||
                                    "isOutgoing": isOutgoing,
 | 
			
		||||
                                    "msgId": Id,
 | 
			
		||||
                                    "msgBody": Body,
 | 
			
		||||
                                    "type": Type,
 | 
			
		||||
                                    "transferName": TransferName,
 | 
			
		||||
                                    "msgBubble": bubble,
 | 
			
		||||
                                    "listView": listView
 | 
			
		||||
                                });
 | 
			
		||||
                            obj.open();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -315,17 +316,11 @@ Control {
 | 
			
		||||
                        anchors.verticalCenter: parent.verticalCenter
 | 
			
		||||
                        anchors.right: isOutgoing ? more.left : undefined
 | 
			
		||||
                        anchors.left: !isOutgoing ? more.right : undefined
 | 
			
		||||
                        visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" &&
 | 
			
		||||
                                    (
 | 
			
		||||
                                        bubbleArea.bubbleHovered
 | 
			
		||||
                                        || hovered
 | 
			
		||||
                                        || more.hovered
 | 
			
		||||
                                        || bgHandler.hovered
 | 
			
		||||
                                    )
 | 
			
		||||
                        visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" && (bubbleArea.bubbleHovered || hovered || more.hovered || bgHandler.hovered)
 | 
			
		||||
 | 
			
		||||
                        onClicked: {
 | 
			
		||||
                            MessagesAdapter.editId = ""
 | 
			
		||||
                            MessagesAdapter.replyToId = Id
 | 
			
		||||
                            MessagesAdapter.editId = "";
 | 
			
		||||
                            MessagesAdapter.replyToId = Id;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@ -335,21 +330,11 @@ Control {
 | 
			
		||||
 | 
			
		||||
                    property bool isEdited: PreviousBodies.length !== 0
 | 
			
		||||
                    visible: !IsEmojiOnly
 | 
			
		||||
                    z:-1
 | 
			
		||||
                    z: -1
 | 
			
		||||
                    out: isOutgoing
 | 
			
		||||
                    type: seq
 | 
			
		||||
                    isReply: root.isReply
 | 
			
		||||
 | 
			
		||||
                    function getBaseColor() {
 | 
			
		||||
                        var baseColor = isOutgoing ? CurrentConversation.color : JamiTheme.messageInBgColor
 | 
			
		||||
                        if (Id === MessagesAdapter.replyToId || Id === MessagesAdapter.editId) {
 | 
			
		||||
                            // If we are replying to or editing the message
 | 
			
		||||
                            return Qt.darker(baseColor, 1.5)
 | 
			
		||||
                        }
 | 
			
		||||
                        return baseColor
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    color: getBaseColor()
 | 
			
		||||
                    color: root.getBaseColor()
 | 
			
		||||
                    radius: msgRadius
 | 
			
		||||
                    anchors.right: isOutgoing ? parent.right : undefined
 | 
			
		||||
                    anchors.top: parent.top
 | 
			
		||||
@ -358,6 +343,54 @@ Control {
 | 
			
		||||
                    height: innerContent.childrenRect.height + (visible ? root.extraHeight : 0)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                EmojiReactions {
 | 
			
		||||
                    id: emojiReactions
 | 
			
		||||
 | 
			
		||||
                    anchors.top: bubble.bottom
 | 
			
		||||
                    anchors.topMargin: -8
 | 
			
		||||
 | 
			
		||||
                    height: contentHeight + 5
 | 
			
		||||
                    reactions: Reactions
 | 
			
		||||
                    borderColor: root.getBaseColor()
 | 
			
		||||
                    maxWidth: 2 / 3 * maxMsgWidth - JamiTheme.emojiMargins
 | 
			
		||||
 | 
			
		||||
                    state: root.isOutgoing ? "anchorsRight" : (emojiReactions.width > bubble.width - JamiTheme.emojiMargins ? "anchorsLeft" : "anchorsRight")
 | 
			
		||||
 | 
			
		||||
                    TapHandler {
 | 
			
		||||
                        onTapped: {
 | 
			
		||||
                            reactionPopup.open();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    states: [
 | 
			
		||||
                        State {
 | 
			
		||||
                            name: "anchorsRight"
 | 
			
		||||
                            AnchorChanges {
 | 
			
		||||
                                target: emojiReactions
 | 
			
		||||
                                anchors.right: bubble.right
 | 
			
		||||
                                anchors.left: undefined
 | 
			
		||||
                            }
 | 
			
		||||
                            PropertyChanges {
 | 
			
		||||
                                target: emojiReactions
 | 
			
		||||
                                anchors.rightMargin: JamiTheme.emojiMargins
 | 
			
		||||
                                anchors.leftMargin: 0
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        State {
 | 
			
		||||
                            name: "anchorsLeft"
 | 
			
		||||
                            AnchorChanges {
 | 
			
		||||
                                target: emojiReactions
 | 
			
		||||
                                anchors.right: undefined
 | 
			
		||||
                                anchors.left: bubble.left
 | 
			
		||||
                            }
 | 
			
		||||
                            PropertyChanges {
 | 
			
		||||
                                target: emojiReactions
 | 
			
		||||
                                anchors.rightMargin: 0
 | 
			
		||||
                                anchors.leftMargin: JamiTheme.emojiMargins
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Rectangle {
 | 
			
		||||
                    id: bg
 | 
			
		||||
@ -412,8 +445,8 @@ Control {
 | 
			
		||||
                    target: CurrentConversation
 | 
			
		||||
                    function onScrollTo(id) {
 | 
			
		||||
                        if (id !== root.id)
 | 
			
		||||
                            return
 | 
			
		||||
                        selectAnimation.start()
 | 
			
		||||
                            return;
 | 
			
		||||
                        selectAnimation.start();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -444,10 +477,10 @@ Control {
 | 
			
		||||
 | 
			
		||||
                    width: {
 | 
			
		||||
                        if (root.readers.length === 0)
 | 
			
		||||
                            return 0
 | 
			
		||||
                        var nbAvatars = root.readers.length
 | 
			
		||||
                        var margin = JamiTheme.avatarReadReceiptSize / 3
 | 
			
		||||
                        return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin
 | 
			
		||||
                            return 0;
 | 
			
		||||
                        var nbAvatars = root.readers.length;
 | 
			
		||||
                        var margin = JamiTheme.avatarReadReceiptSize / 3;
 | 
			
		||||
                        return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin;
 | 
			
		||||
                    }
 | 
			
		||||
                    height: JamiTheme.avatarReadReceiptSize
 | 
			
		||||
 | 
			
		||||
@ -457,25 +490,6 @@ Control {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EmojiReactions {
 | 
			
		||||
            id: emojiReaction
 | 
			
		||||
 | 
			
		||||
            property bool isOutgoing: Author === CurrentAccount.uri
 | 
			
		||||
            Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
 | 
			
		||||
            Layout.rightMargin: isOutgoing ? status.width : undefined
 | 
			
		||||
            Layout.leftMargin: !isOutgoing ? avatarBlock.width : undefined
 | 
			
		||||
            Layout.topMargin: - contentHeight/4
 | 
			
		||||
            Layout.preferredHeight: contentHeight + 5
 | 
			
		||||
            Layout.preferredWidth: contentWidth
 | 
			
		||||
            emojiReaction: Reactions
 | 
			
		||||
 | 
			
		||||
            TapHandler {
 | 
			
		||||
                onTapped: {
 | 
			
		||||
                    reactionPopup.open()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ListView {
 | 
			
		||||
            id: infoCell
 | 
			
		||||
 | 
			
		||||
@ -483,10 +497,10 @@ Control {
 | 
			
		||||
            orientation: ListView.Horizontal
 | 
			
		||||
            Layout.preferredHeight: {
 | 
			
		||||
                if (showTime || seq === MsgSeq.last)
 | 
			
		||||
                    return contentHeight + timestampItem.contentHeight
 | 
			
		||||
                    return contentHeight + timestampItem.contentHeight;
 | 
			
		||||
                else if (readsMultiple.visible)
 | 
			
		||||
                    return JamiTheme.avatarReadReceiptSize
 | 
			
		||||
                return 0
 | 
			
		||||
                    return JamiTheme.avatarReadReceiptSize;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ReadStatus {
 | 
			
		||||
@ -494,14 +508,14 @@ Control {
 | 
			
		||||
                visible: root.readers.length > 1 && CurrentAccount.sendReadReceipt
 | 
			
		||||
                width: {
 | 
			
		||||
                    if (root.readers.length === 0)
 | 
			
		||||
                        return 0
 | 
			
		||||
                    var nbAvatars = root.readers.length
 | 
			
		||||
                    var margin = JamiTheme.avatarReadReceiptSize / 3
 | 
			
		||||
                    return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin
 | 
			
		||||
                        return 0;
 | 
			
		||||
                    var nbAvatars = root.readers.length;
 | 
			
		||||
                    var margin = JamiTheme.avatarReadReceiptSize / 3;
 | 
			
		||||
                    return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                anchors.right: parent.right
 | 
			
		||||
                anchors.top : parent.top
 | 
			
		||||
                anchors.top: parent.top
 | 
			
		||||
                anchors.topMargin: 1
 | 
			
		||||
                readers: root.readers
 | 
			
		||||
            }
 | 
			
		||||
@ -511,7 +525,7 @@ Control {
 | 
			
		||||
    EmojiReactionPopup {
 | 
			
		||||
        id: reactionPopup
 | 
			
		||||
 | 
			
		||||
        emojiReaction: Reactions
 | 
			
		||||
        reactions: Reactions
 | 
			
		||||
        msgId: Id
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										134
									
								
								src/app/commoncomponents/SharePopup.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,134 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2023 Savoir-faire Linux Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
import QtQuick
 | 
			
		||||
import QtQuick.Layouts
 | 
			
		||||
import QtQuick.Controls
 | 
			
		||||
import Qt.labs.platform
 | 
			
		||||
import Qt5Compat.GraphicalEffects
 | 
			
		||||
import net.jami.Models 1.1
 | 
			
		||||
import net.jami.Adapters 1.1
 | 
			
		||||
import net.jami.Constants 1.1
 | 
			
		||||
import "../mainview/components"
 | 
			
		||||
 | 
			
		||||
Popup {
 | 
			
		||||
    id: root
 | 
			
		||||
    padding: 0
 | 
			
		||||
    property list<Action> menuMoreButton
 | 
			
		||||
 | 
			
		||||
    height: childrenRect.height
 | 
			
		||||
    width: childrenRect.width
 | 
			
		||||
 | 
			
		||||
    focus: true
 | 
			
		||||
    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
 | 
			
		||||
 | 
			
		||||
    Rectangle {
 | 
			
		||||
        id: rect
 | 
			
		||||
 | 
			
		||||
        color: JamiTheme.primaryBackgroundColor
 | 
			
		||||
        border.color: JamiTheme.chatViewFooterRectangleBorderColor
 | 
			
		||||
        border.width: 2
 | 
			
		||||
        radius: 5
 | 
			
		||||
        height: listViewMoreButton.childrenRect.height + 16
 | 
			
		||||
        width: listViewMoreButton.childrenRect.width + 16
 | 
			
		||||
 | 
			
		||||
        ListView {
 | 
			
		||||
            id: listViewMoreButton
 | 
			
		||||
 | 
			
		||||
            anchors.centerIn: parent
 | 
			
		||||
            orientation: ListView.Vertical
 | 
			
		||||
 | 
			
		||||
            spacing: 0
 | 
			
		||||
 | 
			
		||||
            width: contentItem.childrenRect.width
 | 
			
		||||
            height: contentHeight
 | 
			
		||||
 | 
			
		||||
            model: menuMoreButton
 | 
			
		||||
 | 
			
		||||
            Rectangle {
 | 
			
		||||
                z: -1
 | 
			
		||||
                anchors.fill: parent
 | 
			
		||||
                color: "transparent"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            onCountChanged: {
 | 
			
		||||
                for (var i = 0; i < count; i++) {
 | 
			
		||||
                    var item = listViewMoreButton.itemAtIndex(i);
 | 
			
		||||
                    item.width = listViewMoreButton.width;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            delegate: ItemDelegate {
 | 
			
		||||
                id: control
 | 
			
		||||
 | 
			
		||||
                text: modelData.toolTip
 | 
			
		||||
 | 
			
		||||
                contentItem: RowLayout {
 | 
			
		||||
                    Rectangle {
 | 
			
		||||
                        id: image
 | 
			
		||||
                        width: 26
 | 
			
		||||
                        height: 26
 | 
			
		||||
                        radius: 5
 | 
			
		||||
                        color: JamiTheme.transparentColor
 | 
			
		||||
                        ResponsiveImage {
 | 
			
		||||
                            anchors.fill: parent
 | 
			
		||||
                            source: modelData.iconSrc
 | 
			
		||||
                            color: control.hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Text {
 | 
			
		||||
                        Layout.alignment: Qt.AlignLeft
 | 
			
		||||
                        text: control.text
 | 
			
		||||
                        color: control.hovered ? JamiTheme.chatViewFooterImgHoverColor : "#7f7f7f"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                background: Rectangle {
 | 
			
		||||
                    color: control.hovered ? JamiTheme.showMoreButtonOpenColor : JamiTheme.transparentColor
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                action: modelData
 | 
			
		||||
 | 
			
		||||
                onClicked: {
 | 
			
		||||
                    root.close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    background: Rectangle {
 | 
			
		||||
        anchors.fill: parent
 | 
			
		||||
        color: JamiTheme.transparentColor
 | 
			
		||||
        radius: 5
 | 
			
		||||
        z: -1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enter: Transition {
 | 
			
		||||
        NumberAnimation {
 | 
			
		||||
            properties: "opacity"
 | 
			
		||||
            from: 0.0
 | 
			
		||||
            to: 1.0
 | 
			
		||||
            duration: JamiTheme.shortFadeDuration
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    exit: Transition {
 | 
			
		||||
        NumberAnimation {
 | 
			
		||||
            properties: "opacity"
 | 
			
		||||
            from: 1.0
 | 
			
		||||
            to: 0.0
 | 
			
		||||
            duration: JamiTheme.shortFadeDuration
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -44,9 +44,6 @@ BaseModalDialog {
 | 
			
		||||
        open();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    width: Math.max(JamiTheme.preferredDialogWidth, buttonTitles.length * (JamiTheme.preferredFieldWidth / 2 + JamiTheme.preferredMarginSize))
 | 
			
		||||
    height: JamiTheme.preferredDialogHeight / 2 - JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
    popupContent: ColumnLayout {
 | 
			
		||||
        Label {
 | 
			
		||||
            id: infoTextLabel
 | 
			
		||||
@ -72,12 +69,10 @@ BaseModalDialog {
 | 
			
		||||
 | 
			
		||||
            data: innerContentData
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RowLayout {
 | 
			
		||||
            spacing: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
            Layout.preferredHeight: contentHeight
 | 
			
		||||
            Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
 | 
			
		||||
            Layout.bottomMargin: JamiTheme.preferredMarginSize
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,9 +32,8 @@ Item {
 | 
			
		||||
    property int spinningAnimationWidth: 4
 | 
			
		||||
    property real outerCutRadius: root.height / 2
 | 
			
		||||
    property int spinningAnimationDuration: 1000
 | 
			
		||||
 | 
			
		||||
    property color color: "white"
 | 
			
		||||
    visible: mode !== SpinningAnimation.Mode.Disabled
 | 
			
		||||
 | 
			
		||||
    ConicalGradient {
 | 
			
		||||
        id: conicalGradientOne
 | 
			
		||||
 | 
			
		||||
@ -48,7 +47,7 @@ Item {
 | 
			
		||||
            }
 | 
			
		||||
            GradientStop {
 | 
			
		||||
                position: 1.0
 | 
			
		||||
                color: "white"
 | 
			
		||||
                color: mode === SpinningAnimation.Mode.Disabled ? "transparent" : root.color
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -90,7 +89,7 @@ Item {
 | 
			
		||||
            }
 | 
			
		||||
            GradientStop {
 | 
			
		||||
                position: 1.0
 | 
			
		||||
                color: "white"
 | 
			
		||||
                color: mode === SpinningAnimation.Mode.Disabled ? "transparent" : root.color
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,15 +30,14 @@ SBSMessageBase {
 | 
			
		||||
 | 
			
		||||
    property bool isRemoteImage
 | 
			
		||||
    property bool isEmojiOnly: IsEmojiOnly
 | 
			
		||||
    property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth
 | 
			
		||||
    property string colorUrl: UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark
 | 
			
		||||
    property string colorText: UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
 | 
			
		||||
 | 
			
		||||
    Connections {
 | 
			
		||||
        target: bubble
 | 
			
		||||
        function onColorChanged(color) {
 | 
			
		||||
            root.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark
 | 
			
		||||
            root.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
 | 
			
		||||
            root.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
 | 
			
		||||
            root.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
 | 
			
		||||
            // Update parsed body with correct colors
 | 
			
		||||
            if (Body !== "")
 | 
			
		||||
                MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
 | 
			
		||||
@ -65,7 +64,7 @@ SBSMessageBase {
 | 
			
		||||
            text: {
 | 
			
		||||
                if (Body !== "" && ParsedBody.length === 0) {
 | 
			
		||||
                    MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
 | 
			
		||||
                    return ""
 | 
			
		||||
                    return "";
 | 
			
		||||
                }
 | 
			
		||||
                return (ParsedBody !== "") ? ParsedBody : "<i>(" + JamiStrings.deletedMessage + ")</i>";
 | 
			
		||||
            }
 | 
			
		||||
@ -86,7 +85,7 @@ SBSMessageBase {
 | 
			
		||||
 | 
			
		||||
            wrapMode: Label.WrapAtWordBoundaryOrAnywhere
 | 
			
		||||
            selectByMouse: true
 | 
			
		||||
            font.pixelSize: isEmojiOnly ? JamiTheme.chatviewEmojiSize : JamiTheme.emojiBubbleSize
 | 
			
		||||
            font.pointSize: isEmojiOnly ? JamiTheme.chatviewEmojiSize : JamiTheme.mediumFontSize
 | 
			
		||||
            font.hintingPreference: Font.PreferNoHinting
 | 
			
		||||
            renderType: Text.NativeRendering
 | 
			
		||||
            textFormat: Text.RichText
 | 
			
		||||
@ -277,5 +276,5 @@ SBSMessageBase {
 | 
			
		||||
            duration: 100
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Component.onCompleted: opacity = 1;
 | 
			
		||||
    Component.onCompleted: opacity = 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -49,8 +49,7 @@ ColumnLayout {
 | 
			
		||||
 | 
			
		||||
        Layout.preferredHeight: childrenRect.height
 | 
			
		||||
        Layout.fillWidth: true
 | 
			
		||||
        Layout.topMargin: JamiTheme.dayTimestampTopMargin
 | 
			
		||||
        Layout.bottomMargin: formattedTimeLabel.visible ? 0 : JamiTheme.dayTimestampBottomMargin
 | 
			
		||||
        Layout.topMargin: 30
 | 
			
		||||
 | 
			
		||||
        Rectangle {
 | 
			
		||||
            id: line
 | 
			
		||||
@ -95,12 +94,12 @@ ColumnLayout {
 | 
			
		||||
        id: formattedTimeLabel
 | 
			
		||||
 | 
			
		||||
        text: formattedTime
 | 
			
		||||
        Layout.bottomMargin: JamiTheme.timestampBottomMargin
 | 
			
		||||
        Layout.topMargin: JamiTheme.timestampTopMargin
 | 
			
		||||
        Layout.topMargin: 30
 | 
			
		||||
        Layout.bottomMargin: 30
 | 
			
		||||
        Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
 | 
			
		||||
        color: JamiTheme.timestampColor
 | 
			
		||||
        color: JamiTheme.chatviewSecondaryInformationColor
 | 
			
		||||
        visible: showTime || showDay
 | 
			
		||||
        Layout.preferredHeight: visible * implicitHeight
 | 
			
		||||
        font.pointSize: JamiTheme.timestampFont
 | 
			
		||||
        font.pointSize: JamiTheme.smallFontSize
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,7 @@ ModalTextEdit {
 | 
			
		||||
 | 
			
		||||
    property bool isActive: false
 | 
			
		||||
    property string infohash: CurrentAccount.uri
 | 
			
		||||
    property string accountId: CurrentAccount.id
 | 
			
		||||
    property string registeredName: CurrentAccount.registeredName
 | 
			
		||||
    staticText: root.isActive ? registeredName : (registeredName ? registeredName : infohash)
 | 
			
		||||
 | 
			
		||||
@ -118,7 +119,7 @@ ModalTextEdit {
 | 
			
		||||
        onTriggered: {
 | 
			
		||||
            if (dynamicText.length !== 0) {
 | 
			
		||||
                nameRegistrationState = UsernameTextEdit.NameRegistrationState.SEARCHING;
 | 
			
		||||
                NameDirectory.lookupName(CurrentAccount.id, dynamicText);
 | 
			
		||||
                NameDirectory.lookupName(root.accountId, dynamicText);
 | 
			
		||||
            } else {
 | 
			
		||||
                nameRegistrationState = UsernameTextEdit.NameRegistrationState.BLANK;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ Item {
 | 
			
		||||
 | 
			
		||||
        antialiasing: true
 | 
			
		||||
        anchors.fill: parent
 | 
			
		||||
        opacity: videoProvider.renderers[rendererId] !== undefined
 | 
			
		||||
        opacity: videoProvider.activeRenderers[rendererId] === true
 | 
			
		||||
        visible: opacity
 | 
			
		||||
 | 
			
		||||
        fillMode: crop ? VideoOutput.PreserveAspectCrop : VideoOutput.PreserveAspectFit
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@
 | 
			
		||||
pragma Singleton
 | 
			
		||||
import QtQuick
 | 
			
		||||
import net.jami.Adapters 1.1
 | 
			
		||||
import net.jami.Enums 1.1
 | 
			
		||||
 | 
			
		||||
Item {
 | 
			
		||||
    property string qmlFilePrefix: "file:/"
 | 
			
		||||
@ -69,4 +70,9 @@ Item {
 | 
			
		||||
    function clamp(val, min, max) {
 | 
			
		||||
        return Math.min(Math.max(val, min), max);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function isDonationBannerVisible() {
 | 
			
		||||
        // The banner is visible if the current date is after the date set in the settings
 | 
			
		||||
        return new Date() > new Date(Date.parse(UtilsAdapter.getAppValue(Settings.Key.DonateVisibleDate)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,6 @@ Item {
 | 
			
		||||
    property string incomingVideoCallFrom: qsTr("Incoming video call from {}")
 | 
			
		||||
    property string startSwarm: qsTr("Start swarm")
 | 
			
		||||
    property string createSwarm: qsTr("Create swarm")
 | 
			
		||||
    property string showInvitations: qsTr("Show invitations")
 | 
			
		||||
    property string invitations: qsTr("Invitations")
 | 
			
		||||
    property string description: qsTr("Jami is a universal communication platform, with privacy as its foundation, that relies on a free distributed network for everyone.")
 | 
			
		||||
    property string updateToSwarm: qsTr("Migrating to the Swarm technology will enable synchronizing this conversation across multiple devices and improve reliability. The legacy conversation history will be cleared in the process.")
 | 
			
		||||
@ -46,13 +45,10 @@ Item {
 | 
			
		||||
    property string reconnectTry: qsTr("Trying to reconnect to the Jami daemon (jamid)…")
 | 
			
		||||
 | 
			
		||||
    // AboutPopUp
 | 
			
		||||
    property string version: qsTr("Version") + (UpdateManager.isCurrentVersionBeta() ? " (Beta)" : "")
 | 
			
		||||
    property string companyDeclarationYear: declarationYear + " " + companyName
 | 
			
		||||
    property string version: qsTr("Version") + (AppVersionManager.isCurrentVersionBeta() ? " (Beta)" : "")
 | 
			
		||||
    property string declarationYear: "© 2015-2023"
 | 
			
		||||
    property string companyName: "Savoir-faire Linux Inc."
 | 
			
		||||
    property string slogan: "Világfa"
 | 
			
		||||
    property string slogan: "Eleutheria"
 | 
			
		||||
    property string declaration: qsTr("Jami is a free universal communication software that respects the freedom and privacy of its users.")
 | 
			
		||||
    property string credits: qsTr("Credits")
 | 
			
		||||
 | 
			
		||||
    // AccountComboBox
 | 
			
		||||
    property string displayQRCode: qsTr("Display QR code")
 | 
			
		||||
@ -84,7 +80,6 @@ Item {
 | 
			
		||||
    property string autoAnswerCalls: qsTr("Automatically answer calls")
 | 
			
		||||
    property string enableCustomRingtone: qsTr("Enable custom ringtone")
 | 
			
		||||
    property string selectCustomRingtone: qsTr("Select custom ringtone")
 | 
			
		||||
    property string addCustomRingtone: qsTr("Add a custom ringtone")
 | 
			
		||||
    property string selectNewRingtone: qsTr("Select a new ringtone")
 | 
			
		||||
    property string certificateFile: qsTr("Certificate File (*.crt)")
 | 
			
		||||
    property string audioFile: qsTr("Audio File (*.wav *.ogg *.opus *.mp3 *.aiff *.wma)")
 | 
			
		||||
@ -100,7 +95,6 @@ Item {
 | 
			
		||||
    // AdvancedSIPSecuritySettings && AdvancedJamiSecuritySettings
 | 
			
		||||
    property string security: qsTr("Security")
 | 
			
		||||
    property string enableSDES: qsTr("Enable SDES key exchange")
 | 
			
		||||
    property string fallbackRTP: qsTr("Allow fallback on RTP")
 | 
			
		||||
    property string encryptNegotiation: qsTr("Encrypt negotiation (TLS)")
 | 
			
		||||
    property string caCertificate: qsTr("CA certificate")
 | 
			
		||||
    property string userCertificate: qsTr("User certificate")
 | 
			
		||||
@ -110,7 +104,6 @@ Item {
 | 
			
		||||
    property string verifyCertificatesClient: qsTr("Verify server TLS certificates")
 | 
			
		||||
    property string tlsRequireConnections: qsTr("Require certificate for incoming TLS connections")
 | 
			
		||||
    property string disableSecureDlgCheck: qsTr("Disable secure dialog check for incoming TLS data")
 | 
			
		||||
    property string audioDeviceSelector: qsTr("Audio input device selector")
 | 
			
		||||
    property string selectPrivateKey: qsTr("Select a private key")
 | 
			
		||||
    property string selectUserCert: qsTr("Select a user certificate")
 | 
			
		||||
    property string selectCACert: qsTr("Select a CA certificate")
 | 
			
		||||
@ -167,7 +160,7 @@ Item {
 | 
			
		||||
    property string back: qsTr("Back")
 | 
			
		||||
    property string accountSettingsMenuTitle: qsTr("Account")
 | 
			
		||||
    property string generalSettingsTitle: qsTr("General")
 | 
			
		||||
    property string pluginSettingsTitle: qsTr("Plugins")
 | 
			
		||||
    property string pluginSettingsTitle: qsTr("Extensions")
 | 
			
		||||
    property string enableAccountSettingsTitle: qsTr("Enable account")
 | 
			
		||||
    property string manageAccountSettingsTitle: qsTr("Manage account")
 | 
			
		||||
    property string linkedDevicesSettingsTitle: qsTr("Linked devices")
 | 
			
		||||
@ -197,21 +190,17 @@ Item {
 | 
			
		||||
    property string selectFPS: qsTr("Select video frame rate (frames per second)")
 | 
			
		||||
    property string enableHWAccel: qsTr("Enable hardware acceleration")
 | 
			
		||||
    property string mirrorLocalVideo: qsTr("Mirror local video")
 | 
			
		||||
    property string previewUnavailable: qsTr("Preview unavailable")
 | 
			
		||||
    property string screenSharing: qsTr("Screen sharing")
 | 
			
		||||
    property string selectScreenSharingFPS: qsTr("Select screen sharing frame rate (frames per second)")
 | 
			
		||||
    property string noVideo: qsTr("no video")
 | 
			
		||||
 | 
			
		||||
    // BackupKeyPage
 | 
			
		||||
    property string whyBackupAccount: qsTr("Why should I back-up this account?")
 | 
			
		||||
    property string backupAccountInfos: qsTr("Your account only exists on this device. " + "If you lose your device or uninstall the application, " + "your account will be deleted and CANNOT be recovered. " + "You can back up your account now or later (in the Account Settings).")
 | 
			
		||||
    property string backupAccountInfos: qsTr("Your account only exists on this device. " + "If you lose your device or uninstall the application, " + "your account will be deleted and CANNOT be recovered. " + "You can <a href='blank'> back up your account </a> now or later (in the Account Settings).")
 | 
			
		||||
    property string backupAccountHere: qsTr("Back up account here")
 | 
			
		||||
    property string backupAccountBtn: qsTr("Back up account")
 | 
			
		||||
    property string skip: qsTr("Skip")
 | 
			
		||||
    property string success: qsTr("Success")
 | 
			
		||||
    property string error: qsTr("Error")
 | 
			
		||||
    property string neverShowAgain: qsTr("Never show me this again")
 | 
			
		||||
    property string recommended: qsTr("Recommended")
 | 
			
		||||
    property string jamiArchiveFiles: qsTr("Jami archive files (*.gz)")
 | 
			
		||||
    property string allFiles: qsTr("All files (*)")
 | 
			
		||||
 | 
			
		||||
@ -223,8 +212,6 @@ Item {
 | 
			
		||||
    // CallOverlay
 | 
			
		||||
    property string isRecording: qsTr("is recording")
 | 
			
		||||
    property string areRecording: qsTr("are recording")
 | 
			
		||||
    property string peerStoppedRecording: qsTr("Peer stopped recording")
 | 
			
		||||
    property string isCallingYou: qsTr("is calling you")
 | 
			
		||||
    property string mute: qsTr("Mute")
 | 
			
		||||
    property string unmute: qsTr("Unmute")
 | 
			
		||||
    property string pauseCall: qsTr("Pause call")
 | 
			
		||||
@ -267,17 +254,14 @@ Item {
 | 
			
		||||
    property string confirmBlockConversation: qsTr("Would you really like to block this conversation?")
 | 
			
		||||
    property string removeContact: qsTr("Remove contact")
 | 
			
		||||
    property string blockContact: qsTr("Block contact")
 | 
			
		||||
    property string blockSwarm: qsTr("Block swarm")
 | 
			
		||||
    property string convDetails: qsTr("Conversation details")
 | 
			
		||||
    property string contactDetails: qsTr("Contact details")
 | 
			
		||||
 | 
			
		||||
    // CallViewContextMenu
 | 
			
		||||
    property string hold: qsTr("Hold")
 | 
			
		||||
    property string sipInputPanel: qsTr("Sip input panel")
 | 
			
		||||
    property string transferCall: qsTr("Transfer call")
 | 
			
		||||
    property string stopRec: qsTr("Stop recording")
 | 
			
		||||
    property string startRec: qsTr("Start recording")
 | 
			
		||||
    property string exitFullScreen: qsTr("Exit full screen")
 | 
			
		||||
    property string viewFullScreen: qsTr("View full screen")
 | 
			
		||||
    property string shareScreen: qsTr("Share screen")
 | 
			
		||||
    property string shareWindow: qsTr("Share window")
 | 
			
		||||
@ -312,10 +296,6 @@ Item {
 | 
			
		||||
    // Share location/position
 | 
			
		||||
    property string shareLocation: qsTr("Share location")
 | 
			
		||||
    property string stopSharingLocation: qsTr("Stop sharing")
 | 
			
		||||
    property string shortSharing: qsTr("10 minutes")
 | 
			
		||||
    property string longSharing: qsTr("One hour")
 | 
			
		||||
    property string minutesLeft: qsTr("%1 minutes left")
 | 
			
		||||
    property string minuteLeft: qsTr("%1 minute left")
 | 
			
		||||
    property string locationServicesError: qsTr("Your precise location could not be determined.\nIn Device Settings, please turn on \"Location Services\".\nOther participants' location can still be received.")
 | 
			
		||||
    property string locationServicesClosedError: qsTr("Your precise location could not be determined. Please check your Internet connection.")
 | 
			
		||||
    property string stopAllSharings: qsTr("Turn off location sharing")
 | 
			
		||||
@ -325,20 +305,22 @@ Item {
 | 
			
		||||
    property string unpinStopSharingTooltip: qsTr("Pin map to be able to share location or to turn off location in specific conversations")
 | 
			
		||||
    property string stopSharingSeveralConversationTooltip: qsTr("Location is shared in several conversations, click to choose how to turn off location sharing")
 | 
			
		||||
    property string shareLocationToolTip: qsTr("Share location to participants of this conversation (%1)")
 | 
			
		||||
    property string minimizeMapTooltip: qsTr("Minimize")
 | 
			
		||||
    property string maximizeMapTooltip: qsTr("Maximize")
 | 
			
		||||
    property string reduceMapTooltip: qsTr("Reduce")
 | 
			
		||||
    property string extendMapTooltip: qsTr("Extend")
 | 
			
		||||
    property string dragMapTooltip: qsTr("Drag")
 | 
			
		||||
    property string centerMapTooltip: qsTr("Center")
 | 
			
		||||
    property string closeMapTooltip: qsTr("Close")
 | 
			
		||||
    property string unpin: qsTr("Unpin")
 | 
			
		||||
    property string pinWindow: qsTr("Pin")
 | 
			
		||||
    property string positionShareDuration: qsTr("Position share duration")
 | 
			
		||||
    property string positionShareLimit: qsTr("Limit the duration of location sharing")
 | 
			
		||||
    property string locationSharingLabel: qsTr("Location sharing")
 | 
			
		||||
    property string maxLocationDuration: qsTr("Unlimited")
 | 
			
		||||
    property string minLocationDuration: qsTr("1 min")
 | 
			
		||||
    property string xhourxmin: qsTr("%1h%2min")
 | 
			
		||||
    property string xhour: qsTr("%1h")
 | 
			
		||||
    property string xminxsec: qsTr("%1min%2s")
 | 
			
		||||
    property string xmin: qsTr("%1min")
 | 
			
		||||
    property string xsec: qsTr("%sec")
 | 
			
		||||
 | 
			
		||||
    // Chatview header
 | 
			
		||||
    property string hideChat: qsTr("Hide chat")
 | 
			
		||||
@ -367,24 +349,16 @@ Item {
 | 
			
		||||
 | 
			
		||||
    // ConnectToAccountManager
 | 
			
		||||
    property string enterJAMSURL: qsTr("Enter the Jami Account Management Server (JAMS) URL")
 | 
			
		||||
    property string required: qsTr("Required")
 | 
			
		||||
    property string jamiManagementServerURL: qsTr("Jami Account Management Server URL")
 | 
			
		||||
    property string jamsCredentials: qsTr("Enter JAMS credentials")
 | 
			
		||||
    property string connect: qsTr("Connect")
 | 
			
		||||
    property string creatingAccount: qsTr("Creating account…")
 | 
			
		||||
    property string backToWelcome: qsTr("Back to welcome page")
 | 
			
		||||
 | 
			
		||||
    // CreateAccountPage
 | 
			
		||||
    property string chooseName: qsTr("Choose name")
 | 
			
		||||
    property string chooseUsername: qsTr("Choose username")
 | 
			
		||||
    property string chooseAUsername: qsTr("Choose a username")
 | 
			
		||||
    property string chooseIdentifier: qsTr("Choose an identifier")
 | 
			
		||||
    property string identifierNotAvailable: qsTr("The identifier is not available")
 | 
			
		||||
    property string createPassword: qsTr("Encrypt account with password")
 | 
			
		||||
    property string createAccount: qsTr("Create account")
 | 
			
		||||
    property string confirmPassword: qsTr("Confirm password")
 | 
			
		||||
    property string notePasswordRecovery: qsTr("Choose a password to encrypt your account on this device. Note that the password CANNOT be recovered.")
 | 
			
		||||
    property string optional: qsTr("Optional")
 | 
			
		||||
    property string chooseUsernameForAccount: qsTr("You can choose a username to help others more easily find and reach you on Jami.")
 | 
			
		||||
    property string chooseUsernameForRV: qsTr("Choose a name for your rendezvous point")
 | 
			
		||||
    property string chooseAName: qsTr("Choose a name")
 | 
			
		||||
@ -408,7 +382,6 @@ Item {
 | 
			
		||||
    property string sipAccount: qsTr("SIP account")
 | 
			
		||||
    property string proxy: qsTr("Proxy")
 | 
			
		||||
    property string server: qsTr("Server")
 | 
			
		||||
    property string createSIPAccount: qsTr("Create SIP account")
 | 
			
		||||
    property string configureExistingSIP: qsTr("Configure an existing SIP account")
 | 
			
		||||
    property string personalizeAccount: qsTr("Personalize account")
 | 
			
		||||
    property string addSip: qsTr("Add SIP account")
 | 
			
		||||
@ -419,7 +392,7 @@ Item {
 | 
			
		||||
    // accountSettingsPages
 | 
			
		||||
    property string customizeAccountDescription: qsTr("Your profile is only shared with your contacts.\nYour picture and your nickname can be changed at all time in the settings of your account.")
 | 
			
		||||
    property string usernameAccountDescription: qsTr("A chosen username can help to be found more easily on Jami.\nIf a username is not chosen, a randomly generated 40-character identifier will be assigned to this account as a username. It is more difficult to be found and reached with this identifier.")
 | 
			
		||||
    property string ecryptAccountDescription: qsTr("Your Jami account is registered only on this device as an archive containing the keys of your account. Access to this archive can be protected by a password.")
 | 
			
		||||
    property string encryptAccountDescription: qsTr("Your Jami account is registered only on this device as an archive containing the keys of your account. Access to this archive can be protected by a password.")
 | 
			
		||||
    property string saveAccountTitle: qsTr("Backup account")
 | 
			
		||||
    property string saveAccountDescription: qsTr("This Jami account exists only on this device.\nThe account will be lost if this device is lost or the application is uninstalled. It is recommended to make a backup of this account.")
 | 
			
		||||
    property string deleteAccountTitle: qsTr("Delete your account")
 | 
			
		||||
@ -427,7 +400,6 @@ Item {
 | 
			
		||||
    property string linkedAccountList: qsTr("List of the devices that are linked to this account:")
 | 
			
		||||
    property string linkedThisDevice: qsTr("This device")
 | 
			
		||||
    property string linkedOtherDevices: qsTr("Other linked devices")
 | 
			
		||||
    property string linkedAccountDescription: qsTr("Linking this account to other devices enables it to be used on other devices.")
 | 
			
		||||
 | 
			
		||||
    // CurrentAccountSettings && AdvancedSettings
 | 
			
		||||
    property string backupSuccessful: qsTr("Backup successful")
 | 
			
		||||
@ -450,30 +422,28 @@ Item {
 | 
			
		||||
    property string encryptTitle: qsTr("Encrypt account with a password")
 | 
			
		||||
    property string encryptDescription: qsTr("A Jami account is created and stored locally only on this device, as an archive containing your account keys. Access to this archive can optionally be protected by a password.")
 | 
			
		||||
    property string encryptWarning: qsTr("Please note that if you lose your password, it CANNOT be recovered!")
 | 
			
		||||
    property string enterNickname: qsTr("Enter a nickname, surname...")
 | 
			
		||||
    property string enterNickname: qsTr("Enter a nickname, surname…")
 | 
			
		||||
    property string linkTitle: qsTr("Use this account on other devices")
 | 
			
		||||
    property string linkDescription: qsTr("This account is created and stored locally, if you want to use it on another device you have to link the new device to this account.")
 | 
			
		||||
    property string linkAnotherDevice: qsTr("Link device")
 | 
			
		||||
 | 
			
		||||
    // NameRegistrationDialog
 | 
			
		||||
    property string setUsername: qsTr("Set username")
 | 
			
		||||
    property string registeringName: qsTr("Registering name")
 | 
			
		||||
 | 
			
		||||
    // JamiUserIdentity
 | 
			
		||||
    property string registerAUsername: qsTr("Register a username")
 | 
			
		||||
    property string registerUsername: qsTr("Register username")
 | 
			
		||||
    property string identity: qsTr("Identity")
 | 
			
		||||
 | 
			
		||||
    // LinkedDevices
 | 
			
		||||
    property string tipLinkNewDevice: qsTr("Link a new device to this account")
 | 
			
		||||
    property string linkAnotherDevice: qsTr("Link a new device")
 | 
			
		||||
    property string linkNewDevice: qsTr("Exporting account…")
 | 
			
		||||
    property string linkDevice: qsTr("Exporting account…")
 | 
			
		||||
    property string removeDevice: qsTr("Remove Device")
 | 
			
		||||
    property string sureToRemoveDevice: qsTr("Are you sure you wish to remove this device?")
 | 
			
		||||
    property string yourPinIs: qsTr("Your PIN is:")
 | 
			
		||||
    property string linkDeviceNetWorkError: qsTr("Error connecting to the network.\nPlease try again later.")
 | 
			
		||||
 | 
			
		||||
    // BannedContacts
 | 
			
		||||
    property string tipBannedContactsDisplay: qsTr("Display banned contacts")
 | 
			
		||||
    property string banned: qsTr("Banned")
 | 
			
		||||
    property string tipBannedContactsHide: qsTr("Hide banned contacts")
 | 
			
		||||
    property string bannedContacts: qsTr("Banned contacts")
 | 
			
		||||
 | 
			
		||||
    // DeleteAccountDialog
 | 
			
		||||
@ -483,6 +453,7 @@ Item {
 | 
			
		||||
    // DeviceItemDelegate
 | 
			
		||||
    property string saveNewDeviceName: qsTr("Save")
 | 
			
		||||
    property string editDeviceName: qsTr("Edit")
 | 
			
		||||
    property string deviceName: qsTr("Device name:")
 | 
			
		||||
    property string unlinkDevice: qsTr("Remove")
 | 
			
		||||
    property string deviceId: qsTr("Device Id")
 | 
			
		||||
 | 
			
		||||
@ -492,16 +463,12 @@ Item {
 | 
			
		||||
    property string light: qsTr("Light")
 | 
			
		||||
    property string selectFolder: qsTr("Select a folder")
 | 
			
		||||
    property string enableNotifications: qsTr("Enable notifications")
 | 
			
		||||
    property string applicationTheme: qsTr("Application theme")
 | 
			
		||||
    property string showNotifications: qsTr("Show notifications")
 | 
			
		||||
    property string keepMinimized: qsTr("Minimize on close")
 | 
			
		||||
    property string tipRunStartup: qsTr("Run at system startup")
 | 
			
		||||
    property string runStartup: qsTr("Launch at startup")
 | 
			
		||||
    property string downloadFolder: qsTr("Choose download directory")
 | 
			
		||||
    property string tipChooseDownloadFolder: qsTr("Choose download directory")
 | 
			
		||||
    property string includeLocalVideo: qsTr("Include local video in recording")
 | 
			
		||||
    property string textZoom: qsTr("Text zoom")
 | 
			
		||||
    property string changeTextSize: qsTr("Change text size (%)")
 | 
			
		||||
    property string defaultSettings: qsTr("Default settings")
 | 
			
		||||
 | 
			
		||||
    // ChatviewSettings
 | 
			
		||||
@ -510,7 +477,6 @@ Item {
 | 
			
		||||
    property string displayHyperlinkPreviews: qsTr("Show link preview in conversations")
 | 
			
		||||
    property string displayHyperlinkPreviewsDescription: qsTr("Preview requires downloading content from third-party servers.")
 | 
			
		||||
 | 
			
		||||
    property string layout: qsTr("Layout")
 | 
			
		||||
    property string language: qsTr("User interface language")
 | 
			
		||||
    property string verticalViewOpt: qsTr("Vertical view")
 | 
			
		||||
    property string horizontalViewOpt: qsTr("Horizontal view")
 | 
			
		||||
@ -530,13 +496,12 @@ Item {
 | 
			
		||||
    // Context Menu
 | 
			
		||||
    property string saveFile: qsTr("Save file")
 | 
			
		||||
    property string openLocation: qsTr("Open location")
 | 
			
		||||
    property string me: qsTr("Me")
 | 
			
		||||
    property string removeLocally: qsTr("Delete file from device")
 | 
			
		||||
 | 
			
		||||
    // Updates
 | 
			
		||||
    property string betaInstall: qsTr("Install beta version")
 | 
			
		||||
    property string checkForUpdates: qsTr("Check for updates now")
 | 
			
		||||
    property string enableAutoUpdates: qsTr("Enable/Disable automatic updates")
 | 
			
		||||
    property string tipAutoUpdate: qsTr("Toggle automatic updates")
 | 
			
		||||
    property string updatesTitle: qsTr("Updates")
 | 
			
		||||
    property string updateDialogTitle: qsTr("Update")
 | 
			
		||||
    property string updateFound: qsTr("A new version of Jami was found\nWould you like to update now?")
 | 
			
		||||
@ -548,6 +513,8 @@ Item {
 | 
			
		||||
    property string updateDownloading: "Downloading"
 | 
			
		||||
    property string confirmBeta: qsTr("This will uninstall your current Release version and you can always download the latest Release version on our website")
 | 
			
		||||
    property string networkDisconnected: qsTr("Network disconnected")
 | 
			
		||||
    property string accessError: qsTr("Content access error")
 | 
			
		||||
    property string contentNotFoundError: qsTr("Content not found")
 | 
			
		||||
    property string genericError: qsTr("Something went wrong")
 | 
			
		||||
 | 
			
		||||
    //Troubleshoot Settings
 | 
			
		||||
@ -559,7 +526,6 @@ Item {
 | 
			
		||||
    property string experimentalCallSwarmTooltip: qsTr("This feature will enable call buttons in swarms with multiple participants.")
 | 
			
		||||
 | 
			
		||||
    // Recording Settings
 | 
			
		||||
    property string tipRecordFolder: qsTr("Select a record directory")
 | 
			
		||||
    property string quality: qsTr("Quality")
 | 
			
		||||
    property string saveRecordingsTo: qsTr("Save recordings to")
 | 
			
		||||
    property string saveScreenshotsTo: qsTr("Save screenshots to")
 | 
			
		||||
@ -573,27 +539,9 @@ Item {
 | 
			
		||||
    property string callKeyboardShortcuts: qsTr("Call")
 | 
			
		||||
    property string settings: qsTr("Settings")
 | 
			
		||||
    property string markdownKeyboardShortcuts: qsTr("Markdown")
 | 
			
		||||
    property string openAccountList: qsTr("Open account list")
 | 
			
		||||
    property string focusConversationsList: qsTr("Focus conversations list")
 | 
			
		||||
    property string requestsList: qsTr("Requests list")
 | 
			
		||||
    property string previousConversation: qsTr("Previous conversation")
 | 
			
		||||
    property string nextConversation: qsTr("Next conversation")
 | 
			
		||||
    property string searchBar: qsTr("Search bar")
 | 
			
		||||
    property string fullScreen: qsTr("Full screen")
 | 
			
		||||
    property string clearHistory: qsTr("Clear history")
 | 
			
		||||
    property string mediaSettings: qsTr("Media settings")
 | 
			
		||||
    property string generalSettings: qsTr("General settings")
 | 
			
		||||
    property string pluginSettings: qsTr("Plugin settings")
 | 
			
		||||
    property string answerIncoming: qsTr("Answer an incoming call")
 | 
			
		||||
    property string declineCallRequest: qsTr("Decline the call request")
 | 
			
		||||
    property string openAccountCreationWizard: qsTr("Open account creation wizard")
 | 
			
		||||
    property string openKeyboardShortcutTable: qsTr("Open keyboard shortcut table")
 | 
			
		||||
 | 
			
		||||
    // View Logs
 | 
			
		||||
    property string logsViewTitle: qsTr("Debug")
 | 
			
		||||
    property string logsViewShowStats: qsTr("Show Stats")
 | 
			
		||||
    property string logsViewStart: qsTr("Start")
 | 
			
		||||
    property string logsViewStop: qsTr("Stop")
 | 
			
		||||
    property string logsViewCopy: qsTr("Copy")
 | 
			
		||||
    property string logsViewReport: qsTr("Report Bug")
 | 
			
		||||
    property string logsViewClear: qsTr("Clear")
 | 
			
		||||
@ -605,19 +553,14 @@ Item {
 | 
			
		||||
    property string archive: qsTr("Archive")
 | 
			
		||||
    property string openFile: qsTr("Open file")
 | 
			
		||||
    property string importAccountArchive: qsTr("Create account from backup")
 | 
			
		||||
    property string importAccountExplanation: qsTr("You can obtain an archive by clicking on \"Back up account\" " + "in the Account Settings. " + "This will create a .gz file on your device.")
 | 
			
		||||
    property string connectFromBackup: qsTr("Restore account from backup")
 | 
			
		||||
    property string generatingAccount: qsTr("Generating account…")
 | 
			
		||||
    property string importFromBackup: qsTr("Import from backup")
 | 
			
		||||
    property string importFromArchiveBackup: qsTr("Import from archive backup")
 | 
			
		||||
    property string importFromArchiveBackupDescription: qsTr("Import Jami account from local archive file.")
 | 
			
		||||
    property string selectArchiveFile: qsTr("Select archive file")
 | 
			
		||||
    property string passwordArchive: qsTr("If the account is encrypted with a password, please fill the following field.")
 | 
			
		||||
 | 
			
		||||
    // ImportFromDevicePage
 | 
			
		||||
    property string mainAccountPassword: qsTr("Enter Jami account password")
 | 
			
		||||
    property string enterPIN: qsTr("Enter the PIN from another configured Jami account. " + "Use the \"Link Another Device\" feature to obtain a PIN.")
 | 
			
		||||
    property string connectFromAnotherDevice: qsTr("Link device")
 | 
			
		||||
    property string importButton: qsTr("Import")
 | 
			
		||||
    property string pin: qsTr("Enter the PIN code")
 | 
			
		||||
    property string importFromDeviceDescription: qsTr("A PIN is required to use an existing Jami account on this device.")
 | 
			
		||||
@ -634,62 +577,70 @@ Item {
 | 
			
		||||
    // LinkDevicesDialog
 | 
			
		||||
    property string pinTimerInfos: qsTr("The PIN and the account password should be entered in your device within 10 minutes.")
 | 
			
		||||
    property string close: qsTr("Close")
 | 
			
		||||
    property string enterAccountPassword: qsTr("Enter account's password")
 | 
			
		||||
    property string enterAccountPassword: qsTr("Enter account password")
 | 
			
		||||
    property string enterPasswordPinCode: qsTr("This account is password encrypted, enter the password to generate a PIN code.")
 | 
			
		||||
    property string addDevice: qsTr("Add Device")
 | 
			
		||||
    property string pinExpired: qsTr("PIN expired")
 | 
			
		||||
    property string onAnotherDevice: qsTr("On another device")
 | 
			
		||||
    property string onAnotherDeviceInstruction: qsTr("Install and launch Jami, select \"Import from another device\" and scan the QR code.")
 | 
			
		||||
    property string linkNewDevice: qsTr("Link new device")
 | 
			
		||||
    property string linkingInstructions: qsTr("In Jami, scan QR code or manually enter the PIN.")
 | 
			
		||||
    property string pinValidity: qsTr("The PIN code is valid for: ")
 | 
			
		||||
 | 
			
		||||
    // PasswordDialog
 | 
			
		||||
    property string enterPassword: qsTr("Enter the password")
 | 
			
		||||
    property string enterPassword: qsTr("Enter password")
 | 
			
		||||
    property string enterCurrentPassword: qsTr("Enter current password")
 | 
			
		||||
    property string confirmRemoval: qsTr("Enter this account's password to confirm the removal of this device")
 | 
			
		||||
    property string confirmRemoval: qsTr("Enter account password to confirm the removal of this device")
 | 
			
		||||
    property string enterNewPassword: qsTr("Enter new password")
 | 
			
		||||
    property string confirmNewPassword: qsTr("Confirm new password")
 | 
			
		||||
    property string change: qsTr("Change")
 | 
			
		||||
    property string confirm: qsTr("Confirm")
 | 
			
		||||
    property string exportAccount: qsTr("Export")
 | 
			
		||||
 | 
			
		||||
    // PhotoBoothView
 | 
			
		||||
    property string chooseAvatarImage: qsTr("Choose a picture as your avatar")
 | 
			
		||||
    property string chooseAvatarPicture: qsTr("Choose a picture")
 | 
			
		||||
    property string importFromFile: qsTr("Import avatar from image file")
 | 
			
		||||
    property string stopTakingPhoto: qsTr("Stop taking photo")
 | 
			
		||||
    property string clearAvatar: qsTr("Clear avatar image")
 | 
			
		||||
    property string takePhoto: qsTr("Take photo")
 | 
			
		||||
    property string imageFiles: qsTr("Image Files (*.png *.jpg *.jpeg *.JPG *.JPEG *.PNG)")
 | 
			
		||||
 | 
			
		||||
    // Plugins
 | 
			
		||||
    property string enable: qsTr("Enable")
 | 
			
		||||
    property string autoUpdate: qsTr("Auto update")
 | 
			
		||||
    property string disableAll: qsTr("Disable all")
 | 
			
		||||
    property string installed: qsTr("Installed")
 | 
			
		||||
    property string install: qsTr("Install")
 | 
			
		||||
    property string installing: qsTr("Installing")
 | 
			
		||||
    property string installManually: qsTr("Install manually")
 | 
			
		||||
    property string installMannuallyDescription: qsTr("Install an extension directly from your device.")
 | 
			
		||||
    property string pluginStoreTitle: qsTr("Available")
 | 
			
		||||
    property string pluginStoreNotAvailable: qsTr("Plugins store is not available")
 | 
			
		||||
    property string pluginPreferences: qsTr("Preferences")
 | 
			
		||||
    property string installationFailed: qsTr("Installation failed")
 | 
			
		||||
    property string pluginInstallationFailed: qsTr("The installation of the plugin failed")
 | 
			
		||||
    property string reset: qsTr("Reset")
 | 
			
		||||
    property string uninstall: qsTr("Uninstall")
 | 
			
		||||
    property string resetPreferences: qsTr("Reset Preferences")
 | 
			
		||||
    property string selectPluginInstall: qsTr("Select a plugin to install")
 | 
			
		||||
    property string installPlugin: qsTr("Install plugin")
 | 
			
		||||
    property string uninstallPlugin: qsTr("Uninstall plugin")
 | 
			
		||||
    property string pluginResetConfirmation: qsTr("Are you sure you wish to reset %1 preferences?")
 | 
			
		||||
    property string pluginUninstallConfirmation: qsTr("Are you sure you wish to uninstall %1?")
 | 
			
		||||
    property string showHidePrefs: qsTr("Display or hide preferences")
 | 
			
		||||
    property string addNewPlugin: qsTr("Add new plugin")
 | 
			
		||||
    property string goBackToPluginsList: qsTr("Go back to plugins list")
 | 
			
		||||
    property string selectFile: qsTr("Select a file")
 | 
			
		||||
    property string select: qsTr("Select")
 | 
			
		||||
    property string chooseImageFile: qsTr("Choose image file")
 | 
			
		||||
    property string tipGeneralPluginSettingsDisplay: qsTr("Display or hide General plugin settings")
 | 
			
		||||
    property string tipAccountPluginSettingsDisplay: qsTr("Display or hide Account plugin settings")
 | 
			
		||||
    property string installedPlugins: qsTr("Installed plugins")
 | 
			
		||||
    property string pluginFiles: qsTr("Plugin Files (*.jpl)")
 | 
			
		||||
    property string loadUnload: qsTr("Load/Unload")
 | 
			
		||||
    property string selectAnImage: qsTr("Select An Image to %1")
 | 
			
		||||
    property string editPreference: qsTr("Edit preference")
 | 
			
		||||
    property string onOff: qsTr("On/Off")
 | 
			
		||||
    property string choosePlugin: qsTr("Choose Plugin")
 | 
			
		||||
 | 
			
		||||
    property string versionPlugin: qsTr("Version %1")
 | 
			
		||||
    property string lastUpdate: qsTr("Last update %1")
 | 
			
		||||
    property string by: qsTr("By %1")
 | 
			
		||||
    property string proposedBy: qsTr("Proposed by %1")
 | 
			
		||||
    // ProfilePage
 | 
			
		||||
    property string profileSharedWithContacts: qsTr("Profile is only shared with contacts")
 | 
			
		||||
    property string saveProfile: qsTr("Save profile")
 | 
			
		||||
    property string enterYourName: qsTr("Enter your name")
 | 
			
		||||
    property string enterRVName: qsTr("Enter the rendezvous point's name")
 | 
			
		||||
    property string generatingRV: qsTr("Creating rendezvous point…")
 | 
			
		||||
    property string information: qsTr("Information")
 | 
			
		||||
    property string moreInformation: qsTr("More information")
 | 
			
		||||
    property string profile: qsTr("Profile")
 | 
			
		||||
 | 
			
		||||
    // RevokeDevicePasswordDialog
 | 
			
		||||
@ -706,11 +657,7 @@ Item {
 | 
			
		||||
    // UserProfile
 | 
			
		||||
    property string qrCode: qsTr("QR code")
 | 
			
		||||
 | 
			
		||||
    // Account QR
 | 
			
		||||
    property string accountQr: qsTr("Account QR")
 | 
			
		||||
 | 
			
		||||
    // WelcomePage
 | 
			
		||||
    property string shareInvite: qsTr("This is your Jami username.\nCopy and share it with your friends!")
 | 
			
		||||
    property string linkFromAnotherDevice: qsTr("Link this device to an existing account")
 | 
			
		||||
    property string importAccountFromAnotherDevice: qsTr("Import from another device")
 | 
			
		||||
    property string importAccountFromBackup: qsTr("Import from an archive backup")
 | 
			
		||||
@ -722,12 +669,10 @@ Item {
 | 
			
		||||
    property string addSIPAccount: qsTr("Configure a SIP account")
 | 
			
		||||
    property string errorCreateAccount: qsTr("Error while creating your account. Check your credentials.")
 | 
			
		||||
    property string createNewRV: qsTr("Create a rendezvous point")
 | 
			
		||||
    property string createAJamiAccount: qsTr("Create a Jami account")
 | 
			
		||||
    property string joinJami: qsTr("Join Jami")
 | 
			
		||||
    property string createNewJamiAccount: qsTr("Create new Jami account")
 | 
			
		||||
    property string createNewSipAccount: qsTr("Create new SIP account")
 | 
			
		||||
    property string aboutJami: qsTr("About Jami")
 | 
			
		||||
    property string welcomeTo: qsTr("Welcome to")
 | 
			
		||||
    property string introductionJami: qsTr("Share freely and privately with Jami")
 | 
			
		||||
    property string alreadyHaveAccount: qsTr("I already have an account")
 | 
			
		||||
    property string useExistingAccount: qsTr("Use existing Jami account")
 | 
			
		||||
@ -736,7 +681,6 @@ Item {
 | 
			
		||||
    // SmartList
 | 
			
		||||
    property string clearText: qsTr("Clear Text")
 | 
			
		||||
    property string conversations: qsTr("Conversations")
 | 
			
		||||
    property string conversation: qsTr("Conversation")
 | 
			
		||||
    property string searchResults: qsTr("Search Results")
 | 
			
		||||
 | 
			
		||||
    // SmartList context menu
 | 
			
		||||
@ -768,7 +712,6 @@ Item {
 | 
			
		||||
    property string localMuted: qsTr("Local muted")
 | 
			
		||||
 | 
			
		||||
    // Settings moderation
 | 
			
		||||
    property string conferenceModeration: qsTr("Conference moderation")
 | 
			
		||||
    property string defaultModerators: qsTr("Default moderators")
 | 
			
		||||
    property string enableLocalModerators: qsTr("Enable local moderators")
 | 
			
		||||
    property string enableAllModerators: qsTr("Make all participants moderators")
 | 
			
		||||
@ -780,34 +723,32 @@ Item {
 | 
			
		||||
    property string reconnectDaemon: qsTr("Trying to reconnect to the Jami daemon (jamid)…")
 | 
			
		||||
    property string reconnectionFailed: qsTr("Could not re-connect to the Jami daemon (jamid).\nJami will now quit.")
 | 
			
		||||
 | 
			
		||||
    // Is Swarm
 | 
			
		||||
    property string isSwarm: qsTr("Is swarm:")
 | 
			
		||||
    property string trueStr: qsTr("True")
 | 
			
		||||
    property string falseStr: qsTr("False")
 | 
			
		||||
 | 
			
		||||
    // Message view
 | 
			
		||||
    property string addEmoji: qsTr("Add emoji")
 | 
			
		||||
    property string moreEmojis: qsTr("more emojis")
 | 
			
		||||
    property string sendFile: qsTr("Send file")
 | 
			
		||||
    property string leaveAudioMessage: qsTr("Leave audio message")
 | 
			
		||||
    property string leaveVideoMessage: qsTr("Leave video message")
 | 
			
		||||
    property string leaveAudioMessage: qsTr("Audio message")
 | 
			
		||||
    property string leaveVideoMessage: qsTr("Video message")
 | 
			
		||||
    property string showMore: qsTr("Show more")
 | 
			
		||||
    property string showLess: qsTr("Show less")
 | 
			
		||||
 | 
			
		||||
    property string showPreview: qsTr("Show preview")
 | 
			
		||||
    property string continueEditing: qsTr("Continue editing")
 | 
			
		||||
    property string bold: qsTr("Bold")
 | 
			
		||||
    property string italic: qsTr("Italic")
 | 
			
		||||
    property string strikethrough: qsTr("Strikethrough")
 | 
			
		||||
    property string title: qsTr("Title")
 | 
			
		||||
    property string heading: qsTr("Heading")
 | 
			
		||||
    property string link: qsTr("Link")
 | 
			
		||||
    property string code: qsTr("Code")
 | 
			
		||||
    property string quote: qsTr("Quote")
 | 
			
		||||
    property string unorderedList: qsTr("Unordered list")
 | 
			
		||||
    property string orderedList: qsTr("Ordered list")
 | 
			
		||||
    property string showFormating: qsTr("Show Formating")
 | 
			
		||||
    property string hideFormating: qsTr("Hide Formating")
 | 
			
		||||
 | 
			
		||||
    property string showFormatting: qsTr("Show formatting")
 | 
			
		||||
    property string hideFormatting: qsTr("Hide formatting")
 | 
			
		||||
    property string shiftEnterNewLine: qsTr("Press Shift+Enter to insert a new line")
 | 
			
		||||
    property string enterNewLine: qsTr("Press Enter to insert a new line")
 | 
			
		||||
    property string send: qsTr("Send")
 | 
			
		||||
    property string remove: qsTr("Remove")
 | 
			
		||||
    property string replyTo: qsTr("Reply to")
 | 
			
		||||
    property string inReplyTo: qsTr("In reply to")
 | 
			
		||||
    property string repliedTo: qsTr(" replied to")
 | 
			
		||||
@ -833,10 +774,8 @@ Item {
 | 
			
		||||
    property string invitationViewWaitingForSync: qsTr("Waiting until %1\nconnects to synchronize the conversation.")
 | 
			
		||||
 | 
			
		||||
    // SwarmDetailsPanel
 | 
			
		||||
    property string about: qsTr("About")
 | 
			
		||||
    property string members: qsTr("%1 Members")
 | 
			
		||||
    property string member: qsTr("Member")
 | 
			
		||||
    property string documents: qsTr("Documents")
 | 
			
		||||
    property string swarmName: qsTr("Swarm's name")
 | 
			
		||||
    property string contactName: qsTr("Contact's name")
 | 
			
		||||
    property string addADescription: qsTr("Add a description")
 | 
			
		||||
@ -846,14 +785,12 @@ Item {
 | 
			
		||||
    property string chooseAColor: qsTr("Choose a color")
 | 
			
		||||
    property string defaultCallHost: qsTr("Default host (calls)")
 | 
			
		||||
    property string leaveConversation: qsTr("Leave conversation")
 | 
			
		||||
    property string leave: qsTr("Leave")
 | 
			
		||||
    property string typeOfSwarm: qsTr("Type of swarm")
 | 
			
		||||
    property string none: qsTr("None")
 | 
			
		||||
 | 
			
		||||
    // NewSwarmPage
 | 
			
		||||
    property string createTheSwarm: qsTr("Create the swarm")
 | 
			
		||||
    property string goToConversation: qsTr("Go to conversation")
 | 
			
		||||
    property string promoteAdministrator: qsTr("Promote to administrator")
 | 
			
		||||
    property string kickMember: qsTr("Kick member")
 | 
			
		||||
    property string reinstateMember: qsTr("Reinstate member")
 | 
			
		||||
    property string administrator: qsTr("Administrator")
 | 
			
		||||
@ -866,9 +803,7 @@ Item {
 | 
			
		||||
    property string tip: qsTr("Tip")
 | 
			
		||||
    property string dismiss: qsTr("Dismiss")
 | 
			
		||||
    property string customizeText: qsTr("Add a profile picture and nickname to complete your profile")
 | 
			
		||||
    property string customizationDescription: qsTr("This profile is only shared with this account's contacts")
 | 
			
		||||
    property string customizationDescription2: qsTr("Your profile is only shared with your contacts")
 | 
			
		||||
    property string whySaveAccount: qsTr("Why should I save my account?")
 | 
			
		||||
    property string customizationDescription: qsTr("Your profile is only shared with your contacts")
 | 
			
		||||
 | 
			
		||||
    // Message options
 | 
			
		||||
    property string deleteMessage: qsTr("Delete message")
 | 
			
		||||
@ -876,8 +811,7 @@ Item {
 | 
			
		||||
    property string editMessage: qsTr("Edit message")
 | 
			
		||||
 | 
			
		||||
    // Jami identifier
 | 
			
		||||
    property string identifierDescription: qsTr("Share this Jami identifier to be contacted on this account!")
 | 
			
		||||
    property string hereIsIdentifier: qsTr("Here is your Jami identifier, don't hesitate to share it in order to be contacted more easily!")
 | 
			
		||||
    property string hereIsIdentifier: qsTr("Share your Jami identifier in order to be contacted more easily!")
 | 
			
		||||
    property string jamiIdentity: qsTr("Jami identity")
 | 
			
		||||
    property string identifierURI: qsTr("Show fingerprint")
 | 
			
		||||
    property string identifierRegisterName: qsTr("Show registered name")
 | 
			
		||||
@ -903,4 +837,9 @@ Item {
 | 
			
		||||
    // Appearence
 | 
			
		||||
    property string theme: qsTr("Theme")
 | 
			
		||||
    property string zoomLevel: qsTr("Text zoom level")
 | 
			
		||||
 | 
			
		||||
    //Donation campaign
 | 
			
		||||
    property string donation: qsTr("Donate")
 | 
			
		||||
    property string donationText: qsTr("If you enjoy using Jami and believe in our mission, would you make a donation?")
 | 
			
		||||
    property string notNow: qsTr("Not now")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -58,9 +58,13 @@ Item {
 | 
			
		||||
    property color blackColor: "#000000"
 | 
			
		||||
    property color redColor: "red"
 | 
			
		||||
    property color whiteColor: "#ffffff"
 | 
			
		||||
    property color darkBlueGreen: "#123F4A"
 | 
			
		||||
    property color darkGreyColor: "#272727"
 | 
			
		||||
    property color darkGreyColorOpacityFade: "#cc000000" // 80%
 | 
			
		||||
    property color darkGreyColorOpacity: "#be272727" // 77%
 | 
			
		||||
    property color tintedBlue: darkTheme ? "#03B9E9" : "#005699"
 | 
			
		||||
    property color tintedBlue: darkTheme ? lightTintedBlue : darkTintedBlue
 | 
			
		||||
    property color lightTintedBlue: "#03B9E9"
 | 
			
		||||
    property color darkTintedBlue: "#005699"
 | 
			
		||||
    property color sysColor: "#F0EFEF"
 | 
			
		||||
 | 
			
		||||
    property color transparentColor: "transparent"
 | 
			
		||||
@ -72,11 +76,11 @@ Item {
 | 
			
		||||
    property color greyBorderColor: "#333"
 | 
			
		||||
    property color selectionBlue: darkTheme ? "#0061a5" : "#109ede"
 | 
			
		||||
 | 
			
		||||
    property color hoverColor: darkTheme ? "#515151" : "#c7c7c7"
 | 
			
		||||
    property color hoverColor: darkTheme ? "#4d4d4d" : "#c7c7c7"
 | 
			
		||||
    property color pressColor: darkTheme ? "#777" : "#c0c0c0"
 | 
			
		||||
    property color selectedColor: darkTheme ? "#0e81c5" : "#e0e0e0"
 | 
			
		||||
    property color smartListHoveredColor: darkTheme ? "#444444" : "#dedede"
 | 
			
		||||
    property color smartListSelectedColor: darkTheme ? "#515151" : "#d1d1d1"
 | 
			
		||||
    property color smartListHoveredColor: darkTheme ? "#4d4d4d" : "#dedede"
 | 
			
		||||
    property color smartListSelectedColor: darkTheme ? "#4d4d4d" : "#dedede"
 | 
			
		||||
    property color editBackgroundColor: darkTheme ? "#373737" : lightGrey_
 | 
			
		||||
    property color textColor: primaryForegroundColor
 | 
			
		||||
    property color textColorHovered: darkTheme ? "#cccccc" : "#333333"
 | 
			
		||||
@ -96,8 +100,8 @@ Item {
 | 
			
		||||
 | 
			
		||||
    // General buttons
 | 
			
		||||
    property color pressedButtonColor: darkTheme ? pressColor : "#a0a0a0"
 | 
			
		||||
    property color hoveredButtonColor: darkTheme ? hoverColor : "#c7c7c7"
 | 
			
		||||
    property color hoveredButtonColorWizard: darkTheme ? "#123F4A" : "#E5EEF5"
 | 
			
		||||
    property color hoveredButtonColor: darkTheme ? "#4d4d4d" : "#dedede"
 | 
			
		||||
    property color hoveredButtonColorWizard: darkTheme ? "#4d4d4d" : "#dedede"
 | 
			
		||||
    property color normalButtonColor: darkTheme ? backgroundColor : "#e0e0e0"
 | 
			
		||||
 | 
			
		||||
    property color invertedPressedButtonColor: Qt.rgba(0, 0, 0, 0.5)
 | 
			
		||||
@ -209,16 +213,17 @@ Item {
 | 
			
		||||
    property color chatviewLinkColorDark: "#353637"
 | 
			
		||||
    property real chatviewFontSize: calcSize(15)
 | 
			
		||||
    property real chatviewEmojiSize: calcSize(60)
 | 
			
		||||
    property color timestampColor: darkTheme ? "#bbb" : "#777"
 | 
			
		||||
    property color timestampColor: darkTheme ? "#515151" : "#E5E5E5"
 | 
			
		||||
    property color messageReplyColor: darkTheme ? "#bbb" : "#A7A7A7"
 | 
			
		||||
    property color messageOutTxtColor: "#000000"
 | 
			
		||||
    property color messageInBgColor: "#e5e5e5"
 | 
			
		||||
    property color messageInBgColor: darkTheme ? "#303030" : "#dbdbdb"
 | 
			
		||||
    property color messageOutBgColor: darkTheme ? "#616161" : "#005699"
 | 
			
		||||
    property color messageInTxtColor: "#FFFFFF"
 | 
			
		||||
    property color fileOutTimestampColor: darkTheme ? "#eee" : "#555"
 | 
			
		||||
    property color fileInTimestampColor: darkTheme ? "#999" : "#555"
 | 
			
		||||
    property color chatviewBgColor: darkTheme ? bgDarkMode_ : whiteColor
 | 
			
		||||
    property color bgInvitationRectColor: darkTheme ? "#222222" : whiteColor
 | 
			
		||||
    property color messageBarPlaceholderTextColor: darkTheme ? "#909090" : "#7e7e7e"
 | 
			
		||||
    property color placeholderTextColor: darkTheme ? "#7a7a7a" : "black" //Qt.rgba(0, 0, 0, 0.2)
 | 
			
		||||
    property color placeholderTextColorWhite: "#cccccc"
 | 
			
		||||
    property color inviteHoverColor: darkTheme ? blackColor : whiteColor
 | 
			
		||||
@ -229,15 +234,23 @@ Item {
 | 
			
		||||
    property color previewCardContainerColor: darkTheme ? blackColor : whiteColor
 | 
			
		||||
    property color previewUrlColor: darkTheme ? "#eeeeee" : "#333"
 | 
			
		||||
    property color messageWebViewFooterButtonImageColor: darkTheme ? "#838383" : "#656565"
 | 
			
		||||
    property color chatviewUsernameColor: "#A7A7A7"
 | 
			
		||||
    property color chatviewSecondaryInformationColor: "#A7A7A7"
 | 
			
		||||
 | 
			
		||||
    // ChatView Footer
 | 
			
		||||
    property color chatViewFooterListColor: darkTheme ? blackColor : "#E5E5E5"
 | 
			
		||||
    property color chatViewFooterImgColor: darkTheme ? whiteColor : blackColor
 | 
			
		||||
    property color showMoreButtonOpenColor: darkTheme ? "#123F4A" : "#CCCCCC"
 | 
			
		||||
    property color chatViewFooterImgHoverColor: darkTheme ? whiteColor : blackColor
 | 
			
		||||
    property color chatViewFooterImgColor: darkTheme ? "#909090" : "#7e7e7e"
 | 
			
		||||
    property color chatViewFooterImgDisableColor: darkTheme ? "#4d4d4d" : "#cbcbcb"
 | 
			
		||||
    property color showMoreButtonOpenColor: darkTheme ? "#4d4d4d" : "#e5e5e5"
 | 
			
		||||
    property color chatViewFooterSeparateLineColor: darkTheme ? "#5c5c5c" : "#929292"
 | 
			
		||||
    property color chatViewFooterSendButtonColor: darkTheme ? "#03B9E9" : "#005699"
 | 
			
		||||
    property color chatViewFooterSendButtonDisableColor: darkTheme ? "#191a1c" : "#f0f0f1"
 | 
			
		||||
    property color chatViewFooterSendButtonImgColor: darkTheme ? blackColor : whiteColor
 | 
			
		||||
    property color chatViewFooterSendButtonImgColorDisable: darkTheme ? "#4d4d4d" : "#cbcbcb"
 | 
			
		||||
    property color chatViewFooterRectangleBorderColor: darkTheme ? "#4d4d4d" : "#e5e5e5"
 | 
			
		||||
 | 
			
		||||
    // ChatView Header
 | 
			
		||||
    property real chatViewHeaderButtonRadius: 5
 | 
			
		||||
 | 
			
		||||
    // mapPosition
 | 
			
		||||
    property color mapButtonsOverlayColor: darkTheme ? "#000000" : "#f0f0f0"
 | 
			
		||||
@ -254,12 +267,13 @@ Item {
 | 
			
		||||
    property real namePopupFontsize: calcSize(15)
 | 
			
		||||
    property real avatarSize: 30
 | 
			
		||||
    property int emojiPushButtonSize: 30
 | 
			
		||||
    property int emojiMargins: 20
 | 
			
		||||
    property int emojiMargins: 16
 | 
			
		||||
    property color emojiReactBubbleBgColor: darkTheme ? darkGreyColor : whiteColor
 | 
			
		||||
    property color emojiReactPushButtonColor: darkTheme ? "#bbb" : "#003b4e"
 | 
			
		||||
    property real messageOptionTextFontSize: calcSize(15)
 | 
			
		||||
    property int emojiPickerWidth: 400
 | 
			
		||||
    property int emojiPickerHeight: 425
 | 
			
		||||
    property int defaulMaxWidthReaction: 350
 | 
			
		||||
 | 
			
		||||
    // Files To Send Container
 | 
			
		||||
    property color removeFileButtonColor: Qt.rgba(96, 95, 97, 0.5)
 | 
			
		||||
@ -296,18 +310,20 @@ Item {
 | 
			
		||||
 | 
			
		||||
    // Sizes
 | 
			
		||||
    property real mainViewLeftPaneMinWidth: 300
 | 
			
		||||
    property real mainViewPaneMinWidth: 430
 | 
			
		||||
    property real currentLeftPaneWidth: mainViewLeftPaneMinWidth
 | 
			
		||||
    property real mainViewPaneMinWidth: 490
 | 
			
		||||
    property real qrCodeImageSize: 256
 | 
			
		||||
    property real splitViewHandlePreferredWidth: 4
 | 
			
		||||
    property real indicatorFontSize: calcSize(6)
 | 
			
		||||
    property real tinyFontSize: calcSize(7 + fontSizeOffset)
 | 
			
		||||
    property real textFontSize: calcSize(9 + fontSizeOffset)
 | 
			
		||||
    property real smallFontSize: calcSize(9 + fontSizeOffsetSmall)
 | 
			
		||||
    property real mediumFontSize: calcSize(10.5 + fontSizeOffset)
 | 
			
		||||
    property real bigFontSize: calcSize(22)
 | 
			
		||||
    property real settingsFontSize: calcSize(11 + fontSizeOffset)
 | 
			
		||||
    property real buttonFontSize: calcSize(9)
 | 
			
		||||
    property real materialButtonPreferredHeight: calcSize(36)
 | 
			
		||||
    property real participantFontSize: calcSize(10 + fontSizeOffset)
 | 
			
		||||
    property real participantSwarmDetailFontSize: calcSize(15 + fontSizeOffset)
 | 
			
		||||
    property real menuFontSize: calcSize(12 + fontSizeOffset)
 | 
			
		||||
    property real headerFontSize: calcSize(14.25 + fontSizeOffset)
 | 
			
		||||
    property real titleFontSize: calcSize(16 + fontSizeOffset)
 | 
			
		||||
@ -315,9 +331,7 @@ Item {
 | 
			
		||||
    property real tinyCreditsTextSize: calcSize(13 + fontSizeOffset)
 | 
			
		||||
    property real creditsTextSize: calcSize(15 + fontSizeOffset)
 | 
			
		||||
    property real primaryRadius: calcSize(4)
 | 
			
		||||
    property real smartlistItemFontSize: calcSize(10.5 + fontSizeOffset)
 | 
			
		||||
    property real smartlistItemInfoFontSize: calcSize(9 + fontSizeOffsetSmall)
 | 
			
		||||
    property real filterItemFontSize: calcSize(smartlistItemFontSize)
 | 
			
		||||
    property real filterItemFontSize: calcSize(mediumFontSize)
 | 
			
		||||
    property real filterBadgeFontSize: calcSize(8.25)
 | 
			
		||||
    property real editedFontSize: calcSize(8)
 | 
			
		||||
    property real accountListItemHeight: 64
 | 
			
		||||
@ -373,6 +387,15 @@ Item {
 | 
			
		||||
    property real minimumMapWidth: 250
 | 
			
		||||
    property real pluginHandlersPopupViewHeight: 200
 | 
			
		||||
    property real pluginHandlersPopupViewDelegateHeight: 50
 | 
			
		||||
    property color pluginDefaultBackgroundColor: "#666666"
 | 
			
		||||
    property real remotePluginMinimumDelegateWidth: 430
 | 
			
		||||
    property real remotePluginMinimumDelegateHeight: 275
 | 
			
		||||
    property real remotePluginMaximumDelegateWidth: 645
 | 
			
		||||
    property real remotePluginMaximumDelegateHeight: 413
 | 
			
		||||
    property real iconMargin: 30 * baseZoom
 | 
			
		||||
    property real remotePluginDelegateWidth: remotePluginMinimumDelegateWidth * baseZoom
 | 
			
		||||
    property real remotePluginDelegateHeight: remotePluginMinimumDelegateHeight * baseZoom
 | 
			
		||||
    property color pluginViewBackgroundColor: darkTheme ? "#000000" : "#F0EFEF"
 | 
			
		||||
    property real secondaryDialogDimension: 500
 | 
			
		||||
 | 
			
		||||
    property real lineEditContextMenuItemsHeight: 15
 | 
			
		||||
@ -385,10 +408,6 @@ Item {
 | 
			
		||||
 | 
			
		||||
    // TimestampInfo
 | 
			
		||||
    property int timestampLinePadding: 40
 | 
			
		||||
    property int dayTimestampTopMargin: 8
 | 
			
		||||
    property int dayTimestampBottomMargin: 8
 | 
			
		||||
    property int timestampBottomMargin: 16
 | 
			
		||||
    property int timestampTopMargin: 16
 | 
			
		||||
    property int dayTimestampHPadding: 16
 | 
			
		||||
    property real dayTimestampVPadding: 32
 | 
			
		||||
    property real timestampFont: calcSize(12)
 | 
			
		||||
@ -420,7 +439,7 @@ Item {
 | 
			
		||||
    // Jami Identifier
 | 
			
		||||
    property color mainColor: "#005699"
 | 
			
		||||
    property real pushButtonSize: 22
 | 
			
		||||
    property real pushButtonMargin: 10
 | 
			
		||||
    property real pushButtonMargins: 10
 | 
			
		||||
 | 
			
		||||
    // Modal Popup
 | 
			
		||||
    property real modalPopupRadius: 20
 | 
			
		||||
@ -435,14 +454,14 @@ Item {
 | 
			
		||||
    property real chatViewHairLineSize: 1
 | 
			
		||||
    property real chatViewMaximumWidth: 900
 | 
			
		||||
    property real chatViewHeaderPreferredHeight: 64
 | 
			
		||||
    property real chatViewFooterPreferredHeight: 50
 | 
			
		||||
    property real chatViewFooterMaximumHeight: 280
 | 
			
		||||
    property real chatViewFooterPreferredHeight: 35
 | 
			
		||||
    property real chatViewFooterMaximumHeight: 315
 | 
			
		||||
    property real chatViewFooterRowSpacing: 4
 | 
			
		||||
    property real chatViewFooterButtonSize: 36
 | 
			
		||||
    property real chatViewFooterRealButtonSize: 26
 | 
			
		||||
    property real chatViewFooterButtonIconSize: 48
 | 
			
		||||
    property real chatViewFooterButtonRadius: 5
 | 
			
		||||
    property real chatViewFooterTextAreaMaximumHeight: 130
 | 
			
		||||
    property real chatViewFooterTextAreaMaximumHeight: 260
 | 
			
		||||
    property real chatViewScrollToBottomButtonBottomMargin: 8
 | 
			
		||||
 | 
			
		||||
    property real usernameBlockFontSize: calcSize(12)
 | 
			
		||||
@ -485,13 +504,11 @@ Item {
 | 
			
		||||
    property real jamiIdMargins: 36
 | 
			
		||||
    property real jamiIdLogoWidth: 70
 | 
			
		||||
    property real jamiIdLogoHeight: 24
 | 
			
		||||
    property real jamiIdFontSize: calcSize(13)
 | 
			
		||||
    property real jamiIdFontSize: calcSize(19)
 | 
			
		||||
    property real jamiIdSmallFontSize: calcSize(11)
 | 
			
		||||
    property color jamiIdColor: darkTheme ? blackColor : sysColor
 | 
			
		||||
 | 
			
		||||
    // MainView
 | 
			
		||||
    property color welcomeViewBackgroundColor: darkTheme ? lightGrey_ : secondaryBackgroundColor
 | 
			
		||||
    property real welcomeRectSideMargins: 45
 | 
			
		||||
    property real welcomeRectTopMargin: 90
 | 
			
		||||
    property color rectColor: darkTheme ? blackColor : "#e5eef5"
 | 
			
		||||
    property color welcomeText: darkTheme ? "#0071c9" : "#002B4A"
 | 
			
		||||
    property real illustrationWidth: 212
 | 
			
		||||
@ -519,6 +536,15 @@ Item {
 | 
			
		||||
    property real welcomeLogoHeight: 100
 | 
			
		||||
    property real wizardButtonWidth: 400
 | 
			
		||||
    property real wizardButtonHeightMargin: 31
 | 
			
		||||
    property color welcomeViewBackgroundColor: darkTheme ? lightGrey_ : secondaryBackgroundColor
 | 
			
		||||
    property real welcomeRectSideMargins: 45
 | 
			
		||||
    property real welcomeRectTopMargin: 90
 | 
			
		||||
    property real welcomePageSpacing: 13
 | 
			
		||||
    property real welcomeGridWidth: 3 * JamiTheme.tipBoxWidth + 2 * JamiTheme.welcomePageSpacing
 | 
			
		||||
    property real welcomeHalfGridWidth: (welcomeGridWidth - JamiTheme.welcomePageSpacing) / 2
 | 
			
		||||
    property real welcomeShortGridWidth: 2 * JamiTheme.tipBoxWidth + JamiTheme.welcomePageSpacing
 | 
			
		||||
    readonly property string welcomeBg: darkTheme ? JamiResources.bg_darkmode_id_jami_png : JamiResources.bg_lightmode_id_jami_png
 | 
			
		||||
    property color welcomeBlockColor: darkTheme ? "#4D000000" : "#4DFFFFFF"
 | 
			
		||||
 | 
			
		||||
    // WizardView Advanced Account Settings
 | 
			
		||||
    property color lightBlue_: darkTheme ? "#03B9E9" : "#e5eef5"
 | 
			
		||||
@ -540,6 +566,7 @@ Item {
 | 
			
		||||
    property real infoBoxDescFontSize: calcSize(12)
 | 
			
		||||
 | 
			
		||||
    // Tipbox
 | 
			
		||||
    property real tipBoxWidth: 200
 | 
			
		||||
    property real tipBoxTitleFontSize: calcSize(13)
 | 
			
		||||
    property real tipBoxContentFontSize: calcSize(12)
 | 
			
		||||
    property color tipBoxBackgroundColor: darkTheme ? blackColor : whiteColor
 | 
			
		||||
@ -583,7 +610,7 @@ Item {
 | 
			
		||||
    property int keyboardShortcutDelegateSize: 50
 | 
			
		||||
 | 
			
		||||
    // Main application spec
 | 
			
		||||
    property real mainViewMinWidth: 430
 | 
			
		||||
    property real mainViewMinWidth: 490
 | 
			
		||||
    property real mainViewMinHeight: 500
 | 
			
		||||
 | 
			
		||||
    property real wizardViewMinWidth: 500
 | 
			
		||||
@ -592,7 +619,7 @@ Item {
 | 
			
		||||
    property real mainViewPreferredWidth: 730
 | 
			
		||||
    property real mainViewPreferredHeight: 600
 | 
			
		||||
 | 
			
		||||
    property real mainViewMargin: 30
 | 
			
		||||
    property real mainViewMargin: 25
 | 
			
		||||
 | 
			
		||||
    // Extras panel
 | 
			
		||||
    property real extrasPanelMinWidth: 300
 | 
			
		||||
@ -610,15 +637,18 @@ Item {
 | 
			
		||||
    property int settingsDescriptionPixelSize: calcSize(15)
 | 
			
		||||
    property int settingsCategorySpacing: 15
 | 
			
		||||
    property int settingsCategoryAudioVideoSpacing: 6
 | 
			
		||||
    property int settingsBoxRadius: 10
 | 
			
		||||
    property int settingsBoxRadius: 5
 | 
			
		||||
    property int settingsBlockSpacing: 40
 | 
			
		||||
    property int settingsMenuChildrenButtonHeight: 30
 | 
			
		||||
    property int settingsMenuHeaderButtonHeight: 50
 | 
			
		||||
    property int settingsListViewsSpacing: 10
 | 
			
		||||
 | 
			
		||||
    // Link Device
 | 
			
		||||
    property color pinBackgroundColor: "#D6E4EF"
 | 
			
		||||
 | 
			
		||||
    // MaterialRadioButton
 | 
			
		||||
    property int radioImageSize: 30
 | 
			
		||||
    property color radioBackgroundColor: darkTheme ? "#515151" : "#F0EFEF"
 | 
			
		||||
    property color radioBackgroundColor: darkTheme ? "#303030" : "#F0EFEF"
 | 
			
		||||
    property color radioBorderColor: darkTheme ? "#03B9E9" : "#005699"
 | 
			
		||||
    property color lightThemeBackgroundColor: JamiTheme.whiteColor
 | 
			
		||||
    property color lightThemeCheckedColor: "#005699"
 | 
			
		||||
@ -627,6 +657,11 @@ Item {
 | 
			
		||||
    property color darkThemeCheckedColor: "#03B9E9"
 | 
			
		||||
    property color darkThemeBorderColor: "#03B9E9"
 | 
			
		||||
 | 
			
		||||
    // Donation campaign
 | 
			
		||||
    property color donationButtonTextColor: "#005699"
 | 
			
		||||
    property color donationBackgroundColor: "#D5E4EF"
 | 
			
		||||
    property string donationUrl: "https://jami.net/donate/"
 | 
			
		||||
 | 
			
		||||
    function setTheme(dark) {
 | 
			
		||||
        darkTheme = dark;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -93,6 +93,7 @@ bool
 | 
			
		||||
ConversationListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
 | 
			
		||||
{
 | 
			
		||||
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
 | 
			
		||||
 | 
			
		||||
    auto rx = filterRegularExpression();
 | 
			
		||||
    auto uriStripper = URI(rx.pattern());
 | 
			
		||||
    bool stripScheme = (uriStripper.schemeType() < URI::SchemeType::COUNT__);
 | 
			
		||||
@ -104,6 +105,11 @@ ConversationListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
 | 
			
		||||
    rx.setPattern(uriStripper.format(flags));
 | 
			
		||||
 | 
			
		||||
    using namespace ConversationList;
 | 
			
		||||
    if (index.data(Role::Uris).toStringList().isEmpty()) {
 | 
			
		||||
        // TODO: Find out why, and fix in libjami/libjamiclient.
 | 
			
		||||
        qCritical() << "Filtering 0 member conversation. Fix me";
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QStringList toFilter;
 | 
			
		||||
    toFilter += index.data(Role::Title).toString();
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,9 @@
 | 
			
		||||
#include "qtutils.h"
 | 
			
		||||
#include "systemtray.h"
 | 
			
		||||
#include "qmlregister.h"
 | 
			
		||||
#include "qtutils.h"
 | 
			
		||||
 | 
			
		||||
#include "namedirectory.h"
 | 
			
		||||
 | 
			
		||||
#include <QApplication>
 | 
			
		||||
#include <QJsonObject>
 | 
			
		||||
@ -232,21 +235,37 @@ ConversationsAdapter::onNewTrustRequest(const QString& accountId,
 | 
			
		||||
            if (convInfo.uid.isEmpty())
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
        auto& accInfo = lrcInstance_->getAccountInfo(accountId);
 | 
			
		||||
        auto from = accInfo.contactModel->bestNameForContact(peerUri);
 | 
			
		||||
        auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
 | 
			
		||||
 | 
			
		||||
        auto preferences = accInfo.conversationModel->getConversationPreferences(convId);
 | 
			
		||||
        // Ignore notifications for this conversation
 | 
			
		||||
        if (preferences["ignoreNotifications"] == "true")
 | 
			
		||||
            return;
 | 
			
		||||
        auto contactPhoto = Utils::contactPhoto(lrcInstance_, peerUri, QSize(50, 50), accountId);
 | 
			
		||||
        auto notifId = QString("%1;%2").arg(accountId, conv);
 | 
			
		||||
        systemTray_->showNotification(notifId,
 | 
			
		||||
                                      tr("%1 received a new trust request").arg(to),
 | 
			
		||||
                                      "New request from " + from,
 | 
			
		||||
                                      SystemTray::NotificationType::REQUEST,
 | 
			
		||||
                                      Utils::QImageToByteArray(contactPhoto));
 | 
			
		||||
        auto cb = [this, to, accountId, conv, peerUri](QString peerBestName) {
 | 
			
		||||
            auto contactPhoto = Utils::contactPhoto(lrcInstance_, peerUri, QSize(50, 50), accountId);
 | 
			
		||||
            auto notifId = QString("%1;%2").arg(accountId, conv);
 | 
			
		||||
            systemTray_->showNotification(notifId,
 | 
			
		||||
                                          tr("%1 received a new trust request").arg(to),
 | 
			
		||||
                                          "New request from " + peerBestName,
 | 
			
		||||
                                          SystemTray::NotificationType::REQUEST,
 | 
			
		||||
                                          Utils::QImageToByteArray(contactPhoto));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // This peer is not yet a contact, so we don't have a name for it,
 | 
			
		||||
        // but we can attempt to look it up using the name service before
 | 
			
		||||
        // falling back to the bestNameForContact.
 | 
			
		||||
        Utils::oneShotConnect(&NameDirectory::instance(),
 | 
			
		||||
                              &NameDirectory::registeredNameFound,
 | 
			
		||||
                              this,
 | 
			
		||||
                              [this, accountId, peerUri, cb](NameDirectory::LookupStatus status,
 | 
			
		||||
                                                             const QString& address,
 | 
			
		||||
                                                             const QString& name) {
 | 
			
		||||
                                  if (address == peerUri) {
 | 
			
		||||
                                      if (status == NameDirectory::LookupStatus::SUCCESS)
 | 
			
		||||
                                          cb(name);
 | 
			
		||||
                                      else {
 | 
			
		||||
                                          auto& accInfo = lrcInstance_->getAccountInfo(accountId);
 | 
			
		||||
                                          cb(accInfo.contactModel->bestNameForContact(peerUri));
 | 
			
		||||
                                      }
 | 
			
		||||
                                  }
 | 
			
		||||
                              });
 | 
			
		||||
        std::ignore = NameDirectory::instance().lookupAddress(accountId, peerUri);
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    Q_UNUSED(accountId)
 | 
			
		||||
 | 
			
		||||
@ -179,7 +179,6 @@ CurrentAccount::updateData()
 | 
			
		||||
 | 
			
		||||
        // SRTP
 | 
			
		||||
        set_enableSRTP(accConfig.SRTP.enable, true);
 | 
			
		||||
        set_rtpFallbackSRTP(accConfig.SRTP.rtpFallback, true);
 | 
			
		||||
        set_keyExchangeSRTP(accConfig.SRTP.keyExchange, true);
 | 
			
		||||
 | 
			
		||||
        // TURN
 | 
			
		||||
@ -222,6 +221,9 @@ CurrentAccount::updateData()
 | 
			
		||||
        set_autoTransferSizeThreshold(settingsManager_->getValue(Settings::Key::AcceptTransferBelow)
 | 
			
		||||
                                          .toInt(),
 | 
			
		||||
                                      true);
 | 
			
		||||
 | 
			
		||||
        // UI Customization settings
 | 
			
		||||
        set_uiCustomization(accConfig.uiCustomization, true);
 | 
			
		||||
    } catch (...) {
 | 
			
		||||
        qWarning() << "Can't update current account info data for" << id_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -160,7 +160,6 @@ class CurrentAccount final : public QObject
 | 
			
		||||
 | 
			
		||||
    // SRTP settings
 | 
			
		||||
    QML_ACCOUNT_CONFIG_CATEGORY_SETTINGS_PROPERTY(bool, enable, SRTP)
 | 
			
		||||
    QML_ACCOUNT_CONFIG_CATEGORY_SETTINGS_PROPERTY(bool, rtpFallback, SRTP)
 | 
			
		||||
    QML_ACCOUNT_CONFIG_CATEGORY_SETTINGS_PROPERTY(lrc::api::account::KeyExchangeProtocol,
 | 
			
		||||
                                                  keyExchange,
 | 
			
		||||
                                                  SRTP)
 | 
			
		||||
@ -191,6 +190,9 @@ class CurrentAccount final : public QObject
 | 
			
		||||
    QML_NEW_ACCOUNT_MODEL_SETTINGS_PROPERTY(bool, autoTransferFromTrusted, AutoAcceptFiles)
 | 
			
		||||
    QML_NEW_ACCOUNT_MODEL_SETTINGS_PROPERTY(int, autoTransferSizeThreshold, AcceptTransferBelow)
 | 
			
		||||
 | 
			
		||||
    // UI Customization settings
 | 
			
		||||
    QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(QJsonObject, uiCustomization)
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit CurrentAccount(LRCInstance* lrcInstance,
 | 
			
		||||
                            AppSettingsManager* settingsManager,
 | 
			
		||||
 | 
			
		||||
@ -125,6 +125,8 @@ CurrentAccountToMigrate::slotAccountStatusChanged(const QString& accountId)
 | 
			
		||||
void
 | 
			
		||||
CurrentAccountToMigrate::slotAccountRemoved(const QString& accountId)
 | 
			
		||||
{
 | 
			
		||||
    if (accountToMigrateList_.isEmpty() || accountToMigrateList_.indexOf(accountId) < 0)
 | 
			
		||||
        return;
 | 
			
		||||
    if (accountToMigrateList_.removeOne(accountId))
 | 
			
		||||
        updateData();
 | 
			
		||||
    if (accountToMigrateList_.isEmpty())
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,13 @@ CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent)
 | 
			
		||||
            this,
 | 
			
		||||
            &CurrentCall::onShowIncomingCallView);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        auto& accInfo = lrcInstance_->getCurrentAccountInfo();
 | 
			
		||||
        set_isSIP(accInfo.profileInfo.type == profile::Type::SIP);
 | 
			
		||||
    } catch (const std::exception& e) {
 | 
			
		||||
        qWarning() << "Can't update current call type" << e.what();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    connectModel();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||