Compare commits

..

494 Commits

Author SHA1 Message Date
DeepBlueV7.X
1ace482c74
Merge pull request #594 from pcworld/narrow-view-notification
Fix notifications in narrow view when content is hidden
2021-05-29 12:11:26 +00:00
pcworld
010debe3e4 Fix notifications in narrow view when content is hidden
In narrow view, a room can be selected even if the view currently only
shows the room list and the timeline is hidden.
This commit ensures that in this case, notifications are not suppressed.
2021-05-29 02:57:37 +02:00
Weblate
72a2cfea7b Translated using Weblate (German)
Currently translated at 99.1% (487 of 491 strings)

Translated using Weblate (German)

Currently translated at 99.1% (487 of 491 strings)

Translated using Weblate (German)

Currently translated at 99.1% (487 of 491 strings)

Co-authored-by: 123 <fof300f@posteo.net>
Co-authored-by: Konstantin Papesh <konstantin@papesh.at>
Co-authored-by: fnetX (aka fralix) <github@fralix.ovh>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-05-25 17:00:44 -04:00
Weblate
25cf098bb5 Translated using Weblate (Spanish)
Currently translated at 5.9% (29 of 491 strings)

Co-authored-by: LluisE <lluise@tutanota.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/es/
Translation: Nheko/nheko
2021-05-24 18:37:43 -04:00
Joseph Donofry
d094ffafb2
Merge pull request #591 from absorber/feature/context_menu_mnemonics
Added mnemonic letters to context menu.
2021-05-24 17:03:11 -04:00
AppAraat
9d25124810 Added mnemonic letters to context menu.
https://doc.qt.io/qt-5/qshortcut.html#mnemonic
2021-05-24 21:42:05 +02:00
Nicolas Werner
409992686e
Use a few more dbus name in flatpak 2021-05-23 19:39:38 +02:00
DeepBlueV7.X
a43fc91b16
Merge pull request #586 from dgalbraith/chocolatey-install-documentation
Add Chocolatey installation documentation
2021-05-21 16:12:30 +00:00
dgalbraith
e1f3cfd92a Add Chocolatey installation documentation
Updated documentation to include instructions on installing Nheko using
the Chocolatey package manager on Windows.
2021-05-21 16:53:39 +01:00
Weblate
e8c682dca7 Added translation using Weblate (Portuguese (Brazil))
Co-authored-by: zerowhy <zerowhy.server@protonmail.com>
2021-05-21 08:02:47 -04:00
Weblate
c776c6fd32 Translated using Weblate (German)
Currently translated at 97.7% (480 of 491 strings)

Translated using Weblate (German)

Currently translated at 97.7% (480 of 491 strings)

Translated using Weblate (German)

Currently translated at 97.7% (480 of 491 strings)

Translated using Weblate (German)

Currently translated at 97.7% (480 of 491 strings)

Co-authored-by: 123 <fof300f@posteo.net>
Co-authored-by: CryptKid <CryptKiddie@chaospott.de>
Co-authored-by: Mr. X <shop.news@posteo.de>
Co-authored-by: fnetX (aka fralix) <github@fralix.ovh>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-05-19 13:27:38 -04:00
DeepBlueV7.X
fdad10ceab
Merge pull request #581 from Kirillpt/translation/russian
update Russian translation
2021-05-16 17:01:18 +00:00
kirill
9078ab8a98 update russian translation 2021-05-16 19:50:42 +03:00
Weblate
a1ea58dd95 Translated using Weblate (Finnish)
Currently translated at 50.5% (248 of 491 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/fi/
Translation: Nheko/nheko
2021-05-13 16:36:31 -04:00
Weblate
3151c6a8e3 Translated using Weblate (Estonian)
Currently translated at 100.0% (491 of 491 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/et/
Translation: Nheko/nheko
2021-05-13 16:36:31 -04:00
Nicolas Werner
44c5236808
Fix warning on gcc11 2021-05-13 01:31:24 +02:00
Nicolas Werner
d52c2515bc
Fix stray 'a' in @room escaping 2021-05-11 15:10:28 +02:00
Nicolas Werner
0bf611beb2
Update translations 2021-05-09 16:53:58 +02:00
Nicolas Werner
b60d6f4d58
Mark unsent events as uneditable
fixes #574
2021-05-09 13:02:41 +02:00
Nicolas Werner
2df4c532ed
Add TOFU (Trust On First Use) mode to encryption 2021-05-07 17:01:57 +02:00
Nicolas Werner
0d0709ccd3
Show verification status next to messages 2021-05-07 17:01:57 +02:00
Weblate
7333de19da Translated using Weblate (Spanish)
Currently translated at 5.3% (26 of 483 strings)

Co-authored-by: Bruno Javier Blasco Smaranda <brunch875@gmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/es/
Translation: Nheko/nheko
2021-05-05 10:25:15 -04:00
Nicolas Werner
ab0baf5d9e
Only show actions, when you have permissions to do them 2021-05-02 18:06:56 +02:00
Nicolas Werner
1321d9bcca
Fix crash when you have no rooms and open the global user profile 2021-04-30 15:37:18 +02:00
Nicolas Werner
986b561c34
Fix crash when you have no rooms and open the profile as well as not rendering rooms without groups 2021-04-30 15:33:17 +02:00
Nicolas Werner
7431b51d27
Update mtxclient to use new login parameters
fixes #558
2021-04-30 14:46:40 +02:00
DeepBlueV7.X
b160baba4e
Merge pull request #565 from absorber/patch-2
UI/UX improvement: Better naming suggestion.
2021-04-29 22:15:11 +00:00
absorber
6ac10963b0
UI/UX improvement: Better naming suggestion.
Copy address location -> Copy link location
2021-04-29 22:10:14 +00:00
Nicolas Werner
2d678bdcf6
Allow copying messages via right click
fixes #291
2021-04-29 23:38:45 +02:00
Nicolas Werner
9ab1dc253e
Copy address location
fixes #463
2021-04-29 23:09:13 +02:00
Nicolas Werner
6d464381e4
Fix missing license header 2021-04-29 22:52:55 +02:00
Nicolas Werner
f626de0447
Copy link to message
fixes #499
2021-04-29 21:46:49 +02:00
Nicolas Werner
7644b6e442
Actually use qt5.12 in CI 2021-04-29 20:09:12 +02:00
Nicolas Werner
82fa8ab292
Highlight navigated to message 2021-04-29 19:09:16 +02:00
Nicolas Werner
620b6e8838
Fix some encoding issues when translating matrix.to to matrix: 2021-04-29 13:12:09 +02:00
Nicolas Werner
dbf23fafbf
Make pagination logic slightly more robust 2021-04-29 10:23:50 +02:00
Nicolas Werner
32962a5616
Update appdata 2021-04-29 10:22:01 +02:00
Nicolas Werner
76a9240076
Rewrite matrix.to links to matrix uris and handle them the same way 2021-04-28 20:03:52 +02:00
Nicolas Werner
5b6671f063
Add Alt-F to forward messages 2021-04-27 12:09:00 +02:00
Nicolas Werner
788131daf0
Clear appveyor deps 2021-04-27 11:48:44 +02:00
DeepBlueV7.X
1e3f0f3b26
Merge pull request #552 from Jedi18/forward_message_feature
Forward Message
2021-04-27 09:35:47 +00:00
Nicolas Werner
2b253ead9e
Make forward messages a bit more readable 2021-04-27 11:33:46 +02:00
Nicolas Werner
65d85768d0
Clean up design a bit 2021-04-27 11:08:21 +02:00
Weblate
0169193a5e Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/
Translation: Nheko/nheko
2021-04-24 23:30:07 -04:00
Nicolas Werner
8236f6ba72
Merge branch 'forward_message_feature' of https://github.com/Jedi18/nheko into Jedi18-forward_message_feature 2021-04-24 14:35:21 +02:00
Nicolas Werner
72d74ac59f
Fix rendering issues with ) in links 2021-04-24 14:32:24 +02:00
Nicolas Werner
6fd485a74a
Fix completions in plain text mode 2021-04-24 14:32:24 +02:00
Weblate
0619120ab5 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/
Translation: Nheko/nheko
2021-04-23 23:30:07 -04:00
Nicolas Werner
33ad65dc17
Fix typo in appdata.xml 2021-04-23 13:49:29 +02:00
Nicolas Werner
2baa0f9537
Fix missing timestamp in appdata.xml 2021-04-23 12:44:06 +02:00
Nicolas Werner
9f3e066414
Update changelog with release 2021-04-23 12:17:19 +02:00
Nicolas Werner
3dc1c3d784
Update changelog with newest translations 2021-04-23 10:48:36 +02:00
Nicolas Werner
a6b284e12c
Merge branch 'master' of github.com:Nheko-Reborn/nheko into 0.8.2-RC 2021-04-23 10:47:31 +02:00
Weblate
7c206b1b43 Translated using Weblate (French)
Currently translated at 98.1% (474 of 483 strings)

Translated using Weblate (French)

Currently translated at 98.1% (474 of 483 strings)

Co-authored-by: Carl Schwan <carl@carlschwan.eu>
Co-authored-by: Mayeul Cantan <mayeul.cantan@gmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/fr/
Translation: Nheko/nheko
2021-04-22 20:21:47 -04:00
targetakhil
ddb1983c63 fix macos build error 2021-04-22 11:06:19 +05:30
Nicolas Werner
b74c5a5d4f
Update changelog 2021-04-21 22:53:59 +02:00
Nicolas Werner
576cd0f569
Merge branch 'master' into 0.8.2-RC 2021-04-21 22:51:21 +02:00
Weblate
70d6b7f06e Translated using Weblate (Estonian)
Currently translated at 100.0% (483 of 483 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/et/
Translation: Nheko/nheko
2021-04-21 16:50:00 -04:00
DeepBlueV7.X
085d064b98
Merge pull request #556 from MTRNord/MTRNord/windows-installer-cleanup
refactor: Remove windows installer feature that was always broken
2021-04-20 20:40:24 +00:00
MTRNord
5cdb296da2 ci: Upload windows installer also in PR builds 2021-04-20 22:18:45 +02:00
MTRNord
721ffc9b7c refactor: Remove windows installer feature that was always broken 2021-04-20 21:47:06 +02:00
Weblate
874c057ce5 Translated using Weblate (Hungarian)
Currently translated at 100.0% (483 of 483 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-04-20 14:53:40 -04:00
Weblate
fdaf7aab27 Translated using Weblate (German)
Currently translated at 100.0% (483 of 483 strings)

Translated using Weblate (German)

Currently translated at 100.0% (483 of 483 strings)

Translated using Weblate (German)

Currently translated at 100.0% (483 of 483 strings)

Co-authored-by: DeepBlueV7.X <nicolas.werner@hotmail.de>
Co-authored-by: Mr. X <shop.news@posteo.de>
Co-authored-by: fnetX (aka fralix) <github@fralix.ovh>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-04-20 14:04:55 -04:00
Weblate
3fb7f1b310 Translated using Weblate (Hungarian)
Currently translated at 94.6% (457 of 483 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-04-20 14:04:55 -04:00
Weblate
207cf9f911 Translated using Weblate (French)
Currently translated at 93.5% (452 of 483 strings)

Co-authored-by: Carl Schwan <carl@carlschwan.eu>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/fr/
Translation: Nheko/nheko
2021-04-20 14:04:55 -04:00
Nicolas Werner
9699e95688
Merge branch 'master' of github.com:Nheko-Reborn/nheko into 0.8.2-RC 2021-04-20 20:04:40 +02:00
Nicolas Werner
1936749ff5
Fix keys not being reshared to the same devices, that already got them, if the message got lost 2021-04-20 19:52:23 +02:00
Nicolas Werner
20c1ca2aae
Fix a a session with a higher minimum index being able to overwrite an older one 2021-04-20 14:19:07 +02:00
Weblate
4ba4586399 Translated using Weblate (German)
Currently translated at 100.0% (483 of 483 strings)

Translated using Weblate (German)

Currently translated at 100.0% (483 of 483 strings)

Co-authored-by: DeepBlueV7.X <nicolas.werner@hotmail.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fnetX (aka fralix) <github@fralix.ovh>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-04-20 03:42:10 -04:00
Weblate
a4aaa5d868 Translated using Weblate (German)
Currently translated at 99.5% (481 of 483 strings)

Translated using Weblate (German)

Currently translated at 99.5% (481 of 483 strings)

Co-authored-by: DeepBlueV7.X <nicolas.werner@hotmail.de>
Co-authored-by: Konstantin Papesh <konstantin@papesh.at>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-04-20 03:39:02 -04:00
Weblate
7b141ed082 Translated using Weblate (German)
Currently translated at 99.3% (480 of 483 strings)

Translated using Weblate (German)

Currently translated at 99.3% (480 of 483 strings)

Co-authored-by: DeepBlueV7.X <nicolas.werner@hotmail.de>
Co-authored-by: Konstantin Papesh <konstantin@papesh.at>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-04-20 03:37:13 -04:00
Weblate
e256f342ff Translated using Weblate (English)
Currently translated at 100.0% (483 of 483 strings)

Co-authored-by: Joseph Donofry <rubberduckie3554@gmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/en/
Translation: Nheko/nheko
2021-04-20 03:13:48 -04:00
Weblate
28dfd9b02b Added translation using Weblate (Spanish)
Co-authored-by: DeepBlueV7.X <nicolas.werner@hotmail.de>
2021-04-19 18:54:12 -04:00
Nicolas Werner
95a603fd62
Linkify matrix uris 2021-04-19 16:35:06 +02:00
Nicolas Werner
d6109b95fb
Escape html in topics and show line breaks in the settings 2021-04-19 11:20:52 +02:00
Joseph Donofry
a792ea1465
Bump version to 0.8.2 2021-04-18 20:21:30 -04:00
Nicolas Werner
59b6712e28
Update changelog for pending 0.8.2 2021-04-18 23:15:45 +02:00
Joseph Donofry
c41879c7d6
Update translations 2021-04-18 15:58:38 -04:00
Nicolas Werner
c2e625756c
Use one CompletionProxy for everything including EmojiPicker 2021-04-18 21:53:39 +02:00
Nicolas Werner
1b0af04cc8
Prevent warning on empty user requests 2021-04-18 21:53:36 +02:00
Joseph Donofry
a402e85a0f
Fix display name referenced for incorrect event types 2021-04-18 15:32:28 -04:00
targetakhil
5a5aba662e make util strip util functions non-static and move definition to cpp file 2021-04-18 12:03:25 +05:30
DeepBlueV7.X
01ee25cee4
Merge pull request #554 from Jedi18/display_name_wording
Change display name wording
2021-04-18 06:24:11 +00:00
targetakhil
ab83c7c3a8 change display name wording 2021-04-18 11:43:02 +05:30
targetakhil
2dfa40e017 strip reply fallbacks from forwarded message 2021-04-18 11:22:44 +05:30
Nicolas Werner
663c3b0014
Set a title for the room settings 2021-04-17 20:40:31 +02:00
Nicolas Werner
fcda5ace6b
Update translations 2021-04-17 20:14:50 +02:00
targetakhil
06e12a0a16 move detection code to nheko namespace and fix a few other bugs 2021-04-17 22:58:04 +05:30
targetakhil
eb13f7c169 directly upload old file object and reuse old message 2021-04-17 22:28:17 +05:30
Nicolas Werner
eaa91b4e56
Fix cursor positioning on edits
fixes #502
2021-04-15 23:21:50 +02:00
Nicolas Werner
3022178334
Fix paste not overwriting selections 2021-04-15 21:13:53 +02:00
Nicolas Werner
84b2cf08a1
Set Dialog flag on Dialogs
Relates to #538
2021-04-15 21:13:53 +02:00
targetakhil
9934004702 remove unused function and set position to center of timeline view 2021-04-15 22:37:47 +05:30
targetakhil
dff5cfc3ba Added overlay and UI changes 2021-04-15 22:37:47 +05:30
targetakhil
6893e3a8d5 show forward menu item only for relevant events, changes to ui 2021-04-15 22:37:47 +05:30
targetakhil
ce8246238e Fix basic UI for forward completer 2021-04-15 22:37:47 +05:30
targetakhil
603ff33ea6 added basic forwarding 2021-04-15 22:37:41 +05:30
Joseph Donofry
a6c89d1362
Merge branch 'master' of ssh://github.com/Nheko-Reborn/nheko 2021-04-13 17:58:39 -04:00
Joseph Donofry
3a0ad55ecc
Fix emoji picker getting cut off 2021-04-13 17:58:21 -04:00
Nicolas Werner
99314c948e
Fix broken olm channels automatically 2021-04-13 23:43:09 +02:00
Nicolas Werner
8108d98fa7
Store state events with state keys 2021-04-13 14:36:37 +02:00
Nicolas Werner
18e96d5c7d
Fix some TapHandler focus issues 2021-04-11 22:24:50 +02:00
DeepBlueV7.X
9d97f9f705
Merge pull request #547 from LordMZTE/feature/notice
add notice and rainbownotice commands
2021-04-11 20:15:26 +00:00
LordMZTE
2e597263a2 add notice and rainbownotice commands 2021-04-11 21:47:20 +02:00
Nicolas Werner
7d6bd67615
Improve sorting a bit and fix some bugs in edge cases
makes nheko appear at the top, if you search for it as well as TWIM match the twim room
2021-04-09 17:20:07 +02:00
Nicolas Werner
28074794e7
Fix undefined warning 2021-04-09 14:12:37 +02:00
Nicolas Werner
ff449c705c
Fix crash on exit 2021-04-09 01:47:13 +02:00
Nicolas Werner
c8a547630b
Fix a weird edge case with persisted empty event ids
No idea, how that happened and where it came from
2021-04-08 12:56:31 +02:00
Nicolas Werner
795b8fb7dd
Fix small leak in TrayIcon 2021-04-08 12:26:15 +02:00
Nicolas Werner
ec6f0f9296
Don't use direct image response objects anymore 2021-04-05 13:58:00 +02:00
Nicolas Werner
6c71802680
Fix download button 2021-04-04 00:15:37 +02:00
Nicolas Werner
007ee38b04
Fix exif rotation in unencrypted rooms 2021-04-04 00:15:08 +02:00
Nicolas Werner
5f4ab925da
Fix qtlabs platform in windows packages 2021-04-03 15:18:57 +02:00
Nicolas Werner
b67520256c
Fix appveyor 2021-04-03 13:24:09 +02:00
Nicolas Werner
8719d59e22
Fix rainbow replies and add rainbowme 2021-04-01 17:51:30 +02:00
DeepBlueV7.X
6cf3d97ebd
Merge pull request #540 from LorenDB/master
Use qsTr() for strings
2021-03-31 21:13:34 +00:00
Loren Burkholder
877685d66d Use qsTr() for strings 2021-03-31 17:07:07 -04:00
Nicolas Werner
fa68ae9fe9
Tweak rainbow slightly 2021-03-28 20:56:08 +02:00
Nicolas Werner
edaeb3ccde
Fix emojis being split by rainbows 2021-03-28 20:41:08 +02:00
DeepBlueV7.X
326f48d87f
Merge pull request #535 from LordMZTE/feature/rainbow
add /rainbow command
2021-03-28 18:23:50 +00:00
DeepBlueV7.X
c5f4245945
Merge pull request #539 from anjanik012/fix_build
Fix build: refactor variables to avoid same names in a scope
2021-03-28 17:21:10 +00:00
Anjani Kumar
a8f5672715
Lint Code 2021-03-28 22:09:57 +05:30
LordMZTE
4b45c61024 run formatter 2021-03-28 17:37:36 +02:00
DeepBlueV7.X
3317d4582d
Update src/timeline/InputBar.cpp 2021-03-28 14:00:13 +00:00
DeepBlueV7.X
463dd20682
Use QRegularExpression 2021-03-28 13:59:47 +00:00
LordMZTE
6c31bb6ddc fix command parsing 2021-03-28 15:36:46 +02:00
LordMZTE
ff2e7bb989 commands now also work with newline after them 2021-03-28 14:49:34 +02:00
LordMZTE
e7f20eeae0 use QString as buf in rainbowifyer 2021-03-28 14:14:05 +02:00
LordMZTE
4e6150f28e implement requested changes 2021-03-28 14:00:35 +02:00
Anjani Kumar
4201ade5ae
Fix build: refactor variables to avoid same names in a scope 2021-03-28 14:22:47 +05:30
Joseph Donofry
38ddfabee6
Merge branch 'master' of ssh://github.com/Nheko-Reborn/nheko 2021-03-27 19:43:16 -04:00
Joseph Donofry
27d6c0fb92
Add new mtxclient 2021-03-27 19:25:00 -04:00
Nicolas Werner
1a448ae584
Remove delay when focusing quick switcher 2021-03-27 22:01:56 +01:00
LordMZTE
64e29b07e2 remove incorrect include 2021-03-27 17:04:00 +01:00
LordMZTE
39ff68c6e6 use QString to format in rainbow function 2021-03-27 16:06:42 +01:00
LordMZTE
939f00c90d rainbow now works with unicode! 2021-03-27 14:35:06 +01:00
LordMZTE
a898abcecb use qtextboundary finder to rainbowify. (not working for unicode chars yet) 2021-03-27 14:16:40 +01:00
LordMZTE
947b8c0291 fix size type 2021-03-27 12:47:18 +01:00
Nicolas Werner
583fd9b5ab
Fix double click on video message 2021-03-27 01:18:20 +01:00
DeepBlueV7.X
1bd59a3939
Merge pull request #536 from LorenDB/master
Add a part command
2021-03-26 00:38:00 +00:00
Nicolas Werner
ae19dd2bc2
Disable horizontal scrollbar in timeline
fixes #530
2021-03-26 01:31:46 +01:00
Loren Burkholder
32f5e35037 Use correct Qt version 2021-03-25 20:20:13 -04:00
Loren Burkholder
ea2fb7f8df Add part/leave command 2021-03-25 20:19:48 -04:00
LordMZTE
44bd3376ce add /rainbow command 2021-03-26 00:42:46 +01:00
Nicolas Werner
0d4ddadb15
Fix emoji picker appearing in wrong locations 2021-03-23 18:05:43 +01:00
DeepBlueV7.X
e46ddbbb45
Merge pull request #532 from salahmak/esc-cancel-upload
Pressing escape hides PreviewUploadOverlay
2021-03-20 23:15:28 +00:00
salahmak
2108d98c6d Pressing escape hides PreviewUploadOverlay 2021-03-20 21:09:04 +01:00
salahmak
21a649492f Pressing escape hides PreviewUploadOverlay 2021-03-20 11:18:16 +01:00
Nicolas Werner
b31e74d9f6
Reserve memory for rooms in completer 2021-03-19 04:59:28 +01:00
Nicolas Werner
0e60c09b19
Fix room settings opening twice on room title 2021-03-19 04:40:41 +01:00
DeepBlueV7.X
f6de66576c
Merge pull request #475 from LorenDB/htmlFormattedNotifs
Better notifications
2021-03-18 15:46:04 +01:00
DeepBlueV7.X
98b2df43ad
Merge pull request #527 from anjanik012/ignore_event
Prevent EventType::Unsupported events to be saved in statesdb (BugFix)
2021-03-17 22:19:23 +01:00
Nicolas Werner
1408b1a97d
Make CI happy 2021-03-17 22:13:12 +01:00
Anjani Kumar
9b9d784a82
Prevent EventType::Unsupported type events to be saved in db, avoiding exceptions 2021-03-18 02:09:23 +05:30
Nicolas Werner
21562eed75
Fix shadowing 2021-03-17 20:32:12 +01:00
Nicolas Werner
95bbc559fa
Add missing QPointer include 2021-03-17 19:45:02 +01:00
Joseph Donofry
9754b94364
Use old hunter directory 2021-03-17 14:27:24 -04:00
Nicolas Werner
f6d2fa5ec1
Fix licenses 2021-03-17 19:18:46 +01:00
Nicolas Werner
e5d75c814b
Clean up notification code a bit 2021-03-17 19:18:07 +01:00
Nicolas Werner
95026dcc62
Refactor image download code to be reusable 2021-03-17 19:18:07 +01:00
Loren Burkholder
41737ac22c
Simplify image loading 2021-03-17 19:18:07 +01:00
Loren Burkholder
95a26edad2
Don't create a QImage every time 2021-03-17 19:18:07 +01:00
Loren Burkholder
716c598f4a
Simplify macOS checks for a null image 2021-03-17 19:18:07 +01:00
Loren Burkholder
5da6ab0aec
make lint 2021-03-17 19:18:07 +01:00
Loren Burkholder
98b2fee71b
Block notifications until the image has been downloaded 2021-03-17 19:18:07 +01:00
Loren Burkholder
64dd10a6a0
Only try to display images if they exist 2021-03-17 19:18:06 +01:00
Loren Burkholder
82bbdfb929
Use better method of resizing images 2021-03-17 19:18:06 +01:00
Loren Burkholder
fda6d7629a
Switch readImage to take a reference instead of a pointer
There was nowhere that an actual pointer was passed, and I wanted to do references for something else.
2021-03-17 19:18:06 +01:00
Loren Burkholder
8b33b1f08b
Simplify regex 2021-03-17 19:18:06 +01:00
Loren Burkholder
3748d7853e
Simplify formatting on Windows 2021-03-17 19:18:06 +01:00
Loren Burkholder
2192e8bea8
Better handle encrypted notifications 2021-03-17 19:18:06 +01:00
Loren Burkholder
9168c2c785
Remove unnecessary header 2021-03-17 19:18:06 +01:00
Nicolas Werner
f578272a0d
Rewrite notification posting logic
This does away with the nice abstraction layers in order to easily get the best-looking notifications for each platform.
2021-03-17 19:17:57 +01:00
Loren Burkholder
37acdad928
Add regex to remove replies in notifications 2021-03-17 19:17:15 +01:00
Loren Burkholder
c693d54598
Fix when "replied" is displayed
I accidentally put it in backwards.
2021-03-17 19:17:15 +01:00
Loren Burkholder
df998ef671
Get event text in event parser function 2021-03-17 19:17:15 +01:00
Loren Burkholder
b57b76d948
Add "replied" marker to regular reply messages 2021-03-17 19:17:15 +01:00
Loren Burkholder
39576fea96
Create function for processing whether a message is a reply 2021-03-17 19:17:15 +01:00
Loren Burkholder
d8fb4d9292
Simplify message body construction 2021-03-17 19:17:14 +01:00
Loren Burkholder
4a86e14d04
Simplify determination of whether markup is supported
This should also result in a speed increase (however slight), since the capabilities are now sorted through only once.
2021-03-17 19:17:14 +01:00
Loren Burkholder
4150d75be7
Only HTML-format the body if it should be formatted 2021-03-17 19:17:14 +01:00
Loren Burkholder
c38c6fe49e
Format notifications according to the FreeDesktop specification 2021-03-17 19:17:14 +01:00
Loren Burkholder
dcd9b80dde
Fix Linux HTML notifications 2021-03-17 19:17:14 +01:00
Loren Burkholder
b05657d51a
Fix colon spacing 2021-03-17 19:17:14 +01:00
Loren Burkholder
01bbec88dd
Don't run markdownToHtml on messages 2021-03-17 19:17:14 +01:00
Loren Burkholder
e630504863
Disable HTML on macOS 2021-03-17 19:17:13 +01:00
Loren Burkholder
648844089c
Move data parsing into a dedicated function
Actually posting the notification is now the responsibility of a private function
2021-03-17 19:17:13 +01:00
Loren Burkholder
09303ca49f
make lint 2021-03-17 19:17:13 +01:00
Loren Burkholder
ae7468a716
Use the class D-Bus member 2021-03-17 19:17:13 +01:00
Loren Burkholder
8d3e463fa6
Use plaintext for Windows notifications 2021-03-17 19:17:13 +01:00
Loren Burkholder
3dcbac8875
Only pass formatted text if it is supported (Linux) 2021-03-17 19:17:13 +01:00
Loren Burkholder
c74e68c945
Parse markdown overrides during replies
I apparently missed this when I originally added the overrides.
2021-03-17 19:17:13 +01:00
Loren Burkholder
029ae18a07
Format markdown as HTML in notifications 2021-03-17 19:17:13 +01:00
Joseph Donofry
dd1ed7e3fb
Test a shorter PATH for appveyor 2021-03-17 01:35:36 -04:00
Nicolas Werner
cc3d32c65e
Move check for duplicate /sync responses to the same thread 2021-03-16 21:01:14 +01:00
Nicolas Werner
0dc40e50f8
Convert flatpak file to yaml to be able to add comments 2021-03-15 21:15:53 +01:00
Nicolas Werner
2ee31aa09e
Use device=all in flatpak for webcam support
fixes #517
2021-03-15 21:03:39 +01:00
Nicolas Werner
8ca3a8b607
Don't send markdown links in body
fixes #422
2021-03-15 20:59:18 +01:00
Nicolas Werner
86766b739d
Store all state events 2021-03-15 17:11:02 +01:00
DeepBlueV7.X
6548b84e29
Merge pull request #523 from Jedi18/master
Fix windows video bug
2021-03-15 15:51:10 +00:00
Nicolas Werner
569ea5b5f4
Rotate session keys properly 2021-03-15 16:24:01 +01:00
DeepBlueV7.X
61c5dffffd
Merge pull request #525 from salahmak/image-upload-dialog
Pressing return on image upload dialog sends the message
2021-03-15 11:48:55 +00:00
salahmak
aa0223c041 Pressing return on image upload dialog sends the message 2021-03-15 12:42:17 +01:00
targetakhil
a4b7966d21 prepend file:// for linux and macos 2021-03-15 09:03:06 +05:30
DeepBlueV7.X
dcaebea0b0
Merge pull request #524 from Nheko-Reborn/native-menus
Use native menus
2021-03-14 23:35:36 +00:00
Nicolas Werner
e438fc1068
Shorten hunter dir path 2021-03-14 23:23:37 +01:00
Nicolas Werner
e490ef953f
Use native menus 2021-03-14 22:22:52 +01:00
targetakhil
5614f70576 Add faq section 2021-03-14 23:32:24 +05:30
targetakhil
02e388e542 fix windows video bug 2021-03-14 23:16:41 +05:30
Nicolas Werner
47a7adf823
Use readonly properties where possible 2021-03-14 16:24:04 +01:00
Nicolas Werner
9b8e6c7f5c
Remove some more allocations 2021-03-14 15:34:18 +01:00
Nicolas Werner
98e0b95635
Reduce allocations when escaping emoji 2021-03-14 14:04:30 +01:00
DeepBlueV7.X
c47ae99805
Merge pull request #493 from Jedi18/quickswitcher_qml
Port QuickSwitcher to QML
2021-03-14 01:54:09 +00:00
Nicolas Werner
eb9603f4c0
Fix room completions not showing label avatars
When no image is set for a room, the room didn't have the first
character in the avatar, when opening any completer, that showed it.
2021-03-14 02:50:44 +01:00
Nicolas Werner
05c636a8d4
Fix qml license headers again 2021-03-14 02:45:20 +01:00
Nicolas Werner
8ebb55623e
Remove background of quick switcher popup 2021-03-14 02:42:41 +01:00
Nicolas Werner
1961312b15
Improve sorting and sizing of completions a bit 2021-03-14 01:24:26 +01:00
Nicolas Werner
b82c11bd79
Fix text corruption from unrounded pixel height 2021-03-13 23:52:46 +01:00
Nicolas Werner
7a356f3832
Merge branch 'quickswitcher_qml' of git://github.com/Jedi18/nheko into Jedi18-quickswitcher_qml 2021-03-13 23:45:05 +01:00
DeepBlueV7.X
df797175ca
Merge pull request #520 from Nheko-Reborn/openssl-fixes
Disable bundled OpenSSL by default, even with hunter
2021-03-13 22:42:42 +00:00
Nicolas Werner
ce547357b8
Disable bundled OpenSSL by default, even with hunter 2021-03-13 22:42:44 +01:00
Nicolas Werner
6ad4065de4
Fix a few db access crashes 2021-03-12 17:14:18 +01:00
Nicolas Werner
2a70847c80
Bump mtxclient version 2021-03-12 17:14:18 +01:00
Nicolas Werner
27fe0a45b6
Disable room pings in replies 2021-03-12 17:14:17 +01:00
Weblate
e78be31429 Translated using Weblate (German)
Currently translated at 93.0% (426 of 458 strings)

Co-authored-by: Konstantin Papesh <konstantin@papesh.at>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-03-11 22:21:30 -05:00
Weblate
e84d3ffb85 Translated using Weblate (English)
Currently translated at 100.0% (458 of 458 strings)

Co-authored-by: Emilie <em@nao.sh>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/en/
Translation: Nheko/nheko
2021-03-11 22:21:29 -05:00
Weblate
5ffd81710a Translated using Weblate (Estonian)
Currently translated at 100.0% (458 of 458 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/et/
Translation: Nheko/nheko
2021-03-08 22:30:05 -05:00
Nicolas Werner
31cc727594
Fix disable certificate validation on login page 2021-03-08 13:44:38 +01:00
DeepBlueV7.X
dea7680604
Merge pull request #510 from Jedi18/master
Add matrix uri handler registry values in windows installer
2021-03-07 17:24:55 +00:00
Nicolas Werner
3da9c45df2
Fix popup opening at wrong place
fixes #512
2021-03-07 18:23:50 +01:00
Nicolas Werner
c03f716e8b
Fix conflict between qmlformat and license check 2021-03-07 05:57:56 +01:00
Nicolas Werner
bb6ff8cec8
Show mxid on hovering username
fixes #507
2021-03-07 05:53:54 +01:00
Nicolas Werner
d5e578d0e4
Fix missing include for FetchContent
fixes #511
2021-03-07 02:18:41 +01:00
Nicolas Werner
47e97d490c
Add config option to disable tls validation 2021-03-06 20:52:08 +01:00
Jedi18
8870455f9d change allowed mistakes, fix minor style issues, remove old completer function from inputbar 2021-03-07 00:18:24 +05:30
Nicolas Werner
973ec13ad8
Add linebreaks to fingerprint 2021-03-06 19:36:18 +01:00
DeepBlueV7.X
57a6edadcb
Merge pull request #506 from Nheko-Reborn/timeline-buttons
Use overlay buttons for message actions
2021-03-06 13:47:37 +00:00
DeepBlueV7.X
22071f4ff7
Merge pull request #509 from trilene/master
Bump mtxclient
2021-03-06 03:27:22 +00:00
trilene
52b38abd08 Bump mtxclient 2021-03-05 21:24:00 -05:00
trilene
3846adfecc Bump mtxclient 2021-03-05 20:08:41 -05:00
Nicolas Werner
e5cff64460
Use short format for time 2021-03-05 17:44:49 +01:00
Nicolas Werner
2685eec6c7
Mark message currently being edited 2021-03-05 17:22:47 +01:00
Nicolas Werner
e1c96569c1
Fix janky hoverhandling for text messages
Add a 1px border to get more hover enter events...
2021-03-05 16:47:20 +01:00
Nicolas Werner
02e459b4e6
Fix unused variable 2021-03-05 15:27:46 +01:00
Nicolas Werner
1be42045ee
fix linting 2021-03-05 15:26:07 +01:00
Nicolas Werner
a305c2689c
Fix cplusplus macro on windows 2021-03-05 15:23:48 +01:00
Nicolas Werner
13cdbd5227
Update translations 2021-03-05 15:04:47 +01:00
Nicolas Werner
f4164cc799
Fix a few join confirmations too many 2021-03-05 14:59:59 +01:00
Nicolas Werner
626d8bf151
Remove tweeny 2021-03-05 14:04:30 +01:00
DeepBlueV7.X
56c44d0454
Merge pull request #505 from Nheko-Reborn/license-headers
License headers
2021-03-05 01:04:36 +00:00
Joseph Donofry
4b12b53133
Fix linting 2021-03-04 19:11:08 -05:00
Nicolas Werner
a6f0d2ea7d
Update license headers 2021-03-05 01:04:07 +01:00
Joseph Donofry
87bf761dc2
Properly format matrix errors in spdlog statements 2021-03-04 18:56:25 -05:00
Nicolas Werner
1142fe2663
Use overlay buttons for message actions 2021-03-04 22:59:10 +01:00
Weblate
387ba1a1e6 Translated using Weblate (French)
Currently translated at 97.8% (447 of 457 strings)

Co-authored-by: Mayeul Cantan <mayeul.cantan@gmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/fr/
Translation: Nheko/nheko
2021-03-03 21:21:28 -05:00
Nicolas Werner
e3803ceb9a
Avoid some duplicate property queries 2021-03-03 21:34:24 +01:00
Nicolas Werner
3b4f8f2016
Fix avatar fallback in rooms 2021-03-03 17:54:00 +01:00
Nicolas Werner
67ab204050
Fix reply relating to the wrong id of edited event 2021-03-03 17:42:41 +01:00
DeepBlueV7.X
d6f80e9275
Merge pull request #504 from Nheko-Reborn/new-lmdbxx
Update to new lmdbxx version
2021-03-03 04:38:59 +00:00
Nicolas Werner
c2898623dd
Fix the include path for lmdb from hunter 2021-03-03 04:26:55 +01:00
Nicolas Werner
0704b3cc84
Download single file for lmdb++.hpp 2021-03-03 02:50:00 +01:00
Nicolas Werner
d28a620f42
Add comment to mark db keys as such 2021-03-03 00:08:33 +01:00
Nicolas Werner
1b0abe97f9
Update to new lmdbxx version 2021-03-03 00:01:17 +01:00
Jedi18
fe6d990ad3 add matrix uri handler registry values in installer 2021-03-03 00:02:21 +05:30
Weblate
aa04c233bd Translated using Weblate (German)
Currently translated at 96.7% (442 of 457 strings)

Co-authored-by: CryptKid <CryptKiddie@chaospott.de>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/de/
Translation: Nheko/nheko
2021-03-02 03:21:24 -05:00
Nicolas Werner
c710b4257c
Fix indentation 2021-03-01 02:20:13 +01:00
Nicolas Werner
40de75c40b
Use one after_build in appveyor 2021-03-01 02:19:07 +01:00
Nicolas Werner
818fc51059
Fix appveyor upload 2021-03-01 02:17:38 +01:00
Nicolas Werner
a0fad2513e
Don't ask to join joined rooms when clicking matrix uri 2021-03-01 01:46:22 +01:00
Weblate
a651643f6e Translated using Weblate (French)
Currently translated at 92.3% (422 of 457 strings)

Translated using Weblate (French)

Currently translated at 92.3% (422 of 457 strings)

Co-authored-by: Mayeul Cantan <mayeul.cantan@gmail.com>
Co-authored-by: Nicolas Guichard <nicolas@guichard.eu>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/fr/
Translation: Nheko/nheko
2021-02-26 13:00:40 -05:00
Weblate
d405ce7878 Translated using Weblate (Swedish)
Currently translated at 95.8% (438 of 457 strings)

Co-authored-by: Emilie <em@nao.sh>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/sv/
Translation: Nheko/nheko
2021-02-26 13:00:40 -05:00
Jedi18
1f8a3ae1e8 changed bottomToTop to true and renamed textWidth to textHeight 2021-02-26 11:39:51 +05:30
Nicolas Werner
b55e6fbae8
Use toggles for screen share settings 2021-02-25 20:45:59 +01:00
DeepBlueV7.X
0bf3f4634d
Merge pull request #484 from trilene/screenshare-x11
Support screen sharing on X11
2021-02-25 19:16:18 +00:00
trilene
099207b88c Restore voice/video calls 2021-02-25 13:44:08 -05:00
trilene
6baa775ec8 add_feature_info for screen sharing window selection 2021-02-25 13:27:22 -05:00
trilene
12e40a13cb Add missing translation mark 2021-02-25 12:44:09 -05:00
trilene
55fb00c67b Merge remote-tracking branch 'upstream/master' into screenshare-x11 2021-02-25 12:10:12 -05:00
trilene
402bd565cb Add screen sharing window preview 2021-02-25 12:00:55 -05:00
Nicolas Werner
1f373479b8
Fix unaligned reads 2021-02-25 15:15:59 +01:00
Nicolas Werner
c62f328590
Merge branch 'master' of github.com:Nheko-Reborn/nheko 2021-02-25 14:55:02 +01:00
Nicolas Werner
8846a2a013
Fix potential issue with modifiers and edit shortcuts 2021-02-25 14:54:50 +01:00
DeepBlueV7.X
4c71ca8110
Merge pull request #494 from Jedi18/minor_issue_fixes
Fix registration bug and navigate to created room
2021-02-25 12:54:08 +00:00
Jedi18
4a5b9d014a change mtxclient url, fix login page assert failure and dendrite registration bug 2021-02-25 18:10:06 +05:30
Jedi18
043737c8cb navigate to newly created rooms 2021-02-25 10:29:30 +05:30
Nicolas Werner
345dc1e61f
Fix text input restoring after edits 2021-02-25 00:50:17 +01:00
Nicolas Werner
f6b5b24d64
Allow editing via up and down arrows 2021-02-24 23:51:05 +01:00
trilene
efe240d609 Allow choice of single window when sharing screen 2021-02-24 17:07:01 -05:00
Jedi18
68c999e5f4 remove old quick switcher 2021-02-24 19:50:36 +05:30
Jedi18
0ce7f78446 added margins and ui changes for quickswitcher and completer 2021-02-24 19:32:13 +05:30
Jedi18
096f37df1f fix row content centering 2021-02-24 13:38:01 +05:30
Nicolas Werner
29a71741f4
Ensure we ask for confirmation when clicking on a matrix uri 2021-02-24 01:37:26 +01:00
Nicolas Werner
db78cec626
Add missing dependency to appimage 2021-02-24 00:54:17 +01:00
Nicolas Werner
6800be2483
Copy upload steps from flathub 2021-02-24 00:24:45 +01:00
Nicolas Werner
be85c51b91
Use flatpakref from repo 2021-02-24 00:07:46 +01:00
Nicolas Werner
b9f72c2098
Update flat-manager-client 2021-02-23 23:13:13 +01:00
Nicolas Werner
2787103903
commit and publish flatpak in one command 2021-02-23 22:56:43 +01:00
Nicolas Werner
150281b4de
Add nightly flatpak repo 2021-02-23 21:58:10 +01:00
Nicolas Werner
8ca819525a
Remove unused link 2021-02-23 21:22:58 +01:00
Nicolas Werner
3e80f55f4e
Make flatpak ref autodownload 2021-02-23 21:18:02 +01:00
Nicolas Werner
28fd47bf8d
Add flatpak nightly repository 2021-02-23 21:09:09 +01:00
Jedi18
ebb35e5cb1 fix matrixtextfield color and quickswitcher font size 2021-02-23 22:07:45 +05:30
Jedi18
97c2505619 add matrix text field 2021-02-23 21:36:21 +05:30
Nicolas Werner
865344c7aa
Fix unused capture 2021-02-23 12:42:57 +01:00
Nicolas Werner
7560972cac
Fix qml formatting 2021-02-23 05:24:34 +01:00
DeepBlueV7.X
736ba5e2e8
Merge pull request #488 from Nheko-Reborn/scroll-view-thingy
Use Scrollview again
2021-02-22 22:14:31 +00:00
Nicolas Werner
78ecffb45b Use scrollview again for input 2021-02-22 23:13:38 +01:00
Nicolas Werner
af9b66dd3e Linkify topic in room settings and use non-deprecated MessageDialog 2021-02-22 21:37:32 +01:00
Jedi18
ee232c5c60 fix timeline focus 2021-02-23 00:46:40 +05:30
Jedi18
b1dec6f6ac enter key now works, fix room highlighting and add overlay 2021-02-23 00:18:31 +05:30
Jedi18
3f4ad1dd8b selecting room in quickswitcher now works, added completionSelected signal 2021-02-22 23:08:42 +05:30
trilene
70c77cdc44 Display screen sharing content locally 2021-02-21 16:30:10 -05:00
Jedi18
0922a8e4c7 add room alias delegate, fix some quickswitcher ui problems 2021-02-22 00:01:50 +05:30
Jedi18
32d419d14f add quick switcher qml file and moved completerFor from inputbar to timeline view class 2021-02-21 23:10:21 +05:30
trilene
e8e88e7d79 Refine X11 test 2021-02-20 17:33:04 -05:00
trilene
c461c0aac0 Require GStreamer 1.18 for voip support 2021-02-20 17:14:22 -05:00
trilene
8ccd2abc6a Screen sharing (X11): support picture-in-picture 2021-02-20 11:26:53 -05:00
Nicolas Werner
8351cc4180 Fix miscalculation of padding in timeline 2021-02-20 02:53:14 +01:00
Nicolas Werner
264a85b9e4 Avoid some copies when sorting the room list 2021-02-20 02:38:41 +01:00
Nicolas Werner
ebd12a6f33 Fix login with SSO and Password supported 2021-02-19 15:48:43 +01:00
DeepBlueV7.X
99efe2f06b
Merge pull request #479 from Jedi18/add_rooms_model_completer
Add rooms completion model
2021-02-19 14:03:42 +00:00
Jedi18
3ea0e79a36 check for empty alias and percent encoding for alias in url 2021-02-19 17:04:31 +05:30
DeepBlueV7.X
744feabeca
Merge pull request #474 from Jedi18/room_settings_qml
Shifted Room Settings Dialog to QML
2021-02-19 08:43:03 +00:00
trilene
3b26cf4ba3 Screen sharing (X11): add hide mouse cursor option 2021-02-18 16:53:30 -05:00
trilene
8df10eeeca Support desktop screen sharing on X11 2021-02-18 15:55:29 -05:00
Nicolas Werner
9f7dc5488e Adapt to changes in MSC2312 2021-02-17 23:45:41 +01:00
Nicolas Werner
b8c6c716be Make inline images work a bit better 2021-02-17 22:14:19 +01:00
Nicolas Werner
c9393fe3f6 Fix crash from logging unset indices (leftover after debugging) 2021-02-17 18:47:59 +01:00
Jedi18
0b6c82dfff added bool to choose between showing only rooms with aliases and all of the rooms 2021-02-17 19:58:41 +05:30
Jedi18
8aadde7885 add matrix link for completed item 2021-02-17 19:26:19 +05:30
DeepBlueV7.X
7ff7e33d4a
Merge pull request #482 from eltociear/patch-1
GitHub format
2021-02-17 02:55:58 +00:00
Jedi18
f76f7b7f8a fixed roomsettings spacing and toggle button right align bug 2021-02-16 22:22:55 +05:30
Ikko Ashimine
a1cd55917b
GitHub format
Github -> GitHub
2021-02-16 23:38:56 +09:00
Jedi18
8c4f0a070e change togglebutton size, set textarea color to colors.text 2021-02-16 11:52:03 +05:30
Weblate
0d971f543b Translated using Weblate (Hungarian)
Currently translated at 100.0% (457 of 457 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-02-15 22:30:04 -05:00
DeepBlueV7.X
cae5531c4b
Merge pull request #476 from Jedi18/master
Fix room leaving related bugs and few minor bugs
2021-02-15 21:56:07 +01:00
Jedi18
96028f00a5 trim invite input text 2021-02-16 01:00:32 +05:30
Jedi18
19dbbb2c6c add rooms model, add room delegate for completer 2021-02-16 00:50:28 +05:30
Jedi18
f35e826485 Fix room list updating on adding to hidden tag or removing tag 2021-02-14 19:57:29 +05:30
Jedi18
a2dab31fd6 Fix two room leaving related bugs and add invite user on clicking invite 2021-02-14 13:01:16 +05:30
Jedi18
b5e351ab02 Replace rowlayouts with gridlayout and fix room settings initializer list 2021-02-14 11:26:10 +05:30
Akhil Nair
8400540428
Merge branch 'master' into room_settings_qml 2021-02-14 00:04:27 -05:00
Nicolas Werner
734fb7e286 Add double tap to reply feature
Does not always work, since Text steals focus...

relates to #414
2021-02-14 01:56:46 +01:00
Nicolas Werner
d43607d01c Fix hover handling in the timeline 2021-02-14 01:28:28 +01:00
Nicolas Werner
0d61f4bff1 Improve scroll to message a bit by using a ScrollView 2021-02-13 23:53:30 +01:00
DeepBlueV7.X
ca237f36b9
Merge pull request #471 from LorenDB/emoteNotif
Display notifications for emote messages properly
2021-02-13 23:19:26 +01:00
Loren Burkholder
8c62df1bab Include notifications header instead of responses header 2021-02-13 13:40:30 -05:00
Loren Burkholder
567b2d05ef Move notification parsing into postNotification 2021-02-13 13:40:26 -05:00
Jedi18
1a406f79e6 replaced with togglebutton using qtquickcontrols2 2021-02-13 23:59:42 +05:30
Loren Burkholder
9f9c499cb2 Fix typo 2021-02-13 13:01:04 -05:00
Loren Burkholder
299c486a2b Display notifications for emote messages properly 2021-02-13 13:01:04 -05:00
Jedi18
4996ae27a0 added togglebutton styling 2021-02-13 21:49:21 +05:30
Jedi18
6540352123 fix roomsetting layout 2021-02-13 20:46:40 +05:30
Jedi18
f3596aed55 added room topic 2021-02-13 19:08:52 +05:30
Nicolas Werner
7ddcab3902 Mark messages as read, when Nheko gets focused
fixes #235
2021-02-13 01:41:09 +01:00
Nicolas Werner
e2fc676c77 Revert keeping whitespace in html, it breaks lists 2021-02-12 18:22:41 +01:00
Nicolas Werner
3c91b5b47b Fix crash when editing an edited message pointing to itself 2021-02-12 16:11:11 +01:00
Jedi18
35aa0126ac added changing of name through edit modal, removed old roomsettings 2021-02-12 12:48:12 +05:30
Weblate
192c3b7a77 Translated using Weblate (Hungarian)
Currently translated at 94.5% (432 of 457 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-02-11 15:44:02 -05:00
Weblate
cc1f9a079b Translated using Weblate (Finnish)
Currently translated at 43.9% (201 of 457 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/fi/
Translation: Nheko/nheko
2021-02-11 14:21:16 -05:00
Jedi18
f044e2d2a1 fix avatar update on timeline sync 2021-02-11 23:50:45 +05:30
Jedi18
a7d7d18e92 shifted room avatar changing 2021-02-11 23:39:11 +05:30
Jedi18
473b14ed0f added roomversion, roomid etc 2021-02-11 21:23:33 +05:30
Jedi18
7401bd13b2 added notifications and encryption for the new roomsettings 2021-02-11 19:54:09 +05:30
Weblate
6ff8db1799 Translated using Weblate (Estonian)
Currently translated at 100.0% (457 of 457 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/et/
Translation: Nheko/nheko
2021-02-10 16:37:10 -05:00
Nicolas Werner
0db4d71ec2 Prevent edits of unsent messages 2021-02-10 21:13:57 +01:00
Nicolas Werner
2a5e20dc6f Fix cancel edit order on Escape key 2021-02-10 18:00:52 +01:00
Jedi18
b70f37194f ui almost looks the same, midway between transition from old room settings to new room settings 2021-02-10 21:22:42 +05:30
Nicolas Werner
6c65136101 Update translations 2021-02-10 15:31:33 +01:00
Nicolas Werner
2606568376 Fix messages sometimes not being rendered, when they are too large 2021-02-10 15:24:00 +01:00
Nicolas Werner
6a2e8a6952 Try to avoid QTBUG-89568 2021-02-10 14:38:41 +01:00
Nicolas Werner
a62276c289 Fix UI allowing edits of foreign messages in some cases 2021-02-10 14:32:16 +01:00
DeepBlueV7.X
4a5b5f992d
Merge pull request #420 from Nheko-Reborn/render-edits
Switch to new relations format and show edits
2021-02-10 14:12:49 +01:00
Nicolas Werner
29c89b1b9e Abort -> Cancel 2021-02-10 14:11:55 +01:00
Nicolas Werner
6d678a108f Use fully read marker and fix stuck read marker with edits 2021-02-10 02:37:47 +01:00
Nicolas Werner
bdb6e6b79e Fix stuck notifications because of edits
Does not fix the read status yet, for that we need to compare read
receipts for all events after the last visible event.
2021-02-10 01:03:20 +01:00
Nicolas Werner
6e2ae1d812 Add edit shortcuts and fix some focus stuff 2021-02-09 20:22:53 +01:00
Nicolas Werner
9b7d33e847 Implement message editing
The UI still looks ugly, but I have no good idea atm.

fixes #134
2021-02-09 20:22:53 +01:00
Nicolas Werner
00fd4eecec Display edits correctly 2021-02-09 20:22:49 +01:00
Nicolas Werner
faeaf9dc6b Fix edited replies 2021-02-09 20:22:02 +01:00
Nicolas Werner
d6504812c7 Render edits 2021-02-09 20:22:02 +01:00
Nicolas Werner
2e77a1554f Switch to new relations format 2021-02-09 20:22:02 +01:00
Nicolas Werner
463cee7146 Fix wrong font used in emoji escape 2021-02-09 20:21:01 +01:00
DeepBlueV7.X
6aabe9d5f7
Merge pull request #468 from tverrbjelke/missing_dep_for_debian
Fixes issue 462
2021-02-09 19:48:11 +01:00
Jedi18
37679ac57e added room settings qml 2021-02-09 23:11:39 +05:30
Nicolas Werner
8d95532b28 Fix linting 2021-02-09 17:00:06 +01:00
Nicolas Werner
0285bf5e4e Remove unused variables 2021-02-09 16:31:33 +01:00
Nicolas Werner
50f994bd23 Clean up config names a bit 2021-02-09 16:26:38 +01:00
DeepBlueV7.X
23a9306383
Merge pull request #459 from Jedi18/minor_fixes
Fix emoji related issues
2021-02-09 16:25:37 +01:00
DeepBlueV7.X
3797f585c2
Merge pull request #450 from kallisti5/haiku-fixes
Haiku fixes
2021-02-09 15:44:20 +01:00
DeepBlueV7.X
cb704006a5
Merge pull request #465 from trilene/call-devices
Add Duplex call devices
2021-02-09 15:43:17 +01:00
tverrbjelke
2fcc9c2fbb Fixes issue 463
Installation description for debian (buster) misses package qtdeclarative5-dev

	modified:   README.md
2021-02-09 13:21:42 +01:00
trilene
974c336c5e make lint 2021-02-07 13:58:32 -05:00
trilene
8d68534456 Add Duplex call devices 2021-02-07 13:54:18 -05:00
Jedi18
04b920fbee linting fix 2021-02-07 22:18:04 +05:30
trilene
f1bc3ba587 Move call device handling out of WebRTCSession 2021-02-07 11:47:47 -05:00
Jedi18
c2a56fc233 emoji default translation fix 2021-02-07 22:15:06 +05:30
Nicolas Werner
375e20462b Native rendering breaks kerning 2021-02-07 02:01:44 +01:00
Jedi18
8d195a4d11 translation fix for default text in emoji combo 2021-02-06 10:24:41 +05:30
Weblate
87122ffe77 Translated using Weblate (Hungarian)
Currently translated at 91.8% (406 of 442 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-02-05 22:30:06 -05:00
DeepBlueV7.X
49ce7701d3
Merge pull request #455 from LorenDB/fixLogoutIssues
Fix issues with logout
2021-02-05 22:44:31 +01:00
Jedi18
4aefac08a4 focus message input on adding emoji/reacting to a message 2021-02-05 22:42:08 +05:30
Jedi18
f02342fe22 close emoji autocompleter if space typed after : issue #433 and adds default option for emoji font family settings 2021-02-05 21:52:49 +05:30
Loren Burkholder
343c9c8116 Don't attempt to begin group "" 2021-02-04 19:01:48 -05:00
Loren Burkholder
777b9bf20d Set profile to "" if it's the default for compatibility 2021-02-04 18:44:36 -05:00
Loren Burkholder
46e15218d4 Use UserSettings where possible 2021-02-04 18:44:36 -05:00
Loren Burkholder
eae09f8f14 Fix bug on logout of non-default profile 2021-02-04 18:44:36 -05:00
Nicolas Werner
7874d61c33 Fix scheme handler not passing arguments 2021-02-04 01:02:38 +01:00
Alexander von Gluck IV
e8ff6c9486 notifications/mananger: Follow Linux code paths on Haiku as well 2021-02-03 14:40:14 -06:00
Alexander von Gluck IV
cf00abc03e cmake: Don't build with Werror on Haiku 2021-02-03 14:39:49 -06:00
DeepBlueV7.X
e0207ff337
Merge pull request #445 from Jedi18/avatar_username_feature
Change user avatar (global and room avatar)
2021-02-03 03:26:44 +01:00
DeepBlueV7.X
a7150b5666
Merge branch 'master' into avatar_username_feature 2021-02-03 03:17:28 +01:00
Nicolas Werner
3433cc3be7 Cleanup privacy screen, no more grabImage 2021-02-03 03:14:43 +01:00
DeepBlueV7.X
724c86e663
Merge pull request #446 from LorenDB/master
Fix QML layout warnings
2021-02-03 01:33:08 +01:00
Loren Burkholder
5109dc5e0f Fix QML layout warnings 2021-02-02 19:30:03 -05:00
Nicolas Werner
e466cd10ff Change matrix to guru overlay
fixes #410
2021-02-02 22:32:09 +01:00
Nicolas Werner
f73418f368 Merge branch 'privacy_screen' into 'master'
Chat Privacy screen

See merge request nheko-reborn/nheko!6
2021-02-02 14:10:34 -05:00
Nicolas Werner
aeec1e12fc Merge branch 'master' into 'privacy_screen'
# Conflicts:
#   resources/qml/TimelineView.qml
2021-02-02 13:00:53 -05:00
Nicolas Werner
935abee62e Fix unused capture warning 2021-02-02 12:57:21 -05:00
Joseph Donofry
2a858d84e2
Update format script again 2021-02-02 11:50:57 -05:00
Joseph Donofry
2bfd44755e
Try to fix format script and fix linting 2021-02-02 11:37:10 -05:00
Jedi18
2ff3c0c97e fixed global avatar updation in the dialog 2021-02-02 17:46:02 +05:30
Jedi18
cd3f719e43 add loading indicator 2021-02-02 17:24:08 +05:30
Jedi18
d535cc5e75 add error message and update avatars on avatar change in timeline and user profile dialog 2021-02-02 13:30:47 +05:30
DeepBlueV7.X
82e80d464d
Merge pull request #444 from LorenDB/dragDropTimeline
Allow drag/drop of files on whole timeline
2021-02-02 03:22:18 +01:00
Joseph Donofry
08dbdac3b7
Change bash variable check 2021-02-01 21:02:27 -05:00
Loren Burkholder
1c3e113d4e Allow drag/drop of files on whole timeline 2021-02-01 20:44:47 -05:00
Joseph Donofry
00885e41f8
Update wording on settings page for privacy timer 2021-02-01 19:07:04 -05:00
Joseph Donofry
1127aa7c91
Small UX fixes 2021-02-01 18:57:59 -05:00
Joseph Donofry
53c653a228
Merge remote-tracking branch 'nheko-im/master' into privacy_screen 2021-02-01 18:42:38 -05:00
Joseph Donofry
d59910a8f2
Remove redundant import and fix visible warning 2021-02-01 18:42:18 -05:00
Nicolas Werner
4874006501 Fix emojis with fe0f in the middle 2021-02-01 21:53:04 +01:00
DeepBlueV7.X
2b7bd09ad3
Merge pull request #431 from Jedi18/avatar_username_feature
Username editing (room specific and global)
2021-02-01 19:14:57 +01:00
Jedi18
c3e02240bf update room and global avatar through user profile 2021-02-01 22:13:04 +05:30
Weblate
50e2b5617f Translated using Weblate (Hungarian)
Currently translated at 81.2% (359 of 442 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-01-31 20:21:11 -05:00
Jedi18
0ebb2947ef focus and select text on clicking edit button 2021-01-30 14:08:02 +05:30
Weblate
f5693b56f0 Translated using Weblate (Russian)
Currently translated at 84.6% (374 of 442 strings)

Translated using Weblate (Russian)

Currently translated at 84.6% (374 of 442 strings)

Co-authored-by: Alexey Murz Korepov <murznn@gmail.com>
Co-authored-by: Artem <ego.cordatus@gmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/ru/
Translation: Nheko/nheko
2021-01-29 20:21:18 -05:00
Weblate
9709d6af16 Translated using Weblate (Hungarian)
Currently translated at 71.7% (317 of 442 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-01-29 20:21:18 -05:00
DeepBlueV7.X
5930b8eb31
Merge pull request #381 from LorenDB/centerUserInfo
Center user info dialog on the screen
2021-01-30 00:33:46 +01:00
Loren Burkholder
58dc79074c Close user profile dialog on Esc 2021-01-29 18:29:38 -05:00
Loren Burkholder
c3fa592018 make lint 2021-01-29 18:17:44 -05:00
Loren Burkholder
d8ebc0b3dc Fix warning about non-NOTIFYable property 2021-01-29 18:16:55 -05:00
Loren Burkholder
cd998d1c35 Center user info dialog on the screen 2021-01-29 18:16:50 -05:00
Jedi18
e09e587796 shifted isUsernameEditingAllowed to qml from c++ 2021-01-29 12:06:38 +05:30
Jedi18
9b5a287d14 made requeste changes 2021-01-29 11:55:24 +05:30
Jedi18
195bb0499b fix linting 2 2021-01-29 00:15:40 +05:30
Jedi18
fa7ad4f234 Shifted fetching of global username fom timeline model to user profile 2021-01-29 00:09:11 +05:30
Jedi18
3b82b2ff97 fix linting 2021-01-28 23:53:56 +05:30
Jedi18
b3f29f592b Changed edit method from double clicking to an edit button 2021-01-28 23:35:02 +05:30
Jedi18
87490c29cd Username can be edited by double clicking on text, added global user profile menu action in user info widget 2021-01-28 20:03:50 +05:30
Weblate
12bc7d4131 Translated using Weblate (Estonian)
Currently translated at 100.0% (442 of 442 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/et/
Translation: Nheko/nheko
2021-01-27 22:30:04 -05:00
Nicolas Werner
1a2517e829 Bump version to 0.8.1 2021-01-27 22:13:06 +01:00
Nicolas Werner
105e097e02 Update changelog 2021-01-27 21:50:02 +01:00
Nicolas Werner
50564985cc Fix crash, when keys for no events were requested 2021-01-27 21:30:06 +01:00
Nicolas Werner
5fbf17f6ec Fix request key loops 2021-01-27 20:29:06 +01:00
Nicolas Werner
73244afdb8 Fix username completion deleting characters before it
fixes #421
2021-01-27 20:26:54 +01:00
Nicolas Werner
6c370b1f0e Update new translations 2021-01-27 19:49:41 +01:00
Weblate
982784883c Translated using Weblate (Estonian)
Currently translated at 100.0% (438 of 438 strings)

Co-authored-by: Priit Jõerüüt <nhkwlate@joeruut.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/et/
Translation: Nheko/nheko
2021-01-27 13:39:25 -05:00
Nicolas Werner
54cc77de0e Only select elements in completer when mouse is moved 2021-01-27 19:36:53 +01:00
Nicolas Werner
9e48659016 Add a few missing strings on the login page to translations 2021-01-27 19:24:06 +01:00
Nicolas Werner
d1280af2e4 Autoclose completer when space is pressed and no suggestion available 2021-01-27 19:19:21 +01:00
Nicolas Werner
498bfb89b3 Translate some of the new messages 2021-01-27 17:20:22 +01:00
Nicolas Werner
b07f26bde6 Update translations 2021-01-27 14:49:25 +01:00
Weblate
0ac7912f50 Translated using Weblate (Hungarian)
Currently translated at 62.7% (273 of 435 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-01-27 08:12:27 -05:00
Weblate
a413c538e4 Translated using Weblate (Hungarian)
Currently translated at 53.7% (234 of 435 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-01-27 01:21:08 -05:00
Jedi18
5e3f513655 update room specific username from userprofile 2021-01-27 11:03:08 +05:30
Joseph Donofry
bfeb766a91
Implement Privacy Screen
* Add handles for window focus gained / focus lossed and connect to timer
* Clean up some of the PrivacyScreen.qml code
* Connect settings to PrivacyScreen visibility
2021-01-26 17:23:28 -05:00
Joseph Donofry
cb93ac3402
Fix formatting 2021-01-26 00:03:09 -05:00
Joseph Donofry
0794f0a3fd
Initial commit for privacy screen
Missing window focus event and knowing when room is encryption
2021-01-25 23:46:55 -05:00
DeepBlueV7.X
e6dc1da22f
Merge pull request #419 from LorenDB/master
Use correct syntax for emoji generation script
2021-01-26 04:06:54 +01:00
Loren Burkholder
f0a6ad6ea4 Use updated emoji-test.txt 2021-01-25 22:05:21 -05:00
Loren Burkholder
61cc4cc37d Use correct syntax for emoji generation script 2021-01-25 22:04:26 -05:00
DeepBlueV7.X
a5cd283423
Merge pull request #418 from LorenDB/fixEmojiCategories
Fix emoji categories
2021-01-26 03:46:12 +01:00
Loren Burkholder
e57dc5ea83 Make lint 2021-01-25 21:41:25 -05:00
Loren Burkholder
7629e9b786 Use new enum structure in emoji generation script 2021-01-25 21:40:47 -05:00
Loren Burkholder
bc7cf9ef39 Get category switching working 2021-01-25 21:40:27 -05:00
Weblate
e3f95fcdab Translated using Weblate (Hungarian)
Currently translated at 48.2% (210 of 435 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-01-25 12:35:39 -05:00
Nicolas Werner
0b5269bfc0 Reload the timeline after key import 2021-01-25 17:06:27 +01:00
Nicolas Werner
e5d2e2b728 Remove test log message 2021-01-25 16:19:28 +01:00
Nicolas Werner
2165eb8cb8 Focus input area on room switch and reply change again
fixes #412
fixes #413
2021-01-25 16:17:14 +01:00
Nicolas Werner
be49d184be Make matrix link chat invites direct chats 2021-01-25 15:59:15 +01:00
Nicolas Werner
6313ecb7d4 Treat empty secrets as no secret 2021-01-25 15:28:35 +01:00
Nicolas Werner
f0102c1e55 Fix old messages being played back after limit reset 2021-01-24 20:02:24 +01:00
DeepBlueV7.X
fc76a939bb
Merge pull request #405 from rnhmjoj/move-state
Write database to the DataLocation
2021-01-24 06:00:23 +01:00
DeepBlueV7.X
a5944ab047
Merge pull request #406 from rnhmjoj/open-in
Add "open in external program" action
2021-01-24 05:52:44 +01:00
DeepBlueV7.X
b76865de61
Merge pull request #409 from Nheko-Reborn/appveyor-python
Add python 3 to path in appveyor
2021-01-24 05:51:35 +01:00
Nicolas Werner
219ed587ca Add python 3 to path in appveyor 2021-01-24 05:25:37 +01:00
c569ab24bc
Add "open in external program" action 2021-01-24 01:50:23 +01:00
DeepBlueV7.X
ccc5903161
Merge pull request #403 from LorenDB/fixEmojiCategories
Get category switching working
2021-01-23 23:31:05 +01:00
Nicolas Werner
577d403be7 Fix emoji update script 2021-01-23 23:25:52 +01:00
Weblate
fdf5acac74 Translated using Weblate (Malayalam)
Currently translated at 13.3% (58 of 435 strings)

Co-authored-by: vachan-maker <revsamtp@hotmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/ml/
Translation: Nheko/nheko
2021-01-23 17:14:02 -05:00
Weblate
7c0b05e89c Translated using Weblate (Hungarian)
Currently translated at 35.6% (155 of 435 strings)

Co-authored-by: maxigaz <maxigaz@protonmail.com>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/hu/
Translation: Nheko/nheko
2021-01-23 17:14:02 -05:00
Weblate
66be966375 Translated using Weblate (Dutch)
Currently translated at 14.2% (62 of 435 strings)

Co-authored-by: Glael <glael@cock.li>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.nheko.im/projects/nheko/nheko-master/nl/
Translation: Nheko/nheko
2021-01-23 17:14:01 -05:00
Nicolas Werner
7bc57f76f7 Request unknown message indices 2021-01-23 20:08:59 +01:00
280 changed files with 37479 additions and 18847 deletions

View File

@ -7,11 +7,24 @@
set -eu
FILES=$(find src -type f -type f \( -iname "*.cpp" -o -iname "*.h" \))
FILES=$(find src -type f \( -iname "*.cpp" -o -iname "*.h" \))
for f in $FILES
do
clang-format -i "$f"
done;
QMLFORMAT_PATH=$(command -v qmlformat || true)
if [ -n "$QMLFORMAT_PATH" ]; then
QML_FILES=$(find resources -type f -iname "*.qml")
for f in $QML_FILES
do
$QMLFORMAT_PATH -i "$f"
done;
else
echo "qmlformat not found; skipping qml formatting"
fi
git diff --exit-code

14
.ci/licenses.sh Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env sh
# Runs the license update
# Return codes:
# - 1 there are files to be formatted
# - 0 everything looks fine
set -eu
FILES=$(find src resources/qml -type f \( -iname "*.cpp" -o -iname "*.h" -o -iname "*.qml" \))
reuse addheader --copyright="Nheko Contributors" --license="GPL-3.0-or-later" $FILES
git diff --exit-code

View File

@ -11,13 +11,13 @@ build-gcc7:
variables:
CXX: g++-7
CC: gcc-7
QT_PKG: 510
QT_PKG: 512
TRAVIS_OS_NAME: linux
before_script:
- apt-get update
- apt-get install -y software-properties-common
- add-apt-repository ppa:ubuntu-toolchain-r/test -y
- add-apt-repository ppa:beineri/opt-qt-5.10.1-xenial -y
- add-apt-repository ppa:beineri/opt-qt-5.12.9-xenial -y
- apt-get update && apt-get -y install --no-install-recommends g++-7 build-essential ninja-build qt${QT_PKG}{base,declarative,tools,multimedia,script,quickcontrols2,svg} liblmdb-dev libgl1-mesa-dev libssl-dev git ccache
# need recommended deps for wget
- apt-get -y install wget
@ -37,7 +37,7 @@ build-gcc7:
- cmake -GNinja -H. -Bbuild
-DCMAKE_INSTALL_PREFIX=.deps/usr
-DHUNTER_ROOT=".hunter"
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=ON -DUSE_BUNDLED_LMDB=OFF
-DCMAKE_BUILD_TYPE=Release -DHUNTER_CONFIGURATION_TYPES=Release
-DCI_BUILD=ON
- cmake --build build
@ -88,7 +88,7 @@ build-flatpak-amd64:
#image: 'registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master'
tags: [docker]
before_script:
- apt-get update && apt-get -y install flatpak-builder git python curl
- apt-get update && apt-get -y install flatpak-builder git python curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
- flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
- flatpak --noninteractive install --user flathub org.kde.Platform//5.15
- flatpak --noninteractive install --user flathub org.kde.Sdk//5.15
@ -96,9 +96,10 @@ build-flatpak-amd64:
- export VERSION=$(git describe)
- mkdir -p build-flatpak
- cd build-flatpak
- flatpak-builder --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date`" app ../io.github.NhekoReborn.Nheko.json
- flatpak-builder --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date`" app ../io.github.NhekoReborn.Nheko.yaml
- flatpak build-bundle repo nheko-amd64.flatpak io.github.NhekoReborn.Nheko ${CI_COMMIT_REF_NAME//\//_}
after_script:
- (cd ./scripts && ./upload-to-flatpak-repo.sh ../build-flatpak/repo) || true
- bash ./.ci/upload-nightly-gitlab.sh build-flatpak/nheko-amd64.flatpak
cache:
key: "$CI_JOB_NAME"
@ -115,7 +116,7 @@ build-flatpak-arm64:
#image: 'registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master'
tags: [docker-arm64]
before_script:
- apt-get update && apt-get -y install flatpak-builder git python curl
- apt-get update && apt-get -y install flatpak-builder git python curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
- flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
- flatpak --noninteractive install --user flathub org.kde.Platform//5.15
- flatpak --noninteractive install --user flathub org.kde.Sdk//5.15
@ -123,9 +124,10 @@ build-flatpak-arm64:
- export VERSION=$(git describe)
- mkdir -p build-flatpak
- cd build-flatpak
- flatpak-builder --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date` for arm64" app ../io.github.NhekoReborn.Nheko.json
- flatpak-builder --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date` for arm64" app ../io.github.NhekoReborn.Nheko.yaml
- flatpak build-bundle repo nheko-arm64.flatpak io.github.NhekoReborn.Nheko ${CI_COMMIT_REF_NAME//\//_}
after_script:
- (cd ./scripts && ./upload-to-flatpak-repo.sh ../build-flatpak/repo) || true
- bash ./.ci/upload-nightly-gitlab.sh build-flatpak/nheko-arm64.flatpak
cache:
key: "$CI_JOB_NAME"
@ -141,9 +143,12 @@ linting:
image: alpine:latest
tags: [docker]
before_script:
- apk update && apk add clang make git
- apk update && apk add clang make git python3 py3-pip
- export PATH="$PATH:/root/.local/bin"
- pip3 install --user reuse
script:
- make lint
- make license
appimage-amd64:
stage: build

View File

@ -79,6 +79,7 @@ AppDir:
- libxv1
- libxxf86vm1
- libzstd1
- qml-module-qt-labs-platform
- qml-module-qtgraphicaleffects
- qml-module-qtmultimedia
- qml-module-qtquick-controls2

View File

@ -1,5 +1,151 @@
# Changelog
## [0.8.2] -- 2021-04-23
### Highlights
- Edits
- If you made a typo, just press the `Up` key and edit what you wrote.
- Messages other users edited will get updated automatically and have a small
pen symbol next to them.
- Privacy Screen
- Blur your messages, when Nheko looses focus, which prevents others from
peeking at your messages.
- You can configure the timeout of when this happens.
- Improved notifications (contributed by lorendb)
- No more breakage, because the message included a > on KDE based DEs.
- Render html and images where possible in the notification.
- Render if a message is a reply or someone sent an emote message more nicely
where possible.
- Encrypted notifications now show, that the content is encrypted instead of
being empty.
- Screenshare support in calls on X11 (contributed by trilene)
- Share your screen in a call!
- Select if your mouse cursor should be shown or not and if your webcam should
be included.
- SEND MESSAGES AS RAINBOWS! (contributed by LordMZTE)
- YES MESSAGES, EMOTES AND NOTICES!
### Features
- Set your displayname and avatar from Nheko either globally or per room.
(contributed by jedi18)
- Show room topic in the room settings.
- Double tap a message to reply to it.
- Leave a room using `/part` or `/leave`. (contributed by lorendb)
- Show mxid when hovering a username or avatar.
- Allow opening matrix: uris on Windows.
- Disable room pings caused by replies sent via Nheko (unless you are using
Element Web/Desktop).
### Improvements
- Userprofile can be closed via the Escape key. No more hotel california!
(contributed by lorendb)
- Most dialogs are now centered on the Nheko window. (contributed by lorendb)
- Update Hungarian translations. (contributed by maxigaz)
- Update Estonian translations. (contributed by Priit)
- Update Russian translations. (contributed by Alexey Murz and Artem)
- Update Swedish translations. (contributed by Emilie)
- Update French translations. (contributed by MayeulC, Nicolas Guichard and Carl Schwan)
- Allow drag and drop of files on the whole timeline. (contributed by lorendb)
- Enable notifications on Haiku. (contributed by kallisti5)
- Update scheme handler to the latest matrix: scheme proposal.
- Close completers when typing a space after the colon. (contributed by jedi18)
- Port room settings to Qml. (contributed by jedi18)
- Improved read marker handling. Read marker should now get stuck less often.
- Various changes around hover and tap handling in the timeline, which hopefully
now works more predicatably.
- Buttons in the timeline are now rendered in a box on hover on desktop
platforms.
- Complete room links in the timeline after typing a # character. (contributed
by jedi18)
- An improved quick switcher with better rendering and search. (contributed by jedi18)
- Some fixes around inline emoji and images.
- Jump into new rooms, after you created them. (contrubuted by jedi18)
- Improved search in the emoji picker.
- Allow disabling certificate checks via the config file.
- Use native menus where possible.
- Fix video playback on Windows. (contrubuted by jedi18)
- Send image messages by pressing Enter. (contributed by salahmak)
- Escape closes the upload widget. (contributed by salahmak)
- Improve session rotation and sharing in E2EE rooms.
### Bugfixes
- Emojis joined from separate emojis with a 0xfe0f in the middle should now
render correctly.
- Fix a bug when logging out of a non default profile clearing the wrong
profile. (contrubuted by lorendb)
- Various fixed around profile handling. (contributed by lorendb)
- Focus message input after a reaction. (contributed by jedi18)
- Disable native rendering to prevent kerning bugs on non integer scale factors.
- Fix duplex call devices not showing up. (contributed by trilene)
- Fix a few crashes when leaving a room. (contributed by jedi18)
- Fix hidden tags not updating properly. (contributed by jedi18)
- Fix some issues with login, when a server had SSO as well as password login
enabled (for example matrix.org).
- Properly set the dialog flag for dialogs on most platforms. (Wayland does not
support that.)
- Properly add license to source files.
- Fix fingerprint increasing the minimum window size.
- Don't send markdown links in the plain text body of events when autocompleting
user or room names.
- Fix webcam not working in flatpaks.
- Fix markdown override in replies.
- Fix unsupported events causing errors when saving them. (contributed by
anjanik)
- Fix exif rotation not being respected anymore in E2EE rooms.
- Remove unused qml plugins in the windows package.
- Fix broken olm channels automatically when noticed.
- Fix pasting not overwriting the selection.
- Fix Nheko sometimes overwriting received keys with keys it requested, even if
they have a higher minimum index.
### Packaging changes
- Added xcb dependency on X11 based platforms for screensharing (optional)
- Bumped lmdbxx version from 0.9.14.0 to 1.0.0, which is a BREAKING change. You
can get the new version here: https://github.com/hoytech/lmdbxx/releases
(repo changed)
- Removed tweeny as a dependency.
## [0.8.1] -- 2021-01-27
### Features
- `/plain` and `/md` commands to override the current markdown setting. (contributed by lorendb)
- Allow persistent hiding of rooms with a specific tag (or from a community) via a context menu.
- Allow open media messages in an external program immediately. (contributed by rnhmjoj)
### Improvements
- Use async dbus connection for notifications. (contributed by lorendb)
- Update Hungarian translations. (contributed by maxigaz)
- Update Finnish translations. (contributed by Priit)
- Update Malayalam translations. (contributed by vachan-maker)
- Update Dutch translations. (contributed by Glael)
- Store splitter size across restarts.
- Add a border around the completer. (contributed by lorendb)
- Request keys for messages with unknown message indices (once per restart, when they are shown).
- Move the database location to XDG_DATA_DIR. (contributed by rnhmjoj)
- Reload the timeline after key backup import.
- Autoclose completer on `space`, when there are no matches.
- Make completer only react, when the mouse cursor is moved.
### Bugfixes
- Fix unhandled exception, when a device has no keys.
- Fix some cmake warnings regarding GNUInstallDirs.
- Fix tags being broken. If you have no tags showing up, you may want to logout and login again.
- Fix versionOk being called on the wrong thread. (contributed by Jedi18)
- Fix font tags showing up in media message filenames.
- Fix user profile in dark themes showing the wrong colors. (contributed by lorendb)
- Fix emoji category switching on old Qt versions. (contributed by lorendb)
- Fix old messages being replayed after a limited timeline.
- Fix empty secrets being returned from the wallet breaking verification.
- Make matrix link chat invites create a direct chat.
- Fix focus handling on room change or reply button clicks.
- Fix username completion deleting the character before it.
## [0.8.0] -- 2021-01-21
### Highlights

View File

@ -33,14 +33,12 @@ option(USE_BUNDLED_CMARK "Use the bundled version of cmark."
option(USE_BUNDLED_JSON "Use the bundled version of nlohmann json."
${HUNTER_ENABLED})
option(USE_BUNDLED_OPENSSL "Use the bundled version of OpenSSL."
${HUNTER_ENABLED})
OFF)
option(USE_BUNDLED_MTXCLIENT "Use the bundled version of the Matrix Client library." ${HUNTER_ENABLED})
option(USE_BUNDLED_LMDB "Use the bundled version of lmdb."
${HUNTER_ENABLED})
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdb++."
${HUNTER_ENABLED})
option(USE_BUNDLED_TWEENY "Use the bundled version of tweeny."
${HUNTER_ENABLED})
option(USE_BUNDLED_QTKEYCHAIN "Use the bundled version of Qt5Keychain."
${HUNTER_ENABLED})
@ -72,16 +70,16 @@ if(${CMAKE_VERSION} VERSION_LESS "3.14.0")
endmacro()
endif()
# Include Qt basic functions
include(QtCommon)
project(nheko LANGUAGES CXX C)
include(GNUInstallDirs)
# Include Qt basic functions
include(QtCommon)
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "8")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_VERSION_PATCH "2")
set(PROJECT_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR})
set(PROJECT_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR})
set(PROJECT_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH})
@ -129,8 +127,11 @@ endif()
if(USE_BUNDLED_LMDB)
hunter_add_package(lmdb)
find_package(liblmdb CONFIG REQUIRED)
target_include_directories(liblmdb::lmdb INTERFACE
"${HUNTER_INSTALL_PREFIX}/include/lmdb")
else()
find_package(LMDB)
find_package(LMDB REQUIRED)
endif()
#
@ -210,7 +211,7 @@ set(SPDLOG_DEBUG_ON false)
# Windows doesn't handle CMAKE_BUILD_TYPE.
if(NOT WIN32)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(SPDLOG_DEBUG_ON true)
else()
set(SPDLOG_DEBUG_ON false)
@ -257,7 +258,6 @@ set(SRC_FILES
src/dialogs/PreviewUploadOverlay.cpp
src/dialogs/ReCaptcha.cpp
src/dialogs/ReadReceipts.cpp
src/dialogs/RoomSettings.cpp
# Emoji
src/emoji/EmojiModel.cpp
@ -271,6 +271,7 @@ set(SRC_FILES
src/timeline/TimelineViewManager.cpp
src/timeline/TimelineModel.cpp
src/timeline/DelegateChooser.cpp
src/timeline/Permissions.cpp
# UI components
src/ui/Avatar.cpp
@ -281,6 +282,7 @@ set(SRC_FILES
src/ui/InfoMessage.cpp
src/ui/Label.cpp
src/ui/LoadingIndicator.cpp
src/ui/NhekoCursorShape.cpp
src/ui/NhekoDropArea.cpp
src/ui/OverlayModal.cpp
src/ui/OverlayWidget.cpp
@ -294,12 +296,18 @@ set(SRC_FILES
src/ui/ThemeManager.cpp
src/ui/ToggleButton.cpp
src/ui/UserProfile.cpp
src/ui/RoomSettings.cpp
# Generic notification stuff
src/notifications/Manager.cpp
src/AvatarProvider.cpp
src/BlurhashProvider.cpp
src/Cache.cpp
src/CallDevices.cpp
src/CallManager.cpp
src/ChatPage.cpp
src/Clipboard.cpp
src/ColorImageProvider.cpp
src/CommunitiesList.cpp
src/CommunitiesListItem.cpp
@ -313,7 +321,6 @@ set(SRC_FILES
src/MatrixClient.cpp
src/MxcImageProvider.cpp
src/Olm.cpp
src/QuickSwitcher.cpp
src/RegisterPage.cpp
src/RoomInfoListItem.cpp
src/RoomList.cpp
@ -324,6 +331,7 @@ set(SRC_FILES
src/UserInfoWidget.cpp
src/UserSettingsPage.cpp
src/UsersModel.cpp
src/RoomsModel.cpp
src/Utils.cpp
src/WebRTCSession.cpp
src/WelcomePage.cpp
@ -355,13 +363,13 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare(
MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
GIT_TAG v0.4.0
GIT_TAG 5d2f055ea9403770039ddf66b1900f890cd5cde7
)
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
FetchContent_MakeAvailable(MatrixClient)
else()
find_package(MatrixClient 0.4.0 REQUIRED)
find_package(MatrixClient 0.5.1 REQUIRED)
endif()
if(USE_BUNDLED_OLM)
include(FetchContent)
@ -373,7 +381,7 @@ if(USE_BUNDLED_OLM)
set(OLM_TESTS OFF CACHE INTERNAL "")
FetchContent_MakeAvailable(Olm)
else()
find_package(Olm 3)
find_package(Olm 3 REQUIRED)
set_package_properties(Olm PROPERTIES
DESCRIPTION "An implementation of the Double Ratchet cryptographic ratchet"
URL "https://git.matrix.org/git/olm/about/"
@ -414,8 +422,18 @@ set_package_properties(nlohmann_json PROPERTIES
)
if(USE_BUNDLED_LMDBXX)
hunter_add_package(lmdbxx)
find_package(lmdbxx CONFIG REQUIRED)
include(FetchContent)
FetchContent_Declare(
lmdbxx
URL "https://raw.githubusercontent.com/hoytech/lmdbxx/1.0.0/lmdb++.h"
DOWNLOAD_NO_EXTRACT TRUE
)
if(NOT lmdbxx_POPULATED)
FetchContent_Populate(lmdbxx)
endif()
add_library(lmdbxx INTERFACE)
target_include_directories(lmdbxx INTERFACE ${lmdbxx_SOURCE_DIR})
add_library(lmdbxx::lmdbxx ALIAS lmdbxx)
else()
if(NOT LMDBXX_INCLUDE_DIR)
find_path(LMDBXX_INCLUDE_DIR
@ -431,24 +449,18 @@ else()
add_library(lmdbxx::lmdbxx ALIAS lmdbxx)
endif()
if(USE_BUNDLED_TWEENY)
include(FetchContent)
FetchContent_Declare(
Tweeny
GIT_REPOSITORY https://github.com/mobius3/tweeny.git
GIT_TAG 6a5033372fe53c4c731c66c8a2d56261746cd85c #v3 <- v3 has unfixed warnings
)
FetchContent_MakeAvailable(Tweeny)
else()
find_package(Tweeny REQUIRED)
endif()
include(FindPkgConfig)
pkg_check_modules(GSTREAMER IMPORTED_TARGET gstreamer-sdp-1.0>=1.16 gstreamer-webrtc-1.0>=1.16)
pkg_check_modules(GSTREAMER IMPORTED_TARGET gstreamer-sdp-1.0>=1.18 gstreamer-webrtc-1.0>=1.18)
if (TARGET PkgConfig::GSTREAMER)
add_feature_info(voip ON "GStreamer found. Call support is enabled automatically.")
pkg_check_modules(XCB IMPORTED_TARGET xcb xcb-ewmh)
if (TARGET PkgConfig::XCB)
add_feature_info("Window selection when screen sharing (X11)" ON "XCB-EWMH found. Window selection is enabled when screen sharing (X11).")
else()
add_feature_info("Window selection when screen sharing (X11)" OFF "XCB-EWMH could not be found on your system. Screen sharing (X11) is limited to the entire screen only. To enable window selection, make sure xcb and xcb-ewmh can be found via pkgconfig.")
endif()
else()
add_feature_info(voip OFF "GStreamer could not be found on your system. As a consequence call support has been disabled. If you don't want that, make sure gstreamer-sdp-1.0>=1.16 gstreamer-webrtc-1.0>=1.16 can be found via pkgconfig.")
add_feature_info(voip OFF "GStreamer could not be found on your system. As a consequence call support has been disabled. If you don't want that, make sure gstreamer-sdp-1.0>=1.18 gstreamer-webrtc-1.0>=1.18 can be found via pkgconfig.")
endif()
# single instance functionality
@ -471,7 +483,6 @@ qt5_wrap_cpp(MOC_HEADERS
src/dialogs/RawMessage.h
src/dialogs/ReCaptcha.h
src/dialogs/ReadReceipts.h
src/dialogs/RoomSettings.h
# Emoji
src/emoji/EmojiModel.h
@ -484,6 +495,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/timeline/TimelineViewManager.h
src/timeline/TimelineModel.h
src/timeline/DelegateChooser.h
src/timeline/Permissions.h
# UI components
src/ui/Avatar.h
@ -494,6 +506,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/ui/Label.h
src/ui/FloatingButton.h
src/ui/Menu.h
src/ui/NhekoCursorShape.h
src/ui/NhekoDropArea.h
src/ui/OverlayWidget.h
src/ui/SnackBar.h
@ -506,14 +519,18 @@ qt5_wrap_cpp(MOC_HEADERS
src/ui/Theme.h
src/ui/ThemeManager.h
src/ui/UserProfile.h
src/ui/RoomSettings.h
src/notifications/Manager.h
src/AvatarProvider.h
src/BlurhashProvider.h
src/Cache_p.h
src/CacheCryptoStructs.h
src/CallDevices.h
src/CallManager.h
src/ChatPage.h
src/Clipboard.h
src/CommunitiesList.h
src/CommunitiesListItem.h
src/CompletionProxyModel.h
@ -522,7 +539,6 @@ qt5_wrap_cpp(MOC_HEADERS
src/LoginPage.h
src/MainWindow.h
src/MxcImageProvider.h
src/QuickSwitcher.h
src/RegisterPage.h
src/RoomInfoListItem.h
src/RoomList.h
@ -533,6 +549,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/UserInfoWidget.h
src/UserSettingsPage.h
src/UsersModel.h
src/RoomsModel.h
src/WebRTCSession.h
src/WelcomePage.h
src/popups/PopupItem.h
@ -548,7 +565,7 @@ set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
if (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa")
set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/emoji/MacHelper.mm)
set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
set_source_files_properties( src/notifications/ManagerMac.mm src/emoji/MacHelper.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
endif()
@ -591,6 +608,9 @@ if(APPLE)
elseif(WIN32)
target_compile_definitions(nheko PRIVATE WIN32_LEAN_AND_MEAN)
target_link_libraries (nheko PRIVATE ${NTDLIB} Qt5::WinMain)
if(MSVC)
target_compile_options(nheko PUBLIC "/Zc:__cplusplus")
endif()
else()
target_link_libraries (nheko PRIVATE Qt5::DBus)
endif()
@ -619,7 +639,6 @@ target_link_libraries(nheko PRIVATE
nlohmann_json::nlohmann_json
lmdbxx::lmdbxx
liblmdb::lmdb
tweeny
SingleApplication::SingleApplication)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
@ -633,6 +652,10 @@ endif()
if (TARGET PkgConfig::GSTREAMER)
target_link_libraries(nheko PRIVATE PkgConfig::GSTREAMER)
target_compile_definitions(nheko PRIVATE GSTREAMER_AVAILABLE)
if (TARGET PkgConfig::XCB)
target_link_libraries(nheko PRIVATE PkgConfig::XCB)
target_compile_definitions(nheko PRIVATE XCB_AVAILABLE)
endif()
endif()
if(MSVC)
@ -645,7 +668,7 @@ if(QML_DEBUGGING)
endif()
if(NOT MSVC)
if(NOT MSVC AND NOT HAIKU)
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR CI_BUILD)
target_compile_options(nheko PRIVATE "-Werror")
endif()

View File

@ -41,6 +41,9 @@ macos-app-install:
lint:
./.ci/format.sh
license:
./.ci/licenses.sh
image:
docker build -t nheko-app-image .

View File

@ -2,8 +2,9 @@ nheko
----
[![Build Status](https://nheko.im/nheko-reborn/nheko/badges/master/pipeline.svg)](https://nheko.im/nheko-reborn/nheko/-/pipelines/latest)
[![Build status](https://ci.appveyor.com/api/projects/status/07qrqbfylsg4hw2h/branch/master?svg=true)](https://ci.appveyor.com/project/redsky17/nheko/branch/master)
[![Stable Version](https://img.shields.io/badge/download-stable-green.svg)](https://github.com/Nheko-Reborn/nheko/releases/v0.8.0)
[![Stable Version](https://img.shields.io/badge/download-stable-green.svg)](https://github.com/Nheko-Reborn/nheko/releases/v0.8.2-RC)
[![Nightly](https://img.shields.io/badge/download-nightly-green.svg)](https://matrix-static.neko.dev/room/!TshDrgpBNBDmfDeEGN:neko.dev/)
<a href='https://flatpak.neko.dev/repo/nightly/appstream/io.github.NhekoReborn.Nheko.flatpakref' download='nheko-nightly.flatpakref'><img alt='Download Nightly Flatpak' src='https://img.shields.io/badge/download-flatpak--nightly-green'/></a>
[![#nheko-reborn:matrix.org](https://img.shields.io/matrix/nheko-reborn:matrix.org.svg?label=%23nheko-reborn:matrix.org)](https://matrix.to/#/#nheko-reborn:matrix.org)
[![AUR: nheko](https://img.shields.io/badge/AUR-nheko-blue.svg)](https://aur.archlinux.org/packages/nheko)
<a href='https://flathub.org/apps/details/io.github.NhekoReborn.Nheko'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
@ -49,7 +50,7 @@ Specifically there is support for:
### Releases
Releases for Linux (AppImage), macOS (disk image) & Windows (x64 installer)
can be found in the [Github releases](https://github.com/Nheko-Reborn/nheko/releases).
can be found in the [GitHub releases](https://github.com/Nheko-Reborn/nheko/releases).
### Repositories
@ -74,7 +75,7 @@ sudo dnf install nheko
#### Gentoo Linux
```bash
sudo eselect repository enable matrix
sudo eselect repository enable guru
sudo emerge -a nheko
```
@ -114,14 +115,29 @@ with [homebrew](https://brew.sh/):
brew install --cask nheko
```
#### Windows
with [Chocolatey](https://chocolatey.org/):
```posh
choco install nheko-reborn
```
### FAQ
##
**Q:** Why don't videos run for me on Windows?
**A:** You're probably missing the required video codecs, download [K-Lite Codec Pack](https://codecguide.com/download_kl.htm).
##
### Build Requirements
- Qt5 (5.10 or greater). Qt 5.7 adds support for color font rendering with
Freetype, which is essential to properly support emoji, 5.8 adds some features
to make interopability with Qml easier, 5.10 makes sliders actually visible with different palettes.
- Qt5 (5.12 or greater). Required for overlapping hover handlers in Qml.
- CMake 3.15 or greater. (Lower version may work, but may break boost linking)
- [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
- [lmdb++](https://github.com/hoytech/lmdbxx)
- [cmark](https://github.com/commonmark/cmark) 0.29 or greater.
- Boost 1.70 or greater.
- [libolm](https://gitlab.matrix.org/matrix-org/olm)
@ -142,7 +158,7 @@ brew install --cask nheko
Nheko can use bundled version for most of those libraries automatically, if the versions in your distro are too old.
To use them, you can enable the hunter integration by passing `-DHUNTER_ENABLED=ON`.
It is probably wise to link those dependencies statically by passing `-DBUILD_SHARED_LIBS=OFF`
You can select which bundled dependencies you want to use by passing various `-DUSE_BUNDLED_*` flags. By default all dependencies are bundled *if* you enable hunter.
You can select which bundled dependencies you want to use by passing various `-DUSE_BUNDLED_*` flags. By default all dependencies are bundled *if* you enable hunter. (The exception to that is OpenSSL, which is always disabled by default.)
If you experience build issues and you are trying to link `mtxclient` library without hunter, make sure the library version(commit) as mentioned in the `CMakeList.txt` is used. Sometimes we have to make breaking changes in `mtxclient` and for that period the master branch of both repos may not be compatible.
The bundle flags are currently:
@ -159,6 +175,8 @@ The bundle flags are currently:
- USE_BUNDLED_LMDBXX
- USE_BUNDLED_TWEENY
A note on bundled OpenSSL: You need to explicitly enable it and it will not be using your system certificate directory by default, if you enable it. You need to override that at runtime with the SSL_CERT_FILE variable. On Windows it will still be using your system certificates though, since it loads them from the system store instead of the OpenSSL directory.
#### Linux
If you don't want to install any external dependencies, you can generate an AppImage locally using docker. It is not that well maintained though...
@ -193,7 +211,7 @@ sudo emerge -a ">=dev-qt/qtgui-5.10.0" media-libs/fontconfig dev-libs/qtkeychain
```bash
# Build requirements + qml modules needed at runtime (you may not need all of them, but the following seem to work according to reports):
sudo apt install g++ cmake zlib1g-dev libssl-dev qt{base,declarative,tools,multimedia,quickcontrols2-}5-dev libqt5svg5-dev libboost-system-dev libboost-thread-dev libboost-iostreams-dev libolm-dev liblmdb++-dev libcmark-dev nlohmann-json3-dev libspdlog-dev libgtest-dev qml-module-qt{gstreamer,multimedia,quick-extras,-labs-settings,graphicaleffects,quick-controls2} qt5keychain-dev
sudo apt install g++ cmake zlib1g-dev libssl-dev qt{base,declarative,tools,multimedia,quickcontrols2-}5-dev libqt5svg5-dev libboost-system-dev libboost-thread-dev libboost-iostreams-dev libolm-dev liblmdb++-dev libcmark-dev nlohmann-json3-dev libspdlog-dev libgtest-dev qml-module-qt{gstreamer,multimedia,quick-extras,-labs-settings,-labs-platform,graphicaleffects,quick-controls2} qt5keychain-dev
```
This will install all dependencies, except for tweeny (use bundled tweeny)
and mtxclient (needs to be build separately).
@ -205,8 +223,8 @@ and mtxclient (needs to be build separately).
```bash
sudo apt install cmake gcc make automake liblmdb-dev \
qt5-default libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev \
qml-module-qtgstreamer qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools \
qml-module-qtgraphicaleffects qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts \
qml-module-qtgstreamer qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev \
qml-module-qtgraphicaleffects qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform\
qt5keychain-dev
```

View File

@ -15,7 +15,7 @@ environment:
cache:
- c:\hunter\
- c:\hunter\ -> appveyor.yml
- build\_deps -> appveyor.yml
build:
@ -23,15 +23,16 @@ build:
install:
- set QT_DIR=C:\Qt\5.15\msvc2019_64
- set PATH=C:\Strawberry\perl\bin;%PATH%;%QT_DIR%\bin
- set PATH=Path=C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin;C:\Program Files\Git\cmd;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\PowerShell\7\;C:\Program Files\7-Zip;C:\Program Files\Microsoft\Web Platform Installer\;C:\Tools\NuGet;C:\Tools\PsTools;C:\Program Files\Git\usr\bin;C:\Program Files\Git LFS;C:\Program Files\Mercurial\;C:\Program Files (x86)\Subversion\bin;C:\Program Files\Docker\Docker\resources\bin;C:\ProgramData\DockerDesktop\version-bin;C:\Program Files\dotnet\;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\150;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin;C:\Tools\xUnit;C:\Tools\xUnit20;C:\Tools\NUnit\bin;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\Extensions\TestPlatform;C:\Ruby193\bin;C:\Tools\WebDriver;C:\Python27;C:\Python27\Scripts;C:\Program Files\erl10.7\bin;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files (x86)\Microsoft DirectX SDK;C:\Program Files\Microsoft Service Fabric\bin\Fabric\Fabric.Code;C:\Program Files\Microsoft SDKs\Service Fabric\Tools\ServiceFabricLocalClusterManager;C:\Tools\Doxygen;C:\Program Files (x86)\CMake\bin;C:\ProgramData\chocolatey\bin;C:\Tools\vcpkg;C:\Tools\Coverity\bin;C:\Program Files (x86)\NSIS;C:\Tools\Octopus;C:\Program Files\Meson\;C:\Tools\GitVersion;C:\Tools\NUnit3\bin;C:\Users\appveyor\AppData\Local\Microsoft\WindowsApps;C:\Users\appveyor\.dotnet\tools;C:\Program Files\AppVeyor\BuildAgent\
- set PATH=C:\Strawberry\perl\bin;C:\Python39-x64;%QT_DIR%\bin;%PATH%
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
build_script:
# VERSION format: branch-master/branch-1.2
# INSTVERSION format: x.y.z
# WINVERSION format: 9999.0.0.123/1.2.0.234
- if "%APPVEYOR_REPO_TAG%"=="false" set INSTVERSION=0.8.0
- if "%APPVEYOR_REPO_TAG%"=="false" set VERSION=0.8.0
- if "%APPVEYOR_REPO_TAG%"=="false" set INSTVERSION=0.8.2
- if "%APPVEYOR_REPO_TAG%"=="false" set VERSION=0.8.2
- if "%APPVEYOR_REPO_TAG%"=="false" if "%APPVEYOR_REPO_BRANCH%"=="master" set INSTVERSION=9999.0
- if "%APPVEYOR_REPO_TAG%"=="false" set WINVERSION=%INSTVERSION%.0.%APPVEYOR_BUILD_NUMBER%
# VERSION format: v1.2.3/v1.3.4
@ -46,10 +47,9 @@ build_script:
- echo %DATE%
# Build nheko
#- cmake -G "Visual Studio 15 2017 Win64" -H. -Bbuild
- cmake -G "Visual Studio 16 2019" -A x64 -H. -Bbuild
-DHUNTER_ROOT="C:\hunter"
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=ON
-DCMAKE_BUILD_TYPE=Release -DHUNTER_CONFIGURATION_TYPES=Release
- cmake --build build --config Release
@ -61,7 +61,7 @@ after_build:
- mkdir NhekoRelease
- copy build\Release\nheko.exe NhekoRelease\nheko.exe
- copy build\_deps\cmark-build\src\Release\cmark.dll NhekoRelease\cmark.dll
- windeployqt --qmldir %QT_DIR%\qml\ --release NhekoRelease\nheko.exe
- windeployqt --qmldir resources\qml\ NhekoRelease\nheko.exe
- 7z a nheko_win_64.zip .\NhekoRelease\*
- ls -lh build\Release\
@ -77,25 +77,19 @@ after_build:
- mkdir installer\packages\io.github.nhekoreborn.nheko
- mkdir installer\packages\io.github.nhekoreborn.nheko\data
- mkdir installer\packages\io.github.nhekoreborn.nheko\meta
- mkdir installer\packages\io.github.nhekoreborn.nheko.cleanup\meta
# Copy installer data
- copy %BUILD%\resources\nheko.ico installer\config
- copy %BUILD%\resources\nheko.png installer\config
- copy %BUILD%\COPYING installer\packages\io.github.nhekoreborn.nheko\meta\license.txt
- copy %BUILD%\COPYING installer\packages\io.github.nhekoreborn.nheko.cleanup\meta\license.txt
- copy %BUILD%\deploy\installer\config.xml installer\config
- copy %BUILD%\deploy\installer\controlscript.qs installer\config
- copy %BUILD%\deploy\installer\uninstall.qs installer\packages\io.github.nhekoreborn.nheko\data
- copy %BUILD%\deploy\installer\gui\package.xml installer\packages\io.github.nhekoreborn.nheko\meta
- copy %BUILD%\deploy\installer\gui\installscript.qs installer\packages\io.github.nhekoreborn.nheko\meta
- copy %BUILD%\deploy\installer\cleanup\package.xml installer\packages\io.github.nhekoreborn.nheko.cleanup\meta
- copy %BUILD%\deploy\installer\cleanup\installscript.qs installer\packages\io.github.nhekoreborn.nheko.cleanup\meta
# Amend version and date
- sed -i "s/__VERSION__/0.8.0/" installer\config\config.xml
- sed -i "s/__VERSION__/0.8.0/" installer\packages\io.github.nhekoreborn.nheko\meta\package.xml
- sed -i "s/__VERSION__/0.8.0/" installer\packages\io.github.nhekoreborn.nheko.cleanup\meta\package.xml
- sed -i "s/__VERSION__/0.8.2/" installer\config\config.xml
- sed -i "s/__VERSION__/0.8.2/" installer\packages\io.github.nhekoreborn.nheko\meta\package.xml
- sed -i "s/__DATE__/%DATE%/" installer\packages\io.github.nhekoreborn.nheko\meta\package.xml
- sed -i "s/__DATE__/%DATE%/" installer\packages\io.github.nhekoreborn.nheko.cleanup\meta\package.xml
# Copy nheko data
- xcopy NhekoData\*.* installer\packages\io.github.nhekoreborn.nheko\data\*.* /s /e /c /y
- move NhekoRelease\nheko.exe installer\packages\io.github.nhekoreborn.nheko\data
@ -107,13 +101,13 @@ after_build:
- set PATH=%BUILD%\tools\bin;%PATH%
- binarycreator.exe -f -c installer\config\config.xml -p installer\packages nheko-installer.exe
- mv nheko-installer.exe nheko-%APPVEYOR_REPO_TAG_NAME%-installer.exe
- copy nheko-installer.exe nheko-%APPVEYOR_REPO_TAG_NAME%-installer.exe
- copy nheko-installer.exe nheko-%APPVEYOR_PULL_REQUEST_HEAD_COMMIT%-installer.exe
- ps: .\.ci\upload-nightly.ps1
on_success:
- if "%APPVEYOR_REPO_TAG%" == "true" (curl -T nheko-%APPVEYOR_REPO_TAG_NAME%-installer.exe -uredsky17:%BINTRAY_APIKEY% https://api.bintray.com/content/nheko-reborn/nheko/%APPVEYOR_REPO_TAG_NAME%/nheko/%APPVEYOR_REPO_TAG_NAME%/)
before_deploy:
- ps: .\.ci\upload-nightly.ps1
deploy:
- description: "Development builds"
provider: GitHub
@ -128,3 +122,4 @@ deploy:
artifacts:
- path: nheko_win_64.zip
- path: nheko-$(APPVEYOR_REPO_TAG_NAME)-installer.exe
- path: nheko-$(APPVEYOR_PULL_REQUEST_HEAD_COMMIT)-installer.exe

View File

@ -1,28 +0,0 @@
function Component()
{
}
Component.prototype.createOperations = function()
{
component.createOperations();
try
{
if( installer.value("os") === "win" )
{
/**
* Cleanup AppData and registry
*/
component.addElevatedOperation("Execute","UNDOEXECUTE","cmd /C reg delete HKEY_CURRENT_USER\Software\nheko\nheko /f");
var localappdata = installer.environmentVariable("LOCALAPPDATA");
if( localappdata != "" )
{
component.addElevatedOperation("Execute","UNDOEXECUTE","cmd /C rmdir "+localappdata+"\nheko /f");
}
}
}
catch( e )
{
print( e );
}
}

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>Cleanup AppData and Registry</DisplayName>
<Description>Cleans up AppData and Registry when selected (logs you out) - Broken</Description>
<Version>__VERSION__</Version>
<ReleaseDate>__DATE__</ReleaseDate>
<SortingPriority>80</SortingPriority>
<Script>installscript.qs</Script>
<Checkable>false</Checkable>
</Package>

View File

@ -23,6 +23,18 @@ Component.prototype.createOperations = function()
component.addOperation( "CreateShortcut", "@TargetDir@\\nheko.exe", "@DesktopDir@\\nheko.lnk",
"workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\nheko.exe",
"iconId=0", "description=Desktop client for the Matrix protocol");
var reg = installer.environmentVariable("SystemRoot") + "\\System32\\reg.exe";
var key= "HKEY_CLASSES_ROOT\\matrix";
component.addOperation("Execute", reg, "ADD", key, "/f", "/t", "REG_SZ", "/d", "URL:Matrix Protocol");
component.addOperation("Execute", reg, "ADD", key, "/f", "/v", "URL Protocol", "/t", "REG_SZ");
var iconkey = "HKEY_CLASSES_ROOT\\matrix\\DefaultIcon";
component.addOperation("Execute", reg, "ADD", iconkey, "/f", "/t", "REG_SZ", "/d", "@TargetDir@\\nheko.exe,1");
component.addOperation("Execute", reg, "ADD", "HKEY_CLASSES_ROOT\\matrix\\shell", "/f");
component.addOperation("Execute", reg, "ADD", "HKEY_CLASSES_ROOT\\matrix\\shell\\open", "/f");
var commandkey = "HKEY_CLASSES_ROOT\\matrix\\shell\\open\\command"
component.addOperation("Execute", reg, "ADD", commandkey, "/f");
component.addOperation("Execute", reg, "ADD", commandkey, "/f", "/t", "REG_SZ", "/d", "\"@TargetDir@\\nheko.exe\" \"%1\"");
}
}
catch( e )

View File

@ -1,269 +0,0 @@
{
"id": "io.github.NhekoReborn.Nheko",
"command": "nheko",
"runtime": "org.kde.Platform",
"runtime-version": "5.15",
"sdk": "org.kde.Sdk",
"rename-icon": "nheko",
"rename-desktop-file": "nheko.desktop",
"rename-appdata-file": "nheko.appdata.xml",
"finish-args": [
"--device=dri",
"--share=ipc",
"--share=network",
"--socket=pulseaudio",
"--socket=wayland",
"--socket=x11",
"--talk-name=org.freedesktop.Notifications",
"--talk-name=org.kde.StatusNotifierWatcher"
],
"cleanup": [
"/include",
"/bin/mdb*",
"*.a"
],
"build-options" : {
"arch": {
"aarch64": {
"cxxflags": "-DBOOST_ASIO_DISABLE_EPOLL"
}
}
},
"modules": [
{
"name": "lmdb",
"sources": [
{
"sha256": "f3927859882eb608868c8c31586bb7eb84562a40a6bf5cc3e13b6b564641ea28",
"type": "archive",
"url": "https://github.com/LMDB/lmdb/archive/LMDB_0.9.22.tar.gz"
}
],
"make-install-args": [
"prefix=/app"
],
"no-autogen": true,
"subdir": "libraries/liblmdb"
},
{
"name": "cmark",
"buildsystem": "cmake-ninja",
"builddir": true,
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DCMARK_TESTS=OFF"
],
"sources": [
{
"sha256": "2558ace3cbeff85610de3bda32858f722b359acdadf0c4691851865bb84924a6",
"type": "archive",
"url": "https://github.com/commonmark/cmark/archive/0.29.0.tar.gz"
}
]
},
{
"name": "spdlog",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DSPDLOG_BUILD_EXAMPLES=0",
"-DSPDLOG_BUILD_BENCH=0",
"-DSPDLOG_BUILD_TESTING=0"
],
"sources": [
{
"sha256": "5197b3147cfcfaa67dd564db7b878e4a4b3d9f3443801722b3915cdeced656cb",
"type": "archive",
"url": "https://github.com/gabime/spdlog/archive/v1.8.1.tar.gz"
}
]
},
{
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release"
],
"buildsystem": "cmake-ninja",
"name": "olm",
"sources": [
{
"commit": "6753595300767dd70150831dbbe6f92d64e75038",
"disable-shallow-clone": true,
"tag": "3.1.4",
"type": "git",
"url": "https://gitlab.matrix.org/matrix-org/olm.git"
}
]
},
{
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DBUILD_TEST_APPLICATION=OFF",
"-DQTKEYCHAIN_STATIC=ON"
],
"buildsystem": "cmake-ninja",
"name": "QtKeychain",
"sources": [
{
"commit": "815fe610353ff8ad7e2f1121c368a74df8db5eb7",
"tag": "v0.12.0",
"type": "git",
"url": "https://github.com/frankosterfeld/qtkeychain.git"
}
]
},
{
"config-opts":[
"-DJSON_BuildTests=OFF"
],
"buildsystem":"cmake",
"name": "nlohmann",
"sources":[
{
"sha256": "d51a3a8d3efbb1139d7608e28782ea9efea7e7933157e8ff8184901efd8ee760",
"type": "archive",
"url": "https://github.com/nlohmann/json/archive/v3.7.0.tar.gz"
}
]
},
{
"build-commands": [
"./bootstrap.sh --with-libraries=thread,system,iostreams --prefix=/app",
"./b2 -d0 variant=release link=static threading=multi --layout=system",
"./b2 -d0 install"
],
"buildsystem": "simple",
"name": "boost",
"sources": [
{
"sha256": "59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722",
"type": "archive",
"url": "https://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2"
}
]
},
{
"buildsystem": "meson",
"name": "gstreamer",
"sources": [
{
"commit": "a42fe476d3ee5576921f67a331464065ec33b9a4",
"tag": "1.18.3",
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git"
}
]
},
{
"config-opts": [
"-Dcompositor=enabled",
"-Dgl=enabled"
],
"buildsystem": "meson",
"name": "gstreamer-plugins-base",
"sources": [
{
"commit": "2cc319ee13f6b72df3d432b7c75aca81feb260e5",
"tag": "1.18.3",
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-base.git"
}
]
},
{
"config-opts": [
"-Dpulse=enabled",
"-Dqt5=enabled",
"-Drtp=enabled",
"-Drtpmanager=enabled",
"-Dvpx=enabled"
],
"buildsystem": "meson",
"name": "gstreamer-plugins-good",
"sources": [
{
"commit": "e816c6cd73c9e0676828c9e227a049ebad3d019f",
"tag": "1.18.3",
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-good.git"
}
]
},
{
"config-opts": [
"-Ddtls=enabled",
"-Dgl=enabled",
"-Dopenh264=enabled",
"-Dopus=enabled",
"-Dsrtp=enabled",
"-Dwebrtc=enabled",
"-Dflite=disabled"
],
"buildsystem": "meson",
"name": "gstreamer-plugins-bad",
"sources": [
{
"commit": "382e373d9be363f1e21b12990a4d12f1ecb6df41",
"tag": "1.18.3",
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad.git"
}
]
},
{
"config-opts": [
"-DBUILD_LIB_TESTS=OFF",
"-DBUILD_LIB_EXAMPLES=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DBUILD_SHARED_LIBS=OFF"
],
"buildsystem": "cmake-ninja",
"name": "mtxclient",
"sources": [
{
"commit": "2d6e3f79917ce2065a54ca32e6a9f9d42c0b6347",
"tag": "v0.4.0",
"type": "git",
"url": "https://github.com/Nheko-Reborn/mtxclient.git"
}
]
},
{
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DTWEENY_BUILD_DOCUMENTATION=OFF",
"-DTWEENY_BUILD_EXAMPLES=OFF"
],
"buildsystem": "cmake-ninja",
"name": "tweeny",
"sources": [
{
"sha256": "482857256a7235646004682912badb6521d361ed6987c8ebdae7986bf64ce694",
"type": "archive",
"url": "https://github.com/mobius3/tweeny/archive/43f4130f7e4a67c19d870b60864bc2862c19b81f.tar.gz"
}
]
},
{
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DLMDBXX_INCLUDE_DIR=.deps/lmdbxx",
"-DCOMPILE_QML=ON"
],
"buildsystem": "cmake-ninja",
"name": "nheko",
"sources": [
{
"path": ".",
"type": "dir",
"skip": ["build-flatpak"]
},
{
"dest": ".deps/lmdbxx",
"sha256": "93721132bbf5045d38ad62de2997655e9984c48ea5c9886746d42128f4b26fbd",
"type": "archive",
"url": "https://github.com/bendiken/lmdbxx/archive/0b43ca87d8cfabba392dfe884eb1edb83874de02.tar.gz"
}
]
}
]
}

View File

@ -0,0 +1,180 @@
id: io.github.NhekoReborn.Nheko
command: nheko
runtime: org.kde.Platform
runtime-version: '5.15'
sdk: org.kde.Sdk
rename-icon: nheko
rename-desktop-file: nheko.desktop
rename-appdata-file: nheko.appdata.xml
finish-args:
- --device=dri
# needed for webcams, see #517
- --device=all
- --share=ipc
- --share=network
- --socket=pulseaudio
- --socket=wayland
- --socket=x11
- --talk-name=org.freedesktop.Notifications
- --talk-name=org.freedesktop.secrets
- --talk-name=org.freedesktop.StatusNotifierItem
- --talk-name=org.kde.*
cleanup:
- /include
- /bin/mdb*
- '*.a'
build-options:
arch:
aarch64:
cxxflags: -DBOOST_ASIO_DISABLE_EPOLL
modules:
- name: lmdb
sources:
- sha256: f3927859882eb608868c8c31586bb7eb84562a40a6bf5cc3e13b6b564641ea28
type: archive
url: https://github.com/LMDB/lmdb/archive/LMDB_0.9.22.tar.gz
make-install-args:
- prefix=/app
no-autogen: true
subdir: libraries/liblmdb
- name: cmark
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DCMARK_TESTS=OFF
sources:
- sha256: 2558ace3cbeff85610de3bda32858f722b359acdadf0c4691851865bb84924a6
type: archive
url: https://github.com/commonmark/cmark/archive/0.29.0.tar.gz
- name: spdlog
buildsystem: cmake-ninja
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DSPDLOG_BUILD_EXAMPLES=0
- -DSPDLOG_BUILD_BENCH=0
- -DSPDLOG_BUILD_TESTING=0
sources:
- sha256: 5197b3147cfcfaa67dd564db7b878e4a4b3d9f3443801722b3915cdeced656cb
type: archive
url: https://github.com/gabime/spdlog/archive/v1.8.1.tar.gz
- config-opts:
- -DCMAKE_BUILD_TYPE=Release
buildsystem: cmake-ninja
name: olm
sources:
- commit: 6753595300767dd70150831dbbe6f92d64e75038
disable-shallow-clone: true
tag: 3.1.4
type: git
url: https://gitlab.matrix.org/matrix-org/olm.git
- config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_TEST_APPLICATION=OFF
- -DQTKEYCHAIN_STATIC=ON
buildsystem: cmake-ninja
name: QtKeychain
sources:
- commit: 815fe610353ff8ad7e2f1121c368a74df8db5eb7
tag: v0.12.0
type: git
url: https://github.com/frankosterfeld/qtkeychain.git
- config-opts:
- -DJSON_BuildTests=OFF
buildsystem: cmake
name: nlohmann
sources:
- sha256: d51a3a8d3efbb1139d7608e28782ea9efea7e7933157e8ff8184901efd8ee760
type: archive
url: https://github.com/nlohmann/json/archive/v3.7.0.tar.gz
- build-commands:
- ./bootstrap.sh --with-libraries=thread,system,iostreams --prefix=/app
- ./b2 -d0 variant=release link=static threading=multi --layout=system
- ./b2 -d0 install
buildsystem: simple
name: boost
sources:
- sha256: 59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722
type: archive
url: https://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2
- buildsystem: meson
name: gstreamer
sources:
- commit: a42fe476d3ee5576921f67a331464065ec33b9a4
tag: 1.18.3
type: git
url: https://gitlab.freedesktop.org/gstreamer/gstreamer.git
- config-opts:
- -Dcompositor=enabled
- -Dgl=enabled
buildsystem: meson
name: gstreamer-plugins-base
sources:
- commit: 2cc319ee13f6b72df3d432b7c75aca81feb260e5
tag: 1.18.3
type: git
url: https://gitlab.freedesktop.org/gstreamer/gst-plugins-base.git
- config-opts:
- -Dpulse=enabled
- -Dqt5=enabled
- -Drtp=enabled
- -Drtpmanager=enabled
- -Dvpx=enabled
buildsystem: meson
name: gstreamer-plugins-good
sources:
- commit: e816c6cd73c9e0676828c9e227a049ebad3d019f
tag: 1.18.3
type: git
url: https://gitlab.freedesktop.org/gstreamer/gst-plugins-good.git
- config-opts:
- -Ddtls=enabled
- -Dgl=enabled
- -Dopenh264=enabled
- -Dopus=enabled
- -Dsrtp=enabled
- -Dwebrtc=enabled
- -Dflite=disabled
buildsystem: meson
name: gstreamer-plugins-bad
sources:
- commit: 382e373d9be363f1e21b12990a4d12f1ecb6df41
tag: 1.18.3
type: git
url: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad.git
- config-opts:
- -DBUILD_LIB_TESTS=OFF
- -DBUILD_LIB_EXAMPLES=OFF
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_SHARED_LIBS=OFF
buildsystem: cmake-ninja
name: mtxclient
sources:
- commit: 5d2f055ea9403770039ddf66b1900f890cd5cde7
type: git
url: https://github.com/Nheko-Reborn/mtxclient.git
- config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DTWEENY_BUILD_DOCUMENTATION=OFF
- -DTWEENY_BUILD_EXAMPLES=OFF
buildsystem: cmake-ninja
name: tweeny
sources:
- sha256: 482857256a7235646004682912badb6521d361ed6987c8ebdae7986bf64ce694
type: archive
url: https://github.com/mobius3/tweeny/archive/43f4130f7e4a67c19d870b60864bc2862c19b81f.tar.gz
- config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DLMDBXX_INCLUDE_DIR=.deps/lmdbxx
- -DCOMPILE_QML=ON
buildsystem: cmake-ninja
name: nheko
sources:
- path: .
type: dir
skip:
- build-flatpak
- dest: .deps/lmdbxx
sha256: 5e12eb3aefe9050068af7df2c663edabc977ef34c9e7ba7b9d2c43e0ad47d8df
type: archive
url: https://github.com/hoytech/lmdbxx/archive/1.0.0.tar.gz

10
nheko-nightly.flatpakref Normal file
View File

@ -0,0 +1,10 @@
[Flatpak Ref]
Title=Nheko Nightly
Name=io.github.NhekoReborn.Nheko
Branch=master
Url=https://flatpak.neko.dev/repo/nightly
Homepage=https://nheko-reborn.github.io/
Icon=https://nheko.im/nheko-reborn/nheko/-/raw/master/resources/nheko.svg
RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo
IsRuntime=false
GPGKey=mDMEXENMphYJKwYBBAHaRw8BAQdAqn+Eo42lPoGpJ5HaOf4nFGfxR0QtOggJTCfsdbOyL4e0Kk5pY29sYXMgV2VybmVyIDxuaWNvbGFzLndlcm5lckBob3RtYWlsLmRlPoiWBBMWCAA+FiEE1YtGJCWmo3Elxv7bkgauGyMeBbsFAlxDTVUCGwMFCQtJjooFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQkgauGyMeBbs2rQD/dAEoOGT21BL85A8LmPK743EboBAjoRbWcI1hHnvS28AA/3b3HYGwgvTC6hQLyz75zjpeO5ZaUtbezRyDUR4xabMAtCROaWNvbGFzIFdlcm5lciA8bmljb2xhc0BuZWtvZGV2Lm5ldD6IlgQTFggAPhYhBNWLRiQlpqNxJcb+25IGrhsjHgW7BQJcQ01GAhsDBQkLSY6KBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEJIGrhsjHgW7GxwBANT4gL03Uu9N5KmTBihC7B71+0r7M/azPbUh86NthCeIAQCF2JXa0axBKhgQF5fWC5ncL+m8ZpH7C5rzDqVgO82WALQnTmljb2xhcyBXZXJuZXIgPG5pY29sYXMud2VybmVyQGdteC5uZXQ+iJYEExYIAD4WIQTVi0YkJaajcSXG/tuSBq4bIx4FuwUCXENMpgIbAwUJC0mOigULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCSBq4bIx4FuxU5APoCRDYlJW0oTsJs3lcTTB5Nsqb3X4iCEDCjIgsA3wtsIwEAlGBzD8ElCYi2+8m8esSRNlmpRcGoqgXbceLxPUXFpQu4OARcQ0ymEgorBgEEAZdVAQUBAQdAD8dBmT3iqrqdlxSw90L0SIH11fVxiX9MdWfBkTi6PzUDAQgHiH4EGBYIACYWIQTVi0YkJaajcSXG/tuSBq4bIx4FuwUCXENMpgIbDAUJC0mOigAKCRCSBq4bIx4Fu/LNAQDhH64IBic6h7H3uvtSAFT4xNn7Epobt2baIaDp7uKsQQEAyI+oc5dLknABwIOMrQQuZCmGejx9e4/8HEqLCdszhgG4MwRgNICHFgkrBgEEAdpHDwEBB0DR9eFFzfR62FIi7g+txcQenLvKFzhlyTz0wo3icOy6RYj1BBgWCAAmFiEE1YtGJCWmo3Elxv7bkgauGyMeBbsFAmA0gIcCGwIFCQlmAYAAgQkQkgauGyMeBbt2IAQZFggAHRYhBGz14re9h4cNPaFEKMjXXmEHc/LZBQJgNICHAAoJEMjXXmEHc/LZhVMBAPdYRspdeFh6E9BDxGubT705e/pZFdCHjCToDyxgdW5KAP9sU0hFI5VDHD1h98RzxSt7hc3jxyPSzbG1MBUJ9gbfCVhcAPsFfeZc3v5UBgmn4uICFEGjlzAWCQ7WctE6QTSkY5aL/wD9ETJH5lB+i/8km/sOBKQozXR0yHHw46gB6ZWMeN1wfgq4MwRgNPutFgkrBgEEAdpHDwEBB0APwMn0FJmnAds8IO8iCl/RHr7fz8xnpGd7E4zVgCNZpIj1BBgWCAAmFiEE1YtGJCWmo3Elxv7bkgauGyMeBbsFAmA0+60CGwIFCQANLwAAgQkQkgauGyMeBbt2IAQZFggAHRYhBAH7QBkzNfVIZJM93RNnXzGtBKQcBQJgNPutAAoJEBNnXzGtBKQcHnUA/0E2H5sxmfZ+EWFTso3X4NWu3uN2xF+MdNaY8C72f9H6AP91XaNmlB9gV61rg6wcB5E/j0998yWS9gltY1XY1ImqDPvlAP4sHFs5zuDazgKYxZ/kFhENCgEStdpnvJjt/DxmQPVT3AD/QK5vGoMTIeYjihv0QCnnRDfboTTZHlaEqJW8i02PQww=

View File

@ -0,0 +1,8 @@
[Flatpak Repo]
Title=Nheko Nightly
Url=https://flatpak.neko.dev/repo/nightly
Homepage=https://nheko.im/
Comment=Nheko nightly release repository
Description=Nheko nightly release repository
Icon=https://nheko.im/nheko-reborn/nheko/-/raw/master/resources/nheko.svg
GPGKey=mDMEXENMphYJKwYBBAHaRw8BAQdAqn+Eo42lPoGpJ5HaOf4nFGfxR0QtOggJTCfsdbOyL4e0Kk5pY29sYXMgV2VybmVyIDxuaWNvbGFzLndlcm5lckBob3RtYWlsLmRlPoiWBBMWCAA+FiEE1YtGJCWmo3Elxv7bkgauGyMeBbsFAlxDTVUCGwMFCQtJjooFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQkgauGyMeBbs2rQD/dAEoOGT21BL85A8LmPK743EboBAjoRbWcI1hHnvS28AA/3b3HYGwgvTC6hQLyz75zjpeO5ZaUtbezRyDUR4xabMAtCROaWNvbGFzIFdlcm5lciA8bmljb2xhc0BuZWtvZGV2Lm5ldD6IlgQTFggAPhYhBNWLRiQlpqNxJcb+25IGrhsjHgW7BQJcQ01GAhsDBQkLSY6KBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEJIGrhsjHgW7GxwBANT4gL03Uu9N5KmTBihC7B71+0r7M/azPbUh86NthCeIAQCF2JXa0axBKhgQF5fWC5ncL+m8ZpH7C5rzDqVgO82WALQnTmljb2xhcyBXZXJuZXIgPG5pY29sYXMud2VybmVyQGdteC5uZXQ+iJYEExYIAD4WIQTVi0YkJaajcSXG/tuSBq4bIx4FuwUCXENMpgIbAwUJC0mOigULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCSBq4bIx4FuxU5APoCRDYlJW0oTsJs3lcTTB5Nsqb3X4iCEDCjIgsA3wtsIwEAlGBzD8ElCYi2+8m8esSRNlmpRcGoqgXbceLxPUXFpQu4OARcQ0ymEgorBgEEAZdVAQUBAQdAD8dBmT3iqrqdlxSw90L0SIH11fVxiX9MdWfBkTi6PzUDAQgHiH4EGBYIACYWIQTVi0YkJaajcSXG/tuSBq4bIx4FuwUCXENMpgIbDAUJC0mOigAKCRCSBq4bIx4Fu/LNAQDhH64IBic6h7H3uvtSAFT4xNn7Epobt2baIaDp7uKsQQEAyI+oc5dLknABwIOMrQQuZCmGejx9e4/8HEqLCdszhgG4MwRgNICHFgkrBgEEAdpHDwEBB0DR9eFFzfR62FIi7g+txcQenLvKFzhlyTz0wo3icOy6RYj1BBgWCAAmFiEE1YtGJCWmo3Elxv7bkgauGyMeBbsFAmA0gIcCGwIFCQlmAYAAgQkQkgauGyMeBbt2IAQZFggAHRYhBGz14re9h4cNPaFEKMjXXmEHc/LZBQJgNICHAAoJEMjXXmEHc/LZhVMBAPdYRspdeFh6E9BDxGubT705e/pZFdCHjCToDyxgdW5KAP9sU0hFI5VDHD1h98RzxSt7hc3jxyPSzbG1MBUJ9gbfCVhcAPsFfeZc3v5UBgmn4uICFEGjlzAWCQ7WctE6QTSkY5aL/wD9ETJH5lB+i/8km/sOBKQozXR0yHHw46gB6ZWMeN1wfgq4MwRgNPutFgkrBgEEAdpHDwEBB0APwMn0FJmnAds8IO8iCl/RHr7fz8xnpGd7E4zVgCNZpIj1BBgWCAAmFiEE1YtGJCWmo3Elxv7bkgauGyMeBbsFAmA0+60CGwIFCQANLwAAgQkQkgauGyMeBbt2IAQZFggAHRYhBAH7QBkzNfVIZJM93RNnXzGtBKQcBQJgNPutAAoJEBNnXzGtBKQcHnUA/0E2H5sxmfZ+EWFTso3X4NWu3uN2xF+MdNaY8C72f9H6AP91XaNmlB9gV61rg6wcB5E/j0998yWS9gltY1XY1ImqDPvlAP4sHFs5zuDazgKYxZ/kFhENCgEStdpnvJjt/DxmQPVT3AD/QK5vGoMTIeYjihv0QCnnRDfboTTZHlaEqJW8i02PQww=

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2721
resources/langs/nheko_es.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,23 @@
</description>
<translation/>
<languages>
<lang>cs</lang>
<lang>de</lang>
<lang>el</lang>
<lang>en</lang>
<lang>eo</lang>
<lang>et</lang>
<lang>fi</lang>
<lang>fr</lang>
<lang>hu</lang>
<lang>it</lang>
<lang>ja</lang>
<lang>ml</lang>
<lang>nl</lang>
<lang>pl</lang>
<lang>ro</lang>
<lang>ru</lang>
<lang>sv</lang>
<lang>zh_CN</lang>
</languages>
<content_rating type="oars-1.0">
@ -43,6 +53,8 @@
<url type="homepage">https://github.com/Nheko-Reborn/nheko</url>
<update_contact>https://github.com/Nheko-Reborn</update_contact>
<releases>
<release date="2021-04-23" version="0.8.2"/>
<release date="2021-01-27" version="0.8.1"/>
<release date="2021-01-21" version="0.8.0"/>
<release date="2020-06-12" version="0.7.2"/>
<release date="2020-04-24" version="0.7.1"/>
@ -57,12 +69,11 @@
<release date="2018-08-12" version="0.5.3"/>
<release date="2018-07-28" version="0.5.2"/>
</releases>
<developer_name>Nheko Reborn</developer_name>
<url type="bugtracker">https://github.com/Nheko-Reborn/nheko/issues</url>
<url type="help">https://github.com/Nheko-Reborn/nheko/</url>
<url type="translate">https://weblate.nheko.im/projects/nheko/</url>
<!--FIXME: where to donate to the application-->
<url type="donation"/>
</component>

View File

@ -2,7 +2,7 @@
Name=nheko
Version=1.0
Comment=Desktop client for Matrix
Exec=nheko
Exec=nheko %u
Icon=nheko
Type=Application
Categories=Network;InstantMessaging;Qt;

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./ui"
import QtGraphicalEffects 1.0
import QtQuick 2.6
@ -57,6 +61,7 @@ Rectangle {
}
layer.effect: OpacityMask {
cached: true
maskSource: Rectangle {
anchors.fill: parent
@ -90,4 +95,9 @@ Rectangle {
}
}
CursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./ui"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -11,8 +15,16 @@ Popup {
property string completerName
property var completer
property bool bottomToTop: true
property bool fullWidth: false
property bool centerRowContent: true
property int avatarHeight: 24
property int avatarWidth: 24
property int rowMargin: 0
property int rowSpacing: 5
property alias count: listView.count
signal completionClicked(string completion)
signal completionSelected(string id)
function up() {
if (bottomToTop)
@ -49,9 +61,18 @@ Popup {
return null;
}
function finishCompletion() {
if (popup.completerName == "room")
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
}
onCompleterNameChanged: {
if (completerName) {
completer = TimelineManager.timeline.input.completerFor(completerName);
if (completerName == "user")
completer = TimelineManager.completerFor(completerName, TimelineManager.timeline.roomId());
else
completer = TimelineManager.completerFor(completerName);
completer.setSearchString("");
} else {
completer = undefined;
@ -70,22 +91,31 @@ Popup {
id: listView
anchors.fill: parent
implicitWidth: contentItem.childrenRect.width
implicitWidth: fullWidth ? parent.width : contentItem.childrenRect.width
model: completer
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
spacing: rowSpacing
pixelAligned: true
delegate: Rectangle {
property variant modelData: model
color: model.index == popup.currentIndex ? colors.highlight : colors.base
height: chooser.childrenRect.height + 4
implicitWidth: chooser.childrenRect.width + 4
height: chooser.childrenRect.height + 2 * popup.rowMargin
implicitWidth: fullWidth ? popup.width : chooser.childrenRect.width + 4
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: popup.currentIndex = model.index
onClicked: popup.completionClicked(completer.completionAt(model.index))
onPositionChanged: popup.currentIndex = model.index
onClicked: {
popup.completionClicked(completer.completionAt(model.index));
if (popup.completerName == "room")
popup.completionSelected(model.roomid);
}
Ripple {
rippleTarget: mouseArea
@ -98,7 +128,8 @@ Popup {
id: chooser
roleValue: popup.completerName
anchors.centerIn: parent
anchors.fill: parent
anchors.margins: popup.rowMargin
DelegateChoice {
roleValue: "user"
@ -107,10 +138,11 @@ Popup {
id: del
anchors.centerIn: parent
spacing: rowSpacing
Avatar {
height: 24
width: 24
height: popup.avatarHeight
width: popup.avatarWidth
displayName: model.displayName
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
onClicked: popup.completionClicked(completer.completionAt(model.index))
@ -137,6 +169,7 @@ Popup {
id: del
anchors.centerIn: parent
spacing: rowSpacing
Label {
text: model.unicode
@ -153,6 +186,67 @@ Popup {
}
DelegateChoice {
roleValue: "room"
RowLayout {
id: del
anchors.centerIn: centerRowContent ? parent : undefined
spacing: rowSpacing
Avatar {
height: popup.avatarHeight
width: popup.avatarWidth
displayName: model.roomName
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
onClicked: {
popup.completionClicked(completer.completionAt(model.index));
popup.completionSelected(model.roomid);
}
}
Label {
text: model.roomName
font.pixelSize: popup.avatarHeight * 0.5
color: model.index == popup.currentIndex ? colors.highlightedText : colors.text
}
}
}
DelegateChoice {
roleValue: "roomAliases"
RowLayout {
id: del
anchors.centerIn: parent
spacing: rowSpacing
Avatar {
height: popup.avatarHeight
width: popup.avatarWidth
displayName: model.roomName
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
onClicked: popup.completionClicked(completer.completionAt(model.index))
}
Label {
text: model.roomName
color: model.index == popup.currentIndex ? colors.highlightedText : colors.text
}
Label {
text: "(" + model.roomAlias + ")"
color: model.index == popup.currentIndex ? colors.highlightedText : colors.buttonText
}
}
}
}
}

View File

@ -1,44 +1,50 @@
import QtQuick 2.5
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Controls 2.1
import im.nheko 1.0
Rectangle {
id: indicator
Image {
id: stateImg
property bool encrypted: false
property int trust: Crypto.Unverified
function getEncryptionImage() {
if (encrypted)
return "image://colorimage/:/icons/icons/ui/lock.png?" + colors.buttonText;
else
return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d";
}
function getEncryptionTooltip() {
if (encrypted)
return qsTr("Encrypted");
else
return qsTr("This message is not encrypted!");
}
color: "transparent"
width: 16
height: 16
ToolTip.visible: ma.containsMouse && indicator.visible
ToolTip.text: getEncryptionTooltip()
source: {
if (encrypted) {
switch (trust) {
case Crypto.Verified:
return "image://colorimage/:/icons/icons/ui/lock.png?green";
case Crypto.TOFU:
return "image://colorimage/:/icons/icons/ui/lock.png?" + colors.buttonText;
default:
return "image://colorimage/:/icons/icons/ui/lock.png?#dd3d3d";
}
} else {
return "image://colorimage/:/icons/icons/ui/unlock.png?#dd3d3d";
}
}
ToolTip.visible: ma.hovered
ToolTip.text: {
if (!encrypted)
return qsTr("This message is not encrypted!");
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
switch (trust) {
case Crypto.Verified:
return qsTr("Encrypted by a verified device");
case Crypto.TOFU:
return qsTr("Encrypted by an unverified device, but you have trusted that user so far.");
default:
return qsTr("Encrypted by an unverified device");
}
}
Image {
id: stateImg
anchors.fill: parent
source: getEncryptionImage()
HoverHandler {
id: ma
}
}

View File

@ -0,0 +1,117 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./delegates/"
import QtQuick 2.9
import QtQuick.Controls 2.3
import im.nheko 1.0
Popup {
id: forwardMessagePopup
property var mid
function setMessageEventId(mid_in) {
mid = mid_in;
}
x: Math.round(parent.width / 2 - width / 2)
y: Math.round(parent.height / 2 - height / 2)
modal: true
palette: colors
parent: Overlay.overlay
width: implicitWidth >= (timelineRoot.width * 0.8) ? implicitWidth : (timelineRoot.width * 0.8)
height: implicitHeight + completerPopup.height + padding * 2
leftPadding: 10
rightPadding: 10
onOpened: {
completerPopup.open();
roomTextInput.forceActiveFocus();
}
onClosed: {
completerPopup.close();
}
Column {
id: forwardColumn
spacing: 5
Label {
id: titleLabel
text: qsTr("Forward Message")
font.bold: true
bottomPadding: 10
color: colors.text
}
Reply {
id: replyPreview
modelData: TimelineManager.timeline ? TimelineManager.timeline.getDump(mid, "") : {
}
userColor: TimelineManager.userColor(modelData.userId, colors.window)
}
MatrixTextField {
id: roomTextInput
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
color: colors.text
onTextEdited: {
completerPopup.completer.searchString = text;
}
Keys.onPressed: {
if (event.key == Qt.Key_Up && completerPopup.opened) {
event.accepted = true;
completerPopup.up();
} else if (event.key == Qt.Key_Down && completerPopup.opened) {
event.accepted = true;
completerPopup.down();
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
completerPopup.finishCompletion();
event.accepted = true;
}
}
}
}
Completer {
id: completerPopup
y: titleLabel.height + replyPreview.height + roomTextInput.height + roomTextInput.bottomPadding + forwardColumn.spacing * 3
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
completerName: "room"
fullWidth: true
centerRowContent: false
avatarHeight: 24
avatarWidth: 24
bottomToTop: false
closePolicy: Popup.NoAutoClose
}
Connections {
onCompletionSelected: {
TimelineManager.timeline.forwardMessage(messageContextMenu.eventId, id);
forwardMessagePopup.close();
}
onCountChanged: {
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
completerPopup.currentIndex = 0;
}
target: completerPopup
}
background: Rectangle {
color: colors.window
}
Overlay.modal: Rectangle {
color: Qt.rgba(colors.window.r, colors.window.g, colors.window.b, 0.7)
}
}

View File

@ -1,6 +1,11 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./ui"
import QtQuick 2.3
import QtQuick.Controls 2.3
import im.nheko 1.0 // for cursor shape
AbstractButton {
id: button
@ -23,11 +28,10 @@ AbstractButton {
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
}
MouseArea {
CursorShape {
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.5
import QtQuick.Controls 2.3
import im.nheko 1.0
@ -8,29 +12,15 @@ TextEdit {
focus: false
wrapMode: Text.Wrap
selectByMouse: !Settings.mobileMode
enabled: selectByMouse
color: colors.text
onLinkActivated: {
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) {
chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1]);
} else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) {
TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]);
} else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) {
var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link);
TimelineManager.setHistoryView(match[1]);
chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain);
} else {
TimelineManager.openLink(link);
}
}
onLinkActivated: TimelineManager.openLink(link)
ToolTip.visible: hoveredLink
ToolTip.text: hoveredLink
MouseArea {
id: ma
CursorShape {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}

View File

@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
TextField {
id: input
palette: colors
Rectangle {
id: blueBar
anchors.top: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
color: colors.highlight
height: 1
width: parent.width
Rectangle {
id: blackBar
anchors.verticalCenter: blueBar.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
height: parent.height + 1
width: 0
color: colors.text
states: State {
name: "focused"
when: input.activeFocus == true
PropertyChanges {
target: blackBar
width: blueBar.width
}
}
transitions: Transition {
from: ""
to: "focused"
reversible: true
NumberAnimation {
target: blackBar
properties: "width"
duration: 500
easing.type: Easing.InOutQuad
alwaysRunToEnd: true
}
}
}
}
background: Rectangle {
color: colors.base
}
}

View File

@ -1,14 +1,20 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./voip"
import QtQuick 2.9
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
import im.nheko 1.0
Rectangle {
id: inputBar
color: colors.window
Layout.fillWidth: true
Layout.preferredHeight: textInput.height + 16
Layout.preferredHeight: row.implicitHeight
Layout.minimumHeight: 40
Component {
@ -20,11 +26,10 @@ Rectangle {
}
RowLayout {
id: inputBar
id: row
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false) || messageContextMenu.isSender
anchors.fill: parent
anchors.margins: 8
spacing: 16
ImageButton {
visible: CallManager.callsSupported
@ -36,7 +41,7 @@ Rectangle {
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.png" : ":/icons/icons/ui/place-call.png"
ToolTip.visible: hovered
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call")
Layout.leftMargin: 8
Layout.margins: 8
onClicked: {
if (TimelineManager.timeline) {
if (CallManager.haveCallInvite) {
@ -44,7 +49,6 @@ Rectangle {
} else if (CallManager.isOnCall) {
CallManager.hangUp();
} else {
CallManager.refreshDevices();
var dialog = placeCallDialog.createObject(timelineRoot);
dialog.open();
}
@ -58,7 +62,7 @@ Rectangle {
width: 22
height: 22
image: ":/icons/icons/ui/paper-clip-outline.png"
Layout.leftMargin: CallManager.callsSupported ? 0 : 8
Layout.margins: 8
onClicked: TimelineManager.timeline.input.openFileSelection()
ToolTip.visible: hovered
ToolTip.text: qsTr("Send a file")
@ -77,71 +81,53 @@ Rectangle {
}
Flickable {
ScrollView {
id: textInput
function ensureVisible(r) {
if (contentX >= r.x)
contentX = r.x;
else if (contentX + width <= r.x + r.width)
contentX = r.x + r.width - width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY + height <= r.y + r.height)
contentY = r.y + r.height - height;
}
Layout.alignment: Qt.AlignBottom
Layout.alignment: Qt.AlignBottom // | Qt.AlignHCenter
Layout.maximumHeight: Window.height / 4
Layout.minimumHeight: Settings.fontSize
Layout.fillWidth: true
clip: true
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick
implicitWidth: textArea.width
implicitHeight: textArea.height
contentWidth: textArea.width
contentHeight: textArea.height
implicitWidth: inputBar.width - 4 * (22 + 16) - 24
TextArea {
id: textArea
id: messageInput
property int completerTriggeredAt: -1
function insertCompletion(completion) {
textArea.remove(completerTriggeredAt, cursorPosition);
textArea.insert(cursorPosition, completion);
messageInput.remove(completerTriggeredAt, cursorPosition);
messageInput.insert(cursorPosition, completion);
}
function openCompleter(pos, type) {
completerTriggeredAt = pos;
popup.completerName = type;
popup.open();
popup.completer.setSearchString(textArea.getText(completerTriggeredAt, cursorPosition));
popup.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition));
}
function positionCursorAtEnd() {
cursorPosition = messageInput.length;
}
function positionCursorAtStart() {
cursorPosition = 0;
}
text: "asfkajsdf"
selectByMouse: true
placeholderText: qsTr("Write a message...")
//placeholderTextColor: colors.buttonText
// only set the anchors on Qt 5.12 or higher
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
Component.onCompleted: {
if (placeholderTextColor !== undefined)
placeholderTextColor = colors.buttonText;
}
placeholderTextColor: colors.buttonText
color: colors.text
width: textInput.width
wrapMode: TextEdit.Wrap
padding: 0
padding: 8
focus: true
onTextChanged: {
if (TimelineManager.timeline)
TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
forceActiveFocus();
}
onCursorRectangleChanged: textInput.ensureVisible(cursorRectangle)
onCursorPositionChanged: {
if (!TimelineManager.timeline)
return ;
@ -152,7 +138,7 @@ Rectangle {
popup.close();
}
if (popup.opened)
popup.completer.setSearchString(textArea.getText(completerTriggeredAt, cursorPosition));
popup.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition));
}
onSelectionStartChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
@ -163,17 +149,28 @@ Rectangle {
if (event.matches(StandardKey.Paste)) {
TimelineManager.timeline.input.paste(false);
event.accepted = true;
} else if (event.key == Qt.Key_Space) {
// close popup if user enters space after colon
if (cursorPosition == completerTriggeredAt + 1)
popup.close();
if (popup.opened && popup.count <= 0)
popup.close();
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
textArea.clear();
messageInput.clear();
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
textArea.text = TimelineManager.timeline.input.previousText();
messageInput.text = TimelineManager.timeline.input.previousText();
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_N) {
textArea.text = TimelineManager.timeline.input.nextText();
messageInput.text = TimelineManager.timeline.input.nextText();
} else if (event.key == Qt.Key_At) {
textArea.openCompleter(cursorPosition, "user");
messageInput.openCompleter(cursorPosition, "user");
popup.open();
} else if (event.key == Qt.Key_Colon) {
textArea.openCompleter(cursorPosition, "emoji");
messageInput.openCompleter(cursorPosition, "emoji");
popup.open();
} else if (event.key == Qt.Key_NumberSign) {
messageInput.openCompleter(cursorPosition, "roomAliases");
popup.open();
} else if (event.key == Qt.Key_Escape && popup.opened) {
completerTriggeredAt = -1;
@ -186,13 +183,12 @@ Rectangle {
popup.completerName = "";
popup.close();
if (currentCompletion) {
textArea.insertCompletion(currentCompletion);
messageInput.insertCompletion(currentCompletion);
event.accepted = true;
return ;
}
}
TimelineManager.timeline.input.send();
textArea.clear();
event.accepted = true;
} else if (event.key == Qt.Key_Tab) {
event.accepted = true;
@ -201,19 +197,22 @@ Rectangle {
} else {
var pos = cursorPosition - 1;
while (pos > -1) {
var t = textArea.getText(pos, pos + 1);
var t = messageInput.getText(pos, pos + 1);
console.log('"' + t + '"');
if (t == '@' || t == ' ' || t == '\t') {
textArea.openCompleter(pos, "user");
if (t == '@') {
messageInput.openCompleter(pos, "user");
return ;
} else if (t == ' ' || t == '\t') {
messageInput.openCompleter(pos + 1, "user");
return ;
} else if (t == ':') {
textArea.openCompleter(pos, "emoji");
messageInput.openCompleter(pos, "emoji");
return ;
}
pos = pos - 1;
}
// At start of input
textArea.openCompleter(0, "user");
messageInput.openCompleter(0, "user");
}
} else if (event.key == Qt.Key_Up && popup.opened) {
event.accepted = true;
@ -221,38 +220,93 @@ Rectangle {
} else if (event.key == Qt.Key_Down && popup.opened) {
event.accepted = true;
popup.down();
} else if (event.key == Qt.Key_Up && event.modifiers == Qt.NoModifier) {
if (cursorPosition == 0) {
event.accepted = true;
var idx = TimelineManager.timeline.edit ? TimelineManager.timeline.idToIndex(TimelineManager.timeline.edit) + 1 : 0;
while (true) {
var id = TimelineManager.timeline.indexToId(idx);
if (!id || TimelineManager.timeline.getDump(id, "").isEditable) {
TimelineManager.timeline.edit = id;
cursorPosition = 0;
Qt.callLater(positionCursorAtEnd);
break;
}
idx++;
}
} else if (positionAt(0, cursorRectangle.y) === 0) {
event.accepted = true;
positionCursorAtStart();
}
} else if (event.key == Qt.Key_Down && event.modifiers == Qt.NoModifier) {
if (cursorPosition == messageInput.length && TimelineManager.timeline.edit) {
event.accepted = true;
var idx = TimelineManager.timeline.idToIndex(TimelineManager.timeline.edit) - 1;
while (true) {
var id = TimelineManager.timeline.indexToId(idx);
if (!id || TimelineManager.timeline.getDump(id, "").isEditable) {
TimelineManager.timeline.edit = id;
Qt.callLater(positionCursorAtStart);
break;
}
idx--;
}
} else if (positionAt(width, cursorRectangle.y + 2) === messageInput.length) {
event.accepted = true;
positionCursorAtEnd();
}
}
}
background: null
Connections {
onTimelineChanged: {
textArea.clear();
textArea.append(TimelineManager.timeline.input.text());
textArea.completerTriggeredAt = -1;
onActiveTimelineChanged: {
messageInput.clear();
messageInput.append(TimelineManager.timeline.input.text());
messageInput.completerTriggeredAt = -1;
popup.completerName = "";
messageInput.forceActiveFocus();
}
target: TimelineManager
}
Connections {
onCompletionClicked: textArea.insertCompletion(completion)
onCompletionClicked: messageInput.insertCompletion(completion)
target: popup
}
Completer {
id: popup
x: textArea.completerTriggeredAt >= 0 ? textArea.positionToRectangle(textArea.completerTriggeredAt).x : 0
y: textArea.completerTriggeredAt >= 0 ? textArea.positionToRectangle(textArea.completerTriggeredAt).y - height : 0
x: messageInput.completerTriggeredAt >= 0 ? messageInput.positionToRectangle(messageInput.completerTriggeredAt).x : 0
y: messageInput.completerTriggeredAt >= 0 ? messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height : 0
}
Connections {
ignoreUnknownSignals: true
onInsertText: textArea.insert(textArea.cursorPosition, text)
onInsertText: {
messageInput.remove(messageInput.selectionStart, messageInput.selectionEnd);
messageInput.insert(messageInput.cursorPosition, text);
}
onTextChanged: {
messageInput.text = newText;
messageInput.cursorPosition = newText.length;
}
target: TimelineManager.timeline ? TimelineManager.timeline.input : null
}
Connections {
ignoreUnknownSignals: true
onReplyChanged: messageInput.forceActiveFocus()
onEditChanged: messageInput.forceActiveFocus()
target: TimelineManager.timeline
}
Connections {
target: TimelineManager
onFocusInput: messageInput.forceActiveFocus()
}
MouseArea {
// workaround for wrong cursor shape on some platforms
anchors.fill: parent
@ -261,14 +315,6 @@ Rectangle {
onClicked: TimelineManager.timeline.input.paste(true)
}
NhekoDropArea {
anchors.fill: parent
roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : ""
}
}
ScrollBar.vertical: ScrollBar {
}
}
@ -277,6 +323,7 @@ Rectangle {
id: emojiButton
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.margins: 8
hoverEnabled: true
width: 22
height: 22
@ -284,12 +331,14 @@ Rectangle {
ToolTip.visible: hovered
ToolTip.text: qsTr("Emoji")
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, function(emoji) {
textArea.insert(textArea.cursorPosition, emoji);
messageInput.insert(messageInput.cursorPosition, emoji);
TimelineManager.focusMessageInput();
})
}
ImageButton {
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.margins: 8
hoverEnabled: true
width: 22
height: 22
@ -299,10 +348,16 @@ Rectangle {
ToolTip.text: qsTr("Send")
onClicked: {
TimelineManager.timeline.input.send();
textArea.clear();
}
}
}
Text {
anchors.centerIn: parent
visible: TimelineManager.timeline ? (!TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage)) : false
text: qsTr("You don't have permission to send messages in this room")
color: colors.text
}
}

View File

@ -1,192 +1,416 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./delegates"
import "./emoji"
import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
import im.nheko 1.0
ListView {
id: chat
ScrollView {
clip: false
palette: colors
padding: 8
ScrollBar.horizontal.visible: false
property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > scrollbar.width * 2) ? Settings.timelineMaxWidth : (parent.width - scrollbar.width * 2 - 8)
ListView {
id: chat
Layout.fillWidth: true
Layout.fillHeight: true
cacheBuffer: 400
model: TimelineManager.timeline
boundsBehavior: Flickable.StopAtBounds
pixelAligned: true
spacing: 4
verticalLayoutDirection: ListView.BottomToTop
onCountChanged: {
// Mark timeline as read
if (atYEnd)
model.currentIndex = 0;
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < parent.availableWidth) ? Settings.timelineMaxWidth : parent.availableWidth) - parent.padding * 2
}
model: TimelineManager.timeline
boundsBehavior: Flickable.StopAtBounds
pixelAligned: true
spacing: 4
verticalLayoutDirection: ListView.BottomToTop
onCountChanged: {
// Mark timeline as read
if (atYEnd)
model.currentIndex = 0;
ScrollHelper {
flickable: parent
anchors.fill: parent
enabled: !Settings.mobileMode
}
Shortcut {
sequence: StandardKey.MoveToPreviousPage
onActivated: {
chat.contentY = chat.contentY - chat.height / 2;
chat.returnToBounds();
}
}
Shortcut {
sequence: StandardKey.MoveToNextPage
onActivated: {
chat.contentY = chat.contentY + chat.height / 2;
chat.returnToBounds();
}
}
Rectangle {
//closePolicy: Popup.NoAutoClose
Shortcut {
sequence: StandardKey.Cancel
onActivated: chat.model.reply = undefined
}
id: messageActions
Shortcut {
sequence: "Alt+Up"
onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0)
}
property Item attached: null
property alias model: row.model
// use comma to update on scroll
property var attachedPos: chat.contentY, attached ? chat.mapFromItem(attached, attached ? attached.width - width : 0, -height) : null
readonly property int padding: 4
Shortcut {
sequence: "Alt+Down"
onActivated: {
var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1;
chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : undefined;
}
}
visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || messageActionHover.hovered)
x: attached ? attachedPos.x : 0
y: attached ? attachedPos.y : 0
z: 10
height: row.implicitHeight + padding * 2
width: row.implicitWidth + padding * 2
color: colors.window
border.color: colors.buttonText
border.width: 1
radius: padding
Component {
id: sectionHeader
HoverHandler {
id: messageActionHover
Column {
topPadding: 4
bottomPadding: 4
spacing: 8
visible: modelData && (modelData.previousMessageUserId !== modelData.userId || modelData.previousMessageDay !== modelData.day)
width: parentWidth
height: ((modelData && modelData.previousMessageDay !== modelData.day) ? dateBubble.height + 8 + userName.height : userName.height) + 8
grabPermissions: PointerHandler.CanTakeOverFromAnything
}
Label {
id: dateBubble
Row {
id: row
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
visible: modelData && modelData.previousMessageDay !== modelData.day
text: modelData ? chat.model.formatDateSeparator(modelData.timestamp) : ""
color: colors.text
height: Math.round(fontMetrics.height * 1.4)
width: contentWidth * 1.2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
property var model
background: Rectangle {
radius: parent.height / 2
color: colors.window
anchors.centerIn: parent
spacing: messageActions.padding
ImageButton {
id: editButton
visible: !!row.model && row.model.isEditable
buttonTextColor: colors.buttonText
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/edit.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Edit")
onClicked: {
if (row.model.isEditable)
chat.model.editAction(row.model.id);
}
}
EmojiButton {
id: reactButton
visible: chat.model ? chat.model.permissions.canSend(MtxEvent.Reaction) : false
width: 16
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("React")
emojiPicker: emojiPopup
event_id: row.model ? row.model.id : ""
}
ImageButton {
id: replyButton
visible: chat.model ? chat.model.permissions.canSend(MtxEvent.TextMessage) : false
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/mail-reply.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Reply")
onClicked: chat.model.replyAction(row.model.id)
}
ImageButton {
id: optionsButton
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/vertical-ellipsis.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Options")
onClicked: messageContextMenu.show(row.model.id, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
}
}
Row {
height: userName.height
spacing: 8
}
Avatar {
width: avatarSize
height: avatarSize
url: modelData ? chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/") : ""
displayName: modelData ? modelData.userName : ""
userid: modelData ? modelData.userId : ""
onClicked: chat.model.openUserProfile(modelData.userId)
ScrollHelper {
flickable: parent
anchors.fill: parent
enabled: !Settings.mobileMode
}
Shortcut {
sequence: StandardKey.MoveToPreviousPage
onActivated: {
chat.contentY = chat.contentY - chat.height / 2;
chat.returnToBounds();
}
}
Shortcut {
sequence: StandardKey.MoveToNextPage
onActivated: {
chat.contentY = chat.contentY + chat.height / 2;
chat.returnToBounds();
}
}
Shortcut {
sequence: StandardKey.Cancel
onActivated: {
if (chat.model.reply)
chat.model.reply = undefined;
else
chat.model.edit = undefined;
}
}
Shortcut {
sequence: "Alt+Up"
onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0)
}
Shortcut {
sequence: "Alt+Down"
onActivated: {
var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1;
chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : null;
}
}
Shortcut {
sequence: "Alt+F"
onActivated: {
if (chat.model.reply) {
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
forwardMess.setMessageEventId(chat.model.reply);
forwardMess.open();
chat.model.reply = null;
}
}
}
Shortcut {
sequence: "Ctrl+E"
onActivated: {
chat.model.edit = chat.model.reply;
}
}
Connections {
target: TimelineManager
onFocusChanged: readTimer.running = TimelineManager.isWindowFocused
}
Timer {
id: readTimer
// force current read index to update
onTriggered: chat.model.setCurrentIndex(chat.model.currentIndex)
interval: 1000
}
Component {
id: sectionHeader
Column {
topPadding: 4
bottomPadding: 4
spacing: 8
visible: modelData && (modelData.previousMessageUserId !== modelData.userId || modelData.previousMessageDay !== modelData.day)
width: parentWidth
height: ((modelData && modelData.previousMessageDay !== modelData.day) ? dateBubble.height + 8 + userName.height : userName.height) + 8
Label {
id: userName
id: dateBubble
text: modelData ? TimelineManager.escapeEmoji(modelData.userName) : ""
color: TimelineManager.userColor(modelData ? modelData.userId : "", colors.window)
textFormat: Text.RichText
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
visible: modelData && modelData.previousMessageDay !== modelData.day
text: modelData ? chat.model.formatDateSeparator(modelData.timestamp) : ""
color: colors.text
height: Math.round(fontMetrics.height * 1.4)
width: contentWidth * 1.2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
MouseArea {
anchors.fill: parent
Layout.alignment: Qt.AlignHCenter
onClicked: chat.model.openUserProfile(modelData.userId)
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: true
background: Rectangle {
radius: parent.height / 2
color: colors.window
}
}
Label {
color: colors.buttonText
text: modelData ? TimelineManager.userStatus(modelData.userId) : ""
textFormat: Text.PlainText
elide: Text.ElideRight
width: chat.delegateMaxWidth - parent.spacing * 2 - userName.implicitWidth - avatarSize
font.italic: true
Row {
height: userName.height
spacing: 8
Avatar {
id: messageUserAvatar
width: avatarSize
height: avatarSize
url: modelData ? chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/") : ""
displayName: modelData ? modelData.userName : ""
userid: modelData ? modelData.userId : ""
onClicked: chat.model.openUserProfile(modelData.userId)
ToolTip.visible: avatarHover.hovered
ToolTip.text: userid
HoverHandler {
id: avatarHover
}
}
Connections {
target: chat.model
onRoomAvatarUrlChanged: {
messageUserAvatar.url = modelData ? chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/") : "";
}
onScrollToIndex: chat.positionViewAtIndex(index, ListView.Visible)
}
Label {
id: userName
text: modelData ? TimelineManager.escapeEmoji(modelData.userName) : ""
color: TimelineManager.userColor(modelData ? modelData.userId : "", colors.window)
textFormat: Text.RichText
ToolTip.visible: displayNameHover.hovered
ToolTip.text: modelData ? modelData.userId : ""
TapHandler {
onSingleTapped: chat.model.openUserProfile(modelData.userId)
}
CursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
HoverHandler {
id: displayNameHover
}
}
Label {
color: colors.buttonText
text: modelData ? TimelineManager.userStatus(modelData.userId) : ""
textFormat: Text.PlainText
elide: Text.ElideRight
width: chat.delegateMaxWidth - parent.spacing * 2 - userName.implicitWidth - avatarSize
font.italic: true
}
}
}
}
}
delegate: Item {
id: wrapper
ScrollBar.vertical: ScrollBar {
id: scrollbar
}
property bool scrolledToThis: model.id === chat.model.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
delegate: Item {
id: wrapper
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
width: chat.delegateMaxWidth
height: section ? section.height + timelinerow.height : timelinerow.height
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
width: chat.delegateMaxWidth
height: section ? section.height + timelinerow.height : timelinerow.height
Rectangle {
id: scrollHighlight
Loader {
id: section
opacity: 0
visible: true
anchors.fill: timelinerow
color: colors.highlight
property var modelData: model
property int parentWidth: parent.width
states: State {
name: "revealed"
when: wrapper.scrolledToThis
}
active: model.previousMessageUserId !== undefined && model.previousMessageUserId !== model.userId || model.previousMessageDay !== model.day
//asynchronous: true
sourceComponent: sectionHeader
visible: status == Loader.Ready
}
transitions: Transition {
from: ""
to: "revealed"
TimelineRow {
id: timelinerow
SequentialAnimation {
PropertyAnimation {
target: scrollHighlight
properties: "opacity"
easing.type: Easing.InOutQuad
from: 0
to: 1
duration: 500
}
y: section.active && section.visible ? section.y + section.height : 0
}
PropertyAnimation {
target: scrollHighlight
properties: "opacity"
easing.type: Easing.InOutQuad
from: 1
to: 0
duration: 500
}
Connections {
target: chat
onMovementEnded: {
if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
chat.model.currentIndex = index;
ScriptAction {
script: chat.model.eventShown()
}
}
}
}
Loader {
id: section
property var modelData: model
property int parentWidth: parent.width
active: model.previousMessageUserId !== undefined && model.previousMessageUserId !== model.userId || model.previousMessageDay !== model.day
//asynchronous: true
sourceComponent: sectionHeader
visible: status == Loader.Ready
}
TimelineRow {
id: timelinerow
property alias hovered: hoverHandler.hovered
y: section.visible && section.active ? section.y + section.height : 0
HoverHandler {
id: hoverHandler
enabled: !Settings.mobileMode
onHoveredChanged: {
if (hovered) {
if (!messageActionHover.hovered) {
messageActions.attached = timelinerow;
messageActions.model = model;
}
}
}
}
}
Connections {
target: chat
onMovementEnded: {
if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
chat.model.currentIndex = index;
}
}
}
}
footer: BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: chat.model && chat.model.paginationInProgress
height: 50
width: 50
z: 3
}
footer: BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: chat.model && chat.model.paginationInProgress
height: 50
width: 50
z: 3
}
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2

View File

@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtGraphicalEffects 1.0
import QtQuick 2.12
import im.nheko 1.0
Item {
id: privacyScreen
property var timelineRoot
property int screenTimeout
Connections {
target: TimelineManager
onFocusChanged: {
if (TimelineManager.isWindowFocused) {
screenSaverTimer.stop();
screenSaver.state = "Invisible";
} else {
if (timelineRoot.visible)
screenSaverTimer.start();
}
}
}
Timer {
id: screenSaverTimer
interval: screenTimeout * 1000
running: true
onTriggered: {
screenSaver.state = "Visible";
}
}
Item {
id: screenSaver
state: "Invisible"
anchors.fill: parent
visible: false
states: [
State {
name: "Visible"
PropertyChanges {
target: screenSaver
visible: true
}
PropertyChanges {
target: screenSaver
opacity: 1
}
},
State {
name: "Invisible"
PropertyChanges {
target: screenSaver
opacity: 0
}
PropertyChanges {
target: screenSaver
visible: false
}
}
]
transitions: [
Transition {
from: "Visible"
to: "Invisible"
SequentialAnimation {
NumberAnimation {
target: screenSaver
property: "opacity"
duration: 250
easing.type: Easing.InQuad
}
NumberAnimation {
target: screenSaver
property: "visible"
duration: 0
}
}
},
Transition {
from: "Invisible"
to: "Visible"
SequentialAnimation {
NumberAnimation {
target: screenSaver
property: "visible"
duration: 0
}
NumberAnimation {
target: screenSaver
property: "opacity"
duration: 500
easing.type: Easing.InQuad
}
}
}
]
FastBlur {
id: blur
anchors.fill: parent
source: timelineRoot
radius: 50
}
}
}

View File

@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.3
import im.nheko 1.0
Popup {
id: quickSwitcher
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
property int textMargin: Math.round(textHeight / 8)
background: null
width: Math.round(parent.width / 2)
x: Math.round(parent.width / 2 - width / 2)
y: Math.round(parent.height / 4 - height / 2)
modal: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
parent: Overlay.overlay
palette: colors
onOpened: {
completerPopup.open();
roomTextInput.forceActiveFocus();
}
onClosed: {
completerPopup.close();
}
MatrixTextField {
id: roomTextInput
anchors.fill: parent
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
padding: textMargin
color: colors.text
onTextEdited: {
completerPopup.completer.searchString = text;
}
Keys.onPressed: {
if (event.key == Qt.Key_Up && completerPopup.opened) {
event.accepted = true;
completerPopup.up();
} else if (event.key == Qt.Key_Down && completerPopup.opened) {
event.accepted = true;
completerPopup.down();
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
completerPopup.finishCompletion();
event.accepted = true;
}
}
}
Completer {
id: completerPopup
x: roomTextInput.x
y: roomTextInput.y + quickSwitcher.textHeight + quickSwitcher.textMargin
visible: roomTextInput.length > 0
width: parent.width
completerName: "room"
bottomToTop: false
fullWidth: true
avatarHeight: quickSwitcher.textHeight
avatarWidth: quickSwitcher.textHeight
centerRowContent: false
rowMargin: Math.round(quickSwitcher.textMargin / 2)
rowSpacing: quickSwitcher.textMargin
closePolicy: Popup.NoAutoClose
}
Connections {
onCompletionSelected: {
TimelineManager.setHistoryView(id);
TimelineManager.highlightRoom(id);
quickSwitcher.close();
}
onCountChanged: {
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
completerPopup.currentIndex = 0;
}
target: completerPopup
}
Overlay.modal: Rectangle {
color: "#aa1E1E1E"
}
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.6
import QtQuick.Controls 2.2
import im.nheko 1.0

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./delegates/"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -10,15 +14,16 @@ Rectangle {
property var room: TimelineManager.timeline
Layout.fillWidth: true
visible: room && room.reply
visible: room && (room.reply || room.edit)
// Height of child, plus margins, plus border
implicitHeight: replyPreview.height + 10
implicitHeight: (room && room.reply ? replyPreview.height : closeEditButton.height) + 10
color: colors.window
z: 3
Reply {
id: replyPreview
visible: room && room.reply
anchors.left: parent.left
anchors.leftMargin: 2 * 22 + 3 * 16
anchors.right: closeReplyButton.left
@ -32,8 +37,9 @@ Rectangle {
ImageButton {
id: closeReplyButton
visible: room && room.reply
anchors.right: parent.right
anchors.rightMargin: 15
anchors.rightMargin: 16
anchors.top: replyPreview.top
hoverEnabled: true
width: 16
@ -44,4 +50,17 @@ Rectangle {
onClicked: room.reply = undefined
}
Button {
id: closeEditButton
visible: room && room.edit
anchors.left: parent.left
anchors.rightMargin: 16
anchors.topMargin: 10
anchors.top: parent.top
//height: 16
text: qsTr("Cancel edit")
onClicked: room.edit = undefined
}
}

View File

@ -0,0 +1,287 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.3
import im.nheko 1.0
ApplicationWindow {
id: roomSettingsDialog
property var roomSettings
x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
minimumWidth: 420
minimumHeight: 650
palette: colors
color: colors.window
modality: Qt.WindowModal
flags: Qt.Dialog
title: qsTr("Room Settings")
Shortcut {
sequence: StandardKey.Cancel
onActivated: roomSettingsDialog.close()
}
ColumnLayout {
id: contentLayout1
anchors.fill: parent
anchors.margins: 10
spacing: 10
Avatar {
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: roomSettings.roomName
height: 130
width: 130
Layout.alignment: Qt.AlignHCenter
onClicked: {
if (roomSettings.canChangeAvatar)
roomSettings.updateAvatar();
}
}
BusyIndicator {
Layout.alignment: Qt.AlignHCenter
running: roomSettings.isLoading
visible: roomSettings.isLoading
}
Text {
id: errorText
color: "red"
visible: opacity > 0
opacity: 0
Layout.alignment: Qt.AlignHCenter
}
SequentialAnimation {
id: hideErrorAnimation
running: false
PauseAnimation {
duration: 4000
}
NumberAnimation {
target: errorText
property: 'opacity'
to: 0
duration: 1000
}
}
Connections {
target: roomSettings
onDisplayError: {
errorText.text = errorMessage;
errorText.opacity = 1;
hideErrorAnimation.restart();
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
MatrixText {
text: roomSettings.roomName
font.pixelSize: 24
Layout.alignment: Qt.AlignHCenter
}
MatrixText {
text: qsTr("%1 member(s)").arg(roomSettings.memberCount)
Layout.alignment: Qt.AlignHCenter
}
}
ImageButton {
Layout.alignment: Qt.AlignHCenter
image: ":/icons/icons/ui/edit.png"
visible: roomSettings.canChangeNameAndTopic
onClicked: roomSettings.openEditModal()
}
ScrollView {
Layout.maximumHeight: 75
Layout.alignment: Qt.AlignHCenter
width: parent.width
TextArea {
text: TimelineManager.escapeEmoji(roomSettings.roomTopic)
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
readOnly: true
background: null
selectByMouse: true
color: colors.text
horizontalAlignment: TextEdit.AlignHCenter
onLinkActivated: TimelineManager.openLink(link)
CursorShape {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}
GridLayout {
columns: 2
rowSpacing: 10
MatrixText {
text: qsTr("SETTINGS")
font.bold: true
}
Item {
Layout.fillWidth: true
}
MatrixText {
text: qsTr("Notifications")
Layout.fillWidth: true
}
ComboBox {
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
currentIndex: roomSettings.notifications
onActivated: {
roomSettings.changeNotifications(index);
}
Layout.fillWidth: true
}
MatrixText {
text: "Room access"
Layout.fillWidth: true
}
ComboBox {
enabled: roomSettings.canChangeJoinRules
model: [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")]
currentIndex: roomSettings.accessJoinRules
onActivated: {
roomSettings.changeAccessRules(index);
}
Layout.fillWidth: true
}
MatrixText {
text: qsTr("Encryption")
}
ToggleButton {
id: encryptionToggle
checked: roomSettings.isEncryptionEnabled
onClicked: {
if (roomSettings.isEncryptionEnabled) {
checked = true;
return ;
}
confirmEncryptionDialog.open();
}
Layout.alignment: Qt.AlignRight
}
Platform.MessageDialog {
id: confirmEncryptionDialog
title: qsTr("End-to-End Encryption")
text: qsTr("Encryption is currently experimental and things might break unexpectedly. <br>
Please take note that it can't be disabled afterwards.")
modality: Qt.WindowModal
onAccepted: {
if (roomSettings.isEncryptionEnabled)
return ;
roomSettings.enableEncryption();
}
onRejected: {
encryptionToggle.checked = false;
}
buttons: Dialog.Ok | Dialog.Cancel
}
MatrixText {
visible: roomSettings.isEncryptionEnabled
text: qsTr("Respond to key requests")
}
ToggleButton {
visible: roomSettings.isEncryptionEnabled
ToolTip.text: qsTr("Whether or not the client should respond automatically with the session keys
upon request. Use with caution, this is a temporary measure to test the
E2E implementation until device verification is completed.")
checked: roomSettings.respondsToKeyRequests
onClicked: {
roomSettings.changeKeyRequestsPreference(checked);
}
Layout.alignment: Qt.AlignRight
}
Item {
// for adding extra space between sections
Layout.fillWidth: true
}
Item {
// for adding extra space between sections
Layout.fillWidth: true
}
MatrixText {
text: qsTr("INFO")
font.bold: true
}
Item {
Layout.fillWidth: true
}
MatrixText {
text: qsTr("Internal ID")
}
MatrixText {
text: roomSettings.roomId
font.pixelSize: 14
Layout.alignment: Qt.AlignRight
}
MatrixText {
text: qsTr("Room Version")
}
MatrixText {
text: roomSettings.roomVersion
font.pixelSize: 14
Layout.alignment: Qt.AlignRight
}
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("OK")
onClicked: close()
}
}
}

View File

@ -1,21 +1,9 @@
/*
* Copyright (C) 2016 Michael Bohlender, <michael.bohlender@kdemail.net>
* Copyright (C) 2017 Christian Mollekopf, <mollekopf@kolabsystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Copyright (C) 2016 Michael Bohlender, <michael.bohlender@kdemail.net>
// Copyright (C) 2017 Christian Mollekopf, <mollekopf@kolabsystems.com>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Shamelessly stolen from:
* https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml
@ -90,7 +78,7 @@ MouseArea {
// Show the scrollbars
flickable.flick(0, 0);
flickable.contentY = newPos;
cancelFlickStateTimer.start();
cancelFlickStateTimer.restart();
}
Timer {

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.5
import QtQuick.Controls 2.1
import im.nheko 1.0

View File

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./delegates"
import "./emoji"
import QtQuick 2.6
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
@ -12,32 +16,32 @@ Item {
height: row.height
Rectangle {
color: (Settings.messageHoverHighlight && hoverHandler.containsMouse) ? colors.alternateBase : "transparent"
color: (Settings.messageHoverHighlight && hoverHandler.hovered) ? colors.alternateBase : "transparent"
anchors.fill: row
}
MouseArea {
HoverHandler {
id: hoverHandler
anchors.fill: parent
propagateComposedEvents: true
preventStealing: false
hoverEnabled: true
acceptedButtons: Qt.AllButtons
onClicked: {
if (mouse.button === Qt.RightButton)
messageContextMenu.show(model.id, model.type, model.isEncrypted, row);
else
event.accepted = false;
}
onPressAndHold: {
messageContextMenu.show(model.id, model.type, model.isEncrypted, row, mapToItem(timelineRoot, mouse.x, mouse.y));
}
acceptedDevices: PointerDevice.GenericPointer
}
TapHandler {
acceptedButtons: Qt.RightButton
onSingleTapped: messageContextMenu.show(model.id, model.type, model.isSender, model.isEncrypted, model.isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
gesturePolicy: TapHandler.ReleaseWithinBounds
}
TapHandler {
onLongPressed: messageContextMenu.show(model.id, model.type, model.isSender, model.isEncrypted, model.isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
onDoubleTapped: chat.model.reply = model.id
gesturePolicy: TapHandler.ReleaseWithinBounds
}
RowLayout {
id: row
anchors.rightMargin: 1
anchors.leftMargin: avatarSize + 16
anchors.left: parent.left
anchors.right: parent.right
@ -46,6 +50,8 @@ Item {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 4
Layout.topMargin: 1
Layout.bottomMargin: 1
// fancy reply, if this is a reply
Reply {
@ -80,67 +86,41 @@ Item {
EncryptionIndicator {
visible: model.isRoomEncrypted
encrypted: model.isEncrypted
trust: model.trustlevel
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
Layout.preferredWidth: 16
}
EmojiButton {
id: reactButton
visible: Settings.buttonsInTimeline
Image {
visible: model.isEdited || model.id == chat.model.edit
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
Layout.preferredWidth: 16
height: 16
width: 16
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("React")
emojiPicker: emojiPopup
event_id: model.id
}
sourceSize.width: 16
sourceSize.height: 16
source: "image://colorimage/:/icons/icons/ui/edit.png?" + ((model.id == chat.model.edit) ? colors.highlight : colors.buttonText)
ToolTip.visible: editHovered.hovered
ToolTip.text: qsTr("Edited")
ImageButton {
id: replyButton
HoverHandler {
id: editHovered
}
visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/mail-reply.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Reply")
onClicked: chat.model.replyAction(model.id)
}
ImageButton {
id: optionsButton
visible: Settings.buttonsInTimeline
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/vertical-ellipsis.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Options")
onClicked: messageContextMenu.show(model.id, model.type, model.isEncrypted, optionsButton)
}
Label {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
text: model.timestamp.toLocaleTimeString("HH:mm")
text: model.timestamp.toLocaleTimeString(Locale.ShortFormat)
width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
color: inactiveColors.text
ToolTip.visible: ma.containsMouse
ToolTip.visible: ma.hovered
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
MouseArea {
HoverHandler {
id: ma
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
}
}

View File

@ -1,7 +1,12 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./delegates"
import "./device-verification"
import "./emoji"
import "./voip"
import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -16,7 +21,7 @@ Page {
property var colors: currentActivePalette
property var systemInactive
property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive
property int avatarSize: 40
readonly property int avatarSize: 40
property real highlightHue: colors.highlight.hslHue
property real highlightSat: colors.highlight.hslSaturation
property real highlightLight: colors.highlight.hslLightness
@ -30,18 +35,8 @@ Page {
EmojiPicker {
id: emojiPopup
width: 7 * 52 + 20
height: 6 * 52
colors: palette
model: EmojiProxyModel {
category: EmojiCategory.People
sourceModel: EmojiModel {
}
}
model: TimelineManager.completerFor("allemoji", "")
}
Component {
@ -52,6 +47,14 @@ Page {
}
Component {
id: roomSettingsComponent
RoomSettings {
}
}
Component {
id: mobileCallInviteDialog
@ -60,71 +63,158 @@ Page {
}
Menu {
Component {
id: quickSwitcherComponent
QuickSwitcher {
}
}
Component {
id: forwardCompleterComponent
ForwardCompleter {
}
}
Shortcut {
sequence: "Ctrl+K"
onActivated: {
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
TimelineManager.focusTimeline();
quickSwitch.open();
}
}
Platform.Menu {
id: messageContextMenu
property string eventId
property string link
property string text
property int eventType
property bool isEncrypted
property bool isEditable
property bool isSender
function show(eventId_, eventType_, isEncrypted_, showAt_, position) {
function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
eventId = eventId_;
eventType = eventType_;
isEncrypted = isEncrypted_;
if (position)
popup(position, showAt_);
isEditable = isEditable_;
isSender = isSender_;
if (text_)
text = text_;
else
popup(showAt_);
text = "";
if (link_)
link = link_;
else
link = "";
if (showAt_)
open(showAt_);
else
open();
}
modal: true
Platform.MenuItem {
visible: messageContextMenu.text
enabled: visible
text: qsTr("&Copy")
onTriggered: Clipboard.text = messageContextMenu.text
}
MenuItem {
text: qsTr("React")
onClicked: emojiPopup.show(messageContextMenu.parent, function(emoji) {
Platform.MenuItem {
visible: messageContextMenu.link
enabled: visible
text: qsTr("Copy &link location")
onTriggered: Clipboard.text = messageContextMenu.link
}
Platform.MenuItem {
id: reactionOption
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false
text: qsTr("Re&act")
onTriggered: emojiPopup.show(null, function(emoji) {
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
})
}
MenuItem {
text: qsTr("Reply")
onClicked: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
Platform.MenuItem {
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false
text: qsTr("Repl&y")
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
}
MenuItem {
text: qsTr("Read receipts")
Platform.MenuItem {
visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false)
enabled: visible
text: qsTr("&Edit")
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
}
Platform.MenuItem {
text: qsTr("Read receip&ts")
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId)
}
MenuItem {
text: qsTr("Mark as read")
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
text: qsTr("&Forward")
onTriggered: {
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
forwardMess.setMessageEventId(messageContextMenu.eventId);
forwardMess.open();
}
}
MenuItem {
Platform.MenuItem {
text: qsTr("&Mark as read")
}
Platform.MenuItem {
text: qsTr("View raw message")
onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId)
}
MenuItem {
Platform.MenuItem {
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
visible: messageContextMenu.isEncrypted
height: visible ? implicitHeight : 0
enabled: visible
text: qsTr("View decrypted raw message")
onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId)
}
MenuItem {
text: qsTr("Remove message")
Platform.MenuItem {
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender
text: qsTr("Remo&ve message")
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
}
MenuItem {
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
height: visible ? implicitHeight : 0
text: qsTr("Save as")
enabled: visible
text: qsTr("&Save as")
onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("&Open in external program")
onTriggered: TimelineManager.timeline.openMedia(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventId
enabled: visible
text: qsTr("Copy link to eve&nt")
onTriggered: TimelineManager.timeline.copyLinkToEvent(messageContextMenu.eventId)
}
}
Rectangle {
@ -147,10 +237,6 @@ Page {
});
dialog.show();
}
}
Connections {
target: TimelineManager.timeline
onOpenProfile: {
var userProfile = userProfileComponent.createObject(timelineRoot, {
"profile": profile
@ -159,6 +245,16 @@ Page {
}
}
Connections {
target: TimelineManager.timeline
onOpenRoomSettingsDialog: {
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
"roomSettings": settings
});
roomSettings.show();
}
}
Connections {
target: CallManager
onNewInviteState: {
@ -187,6 +283,8 @@ Page {
}
ColumnLayout {
id: timelineLayout
visible: TimelineManager.timeline != null
anchors.fill: parent
spacing: 0
@ -231,7 +329,7 @@ Page {
}
Loader {
source: CallManager.isOnCall && CallManager.isVideo ? "voip/VideoCall.qml" : ""
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
onLoaded: TimelineManager.setVideoCallItem()
}
@ -271,6 +369,18 @@ Page {
}
NhekoDropArea {
anchors.fill: parent
roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : ""
}
}
PrivacyScreen {
anchors.fill: parent
visible: Settings.privacyScreen
screenTimeout: Settings.privacyScreenTimeout
timelineRoot: timelineLayout
}
systemInactive: SystemPalette {

View File

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.5
import QtQuick 2.12
import QtQuick.Controls 2.12
import im.nheko 1.0
Switch {
id: toggleButton
implicitWidth: indicatorItem.width
indicator: Item {
id: indicatorItem
implicitWidth: 48
implicitHeight: 24
y: parent.height / 2 - height / 2
Rectangle {
height: 3 * parent.height / 4
radius: height / 2
width: parent.width - height
x: radius
y: parent.height / 2 - height / 2
color: toggleButton.checked ? "skyblue" : "grey"
border.color: "#cccccc"
}
Rectangle {
x: toggleButton.checked ? parent.width - width : 0
y: parent.height / 2 - height / 2
width: parent.height
height: width
radius: width / 2
color: toggleButton.down ? "whitesmoke" : "whitesmoke"
border.color: "#ebebeb"
}
}
}

View File

@ -1,5 +1,10 @@
import QtQuick 2.9
import QtQuick.Controls 2.3
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.2
import im.nheko 1.0
@ -13,9 +18,12 @@ Rectangle {
z: 3
color: colors.window
MouseArea {
anchors.fill: parent
onClicked: TimelineManager.openRoomSettings()
TapHandler {
onSingleTapped: {
TimelineManager.timeline.openRoomSettings();
eventPoint.accepted = true;
}
gesturePolicy: TapHandler.ReleaseWithinBounds
}
GridLayout {
@ -53,7 +61,7 @@ Rectangle {
height: avatarSize
url: room ? room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : ""
displayName: room ? room.roomName : qsTr("No room selected")
onClicked: TimelineManager.openRoomSettings()
onClicked: TimelineManager.timeline.openRoomSettings()
}
Label {
@ -65,12 +73,7 @@ Rectangle {
text: room ? room.roomName : qsTr("No room selected")
maximumLineCount: 1
elide: Text.ElideRight
MouseArea {
anchors.fill: parent
onClicked: TimelineManager.openRoomSettings()
}
textFormat: Text.RichText
}
MatrixText {
@ -92,29 +95,30 @@ Rectangle {
image: ":/icons/icons/ui/vertical-ellipsis.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Room options")
onClicked: roomOptionsMenu.popup(roomOptionsButton)
onClicked: roomOptionsMenu.open(roomOptionsButton)
Menu {
Platform.Menu {
id: roomOptionsMenu
MenuItem {
Platform.MenuItem {
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canInvite() : false
text: qsTr("Invite users")
onTriggered: TimelineManager.openInviteUsersDialog()
}
MenuItem {
Platform.MenuItem {
text: qsTr("Members")
onTriggered: TimelineManager.openMemberListDialog()
}
MenuItem {
Platform.MenuItem {
text: qsTr("Leave room")
onTriggered: TimelineManager.openLeaveRoomDialog()
}
MenuItem {
Platform.MenuItem {
text: qsTr("Settings")
onTriggered: TimelineManager.openRoomSettings()
onTriggered: TimelineManager.timeline.openRoomSettings()
}
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./device-verification"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -10,11 +14,21 @@ ApplicationWindow {
property var profile
x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
height: 650
width: 420
minimumHeight: 420
palette: colors
color: colors.window
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
modality: Qt.WindowModal
flags: Qt.Dialog
Shortcut {
sequence: StandardKey.Cancel
onActivated: userProfileDialog.close()
}
ColumnLayout {
id: contentL
@ -30,16 +44,86 @@ ApplicationWindow {
displayName: profile.displayName
userid: profile.userid
Layout.alignment: Qt.AlignHCenter
onClicked: TimelineManager.openImageOverlay(TimelineManager.timeline.avatarUrl(userid), TimelineManager.timeline.data.id)
onClicked: profile.isSelf ? profile.changeAvatar() : TimelineManager.openImageOverlay(profile.avatarUrl, "")
}
Label {
BusyIndicator {
Layout.alignment: Qt.AlignHCenter
running: profile.isLoading
visible: profile.isLoading
}
Text {
id: errorText
color: "red"
visible: opacity > 0
opacity: 0
Layout.alignment: Qt.AlignHCenter
}
SequentialAnimation {
id: hideErrorAnimation
running: false
PauseAnimation {
duration: 4000
}
NumberAnimation {
target: errorText
property: 'opacity'
to: 0
duration: 1000
}
}
Connections {
target: profile
onDisplayError: {
errorText.text = errorMessage;
errorText.opacity = 1;
hideErrorAnimation.restart();
}
}
TextInput {
id: displayUsername
property bool isUsernameEditingAllowed
readOnly: !isUsernameEditingAllowed
text: profile.displayName
fontSizeMode: Text.HorizontalFit
font.pixelSize: 20
color: TimelineManager.userColor(profile.userid, colors.window)
font.bold: true
Layout.alignment: Qt.AlignHCenter
selectByMouse: true
onAccepted: {
profile.changeUsername(displayUsername.text);
displayUsername.isUsernameEditingAllowed = false;
}
ImageButton {
visible: profile.isSelf
anchors.leftMargin: 5
anchors.left: displayUsername.right
anchors.verticalCenter: displayUsername.verticalCenter
image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.png" : ":/icons/icons/ui/edit.png"
onClicked: {
if (displayUsername.isUsernameEditingAllowed) {
profile.changeUsername(displayUsername.text);
displayUsername.isUsernameEditingAllowed = false;
} else {
displayUsername.isUsernameEditingAllowed = true;
displayUsername.focus = true;
displayUsername.selectAll();
}
}
}
}
MatrixText {
@ -53,32 +137,21 @@ ApplicationWindow {
text: qsTr("Verify")
Layout.alignment: Qt.AlignHCenter
enabled: !profile.isUserVerified
visible: !profile.isUserVerified && !profile.isSelf && profile.userVerificationEnabled
enabled: profile.userVerified != Crypto.Verified
visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled
onClicked: profile.verify()
}
Image {
Layout.preferredHeight: 16
Layout.preferredWidth: 16
source: "image://colorimage/:/icons/icons/ui/lock.png?green"
visible: profile.isUserVerified
source: "image://colorimage/:/icons/icons/ui/lock.png?" + ((profile.userVerified == Crypto.Verified) ? "green" : colors.buttonText)
visible: profile.userVerified != Crypto.Unverified
Layout.alignment: Qt.AlignHCenter
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 8
ImageButton {
image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Ban the user")
onClicked: profile.banUser()
}
// ImageButton{
// image:":/icons/icons/ui/volume-off-indicator.png"
// Layout.margins: {
// left: 5
@ -90,6 +163,10 @@ ApplicationWindow {
// profile.ignoreUser()
// }
// }
Layout.alignment: Qt.AlignHCenter
spacing: 8
ImageButton {
image: ":/icons/icons/ui/black-bubble-speech.png"
hoverEnabled: true
@ -104,6 +181,16 @@ ApplicationWindow {
ToolTip.visible: hovered
ToolTip.text: qsTr("Kick the user")
onClicked: profile.kickUser()
visible: profile.room ? profile.room.permissions.canKick() : false
}
ImageButton {
image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Ban the user")
onClicked: profile.banUser()
visible: profile.room ? profile.room.permissions.canBan() : false
}
}
@ -155,7 +242,7 @@ ApplicationWindow {
id: verifyButton
visible: (!profile.userVerificationEnabled && !profile.isSelf) || (profile.isSelf && (model.verificationStatus != VerificationStatus.VERIFIED || !profile.userVerificationEnabled))
text: (model.verificationStatus != VerificationStatus.VERIFIED) ? "Verify" : "Unverify"
text: (model.verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
onClicked: {
if (model.verificationStatus == VerificationStatus.VERIFIED)
profile.unverify(model.deviceId);

View File

@ -1,4 +1,8 @@
import QtQuick 2.6
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.2
import im.nheko 1.0
@ -29,9 +33,13 @@ Item {
fillMode: Image.Pad
}
MouseArea {
TapHandler {
onSingleTapped: TimelineManager.timeline.saveMedia(model.data.id)
gesturePolicy: TapHandler.ReleaseWithinBounds
}
CursorShape {
anchors.fill: parent
onClicked: TimelineManager.timeline.saveMedia(model.data.id)
cursorShape: Qt.PointingHandCursor
}

View File

@ -1,10 +1,14 @@
import QtQuick 2.6
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12
import im.nheko 1.0
Item {
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
property double tempHeight: tempWidth * model.data.proportionalHeight
property double divisor: model.isReply ? 4 : 2
property double divisor: model.isReply ? 5 : 3
property bool tooHigh: tempHeight > timelineRoot.height / divisor
height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
@ -32,20 +36,24 @@ Item {
smooth: true
mipmap: true
MouseArea {
id: mouseArea
TapHandler {
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
hoverEnabled: true
anchors.fill: parent
onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
onSingleTapped: {
TimelineManager.openImageOverlay(model.data.url, model.data.id);
eventPoint.accepted = true;
}
gesturePolicy: TapHandler.ReleaseWithinBounds
}
HoverHandler {
id: mouseArea
}
Item {
id: overlay
anchors.fill: parent
visible: mouseArea.containsMouse
visible: mouseArea.hovered
Rectangle {
id: container

View File

@ -1,9 +1,14 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.6
import im.nheko 1.0
Item {
property alias modelData: model.data
property alias isReply: model.isReply
property alias child: chooser.child
property real implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : width
height: chooser.childrenRect.height

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
TextMessage {
font.italic: true
color: colors.buttonText
height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
clip: isReply
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.5
import QtQuick.Controls 2.1

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import ".."
MatrixText {

View File

@ -1,5 +1,10 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtMultimedia 5.6
import QtQuick 2.6
import QtQuick 2.12
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import im.nheko 1.0
@ -103,20 +108,40 @@ Rectangle {
width: parent.width
spacing: 15
Rectangle {
ImageButton {
id: button
color: colors.window
radius: 22
height: 44
width: 44
Layout.alignment: Qt.AlignVCenter
//color: colors.window
//radius: 22
height: 32
width: 32
z: 3
image: ":/icons/icons/ui/arrow-pointing-down.png"
onClicked: {
switch (button.state) {
case "":
TimelineManager.timeline.cacheMedia(model.data.id);
break;
case "stopped":
media.play();
console.log("play");
button.state = "playing";
break;
case "playing":
media.pause();
console.log("pause");
button.state = "stopped";
break;
}
}
states: [
State {
name: "stopped"
PropertyChanges {
target: img
source: "image://colorimage/:/icons/icons/ui/play-sign.png?" + colors.text
target: button
image: ":/icons/icons/ui/play-sign.png"
}
},
@ -124,41 +149,15 @@ Rectangle {
name: "playing"
PropertyChanges {
target: img
source: "image://colorimage/:/icons/icons/ui/pause-symbol.png?" + colors.text
target: button
image: ":/icons/icons/ui/pause-symbol.png"
}
}
]
Image {
id: img
anchors.centerIn: parent
z: 3
source: "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?" + colors.text
fillMode: Image.Pad
}
MouseArea {
CursorShape {
anchors.fill: parent
onClicked: {
switch (button.state) {
case "":
TimelineManager.timeline.cacheMedia(model.data.id);
break;
case "stopped":
media.play();
console.log("play");
button.state = "playing";
break;
case "playing":
media.pause();
console.log("pause");
button.state = "stopped";
break;
}
}
cursorShape: Qt.PointingHandCursor
}
@ -178,7 +177,7 @@ Rectangle {
target: TimelineManager.timeline
onMediaCached: {
if (mxcUrl == model.data.url) {
media.source = "file://" + cacheUrl;
media.source = cacheUrl;
button.state = "stopped";
console.log("media loaded: " + mxcUrl + " at " + cacheUrl);
}

View File

@ -1,4 +1,8 @@
import QtQuick 2.6
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
@ -13,10 +17,13 @@ Item {
width: parent.width
height: replyContainer.height
MouseArea {
TapHandler {
onSingleTapped: chat.model.showEvent(modelData.id)
gesturePolicy: TapHandler.ReleaseWithinBounds
}
CursorShape {
anchors.fill: parent
preventStealing: false
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
cursorShape: Qt.PointingHandCursor
}
@ -43,10 +50,9 @@ Item {
color: replyComponent.userColor
textFormat: Text.RichText
MouseArea {
anchors.fill: parent
onClicked: chat.model.openUserProfile(reply.modelData.userId)
cursorShape: Qt.PointingHandCursor
TapHandler {
onSingleTapped: chat.model.openUserProfile(reply.modelData.userId)
gesturePolicy: TapHandler.ReleaseWithinBounds
}
}

View File

@ -1,12 +1,18 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import ".."
import im.nheko 1.0
MatrixText {
property string formatted: model.data.formattedBody
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : model.data.body
text: "<span style='white-space: pre-wrap'><style type=\"text/css\">a { color:" + colors.link + ";}\ncode { background-color: " + colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + colors.alternateBase + "'>") + "</span>"
text: "<style type=\"text/css\">a { color:" + colors.link + ";}\ncode { background-color: " + colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + colors.alternateBase + "'>")
width: parent ? parent.width : undefined
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
clip: isReply
selectByMouse: !Settings.mobileMode && !isReply
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Window 2.10
@ -11,9 +15,12 @@ ApplicationWindow {
onClosing: TimelineManager.removeVerificationFlow(flow)
title: stack.currentItem.title
flags: Qt.Dialog
modality: Qt.WindowModal
palette: colors
height: stack.implicitHeight
width: stack.implicitWidth
x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
StackView {
id: stack

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.10

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtQuick 2.10
import QtQuick.Controls 2.1
@ -14,5 +18,6 @@ ImageButton {
image: ":/icons/icons/ui/smile.png"
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, function(emoji) {
TimelineManager.queueReactionMessage(event_id, emoji);
TimelineManager.focusMessageInput();
})
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtGraphicalEffects 1.0
import QtQuick 2.9
@ -6,7 +10,7 @@ import QtQuick.Layouts 1.3
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
Popup {
Menu {
id: emojiPopup
property var callback
@ -20,13 +24,8 @@ Popup {
function show(showAt, callback) {
console.debug("Showing emojiPicker");
if (showAt) {
parent = showAt;
x = Math.round((showAt.width - width) / 2);
y = showAt.height;
}
emojiPopup.callback = callback;
open();
popup(showAt ? showAt : null);
}
margins: 0
@ -36,24 +35,74 @@ Popup {
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
height: columnView.implicitHeight + 4
width: columnView.implicitWidth
ColumnLayout {
id: columnView
anchors.fill: parent
spacing: 0
Layout.bottomMargin: 0
Layout.leftMargin: 3
Layout.rightMargin: 3
Layout.topMargin: 2
anchors.leftMargin: 3
anchors.rightMargin: 3
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 2
// Search field
TextField {
id: emojiSearch
//width: gridView.width - 6
Layout.topMargin: 3
Layout.preferredWidth: 7 * 52 + 20 - 6
placeholderText: qsTr("Search")
selectByMouse: true
rightPadding: clearSearch.width
onTextChanged: searchTimer.restart()
onVisibleChanged: {
if (visible)
forceActiveFocus();
}
Timer {
id: searchTimer
interval: 350 // tweak as needed?
onTriggered: {
emojiPopup.model.searchString = emojiSearch.text;
emojiPopup.model.category = Emoji.Category.Search;
}
}
ToolButton {
id: clearSearch
visible: emojiSearch.text !== ''
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText)
focusPolicy: Qt.NoFocus
onClicked: emojiSearch.clear()
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
}
// clear the default hover effects.
background: Item {
}
}
}
// emoji grid
GridView {
id: gridView
Layout.preferredHeight: emojiPopup.height
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: cellHeight * 5
Layout.preferredWidth: 7 * 52 + 20
Layout.leftMargin: 4
cellWidth: 52
cellHeight: 52
@ -103,54 +152,6 @@ Popup {
}
// Search field
header: TextField {
id: emojiSearch
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: emojiScroll.width + 4
placeholderText: qsTr("Search")
selectByMouse: true
rightPadding: clearSearch.width
onTextChanged: searchTimer.restart()
onVisibleChanged: {
if (visible)
forceActiveFocus();
}
Timer {
id: searchTimer
interval: 350 // tweak as needed?
onTriggered: {
emojiPopup.model.filter = emojiSearch.text;
emojiPopup.model.category = EmojiCategory.Search;
}
}
ToolButton {
id: clearSearch
visible: emojiSearch.text !== ''
icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText)
focusPolicy: Qt.NoFocus
onClicked: emojiSearch.clear()
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
}
// clear the default hover effects.
background: Item {
}
}
}
ScrollBar.vertical: ScrollBar {
id: emojiScroll
}
@ -159,6 +160,7 @@ Popup {
// Separator
Rectangle {
visible: emojiSearch.text === ''
Layout.fillWidth: true
Layout.preferredHeight: 1
color: emojiPopup.colors.alternateBase
@ -166,6 +168,7 @@ Popup {
// Category picker row
RowLayout {
visible: emojiSearch.text === ''
Layout.bottomMargin: 0
Layout.preferredHeight: 42
implicitHeight: 42
@ -178,42 +181,42 @@ Popup {
// TODO: Would like to get 'simple' icons for the categories
ListElement {
image: ":/icons/icons/emoji-categories/people.png"
category: EmojiCategory.People
category: Emoji.Category.People
}
ListElement {
image: ":/icons/icons/emoji-categories/nature.png"
category: EmojiCategory.Nature
category: Emoji.Category.Nature
}
ListElement {
image: ":/icons/icons/emoji-categories/foods.png"
category: EmojiCategory.Food
category: Emoji.Category.Food
}
ListElement {
image: ":/icons/icons/emoji-categories/activity.png"
category: EmojiCategory.Activity
category: Emoji.Category.Activity
}
ListElement {
image: ":/icons/icons/emoji-categories/travel.png"
category: EmojiCategory.Travel
category: Emoji.Category.Travel
}
ListElement {
image: ":/icons/icons/emoji-categories/objects.png"
category: EmojiCategory.Objects
category: Emoji.Category.Objects
}
ListElement {
image: ":/icons/icons/emoji-categories/symbols.png"
category: EmojiCategory.Symbols
category: Emoji.Category.Symbols
}
ListElement {
image: ":/icons/icons/emoji-categories/flags.png"
category: EmojiCategory.Flags
category: Emoji.Category.Flags
}
}
@ -224,27 +227,28 @@ Popup {
hoverEnabled: true
ToolTip.text: {
switch (model.category) {
case EmojiCategory.People:
case Emoji.Category.People:
return qsTr('People');
case EmojiCategory.Nature:
case Emoji.Category.Nature:
return qsTr('Nature');
case EmojiCategory.Food:
case Emoji.Category.Food:
return qsTr('Food');
case EmojiCategory.Activity:
case Emoji.Category.Activity:
return qsTr('Activity');
case EmojiCategory.Travel:
case Emoji.Category.Travel:
return qsTr('Travel');
case EmojiCategory.Objects:
case Emoji.Category.Objects:
return qsTr('Objects');
case EmojiCategory.Symbols:
case Emoji.Category.Symbols:
return qsTr('Symbols');
case EmojiCategory.Flags:
case Emoji.Category.Flags:
return qsTr('Flags');
}
}
ToolTip.visible: hovered
onClicked: {
emojiPopup.model.category = model.category;
//emojiPopup.model.category = model.category;
gridView.positionViewAtIndex(emojiPopup.model.sourceModel.categoryToIndex(model.category), GridView.Beginning);
}
MouseArea {
@ -275,56 +279,6 @@ Popup {
}
// Separator
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: 1
implicitWidth: 1
height: parent.height
color: emojiPopup.colors.alternateBase
}
// Search Button is special
AbstractButton {
id: searchBtn
hoverEnabled: true
Layout.alignment: Qt.AlignRight
Layout.bottomMargin: 0
ToolTip.text: qsTr("Search")
ToolTip.visible: hovered
onClicked: {
// clear any filters
emojiPopup.model.category = EmojiCategory.Search;
gridView.positionViewAtBeginning();
emojiSearch.forceActiveFocus();
}
Layout.preferredWidth: 36
Layout.preferredHeight: 36
implicitWidth: 36
implicitHeight: 36
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
contentItem: Image {
anchors.right: parent.right
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
sourceSize.width: 32
sourceSize.height: 32
fillMode: Image.Pad
smooth: true
source: "image://colorimage/:/icons/icons/ui/search.png?" + (parent.hovered ? colors.highlight : colors.buttonText)
}
}
}
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtGraphicalEffects 1.0
import QtQuick 2.10
import QtQuick.Controls 2.3
@ -9,9 +13,9 @@ Item {
property real radius: 0
property color color: "#22000000"
property real maxRadius: Math.max(width, height)
property real radiusAnimationRate: 0.05
property real radiusTailAnimationRate: 0.5
property real opacityAnimationDuration: 300
readonly property real radiusAnimationRate: 0.05
readonly property real radiusTailAnimationRate: 0.5
readonly property real opacityAnimationDuration: 300
readonly property real diameter: radius * 2
property real centerX
property real centerY

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -12,7 +16,7 @@ Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
if (CallManager.isVideo)
if (CallManager.callType != CallType.VOICE)
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
}
@ -42,10 +46,46 @@ Rectangle {
}
Image {
id: callTypeIcon
Layout.leftMargin: 4
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: CallManager.isVideo ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
}
Item {
states: [
State {
name: "VOICE"
when: CallManager.callType == CallType.VOICE
PropertyChanges {
target: callTypeIcon
source: "qrc:/icons/icons/ui/place-call.png"
}
},
State {
name: "VIDEO"
when: CallManager.callType == CallType.VIDEO
PropertyChanges {
target: callTypeIcon
source: "qrc:/icons/icons/ui/video-call.png"
}
},
State {
name: "SCREEN"
when: CallManager.callType == CallType.SCREEN
PropertyChanges {
target: callTypeIcon
source: "qrc:/icons/icons/ui/screen-share.png"
}
}
]
}
Label {
@ -103,7 +143,7 @@ Rectangle {
PropertyChanges {
target: stackLayout
currentIndex: CallManager.isVideo ? 1 : 0
currentIndex: CallManager.callType != CallType.VOICE ? 1 : 0
}
},
@ -147,20 +187,28 @@ Rectangle {
}
}
Label {
Layout.leftMargin: 16
visible: CallManager.callType == CallType.SCREEN && CallManager.callState == WebRTCState.CONNECTED
text: qsTr("You are screen sharing")
font.pointSize: fontMetrics.font.pointSize * 1.1
color: "#000000"
}
Item {
Layout.fillWidth: true
}
ImageButton {
visible: CallManager.haveLocalVideo
visible: CallManager.haveLocalPiP
width: 24
height: 24
buttonTextColor: "#000000"
image: ":/icons/icons/ui/toggle-camera-view.png"
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr("Toggle camera view")
onClicked: CallManager.toggleCameraView()
ToolTip.text: qsTr("Hide/Show Picture-in-Picture")
onClicked: CallManager.toggleLocalPiP()
}
ImageButton {

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
@ -40,7 +44,7 @@ Popup {
}
RowLayout {
visible: CallManager.isVideo && CallManager.cameras.length > 0
visible: CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0
Image {
Layout.preferredWidth: 22

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -53,7 +57,7 @@ Popup {
Layout.bottomMargin: msgView.height / 25
Image {
property string image: CallManager.isVideo ? ":/icons/icons/ui/video-call.png" : ":/icons/icons/ui/place-call.png"
property string image: CallManager.callType == CallType.VIDEO ? ":/icons/icons/ui/video-call.png" : ":/icons/icons/ui/place-call.png"
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: msgView.height / 10
@ -63,7 +67,7 @@ Popup {
Label {
Layout.alignment: Qt.AlignCenter
text: CallManager.isVideo ? qsTr("Video Call") : qsTr("Voice Call")
text: CallManager.callType == CallType.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
font.pointSize: fontMetrics.font.pointSize * 2
color: colors.windowText
}
@ -97,7 +101,7 @@ Popup {
}
RowLayout {
visible: CallManager.isVideo && CallManager.cameras.length > 0
visible: CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0
Layout.alignment: Qt.AlignCenter
Image {
@ -159,7 +163,7 @@ Popup {
RoundButton {
id: acceptButton
property string image: CallManager.isVideo ? ":/icons/icons/ui/video-call.png" : ":/icons/icons/ui/place-call.png"
property string image: CallManager.callType == CallType.VIDEO ? ":/icons/icons/ui/video-call.png" : ":/icons/icons/ui/place-call.png"
implicitWidth: buttonLayout.buttonSize
implicitHeight: buttonLayout.buttonSize

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -52,12 +56,12 @@ Rectangle {
Layout.leftMargin: 4
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: CallManager.isVideo ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
source: CallManager.callType == CallType.VIDEO ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
}
Label {
font.pointSize: fontMetrics.font.pointSize * 1.1
text: CallManager.isVideo ? qsTr("Video Call") : qsTr("Voice Call")
text: CallManager.callType == CallType.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
color: "#000000"
}
@ -75,7 +79,6 @@ Rectangle {
ToolTip.visible: hovered
ToolTip.text: qsTr("Devices")
onClicked: {
CallManager.refreshDevices();
var dialog = devicesDialog.createObject(timelineRoot);
dialog.open();
}
@ -83,7 +86,7 @@ Rectangle {
Button {
Layout.rightMargin: 4
icon.source: CallManager.isVideo ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
icon.source: CallManager.callType == CallType.VIDEO ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
text: qsTr("Accept")
palette: colors
onClicked: {
@ -102,7 +105,7 @@ Rectangle {
dialog.open();
return ;
}
if (CallManager.isVideo && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
if (CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
var dialog = deviceError.createObject(timelineRoot, {
"errorString": qsTr("Unknown camera: %1").arg(Settings.camera),
"image": ":/icons/icons/ui/video-call.png"

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtQuick 2.9
import QtQuick.Controls 2.3
@ -23,6 +27,14 @@ Popup {
}
Component {
id: screenShareDialog
ScreenShare {
}
}
ColumnLayout {
id: columnLayout
@ -76,7 +88,7 @@ Popup {
onClicked: {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
CallManager.sendInvite(TimelineManager.timeline.roomId(), false);
CallManager.sendInvite(TimelineManager.timeline.roomId(), CallType.VOICE);
close();
}
}
@ -90,12 +102,23 @@ Popup {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
Settings.camera = cameraCombo.currentText;
CallManager.sendInvite(TimelineManager.timeline.roomId(), true);
CallManager.sendInvite(TimelineManager.timeline.roomId(), CallType.VIDEO);
close();
}
}
}
Button {
visible: CallManager.screenShareSupported
text: qsTr("Screen")
icon.source: "qrc:/icons/icons/ui/screen-share.png"
onClicked: {
var dialog = screenShareDialog.createObject(timelineRoot);
dialog.open();
close();
}
}
Button {
text: qsTr("Cancel")
onClicked: {

View File

@ -0,0 +1,168 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import im.nheko 1.0
Popup {
modal: true
// only set the anchors on Qt 5.12 or higher
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
Component.onCompleted: {
if (anchors)
anchors.centerIn = parent;
frameRateCombo.currentIndex = frameRateCombo.find(Settings.screenShareFrameRate);
}
palette: colors
ColumnLayout {
Label {
Layout.topMargin: 16
Layout.bottomMargin: 16
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.alignment: Qt.AlignLeft
text: qsTr("Share desktop with %1?").arg(TimelineManager.timeline.roomName)
color: colors.windowText
}
RowLayout {
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 8
Label {
Layout.alignment: Qt.AlignLeft
text: qsTr("Window:")
color: colors.windowText
}
ComboBox {
id: windowCombo
Layout.fillWidth: true
model: CallManager.windowList()
}
}
RowLayout {
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 8
Label {
Layout.alignment: Qt.AlignLeft
text: qsTr("Frame rate:")
color: colors.windowText
}
ComboBox {
id: frameRateCombo
Layout.fillWidth: true
model: ["25", "20", "15", "10", "5", "2", "1"]
}
}
GridLayout {
columns: 2
rowSpacing: 10
Layout.margins: 8
MatrixText {
text: qsTr("Include your camera picture-in-picture")
}
ToggleButton {
id: pipCheckBox
enabled: CallManager.cameras.length > 0
checked: Settings.screenSharePiP
Layout.alignment: Qt.AlignRight
}
MatrixText {
text: qsTr("Request remote camera")
ToolTip.text: qsTr("View your callee's camera like a regular video call")
ToolTip.visible: hovered
}
ToggleButton {
id: remoteVideoCheckBox
Layout.alignment: Qt.AlignRight
checked: Settings.screenShareRemoteVideo
ToolTip.text: qsTr("View your callee's camera like a regular video call")
ToolTip.visible: hovered
}
MatrixText {
text: qsTr("Hide mouse cursor")
}
ToggleButton {
id: hideCursorCheckBox
Layout.alignment: Qt.AlignRight
checked: Settings.screenShareHideCursor
}
}
RowLayout {
Layout.margins: 8
Item {
Layout.fillWidth: true
}
Button {
text: qsTr("Share")
icon.source: "qrc:/icons/icons/ui/screen-share.png"
onClicked: {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
if (pipCheckBox.checked)
Settings.camera = cameraCombo.currentText;
Settings.screenShareFrameRate = frameRateCombo.currentText;
Settings.screenSharePiP = pipCheckBox.checked;
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
CallManager.sendInvite(TimelineManager.timeline.roomId(), CallType.SCREEN, windowCombo.currentIndex);
close();
}
}
}
Button {
text: qsTr("Preview")
onClicked: {
CallManager.previewWindow(windowCombo.currentIndex);
}
}
Button {
text: qsTr("Cancel")
onClicked: {
close();
}
}
}
}
background: Rectangle {
color: colors.window
border.color: colors.windowText
}
}

View File

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import org.freedesktop.gstreamer.GLVideoItem 1.0

View File

@ -74,6 +74,7 @@
<file>icons/ui/end-call.png</file>
<file>icons/ui/microphone-mute.png</file>
<file>icons/ui/microphone-unmute.png</file>
<file>icons/ui/screen-share.png</file>
<file>icons/ui/toggle-camera-view.png</file>
<file>icons/ui/video-call.png</file>
@ -128,16 +129,22 @@
<file>qml/EncryptionIndicator.qml</file>
<file>qml/ImageButton.qml</file>
<file>qml/MatrixText.qml</file>
<file>qml/MatrixTextField.qml</file>
<file>qml/ToggleButton.qml</file>
<file>qml/MessageInput.qml</file>
<file>qml/MessageView.qml</file>
<file>qml/NhekoBusyIndicator.qml</file>
<file>qml/PrivacyScreen.qml</file>
<file>qml/Reactions.qml</file>
<file>qml/ReplyPopup.qml</file>
<file>qml/ScrollHelper.qml</file>
<file>qml/StatusIndicator.qml</file>
<file>qml/TimelineRow.qml</file>
<file>qml/TopBar.qml</file>
<file>qml/QuickSwitcher.qml</file>
<file>qml/ForwardCompleter.qml</file>
<file>qml/TypingIndicator.qml</file>
<file>qml/RoomSettings.qml</file>
<file>qml/emoji/EmojiButton.qml</file>
<file>qml/emoji/EmojiPicker.qml</file>
<file>qml/UserProfile.qml</file>
@ -164,6 +171,7 @@
<file>qml/voip/CallInviteBar.qml</file>
<file>qml/voip/DeviceError.qml</file>
<file>qml/voip/PlaceCall.qml</file>
<file>qml/voip/ScreenShare.qml</file>
<file>qml/voip/VideoCall.qml</file>
</qresource>
<qresource prefix="/media">

View File

@ -11,26 +11,13 @@ class Emoji(object):
self.code = repr(code.encode('utf-8'))[1:].strip("'")
self.shortname = shortname
def generate_code(emojis, category):
tmpl = Template('''
const std::vector<Emoji> emoji::Provider::{{ category }} = {
// {{ category.capitalize() }}
{%- for e in emoji %}
Emoji{QString::fromUtf8("{{ e.code }}"), "{{ e.shortname }}", emoji::EmojiCategory::{{ category.capitalize() }}},
{%- endfor %}
};
''')
d = dict(category=category, emoji=emojis)
print(tmpl.render(d))
def generate_qml_list(**kwargs):
tmpl = Template('''
const QVector<Emoji> emoji::Provider::emoji = {
{%- for c in kwargs.items() %}
// {{ c[0].capitalize() }}
{%- for e in c[1] %}
Emoji{QString::fromUtf8("{{ e.code }}"), "{{ e.shortname }}", emoji::EmojiCategory::{{ c[0].capitalize() }}},
Emoji{QString::fromUtf8("{{ e.code }}"), "{{ e.shortname }}", emoji::Emoji::Category::{{ c[0].capitalize() }}},
{%- endfor %}
{%- endfor %}
};
@ -93,12 +80,4 @@ if __name__ == '__main__':
# Use xclip to pipe the output to clipboard.
# e.g ./codegen.py emoji.json | xclip -sel clip
generate_code(people, 'people')
generate_code(nature, 'nature')
generate_code(food, 'food')
generate_code(activity, 'activity')
generate_code(travel, 'travel')
generate_code(objects, 'objects')
generate_code(symbols, 'symbols')
generate_code(flags, 'flags')
generate_qml_list(people=people, nature=nature, food=food, activity=activity, travel=travel, objects=objects, symbols=symbols, flags=flags)
generate_qml_list(people=people, nature=nature, food=food, activity=activity, travel=travel, objects=objects, symbols=symbols, flags=flags)

772
scripts/flat-manager-client Executable file
View File

@ -0,0 +1,772 @@
#!/usr/bin/python3
# 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 2 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import asyncio
import base64
import binascii
import errno
import fnmatch
import gzip
import json
import logging
import os
import sys
import time
import traceback
from argparse import ArgumentParser
from functools import reduce
from urllib.parse import urljoin, urlparse, urlsplit, urlunparse, urlunsplit
import aiohttp
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed
import gi
gi.require_version('OSTree', '1.0')
from gi.repository import Gio, GLib, OSTree
UPLOAD_CHUNK_LIMIT = 4 * 1024 * 1024
DEFAULT_LIMIT = 2 ** 16
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
class UsageException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
class ApiError(Exception):
def __init__(self, response, body):
self.url = str(response.url)
self.status = response.status
try:
self.body = json.loads(response);
except:
self.body = {"status": self.status, "error-type": "no-error", "message": "No json error details from server"}
def repr(self):
return {
"type": "api",
"url": self.url,
"status_code": self.status,
"details": self.body
}
def __str__(self):
return "Api call to %s failed with status %d, details: %s" % (self.url, self.status, self.body)
# This is similar to the regular payload, but opens the file lazily
class AsyncNamedFilePart(aiohttp.payload.Payload):
def __init__(self,
value,
disposition='attachment',
*args,
**kwargs):
self._file = None
if 'filename' not in kwargs:
kwargs['filename'] = os.path.basename(value)
super().__init__(value, *args, **kwargs)
if self._filename is not None and disposition is not None:
self.set_content_disposition(disposition, filename=self._filename, quote_fields=False)
self._size = os.stat(value).st_size
async def write(self, writer):
if self._file is None or self._file.closed:
self._file = open(self._value, 'rb')
try:
chunk = self._file.read(DEFAULT_LIMIT)
while chunk:
await writer.write(chunk)
chunk = self._file.read(DEFAULT_LIMIT)
finally:
self._file.close()
@property
def size(self):
return self._size
def ostree_object_path(repo, obj):
repodir = repo.get_path().get_path()
return os.path.join(repodir, 'objects', obj[0:2], obj[2:])
def ostree_get_dir_files(repo, objects, dirtree):
if dirtree.endswith(".dirtree"):
dirtree = dirtree[:-8]
dirtreev = repo.load_variant(OSTree.ObjectType.DIR_TREE, dirtree)[1]
iter = OSTree.RepoCommitTraverseIter()
iter.init_dirtree(repo, dirtreev, 0)
while True:
type = iter.next()
if type == OSTree.RepoCommitIterResult.END:
break
if type == OSTree.RepoCommitIterResult.ERROR:
break
if type == OSTree.RepoCommitIterResult.FILE:
d = iter.get_file()
objects.add(d.out_checksum + ".filez")
if type == OSTree.RepoCommitIterResult.DIR:
pass
def local_needed_files(repo, metadata_objects):
objects = set()
for c in metadata_objects:
if c.endswith(".dirtree"):
ostree_get_dir_files(repo, objects, c)
return objects
def local_needed_metadata_dirtree(repo, objects, dirtree_content, dirtree_meta):
objects.add(dirtree_meta + ".dirmeta")
dirtree_content_name = dirtree_content + ".dirtree"
if dirtree_content_name in objects:
return
objects.add(dirtree_content_name)
dirtreev = repo.load_variant(OSTree.ObjectType.DIR_TREE, dirtree_content)[1]
iter = OSTree.RepoCommitTraverseIter()
iter.init_dirtree(repo, dirtreev, 0)
while True:
type = iter.next()
if type == OSTree.RepoCommitIterResult.END:
break
if type == OSTree.RepoCommitIterResult.ERROR:
break
if type == OSTree.RepoCommitIterResult.FILE:
pass
if type == OSTree.RepoCommitIterResult.DIR:
d = iter.get_dir()
local_needed_metadata_dirtree(repo, objects, d.out_content_checksum, d.out_meta_checksum)
def local_needed_metadata(repo, commits):
objects = set()
for rev in commits:
objects.add(rev + ".commit")
commitv = repo.load_variant(OSTree.ObjectType.COMMIT, rev)[1]
iter = OSTree.RepoCommitTraverseIter()
iter.init_commit(repo, commitv, 0)
while True:
type = iter.next()
if type == OSTree.RepoCommitIterResult.END:
break
if type == OSTree.RepoCommitIterResult.ERROR:
break
if type == OSTree.RepoCommitIterResult.FILE:
pass
if type == OSTree.RepoCommitIterResult.DIR:
d = iter.get_dir()
local_needed_metadata_dirtree(repo, objects, d.out_content_checksum, d.out_meta_checksum)
return objects
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
async def missing_objects(session, build_url, token, wanted):
missing=[]
for chunk in chunks(wanted, 2000):
wanted_json=json.dumps({'wanted': chunk}).encode('utf-8')
data=gzip.compress(wanted_json)
headers = {
'Authorization': 'Bearer ' + token,
'Content-Encoding': 'gzip',
'Content-Type': 'application/json'
}
resp = await session.get(build_url + "/missing_objects", data=data, headers=headers)
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
data = await resp.json()
missing.extend(data["missing"])
return missing
async def upload_files(session, build_url, token, files):
if len(files) == 0:
return
print("Uploading %d files (%d bytes)" % (len(files), reduce(lambda x, y: x + y, map(lambda f: f.size, files))))
with aiohttp.MultipartWriter() as writer:
for f in files:
writer.append(f)
writer.headers['Authorization'] = 'Bearer ' + token
resp = await session.request("post", build_url + '/upload', data=writer, headers=writer.headers)
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
async def upload_deltas(session, repo_path, build_url, token, deltas, refs, ignore_delta):
if not len(deltas):
return
req = []
for ref, commit in refs.items():
# Skip screenshots here
parts = ref.split("/")
if len(parts) == 4 and (parts[0] == "app" or parts[0] =="runtime") and not should_skip_delta(parts[1], ignore_delta):
for delta in deltas:
# Only upload from-scratch deltas, as these are the only reused ones
if delta == commit:
print(" %s: %s" % (ref, delta))
delta_name = delta_name_encode (delta)
delta_dir = repo_path + "/deltas/" + delta_name[:2] + "/" + delta_name[2:]
parts = os.listdir(delta_dir)
for part in parts:
req.append(AsyncNamedFilePart(delta_dir + "/" + part, filename = delta_name + "." + part + ".delta"))
if len(req):
await upload_files(session, build_url, token, req)
async def upload_objects(session, repo_path, build_url, token, objects):
req = []
total_size = 0
for file_obj in objects:
named = get_object_multipart(repo_path, file_obj)
file_size = named.size
if total_size + file_size > UPLOAD_CHUNK_LIMIT: # The new object would bring us over the chunk limit
if len(req) > 0: # We already have some objects, upload those first
next_req = [named]
total_size = file_size
else:
next_req = []
req.append(named)
total_size = 0
await upload_files(session, build_url, token, req)
req = next_req
else:
total_size = total_size + file_size
req.append(named)
# Upload any remainder
await upload_files(session, build_url, token, req)
async def create_ref(session, build_url, token, ref, commit):
print("Creating ref %s with commit %s" % (ref, commit))
resp = await session.post(build_url + "/build_ref", headers={'Authorization': 'Bearer ' + token}, json= { "ref": ref, "commit": commit} )
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
data = await resp.json()
return data
async def add_extra_ids(session, build_url, token, extra_ids):
print("Adding extra ids %s" % (extra_ids))
resp = await session.post(build_url + "/add_extra_ids", headers={'Authorization': 'Bearer ' + token}, json= { "ids": extra_ids} )
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
data = await resp.json()
return data
async def get_build(session, build_url, token):
resp = await session.get(build_url, headers={'Authorization': 'Bearer ' + token})
if resp.status != 200:
raise ApiError(resp, await resp.text())
data = await resp.json()
return data
# For stupid reasons this is a string with json, lets expand it
def reparse_job_results(job):
job["results"] = json.loads(job.get("results", "{}"))
return job
async def get_job(session, job_url, token):
resp = await session.get(job_url, headers={'Authorization': 'Bearer ' + token}, json={})
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
data = await resp.json()
return data
async def wait_for_job(session, job_url, token):
reported_delay = False
old_job_status = 0
printed_len = 0
iterations_since_change=0
error_iterations = 0
while True:
try:
resp = await session.get(job_url, headers={'Authorization': 'Bearer ' + token}, json={'log-offset': printed_len})
async with resp:
if resp.status == 200:
error_iterations = 0
job = await resp.json()
job_status = job['status']
if job_status == 0 and not reported_delay:
reported_delay = True
start_after_struct = job.get("start_after", None)
if start_after_struct:
start_after = start_after_struct.get("secs_since_epoch", None)
now = time.time()
if start_after and start_after > now:
print("Waiting %d seconds before starting job" % (int(start_after - now)))
if job_status > 0 and old_job_status == 0:
print("/ Job was started");
old_job_status = job_status
log = job['log']
if len(log) > 0:
iterations_since_change=0
for line in log.splitlines(True):
print("| %s" % line, end="")
printed_len = printed_len + len(log)
else:
iterations_since_change=iterations_since_change+1
if job_status > 1:
if job_status == 2:
print("\ Job completed successfully")
else:
print("\ Job failed")
return job
else:
iterations_since_change=4 # Start at 4 so we ramp up the delay faster
error_iterations=error_iterations + 1
if error_iterations <= 5:
print("Unexpected response %s getting job log, ignoring" % resp.status)
else:
raise ApiError(resp, await resp.text())
except OSError as e:
if e.args[0] == errno.ECONNRESET:
# Client disconnected, retry
# Not sure exactly why, but i got a lot of ConnectionResetErrors here
# in tests. I guess the server stops reusing a http2 session after a bit
# Should be fine to retry with the backof
pass
else:
raise
# Some polling backoff to avoid loading the server
if iterations_since_change <= 1:
sleep_time=1
elif iterations_since_change < 5:
sleep_time=3
elif iterations_since_change < 15:
sleep_time=5
elif iterations_since_change < 30:
sleep_time=10
else:
sleep_time=60
time.sleep(sleep_time)
async def commit_build(session, build_url, eol, eol_rebase, token_type, wait, token):
print("Committing build %s" % (build_url))
json = {
"endoflife": eol,
"endoflife_rebase": eol_rebase
}
if token_type != None:
json['token_type'] = token_type
resp = await session.post(build_url + "/commit", headers={'Authorization': 'Bearer ' + token}, json=json)
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
job = await resp.json()
job_url = resp.headers['location'];
if wait:
print("Waiting for commit job")
job = await wait_for_job(session, job_url, token);
reparse_job_results(job)
job["location"] = job_url
return job
async def publish_build(session, build_url, wait, token):
print("Publishing build %s" % (build_url))
resp = await session.post(build_url + "/publish", headers={'Authorization': 'Bearer ' + token}, json= { } )
async with resp:
if resp.status == 400:
body = await resp.text()
try:
msg = json.loads(body)
if msg.get("current-state", "") == "published":
print("the build has been already published")
return {}
except:
pass
if resp.status != 200:
raise ApiError(resp, await resp.text())
job = await resp.json()
job_url = resp.headers['location'];
if wait:
print("Waiting for publish job")
job = await wait_for_job(session, job_url, token);
reparse_job_results(job)
job["location"] = job_url
return job
async def purge_build(session, build_url, token):
print("Purging build %s" % (build_url))
resp = await session.post(build_url + "/purge", headers={'Authorization': 'Bearer ' + token}, json= {} )
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
return await resp.json()
async def create_token(session, manager_url, token, name, subject, scope, duration):
token_url = urljoin(manager_url, "api/v1/token_subset")
resp = await session.post(token_url, headers={'Authorization': 'Bearer ' + token}, json = {
"name": name,
"sub": subject,
"scope": scope,
"duration": duration,
})
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
return await resp.json()
def get_object_multipart(repo_path, object):
return AsyncNamedFilePart(repo_path + "/objects/" + object[:2] + "/" + object[2:], filename=object)
async def create_command(session, args):
build_url = urljoin(args.manager_url, "api/v1/build")
resp = await session.post(build_url, headers={'Authorization': 'Bearer ' + args.token}, json={
"repo": args.repo
})
async with resp:
if resp.status != 200:
raise ApiError(resp, await resp.text())
data = await resp.json()
data["location"] = resp.headers['location']
if not args.print_output:
print(resp.headers['location'])
return data
def delta_name_part_encode(commit):
return base64.b64encode(binascii.unhexlify(commit), b"+_")[:-1].decode("utf-8")
def delta_name_encode (delta):
return "-".join(map(delta_name_part_encode, delta.split("-")))
def should_skip_delta(id, globs):
if globs:
for glob in globs:
if fnmatch.fnmatch(id, glob):
return True
return False
def build_url_to_api(build_url):
parts = urlparse(build_url)
path = os.path.dirname(os.path.dirname(parts.path))
return urlunparse((parts.scheme, parts.netloc, path, None, None, None))
@retry(
stop=stop_after_attempt(6),
wait=wait_fixed(10),
retry=retry_if_exception_type(ApiError),
reraise=True,
)
async def push_command(session, args):
local_repo = OSTree.Repo.new(Gio.File.new_for_path(args.repo_path))
try:
local_repo.open(None)
except GLib.Error as err:
raise UsageException("Can't open repo %s: %s" % (args.repo_path, err.message)) from err
refs = {}
if len(args.branches) == 0:
_, all_refs = local_repo.list_refs(None, None)
for ref in all_refs:
if ref.startswith("app/") or ref.startswith("runtime/") or ref.startswith("screenshots/"):
refs[ref] = all_refs[ref]
else:
for branch in args.branches:
_, rev = local_repo.resolve_rev(branch, False)
refs[branch] = rev
if (args.minimal_token):
id = os.path.basename(urlparse(args.build_url).path)
token = create_token(args.build_url, args.token, "minimal-upload", "build/%s" % (id), ["upload"], 60*60)["token"]
else:
token = args.token
print("Uploading refs to %s: %s"% (args.build_url, list(refs)))
metadata_objects = local_needed_metadata(local_repo, refs.values())
print("Refs contain %d metadata objects" % (len(metadata_objects)))
missing_metadata_objects = await missing_objects(session, args.build_url, token, list(metadata_objects))
print("Remote missing %d of those" % (len(missing_metadata_objects)))
file_objects = local_needed_files(local_repo, missing_metadata_objects)
print("Has %d file objects for those" % (len(file_objects)))
missing_file_objects = await missing_objects(session, args.build_url, token, list(file_objects))
print("Remote missing %d of those" % (len(missing_file_objects)))
# First upload all missing file objects
print("Uploading file objects")
await upload_objects(session, args.repo_path, args.build_url, token, missing_file_objects)
# Then all the metadata
print("Uploading metadata objects")
await upload_objects(session, args.repo_path, args.build_url, token, missing_metadata_objects)
_, deltas = local_repo.list_static_delta_names()
print("Uploading deltas")
await upload_deltas(session, args.repo_path, args.build_url, token, deltas, refs, args.ignore_delta)
# Then the refs
for ref, commit in refs.items():
await create_ref(session, args.build_url, token, ref, commit)
# Then any extra ids
if args.extra_id:
await add_extra_ids(session, args.build_url, token, args.extra_id)
commit_job = None
publish_job = None
update_job = None
# Note, this always uses the full token, as the minimal one only has upload permissions
if args.commit or args.publish:
commit_job = await commit_build(session, args.build_url, args.end_of_life, args.end_of_life_rebase, args.token_type, args.publish or args.wait, args.token)
if args.publish:
publish_job = await publish_build(session, args.build_url, args.wait or args.wait_update, args.token)
update_job_id = publish_job.get("results", {}).get("update-repo-job", None)
if update_job_id:
print("Queued repo update job %d" %(update_job_id))
update_job_url = build_url_to_api(args.build_url) + "/job/" + str(update_job_id)
if args.wait_update:
print("Waiting for repo update job")
update_job = await wait_for_job (session, update_job_url, token);
else:
update_job = await get_job(session, update_job_url, token)
reparse_job_results(update_job)
update_job["location"] = update_job_url
data = await get_build(session, args.build_url, args.token)
if commit_job:
data["commit_job"] = commit_job
if publish_job:
data["publish_job"] = publish_job
if update_job:
data["update_job"] = update_job
return data
async def commit_command(session, args):
job = await commit_build(session, args.build_url, args.end_of_life, args.end_of_life_rebase, args.token_type, args.wait, args.token)
return job
async def publish_command(session, args):
job = await publish_build(session, args.build_url, args.wait or args.wait_update, args.token)
update_job_id = job.get("results", {}).get("update-repo-job", None)
if update_job_id:
print("Queued repo update job %d" %(update_job_id))
update_job_url = build_url_to_api(args.build_url) + "/job/" + str(update_job_id)
if args.wait_update:
print("Waiting for repo update job")
update_job = await wait_for_job(session, update_job_url, args.token);
else:
update_job = await get_job(session, update_job_url, args.token)
reparse_job_results(update_job)
update_job["location"] = update_job_url
return job
async def purge_command(session, args):
job = await purge_build(session, args.build_url, args.token)
return job
async def create_token_command(session, args):
data = await create_token(session, args.manager_url, args.token, args.name, args.subject, args.scope, args.duration)
if not args.print_output:
print(data['token'])
return data
async def follow_job_command(session, args):
job = await wait_for_job(session, args.job_url, args.token)
return job
async def run_with_session(args):
timeout = aiohttp.ClientTimeout(total=90*60)
async with aiohttp.ClientSession(timeout=timeout) as session:
result = await args.func(session, args)
return result
if __name__ == '__main__':
progname = os.path.basename(sys.argv[0])
parser = ArgumentParser(prog=progname)
parser.add_argument('-v', '--verbose', action='store_true',
help='enable verbose output')
parser.add_argument('--debug', action='store_true',
help='enable debugging output')
parser.add_argument('--output', help='Write output json to file')
parser.add_argument('--print-output', action='store_true', help='Print output json')
parser.add_argument('--token', help='use this token')
parser.add_argument('--token-file', help='use token from file')
subparsers = parser.add_subparsers(title='subcommands',
dest='subparser_name',
description='valid subcommands',
help='additional help')
create_parser = subparsers.add_parser('create', help='Create new build')
create_parser.add_argument('manager_url', help='remote repo manager url')
create_parser.add_argument('repo', help='repo name')
create_parser.set_defaults(func=create_command)
push_parser = subparsers.add_parser('push', help='Push to repo manager')
push_parser.add_argument('build_url', help='remote build url')
push_parser.add_argument('repo_path', help='local repository')
push_parser.add_argument('branches', nargs='*', help='branches to push')
push_parser.add_argument('--commit', action='store_true',
help='commit build after pushing')
push_parser.add_argument('--publish', action='store_true',
help='publish build after committing')
push_parser.add_argument('--extra-id', action='append', help='add extra collection-id')
push_parser.add_argument('--ignore-delta', action='append', help='don\'t upload deltas matching this glob')
push_parser.add_argument('--wait', action='store_true',
help='wait for commit/publish to finish')
push_parser.add_argument('--wait-update', action='store_true',
help='wait for update-repo to finish')
push_parser.add_argument('--minimal-token', action='store_true',
help='Create minimal token for the upload')
push_parser.add_argument('--end-of-life', help='Set end of life')
push_parser.add_argument('--end-of-life-rebase', help='Set new ID which will supercede the current one')
push_parser.add_argument('--token-type', help='Set token type', type=int)
push_parser.set_defaults(func=push_command)
commit_parser = subparsers.add_parser('commit', help='Commit build')
commit_parser.add_argument('--wait', action='store_true',
help='wait for commit to finish')
commit_parser.add_argument('--end-of-life', help='Set end of life')
commit_parser.add_argument('--end-of-life-rebase', help='Set new ID which will supercede the current one')
commit_parser.add_argument('--token-type', help='Set token type', type=int)
commit_parser.add_argument('build_url', help='remote build url')
commit_parser.set_defaults(func=commit_command)
publish_parser = subparsers.add_parser('publish', help='Publish build')
publish_parser.add_argument('--wait', action='store_true',
help='wait for publish to finish')
publish_parser.add_argument('--wait-update', action='store_true',
help='wait for update-repo to finish')
publish_parser.add_argument('build_url', help='remote build url')
publish_parser.set_defaults(func=publish_command)
purge_parser = subparsers.add_parser('purge', help='Purge build')
purge_parser.add_argument('build_url', help='remote build url')
purge_parser.set_defaults(func=purge_command)
create_token_parser = subparsers.add_parser('create-token', help='Create subset token')
create_token_parser.add_argument('manager_url', help='remote repo manager url')
create_token_parser.add_argument('name', help='Name')
create_token_parser.add_argument('subject', help='Subject')
create_token_parser.add_argument('scope', nargs='*', help='Scope')
create_token_parser.add_argument('--duration', help='Duration until expires, in seconds',
default=60*60*24, # Default duration is one day
type=int)
create_token_parser.set_defaults(func=create_token_command)
follow_job_parser = subparsers.add_parser('follow-job', help='Follow existing job log')
follow_job_parser.add_argument('job_url', help='url of job')
follow_job_parser.set_defaults(func=follow_job_command)
args = parser.parse_args()
loglevel = logging.WARNING
if args.verbose:
loglevel = logging.INFO
if args.debug:
loglevel = logging.DEBUG
logging.basicConfig(format='%(module)s: %(levelname)s: %(message)s',
level=loglevel, stream=sys.stderr)
if not args.subparser_name:
print("No subcommand specified, see --help for usage")
exit(1)
if not args.token:
if args.token_file:
file = open(args.token_file, 'rb')
args.token = file.read().splitlines()[0].decode("utf-8").strip()
elif "REPO_TOKEN" in os.environ:
args.token = os.environ["REPO_TOKEN"]
else:
print("No token available, pass with --token, --token-file or $REPO_TOKEN")
exit(1)
res = 1
output = None
try:
loop = asyncio.get_event_loop()
result = loop.run_until_complete(run_with_session(args))
output = {
"command": args.subparser_name,
"result": result,
}
res = 0
except SystemExit:
# Something called sys.exit(), lets just exit
res = 1
raise # Pass on regular exit callse
except ApiError as e:
eprint(str(e))
output = {
"command": args.subparser_name,
"error": e.repr(),
}
except UsageException as e:
eprint(str(e))
output = {
"error": {
"type": "usage",
"details": {
"message": str(e),
}
}
}
except:
ei = sys.exc_info()
eprint("Unexpected %s exception in %s: %s" % (ei[0].__name__, args.subparser_name, ei[1]))
eprint(traceback.format_exc())
output = {
"command": args.subparser_name,
"error": {
"type": "exception",
"details": {
"error-type": ei[0].__name__,
"message": str(ei[1]),
}
}
}
res = 1
if output:
if args.print_output:
print(json.dumps(output, indent=4))
if args.output:
f = open(args.output,"w+")
f.write(json.dumps(output, indent=4))
f.write("\n")
f.close()
exit(res)

View File

@ -0,0 +1,24 @@
#!/bin/bash
if [ -z "$1" ]; then
echo "Missing repo to upload!"
exit 1
fi
if [ -n "${CI_COMMIT_TAG}" ]; then
BUILD_URL=$(./flat-manager-client create https://flatpak.neko.dev stable)
elif [ "master" = "${CI_COMMIT_REF_NAME}" ]; then
BUILD_URL=$(./flat-manager-client create https://flatpak.neko.dev nightly)
fi
if [ -z "${BUILD_URL}" ]; then
echo "No upload to repo."
exit 0
fi
BUILD_URL=${BUILD_URL/http:/https:}
./flat-manager-client push $BUILD_URL $1
./flat-manager-client commit --wait $BUILD_URL
./flat-manager-client publish --wait $BUILD_URL

View File

@ -1,22 +1,11 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QBuffer>
#include <QPixmapCache>
#include <QPointer>
#include <memory>
#include <unordered_map>
@ -24,13 +13,14 @@
#include "Cache.h"
#include "Logging.h"
#include "MatrixClient.h"
#include "MxcImageProvider.h"
#include "Utils.h"
static QPixmapCache avatar_cache;
namespace AvatarProvider {
void
resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback callback)
resolve(QString avatarUrl, int size, QObject *receiver, AvatarCallback callback)
{
const auto cacheKey = QString("%1_size_%2").arg(avatarUrl).arg(size);
@ -45,44 +35,32 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca
return;
}
auto data = cache::image(cacheKey);
if (!data.isNull()) {
pixmap = QPixmap::fromImage(utils::readImage(&data));
avatar_cache.insert(cacheKey, pixmap);
callback(pixmap);
return;
}
MxcImageProvider::download(avatarUrl.remove(QStringLiteral("mxc://")),
QSize(size, size),
[callback, cacheKey, recv = QPointer<QObject>(receiver)](
QString, QSize, QImage img, QString) {
if (!recv)
return;
auto proxy = std::make_shared<AvatarProxy>();
QObject::connect(proxy.get(),
&AvatarProxy::avatarDownloaded,
receiver,
[callback, cacheKey](QByteArray data) {
QPixmap pm = QPixmap::fromImage(utils::readImage(&data));
avatar_cache.insert(cacheKey, pm);
callback(pm);
});
auto proxy = std::make_shared<AvatarProxy>();
QObject::connect(proxy.get(),
&AvatarProxy::avatarDownloaded,
recv,
[callback, cacheKey](QPixmap pm) {
if (!pm.isNull())
avatar_cache.insert(
cacheKey, pm);
callback(pm);
});
mtx::http::ThumbOpts opts;
opts.width = size;
opts.height = size;
opts.mxc_url = avatarUrl.toStdString();
if (img.isNull()) {
emit proxy->avatarDownloaded(QPixmap{});
return;
}
http::client()->get_thumbnail(
opts,
[opts, cacheKey, proxy = std::move(proxy)](const std::string &res,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn("failed to download avatar: {} - ({} {})",
opts.mxc_url,
mtx::errors::to_string(err->matrix_error.errcode),
err->matrix_error.error);
} else {
cache::saveImage(cacheKey.toStdString(), res);
}
emit proxy->avatarDownloaded(QByteArray(res.data(), (int)res.size()));
});
auto pm = QPixmap::fromImage(std::move(img));
emit proxy->avatarDownloaded(pm);
});
}
void
@ -92,8 +70,8 @@ resolve(const QString &room_id,
QObject *receiver,
AvatarCallback callback)
{
const auto avatarUrl = cache::avatarUrl(room_id, user_id);
auto avatarUrl = cache::avatarUrl(room_id, user_id);
resolve(avatarUrl, size, receiver, callback);
resolve(std::move(avatarUrl), size, receiver, callback);
}
}

View File

@ -1,38 +1,26 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QPixmap>
#include <functional>
using AvatarCallback = std::function<void(QPixmap)>;
class AvatarProxy : public QObject
{
Q_OBJECT
signals:
void avatarDownloaded(const QByteArray &data);
void avatarDownloaded(QPixmap pm);
};
using AvatarCallback = std::function<void(QPixmap)>;
namespace AvatarProvider {
void
resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback cb);
resolve(QString avatarUrl, int size, QObject *receiver, AvatarCallback cb);
void
resolve(const QString &room_id,
const QString &user_id,

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