Compare commits

...

95 Commits

Author SHA1 Message Date
aee632c967 misc: fix warning
Change-Id: I7fd957e621f5af1cc76b94c47fffc2523009fa52
2023-07-31 08:41:50 -04:00
e3a73ac932 TextEditor: General upgrade
Popup take text size according to languagePopup takes size of the text in fonction
Edition button are now colored when actif

GitLab: #1096
Change-Id: I4ddfe8de267a0e0582dde602aa2ad42f5cf535e8
2023-07-31 08:41:37 -04:00
416417d15a contactmessagedelegate: fix placement on resize
Change-Id: Icd84dcc248ee0097546a23eed03f4b1379337d0e
GitLab: #1268
2023-07-31 08:28:38 -04:00
36a36dadcf contextmenu: remove "Block contact" on self
https://git.jami.net/savoirfairelinux/jami-project/-/issues/1586

Change-Id: I9b86781869d80eda347659f0c009b8dfe09bdfd0
2023-07-31 08:28:24 -04:00
38735b052a whitelabeling: fix bug on custom color for background
Change-Id: I91544414664008b26397d041998da5c895b48d7a
2023-07-28 12:53:12 -04:00
5b984396cf whitelabeling: change color of text and icons to contrast with custom colors
Change-Id: Ie990bdf7e3ce5253eb55317d2d57a43dd7f543d7
2023-07-28 12:52:51 -04:00
22cd3d4d4e whitelabeling: fix problem when no custom logo in custom Ui
Change-Id: I3a90a20e1963277e72c3980bf5002c16a9a4f213
2023-07-28 12:52:36 -04:00
53811f93b2 misc: remove weird hovered animation on push buttons
Change-Id: Ibad1dd9a6ab8c780123a0342bd4cc138b04663c5
2023-07-28 09:47:16 -04:00
5e874f4f4b contextmenu: remove incorrect block contact
Change-Id: Ia3c72757c903e022f2cbf6d74fc0e0916cd83a94
GitLab: #1270
2023-07-28 09:47:16 -04:00
a5bf258476 swarmdetailspanel: always open on first tab option
Change-Id: If04bdf1b1398c04cdd980d28e0ee594b225ebc3d
GitLab: #1231
2023-07-28 08:20:34 -04:00
3b85425aa3 smartlist: don't reposition the view at the current index
Gitlab: #1273
Change-Id: I4a8cac7caef3c5935b65dacb35b4fc235525ca48
2023-07-27 11:37:38 -04:00
e8e9fd30c9 avatars: fix missing avatars
Strip whitespace for avatars pulled from conversation info.

Gitlab: #1246
Change-Id: I767a53ee3e492507f9cb80b802a9eaaaa66d3d50
2023-07-27 10:47:06 -04:00
e914f795bc i18n: automatic bump
Change-Id: Ia5ee4d6887d4680918ccd5d247dbfdf2197ee7f5
2023-07-27 10:07:14 -04:00
007b0c1132 i18n: fix configuration
+ Only look for translations in valid files.

Change-Id: I350478d050118c8b5d6bd127aaeed6c3965fe05f
GitLab: #1266
2023-07-27 09:59:43 -04:00
11f67c73c8 customizeprofilepage: fix double placeholder
Change-Id: I6628fe83c9818698cc6a2357ab283c97fc94ca31
2023-07-26 13:58:31 -04:00
01cbcbbb2c chatview: update image uri for contact avatar
Gitlab: #1267
Change-Id: I651232cbbf68997f8111c9374486bde4bd8ceeef
2023-07-26 12:50:38 -04:00
76fcd5f910 jamiidentifier: remove useless mask
Change-Id: Ie7e1ae5e5be9b710f53e21cf51385ae8addb54f6
2023-07-26 07:31:15 -04:00
38a3da38c5 i18n: automatic bump
Change-Id: I133ca9ec80aa8ff6a967dd1a4eaf22330fa89279
2023-07-24 16:42:32 -04:00
f0b78036e3 ModalTextEdit: add required placeHolder
Change-Id: I7e450a3f68176bd67801700b621ff6f076531193
2023-07-24 10:44:57 -04:00
2f7acbd31b whitelabeling: change welcomePage to take into account ui customization
GitLab: #1097
Change-Id: I8967146c3ca04daee96b4a4fb10bcb6811c1c7a4
2023-07-19 16:46:32 -04:00
8c1b214619 replytorow: fix message size
Change-Id: I5fd6054a1ac2e409a3ddecef82ec656ad5639e98
2023-07-19 13:00:04 -04:00
e3e4de0fe7 misc: remove unused method
Change-Id: Ib58dabb342a293a1e4e699fcb75b1b04e5541bc4
2023-07-19 11:02:12 -04:00
452d49a439 settings: remove RTP fallback
As not present in other clients and if SDES is enabled, all calls
must be in SRTP. Else UX would be unclear.

Change-Id: If756f8738ef08109aa7fbf8cbcade3b4f4792093
GitLab: #1263
2023-07-19 10:54:35 -04:00
f53c2be978 MediaSettings: pass arguments to method
Change-Id: I9c751dbc1e9557b95c8d34e80e1e43cbe26d57d4
2023-07-18 14:53:17 -04:00
36e5bdb839 i18n: automatic bump
Change-Id: I3050e54142fd9bd9a59e2a5c18adbe3fcf82d8d5
2023-07-18 14:16:45 -04:00
b54cb31d75 misc: fix typo on formatting
Change-Id: I35a0791ba0c8cc906ac441c7afb588e40b1be5c5
2023-07-18 11:16:17 -04:00
5134160539 misc: bump daemon submodule
Change-Id: Idc2990e82ef0f87b07f79a576390c4b1d99051ed
2023-07-17 15:03:27 -04:00
a9ad7d0bde Plugins: fix unavailable view
Plugins settings view is unavailable due to missing QML_REGISTERTYPE
removed at ec0feef74d.

Change-Id: I07d7fef42e9c25d6ee867f7c682286ea3c52a863
2023-07-17 14:47:53 -03:00
6a1a9b60aa MessageBar: do not show sendFile/Location for a SIP account
Change-Id: I4f2fc87c2ec1083605f8a287eecac67ff950c821
2023-07-17 07:48:25 -04:00
61126cfa64 messageparser: replace regexes with tidy API
Depend on tidy API for attribute extraction rather than regexes.

1. htmlparser methods return nodes instead of pre-parsed strings
2. htmlparser provides some methods to extract text/attr from nodes

Gitlab: #1248
Change-Id: I367d703680938fb0b7c5055ac41e079c1322da30
2023-07-14 13:32:20 -04:00
ec0feef74d CachedImage: add icon downloader for pluginstore and welcome page
Gitlab: #1228
Gitlab: #1166

Change-Id: I0117cecdb8a77ded8f3da3c2f25028012002a285
2023-07-14 07:57:45 -04:00
eff76eddc7 buttons: remove bold effect
Change-Id: Ie483a9af270990622b876ca44a975d915e458783
GitLab: #1236
2023-07-13 15:44:07 -04:00
83039abc1b TextEditor: General upgrade
Implement new design
Add multiline button
Use correct color and implement dark theme

GitLab: #1096
Change-Id: I5699c81fbc41e50300ca8b32b93ee47cd17ceee1
2023-07-13 13:48:00 -04:00
bbdd68fb1d misc: fix isRTL function
Uses the system locale name when the preference is set to "SYSTEM".
Adds some extra ISO 639-1 RTL languages.

Change-Id: Ia28ae1bc15992ce26f88ab11bc447d76054f0581
2023-07-13 11:11:59 -04:00
afc0423bf6 swarmdetailspanel: avoid bugguy column layout
Since Qt 6.4, some layout are bugguy causing bad positioning.
(Even some examples in Qt's doc are incorrect)
So, use anchors instead.

Change-Id: Id0f0175208312b6e4b9fe503d8713c7ff037024a
GitLab: #1217
2023-07-13 09:39:24 -04:00
0b18f3d145 swarmdetailspanel: fix identifier row
also separate debug and muteDaemon options so that "jami -dq" shows
debug informations from the client.

Change-Id: Ic69c5cf8b6a8ef4aa1fff607d01a541dab3e6da2
2023-07-13 09:39:19 -04:00
593ecc9910 whitelabeling: add uicustom info in account property
GitLab: #1097

Change-Id: I1f7db2b14c437d033264ff58457d8067e4ff6c05
2023-07-11 14:34:31 -04:00
8fd7c70d1f wizardview: manage through MainView
Change-Id: I083d293784e07f4fcc099e60ada84e433a1b2fa0
GitLab: #1216
2023-07-11 08:05:56 -04:00
e71c1d1729 i18n: automatic bump
Change-Id: I9af964c0078f96730c42ee525ce23e1a83850579
2023-07-10 16:42:30 -04:00
207872244b moderatorlistmodel: fix refresh on account change
Change-Id: Ib689e97d903a42fc241f505e3fb5f37c155c65f9
GitLab: #1122
2023-07-10 07:41:54 -04:00
03b3530d3d conversationmodel: specify "you" for self-user in title
Change-Id: I01d2cd868c7f1c513290792b5f8bff0f6a110f0e
GitLab: #1250
2023-07-07 13:29:28 -04:00
a7bd860e2b Doc: Clarification of install.md instructions
GitLab: #1168

Change-Id: I478b7db6fd6438ed3a35defcc9a7b3e5e7176f32
2023-07-07 13:02:52 -04:00
df9c4b0653 materialradiobutton: update radius
Change-Id: I00feb393feeb1c7e17dccfd75209b00de6e75e4f
GitLab: #1241
2023-07-07 11:12:30 -04:00
d2eed3af64 chatview timestamp: update colors
+ update scroll to bottom button radius
GitLab: #1244

Change-Id: I76ef89123a9a1dfc521aff2983bd46288abab698
2023-07-07 10:54:14 -03:00
f0eb826b64 contactmessagebase: update design
Also move all spacing in TimeStampInfo, so sequences doesn't have
weird spacing and can better be controlled.

GitLab: #1243
Change-Id: Ica7235856c3a7ed38ed3e390c4bf14decede25b0
2023-07-07 08:35:01 -04:00
2ed89fec3c materialbutton: group hoverEnabled and enabled
The hover animation was shown on disabled buttons

Change-Id: I2d8579a1d712e785b8edc82472df6ac798f1b523
GitLab: #1247
2023-07-07 07:53:34 -04:00
281516823e chatviewheader: keep button selected when linked tab is opened
Change-Id: I5b4df32352bc1e6e5281a18e16c8da902242bc4f
GitLab: #1233
2023-07-07 07:43:33 -04:00
a2d110740d swarmdetailspanel: avoid bugguy column layout
Since Qt 6.4, some layout are bugguy causing bad positioning.
(Even some examples in Qt's doc are incorrect)
So, use anchors instead.

Change-Id: Iebb47b8c7e694049ba297609a12bcf43db7dddd3
GitLab: #1217
2023-07-07 07:32:16 -04:00
ad14302ac5 macOS: remove unnecessary permission for photo library access
This commit removes a permission that is not needed, as the photo
library is not accessed in the code.

Change-Id: I47a3af739252733e3e8a2379b417396b2b67578c
2023-07-06 14:48:25 -04:00
f903c635a7 EmojiPicker: close popup when repress the button
Fixing issues where clicking a second time on
the emoji button open a second popup instead
of closing the first one

GitLab: #1249
Change-Id: I76f0094c4024ae27f260e6aeba93522d3709feef
2023-07-06 11:30:49 -04:00
5582d39a3f settings: update selected colors
Change-Id: I18191ef1a4e5e3a62d509e3871ea33550951e690
GitLab: #1237
2023-07-06 11:30:12 -04:00
4f1e04a9b9 HoveredButton: homogenise effects on hovering
Creating a JamiPushButton in oder to regroup definition
Color correction
Homogenization of the other button (Layout QR Code, Settings, and Hide conversation)

GitLab: #1209
Change-Id: If9f891dba1c1d4e83aaebc16a2202dc2a3b719f2
2023-07-05 11:43:05 -04:00
7514d75242 conversationmodel: ignore notifications from muted conversations
This use case has been observed when taking a look at the system tray
icon, which uses conversation model's notificationsCount() to define
whether or not to show a red dot. As we don't want this for muted
conversations these are ignored.

Change-Id: Ic185de9170dfcbfbd8ffc3513676dd90471f3baa
2023-07-04 16:23:35 -04:00
fe2f3258b2 Popup : Close all popups by pressing escape
Using a key OnPressed instead of Shortcut
Making all popups closable with escape (focus true and a close policy)

GitLab: #1234
Change-Id: I1a612834c439aea94a59e91cb915b18b4ef5cf84
2023-07-04 14:30:23 -04:00
c8ec980a3b misc: invert horizontal and vertical view labels
Change-Id: Ic00b2f5073ee3c3b866c8042be3c19993d446699
GitLab: #1142
2023-07-04 08:39:55 -04:00
cb0e45c3fa swarmdetailspanel: reorder tabbar options
Change-Id: Idf6e9c41148b29b3d3a31ecc3b3ae55c551433bf
GitLab: #1231
2023-07-04 08:21:35 -04:00
532bf6c4ad i18n: automatic bump
Change-Id: I8be71ba4848f90f034f9bc81318cba739c9865f2
2023-07-03 16:42:32 -04:00
d7200cc8a3 ChatViewFooter : use Escape for closing box
Closing emoji box, record box and location box by pressing Esc Key
Using opened that already exists instead of creating isOpen

GitLab: #1197
Change-Id: I8461a7f2e8acaffe4ab66647d3e98701c608f270
2023-06-29 08:08:11 -04:00
3383f43688 chatview: update font sizes and margin
Change-Id: I8186083bbe0854dfa6e1d48b61b14b4aef6d135b
GitLab: #1213
2023-06-28 11:42:44 -04:00
26212b21b2 i18n: automatic bump
Change-Id: I874ad3f1ad9181105cb538349862535a2a9f0677
2023-06-28 08:48:46 -04:00
136f365fe2 fix: avoid crash when deleting account
GitLab: #1173
Change-Id: Id30f259ce286ea8fb76af5aee59ed49edb99011a
2023-06-28 01:30:00 -04:00
2145ee6229 callsettings: correctly get isSIP property
Change-Id: I3512cc9890207f2322d7d843e2137dc09d77ebe5
GitLab: #1207
2023-06-27 15:44:13 -04:00
0f66152d72 build.py: allow initialization without installing pre-commit hook
Gitlab: #1219
Change-Id: I5a2e2099d6ad1b551e2d744ffbf800b79c36a821
2023-06-27 14:59:52 -04:00
24a0a384ff contactmodel: no need to call both addContact/acceptConversationRequest
only acceptConversationRequest is enough to accept a request

Change-Id: I4720ea70def6cd35153167ef9577b1c7528e7140
2023-06-27 13:54:19 -04:00
97297eacf7 jamitheme: update color bubble
Change-Id: Ib7bf909811675dc50c3f964feee93191f8d43338
GitLab: #1212
2023-06-27 13:54:19 -04:00
5a70c7e7e8 misc: fix some overlap issues
+ Fix the password entry for linking a new device with password
+ Fix the linked devices page with a lot of devices
+ Fix the add default moderators popup

Change-Id: I1bac4bd8f55dd91825278399fe39f3503e153edb
GitLab: #886
2023-06-27 13:45:07 -04:00
7931d66b81 calloverlay: don't load call action item models when not visible
Gitlab: #1173
Change-Id: I912cb7a16eb22de188350a741efeaa7e23d99d9b
2023-06-27 11:46:59 -04:00
9fd48580bb i18n: automatic bump
Change-Id: I9df6e8bcd1e599e65508e04d183c558c0d9c9978
2023-06-26 10:25:58 -04:00
1789402949 currentconversation: untick mute conversation setting if unset
Change-Id: I3c0586d17cbb72b0a71886f40409e97d8a37502a
GitLab: #1218
2023-06-22 14:32:23 -04:00
2546b69343 networkmanager: changed handle error in sendGetRequest
Change-Id: I3f3b963b7443a5319799e6f6430f9ddd22414d5f
2023-06-22 14:18:52 -04:00
fb420b2ff7 addmemberpanel: simplify and avoid extra model resets
Change-Id: I3989f386127c55c224311afc0aa0b89303579dec
2023-06-22 09:04:39 -04:00
8a7547aaba smartList: update underlying model
This patch ensures the underlying model is updated whenever
a new conversation is added or removed. This will prevent a
possible crash when the model gets invalidated.

GitLab: #1210
Change-Id: I2bd6f396a6ea09ddd357a567456a057ac1805734
2023-06-21 17:10:47 -04:00
9fe34c5282 i18n: automatic bump
Change-Id: Ib40a6b0eb8fb1d96eb6326f2fbfc8059ea434ac7
2023-06-19 13:30:00 -04:00
b948646cbd misc: bump libjami version
Change-Id: I0cc0e0b798f8f79aa6c8686716848a10c167b2b7
2023-06-19 11:40:22 -04:00
21c4afa564 makefile: remove fedora_36
Change-Id: I006e3d940066510dd2831371d9940360a6149276
2023-06-19 10:29:49 -04:00
41b9e541bb JamiIdentifier: reset edit status on account changes
Change-Id: Ifc2c5380b035c10ec188e2b643d87cca9478cd51
GitLab: #1180
2023-06-19 07:55:34 -04:00
e867b578bf positionmanager: fix signature
Change-Id: I68b830eeb775ed043c617db838a5160213152720
GitLab: #1194
2023-06-19 07:55:14 -04:00
944a20b2f3 swarmdetailspanel: remove incorrect margins on members list
Change-Id: Iffc1ee897507aa11ff845686123a14a94c39ba80
GitLab: #1175
2023-06-19 07:54:44 -04:00
7f4e3c4739 JamiIdentifier: fix reduction overflow
GitLab: #1146
Change-Id: Icafcded965e4517ffd01301ad6f11fbc9d404911
2023-06-15 15:50:22 -04:00
2ef5dcae71 packaging: deprecate fedora 36 (eol) - add debian 12
Change-Id: Ibc7d68a651b5f0ba488d6f45e910ddbe339568a2
2023-06-15 09:26:41 -04:00
ad4d7aecc3 ManageAccountPage: no bug on view when resize the view
Gitlab: #886

Change-Id: Ib3d260271d951a16b63db1ac4a0c3c29a4d87f52
2023-06-15 09:11:56 -04:00
388ad92d96 message panel: update location button visibility
Disable the location sharing button when building without
WebEngine.

Change-Id: I62494147a26035a507742e5e80a85e3c044bc3ff
2023-06-14 15:54:58 -04:00
c37ec740e2 misc: fix click on remove conversation
https://git.jami.net/savoirfairelinux/jami-daemon/-/issues/855
Change-Id: I2c0caa3db05c663c5ee23367774c987aeca3324f
2023-06-14 15:41:38 -04:00
cb31ea3575 messagesadapter: fix click on reply header
The signature was incorrect

Change-Id: I35454bd96cea2d2eb959a80605375e1eb962a0da
2023-06-14 15:41:38 -04:00
a27c344cb7 misc: clarify log
Change-Id: I66b2b8cb1c289575b5568cc40f7402282bede314
2023-06-14 15:41:38 -04:00
a65c4f28e1 snap: add libsystem-dev
Change-Id: I2d337149bf2ef8c7807bbb2562e35cc05e3dbbba
2023-06-14 08:07:05 -04:00
ecd291c1be misc: bump daemon
Change-Id: I4827bd4cc4fb8e853c42f7c6bea6126b30e37968
2023-06-13 08:28:15 -04:00
498dfed98b misc: only check for app updates on windows
Change-Id: I508300446e5c8891bb018af0b0c42de4309685c6
2023-06-12 14:34:48 -04:00
da7366f23d Revert "misc: bump libjami version"
This reverts commit 7743c14598.

Reason for revert: Current version of libjami is causing duplicate conversations for some. The offending commits are unknown.

Change-Id: Ica81e8212117bcba30a1c34776fd6dda44c6705d
2023-06-12 09:29:09 -04:00
8990162f99 Doc: fix package name and clarify instruction
Change-Id: I833f62eac5ffba820d1fdd95f9979c250f56fcf2
2023-06-08 12:58:23 -04:00
1f0e2e92ad build: Add dependencies.
Add libsystemd-dev to APT_DEPENDENCIES
    Add systemd-devel to ZYPPER_DEPENDENCIES
    Add systemd-libs to PACMAN_DEPENDENCIES

Change-Id: Ie997202acd85b04b7c4376cf936b98e04dcb23b6
2023-06-08 12:58:02 -04:00
7743c14598 misc: bump libjami version
Change-Id: I684c5c5888f1e2a0c53b2335a19f06a03ce1b96a
2023-06-08 12:56:01 -04:00
c47cfe446d notifications: gnu/linux: do a lookup for incoming trust requests
Attempt a name directory lookup for trust requests before popping a notification. Fall back to the display name, then peer URI if needed.

Gitlab: #1141
Change-Id: Ie91c3fdf518cb8f27d8f0d6a74f015e9c4034d42
2023-06-08 10:25:32 -04:00
948e2cc837 callactionbar: avoid multiple resets
GitLab: #1073

Change-Id: I7dc2ab632170a959b8d29979deacd3fb03ff6671
2023-06-08 08:44:14 -04:00
b611685653 settingsmaterialtextedit: fix focus changes
Change-Id: I289b610e43317470d061e7ecd6338bfa805c5ce7
GitLab: #1171
2023-06-07 16:10:46 -04:00
253 changed files with 216820 additions and 196527 deletions

View File

@ -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

View File

@ -238,7 +238,8 @@ 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)
set(COMMON_HEADERS
${APP_SRC_DIR}/avatarimageprovider.h
@ -301,7 +302,9 @@ 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)
# For libavutil/avframe.
set(LIBJAMI_CONTRIB_DIR "${DAEMON_DIR}/contrib")

View File

@ -11,32 +11,38 @@ Because the client-qt is multi-platforms and supporting macOS, we need a recent
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.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
```
@ -49,7 +55,7 @@ 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:

View File

@ -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

2
daemon

Submodule daemon updated: c814c4de0a...e77b247a23

View File

@ -161,13 +161,13 @@ 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 \

View File

@ -0,0 +1,22 @@
FROM debian:bookworm
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get clean
RUN apt-get update && \
apt-get install -y -o Acquire::Retries=10 \
devscripts \
equivs \
python-is-python3 \
wget
ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
COPY extras/packaging/gnu-linux/rules/debian-qt/control /tmp/builddeps/debian/control
RUN /opt/prebuild-package-debian.sh qt-deps
COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
RUN /opt/prebuild-package-debian.sh jami-deps
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
CMD ["/opt/build-package-debian.sh"]

View File

@ -1,104 +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 \
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"]

View File

@ -93,6 +93,8 @@ 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

View File

@ -308,7 +308,7 @@ parts:
- libexpat1-dev
- libjsoncpp-dev
- libnm-dev # connectivityChanged()
- libdbus-1-dev # dbus
- libsystemd-dev # sdbus-cpp
- libpulse-dev # pulse
- libudev-dev
- libopus-dev # Avoid to build opus from contrib

View File

@ -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"],

View File

@ -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")

View File

@ -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>

View 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

View 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

View 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

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

View File

@ -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);

View File

@ -43,7 +43,6 @@ ApplicationWindow {
LayoutMirroring.childrenInherit: isRTL
enum LoadedSource {
WizardView,
MainView,
AccountMigrationView,
None
@ -95,19 +94,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) {
@ -159,14 +152,6 @@ ApplicationWindow {
asynchronous: true
visible: status == Loader.Ready
Connections {
target: viewCoordinator
function onRequestAppWindowWizardView() {
setMainLoaderSource(JamiQmlUtils.wizardViewLoadPath);
}
}
Connections {
id: connectionMigrationEnded
@ -182,32 +167,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 +189,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,7 +203,7 @@ 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);
@ -300,7 +270,7 @@ ApplicationWindow {
Connections {
target: UpdateManager
function onUpdateDownloadStarted() {
function onDownloadStarted() {
viewCoordinator.presentDialog(appWindow, "settingsview/components/UpdateDownloadDialog.qml", {
"title": JamiStrings.updateDialogTitle
});

View File

@ -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: {

View File

@ -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,

View File

@ -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*)

View File

@ -60,6 +60,7 @@ extern const QString defaultDownloadPath;
X(PositionShareLimit, true) \
X(FlipSelf, true) \
X(ShowMardownOption, false) \
X(ChatViewEnterIsNewLine, false) \
X(ShowSendOption, false)
/*

View File

@ -77,6 +77,7 @@ void
AvatarRegistry::onProfileUpdated(const QString& uri)
{
auto& convInfo = lrcInstance_->getConversationFromPeerUri(uri);
addOrUpdateImage(uri);
if (convInfo.uid.isEmpty())
return;

View File

@ -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);
}

View File

@ -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();

View File

@ -47,6 +47,7 @@ Popup {
// A popup is invisible until opened.
visible: false
focus: true
closePolicy: autoClose ? (Popup.CloseOnEscape | Popup.CloseOnPressOutside) : Popup.NoAutoClose
Rectangle {
@ -67,7 +68,7 @@ Popup {
Layout.preferredHeight: text.length === 0 ? 0 : contentHeight
font.pointSize: 12
font.pointSize: JamiTheme.menuFontSize
color: JamiTheme.textColor
}

View File

@ -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

View File

@ -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

View File

@ -42,6 +42,7 @@ Popup {
padding: 0
visible: false
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
Rectangle {

View File

@ -40,6 +40,7 @@ Column {
ColumnLayout {
width: parent.width
spacing: 0
TimestampInfo {
id: timestampItem

View File

@ -23,151 +23,170 @@ import net.jami.Adapters 1.1
import net.jami.Constants 1.1
Item {
id: root
id: jamiId
property bool slimDisplay: true
property color backgroundColor: JamiTheme.welcomeBlockColor
property color contentColor: JamiTheme.tintedBlue
height: getHeight()
property alias backgroundColor: outerRect.color
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
width: parent.width
RoundedBorderRectangle {
id: leftRect
fillColor: jamiId.backgroundColor
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
radius: {
"tl": 5,
"tr": 0,
"br": 0,
"bl": 5
}
RowLayout {
width: parent.width
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
Layout.fillWidth: true
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter
textColor: jamiId.contentColor
fontPixelSize: staticText.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.jamiIdFontSize
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;
});
}
}
}
}
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
width: childrenRect.width
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) {
usernameTextEdit.staticText = CurrentAccount.uri;
btnId.toolTipText = JamiStrings.identifierRegisterName;
} else {
usernameTextEdit.staticText = 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 +194,13 @@ Item {
component JamiIdControlButton: PushButton {
property bool clicked: true
preferredSize: 30
radius: 5
normalColor: JamiTheme.transparentColor
hoveredColor: JamiTheme.hoveredButtonColorWizard
//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
}
}

View File

@ -30,6 +30,9 @@ Popup {
padding: 0
property list<Action> menuTypoActionsSecond
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
contentItem: ListView {
id: listViewTypoSecond
@ -64,7 +67,7 @@ Popup {
focusPolicy: Qt.TabFocus
normalColor: JamiTheme.chatViewFooterListColor
imageColor: JamiTheme.chatViewFooterImgColor
imageColor: JamiTheme.chatViewFooterImgHoverColor
hoveredColor: JamiTheme.showMoreButtonOpenColor
pressedColor: hoveredColor

View File

@ -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 {

View File

@ -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

View File

@ -49,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

View File

@ -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;

View File

@ -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

View 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();
}
}

View File

@ -90,14 +90,15 @@ 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
font.pointSize: JamiTheme.smallFontSize
color: JamiTheme.chatviewSecondaryInformationColor
lineHeight: JamiTheme.usernameBlockLineHeight
leftPadding: JamiTheme.usernameBlockPadding
textFormat: TextEdit.PlainText
@ -114,7 +115,7 @@ 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

View 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
}
}
}

View File

@ -86,7 +86,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

View File

@ -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
}
}

View File

@ -100,7 +100,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")
@ -204,7 +203,7 @@ Item {
// 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")
@ -267,7 +266,6 @@ 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")
@ -483,6 +481,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")
@ -789,8 +788,8 @@ Item {
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")
@ -803,9 +802,10 @@ Item {
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")
@ -836,7 +836,6 @@ Item {
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")
@ -877,7 +876,7 @@ Item {
// 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")

View File

@ -60,7 +60,9 @@ Item {
property color whiteColor: "#ffffff"
property color darkGreyColor: "#272727"
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 +74,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 +98,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 +211,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 +232,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"
@ -296,12 +307,14 @@ Item {
// Sizes
property real mainViewLeftPaneMinWidth: 300
property real mainViewPaneMinWidth: 430
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)
@ -314,9 +327,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
@ -384,10 +395,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)
@ -419,7 +426,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
@ -434,14 +441,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)
@ -484,13 +491,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
@ -518,6 +523,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"
@ -539,6 +553,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
@ -582,7 +597,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
@ -591,7 +606,7 @@ Item {
property real mainViewPreferredWidth: 730
property real mainViewPreferredHeight: 600
property real mainViewMargin: 30
property real mainViewMargin: 25
// Extras panel
property real extrasPanelMinWidth: 300
@ -609,7 +624,7 @@ 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
@ -617,7 +632,7 @@ Item {
// 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"

View File

@ -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)

View File

@ -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_;
}

View File

@ -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,

View File

@ -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())

View File

@ -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();
}

View File

@ -258,9 +258,8 @@ CurrentConversation::updateConversationPreferences(const QString& convId)
color = convInfo.preferences["color"];
}
set_color(color);
if (convInfo.preferences.contains("ignoreNotifications")) {
set_ignoreNotifications(convInfo.preferences["ignoreNotifications"] == "true");
}
set_ignoreNotifications(convInfo.preferences.contains("ignoreNotifications")
&& convInfo.preferences["ignoreNotifications"] == "true");
}
}

View File

@ -39,6 +39,7 @@ public:
doc_ = tidyCreate();
tidyOptSetBool(doc_, TidyQuiet, yes);
tidyOptSetBool(doc_, TidyShowWarnings, no);
tidyOptSetInt(doc_, TidyUseCustomTags, TidyCustomEmpty);
}
~HtmlParser()
@ -51,46 +52,88 @@ public:
return tidyParseString(doc_, html.toLocal8Bit().data()) >= 0;
}
using TagInfoList = QMap<TidyTagId, QList<QString>>;
using TagNodeList = QMap<TidyTagId, QList<TidyNode>>;
// A function that traverses the DOM tree and fills a QVariantMap with a list
// of the tags and their values. The result is structured as follows:
// {tagId1: ["tagValue1", "tagValue2", ...],
// tagId: ["tagValue1", "tagValue2", ...],
// of the tags and their nodes. The result is structured as follows:
// {tagId1: [tagNode1, tagNode2, ...],
// tagId2: [tagNode3, tagNode4, ...],
// ... }
TagInfoList getTags(QList<TidyTagId> tags, int maxDepth = -1)
TagNodeList getTagsNodes(const QList<TidyTagId>& tags, int maxDepth = -1)
{
TagInfoList result;
TagNodeList result;
traverseNode(
tidyGetRoot(doc_),
tags,
[&result](const QString& value, TidyTagId tag) { result[tag].append(value); },
[&result](TidyNode node, TidyTagId tag) { result[tag].append(node); },
maxDepth);
return result;
}
QString getFirstTagValue(TidyTagId tag, int maxDepth = -1)
// The same as the above function, only it returns the first node for a single tag.
TidyNode getFirstTagNode(TidyTagId tag, int maxDepth = -1)
{
QString result;
TidyNode result = nullptr;
traverseNode(
tidyGetRoot(doc_),
{tag},
[&result](const QString& value, TidyTagId) { result = value; },
[&result](TidyNode node, TidyTagId) { result = node; },
maxDepth);
return result;
}
private:
void traverseNode(TidyNode node,
QList<TidyTagId> tags,
const std::function<void(const QString&, TidyTagId)>& cb,
int depth = -1)
// Extract the text value from a node.
QString getNodeText(TidyNode node)
{
TidyBuffer nodeValue = {};
if (!node || tidyNodeGetText(doc_, node, &nodeValue) != yes) {
return QString();
}
QString result = QString::fromUtf8((char*) nodeValue.bp, nodeValue.size);
tidyBufFree(&nodeValue);
return result;
}
// Extract the attribute value from a node.
QString getNodeAttr(TidyNode node, TidyAttrId attrId)
{
TidyAttr attr = tidyAttrGetById(node, attrId);
if (!attr) {
return QString();
}
const auto* attrValue = tidyAttrValue(attr);
if (!attrValue) {
return QString();
}
return QString::fromLocal8Bit(attrValue);
}
// Extract the inner HTML of a node.
QString getNodeInnerHtml(TidyNode node)
{
if (!node) {
return QString();
}
const auto* child = tidyGetChild(node);
return child ? getNodeText(child) : QString();
}
QString getTagInnerHtml(TidyTagId tag)
{
return getNodeInnerHtml(getFirstTagNode(tag));
}
private:
// NOLINTNEXTLINE(misc-no-recursion)
void traverseNode(TidyNode node,
const QList<TidyTagId>& tags,
const std::function<void(TidyNode, TidyTagId)>& cb,
int depth = -1)
{
for (auto tag : tags) {
if (tidyNodeGetId(node) == tag && tidyNodeGetText(doc_, node, &nodeValue) == yes && cb) {
cb(QString::fromLocal8Bit(nodeValue.bp), tag);
if (tidyNodeGetId(node) == tag && cb) {
cb(node, tag);
if (depth != -1 && --depth == 0) {
return;
}

View File

@ -0,0 +1,66 @@
/*
* 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/>.
*/
#include "imagedownloader.h"
#include <QDir>
#include <QLockFile>
ImageDownloader::ImageDownloader(ConnectivityMonitor* cm, QObject* parent)
: NetworkManager(cm, parent)
{}
void
ImageDownloader::downloadImage(const QUrl& url, const QString& localPath)
{
Utils::oneShotConnect(this, &NetworkManager::errorOccured, this, [this, localPath]() {
onDownloadImageFinished({}, localPath);
});
sendGetRequest(url, [this, localPath](const QByteArray& imageData) {
onDownloadImageFinished(imageData, localPath);
});
}
void
ImageDownloader::onDownloadImageFinished(const QByteArray& data, const QString& localPath)
{
if (!data.isEmpty()) {
// Check if the parent folders exist create them if not
QString dirPath = localPath.left(localPath.lastIndexOf('/'));
QDir dir;
dir.mkpath(dirPath);
QLockFile lf(localPath + ".lock");
QFile file(localPath);
if (!lf.lock()) {
qWarning().noquote() << "Can't lock file for writing: " << file.fileName();
return;
}
if (!file.open(QIODevice::WriteOnly)) {
qWarning().noquote() << "Can't open file for writing: " << file.fileName();
return;
}
file.write(data);
file.close();
qWarning() << Q_FUNC_INFO;
Q_EMIT downloadImageSuccessful(localPath);
return;
}
Q_EMIT downloadImageFailed(localPath);
}

42
src/app/imagedownloader.h Normal file
View File

@ -0,0 +1,42 @@
/*
* 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/>.
*/
#pragma once
#include "networkmanager.h"
#include "qtutils.h"
class ImageDownloader : public NetworkManager
{
Q_OBJECT
QML_PROPERTY(QString, cachePath)
public:
explicit ImageDownloader(ConnectivityMonitor* cm, QObject* parent = nullptr);
// Download an image and call onDownloadImageFinished when done
Q_INVOKABLE void downloadImage(const QUrl& url, const QString& localPath);
Q_SIGNALS:
void downloadImageSuccessful(const QString& localPath);
void downloadImageFailed(const QString& localPath);
private Q_SLOTS:
// Saves the image to the localPath and emits the appropriate signal
void onDownloadImageFinished(const QByteArray& reply, const QString& localPath);
};

View File

@ -31,12 +31,14 @@ LRCInstance::LRCInstance(migrateCallback willMigrateCb,
migrateCallback didMigrateCb,
const QString& updateUrl,
ConnectivityMonitor* connectivityMonitor,
bool muteDring)
: lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb, muteDring))
bool debugMode,
bool muteDaemon)
: lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb, !debugMode || muteDaemon))
, updateManager_(std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this))
, threadPool_(new QThreadPool(this))
{
debugMode_ = !muteDring;
debugMode_ = debugMode;
muteDaemon_ = muteDaemon;
threadPool_->setMaxThreadCount(1);
connect(this, &LRCInstance::currentAccountIdChanged, [this] {

View File

@ -65,7 +65,8 @@ public:
migrateCallback didMigrateCb,
const QString& updateUrl,
ConnectivityMonitor* connectivityMonitor,
bool muteDring);
bool debugMode,
bool muteDaemon);
~LRCInstance() = default;
void finish();
@ -153,6 +154,7 @@ private:
conversation::Info invalid {};
bool debugMode_ {false};
bool muteDaemon_ {true};
QThreadPool* threadPool_;
};

View File

@ -25,7 +25,6 @@
#include "appsettingsmanager.h"
#include "connectivitymonitor.h"
#include "systemtray.h"
#include "previewengine.h"
#include "videoprovider.h"
#include <QAction>
@ -133,7 +132,6 @@ MainApplication::init()
connectivityMonitor_.reset(new ConnectivityMonitor(this));
settingsManager_.reset(new AppSettingsManager(this));
systemTray_.reset(new SystemTray(settingsManager_.get(), this));
previewEngine_.reset(new PreviewEngine(connectivityMonitor_.get(), this));
QObject::connect(settingsManager_.get(),
&AppSettingsManager::retranslate,
@ -149,7 +147,8 @@ MainApplication::init()
initLrc(runOptions_[Option::UpdateUrl].toString(),
connectivityMonitor_.get(),
runOptions_[Option::Debug].toBool() && !runOptions_[Option::MuteJamid].toBool());
runOptions_[Option::Debug].toBool(),
runOptions_[Option::MuteDaemon].toBool());
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
using namespace Interfaces;
@ -227,7 +226,10 @@ MainApplication::handleUriAction(const QString& arg)
}
void
MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bool logDaemon)
MainApplication::initLrc(const QString& downloadUrl,
ConnectivityMonitor* cm,
bool debugMode,
bool muteDaemon)
{
lrc::api::Lrc::cacheAvatars.store(false);
/*
@ -252,7 +254,8 @@ MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bo
},
downloadUrl,
cm,
!logDaemon));
debugMode,
muteDaemon));
lrcInstance_->subscribeToDebugReceived();
}
@ -318,7 +321,7 @@ MainApplication::parseArguments()
runOptions_[Option::UpdateUrl] = parser.value(updateUrlOption);
#endif
runOptions_[Option::TerminationRequested] = parser.isSet(terminateOption);
runOptions_[Option::MuteJamid] = parser.isSet(muteDaemonOption);
runOptions_[Option::MuteDaemon] = parser.isSet(muteDaemonOption);
}
void
@ -342,7 +345,7 @@ MainApplication::initQmlLayer()
systemTray_.get(),
lrcInstance_.get(),
settingsManager_.get(),
previewEngine_.get(),
connectivityMonitor_.get(),
&screenInfo_,
this);

View File

@ -20,6 +20,7 @@
#pragma once
#include "imagedownloader.h"
#include "lrcinstance.h"
#include "qtutils.h"
@ -36,7 +37,6 @@ class ConnectivityMonitor;
class AppSettingsManager;
class SystemTray;
class CallAdapter;
class PreviewEngine;
// Provides information about the screen the app is displayed on
class ScreenInfo : public QObject
@ -73,7 +73,7 @@ public:
StartMinimized = 0,
Debug,
UpdateUrl,
MuteJamid,
MuteDaemon,
TerminationRequested,
StartUri
};
@ -99,7 +99,10 @@ Q_SIGNALS:
void searchAndSelect(const QString& request);
private:
void initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bool logDaemon);
void initLrc(const QString& downloadUrl,
ConnectivityMonitor* cm,
bool debugMode,
bool muteDaemon);
void parseArguments();
void setApplicationFont();
void initQmlLayer();
@ -115,7 +118,7 @@ private:
QScopedPointer<ConnectivityMonitor> connectivityMonitor_;
QScopedPointer<AppSettingsManager> settingsManager_;
QScopedPointer<SystemTray> systemTray_;
QScopedPointer<PreviewEngine> previewEngine_;
QScopedPointer<ImageDownloader> imageDownloader_;
ScreenInfo screenInfo_;
};

View File

@ -43,8 +43,6 @@ Rectangle {
property int tabBarLeftMargin: 8
property int tabButtonShrinkSize: 8
signal loaderSourceChangeRequested(int sourceToLoad)
property string currentConvId: CurrentConversation.id
onCurrentConvIdChanged: {
if (currentConvId !== '') {
@ -149,13 +147,12 @@ Rectangle {
onActivated: layoutManager.toggleWindowFullScreen()
}
Shortcut {
sequence: "Escape"
context: Qt.ApplicationShortcut
onActivated: {
MessagesAdapter.replyToId = ""
MessagesAdapter.editId = ""
layoutManager.popFullScreenItem()
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Escape) {
MessagesAdapter.replyToId = "";
MessagesAdapter.editId = "";
layoutManager.popFullScreenItem();
keyEvent.accepted = true;
}
}

View File

@ -181,7 +181,7 @@ Label {
source: !root.popup.opened ? JamiResources.expand_more_24dp_svg : JamiResources.expand_less_24dp_svg
}
PushButton {
JamiPushButton {
id: shareButton
width: visible ? preferredSize : 0
@ -199,7 +199,7 @@ Label {
onClicked: viewCoordinator.presentDialog(appWindow, "mainview/components/WelcomePageQrDialog.qml")
}
PushButton {
JamiPushButton {
id: settingsButton
anchors.verticalCenter: parent.verticalCenter

View File

@ -40,6 +40,9 @@ Popup {
color: JamiTheme.transparentColor
}
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
contentItem: ColumnLayout {
spacing: 0

View File

@ -57,20 +57,8 @@ Rectangle {
Layout.leftMargin: 4
Layout.rightMargin: 4
model: ContactAdapter.getContactSelectableModel(type)
Connections {
target: CurrentConversationMembers
function onCountChanged() {
contactPickerListView.model = ContactAdapter.getContactSelectableModel(type);
}
}
onVisibleChanged: {
if (visible)
model = ContactAdapter.getContactSelectableModel(type);
}
// Reset the model if visible or the CurrentConversationMembers.count changes (0 or greater)
model: visible && CurrentConversationMembers.count >= 0 ? ContactAdapter.getContactSelectableModel(type) : null
delegate: ContactPickerItemDelegate {
id: contactPickerItemDelegate

View File

@ -32,6 +32,11 @@ Item {
property var margin: 5
property var prefWidth: 170
property real maxHeight: 250
property color textColor: JamiTheme.textColor
property color iconColor: JamiTheme.tintedBlue
signal ignore
ColumnLayout {
@ -41,6 +46,7 @@ Item {
width: parent.width
RowLayout {
id: rowlayout
Layout.leftMargin: 15
Layout.alignment: Qt.AlignLeft
@ -58,16 +64,16 @@ Item {
containerHeight: Layout.preferredHeight
containerWidth: Layout.preferredWidth
source: JamiResources.noun_paint_svg
color: JamiTheme.buttonTintedBlue
source: JamiResources.backup_svg
color: root.iconColor
}
Text {
id: title
text: JamiStrings.backupAccountBtn
color: JamiTheme.textColor
color: root.textColor
font.weight: Font.Medium
Layout.topMargin: root.margin
visible: !opened
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: root.margin
Layout.preferredWidth: root.prefWidth - 2 * root.margin - root.iconSize
@ -81,69 +87,60 @@ Item {
Layout.preferredWidth: root.prefWidth
Layout.leftMargin: 20
Layout.topMargin: 8
Layout.bottomMargin: 15
Layout.bottomMargin: 8
font.pixelSize: JamiTheme.tipBoxContentFontSize
visible: !opened
wrapMode: Text.WordWrap
font.weight: Font.Normal
text: JamiStrings.whyBackupAccount
color: JamiTheme.textColor
color: root.textColor
horizontalAlignment: Text.AlignLeft
}
Text {
JamiFlickable {
Layout.preferredWidth: root.width - 32
Layout.leftMargin: 20
Layout.topMargin: 20
font.pixelSize: JamiTheme.tipBoxContentFontSize
visible: opened
wrapMode: Text.WordWrap
text: JamiStrings.backupAccountInfos
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
}
property real maxDescriptionHeight: maxHeight - rowlayout.Layout.preferredHeight - title.Layout.preferredHeight - 3 * JamiTheme.preferredMarginSize
Layout.preferredHeight: opened ? Math.min(contentHeight, maxDescriptionHeight) : 0
contentHeight: description.height
Text {
id: description
width: parent.width
font.pixelSize: JamiTheme.tipBoxContentFontSize
visible: opened
wrapMode: Text.WordWrap
text: JamiStrings.backupAccountInfos
color: root.textColor
horizontalAlignment: Text.AlignLeft
linkColor: JamiTheme.buttonTintedBlue
MaterialButton {
id: backupBtn
Layout.alignment: Qt.AlignCenter
preferredWidth: parent.width - 32
height: 32
visible: opened
text: JamiStrings.backupAccountBtn
autoAccelerator: true
color: JamiTheme.buttonTintedGrey
hoveredColor: JamiTheme.buttonTintedGreyHovered
pressedColor: JamiTheme.buttonTintedGreyPressed
onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
"title": JamiStrings.backupAccountHere,
"fileMode": JamiFileDialog.SaveFile,
"folder": StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/Desktop",
"nameFilters": [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles]
});
dlg.fileAccepted.connect(function (file) {
// Is there password? If so, go to password dialog, else, go to following directly
if (CurrentAccount.hasArchivePassword) {
var pwdDlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/PasswordDialog.qml", {
"path": UtilsAdapter.getAbsPath(file),
"purpose": PasswordDialog.ExportAccount
});
pwdDlg.done.connect(function () {
onLinkActivated: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
"title": JamiStrings.backupAccountHere,
"fileMode": JamiFileDialog.SaveFile,
"folder": StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/Desktop",
"nameFilters": [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles]
});
dlg.fileAccepted.connect(function (file) {
// Is there password? If so, go to password dialog, else, go to following directly
if (CurrentAccount.hasArchivePassword) {
var pwdDlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/PasswordDialog.qml", {
"path": UtilsAdapter.getAbsPath(file),
"purpose": PasswordDialog.ExportAccount
});
pwdDlg.done.connect(function () {
root.ignore();
});
} else {
if (file.toString().length > 0) {
root.ignore();
});
} else {
if (file.toString().length > 0) {
root.ignore();
}
}
}
});
dlg.rejected.connect(function () {
backupBtn.forceActiveFocus();
});
});
dlg.rejected.connect(function () {
backupBtn.forceActiveFocus();
});
}
}
}
}

View File

@ -0,0 +1,112 @@
/*
* 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 net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Helpers 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
Item {
id: cachedImage
property bool customLogo: false
property alias source: image.source
property string defaultImage: ""
property string downloadUrl: ""
property string fileExtension: downloadUrl.substring(downloadUrl.lastIndexOf("."), downloadUrl.length)
property string localPath: ""
property int imageFillMode: 0
AnimatedImage {
id: image
objectName: "image"
anchors.fill: parent
fillMode: imageFillMode
smooth: true
antialiasing: true
property bool isGif: getIsGif(this)
Image {
id: default_img
objectName: "default_img"
anchors.fill: parent
source: defaultImage
visible: image.status != Image.Ready
smooth: true
antialiasing: true
property bool isGif: getIsGif(this)
}
}
function getIsGif(img) {
if (img.source && img.source != "") {
var localPath = img.source.toString();
if (localPath.startsWith("file://")) {
localPath = localPath.substring(7);
}
return UtilsAdapter.getMimeName(localPath).startsWith("image/gif");
}
return false;
}
Connections {
target: ImageDownloader
function onDownloadImageSuccessful(localPath) {
if (localPath === cachedImage.localPath) {
image.source = "file://" + localPath;
print("onDownloadImageSuccessful", localPath);
}
}
function onDownloadImageFailed(localPath) {
if (localPath === cachedImage.localPath) {
print("Failed to download image: " + downloadUrl);
image.source = defaultImage;
}
}
}
Connections {
target: cachedImage
function onDownloadUrlChanged() {
updateImageSource(downloadUrl, localPath, defaultImage);
}
}
Component.onCompleted: {
updateImageSource(downloadUrl, localPath, defaultImage);
}
function updateImageSource(downloadUrl, localPath, defaultImage) {
if (downloadUrl === "") {
image.source = defaultImage;
return;
}
if (downloadUrl && downloadUrl !== "" && localPath !== "") {
if (!UtilsAdapter.fileExists(localPath)) {
print("ImageDownloader.downloadImage", downloadUrl, localPath);
ImageDownloader.downloadImage(downloadUrl, localPath);
} else {
image.source = "file://" + localPath;
if (image.isGif) {
image.playing = true;
}
}
}
}
}

View File

@ -18,6 +18,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
@ -299,6 +300,8 @@ Control {
text: !checked ? JamiStrings.muteCamera : JamiStrings.unmuteCamera
checked: !CurrentCall.isCapturing
property var menuAction: videoInputMenuAction
enabled: CurrentAccount.videoEnabled_Video
onEnabledChanged: CallOverlayModel.setEnabled(this, muteVideoAction.enabled)
}
]
@ -319,6 +322,8 @@ Control {
icon.source: JamiResources.add_people_black_24dp_svg
icon.color: "white"
text: JamiStrings.addParticipants
enabled: CurrentCall.isModerator && !CurrentCall.isSIP
onEnabledChanged: CallOverlayModel.setEnabled(this, addPersonAction.enabled)
},
Action {
id: chatAction
@ -333,6 +338,8 @@ Control {
icon.source: CurrentCall.isPaused ? JamiResources.play_circle_outline_24dp_svg : JamiResources.pause_circle_outline_24dp_svg
icon.color: "white"
text: CurrentCall.isPaused ? JamiStrings.resumeCall : JamiStrings.pauseCall
enabled: CurrentCall.isSIP
onEnabledChanged: CallOverlayModel.setEnabled(this, resumePauseCallAction.enabled)
},
Action {
id: inputPanelSIPAction
@ -340,6 +347,8 @@ Control {
icon.source: JamiResources.ic_keypad_svg
icon.color: "white"
text: JamiStrings.sipInputPanel
enabled: CurrentCall.isSIP
onEnabledChanged: CallOverlayModel.setEnabled(this, inputPanelSIPAction.enabled)
},
Action {
id: callTransferAction
@ -347,6 +356,8 @@ Control {
icon.source: JamiResources.phone_forwarded_24dp_svg
icon.color: "white"
text: JamiStrings.transferCall
enabled: CurrentCall.isSIP
onEnabledChanged: CallOverlayModel.setEnabled(this, callTransferAction.enabled)
},
Action {
id: shareAction
@ -361,6 +372,8 @@ Control {
text: CurrentCall.isSharing ? JamiStrings.stopSharing : JamiStrings.shareScreen
property real size: 34
property var menuAction: shareMenuAction
enabled: CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP
onEnabledChanged: CallOverlayModel.setEnabled(this, shareAction.enabled)
},
Action {
id: raiseHandAction
@ -371,6 +384,8 @@ Control {
text: checked ? JamiStrings.lowerHand : JamiStrings.raiseHand
checked: CurrentCall.isHandRaised
property real size: 34
enabled: CurrentCall.isConference
onEnabledChanged: CallOverlayModel.setEnabled(this, raiseHandAction.enabled)
},
Action {
id: layoutAction
@ -406,6 +421,7 @@ Control {
icon.color: "white"
text: JamiStrings.viewPlugin
enabled: PluginAdapter.isEnabled && PluginAdapter.callMediaHandlersListCount
onEnabledChanged: CallOverlayModel.setEnabled(this, pluginsAction.enabled)
},
Action {
id: swarmDetailsAction
@ -414,7 +430,7 @@ Control {
icon.color: "white"
text: JamiStrings.details
enabled: {
if (LRCInstance.currentAccountType === Profile.Type.SIP)
if (CurrentCall.isSIP)
return true;
if (!CurrentConversation.isTemporary && !CurrentConversation.isSwarm)
return false;
@ -422,81 +438,35 @@ Control {
return false;
return true;
}
onEnabledChanged: CallOverlayModel.setEnabled(this, swarmDetailsAction.enabled)
}
]
property var overflowItemCount
Connections {
target: CurrentCall
function onIsActiveChanged() {
if (CurrentCall.isActive)
reset();
}
function onIsRecordingLocallyChanged() {
Qt.callLater(reset);
}
function onIsHandRaisedChanged() {
Qt.callLater(reset);
}
function onIsConferenceChanged() {
Qt.callLater(reset);
}
function onIsModeratorChanged() {
Qt.callLater(reset);
}
function onIsSIPChanged() {
Qt.callLater(reset);
}
function onIsAudioOnlyChanged() {
Qt.callLater(reset);
}
function onIsAudioMutedChanged() {
Qt.callLater(reset);
}
function onIsVideoMutedChanged() {
Qt.callLater(reset);
}
}
Connections {
target: CurrentAccount
function onVideoEnabledVideoChanged() {
reset();
}
}
function reset() {
Component.onCompleted: {
CallOverlayModel.clearControls();
// centered controls
CallOverlayModel.addPrimaryControl(muteAudioAction);
CallOverlayModel.addPrimaryControl(hangupAction);
if (CurrentAccount.videoEnabled_Video)
CallOverlayModel.addPrimaryControl(muteVideoAction);
CallOverlayModel.addPrimaryControl(muteAudioAction, muteAudioAction.enabled);
CallOverlayModel.addPrimaryControl(hangupAction, hangupAction.enabled);
CallOverlayModel.addPrimaryControl(muteVideoAction, muteVideoAction.enabled);
// overflow controls
CallOverlayModel.addSecondaryControl(audioOutputAction);
if (CurrentCall.isConference) {
CallOverlayModel.addSecondaryControl(raiseHandAction);
}
if (CurrentCall.isModerator && !CurrentCall.isSIP)
CallOverlayModel.addSecondaryControl(addPersonAction);
if (CurrentCall.isSIP) {
CallOverlayModel.addSecondaryControl(resumePauseCallAction);
CallOverlayModel.addSecondaryControl(inputPanelSIPAction);
CallOverlayModel.addSecondaryControl(callTransferAction);
}
CallOverlayModel.addSecondaryControl(chatAction);
if (CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP)
CallOverlayModel.addSecondaryControl(shareAction);
CallOverlayModel.addSecondaryControl(layoutAction);
CallOverlayModel.addSecondaryControl(recordAction);
if (pluginsAction.enabled)
CallOverlayModel.addSecondaryControl(pluginsAction);
if (swarmDetailsAction.enabled)
CallOverlayModel.addSecondaryControl(swarmDetailsAction);
CallOverlayModel.addSecondaryControl(audioOutputAction, audioOutputAction.enabled);
CallOverlayModel.addSecondaryControl(raiseHandAction, raiseHandAction.enabled);
CallOverlayModel.addSecondaryControl(addPersonAction, addPersonAction.enabled);
CallOverlayModel.addSecondaryControl(resumePauseCallAction, resumePauseCallAction.enabled);
CallOverlayModel.addSecondaryControl(inputPanelSIPAction, inputPanelSIPAction.enabled);
CallOverlayModel.addSecondaryControl(callTransferAction, callTransferAction.enabled);
CallOverlayModel.addSecondaryControl(chatAction, chatAction.enabled);
CallOverlayModel.addSecondaryControl(shareAction, shareAction.enabled);
CallOverlayModel.addSecondaryControl(layoutAction, layoutAction.enabled);
CallOverlayModel.addSecondaryControl(recordAction, recordAction.enabled);
CallOverlayModel.addSecondaryControl(pluginsAction, pluginsAction.enabled);
CallOverlayModel.addSecondaryControl(swarmDetailsAction, swarmDetailsAction.enabled);
overflowItemCount = CallOverlayModel.secondaryModel().rowCount();
}
@ -519,7 +489,13 @@ Control {
implicitHeight: contentHeight
interactive: false
model: CallOverlayModel.primaryModel()
model: SortFilterProxyModel {
sourceModel: root.visible ? CallOverlayModel.primaryModel() : null
filters: ValueFilter {
roleName: "Enabled"
value: true
}
}
delegate: buttonDelegate
}
}
@ -547,7 +523,15 @@ Control {
property int overflowIndex: {
var maxItems = Math.floor((overflowRect.remainingSpace) / (root.height + itemSpacing)) - 2;
return Math.min(overflowItemCount, maxItems);
var idx = Math.min(overflowItemCount, maxItems);
idx = Math.max(0, idx);
if (CallOverlayModel.overflowModel().rowCount() > 0 || CallOverlayModel.overflowHiddenModel().rowCount() > 0) {
var visibleIdx = CallOverlayModel.overflowModel().mapToSource(CallOverlayModel.overflowModel().index(idx, 0)).row;
var hiddenIdx = CallOverlayModel.overflowHiddenModel().mapToSource(CallOverlayModel.overflowHiddenModel().index(idx - CallOverlayModel.overflowModel().rowCount(), 0)).row;
if (visibleIdx >= 0 || hiddenIdx >= 0)
idx = Math.max(visibleIdx, hiddenIdx);
}
return idx;
}
property int nOverflowItems: overflowItemCount - overflowIndex
onNOverflowItemsChanged: {
@ -558,7 +542,7 @@ Control {
CallOverlayModel.overflowIndex = effectiveOverflowIndex;
}
model: CallOverlayModel.overflowModel()
model: root.visible ? CallOverlayModel.overflowModel() : null
delegate: buttonDelegate
}
@ -569,7 +553,7 @@ Control {
width: root.height
height: width
model: CallOverlayModel.overflowHiddenModel()
model: root.visible ? CallOverlayModel.overflowHiddenModel() : null
delegate: buttonDelegate

View File

@ -99,6 +99,9 @@ Rectangle {
ChatViewHeader {
id: chatViewHeader
addParticipantOpened: extrasPanel.currentIndex === ChatView.AddMemberPanel
swarmDetailsOpened: extrasPanel.currentIndex === ChatView.SwarmDetailsPanel
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
@ -260,6 +263,7 @@ Rectangle {
currentIndex: CurrentConversation.isRequest || CurrentConversation.needsSyncing
Loader {
id: loader
active: CurrentConversation.id !== ""
sourceComponent: MessageListView {
DropArea {
@ -299,6 +303,12 @@ Rectangle {
return CurrentConversation.isSwarm || CurrentConversation.isTemporary;
}
onHeightChanged: {
if (loader.item != null) {
Qt.callLater(loader.item.scrollToBottom);
}
}
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight

View File

@ -25,10 +25,10 @@ import "../../commoncomponents"
Rectangle {
id: root
property alias textInput: messageBar.textAreaObj
property string previousConvId
property string previousAccountId
property bool showTypo: messageBar.showTypo
function setFilePathsToSend(filePaths) {
for (var index = 0; index < filePaths.length; ++index) {
@ -96,7 +96,6 @@ Rectangle {
ColumnLayout {
id: footerColumnLayout
anchors.centerIn: root
width: root.width
@ -130,6 +129,8 @@ Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: footerColumnLayout.width
Layout.preferredHeight: height
property var emojiPicker
Connections {
@ -156,11 +157,7 @@ Rectangle {
}
function setXposition() {
if (UtilsAdapter.isRTL) {
return messageBar.width - JamiTheme.emojiPickerWidth;
} else {
return 0;
}
return messageBar.width - JamiTheme.emojiPickerWidth;
}
function setYposition() {
@ -170,7 +167,11 @@ Rectangle {
sendButtonVisibility: text || dataTransferSendContainer.filesToSendCount
onEmojiButtonClicked: {
openEmojiPicker();
if (emojiPicker != null && emojiPicker.opened) {
emojiPicker.closeEmojiPicker();
} else {
openEmojiPicker();
}
}
onShowMapClicked: {
@ -217,6 +218,16 @@ Rectangle {
messageBar.textAreaObj.clearText();
MessagesAdapter.replyToId = "";
}
Keys.onShortcutOverride: function (keyEvent) {
if (keyEvent.key === Qt.Key_Escape) {
if (recordBox != null && recordBox.opened) {
recordBox.closeRecorder();
} else if (PositionManager.isMapActive(CurrentAccount.id)) {
PositionManager.setMapInactive(CurrentAccount.id);
}
}
}
}
FilesToSendContainer {

View File

@ -60,6 +60,9 @@ Rectangle {
return true;
}
property bool addParticipantOpened: false
property bool swarmDetailsOpened: false
property bool addMemberVisibility: {
return swarmDetailsVisibility && !CurrentConversation.isCoreDialog && !CurrentConversation.isRequest;
}
@ -77,7 +80,7 @@ Rectangle {
anchors.rightMargin: 8
spacing: 16
PushButton {
JamiPushButton {
id: backToWelcomeViewButton
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
@ -90,9 +93,6 @@ Rectangle {
source: JamiResources.back_24dp_svg
toolTipText: CurrentConversation.inCall ? JamiStrings.backCall : JamiStrings.hideChat
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: root.backClicked()
}
@ -158,6 +158,8 @@ Rectangle {
Layout.preferredWidth: 40 + (isOpen? JamiTheme.searchbarSize : 0)
colorSearchBar: JamiTheme.backgroundColor
hoverButtonRadius: JamiTheme.chatViewHeaderButtonRadius
Behavior on Layout.preferredWidth {
NumberAnimation {
duration: 150
@ -184,88 +186,67 @@ Rectangle {
}
}
PushButton {
JamiPushButton {
id: startAAudioCallButton
visible: interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
source: JamiResources.place_audiocall_24dp_svg
toolTipText: JamiStrings.placeAudioCall
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: CallAdapter.placeAudioOnlyCall()
}
PushButton {
JamiPushButton {
id: startAVideoCallButton
visible: CurrentAccount.videoEnabled_Video && interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
source: JamiResources.videocam_24dp_svg
toolTipText: JamiStrings.placeVideoCall
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: {
CallAdapter.placeCall();
}
onClicked: CallAdapter.placeCall()
}
PushButton {
JamiPushButton {
id: addParticipantsButton
checkable: true
checked: root.addParticipantOpened
visible: interactionButtonsVisibility && addMemberVisibility
source: JamiResources.add_people_24dp_svg
toolTipText: JamiStrings.addParticipants
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
visible: interactionButtonsVisibility && addMemberVisibility
onClicked: addToConversationClicked()
}
PushButton {
JamiPushButton {
id: selectPluginButton
visible: PluginAdapter.isEnabled && PluginAdapter.chatHandlersListCount && interactionButtonsVisibility
source: JamiResources.plugins_24dp_svg
toolTipText: JamiStrings.showPlugins
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: pluginSelector()
}
PushButton {
JamiPushButton {
id: sendContactRequestButton
visible: CurrentConversation.isTemporary || CurrentConversation.isBanned
source: JamiResources.add_people_24dp_svg
toolTipText: JamiStrings.addToConversations
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: CurrentConversation.isBanned ? MessagesAdapter.unbanConversation(CurrentConversation.id) : MessagesAdapter.sendConversationRequest()
}
PushButton {
JamiPushButton {
id: detailsButton
checkable: true
checked: root.swarmDetailsOpened
visible: interactionButtonsVisibility && (swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP) // TODO if SIP not a request
source: JamiResources.swarm_details_panel_svg
toolTipText: JamiStrings.details
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: showDetailsClicked()
}
}

View File

@ -23,113 +23,66 @@ import net.jami.Models 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
Popup {
BaseModalDialog {
id: contactPickerPopup
property int type: ContactList.CONFERENCE
contentWidth: 250
contentHeight: contactPickerPopupRectColumnLayout.height + 50
width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth)
height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight)
padding: 0
modal: true
title: {
switch (type) {
case ContactList.CONFERENCE:
return JamiStrings.addToConference;
case ContactList.ADDCONVMEMBER:
return JamiStrings.addToConversation;
case ContactList.TRANSFER:
return JamiStrings.transferThisCall;
default:
return JamiStrings.addDefaultModerator;
}
}
contentItem: Rectangle {
id: contactPickerPopupRect
width: 250
popupContent: ColumnLayout {
id: contactPickerPopupRectColumnLayout
PushButton {
id: closeButton
Searchbar {
id: contactPickerContactSearchBar
anchors.top: contactPickerPopupRect.top
anchors.topMargin: 5
anchors.right: contactPickerPopupRect.right
anchors.rightMargin: 5
imageColor: JamiTheme.textColor
Layout.alignment: Qt.AlignCenter
Layout.margins: 5
Layout.fillWidth: true
Layout.preferredHeight: 35
source: JamiResources.round_close_24dp_svg
placeHolderText: type === ContactList.TRANSFER ? JamiStrings.transferTo : JamiStrings.addParticipant
onClicked: {
contactPickerPopup.close();
onSearchBarTextChanged: function(text){
ContactAdapter.setSearchFilter(text);
}
}
ColumnLayout {
id: contactPickerPopupRectColumnLayout
JamiListView {
id: contactPickerListView
anchors.top: contactPickerPopupRect.top
anchors.topMargin: 15
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
Layout.preferredHeight: 180
Layout.bottomMargin: JamiTheme.preferredMarginSize
Text {
id: contactPickerTitle
model: ContactAdapter.getContactSelectableModel(type)
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: contactPickerPopupRect.width
Layout.preferredHeight: 30
delegate: ContactPickerItemDelegate {
id: contactPickerItemDelegate
font.pointSize: JamiTheme.textFontSize
font.bold: true
color: JamiTheme.textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: {
switch (type) {
case ContactList.CONFERENCE:
return JamiStrings.addToConference;
case ContactList.ADDCONVMEMBER:
return JamiStrings.addToConversation;
case ContactList.TRANSFER:
return JamiStrings.transferThisCall;
default:
return JamiStrings.addDefaultModerator;
}
}
}
Searchbar {
id: contactPickerContactSearchBar
Layout.alignment: Qt.AlignCenter
Layout.margins: 5
Layout.fillWidth: true
Layout.preferredHeight: 35
placeHolderText: type === ContactList.TRANSFER ? JamiStrings.transferTo : JamiStrings.addParticipant
onSearchBarTextChanged: function(text){
ContactAdapter.setSearchFilter(text);
}
}
JamiListView {
id: contactPickerListView
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: contactPickerPopupRect.width
Layout.preferredHeight: 200
model: ContactAdapter.getContactSelectableModel(type)
delegate: ContactPickerItemDelegate {
id: contactPickerItemDelegate
showPresenceIndicator: type !== ContactList.TRANSFER
}
showPresenceIndicator: type !== ContactList.TRANSFER
}
}
radius: 10
color: JamiTheme.backgroundColor
}
onAboutToShow: {
contactPickerListView.model = ContactAdapter.getContactSelectableModel(type);
}
background: Rectangle {
color: "transparent"
}
}

View File

@ -35,9 +35,9 @@ JamiListView {
}
currentIndex: model.currentFilteredRow
// highlight selection
// down and hover states are done within the delegate
highlightMoveDuration: 60
// Disable highlight on current item; we do this ourselves with the
// SmartListItemDelegate.
highlightFollowsCurrentItem: false
headerPositioning: ListView.OverlayHeader
header: Rectangle {
@ -53,7 +53,7 @@ JamiListView {
verticalCenter: parent.verticalCenter
}
text: headerLabel + " (" + root.count + ")"
font.pointSize: JamiTheme.smartlistItemFontSize
font.pointSize: JamiTheme.mediumFontSize
font.weight: Font.DemiBold
color: JamiTheme.textColor
}

View File

@ -30,7 +30,7 @@ ContextMenuAutoLoader {
property string responsibleAccountId: ""
property string responsibleConvUid: ""
property bool isBanned: false
property var isCoreDialog: undefined
property bool isCoreDialog: false
property var mode: undefined
property int contactType: Profile.Type.INVALID
property bool hasCall: false
@ -91,7 +91,7 @@ ContextMenuAutoLoader {
"confirmLabel": JamiStrings.optionRemove
});
dlg.accepted.connect(function () {
if (!isCoreDialog)
if (mode !== Conversation.Mode.NON_SWARM)
MessagesAdapter.removeConversation(responsibleConvUid);
else
MessagesAdapter.removeContact(responsibleConvUid);
@ -126,8 +126,8 @@ ContextMenuAutoLoader {
GeneralMenuItem {
id: blockContact
canTrigger: !hasCall && contactType !== Profile.Type.SIP && !root.isBanned
itemName: !(mode && isCoreDialog) ? JamiStrings.blockContact : JamiStrings.blockSwarm
canTrigger: !hasCall && contactType !== Profile.Type.SIP && !root.isBanned && isCoreDialog && root.idText !== CurrentAccount.uri
itemName: JamiStrings.blockContact
iconSource: JamiResources.block_black_24dp_svg
addMenuSeparatorAfter: canTrigger
onClicked: {

View File

@ -29,6 +29,17 @@ ColumnLayout {
property var iconSize: 26
property var margin: 5
property var prefWidth: 170
property bool opened: root.opened
property color textColor: JamiTheme.textColor
property color iconColor: JamiTheme.tintedBlue
focus: true
onOpenedChanged: {
if (opened)
displayNameLineEdit.forceActiveFocus();
}
RowLayout {
@ -49,16 +60,16 @@ ColumnLayout {
containerWidth: Layout.preferredWidth
source: JamiResources.noun_paint_svg
color: JamiTheme.buttonTintedBlue
color: column.iconColor
focus: activeFocus
}
Label {
text: JamiStrings.customize
color: JamiTheme.textColor
color: column.textColor
font.weight: Font.Medium
Layout.topMargin: column.margin
Layout.preferredWidth: column.prefWidth - 2 * column.margin - column.iconSize
visible: !opened
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: column.margin
horizontalAlignment: Text.AlignLeft
@ -78,7 +89,7 @@ ColumnLayout {
wrapMode: Text.WordWrap
font.weight: Font.Normal
text: JamiStrings.customizeText
color: JamiTheme.textColor
color: column.textColor
}
PhotoboothView {
@ -106,17 +117,18 @@ ColumnLayout {
placeholderText: JamiStrings.enterNickname
onAccepted: AccountAdapter.setCurrAccDisplayName(dynamicText)
focus: activeFocus
}
Text {
Layout.preferredWidth: root.width - 32
Layout.leftMargin: 20
Layout.topMargin: 6
Layout.topMargin: 15
font.pixelSize: JamiTheme.tipBoxContentFontSize
visible: opened
wrapMode: Text.WordWrap
text: JamiStrings.customizationDescription2
color: JamiTheme.textColor
color: column.textColor
}
}

View File

@ -26,12 +26,18 @@ ColumnLayout {
id: column
width: parent.width
property real maxHeight: 250
property var iconSize: 26
property var margin: 5
property var prefWidth: 170
RowLayout {
property color textColor: JamiTheme.textColor
property color iconColor: JamiTheme.tintedBlue
RowLayout {
id: rowlayout
Layout.preferredHeight: opened ? 0 : childrenRect.height
Layout.leftMargin: 15
Layout.alignment: Qt.AlignLeft
@ -49,12 +55,12 @@ ColumnLayout {
containerWidth: Layout.preferredWidth
source: JamiResources.glasses_tips_svg
color: JamiTheme.buttonTintedBlue
color: column.iconColor
}
Label {
text: JamiStrings.tip
color: JamiTheme.textColor
color: column.textColor
font.weight: Font.Medium
Layout.topMargin: column.margin
visible: !opened
@ -68,26 +74,35 @@ ColumnLayout {
}
Text {
id: title
Layout.preferredHeight: contentHeight
Layout.preferredWidth: opened ? 140 : 150
Layout.leftMargin: 20
Layout.topMargin: opened ? 0 : 8
Layout.bottomMargin: 15
Layout.bottomMargin: 8
font.pixelSize: JamiTheme.tipBoxContentFontSize
wrapMode: Text.WordWrap
font.weight: opened ? Font.Medium : Font.Normal
text: root.title
horizontalAlignment: Text.AlignLeft
color: JamiTheme.textColor
color: column.textColor
}
Text {
JamiFlickable {
Layout.preferredWidth: root.width - 32
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
font.pixelSize: JamiTheme.tipBoxContentFontSize
visible: opened
wrapMode: Text.WordWrap
text: root.description
horizontalAlignment: Text.AlignLeft
color: JamiTheme.textColor
property real maxDescriptionHeight: maxHeight - rowlayout.Layout.preferredHeight - title.Layout.preferredHeight - 2 * JamiTheme.preferredMarginSize
Layout.preferredHeight: opened ? Math.min(contentHeight, maxDescriptionHeight) : 0
contentHeight: description.height
Text {
id: description
width: parent.width
font.pixelSize: JamiTheme.tipBoxContentFontSize
visible: opened
wrapMode: Text.WordWrap
text: root.description
horizontalAlignment: Text.AlignLeft
color: column.textColor
}
}
}

View File

@ -169,7 +169,7 @@ Rectangle {
Layout.preferredWidth: root.width
Layout.topMargin: 8
font.pointSize: JamiTheme.smartlistItemFontSize
font.pointSize: JamiTheme.mediumFontSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@ -238,7 +238,7 @@ Rectangle {
Layout.preferredWidth: JamiTheme.callButtonPreferredSize
Layout.preferredHeight: JamiTheme.preferredFieldHeight
font.pointSize: JamiTheme.smartlistItemInfoFontSize
font.pointSize: JamiTheme.smallFontSize
font.kerning: true
color: actionButton.hovered ? JamiTheme.whiteColor : JamiTheme.whiteColorTransparent

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2022-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 net.jami.Constants 1.1
import "../../commoncomponents"
PushButton {
pressedColor: JamiTheme.pressedButtonColor
hoveredColor: JamiTheme.hoveredButtonColor
radius: JamiTheme.chatViewHeaderButtonRadius
normalColor: JamiTheme.chatviewBgColor
imageColor: hovered ? JamiTheme.chatviewButtonColor : JamiTheme.chatViewFooterImgColor
}

View File

@ -255,6 +255,16 @@ Window {
shortcut2: ""
description: qsTr("Ordered list")
}
ListElement {
shortcut: "Shift + Alt + T"
shortcut2: ""
description: qsTr("Show Formatting")
}
ListElement {
shortcut: "Shift + Alt + P"
shortcut2: ""
description: qsTr("Show Preview")
}
}
Rectangle {

File diff suppressed because it is too large Load Diff

View File

@ -19,23 +19,36 @@ import QtQuick
import QtQuick.Controls
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
import QtQuick.Layouts
import SortFilterProxyModel 0.2
JamiFlickable {
id: root
property int maxWidth: 330
property bool tooMuch: {
return textArea.contentWidth > maxWidth;
}
property alias text: textArea.text
property var textAreaObj: textArea
property alias placeholderText: textArea.placeholderText
property alias selectedText: textArea.selectedText
property alias selectionStart: textArea.selectionStart
property alias selectionEnd: textArea.selectionEnd
property bool showPreview: false
ScrollBar.vertical.visible: textArea.text
ScrollBar.horizontal.visible: textArea.text
signal sendMessagesRequired
function heightBinding() {
textArea.height = Qt.binding(() => textArea.paintedHeight);
}
function selectText(start, end) {
textArea.select(start, end);
}
@ -65,11 +78,72 @@ JamiFlickable {
}
interactive: true
attachedFlickableMoving: contentHeight > height || root.moving
attachedFlickableMoving: textAreaPreview.height > height || textArea.height > height || root.moving
contentHeight: showPreview ? textAreaPreview.height : textArea.height
onShowPreviewChanged: {
if (showPreview) {
textAreaPreview.height = textArea.lineCount === 1 ? textArea.height : textAreaPreview.paintedHeight;
}
heightBinding();
}
TextArea {
id: textAreaPreview
onWidthChanged: root.height = this.height
overwriteMode: false
readOnly: true
height: textArea.lineCount === 1 ? textArea.height : this.paintedHeight
width: textArea.width
visible: showPreview
leftPadding: JamiTheme.scrollBarHandleSize
rightPadding: JamiTheme.scrollBarHandleSize
topPadding: 0
bottomPadding: 0
Connections {
target: textArea
function onTextChanged() {
MessagesAdapter.parseMessage("", textArea.text, false, "", "");
}
}
Connections {
target: MessagesAdapter
function onMessageParsed(messageId, messageText) {
if (messageId === "") {
textAreaPreview.text = messageText;
}
}
}
verticalAlignment: TextEdit.AlignVCenter
font.pointSize: JamiTheme.textFontSize + 2
font.hintingPreference: Font.PreferNoHinting
color: JamiTheme.textColor
wrapMode: TextEdit.Wrap
textFormat: TextEdit.RichText
placeholderTextColor: JamiTheme.messageBarPlaceholderTextColor
horizontalAlignment: Text.AlignLeft
background: Rectangle {
border.width: 0
color: "transparent"
}
}
TextArea.flickable: TextArea {
id: textArea
visible: !showPreview
leftPadding: JamiTheme.scrollBarHandleSize
rightPadding: JamiTheme.scrollBarHandleSize
topPadding: 0
@ -77,6 +151,8 @@ JamiFlickable {
persistentSelection: true
height: this.paintedHeight
verticalAlignment: TextEdit.AlignVCenter
font.pointSize: JamiTheme.textFontSize + 2
@ -86,12 +162,12 @@ JamiFlickable {
wrapMode: TextEdit.Wrap
selectByMouse: true
textFormat: TextEdit.PlainText
placeholderTextColor: JamiTheme.placeholderTextColor
placeholderTextColor: JamiTheme.messageBarPlaceholderTextColor
horizontalAlignment: Text.AlignLeft
background: Rectangle {
border.width: 0
color: JamiTheme.transparentColor
color: "transparent"
}
onReleased: function (event) {
@ -121,7 +197,10 @@ JamiFlickable {
MessagesAdapter.editId = CurrentConversation.lastSelfMessageId;
keyEvent.accepted = true;
} else if (keyEvent.key === Qt.Key_Enter || keyEvent.key === Qt.Key_Return) {
if (!(keyEvent.modifiers & Qt.ShiftModifier)) {
const isEnterNewLine = UtilsAdapter.getAppValue(Settings.Key.ChatViewEnterIsNewLine);
const isShiftPressed = (keyEvent.modifiers & Qt.ShiftModifier);
const isCtrlPressed = (keyEvent.modifiers & Qt.ControlModifier);
if (!isEnterNewLine && !isShiftPressed || isCtrlPressed) {
root.sendMessagesRequired();
keyEvent.accepted = true;
}

View File

@ -54,6 +54,10 @@ JamiListView {
return false
}
function scrollToBottom() {
verticalScrollBar.position = 1 - verticalScrollBar.size;
}
function computeChatview(item, itemIndex) {
if (!root) return
var rootItem = root.itemAtIndex(0)
@ -277,7 +281,7 @@ JamiListView {
anchors.horizontalCenter: root.horizontalCenter
visible: 1 - verticalScrollBar.position >= verticalScrollBar.size * 2
onClicked: verticalScrollBar.position = 1 - verticalScrollBar.size
onClicked: scrollToBottom()
}
header: Control {

View File

@ -105,7 +105,7 @@ ListView {
Layout.rightMargin: 10
Layout.leftMargin: 10
font.pixelSize: 0
color: JamiTheme.chatviewUsernameColor
color: JamiTheme.chatviewSecondaryInformationColor
font.bold: true
}
@ -145,7 +145,7 @@ ListView {
id: buttonJumpText
text: JamiStrings.jumpTo
color: buttonJumpTo.hovered ? JamiTheme.blueLinkColor : JamiTheme.chatviewUsernameColor
color: buttonJumpTo.hovered ? JamiTheme.blueLinkColor : JamiTheme.chatviewSecondaryInformationColor
font.underline: buttonJumpTo.hovered
anchors.centerIn: parent
font.pointSize: JamiTheme.jumpToFontSize

View File

@ -125,18 +125,18 @@ Rectangle {
anchors.fill: parent
property bool isHorizontal: false // Calculated when showing the stack view
orientation: isHorizontal ? Qt.Horizontal : Qt.Vertical
orientation: isHorizontal ? Qt.Vertical : Qt.Horizontal // Chatview is horizontal if split is vertical (so chatview takes full width)
handle: Rectangle {
implicitWidth: mainColumnLayout.isHorizontal ? JamiTheme.splitViewHandlePreferredWidth : root.width
implicitHeight: mainColumnLayout.isHorizontal ? root.height : JamiTheme.splitViewHandlePreferredWidth
implicitWidth: mainColumnLayout.isHorizontal ? root.width : JamiTheme.splitViewHandlePreferredWidth
implicitHeight: mainColumnLayout.isHorizontal ? JamiTheme.splitViewHandlePreferredWidth : root.height
color: SplitHandle.pressed ? JamiTheme.pressColor : (SplitHandle.hovered ? JamiTheme.hoverColor : JamiTheme.tabbarBorderColor)
}
Rectangle {
id: callPageMainRect
SplitView.preferredHeight: mainColumnLayout.isHorizontal ? root.height : (root.height / 3) * 2
SplitView.preferredHeight: mainColumnLayout.isHorizontal ? (root.height / 3) * 2 : root.height
SplitView.minimumWidth: JamiTheme.mainViewPaneMinWidth
SplitView.fillWidth: true

View File

@ -51,6 +51,9 @@ Popup {
signal validatePhoto(string photo)
modal: true
closePolicy: Popup.CloseOnPressOutsideParent
function openRecorder(vid) {
isVideo = vid;
updateState(RecordBox.States.INIT);
@ -121,9 +124,6 @@ Popup {
time.text = min + ":" + sec;
}
modal: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
onActiveFocusChanged: {
if (visible) {
closeRecorder();

View File

@ -101,7 +101,7 @@ Control {
}
background: Rectangle {
radius: 20
radius: 5
color: CurrentConversation.color
MouseArea {
@ -117,7 +117,7 @@ Control {
z: -1
horizontalOffset: 3.0
verticalOffset: 3.0
radius: 8.0
radius: 5.0
color: JamiTheme.shadowColor
transparentBorder: true
samples: radius + 1

View File

@ -24,7 +24,6 @@ import "../../commoncomponents"
Rectangle {
id: root
signal searchBarTextChanged(string text)
signal returnPressedWhileSearching
signal searchClicked
@ -33,6 +32,8 @@ Rectangle {
property alias textContent: textArea.text
property alias placeHolderText: textArea.placeholderText
property real hoverButtonRadius: JamiTheme.chatViewHeaderButtonRadius
property var colorSearchBar: JamiTheme.secondaryBackgroundColor
property string currentConversationId: CurrentConversation.id
@ -40,7 +41,7 @@ Rectangle {
property bool isOpen: reductionEnabled ? extrasPanel.isOpen(ChatView.MessagesResearchPanel) : true
onIsOpenChanged: {
if (isOpen)
textArea.forceActiveFocus()
textArea.forceActiveFocus();
}
function clearText() {
@ -71,11 +72,21 @@ Rectangle {
anchors.leftMargin: 10
hoverEnabled: reductionEnabled
enabled: reductionEnabled
radius: hoverButtonRadius
hoveredColor: JamiTheme.hoveredButtonColor
source: JamiResources.ic_baseline_search_24dp_svg
normalColor: "transparent"
imageColor: JamiTheme.chatviewButtonColor
imageColor: {
if (reductionEnabled) {
if (hovered) {
JamiTheme.chatviewButtonColor;
} else {
JamiTheme.chatViewFooterImgColor;
}
} else {
JamiTheme.chatviewButtonColor;
}
}
onClicked: root.searchClicked()
}
@ -83,7 +94,7 @@ Rectangle {
Rectangle {
id: rectTextArea
height: root.height-5
height: root.height - 5
anchors.left: startSearch.right
anchors.right: root.right
anchors.verticalCenter: root.verticalCenter
@ -98,7 +109,7 @@ Rectangle {
}
width: isOpen ? JamiTheme.searchbarSize : 0
Behavior on width {
Behavior on width {
NumberAnimation {
duration: 150
}
@ -111,7 +122,6 @@ Rectangle {
background.visible: false
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: textArea.text.length ? clearTextButton.left : parent.right
@ -136,7 +146,6 @@ Rectangle {
PushButton {
id: clearTextButton
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 15

View File

@ -139,7 +139,7 @@ ItemDelegate {
elide: Text.ElideMiddle
text: Title === undefined ? "" : Title
textFormat: TextEdit.PlainText
font.pointSize: JamiTheme.smartlistItemFontSize
font.pointSize: JamiTheme.mediumFontSize
font.weight: UnreadMessagesCount ? Font.Bold : Font.Normal
color: JamiTheme.textColor
}
@ -157,7 +157,7 @@ ItemDelegate {
Layout.alignment: Qt.AlignVCenter
text: lastInteractionFormattedDate === undefined ? "" : lastInteractionFormattedDate
textFormat: TextEdit.PlainText
font.pointSize: JamiTheme.smartlistItemInfoFontSize
font.pointSize: JamiTheme.smallFontSize
font.weight: UnreadMessagesCount ? Font.DemiBold : Font.Normal
color: JamiTheme.textColor
}
@ -172,7 +172,7 @@ ItemDelegate {
Draft :
(LastInteraction === undefined ? "" : LastInteraction)
textFormat: TextEdit.PlainText
font.pointSize: JamiTheme.smartlistItemInfoFontSize
font.pointSize: JamiTheme.smallFontSize
font.weight: UnreadMessagesCount ? Font.Normal : Font.Light
font.hintingPreference: Font.PreferNoHinting
maximumLineCount: 1
@ -186,7 +186,7 @@ ItemDelegate {
text: JamiStrings.banned
textFormat: TextEdit.PlainText
visible: IsBanned
font.pointSize: JamiTheme.smartlistItemFontSize
font.pointSize: JamiTheme.mediumFontSize
font.weight: Font.Bold
color: JamiTheme.textColor
}
@ -232,7 +232,7 @@ ItemDelegate {
Layout.alignment: Qt.AlignRight
text: InCall ? UtilsAdapter.getCallStatusStr(CallState) : ""
textFormat: TextEdit.PlainText
font.pointSize: JamiTheme.smartlistItemInfoFontSize
font.pointSize: JamiTheme.smallFontSize
font.weight: Font.Medium
color: JamiTheme.textColor
}

View File

@ -38,20 +38,27 @@ Rectangle {
property string textColor: UtilsAdapter.luma(root.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
ColumnLayout {
Item {
id: swarmProfileDetails
anchors.fill: parent
spacing: 0
ColumnLayout {
Item {
id: header
Layout.topMargin: JamiTheme.swarmDetailsPageTopMargin
Layout.fillWidth: true
spacing: JamiTheme.preferredMarginSize
anchors.top: parent.top
anchors.topMargin: JamiTheme.swarmDetailsPageTopMargin
anchors.left: parent.left
anchors.right: parent.right
height: headerRow.implicitHeight + JamiTheme.preferredMarginSize + settingsTabButton.height
RowLayout {
id: headerRow
spacing: 15
Layout.leftMargin: 15
anchors.top: parent.top
anchors.leftMargin: 15
anchors.left: parent.left
anchors.right: parent.right
PhotoboothView {
id: currentAccountAvatar
@ -100,6 +107,7 @@ Rectangle {
editMode: false
isPersistent: false
placeholderText: JamiStrings.title
onActiveFocusChanged: {
if (!activeFocus) {
@ -159,20 +167,26 @@ Rectangle {
currentIndex: 0
Layout.preferredWidth: root.width
Layout.preferredHeight: settingsTabButton.height
anchors.top: headerRow.bottom
anchors.topMargin: JamiTheme.preferredMarginSize
anchors.left: parent.left
anchors.right: parent.right
height: settingsTabButton.height
property string currentItemName: itemAt(currentIndex).objectName
function addRemoveButtons() {
if (CurrentConversation.isCoreDialog) {
if (tabBar.contentChildren.length === 3)
tabBar.removeItem(tabBar.itemAt(1));
if (tabBar.contentChildren.length === 3) {
tabBar.setCurrentIndex(1);
tabBar.removeItem(tabBar.itemAt(0));
}
} else {
if (tabBar.contentChildren.length === 2) {
const obj = membersTabButtonComp.createObject(tabBar);
tabBar.insertItem(1, obj);
tabBar.insertItem(0, obj);
}
tabBar.setCurrentIndex(0);
}
}
@ -201,9 +215,9 @@ Rectangle {
}
DetailsTabButton {
id: documentsTabButton
objectName: "documents"
labelText: JamiStrings.documents
id: filesTabButton
objectName: "files"
labelText: JamiStrings.files
}
DetailsTabButton {
@ -230,8 +244,11 @@ Rectangle {
Rectangle {
id: details
Layout.fillWidth: true
Layout.preferredHeight: root.height - header.height - 2 * JamiTheme.preferredMarginSize
anchors.top: header.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: JamiTheme.secondaryBackgroundColor
JamiFlickable {
@ -430,7 +447,7 @@ Rectangle {
}
maxWidth: settingsSwarmItem.width / 2 - JamiTheme.contactMessageAvatarSize
font.pointSize: eText === JamiStrings.none ? JamiTheme.settingsFontSize : JamiTheme.smartlistItemInfoFontSize
font.pointSize: eText === JamiStrings.none ? JamiTheme.settingsFontSize : JamiTheme.smallFontSize
font.weight: eText === JamiStrings.none ? Font.Medium : Font.Normal
color: JamiTheme.primaryForegroundColor
font.kerning: true
@ -503,14 +520,12 @@ Rectangle {
RowLayout {
Layout.leftMargin: JamiTheme.preferredMarginSize
Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
Layout.maximumWidth: parent.width
visible: LRCInstance.debugMode()
Text {
id: idLabel
Layout.fillWidth: true
Layout.preferredHeight: 30
Layout.rightMargin: JamiTheme.preferredMarginSize
Layout.maximumWidth: parent.width / 2
text: JamiStrings.identifier
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
@ -524,12 +539,11 @@ Rectangle {
Text {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: JamiTheme.settingsMarginSize
Layout.maximumWidth: parent.width / 2
Layout.rightMargin: JamiTheme.preferredMarginSize
color: JamiTheme.textColor
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
font.weight: Font.Medium
text: CurrentConversation.id
elide: Text.ElideRight
@ -540,8 +554,6 @@ Rectangle {
JamiListView {
id: members
anchors.topMargin: JamiTheme.preferredMarginSize
anchors.bottomMargin: JamiTheme.preferredMarginSize
anchors.fill: parent
visible: tabBar.currentItemName === "members"
@ -660,7 +672,7 @@ Rectangle {
DocumentsScrollview {
id: documents
visible: tabBar.currentItemName === "documents"
visible: tabBar.currentItemName === "files"
anchors.fill: parent
}
}

View File

@ -24,7 +24,7 @@ import net.jami.Constants 1.1
import Qt5Compat.GraphicalEffects
import "../../commoncomponents"
Item {
FocusScope {
id: root
property string title: ""
property string description: ""
@ -32,7 +32,11 @@ Item {
property string type: ""
property bool hovered: false
property bool clicked: false
property bool opened: false
property bool opened: activeFocus
property color backgroundColor: JamiTheme.welcomeBlockColor
property color textColor: JamiTheme.textColor
property color iconColor: JamiTheme.tintedBlue
property string customizeTip: "CustomizeTipBox {}"
@ -40,18 +44,28 @@ Item {
property string infoTip: "InformativeTipBox {}"
width: 200
height: tipColumnLayout.implicitHeight + 2 * JamiTheme.preferredMarginSize
width: JamiTheme.tipBoxWidth
property real minimumHeight: 150
property real maximumHeight: 250
height: Math.max(minimumHeight, Math.min(maximumHeight, tipColumnLayout.implicitHeight + 2 * JamiTheme.preferredMarginSize))
signal ignoreClicked
focus: true
activeFocusOnTab: true
Rectangle {
id: rect
anchors.fill: parent
color: opened || hovered ? JamiTheme.tipBoxBackgroundColor : "transparent"
border.color: JamiTheme.tipBoxBorderColor
radius: 20
color: root.backgroundColor
radius: 5
focus: true
Column {
id: tipColumnLayout
@ -59,14 +73,40 @@ Item {
width: parent.width
anchors.topMargin: 10
Component.onCompleted: {
if (type === "customize") {
Qt.createQmlObject(customizeTip, this, 'tip');
} else if (type === "backup") {
Qt.createQmlObject(backupTip, this, 'tip');
} else {
Qt.createQmlObject(infoTip, this, 'tip');
Loader {
id: loader_backupTip
active: type === "backup"
sourceComponent: BackupTipBox {
onIgnore: {
root.ignoreClicked();
}
maxHeight: root.maximumHeight
textColor: root.textColor
iconColor: root.iconColor
}
width: parent.width
}
Loader {
id: loader_customizeTip
active: type === "customize"
sourceComponent: CustomizeTipBox {
textColor: root.textColor
iconColor: root.iconColor
}
width: parent.width
focus: true
}
Loader {
id: loader_infoTip
active: type === "tip"
sourceComponent: InformativeTipBox {
maxHeight: root.maximumHeight
textColor: root.textColor
iconColor: root.iconColor
}
width: parent.width
}
}
}
@ -79,7 +119,9 @@ Item {
TapHandler {
target: rect
onTapped: opened = !opened
onTapped: {
return opened ? focus = false : root.forceActiveFocus();
}
}
DropShadow {
@ -96,25 +138,35 @@ Item {
samples: radius + 1
}
PushButton {
id: btnClose
Loader {
id: loader_btnClose
active: type === "tip"
sourceComponent: component_btnClose
anchors.margins: 8
anchors.bottom: root.top
anchors.horizontalCenter: root.horizontalCenter
}
width: 20
height: 20
imageContainerWidth: 20
imageContainerHeight: 20
anchors.margins: 14
anchors.top: parent.top
anchors.right: parent.right
visible: opened
circled: true
Component {
id: component_btnClose
PushButton {
id: btnClose
imageColor: Qt.rgba(0, 86 / 255, 153 / 255, 1)
normalColor: "transparent"
toolTipText: JamiStrings.dismiss
width: 20
height: 20
imageContainerWidth: 20
imageContainerHeight: 20
source: JamiResources.round_close_24dp_svg
visible: opened
circled: true
onClicked: root.ignoreClicked()
imageColor: root.iconColor
normalColor: "transparent"
toolTipText: JamiStrings.dismiss
source: JamiResources.trash_black_24dp_svg
onClicked: root.ignoreClicked()
}
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2022-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 net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
import "../js/keyboardshortcuttablecreation.js" as KeyboardShortcutTableCreation
JamiFlickable {
id: tipsRow
clip: false
property color tipsColor: JamiTheme.welcomeBlockColor
property color tipsTextColor: JamiTheme.textColor
property color iconColor: JamiTheme.tintedBlue
width: JamiTheme.welcomeGridWidth
height: getHeight()
function getHeight() {
return row.height;
}
Row {
id: row
clip: false
spacing: JamiTheme.welcomePageSpacing
height: 150
width: JamiTheme.welcomeGridWidth
property real openedTipCount: 0
Repeater {
id: tipsRepeater
model: TipsModel
anchors.bottom: row.bottom
delegate: TipBox {
backgroundColor: tipsRow.tipsColor
tipId: TipId
title: Title
description: Description
type: Type
property bool hideTipBox: false
anchors.bottom: row.bottom
textColor:tipsTextColor
iconColor: tipsRow.iconColor
visible: {
if (hideTipBox)
return false;
if (type === "backup") {
return LRCInstance.currentAccountType !== Profile.Type.SIP && CurrentAccount.managerUri.length === 0;
} else if (type === "customize") {
return CurrentAccount.alias.length === 0;
}
return true;
}
enabled: {
if (x >= tipsRow.width)
return false;
else
return true;
}
opacity: {
if (x >= tipsRow.width)
return 0;
else
return 1;
}
onIgnoreClicked: {
hideTipBox = true;
}
onOpenedChanged: {
if (opened)
row.openedTipCount++;
else
row.openedTipCount--;
}
}
}
}
}

View File

@ -0,0 +1,165 @@
/*
* 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 net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
import "../js/keyboardshortcuttablecreation.js" as KeyboardShortcutTableCreation
Item {
id: welcomeInfo
property color backgroundColor: "transparent"
property color textColor: JamiTheme.textColor
property color idColor: JamiTheme.welcomeBlockColor
property color contentIdColor: JamiTheme.tintedBlue
property bool hasTitle: true
property bool hasDescription: true
property string title: JamiStrings.welcomeToJami
property string description: JamiStrings.hereIsIdentifier
property real contentWidth: welcomeInfo.width - 2 * JamiTheme.mainViewMargin
function getHeight() {
return bgRect.height;
}
Rectangle {
id: bgRect
radius: 5
color: welcomeInfo.backgroundColor
height: childrenRect.height + JamiTheme.mainViewMargin / 2
width: welcomeInfo.width
ColumnLayout {
id: columnLayoutInfo
anchors.horizontalCenter: bgRect.horizontalCenter
spacing: 0
Loader {
id: loader_welcomeTitle
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: item ? item.contentHeight : 0
Layout.topMargin: JamiTheme.mainViewMargin / 2
Layout.bottomMargin: loader_identifierDescription.item ? JamiTheme.mainViewMargin - 15 : 0
sourceComponent: welcomeInfo.hasTitle ? component_welcomeTitle : undefined
}
Loader {
id: loader_identifierDescription
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: contentWidth
Layout.preferredHeight: item ? item.contentHeight : 0
Layout.bottomMargin: loader_bottomIdentifier.item ? JamiTheme.mainViewMargin - 10 : 0
sourceComponent: {
if (welcomeInfo.hasDescription) {
if (CurrentAccount.type !== Profile.Type.SIP) {
return component_identifierDescription;
} else {
return component_identifierDescriptionSIP;
}
} else {
return undefined;
}
}
}
Loader {
id: loader_bottomIdentifier
active: CurrentAccount.type !== Profile.Type.SIP
objectName: "loader_bottomIdentifier"
sourceComponent: JamiIdentifier {
backgroundColor: welcomeInfo.idColor
contentColor: contentIdColor
}
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: active ? item.getHeight() : 0
Layout.preferredWidth: active ? contentWidth : 0
}
Binding {
target: loader_bottomIdentifier.item
property: "slimDisplay"
value: false
}
}
}
Component {
id: component_welcomeTitle
Label {
id: welcomeTitle
width: welcomeInfo.contentWidth
height: contentHeight
font.pixelSize: JamiTheme.bigFontSize
wrapMode: Text.WordWrap
text: welcomeInfo.title
color: welcomeInfo.textColor
textFormat: TextEdit.PlainText
horizontalAlignment: Text.AlignHCenter
}
}
Component {
id: component_identifierDescription
Label {
id: identifierDescription
visible: CurrentAccount.type !== Profile.Type.SIP
width: welcomeInfo.contentWidth
height: contentHeight
font.pixelSize: JamiTheme.headerFontSize
wrapMode: Text.WordWrap
text: welcomeInfo.description
lineHeight: 1.25
color: welcomeInfo.textColor
textFormat: TextEdit.PlainText
horizontalAlignment: Text.AlignHCenter
}
}
Component {
id: component_identifierDescriptionSIP
Label {
id: identifierDescriptionSIP
width: welcomeInfo.contentWidth
height: contentHeight
font.pixelSize: JamiTheme.headerFontSize
wrapMode: Text.WordWrap
text: JamiStrings.description
color: welcomeInfo.textColor
textFormat: TextEdit.PlainText
horizontalAlignment: Text.AlignHCenter
}
}
}

View File

@ -0,0 +1,67 @@
/*
* 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 net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
import "../js/keyboardshortcuttablecreation.js" as KeyboardShortcutTableCreation
Item {
id: welcomeLogo
property bool hasCustomLogo: viewNode.hasCustomLogo
property string logoUrl: viewNode.customLogoUrl
//logoSize has to be between 0 and 1
property real logoSize: 1
height: getHeight()
width: getWidth()
function getWidth() {
return JamiTheme.welcomeHalfGridWidth;
}
function getHeight() {
return 120;
}
CachedImage {
id: cachedImgLogo
objectName: "cachedImgLogo"
downloadUrl: logoUrl
defaultImage: JamiResources.jami_logo_icon_svg
visible: welcomeLogo.visible
height: parent.height * logoSize
width: parent.width * logoSize
anchors.centerIn: parent
opacity: visible ? 1 : 0
customLogo: hasCustomLogo
localPath: UtilsAdapter.getCachePath() + "/" + CurrentAccount.id + "/welcomeview/" + UtilsAdapter.base64Encode(downloadUrl) + fileExtension
imageFillMode: Image.PreserveAspectFit
Behavior on opacity {
NumberAnimation {
duration: JamiTheme.shortFadeDuration
}
}
}
}

View File

@ -26,6 +26,7 @@ import "../js/keyboardshortcuttablecreation.js" as KeyboardShortcutTableCreation
ListSelectionView {
id: viewNode
objectName: "WelcomePage"
splitViewStateKey: "Main"
@ -35,8 +36,85 @@ ListSelectionView {
onPresented: LRCInstance.deselectConversation()
leftPaneItem: viewCoordinator.getView("SidePanel")
property variant uiCustomization: CurrentAccount.uiCustomization
onUiCustomizationChanged: {
updateUiFlags();
}
Component.onCompleted: {
updateUiFlags();
}
property bool hasCustomUi: false
property bool hasTitle: true
property bool hasDescription: true
property bool hasCustomTitle: false
property string title: JamiStrings.welcomeToJami
property bool hasCustomDescription: false
property string description: JamiStrings.hereIsIdentifier
property bool hasLogo: true
property bool hasTips: true
//logoSize has to be between 0 and 1
property real logoSize: 1
property bool hasCustomBgImage: false
property string customBgUrl: ""
property bool hasCustomBgColor: false
property string customBgColor: ""
property bool hasCustomLogo: false
property string customLogoUrl: ""
property bool hasWelcomeInfo: true
property bool hasBottomId: false
property bool hasTopId: false
property color tipBoxAndIdColor: JamiTheme.welcomeBlockColor
property color mainBoxColor: "transparent"
property color tipsTextColor: JamiTheme.textColor
property color mainBoxTextColor: JamiTheme.textColor
property color contentTipAndIdColor: JamiTheme.tintedBlue
function updateUiFlags() {
hasCustomUi = Object.keys(uiCustomization).length > 0;
hasTitle = hasCustomUi ? uiCustomization.title !== "" : true;
hasDescription = hasCustomUi ? uiCustomization.description !== "" : true;
title = hasCustomUi && uiCustomization.title !== undefined ? uiCustomization.title : JamiStrings.welcomeToJami;
description = hasCustomUi && uiCustomization.description !== undefined ? uiCustomization.description : JamiStrings.hereIsIdentifier;
hasLogo = hasCustomUi ? uiCustomization.logoUrl !== "" : true;
hasTips = hasCustomUi ? uiCustomization.areTipsEnabled : true;
hasCustomBgImage = (hasCustomUi && uiCustomization.backgroundType === "image");
customBgUrl = hasCustomBgImage ? (CurrentAccount.managerUri + uiCustomization.backgroundColorOrUrl) : "";
hasCustomBgColor = (hasCustomUi && uiCustomization.backgroundType === "color");
customBgColor = hasCustomBgColor ? uiCustomization.backgroundColorOrUrl : "";
hasCustomLogo = (hasCustomUi && hasLogo && uiCustomization.logoUrl !== undefined);
customLogoUrl = hasCustomLogo ? CurrentAccount.managerUri + uiCustomization.logoUrl : "";
hasWelcomeInfo = hasTitle || hasDescription;
hasBottomId = !hasWelcomeInfo && !hasTips && hasLogo;
hasTopId = !hasWelcomeInfo && (!hasLogo || hasTips);
logoSize = (hasCustomUi && uiCustomization.logoSize !== undefined) ? uiCustomization.logoSize / 100 : 1;
tipBoxAndIdColor = (hasCustomUi && uiCustomization.tipBoxAndIdColor !== undefined) ? uiCustomization.tipBoxAndIdColor : JamiTheme.welcomeBlockColor;
mainBoxColor = (hasCustomUi && uiCustomization.mainBoxColor !== undefined) ? uiCustomization.mainBoxColor : "transparent";
tipsTextColor = (hasCustomUi && uiCustomization.tipBoxAndIdColor !== undefined) ? (UtilsAdapter.luma(tipBoxAndIdColor) ? JamiTheme.whiteColor : JamiTheme.blackColor) : JamiTheme.textColor;
mainBoxTextColor = (hasCustomUi && uiCustomization.mainBoxColor !== undefined) ? (UtilsAdapter.luma(mainBoxColor) ? JamiTheme.whiteColor : JamiTheme.blackColor) : JamiTheme.textColor;
contentTipAndIdColor = (hasCustomUi && uiCustomization.tipBoxAndIdColor !== undefined) ? (UtilsAdapter.luma(tipBoxAndIdColor) ? JamiTheme.lightTintedBlue : JamiTheme.darkTintedBlue) : JamiTheme.tintedBlue;
}
rightPaneItem: JamiFlickable {
id: root
anchors.fill: parent
property int thresholdSize: 700
property int thresholdHeight: 570
MouseArea {
anchors.fill: parent
@ -44,179 +122,113 @@ ListSelectionView {
onClicked: root.forceActiveFocus()
}
anchors.fill: parent
contentHeight: Math.max(root.height, welcomePageLayout.implicitHeight)
contentWidth: Math.max(300, root.width)
Item {
Rectangle {
id: bgRect
anchors.fill: parent
color: hasCustomBgColor ? customBgColor : "transparent"
}
CachedImage {
id: cachedImgLogo
downloadUrl: hasCustomBgImage ? customBgUrl : JamiTheme.welcomeBg
visible: !hasCustomBgColor
anchors.fill: parent
opacity: visible ? 1 : 0
localPath: UtilsAdapter.getCachePath() + "/" + CurrentAccount.id + "/welcomeview/" + UtilsAdapter.base64Encode(downloadUrl) + fileExtension
imageFillMode: Image.PreserveAspectCrop
Connections {
target: JamiTheme
function onDarkThemeChanged() {
customBgUrl = hasCustomBgImage ? customBgUrl : JamiTheme.welcomeBg;
tipBoxAndIdColor = (hasCustomUi && uiCustomization.tipBoxAndIdColor !== undefined) ? uiCustomization.tipBoxAndIdColor : JamiTheme.welcomeBlockColor;
tipsTextColor = (hasCustomUi && uiCustomization.tipBoxAndIdColor !== undefined) ? (UtilsAdapter.luma(tipBoxAndIdColor) ? JamiTheme.whiteColor : JamiTheme.blackColor) : JamiTheme.textColor;
mainBoxTextColor = (hasCustomUi && uiCustomization.mainBoxColor !== undefined) ? (UtilsAdapter.luma(mainBoxColor) ? JamiTheme.whiteColor : JamiTheme.blackColor) : JamiTheme.textColor;
contentTipAndIdColor = (hasCustomUi && uiCustomization.tipBoxAndIdColor !== undefined) ? (UtilsAdapter.luma(tipBoxAndIdColor) ? JamiTheme.lightTintedBlue : JamiTheme.darkTintedBlue) : JamiTheme.tintedBlue;
}
}
}
ColumnLayout {
id: welcomePageLayout
width: Math.max(300, root.width)
height: parent.height
Item {
anchors.centerIn: parent
height: childrenRect.height
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
Rectangle {
id: welcomeInfo
ColumnLayout {
anchors.centerIn: parent
radius: 30
color: JamiTheme.rectColor
anchors.topMargin: 25
anchors.horizontalCenter: parent.horizontalCenter
width: identifier.width + 2 * JamiTheme.mainViewMargin + (welcomeLogo.visible ? welcomeLogo.width : 0)
height: childrenRect.height + 10
opacity: 1
Behavior on width {
NumberAnimation {
duration: JamiTheme.shortFadeDuration
Loader {
id: loader_welcomeLogo
objectName: "loader_welcomeLogo"
active: viewNode.hasLogo
sourceComponent: WelcomeLogo {
logoSize: viewNode.logoSize
}
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: active ? item.getWidth() : 0
Layout.preferredHeight: active ? item.getHeight() : 0
Layout.topMargin: 20
}
Label {
id: welcome
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: JamiTheme.mainViewMargin
anchors.leftMargin: JamiTheme.mainViewMargin
width: 300
font.pixelSize: JamiTheme.bigFontSize
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
text: JamiStrings.welcomeToJami
color: JamiTheme.textColor
}
Label {
id: descriptionLabel
visible: CurrentAccount.type === Profile.Type.SIP
anchors.top: welcome.bottom
anchors.left: parent.left
anchors.topMargin: JamiTheme.preferredMarginSize * 2
anchors.leftMargin: JamiTheme.mainViewMargin
width: 300
font.pixelSize: JamiTheme.headerFontSize
wrapMode: Text.WordWrap
text: JamiStrings.description
color: JamiTheme.textColor
}
Label {
id: identifierDescription
visible: CurrentAccount.type !== Profile.Type.SIP
anchors.top: welcome.bottom
anchors.left: parent.left
anchors.topMargin: JamiTheme.preferredMarginSize
anchors.leftMargin: JamiTheme.mainViewMargin
width: 330
font.pixelSize: JamiTheme.headerFontSize
wrapMode: Text.WordWrap
text: JamiStrings.hereIsIdentifier
lineHeight: 1.25
color: JamiTheme.textColor
}
JamiIdentifier {
id: identifier
visible: CurrentAccount.type !== Profile.Type.SIP
anchors.top: identifierDescription.bottom
anchors.left: parent.left
anchors.topMargin: JamiTheme.preferredMarginSize
anchors.rightMargin: JamiTheme.preferredMarginSize
anchors.leftMargin: JamiTheme.mainViewMargin
}
Image {
id: welcomeLogo
visible: root.width > 630
width: 212
height: 244
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: JamiTheme.preferredMarginSize
anchors.topMargin: -20
opacity: visible
source: JamiResources.welcome_illustration_2_svg
Behavior on opacity {
NumberAnimation {
duration: JamiTheme.shortFadeDuration
}
}
}
}
JamiFlickable {
id: tipsFlow
anchors.top: welcomeInfo.bottom
anchors.topMargin: JamiTheme.preferredMarginSize * 2
anchors.horizontalCenter: parent.horizontalCenter
width: welcomeInfo.width
height: flow.height + JamiTheme.preferredMarginSize * 2
clip: true
Flow {
id: flow
spacing: 13
layoutDirection: UtilsAdapter.isRTL ? Qt.RightToLeft : Qt.LeftToRight
Repeater {
id: tipsRepeater
model: TipsModel
Layout.alignment: Qt.AlignCenter
delegate: TipBox {
tipId: TipId
title: Title
description: Description
type: Type
property bool hideTipBox: false
visible: {
if (hideTipBox)
return false;
if (type === "backup") {
return LRCInstance.currentAccountType !== Profile.Type.SIP && CurrentAccount.managerUri.length === 0;
} else if (type === "customize") {
return CurrentAccount.alias.length === 0;
}
return true;
}
onIgnoreClicked: {
hideTipBox = true;
}
}
Loader {
id: loader_welcomeInfo
objectName: "loader_welcomeInfo"
sourceComponent: WelcomeInfo {
backgroundColor: viewNode.mainBoxColor
hasTitle: viewNode.hasTitle
hasDescription: viewNode.hasDescription
title: viewNode.title
description: viewNode.description
idColor: viewNode.tipBoxAndIdColor
textColor: mainBoxTextColor
contentIdColor: viewNode.contentTipAndIdColor
}
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: item.getHeight()
Layout.preferredWidth: 500
}
}
}
Loader {
id: loader_tipsRow
objectName: "loader_tipsRow"
active: viewNode.hasTips && root.height > root.thresholdHeight
sourceComponent: TipsRow {
tipsColor: viewNode.tipBoxAndIdColor
tipsTextColor: viewNode.tipsTextColor
iconColor: viewNode.contentTipAndIdColor
}
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: active ? item.getHeight() : 0
Layout.preferredWidth: {
if (!active) {
return 0;
} else {
if (root.width > root.thresholdSize) {
return JamiTheme.welcomeGridWidth;
} else {
return JamiTheme.welcomeShortGridWidth;
}
}
}
focus: true
}
Item {
id: bottomRow
width: Math.max(300, root.width)
height: aboutJami.height + JamiTheme.preferredMarginSize
anchors.bottom: parent.bottom
Layout.preferredWidth: Math.max(300, root.width)
Layout.preferredHeight: aboutJami.height
Layout.margins: JamiTheme.welcomePageSpacing / 2
Layout.alignment: Qt.AlignHCenter
MaterialButton {
id: aboutJami
@ -224,15 +236,16 @@ ListSelectionView {
TextMetrics {
id: textSize
font.weight: Font.Bold
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.pixelSize: 20
font.capitalization: Font.AllUppercase
text: aboutJami.text
}
tertiary: true
anchors.horizontalCenter: parent.horizontalCenter
preferredWidth: textSize.width + 2 * JamiTheme.buttontextWizzardPadding
preferredWidth: textSize.width
text: JamiStrings.aboutJami
fontSize: 12
onClicked: viewCoordinator.presentDialog(appWindow, "mainview/components/AboutPopUp.qml")
}
@ -262,13 +275,4 @@ ListSelectionView {
}
}
}
CustomBorder {
commonBorder: false
lBorderwidth: 1
rBorderwidth: 0
tBorderwidth: 0
bBorderwidth: 0
borderColor: JamiTheme.tabbarBorderColor
}
}

View File

@ -20,7 +20,7 @@ function presentContactPickerPopup(type, parent) {
var comp = Qt.createComponent(
"../components/ContactPicker.qml")
if (comp.status === Component.Ready) {
var obj = comp.createObject(parent, { type: type })
var obj = comp.createObject(parent, { type: type, parent: parent })
if (obj === null) {
console.log("Error creating object for contact picker")
} else {

View File

@ -25,6 +25,18 @@
#include "md4c-html.h"
namespace {
// A callback function that will be called by the md4c library (`md_html`) to output the HTML.
void
htmlChunkCb(const MD_CHAR* data, MD_SIZE data_size, void* userData)
{
QByteArray* array = static_cast<QByteArray*>(userData);
if (data_size > 0) {
array->append(data, int(data_size));
}
};
} // namespace
MessageParser::MessageParser(PreviewEngine* previewEngine, QObject* parent)
: QObject(parent)
, previewEngine_(previewEngine)
@ -51,9 +63,9 @@ MessageParser::parseMessage(const QString& messageId,
// Now that we have the HTML, we can parse it to get a list of tags and their values.
// We are only interested in the <a> and <pre> tags.
htmlParser_->parseHtmlString(html);
auto tagsMap = htmlParser_->getTags({TidyTag_A, TidyTag_DEL, TidyTag_PRE});
auto tagsMap = htmlParser_->getTagsNodes({TidyTag_A, TidyTag_DEL, TidyTag_PRE});
static QString styleTag("<style>%1</style>");
static const QString styleTag("<style>%1</style>");
QString style;
// Check for any <pre> tags. If there are any, we need to:
@ -89,11 +101,9 @@ MessageParser::parseMessage(const QString& messageId,
// If the user has enabled link previews, then we need to generate the link preview.
if (previewLinks) {
// Get the first link in the message.
auto anchorTag = tagsMap[TidyTag_A].first();
static QRegularExpression hrefRegex("href=\"(.*?)\"");
auto match = hrefRegex.match(anchorTag);
if (match.hasMatch()) {
Q_EMIT previewEngine_->parseLink(messageId, match.captured(1));
auto href = htmlParser_->getNodeAttr(tagsMap[TidyTag_A].first(), TidyAttr_HREF);
if (!href.isEmpty()) {
Q_EMIT previewEngine_->parseLink(messageId, href);
}
}
@ -110,13 +120,13 @@ void
MessageParser::preprocessMarkdown(QString& markdown)
{
// Match all instances of the linefeed character.
static QRegularExpression newlineRegex("\n");
static const QRegularExpression newlineRegex("\\r?\\n");
static const QString newline = " \n";
// Replace all instances of the linefeed character with 2 spaces + a linefeed character
// in order to force a line break in the HTML.
// Note: we should only do this for non-code fenced blocks.
static QRegularExpression codeFenceRe("`{1,3}([\\s\\S]*?)`{1,3}");
static const QRegularExpression codeFenceRe("`{1,3}([\\s\\S]*?)`{1,3}");
auto match = codeFenceRe.globalMatch(markdown);
// If there are no code blocks, then we can just replace all linefeeds with 2 spaces
@ -132,7 +142,7 @@ MessageParser::preprocessMarkdown(QString& markdown)
enum BlockType { Text, Code };
QVector<QPair<BlockType, QString>> codeBlocks;
int start = 0;
qsizetype start = 0;
while (match.hasNext()) {
auto m = match.next();
auto nonCodelength = m.capturedStart() - start;
@ -158,27 +168,16 @@ MessageParser::preprocessMarkdown(QString& markdown)
}
}
// A callback function that will be called by the md4c library (`md_html`) to output the HTML.
static void
htmlChunkCb(const MD_CHAR* data, MD_SIZE data_size, void* userData)
{
QByteArray* array = static_cast<QByteArray*>(userData);
if (data_size > 0) {
array->append(data, int(data_size));
}
};
QString
MessageParser::markdownToHtml(const char* markdown)
{
static auto md_flags = MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_NOINDENTEDCODEBLOCKS
| MD_FLAG_TASKLISTS | MD_FLAG_STRIKETHROUGH | MD_FLAG_UNDERLINE;
size_t data_len = strlen(markdown);
const size_t data_len = strlen(markdown);
if (data_len <= 0) {
return QString();
} else {
QByteArray array;
int result = md_html(markdown, MD_SIZE(data_len), &htmlChunkCb, &array, md_flags, 0);
return result == 0 ? QString::fromUtf8(array) : QString();
}
QByteArray array;
const int result = md_html(markdown, MD_SIZE(data_len), &htmlChunkCb, &array, md_flags, 0);
return result == 0 ? QString::fromUtf8(array) : QString();
}

View File

@ -449,6 +449,10 @@ MessagesAdapter::onNewInteraction(const QString& convUid,
void
MessagesAdapter::onMessageParsed(const QString& messageId, const QString& parsed)
{
if (messageId.isEmpty()) {
Q_EMIT messageParsed(messageId, parsed);
return;
}
const QString& convId = lrcInstance_->get_selectedConvUid();
const QString& accId = lrcInstance_->get_currentAccountId();
auto& conversation = lrcInstance_->getConversationFromConvUid(convId, accId);
@ -764,7 +768,7 @@ MessagesAdapter::startSearch(const QString& text, bool isMedia)
}
int
MessagesAdapter::getMessageIndexFromId(QString& id)
MessagesAdapter::getMessageIndexFromId(const QString& id)
{
const QString& convId = lrcInstance_->get_selectedConvUid();
const auto& conversation = lrcInstance_->getConversationFromConvUid(convId);

View File

@ -78,6 +78,7 @@ Q_SIGNALS:
void moreMessagesLoaded(qint32 loadingRequestId);
void timestampUpdated();
void fileCopied(const QString& dest);
void messageParsed(const QString& msgId, const QString& msg);
protected:
Q_INVOKABLE bool isDocument(const interaction::Type& type);
@ -135,7 +136,7 @@ protected:
Q_INVOKABLE QVariant dataForInteraction(const QString& interactionId,
int role = Qt::DisplayRole) const;
Q_INVOKABLE void startSearch(const QString& text, bool isMedia);
Q_INVOKABLE int getMessageIndexFromId(QString& id);
Q_INVOKABLE int getMessageIndexFromId(const QString& id);
// Run corrsponding js functions, c++ to qml.
void setMessagesImageContent(const QString& path, bool isBased64 = false);

Some files were not shown because too many files have changed in this diff Show More