Compare commits

...

98 commits

Author SHA1 Message Date
1b0e404fce prepare to aurora 5.1.1 2024-07-04 01:30:15 +03:00
2d1ac1e98f Merge remote-tracking branch 'my_github/aurora_beta' into aurora_beta 2024-07-03 22:34:57 +03:00
4b7bfc5f33 renamed for aurora 2024-07-03 22:34:48 +03:00
e813f334bb Merge remote-tracking branch 'handydevcom/power_saving_v3' into aurora_beta 2024-07-02 16:27:46 +03:00
74cdee5652 Merge remote-tracking branch 'handydevcom/dont_update_model_when_forwarding_message' into aurora_beta 2024-07-02 16:25:01 +03:00
25ce179a31 Merge remote-tracking branch 'handydevcom/Fix_Scroll_To_Quoted_Message' into aurora_beta 2024-07-02 16:24:22 +03:00
a11e2b9123 Merge remote-tracking branch 'origin_github/master' into aurora_beta 2024-07-02 16:20:58 +03:00
1b659ae219 Deleted yaml 2024-07-02 13:36:55 +03:00
3240a122a7 Merge remote-tracking branch 'monich/configure-reaction-button' into aurora_beta 2024-07-02 11:35:09 +03:00
48aaf577d6 Update git ignore 2024-07-02 11:24:47 +03:00
Mikhail Barashkov
15caa6f3c6 Save power by adding delay between network requests in background. 2024-07-02 11:01:39 +03:00
Mikhail Barashkov
e966eb4abd tdlibwrapper changes: refactored misleadingly names variables allUsers and to more proper names usersById and usersByName; fixed handleUserUpdated function as it mixed up userInformation parameter with userInformation member variable; made a dedicated method updateUserInformation to update both usersById and usersByName; eliminated unneeded status updates when user status didn't really change (those tend to happen a lot). 2024-06-21 17:32:25 +03:00
Mikhail Barashkov
23d361a987 Avoid UI updates in Chat selection screen when forwarding a chat, by cloning the chat list model. 2024-06-15 14:19:59 +03:00
Slava Monich
fd7563bc88 Force xz rpm compression
That improves the chances of rpm being installable on a system that
doesn't support zstd compression
2024-05-18 14:24:56 +02:00
Mikhail Barashkov
4eb718b02e Fix scroll to quoted message #566 2024-05-14 18:41:28 +03:00
Mikhail Barashkov
a984b94f2f Merge branch 'wunderfitz_master' into mbarashkov_master 2024-05-14 18:12:33 +03:00
jgibbon
0817e8c05b rebuild translation files + slight cleanup 2024-05-09 21:58:57 +02:00
jgibbon
80f76c8eb8 fix range + message updates; implement album filter 2024-05-04 21:30:32 +02:00
Sebastian Wolf
6ec3d2931f Restore content width in landscape as per discussion in #540 2024-05-04 22:25:56 +03:00
free software
053f5eae10 Update harbour-fernschreiber-es.ts (#542) 2024-05-04 22:25:56 +03:00
mbarashkov
3ac98a4e17 Jump to post from quote (#538)
* Jump to post from quote

* Add a setting to go to quoted message.

---------

Co-authored-by: Mikhail Barashkov <git@mbarashkov.ru>
2024-05-04 22:25:56 +03:00
Sebastian Wolf
4fc0ce70b5 Not only tablets have widescreen ;) 2024-05-04 22:25:56 +03:00
mbarashkov
06dcda1fd4 Improve chat UI on tablets by making messages narrower and limiting content items width as well. (#540)
Co-authored-by: Mikhail Barashkov <git@mbarashkov.ru>
2024-05-04 22:25:56 +03:00
Sebastian Wolf
18957e5f9d Handle event updateChatAvailableReactions 2024-05-04 22:25:55 +03:00
Sebastian Wolf
24d4b9ffb1 Tweak reactions again... 2024-05-04 22:25:37 +03:00
Patrick Hervieux
d6444c22b2 Update French translation (#532)
Co-authored-by: Patrick Hervieux <patrick.hervieux+git@pherjung.ch>
2024-05-04 22:23:21 +03:00
free software
20be5acfdc Update harbour-fernschreiber-es.ts (#529)
* Update harbour-fernschreiber-es.ts

* Update harbour-fernschreiber-es.ts
2024-05-04 22:23:07 +03:00
Slava Monich
fd8dee466e Updated Russian translation (#528) 2024-05-04 22:23:07 +03:00
Sebastian Wolf
72c4419a48 Test build no.4 2024-05-04 22:23:06 +03:00
Sebastian Wolf
02573174fc Minor tweaks for the new session timout setting 2024-05-04 22:22:54 +03:00
Sebastian Wolf
1e88f3c9ba Fix chat permissions handling 2024-05-04 22:22:54 +03:00
Slava Monich
4ea558bfa5 Tweaked notification feedback settings UI (#526) 2024-05-04 22:22:54 +03:00
Sebastian Wolf
ea513a5e42 Enable privileged mode for contacts sync 2024-05-04 22:22:52 +03:00
Sebastian Wolf
070f3e1bd7 Some minor adjustments 2024-05-04 22:22:18 +03:00
Johannes Bachmann
77f03f1f02 Always append last message content to notifications (#514)
* always append last message content to notifications

* make "always show notification" configurable

* add unfinished translations

* Fix spacing if no sender is printed
2024-05-04 22:21:03 +03:00
547b6b1e0b Fixed 2024-05-04 22:19:40 +03:00
659cf4e8ca Fixed 2024-05-04 22:19:40 +03:00
234a84ac04 Fixed merge 2024-05-04 22:19:40 +03:00
4471e543d2 merge translation 2024-05-04 19:45:21 +03:00
42e59badae Merged 2024-05-04 18:41:19 +03:00
7e125aabf6 Fixed merged problems in translation 2024-05-04 18:19:28 +03:00
7847a22a3e Mergerd 2024-05-04 17:23:27 +03:00
Mikhail Barashkov
3a8f20a1ce Fix Copy message to clipboard not working from extended menu 2024-02-05 13:43:12 +01:00
jgibbon
fafa2ba355
Emoji update (#563)
* Update twemoji.js

* add new emojis

fixes #561

* update emoji db
2024-02-01 22:08:45 +01:00
Mikhail Barashkov
e0019d33d6 Fix not all possible destinations shown to forward a message. 2024-01-24 03:58:16 +02:00
jgibbon
4857b14fb8 fix range + message updates; implement album filter 2023-12-28 13:03:13 +01:00
Slava Monich
f241703784 Make the reaction button configurable 2023-12-07 02:54:09 +02:00
Åke Engelbrektson
2fad48e359
Update harbour-fernschreiber-sv.ts (#547)
Update Swedish translation
2023-12-04 22:19:53 +01:00
okruhliak
047ea51d0e
Update harbour-fernschreiber-sk.ts (#545) 2023-12-04 22:19:07 +01:00
free software
a1277fe79a
Improvements: harbour-fernschreiber-es.ts (#544) 2023-12-04 22:18:30 +01:00
Sebastian Wolf
ab647e6e4d
Switch to gh tool for release creation, see https://github.com/actions/runner-images/issues/8362 2023-12-03 14:30:57 +01:00
Sebastian Wolf
4af73184b2
Prepare 0.17 release 2023-12-03 13:22:45 +01:00
Sebastian Wolf
c1ab982cb1
Restore content width in landscape as per discussion in #540 2023-12-03 12:59:41 +01:00
Slava Monich
b2363af36f
Tweaked the logic of moving reactions into the view (#543)
There's no need to reposition list items if reactions bar are already
fully visible.
2023-12-03 12:53:32 +01:00
free software
47ee85915d
Update harbour-fernschreiber-es.ts (#542) 2023-12-03 10:48:20 +01:00
Sebastian Wolf
4ed9495376
Highlight message that was jumped to 2023-12-03 00:48:41 +01:00
mbarashkov
2dd1d2c380
Jump to post from quote (#538)
* Jump to post from quote

* Add a setting to go to quoted message.

---------

Co-authored-by: Mikhail Barashkov <git@mbarashkov.ru>
2023-12-03 00:46:47 +01:00
Sebastian Wolf
bba4a6468c
Not only tablets have widescreen ;) 2023-12-02 23:50:13 +01:00
mbarashkov
64479f0dd3
Improve chat UI on tablets by making messages narrower and limiting content items width as well. (#540)
Co-authored-by: Mikhail Barashkov <git@mbarashkov.ru>
2023-12-02 23:44:14 +01:00
Slava Monich
8eda82da18
Fix broken signal-slot connection (#541)
QObject::connect: No such signal TDLibWrapper::chatAvailableReactionsUpdated(qlonglong, QString) in src/chatlistmodel.cpp:410
2023-12-02 23:12:52 +01:00
Sebastian Wolf
07fdabc178
Handle event updateChatAvailableReactions 2023-12-01 00:23:23 +01:00
Sebastian Wolf
80ecf50e74
Only one star per list, restore users in poll results 2023-11-29 23:47:59 +01:00
Sebastian Wolf
8258867407
Open URLs without prefix 2023-11-27 21:29:43 +01:00
Sebastian Wolf
61faf0f407
More tweaks for reactions 2023-11-26 23:36:29 +01:00
Sebastian Wolf
24edb17347
Animate new reaction button 2023-11-26 20:53:09 +01:00
Sebastian Wolf
98f067cda8
Tweak reactions again... 2023-11-26 20:36:30 +01:00
Peter G
57017e8bff
[Trivial]: Don't own bindir (#534)
* [Trivial]: Don't own bindir

This may seem like nitpicking, but the package should not own /usr/bin

* Update rpm/harbour-fernschreiber.spec

Co-authored-by: Sebastian Wolf <sebastian@ygriega.de>

---------

Co-authored-by: nephros <nemo@pgxperiiia10>
Co-authored-by: Sebastian Wolf <sebastian@ygriega.de>
2023-11-26 20:12:47 +01:00
Slava Monich
84594e4c2c
Allow to remove a reaction (#536) 2023-11-26 18:05:51 +01:00
Patrick Hervieux
2ae4e2eb05
Update French translation (#532)
Co-authored-by: Patrick Hervieux <patrick.hervieux+git@pherjung.ch>
2023-11-24 23:13:18 +01:00
free software
bada6fb3e2
Update harbour-fernschreiber-es.ts (#531) 2023-11-24 23:11:41 +01:00
Sebastian Wolf
16bcef3c78
Interaction hint for new reactions behavior 2023-11-22 22:53:17 +01:00
Sebastian Wolf
02b6dd2e6d
Show reactions on double-click 2023-11-21 21:55:08 +01:00
Slava Monich
256514e55d
Added "unread mention" indicator to the chat list (#530)
It's displayed in place of the "unread reaction" indicator. In case
if there are both unread mentions and reactions, "unread mention"
takes precedence.
2023-11-21 21:34:10 +01:00
free software
4b9732b64f
Update harbour-fernschreiber-es.ts (#529)
* Update harbour-fernschreiber-es.ts

* Update harbour-fernschreiber-es.ts
2023-11-21 21:31:41 +01:00
Slava Monich
75bf146154
Updated Russian translation (#528) 2023-11-21 21:31:23 +01:00
Sebastian Wolf
58382ac1b6
Test build no.4 2023-11-20 22:34:52 +01:00
Sebastian Wolf
5b2fa38f7e
Display reactions on last message again 2023-11-20 21:53:57 +01:00
Sebastian Wolf
8d59ee4c00
Restore search in chats 2023-11-20 21:27:55 +01:00
Sebastian Wolf
acfcfd3ca4
Minor tweaks for the new session timout setting 2023-11-19 22:51:33 +01:00
Slava Monich
195e255f08
Added UI for configuring session inactivity timeout (#527) 2023-11-19 21:54:50 +01:00
Sebastian Wolf
8531a46015
More places affected by new username handling 2023-11-19 21:49:00 +01:00
Sebastian Wolf
f133586aa2
Hide unsupported emojis for reactions 2023-11-19 14:36:06 +01:00
Sebastian Wolf
ea89ef4fcd
Make replies backward-compatible again 2023-11-19 14:24:19 +01:00
Sebastian Wolf
00deaa79fd
Fix chat permissions handling 2023-11-19 13:34:31 +01:00
Slava Monich
61e3e366e6
Tweaked notification feedback settings UI (#526) 2023-11-19 12:52:18 +01:00
Sebastian Wolf
0224d2f338
Trouble with usernames 2023-11-19 00:18:29 +01:00
Sebastian Wolf
3ac8cebc06
Enable privileged mode for contacts sync 2023-11-18 23:45:10 +01:00
Sebastian Wolf
f771a9a81b
Some minor adjustments 2023-11-18 23:39:09 +01:00
Johannes Bachmann
b11f7dd8b1
Always append last message content to notifications (#514)
* always append last message content to notifications

* make "always show notification" configurable

* add unfinished translations

* Fix spacing if no sender is printed
2023-11-18 23:11:17 +01:00
Peter G
3620b8ed03
Add option to suppress notification previews (#521)
* Add Switch in Settings

* Don't set notification preview body

* Support the setting in appSettings

* fixup! Add Switch in Settings

* Just show message count

* Also show only when notifications are enabled at all

---------

Co-authored-by: nephros <nemo@pgxperiiia10>
2023-11-18 22:58:05 +01:00
Sebastian Wolf
e44c69281c
Don't enable highlight conversations by default 2023-11-18 22:44:49 +01:00
Peter G
3ab0b94831
Highlight unread Converstations (#513)
* Highlight unread conversations

See: #512

* make highlighting configurable

* more verbose variable names

* remove the rectangle gain, it is too annoying

* respect the setting

---------

Co-authored-by: nephros <nemo@pgxperiiia10>
2023-11-18 22:40:12 +01:00
jgibbon
1ffcfb48ca
Add specific unread info for higher counts of unread messages (#516) 2023-11-18 22:24:27 +01:00
okruhliak
6b0e54ec65
Update harbour-fernschreiber-sk.ts (#520)
* Update harbour-fernschreiber-sk.ts

* Update harbour-fernschreiber-sk.ts
2023-11-18 22:19:35 +01:00
Peter G
de7b119941
Move macros to yaml (#523)
Co-authored-by: nephros <nemo@pgxperiiia10>
2023-11-18 22:18:47 +01:00
Slava Monich
527b2c3c9a
Adapt to changes in reply message info (#525)
1.8.14:
https://github.com/tdlib/td/commit/fa94aba

1.8.21:
https://github.com/tdlib/td/commit/811a7c6
https://github.com/tdlib/td/commit/5216ea1
2023-11-18 22:14:59 +01:00
Sebastian Wolf
2b51405743
Only expect chat partner information in private chats 2023-11-18 15:35:02 +01:00
Slava Monich
1ee8d134bc
Adapt to changes in TdLib (#524)
* Adapt setTdlibParameters for TdLib > 1.8.5

For some reason tdlibParameters were inlined between 1.8.5 and 1.8.6
See https://github.com/tdlib/td/commit/f6a2ecd

* sponsoredMessage => sponsoredMessages in TdLib 1.8.8

See https://github.com/tdlib/td/commit/ec1310a

* Support another variant of messageReaction

The reaction field has changed from string to ReactionType somewhere
between 1.8.5 and 1.8.6

See https://github.com/tdlib/td/commit/b14708f

* Add support for new message reactions API

It has changed between 1.8.5 and 1.8.6

https://github.com/tdlib/td/commit/b14708f (ReactionType)
https://github.com/tdlib/td/commit/0b8e143 (ChatAvailableReactions)
https://github.com/tdlib/td/commit/6b2f6b4 (addMessageReaction)
https://github.com/tdlib/td/commit/d29d367 (updateActiveEmojiReactions)

etc.

* Highlight chosen reaction

* Support username in the new format

username attribute has been replaced with usernames in 1.8.8 and
now looks like this:

    "usernames": {
        "@type": "usernames",
        "active_usernames": [
            "whatever"
        ],
        "disabled_usernames": [
        ],
        "editable_username": "whatever"
    }

See https://github.com/tdlib/td/commit/897032e

* Support new reply_to message attribute

Since 1.8.15 it replaces reply_to_message_id and reply_in_chat_id.
Looks like this:

    "reply_to": {
        "@type": "messageReplyToMessage",
        "chat_id": -1001234567890,
        "is_quote_manual": false,
        "message_id": 234567890,
        "origin_send_date": 0
    },

See https://github.com/tdlib/td/commit/6116573

* Added support for MessageOrigin values

All of a sudden MessageForwardOrigin has been renamed into MessageOrigin
in TdLib 1.8.20 just because why not:

https://github.com/tdlib/td/commit/10c9e40
2023-11-18 14:45:22 +01:00
103 changed files with 28601 additions and 288 deletions

4
.gitignore vendored
View file

@ -31,6 +31,7 @@ Makefile*
*build-* *build-*
*.qm *.qm
*.prl *.prl
*.list
# Qt unit tests # Qt unit tests
target_wrapper.* target_wrapper.*
@ -56,3 +57,6 @@ tdlibsecrets.h
#Convinience scripts #Convinience scripts
*.sh *.sh
#vscode
.vscode

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -1,11 +1,13 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
X-Nemo-Application-Type=silica-qt5 X-Nemo-Application-Type=silica-qt5
Icon=harbour-fernschreiber Icon=org.ygriega.Fernschreiber
Exec=harbour-fernschreiber Exec=/usr/bin/org.ygriega.Fernschreiber
Name=Fernschreiber Name=Fernschreiber
Name[ru]=Fernschreiber
[X-Sailjail] [X-Application]
Permissions=Audio;Documents;Downloads;Internet;Location;MediaIndexing;Microphone;Music;Pictures;PublicDir;RemovableMedia;UserDirs;Videos Permissions=Audio;Documents;Downloads;Internet;Location;MediaIndexing;Microphone;Music;Pictures;PublicDir;RemovableMedia;UserDirs;Videos
OrganizationName=de.ygriega OrganizationName=org.ygriega
ApplicationName=fernschreiber ApplicationName=Fernschreiber
ExecDBus=/usr/bin/org.ygriega.Fernschreiber

View file

@ -10,9 +10,9 @@
# - translation filenames have to be changed # - translation filenames have to be changed
# The name of your application # The name of your application
TARGET = harbour-fernschreiber TARGET = org.ygriega.Fernschreiber
CONFIG += sailfishapp sailfishapp_i18n CONFIG += auroraapp sailfishapp_i18n
PKGCONFIG += nemonotifications-qt5 zlib PKGCONFIG += nemonotifications-qt5 zlib
@ -22,6 +22,7 @@ DEFINES += QT_STATICPLUGIN
SOURCES += src/harbour-fernschreiber.cpp \ SOURCES += src/harbour-fernschreiber.cpp \
src/appsettings.cpp \ src/appsettings.cpp \
src/boolfiltermodel.cpp \
src/chatpermissionfiltermodel.cpp \ src/chatpermissionfiltermodel.cpp \
src/chatlistmodel.cpp \ src/chatlistmodel.cpp \
src/chatmodel.cpp \ src/chatmodel.cpp \
@ -105,14 +106,21 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/messageContent/MessageGame.qml \ qml/components/messageContent/MessageGame.qml \
qml/components/messageContent/MessageLocation.qml \ qml/components/messageContent/MessageLocation.qml \
qml/components/messageContent/MessagePhoto.qml \ qml/components/messageContent/MessagePhoto.qml \
qml/components/messageContent/MessagePhotoAlbum.qml \
qml/components/messageContent/MessagePoll.qml \ qml/components/messageContent/MessagePoll.qml \
qml/components/messageContent/MessageSticker.qml \ qml/components/messageContent/MessageSticker.qml \
qml/components/messageContent/MessageVenue.qml \ qml/components/messageContent/MessageVenue.qml \
qml/components/messageContent/MessageVideoAlbum.qml \
qml/components/messageContent/MessageVideoNote.qml \ qml/components/messageContent/MessageVideoNote.qml \
qml/components/messageContent/MessageVideo.qml \ qml/components/messageContent/MessageVideo.qml \
qml/components/messageContent/MessageVoiceNote.qml \ qml/components/messageContent/MessageVoiceNote.qml \
qml/components/messageContent/SponsoredMessage.qml \ qml/components/messageContent/SponsoredMessage.qml \
qml/components/messageContent/WebPagePreview.qml \ qml/components/messageContent/WebPagePreview.qml \
qml/components/messageContent/mediaAlbumPage/FullscreenOverlay.qml \
qml/components/messageContent/mediaAlbumPage/PhotoComponent.qml \
qml/components/messageContent/mediaAlbumPage/VideoComponent.qml \
qml/components/messageContent/mediaAlbumPage/ZoomArea.qml \
qml/components/messageContent/mediaAlbumPage/ZoomImage.qml \
qml/components/settingsPage/Accordion.qml \ qml/components/settingsPage/Accordion.qml \
qml/components/settingsPage/AccordionItem.qml \ qml/components/settingsPage/AccordionItem.qml \
qml/components/settingsPage/ResponsiveGrid.qml \ qml/components/settingsPage/ResponsiveGrid.qml \
@ -130,6 +138,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/pages/CoverPage.qml \ qml/pages/CoverPage.qml \
qml/pages/DebugPage.qml \ qml/pages/DebugPage.qml \
qml/pages/InitializationPage.qml \ qml/pages/InitializationPage.qml \
qml/pages/MediaAlbumPage.qml \
qml/pages/NewChatPage.qml \ qml/pages/NewChatPage.qml \
qml/pages/OverviewPage.qml \ qml/pages/OverviewPage.qml \
qml/pages/AboutPage.qml \ qml/pages/AboutPage.qml \
@ -138,25 +147,25 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/pages/SearchChatsPage.qml \ qml/pages/SearchChatsPage.qml \
qml/pages/SettingsPage.qml \ qml/pages/SettingsPage.qml \
qml/pages/VideoPage.qml \ qml/pages/VideoPage.qml \
rpm/harbour-fernschreiber.changes \ rpm/org.ygriega.Fernschreiber.changes \
rpm/harbour-fernschreiber.spec \ rpm/org.ygriega.Fernschreiber.spec \
translations/*.ts \ translations/*.ts \
harbour-fernschreiber.desktop org.ygriega.Fernschreiber.desktop
SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172 256x256 AURORAAPP_ICONS = 86x86 108x108 128x128 172x172 256x256
TRANSLATIONS += translations/harbour-fernschreiber-de.ts \ TRANSLATIONS += translations/org.ygriega.Fernschreiber-de.ts \
translations/harbour-fernschreiber-es.ts \ translations/org.ygriega.Fernschreiber-es.ts \
translations/harbour-fernschreiber-fi.ts \ translations/org.ygriega.Fernschreiber-fi.ts \
translations/harbour-fernschreiber-fr.ts \ translations/org.ygriega.Fernschreiber-fr.ts \
translations/harbour-fernschreiber-hu.ts \ translations/org.ygriega.Fernschreiber-hu.ts \
translations/harbour-fernschreiber-it.ts \ translations/org.ygriega.Fernschreiber-it.ts \
translations/harbour-fernschreiber-pl.ts \ translations/org.ygriega.Fernschreiber-pl.ts \
translations/harbour-fernschreiber-ru.ts \ translations/org.ygriega.Fernschreiber-ru.ts \
translations/harbour-fernschreiber-sv.ts \ translations/org.ygriega.Fernschreiber-sv.ts \
translations/harbour-fernschreiber-sk.ts \ translations/org.ygriega.Fernschreiber-sk.ts \
translations/harbour-fernschreiber-en.ts \ translations/org.ygriega.Fernschreiber-en.ts \
translations/harbour-fernschreiber-zh_CN.ts translations/org.ygriega.Fernschreiber-zh_CN.ts
equals(QT_ARCH, arm) { equals(QT_ARCH, arm) {
message(Building ARM) message(Building ARM)
@ -186,22 +195,22 @@ images.path = /usr/share/$${TARGET}
ICONPATH = /usr/share/icons/hicolor ICONPATH = /usr/share/icons/hicolor
86.png.path = $${ICONPATH}/86x86/apps/ 86.png.path = $${ICONPATH}/86x86/apps/
86.png.files += icons/86x86/harbour-fernschreiber.png 86.png.files += icons/86x86/$${TARGET}.png
108.png.path = $${ICONPATH}/108x108/apps/ 108.png.path = $${ICONPATH}/108x108/apps/
108.png.files += icons/108x108/harbour-fernschreiber.png 108.png.files += icons/108x108/$${TARGET}.png
128.png.path = $${ICONPATH}/128x128/apps/ 128.png.path = $${ICONPATH}/128x128/apps/
128.png.files += icons/128x128/harbour-fernschreiber.png 128.png.files += icons/128x128/$${TARGET}.png
172.png.path = $${ICONPATH}/172x172/apps/ 172.png.path = $${ICONPATH}/172x172/apps/
172.png.files += icons/172x172/harbour-fernschreiber.png 172.png.files += icons/172x172/$${TARGET}.png
256.png.path = $${ICONPATH}/256x256/apps/ 256.png.path = $${ICONPATH}/256x256/apps/
256.png.files += icons/256x256/harbour-fernschreiber.png 256.png.files += icons/256x256/$${TARGET}.png
fernschreiber.desktop.path = /usr/share/applications/ fernschreiber.desktop.path = /usr/share/applications/
fernschreiber.desktop.files = harbour-fernschreiber.desktop fernschreiber.desktop.files = $${TARGET}.desktop
database.files = db database.files = db
database.path = /usr/share/$${TARGET} database.path = /usr/share/$${TARGET}
@ -211,6 +220,7 @@ INSTALLS += telegram 86.png 108.png 128.png 172.png 256.png \
HEADERS += \ HEADERS += \
src/appsettings.h \ src/appsettings.h \
src/boolfiltermodel.h \
src/chatpermissionfiltermodel.h \ src/chatpermissionfiltermodel.h \
src/chatlistmodel.h \ src/chatlistmodel.h \
src/chatmodel.h \ src/chatmodel.h \

View file

@ -32,6 +32,7 @@ ListItem {
property int messageIndex property int messageIndex
property int messageViewCount property int messageViewCount
property var myMessage property var myMessage
property var messageAlbumMessageIds
property var reactions property var reactions
property bool canReplyToMessage property bool canReplyToMessage
readonly property bool isAnonymous: myMessage.sender_id["@type"] === "messageSenderChat" readonly property bool isAnonymous: myMessage.sender_id["@type"] === "messageSenderChat"
@ -68,7 +69,7 @@ ListItem {
property var chatReactions property var chatReactions
property var messageReactions property var messageReactions
highlighted: (down || isSelected || additionalOptionsOpened || wasNavigatedTo) && !menuOpen highlighted: (down || (isSelected && messageAlbumMessageIds.length === 0) || additionalOptionsOpened || wasNavigatedTo) && !menuOpen
openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting
signal replyToMessage() signal replyToMessage()
@ -159,15 +160,7 @@ ListItem {
} }
onDoubleClicked: { onDoubleClicked: {
if (messageListItem.chatReactions) { openReactions();
Debug.log("Using chat reactions")
messageListItem.messageReactions = chatReactions
showItemCompletelyTimer.requestedIndex = index;
showItemCompletelyTimer.start();
} else {
Debug.log("Obtaining message reactions")
tdLibWrapper.getMessageAvailableReactions(messageListItem.chatId, messageListItem.messageId);
}
} }
onPressAndHold: { onPressAndHold: {
@ -276,20 +269,20 @@ ListItem {
Connections { Connections {
target: chatModel target: chatModel
onMessagesReceived: { onMessagesReceived: {
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"; messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
} }
onMessagesIncrementalUpdate: { onMessagesIncrementalUpdate: {
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"; messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
} }
onNewMessageReceived: { onNewMessageReceived: {
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"; messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
} }
onUnreadCountUpdated: { onUnreadCountUpdated: {
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"; messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
} }
onLastReadSentMessageUpdated: { onLastReadSentMessageUpdated: {
Debug.log("[ChatModel] Messages in this chat were read, new last read: ", lastReadSentIndex, ", updating description for index ", index, ", status: ", (index <= lastReadSentIndex)); Debug.log("[ChatModel] Messages in this chat were read, new last read: ", lastReadSentIndex, ", updating description for index ", index, ", status: ", (messageIndex <= lastReadSentIndex));
messageDateText.text = getMessageStatusText(myMessage, index, lastReadSentIndex, messageDateText.useElapsed); messageDateText.text = getMessageStatusText(myMessage, messageIndex, lastReadSentIndex, messageDateText.useElapsed);
} }
} }
@ -310,7 +303,7 @@ ListItem {
pageStack.currentPage === chatPage) { pageStack.currentPage === chatPage) {
Debug.log("Available reactions for this message: " + reactions); Debug.log("Available reactions for this message: " + reactions);
messageListItem.messageReactions = reactions; messageListItem.messageReactions = reactions;
showItemCompletelyTimer.requestedIndex = index; showItemCompletelyTimer.requestedIndex = messageIndex;
showItemCompletelyTimer.start(); showItemCompletelyTimer.start();
} else { } else {
messageListItem.messageReactions = null; messageListItem.messageReactions = null;
@ -331,6 +324,13 @@ ListItem {
interval: 200 interval: 200
triggeredOnStart: false triggeredOnStart: false
onTriggered: { onTriggered: {
if (requestedIndex === messageIndex) {
chatView.highlightMoveDuration = -1;
chatView.highlightResizeDuration = -1;
chatView.scrollToIndex(requestedIndex);
chatView.highlightMoveDuration = 0;
chatView.highlightResizeDuration = 0;
}
Debug.log("Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index) Debug.log("Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index)
if (requestedIndex === index) { if (requestedIndex === index) {
var p = chatView.contentItem.mapFromItem(reactionsColumn, 0, 0) var p = chatView.contentItem.mapFromItem(reactionsColumn, 0, 0)
@ -384,8 +384,10 @@ ListItem {
onTriggered: { onTriggered: {
if (messageListItem.hasContentComponent) { if (messageListItem.hasContentComponent) {
var type = myMessage.content["@type"]; var type = myMessage.content["@type"];
var albumComponentPart = (myMessage.media_album_id !== "0" && ['messagePhoto', 'messageVideo'].indexOf(type) !== -1) ? 'Album' : '';
console.log('delegateComponentLoadingTimer', myMessage.media_album_id, albumComponentPart)
extraContentLoader.setSource( extraContentLoader.setSource(
"../components/messageContent/" + type.charAt(0).toUpperCase() + type.substring(1) + ".qml", "../components/messageContent/" + type.charAt(0).toUpperCase() + type.substring(1) + albumComponentPart + ".qml",
{ {
messageListItem: messageListItem messageListItem: messageListItem
}) })
@ -449,7 +451,7 @@ ListItem {
} }
height: messageTextColumn.height + precalculatedValues.paddingMediumDouble height: messageTextColumn.height + precalculatedValues.paddingMediumDouble
width: precalculatedValues.backgroundWidth width: precalculatedValues.backgroundWidth
property bool isUnread: index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage" property bool isUnread: messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"
color: Theme.colorScheme === Theme.LightOnDark ? (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.secondaryHighlightColor : Theme.secondaryColor)) : (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.backgroundGlowColor : Theme.overlayBackgroundColor)) color: Theme.colorScheme === Theme.LightOnDark ? (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.secondaryHighlightColor : Theme.secondaryColor)) : (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.backgroundGlowColor : Theme.overlayBackgroundColor))
radius: parent.width / 50 radius: parent.width / 50
opacity: isUnread ? 0.5 : 0.2 opacity: isUnread ? 0.5 : 0.2
@ -471,7 +473,13 @@ ListItem {
id: userText id: userText
width: parent.width width: parent.width
text: messageListItem.isOwnMessage ? qsTr("You") : Emoji.emojify( myMessage['@type'] === "sponsoredMessage" ? tdLibWrapper.getChat(myMessage.sponsor_chat_id).title : ( messageListItem.isAnonymous ? page.chatInformation.title : Functions.getUserName(messageListItem.userInformation) ), font.pixelSize) text: messageListItem.isOwnMessage
? qsTr("You")
: Emoji.emojify( myMessage['@type'] === "sponsoredMessage"
? tdLibWrapper.getChat(myMessage.sponsor_chat_id).title
: ( messageListItem.isAnonymous
? page.chatInformation.title
: Functions.getUserName(messageListItem.userInformation) ), font.pixelSize)
font.pixelSize: Theme.fontSizeExtraSmall font.pixelSize: Theme.fontSizeExtraSmall
font.weight: Font.ExtraBold font.weight: Font.ExtraBold
color: messageListItem.textColor color: messageListItem.textColor
@ -654,7 +662,8 @@ ListItem {
id: extraContentLoader id: extraContentLoader
width: parent.width * getContentWidthMultiplier() width: parent.width * getContentWidthMultiplier()
asynchronous: true asynchronous: true
height: item ? item.height : (messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width) : 0) readonly property var defaultExtraContentHeight: messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width, model.album_message_ids.length) : 0
height: item ? item.height : defaultExtraContentHeight
} }
Binding { Binding {
@ -679,7 +688,7 @@ ListItem {
running: true running: true
repeat: true repeat: true
onTriggered: { onTriggered: {
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed); messageDateText.text = getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed);
} }
} }
@ -692,13 +701,13 @@ ListItem {
font.pixelSize: Theme.fontSizeTiny font.pixelSize: Theme.fontSizeTiny
color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor
horizontalAlignment: messageListItem.textAlign horizontalAlignment: messageListItem.textAlign
text: getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed) text: getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed)
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
enabled: !messageListItem.precalculatedValues.pageIsSelecting enabled: !messageListItem.precalculatedValues.pageIsSelecting
onClicked: { onClicked: {
messageDateText.useElapsed = !messageDateText.useElapsed; messageDateText.useElapsed = !messageDateText.useElapsed;
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed); messageDateText.text = getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed);
} }
} }
} }
@ -719,12 +728,50 @@ ListItem {
textFormat: Text.StyledText textFormat: Text.StyledText
maximumLineCount: 1 maximumLineCount: 1
elide: Text.ElideRight elide: Text.ElideRight
MouseArea {
anchors.fill: parent
onClicked: {
if (messageListItem.messageReactions) {
messageListItem.messageReactions = null;
selectReactionBubble.visible = false;
} else {
openReactions();
}
}
}
} }
} }
} }
} }
Rectangle {
id: selectReactionBubble
visible: false
opacity: visible ? 0.5 : 0.0
Behavior on opacity { NumberAnimation {} }
anchors {
horizontalCenter: messageListItem.isOwnMessage ? messageBackground.left : messageBackground.right
verticalCenter: messageBackground.verticalCenter
}
height: Theme.itemSizeExtraSmall
width: Theme.itemSizeExtraSmall
color: Theme.primaryColor
radius: parent.width / 2
}
IconButton {
id: selectReactionButton
visible: selectReactionBubble.visible
opacity: visible ? 1.0 : 0.0
Behavior on opacity { NumberAnimation {} }
icon.source: "image://theme/icon-s-favorite"
anchors.centerIn: selectReactionBubble
onClicked: {
openReactions();
}
}
} }
} }
@ -733,7 +780,7 @@ ListItem {
id: reactionsColumn id: reactionsColumn
width: parent.width - ( 2 * Theme.horizontalPageMargin ) width: parent.width - ( 2 * Theme.horizontalPageMargin )
anchors.top: messageTextRow.bottom anchors.top: messageTextRow.bottom
anchors.topMargin: Theme.paddingSmall anchors.topMargin: Theme.paddingMedium
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: messageListItem.messageReactions ? ( messageListItem.messageReactions.length > 0 ? true : false ) : false visible: messageListItem.messageReactions ? ( messageListItem.messageReactions.length > 0 ? true : false ) : false
opacity: messageListItem.messageReactions ? ( messageListItem.messageReactions.length > 0 ? 1 : 0 ) : 0 opacity: messageListItem.messageReactions ? ( messageListItem.messageReactions.length > 0 ? 1 : 0 ) : 0
@ -742,7 +789,7 @@ ListItem {
Flickable { Flickable {
width: parent.width width: parent.width
height: reactionsResultRow.height + Theme.paddingSmall height: reactionsResultRow.height + 2 * Theme.paddingMedium
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
contentWidth: reactionsResultRow.width contentWidth: reactionsResultRow.width
clip: true clip: true
@ -758,13 +805,13 @@ ListItem {
Row { Row {
id: singleReactionRow id: singleReactionRow
spacing: Theme.paddingSmall spacing: Theme.paddingMedium
Image { Image {
id: emojiPicture id: emojiPicture
source: Emoji.getEmojiPath(modelData) source: Emoji.getEmojiPath(modelData)
width: status === Image.Ready ? Theme.fontSizeLarge : 0 width: status === Image.Ready ? Theme.fontSizeExtraLarge : 0
height: Theme.fontSizeLarge height: Theme.fontSizeExtraLarge
} }
} }
@ -772,12 +819,26 @@ ListItem {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
tdLibWrapper.setMessageReaction(messageListItem.chatId, messageListItem.messageId, modelData); for (var i = 0; i < reactions.length; i++) {
messageListItem.messageReactions = null; var reaction = reactions[i]
var reactionText = reaction.reaction ? reaction.reaction : (reaction.type && reaction.type.emoji) ? reaction.type.emoji : ""
if (reactionText === modelData) {
if (reaction.is_chosen) {
// Reaction is already selected
tdLibWrapper.removeMessageReaction(chatId, messageId, reactionText)
messageReactions = null
return
}
break
}
}
// Reaction is not yet selected
tdLibWrapper.addMessageReaction(chatId, messageId, modelData)
messageReactions = null
selectReactionBubble.visible = false
} }
} }
} }
} }
} }
} }

View file

@ -0,0 +1,19 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Rectangle {
id: button
height: Theme.itemSizeExtraSmall
width: Theme.itemSizeExtraSmall
color: Theme.rgba(Theme.primaryColor, 0.4)
radius: width / 2
signal clicked()
IconButton {
icon.source: "image://theme/icon-s-favorite"
anchors.centerIn: parent
onClicked: button.clicked()
}
}

View file

@ -24,6 +24,7 @@ Loader {
id: loader id: loader
property var minithumbnail property var minithumbnail
property bool highlighted property bool highlighted
property int fillMode: tdLibImage.fillMode
anchors.fill: parent anchors.fill: parent
active: !!minithumbnail active: !!minithumbnail
sourceComponent: Component { sourceComponent: Component {
@ -32,7 +33,7 @@ Loader {
id: minithumbnailImage id: minithumbnailImage
anchors.fill: parent anchors.fill: parent
source: "data:image/jpg;base64,"+minithumbnail.data source: "data:image/jpg;base64,"+minithumbnail.data
fillMode: tdLibImage.fillMode fillMode: loader.fillMode
opacity: status === Image.Ready ? 1.0 : 0.0 opacity: status === Image.Ready ? 1.0 : 0.0
cache: false cache: false
visible: opacity > 0 visible: opacity > 0
@ -43,12 +44,12 @@ Loader {
effect: PressEffect { source: minithumbnailImage } effect: PressEffect { source: minithumbnailImage }
} }
} }
// this had a visible impact on performance
FastBlur { // FastBlur {
anchors.fill: parent // anchors.fill: parent
source: minithumbnailImage // source: minithumbnailImage
radius: Theme.paddingLarge // radius: Theme.paddingLarge
} // }
} }
} }
} }

View file

@ -59,7 +59,7 @@ Item {
readonly property bool hasVisibleThumbnail: thumbnailImage.opacity !== 1.0 readonly property bool hasVisibleThumbnail: thumbnailImage.opacity !== 1.0
&& !(videoThumbnailLoader.item && videoThumbnailLoader.item.opacity === 1.0) && !(videoThumbnailLoader.item && videoThumbnailLoader.item.opacity === 1.0)
property alias fillMode: thumbnailImage.fillMode
layer { layer {
enabled: highlighted enabled: highlighted
effect: PressEffect { source: tdlibThumbnail } effect: PressEffect { source: tdlibThumbnail }
@ -67,6 +67,7 @@ Item {
TDLibMinithumbnail { TDLibMinithumbnail {
id: minithumbnailLoader id: minithumbnailLoader
fillMode: thumbnailImage.fillMode
active: !!minithumbnail && thumbnailImage.opacity < 1.0 active: !!minithumbnail && thumbnailImage.opacity < 1.0
} }
BackgroundImage { BackgroundImage {
@ -103,6 +104,7 @@ Item {
sourceSize.width: width sourceSize.width: width
sourceSize.height: height sourceSize.height: height
mimeType: tdlibThumbnail.videoMimeType mimeType: tdlibThumbnail.videoMimeType
fillMode: thumbnailImage.fillMode == Image.PreserveAspectFit ? Thumbnail.PreserveAspectFit : Thumbnail.PreserveAspectCrop
visible: opacity > 0 visible: opacity > 0
opacity: status === Thumbnail.Ready ? 1.0 : 0.0 opacity: status === Thumbnail.Ready ? 1.0 : 0.0
Behavior on opacity { FadeAnimation {} } Behavior on opacity { FadeAnimation {} }

View file

@ -20,7 +20,6 @@ import QtQuick 2.6
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import QtMultimedia 5.6 import QtMultimedia 5.6
import "../" import "../"
import "../../js/functions.js" as Functions
import "../../js/debug.js" as Debug import "../../js/debug.js" as Debug
Item { Item {

View file

@ -22,28 +22,25 @@ import "../"
MessageContentBase { MessageContentBase {
function calculateBiggest() { height: Math.max(Theme.itemSizeExtraSmall, Math.min(Math.round(width * 0.66666666), width / getAspectRatio()))
var candidateBiggest = rawMessage.content.photo.sizes[rawMessage.content.photo.sizes.length - 1]; readonly property alias photoData: photo.photo;
if (candidateBiggest.width === 0 && rawMessage.content.photo.sizes.length > 1) {
for (var i = (rawMessage.content.photo.sizes.length - 2); i >= 0; i--) { onClicked: {
candidateBiggest = rawMessage.content.photo.sizes[i]; pageStack.push(Qt.resolvedUrl("../../pages/MediaAlbumPage.qml"), {
if (candidateBiggest.width > 0) { "messages" : [rawMessage],
})
}
function getAspectRatio() {
var candidate = photoData.sizes[photoData.sizes.length - 1];
if (candidate.width === 0 && photoData.sizes.length > 1) {
for (var i = (photoData.sizes.length - 2); i >= 0; i--) {
candidate = photoData.sizes[i];
if (candidate.width > 0) {
break; break;
} }
} }
} }
return candidateBiggest; return candidate.width / candidate.height;
}
height: Math.max(Theme.itemSizeExtraSmall, Math.min(defaultHeight, width / (biggest.width/biggest.height)))
readonly property int defaultHeight: Math.round(width * 0.66666666)
readonly property var biggest: calculateBiggest();
onClicked: {
pageStack.push(Qt.resolvedUrl("../../pages/ImagePage.qml"), {
"photoData" : photo.photo,
// "pictureFileInformation" : photo.fileInformation
})
} }
TDLibPhoto { TDLibPhoto {
id: photo id: photo
@ -51,7 +48,4 @@ MessageContentBase {
photo: rawMessage.content.photo photo: rawMessage.content.photo
highlighted: parent.highlighted highlighted: parent.highlighted
} }
BackgroundImage {
visible: !rawMessage.content.photo.minithumbnail && photo.image.status !== Image.Ready
}
} }

View file

@ -0,0 +1,207 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import "../"
MessageContentBase {
id: messageContent
property string chatId
readonly property int heightUnit: Math.round(width * 0.66666666)
readonly property var albumId: rawMessage.media_album_id
property var albumMessageIds: messageListItem ? messageListItem.messageAlbumMessageIds : []//overlayFlickable.messageAlbumMessageIds
onAlbumMessageIdsChanged: albumMessages = getMessages() //chatModel.getMessagesForAlbum(messageContent.albumId)
property var albumMessages: getMessages()//chatModel.getMessagesForAlbum(messageContent.albumId)
property bool firstLarge: albumMessages.length % 2 !== 0;
clip: true
height: defaultExtraContentHeight//(firstLarge ? heightUnit * 0.75 : 0 ) + heightUnit * 0.25 * albumMessageIds.length
onClicked: {
if(messageListItem.precalculatedValues.pageIsSelecting) {
page.toggleMessageSelection(rawMessage);
return;
}
openDetail(-1);
}
function getMessages() {
var msgs = [rawMessage];
if(messageContent.albumId === '0' || messageContent.albumMessageIds.length < 2) {
return msgs;
}
// var othermsgIds =
// getMessages from tdlib isn't faster
// if(rawMessage && rawMessage.chat_id) {
// var messages = [];
// return albumMessageIds.map(function(msgId){
// if(msgId === rawMessage.id) {
// return rawMessage;
// }
// return tdLibWrapper.getMessage(rawMessage.chat_id, msgId);
// })
// }
chatModel.getMessagesForAlbum(messageContent.albumId, 1).forEach(function(msg){
msgs.push(msg);
});
//
return msgs; //chatModel.getMessagesForAlbum(messageContent.albumId);
}
function openDetail(index) {
console.log('open detail', index || 0);
pageStack.push(Qt.resolvedUrl("../../pages/MediaAlbumPage.qml"), {
"messages" : albumMessages,
"index": index || 0
})
}
Connections { // TODO: needed?
target: tdLibWrapper
onReceivedMessage: {
if (albumMessageIds.indexOf(messageId)) {
// albumMessages = getMessages()
}
}
}
Component {
id: photoPreviewComponent
MessagePhoto {
// width: parent.width
// height: parent.height
messageListItem: messageContent.messageListItem
overlayFlickable: messageContent.overlayFlickable
rawMessage: albumMessages[modelIndex]
highlighted: mediaBackgroundItem.highlighted
}
}
Component {
id: videoPreviewComponent
Item {
property bool highlighted: mediaBackgroundItem.highlighted
anchors.fill: parent
clip: true
TDLibThumbnail {
id: tdLibImage
width: parent.width //don't use anchors here for easier custom scaling
height: parent.height
highlighted: parent.highlighted
thumbnail: albumMessages[modelIndex].content.video.thumbnail
minithumbnail: albumMessages[modelIndex].content.video.minithumbnail
}
Rectangle {
anchors {
fill: videoIcon
leftMargin: -Theme.paddingSmall
topMargin: -Theme.paddingSmall
bottomMargin: -Theme.paddingSmall
rightMargin: -Theme.paddingLarge
}
radius: Theme.paddingSmall
color: Theme.rgba(Theme.overlayBackgroundColor, 0.4)
}
Icon {
id: videoIcon
source: "image://theme/icon-m-video"
width: Theme.iconSizeSmall
height: Theme.iconSizeSmall
highlighted: parent.highlighted
anchors {
right: parent.right
rightMargin: Theme.paddingSmall
bottom: parent.bottom
}
}
}
}
Flow {
id: contentGrid
property int firstWidth: firstLarge ? contentGrid.width : normalWidth
property int firstHeight: firstLarge ? heightUnit - contentGrid.spacing : normalHeight
property int normalWidth: (contentGrid.width - contentGrid.spacing) / 2
property int normalHeight: (heightUnit / 2) - contentGrid.spacing
anchors.fill: parent
spacing: Theme.paddingMedium
Repeater {
model: albumMessages
delegate: BackgroundItem {
id: mediaBackgroundItem
property bool isLarge: firstLarge && model.index === 0
width: model.index === 0 ? contentGrid.firstWidth : contentGrid.normalWidth
height: model.index === 0 ? contentGrid.firstHeight : contentGrid.normalHeight
readonly property bool isSelected: messageListItem.precalculatedValues.pageIsSelecting && page.selectedMessages.some(function(existingMessage) {
return existingMessage.id === albumMessages[index].id
});
highlighted: isSelected || down || messageContent.highlighted
onClicked: {
if(messageListItem.precalculatedValues.pageIsSelecting) {
page.toggleMessageSelection(albumMessages[index]);
return;
}
openDetail(index);
}
onPressAndHold: {
page.toggleMessageSelection(albumMessages[index]);
}
Loader {
anchors.fill: parent
// asynchronous: true
readonly property int modelIndex: index
sourceComponent: albumMessages[index].content["@type"] === 'messageVideo' ? videoPreviewComponent : photoPreviewComponent
opacity: status === Loader.Ready
Behavior on opacity {FadeAnimator{}}
}
/*
TODO video:
rawMessage.content.video.thumbnail
TDLibPhoto {
id: photo
anchors.fill: parent
photo: rawMessage.content.photo
highlighted: parent.highlighted
}
*/
Rectangle {
visible: mediaBackgroundItem.isSelected
anchors {
fill: parent
}
color: 'transparent'
border.color: Theme.highlightColor
border.width: Theme.paddingSmall
}
}
}
}
}

View file

@ -26,7 +26,12 @@ import "../../js/debug.js" as Debug
MessageContentBase { MessageContentBase {
id: videoMessageComponent id: videoMessageComponent
property var videoData: ( rawMessage.content['@type'] === "messageVideo" ) ? rawMessage.content.video : ( ( rawMessage.content['@type'] === "messageAnimation" ) ? rawMessage.content.animation : rawMessage.content.video_note ) property var videoData: ( rawMessage.content['@type'] === "messageVideo" )
? rawMessage.content.video
: (
( rawMessage.content['@type'] === "messageAnimation" )
? rawMessage.content.animation
: rawMessage.content.video_note )
property string videoUrl; property string videoUrl;
property int previewFileId; property int previewFileId;
property int videoFileId; property int videoFileId;

View file

@ -0,0 +1,19 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
MessagePhotoAlbum {}

View file

@ -0,0 +1,279 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
import QtGraphicalEffects 1.0
import Sailfish.Silica 1.0
import "../../../js/functions.js" as Functions
Item {
// id
id: overlay
// property declarations
property int pageCount
property int currentIndex
property alias text: captionLabel.text
property bool active: true
property var message
readonly property color gradientColor: '#bb000000'
readonly property int gradientPadding: Theme.itemSizeMedium
// signal declarations
// JavaScript functions
// object properties
anchors.fill: parent
opacity: active ? 1 : 0
Behavior on opacity { FadeAnimator {} }
// large property bindings
// child objects
// states
// transitions
onActiveChanged: {
console.log('overlay active', active)
}
function forwardMessage() {
var neededPermissions = Functions.getMessagesNeededForwardPermissions([message]);
pageStack.push(Qt.resolvedUrl("../../../pages/ChatSelectionPage.qml"), {
myUserId: tdLibWrapper.getUserInformation().id,
headerDescription: qsTr("Forward %Ln messages", "dialog header", 1),
payload: {fromChatId: message.chat_id, messageIds:[message.id], neededPermissions: neededPermissions},
state: "forwardMessages"
});
}
// "header"
LinearGradient {
id: topGradient
property int startY: 0;
// Behavior on startY { NumberAnimation {duration: 2000} }
start: Qt.point(0, Math.min(height-gradientPadding*2, startY))
anchors {
left: parent.left
right: parent.right
top: parent.top
bottom: closeButton.bottom
bottomMargin: -gradientPadding
}
gradient: Gradient {
GradientStop { position: 0.0; color: gradientColor }
GradientStop { position: 1.0; color: 'transparent' }
}
}
IconButton {
id: closeButton
icon.source: "image://theme/icon-m-cancel?" + (pressed
? Theme.highlightColor
: Theme.lightPrimaryColor)
onClicked: pageStack.pop()
anchors {
right: parent.right
top: parent.top
margins: Theme.horizontalPageMargin
}
}
SilicaFlickable {
id: captionFlickable
anchors {
left: parent.left
// leftMargin: Theme.horizontalPageMargin
right: closeButton.left
top: parent.top
// topMargin: Theme.horizontalPageMargin
}
interactive: captionLabel.expanded && contentHeight > height
clip: true
height: Math.min(contentHeight, parent.height / 4)
contentHeight: captionLabel.height + Theme.horizontalPageMargin
flickableDirection: Flickable.VerticalFlick
VerticalScrollDecorator {
opacity: visible ? 1.0 : 0.0
flickable: captionFlickable
}
Label {
id: captionLabel
property bool expandable: expanded || height < contentHeight
property bool expanded
height: text ?
expanded
? contentHeight
: Theme.itemSizeMedium
: 0;
// maximumLineCount: expanded ? 0 : 3
color: Theme.primaryColor
// text: model.modelData.content.caption.text
text: Emoji.emojify(Functions.enhanceMessageText(message.content.caption, false), Theme.fontSizeExtraSmall)
onTextChanged: expanded = false
font.pixelSize: Theme.fontSizeExtraSmall
wrapMode: Text.WrapAnywhere
bottomPadding: expanded ? Theme.paddingLarge : 0
anchors {
left: parent.left
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.paddingLarge
right: parent.right
top: parent.top
topMargin: Theme.horizontalPageMargin
}
Behavior on height { NumberAnimation {duration: 300} }
Behavior on text {
SequentialAnimation {
FadeAnimation {
target: captionLabel
to: 0.0
duration: 300
}
PropertyAction {}
FadeAnimation {
target: captionLabel
to: 1.0
duration: 300
}
}
}
}
OpacityRampEffect {
sourceItem: captionLabel
enabled: !captionLabel.expanded
direction: OpacityRamp.TopToBottom
}
MouseArea {
anchors.fill: captionLabel
enabled: captionLabel.expandable
onClicked: {
captionLabel.expanded = !captionLabel.expanded
}
}
}
// "footer"
LinearGradient {
anchors {
left: parent.left
right: parent.right
top: buttons.top
bottom: parent.bottom
topMargin: -gradientPadding
}
gradient: Gradient {
GradientStop { position: 0.0; color: 'transparent' }
GradientStop { position: 1.0; color: gradientColor }
}
}
Loader {
asynchronous: true
active: overlay.pageCount > 1
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: buttons.bottom
}
sourceComponent: Component {
Row {
id: pageIndicatorRow
height: Theme.paddingSmall
spacing: height
Repeater {
id: pageIndicator
model: overlay.pageCount
Rectangle {
property bool active: model.index === overlay.currentIndex
width: pageIndicatorRow.height
height: pageIndicatorRow.height
color: active ? Theme.lightPrimaryColor : Theme.rgba(Theme.lightSecondaryColor, Theme.opacityLow)
Behavior on color { ColorAnimation {} }
radius: Theme.paddingSmall
}
}
}
}
}
Row {
id: buttons
height: Theme.itemSizeSmall
width: childrenRect.width
spacing: Theme.paddingLarge
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: Theme.paddingLarge
}
// IconButton {
// icon.source: "image://theme/icon-m-cancel?" + (pressed
// ? Theme.highlightColor
// : Theme.lightPrimaryColor)
// onClicked: pageStack.pop()
// }
IconButton {
icon.source: "image://theme/icon-m-downloads?" + (pressed
? Theme.highlightColor
: Theme.lightPrimaryColor)
onClicked: pageStack.pop()
}
Item {
width: Theme.itemSizeSmall
height: Theme.itemSizeSmall
}
IconButton {
enabled: message.can_be_forwarded
opacity: enabled ? 1.0 : 0.2
icon.source: "image://theme/icon-m-share?" + (pressed
? Theme.highlightColor
: Theme.lightPrimaryColor)
onClicked: forwardMessage()
}
}
states: [
State {
name: 'hasCaption'
when: captionLabel.height > 0
PropertyChanges { target: topGradient;
startY: captionFlickable.height
}
AnchorChanges {
target: topGradient
// anchors.top: captionLabel.verticalCenter
anchors.bottom: captionFlickable.bottom
}
}
]
transitions:
Transition {
AnchorAnimation { duration: 200 }
NumberAnimation { properties: "startY"; duration: 200 }
}
}

View file

@ -0,0 +1,16 @@
import QtQuick 2.6
ZoomImage {
photoData: model.modelData.content.photo
onClicked: {
console.log('clicked', zoomed)
if(zoomed) {
zoomOut(true)
page.overlayActive = true
} else {
page.overlayActive = !page.overlayActive
}
}
}

View file

@ -0,0 +1,181 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0
import QtMultimedia 5.6
import QtGraphicalEffects 1.0
import "../../"
Video {
id: video
property var videoData: model.modelData.content.video
readonly property bool isPlaying: playbackState === MediaPlayer.PlayingState
readonly property bool isCurrent: index === page.index
property bool shouldPlay
autoLoad: true
source: file.isDownloadingCompleted ? file.path : ''
onIsCurrentChanged: {
if(!isCurrent) {
pause()
}
}
onStatusChanged: {
if(status === MediaPlayer.EndOfMedia) {
page.overlayActive = true
}
}
TDLibThumbnail {
id: tdLibImage
property bool active: !file.isDownloadingCompleted || (!video.isPlaying && (video.position === 0 || video.status === MediaPlayer.EndOfMedia))
opacity: active ? 1 : 0
visible: active || opacity > 0
width: parent.width //don't use anchors here for easier custom scaling
height: parent.height
// highlighted: parent.highlighted
thumbnail: videoData.thumbnail
minithumbnail: videoData.minithumbnail
fillMode: Image.PreserveAspectFit
}
TDLibFile {
id: file
autoLoad: false
tdlib: tdLibWrapper
fileInformation: videoData.video
property real progress: isDownloadingCompleted ? 1.0 : (downloadedSize / size)
onDownloadingCompletedChanged: {
if(isDownloadingCompleted) {
video.source = file.path
if(video.shouldPlay) {
video.play()
delayedOverlayHide.start()
video.shouldPlay = false
}
}
}
}
Label {
anchors.centerIn: parent
text: 'dl: '+file.downloadedSize
+ ' \ns: '+file.size
+ ' \nes: '+file.expectedSize
+ ' \nd:'+file.isDownloadingActive
+ ' \nc:'+file.isDownloadingCompleted
}
MouseArea {
anchors.fill: parent
onClicked: page.overlayActive = !page.overlayActive
}
RadialGradient { // white videos = invisible button. I can't tell since which SFOS version the opaque button is available, so:
id: buttonBg
anchors.centerIn: parent
width: Theme.itemSizeLarge; height: Theme.itemSizeLarge
property color baseColor: Theme.rgba(palette.overlayBackgroundColor, 0.2)
enabled: videoUI.active || !file.isDownloadingCompleted
opacity: enabled ? 1 : 0
Behavior on opacity { FadeAnimator {} }
gradient: Gradient {
GradientStop { position: 0.0; color: buttonBg.baseColor }
GradientStop { position: 0.3; color: buttonBg.baseColor }
GradientStop { position: 0.5; color: 'transparent' }
}
IconButton {
anchors.fill: parent
icon.source: "image://theme/icon-l-"+(video.isPlaying || video.shouldPlay ? 'pause' : 'play')+"?" + (pressed
? Theme.highlightColor
: Theme.lightPrimaryColor)
onClicked: {
if (!file.isDownloadingCompleted) {
video.shouldPlay = !video.shouldPlay;
if(video.shouldPlay) {
file.load()
} else {
file.cancel()
}
return;
}
if (video.isPlaying) {
video.pause()
} else {
video.play()
delayedOverlayHide.start()
}
}
}
}
ProgressCircle {
property bool active: file.isDownloadingActive
opacity: active ? 1 : 0
Behavior on opacity { FadeAnimator {} }
anchors.centerIn: parent
value: file.progress
}
Item {
id: videoUI
property bool active: overlay.active// && file.isDownloadingCompleted
anchors.fill: parent
opacity: active ? 1 : 0
Behavior on opacity { FadeAnimator {} }
Slider {
id: slider
value: video.position
minimumValue: 0
maximumValue: video.duration || 0.1
enabled: parent.active && video.seekable
width: parent.width
handleVisible: false
animateValue: true
stepSize: 500
anchors {
bottom: parent.bottom
bottomMargin: Theme.itemSizeMedium
}
valueText: value > 0 || down ? Format.formatDuration((value)/1000, Formatter.Duration) : ''
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
onDownChanged: {
if(!down) {
video.seek(value)
value = Qt.binding(function() { return video.position })
}
}
Label {
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
bottom: parent.bottom
topMargin: Theme.paddingSmall
}
font.pixelSize: Theme.fontSizeExtraSmall
text: file.isDownloadingCompleted
? Format.formatDuration((parent.maximumValue - parent.value)/1000, Formatter.Duration)
: (video.videoData.duration
? Format.formatDuration(video.videoData.duration, Formatter.Duration) + ', '
: '') + Format.formatFileSize(file.size || file.expectedSize)
color: Theme.secondaryColor
}
}
Timer {
id: delayedOverlayHide
interval: 500
onTriggered: {
if(video.isPlaying) {
page.overlayActive = false
}
}
}
}
}

View file

@ -0,0 +1,148 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
SilicaFlickable {
// id
id: flickable
// property declarations
property real zoom
property bool zoomed
// override if needed
property bool zoomEnabled: true
property real minimumZoom: fitZoom
property real maximumZoom: 4 //Math.max(fitZoom, 1) * 3
default property alias zoomContentItem: zoomContentItem.data
property alias implicitContentWidth: zoomContentItem.implicitWidth
property alias implicitContentHeight: zoomContentItem.implicitHeight
// factor for "PreserveAspectFit"
readonly property real fitZoom: implicitContentWidth > 0 && implicitContentHeight > 0
? Math.min(maximumZoom, width / implicitContentWidth, height / implicitContentHeight)
: 1.0
readonly property int minimumBoundaryAxis: (implicitContentWidth / implicitContentHeight) > (width / height) ? Qt.Horizontal : Qt.Vertical
// JavaScript functions
function zoomOut(animated) {
if (zoomed) {
if(animated) { zoomOutAnimation.start() }
else {
zoom = fitZoom
zoomed = false
}
}
}
// object properties
contentWidth: Math.max(width, zoomContentItem.width)
contentHeight: Math.max(height, zoomContentItem.height)
enabled: !zoomOutAnimation.running && implicitContentWidth > 0 && implicitContentHeight > 0
flickableDirection: Flickable.HorizontalAndVerticalFlick
interactive: zoomed
// According to Jolla, otherwise pinching would sometimes not work:
pressDelay: 0
Binding { // Update zoom on orientation changes and set as default
target: flickable
when: !zoomed
property: "zoom"
value: minimumZoom
}
// child objects
PinchArea {
id: pinchArea
parent: flickable.contentItem
width: flickable.contentWidth
height: flickable.contentHeight
enabled: zoomEnabled && minimumZoom !== maximumZoom && flickable.enabled
onPinchUpdated: {
scrollDecoratorTimer.restart()
var f = flickable;
var requestedZoomFactor = 1.0 + pinch.scale - pinch.previousScale;
var previousWidth = f.contentWidth
var previousHeight = f.contentHeight
var targetWidth
var targetHeight
var targetZoom = requestedZoomFactor * f.zoom;
if (targetZoom < f.minimumZoom) {
f.zoom = f.minimumZoom;
f.zoomed = false;
f.contentX = 0;
f.contentY = 0;
return
} else if(targetZoom >= f.maximumZoom) {
f.zoom = f.maximumZoom;
targetHeight = f.implicitContentHeight * f.zoom
targetWidth = f.implicitContentWidth * f.zoom
}
else if(targetZoom < f.maximumZoom) {
if (f.minimumBoundaryAxis == Qt.Horizontal) {
targetWidth = f.contentWidth * requestedZoomFactor
f.zoom = targetWidth / f.implicitContentWidth
targetHeight = f.implicitContentHeight * f.zoom
} else {
targetHeight = f.contentHeight * requestedZoomFactor
f.zoom = targetHeight / f.implicitContentHeight
targetWidth = f.implicitContentWidth * f.zoom
}
}
// calculate center difference
f.contentX += pinch.previousCenter.x - pinch.center.x
f.contentY += pinch.previousCenter.y - pinch.center.y
// move to new (zoomed) center. this jumps a tiny bit, but is bearable:
if (targetWidth > f.width)
f.contentX -= (previousWidth - targetWidth)/(previousWidth/pinch.previousCenter.x)
if (targetHeight > f.height)
f.contentY -= (previousHeight - targetHeight)/(previousHeight/pinch.previousCenter.y)
f.zoomed = true
}
onPinchFinished: {
returnToBounds()
}
Item {
id: zoomContentItem
anchors.centerIn: parent
implicitWidth: flickable.width
implicitHeight: flickable.height
width: Math.ceil(implicitWidth * zoom)
height: Math.ceil(implicitHeight * zoom)
}
}
// enable zoom to minimumZoom on click
ParallelAnimation {
id: zoomOutAnimation
NumberAnimation {
target: flickable
properties: "contentX, contentY"
to: 0
}
NumberAnimation {
target: flickable
property: "zoom"
to: fitZoom
}
onRunningChanged: {
if(!running) {
zoomed = false
}
}
}
// show scroll decorators when scrolling OR zooming
Timer {
id: scrollDecoratorTimer
readonly property bool moving: flickable.moving
readonly property bool showing: moving || running
onMovingChanged: restart()
interval: 300
}
VerticalScrollDecorator {
flickable: flickable
opacity: scrollDecoratorTimer.showing ? 1.0 : 0.0
}
HorizontalScrollDecorator {
flickable: flickable
opacity: scrollDecoratorTimer.showing ? 1.0 : 0.0
}
}

View file

@ -0,0 +1,127 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0
import "../../"
ZoomArea {
// id
id: zoomArea
property var photoData //albumMessages[index].content.photo
property bool active: true
property alias image: image
signal clicked
maximumZoom: Math.max(Screen.width, Screen.height) / 200
// maximumZoom: Math.max(fitZoom, 1) * 3
implicitContentWidth: image.implicitWidth
implicitContentHeight: image.implicitHeight
zoomEnabled: image.status == Image.Ready
onActiveChanged: {
if (!active) {
zoomOut()
}
}
Component.onCompleted: {
// var photoData = albumMessages[index].content.photo;
if (photoData) {
var biggestIndex = -1
for (var i = 0; i < photoData.sizes.length; i++) {
if (biggestIndex === -1 || photoData.sizes[i].width > photoData.sizes[biggestIndex].width) {
biggestIndex = i;
}
}
if (biggestIndex > -1) {
// imageDelegate.imageWidth = photoData.sizes[biggestIndex].width;
// imageDelegate.imageHeight = photoData.sizes[biggestIndex].height;
image.sourceSize.width = photoData.sizes[biggestIndex].width
image.sourceSize.height = photoData.sizes[biggestIndex].height
image.fileInformation = photoData.sizes[biggestIndex].photo
console.log('loading photo', JSON.stringify(image.fileInformation))
}
}
}
TDLibImage {
id: image
width: parent.width
height: parent.height
source: file.isDownloadingCompleted ? file.path : ""
// enabled: true //!!file.fileId
// anchors.fill: parent
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
asynchronous: true
smooth: !(movingVertically || movingHorizontally)
// sourceSize.width: Screen.height
// visible: opacity > 0
// opacity: status === Image.Ready ? 1 : 0
Behavior on opacity { FadeAnimator{} }
}
// Label {
// anchors.fill: parent
// text: 'ok?' + image.enabled +' fileid:' +!!(image.file.fileId)
// + '\n - dl?' + image.file.isDownloadingActive
// + '\n completed?' + image.file.isDownloadingCompleted + ' path:'+ image.file.path
// + '\n ' + image.source
// wrapMode: Text.WrapAtWordBoundaryOrAnywhere
// }
// Rectangle {
// color: 'green'
// anchors.fill: image
// opacity: 0.3
// }
// Image {
// id: image
// anchors.fill: parent
// smooth: !(movingVertically || movingHorizontally)
// sourceSize.width: Screen.height
// fillMode: Image.PreserveAspectFit
// asynchronous: true
// cache: false
// onSourceChanged: {
// zoomOut()
// }
// opacity: status == Image.Ready ? 1 : 0
// Behavior on opacity { FadeAnimator{} }
// }
Item {
anchors.fill: parent
}
MouseArea {
anchors.centerIn: parent
width: zoomArea.contentWidth
height: zoomArea.contentHeight
onClicked: zoomArea.clicked()
}
BusyIndicator {
running: image.file.isDownloadingActive && !delayBusyIndicator.running
size: BusyIndicatorSize.Large
anchors.centerIn: parent
parent: zoomArea
Timer {
id: delayBusyIndicator
running: image.file.isDownloadingActive
interval: 1000
}
}
// Rectangle {
// color: 'green'
// anchors.fill: parent
// parent: zoomArea
// }
}

View file

@ -21,6 +21,8 @@ import QtQuick 2.6
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0 import WerkWolf.Fernschreiber 1.0
import ".."
AccordionItem { AccordionItem {
text: qsTr("Behavior") text: qsTr("Behavior")
Component { Component {
@ -114,6 +116,31 @@ AccordionItem {
} }
} }
TextSwitch {
width: parent.columnWidth
checked: appSettings.showReactionButton
text: qsTr("Show reaction button on tap")
description: qsTr("The reaction button may appear when you tap the message bubble, to make access to the reactions even easier.")
automaticCheck: false
onClicked: {
appSettings.showReactionButton = !checked
}
ReactionButton {
Behavior on opacity { FadeAnimation {} }
opacity: appSettings.showReactionButton ? 1 : 0
visible: opacity > 0
anchors {
right: parent.right
rightMargin: parent.rightMargin
verticalCenter: parent.verticalCenter
}
onClicked: {
appSettings.showReactionButton = !parent.checked
}
}
}
ComboBox { ComboBox {
id: feedbackComboBox id: feedbackComboBox
width: parent.columnWidth width: parent.columnWidth
@ -205,6 +232,38 @@ AccordionItem {
} }
} }
TextSwitch {
checked: appSettings.notificationSoundsEnabled && enabled
text: qsTr("Enable notification sounds")
description: qsTr("When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.")
enabled: parent.enabled
automaticCheck: false
onClicked: {
appSettings.notificationSoundsEnabled = !checked
}
}
}
TextSwitch {
checked: appSettings.notificationSuppressContent && enabled
text: qsTr("Hide content in notifications")
enabled: parent.enabled
automaticCheck: false
onClicked: {
appSettings.notificationSuppressContent = !checked
}
}
TextSwitch {
checked: appSettings.notificationTurnsDisplayOn && enabled
text: qsTr("Notification turns on the display")
enabled: parent.enabled
automaticCheck: false
onClicked: {
appSettings.notificationTurnsDisplayOn = !checked
}
}
TextSwitch { TextSwitch {
checked: appSettings.notificationSoundsEnabled && enabled checked: appSettings.notificationSoundsEnabled && enabled
text: qsTr("Enable notification sounds") text: qsTr("Enable notification sounds")
@ -218,4 +277,3 @@ AccordionItem {
} }
} }
} }
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFAC33" d="M8.916 12.88c-.111 1.652 1.768 3.126-.712 2.959-2.48-.167-7.836-2.533-7.768-3.53s3.708-2.757 6.188-2.59c2.48.166 2.404 1.508 2.292 3.161m20.122 16.049a.966.966 0 0 0-.564.095c-2.325.232-3.225-1.885-3.225-1.885-.439-.336-.981-2.009-1.589-1.215l.187 1.402c.187 1.402 2.57 3.224 2.57 3.224l-1.215 1.589a1 1 0 1 0 1.589 1.215l.673-.88-.039.249a1 1 0 1 0 1.976.314l.47-2.963a1.003 1.003 0 0 0-.833-1.145zm-6.278.623a.984.984 0 0 0-.572.018c-2.335-.082-2.944-2.3-2.944-2.3-.39-.392-.703-2.123-1.412-1.417l-.003 1.414c-.003 1.414 2.115 3.539 2.115 3.539l-1.417 1.412a.999.999 0 1 0 1.411 1.417l.785-.782-.073.242a1 1 0 0 0 1.916.576l.862-2.873a.996.996 0 0 0-.668-1.246z"/><path fill="#31373D" d="M35.009 6.729c-.383-.17-.758-.057-1.05.244-.054.056-4.225 6.306-14.532 4.944-.34-.045 3.139 11.968 3.199 11.962.124-.014 3.07-.368 6.14-2.553 2.818-2.005 6.284-5.991 6.797-13.598.028-.418-.171-.828-.554-.999z"/><path fill="#31373D" d="M34.477 21.108c-.204-.336-.59-.56-.979-.471-1.293.295-3.197.543-4.53.453-6.357-.428-9.361-4.129-9.392-4.16-.275-.282.466 11.552.816 11.576 9.194.62 13.862-6.027 14.057-6.31.222-.326.233-.751.028-1.088"/><path fill="#31373D" d="M24.586 19.016c-.371 5.51 1.316 9.861-4.194 9.489-5.51-.371-10.145-4.92-9.774-10.431s14.34-4.568 13.968.942"/><path fill="#31373D" d="M23.257 12.412c-.353 5.235-3.922 9.257-9.156 8.904-5.235-.353-9.193-4.882-8.84-10.117.353-5.235 4.832-8.444 10.067-8.091 4.001.269 8.24 4.683 7.929 9.304z"/><circle cx="10.67" cy="8.989" r="2"/><path d="M18.179 16.645s7.63 5.648 12.387-4.459c.396-.842 1.685.793.099 4.162s-8.175 6.44-12.04 1.536c-.815-1.035-.446-1.239-.446-1.239"/><path fill="#31373D" d="M15.327 3.107s6.246.254 7.798-.477.136 2.932-3.262 3.789-4.536-3.312-4.536-3.312z"/><path fill="#31373D" d="M17.428 5.788s4.501.136 6.054-.594.136 2.932-3.262 3.789c-3.399.857-2.792-3.195-2.792-3.195z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

1
qml/js/emoji/1f6dc.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><defs><clipPath id="a"><path fill="none" d="M0 0h36v36H0z"/></clipPath></defs><g clip-path="url(#a)"><path fill="#2b87c7" d="M36 32c0 4-4 4-4 4H4c-4 0-4-4-4-4V4c0-4 4-4 4-4h28s4 0 4 4z"/></g><path fill="#fff" d="m5.1 13.01 1.46 1.65c.15.16.39.19.57.07 3.01-1.98 6.77-3.17 10.83-3.17s7.88 1.2 10.9 3.21c.18.12.42.09.57-.07l1.47-1.64c.17-.2.14-.5-.07-.65-3.56-2.45-8.02-3.91-12.87-3.91S8.72 9.94 5.17 12.36c-.22.15-.25.45-.08.65Z"/><path fill="#fff" d="m9.43 17.9 1.45 1.64c.15.16.39.2.58.08 1.8-1.2 4.06-1.92 6.51-1.92s4.74.73 6.54 1.95c.18.12.42.1.57-.07l1.47-1.64c.18-.2.14-.5-.08-.65-2.34-1.66-5.29-2.65-8.5-2.65s-6.12.98-8.45 2.61a.43.43 0 0 0-.08.65Zm4.22 4.77 1.46 1.64c.14.16.39.19.57.07.63-.41 1.42-.65 2.28-.65s1.67.25 2.3.66c.18.12.42.09.57-.07l1.46-1.64a.44.44 0 0 0-.06-.64c-1.17-.86-2.65-1.37-4.27-1.37s-3.08.51-4.24 1.35c-.21.15-.24.45-.07.64Z"/><circle cx="17.91" cy="27.64" r="1.86" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 977 B

1
qml/js/emoji/1fa75.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#88C9F9" d="M35.885 11.833c0-5.45-4.418-9.868-9.867-9.868-3.308 0-6.227 1.633-8.018 4.129-1.791-2.496-4.71-4.129-8.017-4.129-5.45 0-9.868 4.417-9.868 9.868 0 .772.098 1.52.266 2.241C1.751 22.587 11.216 31.568 18 34.034c6.783-2.466 16.249-11.447 17.617-19.959.17-.721.268-1.469.268-2.242z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

1
qml/js/emoji/1fa76.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#99AAB5" d="M35.885 11.833c0-5.45-4.418-9.868-9.867-9.868-3.308 0-6.227 1.633-8.018 4.129-1.791-2.496-4.71-4.129-8.017-4.129-5.45 0-9.868 4.417-9.868 9.868 0 .772.098 1.52.266 2.241C1.751 22.587 11.216 31.568 18 34.034c6.783-2.466 16.249-11.447 17.617-19.959.17-.721.268-1.469.268-2.242z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

1
qml/js/emoji/1fa77.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#F4ABBA" d="M35.885 11.833c0-5.45-4.418-9.868-9.867-9.868-3.308 0-6.227 1.633-8.018 4.129-1.791-2.496-4.71-4.129-8.017-4.129-5.45 0-9.868 4.417-9.868 9.868 0 .772.098 1.52.266 2.241C1.751 22.587 11.216 31.568 18 34.034c6.783-2.466 16.249-11.447 17.617-19.959.17-.721.268-1.469.268-2.242z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

1
qml/js/emoji/1fa87.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><defs><clipPath id="a" clipPathUnits="userSpaceOnUse"><path d="M-35.367 13.848h36v-36h-36Z"/></clipPath><clipPath id="b" clipPathUnits="userSpaceOnUse"><path d="M-24.343 34.246h36v-36h-36Z"/></clipPath><clipPath id="c" clipPathUnits="userSpaceOnUse"><path d="M-36 10.269H0v-36h-36Z"/></clipPath><clipPath id="d" clipPathUnits="userSpaceOnUse"><path d="M-34.38 15.907h36v-36h-36Z"/></clipPath><clipPath id="e" clipPathUnits="userSpaceOnUse"><path d="M-31.406 19.303h36v-36h-36Z"/></clipPath><clipPath id="f" clipPathUnits="userSpaceOnUse"><path d="M-28.11 21.53h36v-36h-36Z"/></clipPath><clipPath id="g" clipPathUnits="userSpaceOnUse"><path d="M-12.71 6.294h36v-36h-36Z"/></clipPath><clipPath id="h" clipPathUnits="userSpaceOnUse"><path d="M-16.316 29.198h36v-36h-36Z"/></clipPath><clipPath id="i" clipPathUnits="userSpaceOnUse"><path d="M-11.042 3.065h36v-36h-36Z"/></clipPath><clipPath id="j" clipPathUnits="userSpaceOnUse"><path d="M-13.173 8.53h36v-36h-36Z"/></clipPath><clipPath id="k" clipPathUnits="userSpaceOnUse"><path d="M-12.869 13.034h36v-36h-36Z"/></clipPath><clipPath id="l" clipPathUnits="userSpaceOnUse"><path d="M-11.599 16.805h36v-36h-36Z"/></clipPath></defs><path fill="#fcd646" d="M0 0a10.926 10.926 0 0 0-.987-2.058l-.001-.001c-1.379-1.093-5.157-1.888-7.379-1.093-2.222.794-4 3-4.227 5.246.098.732.276 1.477.54 2.217a10.562 10.562 0 0 0 1.783 3.168c1.904-.631 3.847.105 5.904-.631 2.058-.736 3-2 5-3.269A10.622 10.622 0 0 0 0 0" clip-path="url(#a)" transform="matrix(1 0 0 -1 35.367 13.848)"/><path fill="#d4a086" d="M0 0c-.55-1.538-2.122-2.092-3.626-1.554-1.502.537-2.368 1.963-1.818 3.501.348.972.898 1.811 1.275 2.334 1.491 2.067 2.65 4.354 3.508 6.755l.036.101c.045.125.086.251.123.378 1.159.731 1.691.898 2.159.731.468-.168 0 0 .592-1.715a6.6 6.6 0 0 1-.145-.37l-.036-.101C1.209 7.659.654 5.156.496 2.612.456 1.969.394 1.102 0 0" clip-path="url(#b)" transform="matrix(1 0 0 -1 24.343 34.246)"/><path fill="#d24248" d="M0 0c-1.778-.237-3.836-.021-5.895.715-2.056.736-3.785 1.875-5.009 3.185 1.922 2.334 4.664 3.444 7.073 2.583C-1.422 5.621-.007 3.024 0 0" clip-path="url(#c)" transform="matrix(1 0 0 -1 36 10.269)"/><path fill="#82ae63" d="M0 0a10.36 10.36 0 0 0-.431-.64 12.303 12.303 0 0 0-2.543-2.757c-1.406-.696-3.867-.247-5.406.304-1.538.55-2 1-3.082 2.732a12.312 12.312 0 0 0-.217 3.745c.015.255.039.511.073.769C-10.373 2.709-8.535 1.445-6.314.65-4.091-.145-1.869-.333 0 0" clip-path="url(#d)" transform="matrix(1 0 0 -1 34.38 15.907)"/><path fill="#d24248" d="M0 0a10.735 10.735 0 0 0-1.724-1.11 6.727 6.727 0 0 1-1.573-1.117c-2.109 0-1.41-.72-2.109-.47-.697.25-1.013.987-1.946 1.92a6.698 6.698 0 0 1-.507 1.861 10.78 10.78 0 0 0-.629 1.952c1.096-.904 2.45-1.679 3.989-2.229C-2.961.256-1.422-.003 0 0" clip-path="url(#e)" transform="matrix(1 0 0 -1 31.406 19.303)"/><path fill="#ba6e54" d="M0 0a6.63 6.63 0 0 1-1.517-2.184 14.314 14.314 0 0 0-2.751.984c.252.865.32 1.766.213 2.651.614-.334 1.274-.63 1.971-.88C-1.385.321-.688.132 0 0" clip-path="url(#f)" transform="matrix(1 0 0 -1 28.11 21.53)"/><path fill="#fcd646" d="M0 0c.239-.75.391-1.499.463-2.235v-.001C.029-3.942-2.492-6.865-4.741-7.581c-2.248-.716-5-.041-6.542 1.608a10.935 10.935 0 0 0-.916 2.091 10.586 10.586 0 0 0-.504 3.599c1.897.653 2.996 2.416 5.077 3.079 2.083.663 3.599.229 5.958.433A10.592 10.592 0 0 0 0 0" clip-path="url(#g)" transform="matrix(1 0 0 -1 12.71 6.294)"/><path fill="#d4a086" d="M0 0c.496-1.557-.419-2.95-1.94-3.434s-3.074.124-3.569 1.681C-5.822-.77-5.893.23-5.911.875c-.068 2.548-.534 5.069-1.307 7.499l-.033.102c-.04.127-.084.252-.132.375.478 1.284.8 1.739 1.273 1.89.474.151 0 0 1.511-1.003.033-.129.069-.256.109-.383l.033-.103c.773-2.429 1.85-4.756 3.267-6.875C-.832 1.841-.355 1.115 0 0" clip-path="url(#h)" transform="matrix(1 0 0 -1 16.316 29.198)"/><path fill="#d24248" d="M0 0c-1.27-1.267-3.037-2.343-5.12-3.006-2.082-.663-4.147-.806-5.915-.506.113 3.022 1.62 5.567 4.057 6.343C-4.54 3.607-1.84 2.4 0 0" clip-path="url(#i)" transform="matrix(1 0 0 -1 11.042 3.065)"/><path fill="#ec9435" d="M0 0c.024-.258.04-.516.046-.77.059-1.085 0-2.378-.35-3.734C-1-5.911-3.229-7.046-4.786-7.542c-1.557-.495-2.197-.417-4.109.304a12.287 12.287 0 0 0-2.443 2.845c-.143.212-.279.43-.409.656 1.857-.4 4.084-.291 6.332.425C-3.165-2.596-1.284-1.398 0 0" clip-path="url(#j)" transform="matrix(1 0 0 -1 13.173 8.53)"/><path fill="#d24248" d="M0 0a10.72 10.72 0 0 0-.698-1.928A6.694 6.694 0 0 1-1.27-3.77c-1.677-1.279-.686-1.428-1.393-1.653-.705-.224-1.404.171-2.712.347a6.693 6.693 0 0 1-1.531 1.172c-.624.35-1.184.747-1.684 1.17 1.42-.054 2.966.151 4.523.647C-2.51-1.592-1.129-.865 0 0" clip-path="url(#k)" transform="matrix(1 0 0 -1 12.869 13.035)"/><path fill="#ba6e54" d="M0 0a6.594 6.594 0 0 1 .119-2.656 13.854 13.854 0 0 0-1.371-.514 14.2 14.2 0 0 0-1.414-.373 6.617 6.617 0 0 1-1.438 2.237c.691.107 1.395.272 2.101.497C-1.296-.584-.627-.312 0 0" clip-path="url(#l)" transform="matrix(1 0 0 -1 11.599 16.805)"/></svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

1
qml/js/emoji/1fa88.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="m29.346.937 4.95 4.95L6.012 34.17l-4.95-4.95z"/><circle cx="4.5" cy="30.75" r="3.5" fill="#C1694F"/><circle cx="3.5" cy="31.75" r="3.5" fill="#FFCC4D"/><circle cx="3.5" cy="31.75" r="2" fill="#292F33"/><circle cx="31.75" cy="3.5" r="3.5" fill="#FFCC4D"/><circle cx="25.75" cy="9.5" r="3.5" fill="#C1694F"/><circle cx="24.75" cy="10.5" r="3.5" fill="#FFCC4D"/><circle cx="7.95" cy="26.15" r="1.25" fill="#C1694F"/><circle cx="8.2" cy="26.4" r="1" fill="#292F33"/><circle cx="10.95" cy="23.15" r="1.25" fill="#C1694F"/><circle cx="11.2" cy="23.4" r="1" fill="#292F33"/><circle cx="13.95" cy="20.15" r="1.25" fill="#C1694F"/><circle cx="14.2" cy="20.4" r="1" fill="#292F33"/><circle cx="16.95" cy="17.15" r="1.25" fill="#C1694F"/><circle cx="17.2" cy="17.4" r="1" fill="#292F33"/><circle cx="19.95" cy="14.15" r="1.25" fill="#C1694F"/><circle cx="20.2" cy="14.4" r="1" fill="#292F33"/><circle cx="22.95" cy="11.15" r="1.25" fill="#C1694F"/><circle cx="23.2" cy="11.4" r="1" fill="#292F33"/><circle cx="29.95" cy="4.15" r="1.25" fill="#C1694F"/><circle cx="30.2" cy="4.4" r="1" fill="#292F33"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
qml/js/emoji/1faad.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#e80040" d="m18 28 17.08-10.2c.42-.25.52-.84.19-1.2C30.98 11.94 24.84 9.01 18 9.01S5.02 11.93.73 16.59c-.33.36-.24.95.19 1.2L18 27.99Z"/><path fill="#a80018" d="m18 25.79.03-.05.47-1.71V9.01c-.17 0-.33-.01-.5-.01s-.33 0-.5.01v15.02l.47 1.71zm-.81 1.26-.06.1.23.15-.03-.1zm2.99-2.79 8.25-12.81c-.3-.15-.6-.3-.91-.43l-7.68 11.93-.58 2.12.92-.81Z"/><path fill="#a80018" d="m18.01 25.81.02-.07-.03.05zm.76 1.01 14.15-12.45c-.26-.22-.53-.43-.8-.63L20.17 24.25l-.92.81-.48 1.75Zm-1.54.03-.12.11.08.09.14.15zm1.21.62.06.23.04-.02.1-.38-.13.08z"/><path fill="#a80018" d="m19.25 25.07.58-2.12 3.63-13.3c-.32-.08-.65-.15-.98-.21L18.5 24.03l-.47 1.71-.02.07.55.86.2.18v-.03zm-3.08-2.12L8.49 11.02c-.31.14-.61.28-.91.43l8.25 12.81.92.81-.58-2.12Z"/><path fill="#a80018" d="m17.97 25.74.14.53.46.4-.56-.86-.01-.02zm.54 1.64.13-.08.23-.15-.06-.1z"/><path fill="#a80018" d="m18.57 26.67-.46-.4.33 1.2.07-.09.3-.33.08-.09-.12-.11zm-1.82-1.6-.92-.81L3.87 13.75c-.27.2-.54.41-.8.63l14.15 12.45-.48-1.75Z"/><path fill="#a80018" d="m18.44 27.47-.33-1.19-.14-.53-.47-1.71-3.98-14.59c-.33.06-.66.13-.98.21l3.63 13.3.58 2.12.48 1.75v.03l.1.35.03.09.1.38.54.32.5-.3-.06-.23Z"/><path fill="#de9a7e" d="m18 28 6.59-3.94a8.628 8.628 0 0 0-13.18 0z"/><path fill="#fff" d="M12.19 23.95c1.53-1.56 3.62-2.45 5.81-2.45s4.28.88 5.81 2.45L18 27.42l-5.81-3.47Z"/><path fill="#de9a7e" d="m19.52 27.09 5.07-3.03a8.628 8.628 0 0 0-13.18 0l5.07 3.03-2.08 1.49a.38.38 0 0 0-.08.55c.93 1.15 2.24 1.87 3.69 1.87s2.75-.72 3.69-1.87c.14-.17.09-.42-.08-.55l-2.08-1.49Zm3.47-3.23-3.4 2.03c-.1-.15-.22-.28-.35-.4l1.83-2.84c.69.3 1.34.71 1.92 1.21Zm-4.61-1.84c.68.03 1.34.16 1.97.36l-1.76 2.73c-.07-.02-.14-.04-.22-.06v-3.03Zm-.75 0v3.03c-.07.02-.15.03-.22.06l-1.76-2.73c.63-.21 1.3-.33 1.97-.36Zm-4.61 1.84c.58-.5 1.23-.9 1.92-1.21l1.83 2.84c-.13.12-.25.25-.35.4l-3.4-2.03Z"/><circle cx="18" cy="27" r="1" fill="#a80018"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

1
qml/js/emoji/1faae.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#31373d" d="M35.9 12.22c-.13-.13-.37-.13-.51 0-.24.23-11.05 10.8-11.92 11.52-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 11.29-11.67 11.52-11.92.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.49 11.24-12.35 11.95-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 11.72-12.11 11.95-12.35.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.77 11.52-12.64 12.24-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 12.01-12.39 12.24-12.64.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.92 11.67-12.78 12.38-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 12.15-12.54 12.38-12.78.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.92 11.67-12.78 12.38-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 12.15-12.54 12.38-12.78.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.77 11.52-12.64 12.24-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 12.01-12.39 12.24-12.64.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.49 11.24-12.35 11.95-.19.16-.45.16-.61 0s-.16-.42 0-.61c.72-.86 11.72-12.11 11.95-12.35.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.05 10.8-11.92 11.52-.19.16-.45.16-.61 0s-.16-.42 0-.61C12.94 11.63 23.51.82 23.74.57c.13-.14.13-.38 0-.51s-.37-.13-.51 0c-.24.23-11.31 10.98-12.15 11.73-4.62 4.17-2.53 6.17-2.3 8.58.37 3.97-2.44 6.21-4.38 6.41-2.52.26-4.45 2.06-4.45 4.59 0 1.27.51 2.42 1.35 3.25.83.83 1.98 1.35 3.25 1.35 2.54 0 4.33-1.93 4.59-4.45.2-1.94 2.44-4.75 6.41-4.38 2.41.22 4.41 2.32 8.58-2.3.75-.83 11.5-11.9 11.73-12.15.13-.14.13-.38 0-.51zM4.56 32.69c-.35 0-.66-.14-.88-.37a1.24 1.24 0 0 1-.37-.88c0-.69.56-1.25 1.25-1.25.35 0 .66.14.88.37s.37.54.37.88c0 .69-.56 1.25-1.25 1.25"/><path fill="none" stroke="#7a8891" stroke-linecap="round" stroke-miterlimit="10" d="M3.88 28.12c-1.46.3-2.58 1.55-2.67 3.08m9.28-17.1c-1.61 2.13-1.3 2.91-.65 5.23.55 1.97.06 3.81-.65 5.23"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

1
qml/js/emoji/1faaf.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 36 36"><path fill="#9266cc" d="M36 32c0 2.2-1.8 4-4 4H4c-2.2 0-4-1.8-4-4V4c0-2.2 1.8-4 4-4h28c2.2 0 4 1.8 4 4z"/><path fill="#fff" d="M23.5 6.9s4 2.3 3.6 7.5c-.4 5.1-8.3 10.1-8.3 10.1v-1.6l2.1-1.5c-.1-.5-.2-.9-.3-1.4 2.8-1.1 4.9-3.8 4.9-7s-2-5.9-4.8-7c.1-.7.2-1 .2-1L18 2l-2.9 3s.1.3.2.9c-2.8 1.1-4.8 3.8-4.8 7s2 5.9 4.8 7c-.1.5-.2.9-.3 1.4l2.1 1.5v1.6s-7.9-4.9-8.3-10.1c-.4-5.1 3.6-7.5 3.6-7.5s-6.1 1-6.5 7.5c-.4 6.3 5.8 11 5.8 11s1.1.9 1.2 1.9l2.7-2 1 .7-3.4 2.6c-.3.2-.5.6-.5 1 0 .8.6 1.4 1.4 1.4s1.4-.6 1.4-1.4c0-.3-.1-.6-.3-.8l1.9-1.3v3c-.6.3-1 .9-1 1.6 0 1 .8 1.9 1.9 1.9s1.9-.8 1.9-1.9c0-.7-.4-1.3-1-1.6v-3l1.9 1.3c-.2.2-.3.5-.3.8 0 .8.6 1.4 1.4 1.4s1.4-.6 1.4-1.4c0-.4-.2-.8-.5-1l-3.4-2.6 1-.7 2.7 2c.1-1 1.2-1.9 1.2-1.9s6.2-4.7 5.8-11c-.5-6.4-6.6-7.4-6.6-7.4m.5 6c0 2.5-1.5 4.6-3.6 5.5-.7-4.6-.3-8.6.1-11 2 1 3.5 3.1 3.5 5.5m-12 0c0-2.4 1.5-4.5 3.6-5.5.4 2.4.7 6.4.1 11-2.2-.9-3.7-3-3.7-5.5"/></svg>

After

Width:  |  Height:  |  Size: 982 B

1
qml/js/emoji/1fabb.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#69b546" d="M31.71 28.26c-1.78-.49-4.33.36-6.77 1.57 1.9-2.33 3.91-4.76 6.1-6.08 1.82-1.09 2.07-2.09 1.86-2.82-.24-.86-1.17-1.32-2.05-1.18-4.49.7-9.71 8.85-11.86 12.52v-5.53h-2v5.53c-2.14-3.68-7.36-11.82-11.86-12.52-.88-.14-1.81.32-2.05 1.18-.2.73.05 1.73 1.86 2.82 2.19 1.32 4.2 3.75 6.1 6.08-2.44-1.21-5-2.07-6.77-1.57-.68.19-1.2.83-1.09 1.53.09.59.6 1.24 2.28 1.43 4.21.49 9.43 4.74 12.53 4.52 3.1.23 8.32-4.03 12.53-4.52 1.68-.19 2.18-.84 2.28-1.43.11-.7-.41-1.34-1.09-1.53Z"/><path fill="#894fc4" d="M23.68 6.26c1.93-4-1.93-3-1.93-3C21-.24 18 2.32 18 2.32s-3-2.56-3.75.94c0 0-3.86-1-1.93 3 0 0-3.07 1-2.07 4l.82 3.68S8.75 21.85 13 19.56c0 0-1.75 3.7 2.25 2.7 0 0-.46 3.38 1.02 5.32a2.145 2.145 0 0 0 3.45 0c1.48-1.94 1.02-5.32 1.02-5.32 4 1 2.25-2.7 2.25-2.7 4.25 2.3 1.93-5.62 1.93-5.62l.82-3.68c1-3-2.07-4-2.07-4Z"/><path fill="#ae6dee" d="M19.17 2.98c0 1.93-.52 4.28-1.17 4.28s-1.17-2.34-1.17-4.28S17.35.26 18 .26s1.17.79 1.17 2.72"/><path fill="#ae6dee" d="M19.17 10.54c0-1.93-.52-4.28-1.17-4.28s-1.17 2.34-1.17 4.28.52 2.72 1.17 2.72 1.17-.79 1.17-2.72"/><path fill="#ae6dee" d="M21.62 5.05c-1.17 1.17-2.9 2.27-3.29 1.88s.71-2.12 1.88-3.29 1.96-1.33 2.35-.94.23 1.18-.94 2.35"/><path fill="#ae6dee" d="M21.62 8.47c-1.17-1.17-2.9-2.27-3.29-1.88s.71 2.12 1.88 3.29 1.96 1.33 2.35.94.23-1.18-.94-2.35m-7.24-3.42c1.17 1.17 2.9 2.27 3.29 1.88s-.71-2.12-1.88-3.29-1.96-1.33-2.35-.94-.23 1.18.94 2.35Z"/><path fill="#ae6dee" d="M14.38 8.47c1.17-1.17 2.9-2.27 3.29-1.88s-.71 2.12-1.88 3.29-1.96 1.33-2.35.94-.23-1.18.94-2.35Z"/><circle cx="18" cy="6.76" r=".8" fill="#cd95ff"/><path fill="#ae6dee" d="M19.17 16.98c0 1.93-.52 4.28-1.17 4.28s-1.17-2.34-1.17-4.28.52-2.72 1.17-2.72 1.17.79 1.17 2.72"/><path fill="#ae6dee" d="M19.17 24.54c0-1.93-.52-4.28-1.17-4.28s-1.17 2.34-1.17 4.28.52 2.72 1.17 2.72 1.17-.79 1.17-2.72"/><path fill="#ae6dee" d="M21.62 19.05c-1.17 1.17-2.9 2.27-3.29 1.88s.71-2.12 1.88-3.29 1.96-1.33 2.35-.94.23 1.18-.94 2.35"/><path fill="#ae6dee" d="M21.62 22.47c-1.17-1.17-2.9-2.27-3.29-1.88s.71 2.12 1.88 3.29 1.96 1.33 2.35.94.23-1.18-.94-2.35m-7.24-3.42c1.17 1.17 2.9 2.27 3.29 1.88s-.71-2.12-1.88-3.29-1.96-1.33-2.35-.94-.23 1.18.94 2.35Z"/><path fill="#ae6dee" d="M14.38 22.47c1.17-1.17 2.9-2.27 3.29-1.88s-.71 2.12-1.88 3.29-1.96 1.33-2.35.94-.23-1.18.94-2.35Z"/><circle cx="18" cy="20.76" r=".8" fill="#cd95ff"/><path fill="#ae6dee" d="M22.17 13.3c1.19.62 2.8 1.04 3.01.65s-1.07-1.47-2.26-2.08-1.84-.55-2.05-.15.11.97 1.31 1.59Z"/><path fill="#ae6dee" d="M22.17 14.21c1.19-.62 2.8-1.04 3.01-.65s-1.07 1.47-2.26 2.08-1.84.55-2.05.15.11-.97 1.31-1.59Z"/><path fill="#ae6dee" d="M27.83 13.3c-1.19.62-2.8 1.04-3.01.65s1.07-1.47 2.26-2.08 1.84-.55 2.05-.15-.11.97-1.31 1.59Z"/><path fill="#ae6dee" d="M27.83 14.21c-1.19-.62-2.8-1.04-3.01-.65s1.07 1.47 2.26 2.08 1.84.55 2.05.15-.11-.97-1.31-1.59Z"/><path fill="#ae6dee" d="M26.84 11.03c-.58 1.47-1.67 3.1-2.16 2.91s-.19-2.13.39-3.61 1.21-1.92 1.7-1.73.65.95.08 2.42Z"/><path fill="#ae6dee" d="M26.84 16.49c-.58-1.47-1.67-3.1-2.16-2.91s-.19 2.13.39 3.61 1.21 1.92 1.7 1.73.65-.95.08-2.42Z"/><ellipse cx="25.35" cy="13.76" fill="#cd95ff" rx=".4" ry=".8"/><path fill="#ae6dee" d="M13.83 13.3c-1.19.62-2.8 1.04-3.01.65s1.07-1.47 2.26-2.08 1.84-.55 2.05-.15-.11.97-1.31 1.59Z"/><path fill="#ae6dee" d="M13.83 14.21c-1.19-.62-2.8-1.04-3.01-.65s1.07 1.47 2.26 2.08 1.84.55 2.05.15-.11-.97-1.31-1.59Z"/><path fill="#ae6dee" d="M8.17 13.3c1.19.62 2.8 1.04 3.01.65s-1.07-1.47-2.26-2.08-1.84-.55-2.05-.15.11.97 1.31 1.59Z"/><path fill="#ae6dee" d="M8.17 14.21c1.19-.62 2.8-1.04 3.01-.65s-1.07 1.47-2.26 2.08-1.84.55-2.05.15.11-.97 1.31-1.59Z"/><path fill="#ae6dee" d="M9.16 11.03c.58 1.47 1.67 3.1 2.16 2.91s.19-2.13-.39-3.61-1.21-1.92-1.7-1.73-.65.95-.08 2.42Z"/><path fill="#ae6dee" d="M9.16 16.49c.58-1.47 1.67-3.1 2.16-2.91s.19 2.13-.39 3.61-1.21 1.92-1.7 1.73-.65-.95-.08-2.42Z"/><ellipse cx="10.65" cy="13.76" fill="#cd95ff" rx=".4" ry=".8"/></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

1
qml/js/emoji/1fabc.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#44abf3" d="M13.5 35c-.26 0-.53-.07-.77-.21-.71-.43-.94-1.35-.51-2.06l1.59-2.65c.45-.75.3-1.71-.34-2.29a4.804 4.804 0 0 1-.84-6.19l2.2-3.44c.45-.7 1.37-.9 2.07-.45.7.45.9 1.37.45 2.07l-2.2 3.44c-.48.75-.35 1.73.32 2.33 1.72 1.54 2.1 4.09.92 6.06l-1.59 2.65a1.5 1.5 0 0 1-1.29.73Zm-6-2c-.26 0-.53-.07-.77-.21-.71-.43-.94-1.35-.51-2.06l1.59-2.65c.45-.75.3-1.71-.34-2.29a4.804 4.804 0 0 1-.84-6.19l2.2-3.44c.45-.7 1.37-.9 2.07-.45.7.45.9 1.38.45 2.07l-2.2 3.44c-.48.75-.35 1.73.32 2.33 1.72 1.54 2.1 4.09.92 6.06L8.8 32.26a1.5 1.5 0 0 1-1.29.73Zm14.44 2a1.5 1.5 0 0 1-1.29-.73l-1.59-2.65c-1.19-1.98-.8-4.53.92-6.06.67-.6.8-1.58.32-2.33l-2.2-3.44a1.499 1.499 0 0 1 2.52-1.62l2.2 3.44c1.28 2 .93 4.6-.84 6.19-.65.58-.79 1.54-.34 2.29l1.59 2.65c.43.71.2 1.63-.51 2.06-.24.15-.51.21-.77.21Zm6-2a1.5 1.5 0 0 1-1.29-.73l-1.59-2.65c-1.19-1.98-.8-4.53.92-6.06.67-.6.8-1.58.32-2.33l-2.2-3.44a1.499 1.499 0 0 1 2.52-1.62l2.2 3.44c1.28 2 .93 4.6-.84 6.19-.65.58-.79 1.54-.34 2.29l1.59 2.65c.43.71.2 1.63-.51 2.06-.24.15-.51.21-.77.21Z"/><path fill="#44abf3" d="M18 22.27c-1.17 0-2.3-.29-3.36-.85-.71-.38-1.28-.82-1.68-1.31-.61-.75-1.5-1.17-2.42-1.17-.11 0-.21 0-.32.02-.33.04-.72.06-1.15.06-3.21 0-5.26-1.36-6.1-4.04C1.6 10.6 4.73 7.51 7.59 5.7c1.13-.72 5.7-2.68 10.42-2.68S27.3 4.99 28.43 5.7c2.87 1.82 6 4.9 4.62 9.28-.84 2.68-2.89 4.04-6.1 4.04-.43 0-.82-.03-1.15-.06-.11-.01-.21-.02-.32-.02-.92 0-1.81.43-2.42 1.17-.41.5-.97.94-1.68 1.32-1.06.57-2.2.85-3.36.85Z"/><path fill="#2b87c7" d="M18 4c4.44 0 8.86 1.88 9.89 2.53 2.57 1.63 5.39 4.36 4.21 8.14-.26.83-1.05 3.34-5.15 3.34-.34 0-.7-.02-1.05-.06-.14-.02-.28-.02-.43-.02-1.23 0-2.39.56-3.19 1.54-.32.39-.79.75-1.38 1.07-.92.49-1.89.74-2.9.74s-1.98-.25-2.9-.74c-.59-.32-1.06-.67-1.38-1.07-.8-.98-1.97-1.54-3.19-1.54-.14 0-.28 0-.43.02-.35.04-.7.06-1.05.06-4.1 0-4.89-2.51-5.15-3.34-1.18-3.78 1.63-6.51 4.21-8.14C9.13 5.88 13.55 4 18 4m0-2C13 2 8.24 4.08 7.04 4.84 4.44 6.48.36 10.02 2 15.26 3.25 19.25 6.67 20 9.06 20c.47 0 .89-.03 1.26-.07.07 0 .14-.01.22-.01.61 0 1.21.27 1.65.81.54.66 1.25 1.17 1.99 1.56 1.22.65 2.53.97 3.83.97s2.62-.32 3.83-.97c.73-.39 1.44-.9 1.99-1.56.44-.54 1.04-.81 1.65-.81.07 0 .14 0 .22.01.36.04.79.07 1.26.07 2.39 0 5.81-.75 7.06-4.74 1.64-5.25-2.44-8.78-5.04-10.43-1.19-.76-5.96-2.84-10.96-2.84Z"/><path fill="#fff" d="M8.12 12.67c-.57 1.67-.98 2.83-1.99 2.49s-1.36-1.97-.79-3.64 1.84-2.75 2.84-2.41.5 1.89-.06 3.57Z"/><path fill="#2b87c7" d="M23.12 6.98c-.61-.48-1.76-1.72-5.12-1.72s-4.51 1.25-5.12 1.72c-.78.61-.86 1.53-.19 2.06.67.53 1.85.46 2.62-.15.11-.08.24-.21.39-.35-.11.37-.18.7-.18.9 0 .86 1.11 1.56 2.48 1.56s2.48-.7 2.48-1.56c0-.21-.06-.53-.18-.9.15.15.28.27.39.35.78.61 1.95.68 2.62.15.67-.53.59-1.45-.19-2.06"/><ellipse cx="10" cy="7.5" fill="#fff" rx="1.5" ry="1" transform="rotate(-42.63 9.993 7.502)"/><path fill="#fff" d="M30.24 15.68c-.77 1.03-1.9 1.49-2.52 1.03s-.5-1.68.28-2.71 1.9-1.49 2.52-1.03.5 1.67-.27 2.71Z"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

1
qml/js/emoji/1fabd.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#CAD4DB" d="M33.86 3.06c.03-.8-.92-1.26-1.53-.75-6.2 5.13-13.64 8.46-18.68 9.1-4.06.52-5.34 2.11-6.26 5.18-.29.97-.69 1.9-1.22 2.75l-3.19 5.12-.117.252c-.428.923-.873 1.881-.873 2.898 0 2.47 3.16 3.67 4.89 4.14.39.11.74-.15.82-.51l-.044-.063.078.022-1.515-4.012.563.241.026.072c.038.107.075.213.122.32v.01c.2.48.44.96.71 1.44 2.03 3.27 5.56 3.85 7.66 3.88.73.01 1.14-.83.69-1.41l-.68-.88-.036.073-2.524-2.495.763.108.067.054-.04-.05.052.007c4.516 2.8 8.073 3.373 10.298 3.333.97-.01 1.34-1.27.54-1.82l-.17-.12a23.27 23.27 0 0 1-.243-.065l-3.11-1.666.62-.19c6.053 1.66 9.432-.137 10.953-1.439.49-.42.27-1.21-.37-1.3l-1.17-.18-.137.024-5.49-.743.189-.099.208.018-.19-.027.007-.004c3.684-.136 5.926-1.552 7.103-2.609.35-.31.26-.82-.06-1.06l-.18.045-6.015-.093.102-.106.113-.006c3.99-1.03 5.97-2.54 6.95-3.79.65-.82-.07-2.01-1.11-1.87l-.73.1.008.02-4.599 1.012.32-.353c.117-.035.234-.072.351-.109l-.28.04c4.44-1.27 6.06-3.74 6.66-5.38a.795.795 0 0 0-.81-1.07l-.029.021.023-.024-4.512 2.012c-.562.18-.857.3-1.16.392a4.781 4.781 0 0 1-.234.066l.015-.025c5.22-2.61 6.277-6.232 6.377-8.442z"/><path fill="#97A8B3" d="M24.8 21.53c3.37 0 6.05-.49 7.77-.92a.69.69 0 0 0-.41-.13h-5.68l.11-.03c-3.13.17-7.02-.05-11.37-1.25.31-.41.61-.86.9-1.33.58.03 1.19.04 1.82.04 4.11 0 9.13-.64 13.78-2.96l-.02-.05-3.92.56c-3.9 1.24-7.86 1.5-11.09 1.4.27-.52.52-1.08.75-1.66h.24c2.98 0 8.64-.88 15.67-6.16a.76.76 0 0 0-.25.06l-5.29 2.27c-4.39 2.28-7.9 2.77-10 2.8.06-.18.13-.35.19-.53a.5.5 0 0 0-.33-.62c-.04-.01-.08 0-.12-.01-.22 0-.43.12-.5.35-2.41 8.04-8.32 10.1-12 10.58-.19.03-.39.05-.57.07-.28.03-.48.27-.45.54.02.26.24.45.5.45h.05c.2-.02.41-.04.64-.07.19 2.07 1.04 4.19 2.52 6.32.02-.1.03-.21 0-.32L6.69 27.1c-.27-.79-.43-1.57-.49-2.34.78-.16 1.66-.39 2.59-.75 1.92 4.58 6.46 6.91 6.52 6.94l.04-.08-1.74-2.26c-1.33-1.07-2.96-2.73-3.9-4.98.36-.16.72-.35 1.08-.55 4.17 4.34 12.35 6.58 13.49 6.88l-2.8-1.93c-2.93-1.03-7.2-2.87-9.82-5.49.4-.26.79-.55 1.19-.87 4.01 1.96 9.68 3.72 14.83 3.72 1.13 0 2.24-.09 3.3-.28l-5.25-.8c-4.31-.35-8.75-1.81-12.05-3.37.3-.28.59-.58.87-.91 3.83 1.11 7.32 1.5 10.27 1.5z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

1
qml/js/emoji/1fabf.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#ffa800" d="M20.43 33.26a.736.736 0 0 0-.32-.31s-.36-.08-.42-.17c-.67-1.12 1.29-5.33 1.29-5.33h-2l-1.21 4.9-1.11-.03a.773.773 0 0 0-.79.76c-.01.43.33.78.76.79l.86.02-.17.09c-.38.19-.53.66-.34 1.04s.66.53 1.04.34l2.07-1.06c.38-.19.53-.66.34-1.04Z"/><path fill="#ffa800" d="M25.12 31.7a.743.743 0 0 0-.44-.06s-.33.15-.44.11c-1.21-.49-2.15-5.05-2.15-5.05l-1.6 1.19 1.96 4.65-.91.64c-.35.25-.43.73-.19 1.08.25.35.73.43 1.08.19l.7-.5-.09.18c-.19.38-.03.85.35 1.04s.85.03 1.04-.35l1.03-2.08a.78.78 0 0 0-.35-1.04Z"/><path fill="#dfe7ed" d="M9.23 11.39c-1.03 1.43-1.74 3.08-1.77 4.84-.08 5.16 4.11 7.45 6.01 8.71 1.33.88 2.85 2.35 4.13 3.72 1.47 1.56 3.98 1.41 5.24-.32 1.5-2.07 3.54-4.48 5.57-5.62 2.41-1.37 3.27-3.3 3.57-4.59.16-.66-.4-1.26-1.08-1.23-3.54.17-5.43-1.96-5.43-1.96-5.02-5.02-10.04-2.38-11.55-1.38-.25.17-.6.04-.68-.25-.38-1.35-.12-3.35.77-5.42.57-1.32.76-2.79.47-4.19-.12-.61-.34-1.22-.69-1.76-1.31-2-5.31-2-6.32 1.17-1.21 3.77 1.02 2.83 1.02 2.83s3.12 2.16.75 5.45Z"/><path fill="#fc8a00" d="M4.47 4.8c.91-.22 2.04-.55 2.94-.97.31-.14.66-.02.85.27.23.35.63.73 1.34.81.42.04.52.63.17.86-1.16.74-3.06 1.42-5.44.24-.53-.26-.43-1.07.14-1.21Z"/><circle cx="10.98" cy="3.44" r="1" fill="#282f33"/><path fill="#cad4db" d="M21.43 23.72c-1.02 0-1.99-.18-2.91-.55-4.06-1.62-5.47-6.22-5.52-6.41-.08-.26.07-.54.34-.62.27-.08.54.07.62.34.01.04 1.33 4.34 4.94 5.77 2.38.95 5.25.46 8.54-1.43a.35.35 0 0 0 .17-.3c0-.06-.02-.22-.19-.3l-2.65-1.33a.488.488 0 0 1-.22-.67c.12-.25.42-.34.67-.22l2.65 1.33c.44.22.73.67.74 1.16.01.49-.24.95-.67 1.2-2.35 1.36-4.53 2.04-6.51 2.04Z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

1
qml/js/emoji/1face.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#6c1a0d" d="M.75 8.93s.24-3.25 3.01-5.01c.67-.43 1.46.33 1.08 1.03-.82 1.47-1.64 3.51-1.12 5.2.1.32.54.34.65.03.25-.73.67-1.83 1.28-2.75.37-.56 1.26-.23 1.18.44-.12.96-.21 2.12-.11 2.99.07.64.84.93 1.31.49.3-.28.62-.66.83-1.13.23-.5.96-.44 1.13.08.12.37.19.84.15 1.41-.05.85.36 1.66 1.08 2.1l2.05 1.26-1.15.94-1.92-1.28c-.63-.42-1.42-.52-2.12-.23-2.31.93-7.59 2.22-7.31-5.56Zm34.5 0s-.24-3.25-3.01-5.01c-.67-.43-1.46.33-1.08 1.03.82 1.47 1.64 3.51 1.12 5.2-.1.32-.54.34-.65.03-.25-.73-.67-1.83-1.28-2.75-.37-.56-1.26-.23-1.18.44.12.96.21 2.12.11 2.99-.07.64-.84.93-1.31.49-.3-.28-.62-.66-.83-1.13-.23-.5-.96-.44-1.13.08-.12.37-.19.84-.15 1.41.05.85-.36 1.66-1.08 2.1l-2.05 1.26 1.15.94 1.92-1.28c.63-.42 1.42-.52 2.12-.23 2.31.93 7.59 2.22 7.31-5.56Zm-15.73.53-.7.63-.49-.87c-.16-.29-.49-.29-.65 0l-.49.87-.7-.63c-.22-.2-.52-.07-.61.27L15.01 13h6l-.87-3.27c-.09-.34-.39-.47-.61-.27Z"/><path fill="#282f33" d="M13.15 15.06s-2.37-3.81-.72-4.76 3.73 3.06 3.73 3.06z"/><path fill="#c86349" d="m12.97 15.73-.25-.41c-.18-.28-1.71-2.82-1.31-4.42.11-.46.38-.81.76-1.04.39-.22.83-.27 1.27-.14 1.58.46 3 3.1 3.15 3.4l.22.43-3.85 2.18Zm-.01-5.08c-.1 0-.2.03-.29.08-.15.09-.25.22-.3.42-.21.84.49 2.38.95 3.23l2.15-1.21c-.58-.98-1.54-2.26-2.31-2.48a.761.761 0 0 0-.21-.03Z"/><path fill="#282f33" d="M22.85 15.06s2.37-3.81.72-4.76-3.73 3.06-3.73 3.06z"/><path fill="#c86349" d="m23.03 15.73-3.85-2.18.22-.43c.16-.3 1.57-2.94 3.15-3.4.45-.13.89-.08 1.28.14s.65.58.76 1.04c.4 1.6-1.14 4.14-1.31 4.42l-.25.41Zm-2.5-2.56 2.15 1.21c.47-.86 1.16-2.39.95-3.23-.05-.19-.14-.33-.29-.41-.15-.09-.31-.1-.5-.05-.83.24-1.81 1.64-2.31 2.48Z"/><circle cx="18" cy="30" r="2.5" fill="#282f33"/><path fill="#c86349" d="M18 33c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3m0-5c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2"/><path fill="#c86349" d="M25 17.5c0-3.31-3.13-6-7-6s-7 2.69-7 6c0 1.2.41 2.32 1.13 3.26.78 1.02 1 2.37.76 3.64-.25 1.33-.39 2.46-.39 3.11 0 2.76 2.46 3 5.5 3s5.5-.24 5.5-3c0-.65-.14-1.77-.39-3.11-.24-1.26-.02-2.61.76-3.64.71-.94 1.13-2.05 1.13-3.26"/><path fill="#262b2b" d="M14.4 20.01s0-1.03 1.03-1.03 1.03 1.03 1.03 1.03v1.03s0 1.03-1.03 1.03-1.03-1.03-1.03-1.03v-1.03Zm5.15 0s0-1.03 1.03-1.03 1.03 1.03 1.03 1.03v1.03s0 1.03-1.03 1.03-1.03-1.03-1.03-1.03z"/><path fill="#6c1a0d" d="M16.06 28.03c.36-.09.51-.76.33-1.49s-.63-1.25-1-1.15-.51.76-.33 1.49.63 1.25 1 1.15Zm4.86-1.16c.19-.73.04-1.4-.33-1.49s-.81.42-1 1.15-.04 1.4.33 1.49.81-.42 1-1.15"/></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
qml/js/emoji/1facf.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#738695" d="m16.07 26.09 3.23 6.04c.1.18.35.22.49.07l1.49-1.49c.12-.12.12-.31 0-.43l-2.24-2.43a1.03 1.03 0 0 1-.27-.69v-3.72l-3.28-1.94-1 3 1.57 1.59Zm5.7-2.88S23 26 25 27l-1.33 4.07c-.08.2.07.43.29.43h1.52c.14 0 .26-.09.3-.22l1.72-5.78-3-5.38-3 2z"/><path fill="#bfccd5" d="M6.97 5s-1.86 3.92-.18 5.5h1.14S9.02 8.48 6.97 5"/><path fill="#738695" d="M7.02 8.82s.38-1.7 3.48-2.32c1.36-.27 2.42.05 3.11.39.48.24.57.89.16 1.25l-.76.67s1.72-.24 1.99.18c.22.33-.5 1.5-.5 1.5s1.08.02 1.5.5c.11.13.52.54.94.96.69.68 1.54 1.16 2.48 1.4l2.57.64-6.5-.5-4.04-2.91-4.44-1.77Z"/><path fill="#bfccd5" d="M31.4 12.92c-1.91-2.01-5.4-2.75-8.9-.42-4.85 3.23-10-2-10-2-3.36-2.69-5.37-2.21-6.32-1.61-.38.24-.67.59-.87.99l-2.2 4.36c-.84 1.66.82 3.47 2.55 2.79L9.01 16c.71 2.83 2.17 4.41 2.96 5.09.35.3.58.72.64 1.18l.9 7.23-.75 4.05c-.05.27.15.51.42.51h1.89c.24 0 .43-.19.43-.43V29.5s.63-3.81.89-5.36c.06-.34.4-.53.72-.42 3.39 1.12 5.78-.06 6.81-.77.28-.19.67-.08.78.25 1.15 3.42 3.79 4.3 3.79 4.3l-1.34 6.04c-.06.27.15.53.42.53h1.58c.2 0 .37-.13.42-.32l1.81-6.86c.06-.24.03-.51-.1-.73l-.78-1.36a.788.788 0 0 1-.06-.67l2.03-5.63c.06-.16.1-.32.15-.48.22 1.19.35 2.66.35 4.48 0 .28.22.5.5.5s.5-.22.5-.5c0-5.84-1.37-8.43-2.6-9.58Z"/><path fill="#dfe7ed" d="M5.82 15.37S6.5 11.5 2.5 13.5c-2 1-1.06 2.92.36 3.98 1.04.77 2.53.6 3.33-.41.5-.63.67-1.31-.38-1.7Z"/><path fill="#bfccd5" d="M10.38 4.3s-2.86 3.97-1.32 6.06l1.26.23s1.62-2.02.06-6.3Z"/><path fill="#738695" d="M9.76 8.82c.25.1.52.23.79.38.11-.74.08-1.78-.43-3.18 0 0-.88 1.26-1.08 2.59.24.04.48.11.71.2Zm-6 6.12c.13-.52.06-.98-.15-1.04s-.5.32-.63.84-.06.98.15 1.04.5-.32.63-.84"/><path d="M7.2 12.82c.44 0 .8-.36.8-.8s-.36-.8-.8-.8-.8.36-.8.8.36.8.8.8"/><path fill="#738695" d="M13.18 34.06h1.89c.24 0 .43-.19.43-.43V33h-2.65l-.1.55c-.05.27.15.51.42.51ZM27.28 33l-.12.54c-.06.27.15.53.42.53h1.58c.2 0 .37-.13.42-.32l.2-.74h-2.5Zm6.21-11.18c-.98 0-1.93 1.45 0 3.91 0 0 2.44-3.92 0-3.91"/></svg>

After

Width:  |  Height:  |  Size: 2 KiB

1
qml/js/emoji/1fada.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#f1b777" d="M16.4 26.75c.52.82 1.17 2 1.81 3.58.69 1.71 2.05 3.05 3.8 3.64.05.02.09.03.14.05 3.88 1.31 8.02-1.13 8.75-5.15.22-1.19-.05-2.42-.74-3.42l-1.32-1.9c-.3-.43-.25-1.01.13-1.37 1.35-1.29 4.18-4.55 3.34-8.56-.08-.4-2.81-4.11-6.72-1.05a1.77 1.77 0 0 0-.4 1.96c.36.84.71 2.15.48 3.73-.04.27-.41.32-.51.07-.42-.99-1.31-2.54-2.93-3.14-.42-.16-.71-.55-.71-1v-.98c0-.41.25-.78.63-.91 1.17-.42 3.61-1.55 4.37-3.8 0 0 .49-1.47-.59-2.35-.9-.74-2.29-.51-3.01.4-.38.48-.96.95-1.75.95 0 0 .08-.78.06-1.73a3.497 3.497 0 0 0-2.92-3.37h-.08c-2.49-.42-5.71.1-7.88 2.44-.51.55-1.01 1.1-1.2 1.83-.6 2.35-1.33 8.02 2.85 14.83 0 0-3.43-.77-4.95-4.31a2.197 2.197 0 0 0-2.46-1.3c-1.4.28-2.51 1.36-2.81 2.76-.41 1.87.14 3.82 1.47 5.2 1.9 1.96 5.27 3.9 10.54 2.02.98-.35 2.07 0 2.63.88Z"/><path fill="#ffd875" d="M29.06 27.58c-.69-1.6-3.11-2.09-5.41-1.1-2.3.99-3.6 3.09-2.91 4.69.69 1.6 3.11 2.09 5.41 1.1s3.6-3.09 2.91-4.69Z"/><path fill="#ea9e48" d="M20.21 24.17c-1.56 1.01-2.54 2.4-3.17 3.69.19.35.39.74.59 1.17.52-1.35 1.46-2.96 3.12-4.03 2.16-1.39 5.07-1.59 8.66-.63l-.58-.84c-.08-.11-.13-.24-.16-.36-3.43-.74-6.27-.42-8.46 1m-16.99-.32c.14.14.29.29.45.43-.14-1.9.23-3.4 1.09-4.48.83-1.04 1.96-1.47 2.77-1.65-.17-.28-.34-.59-.48-.92-.93.25-2.13.79-3.06 1.94-.83 1.03-1.27 2.37-1.33 3.98.17.25.36.48.57.7Zm6.8-3.21c-.51-.32-1.07-.74-1.59-1.3-.78.65-1.84 1.79-2.25 3.48-.24 1-.23 2.04.04 3.11.7.3 1.47.52 2.34.63-.53-1.17-.68-2.26-.44-3.24.34-1.42 1.4-2.32 1.9-2.68m6.88-1.34c1.98-2.42 5.18-3.07 6.72-3.24a4.27 4.27 0 0 0-1.27-.82c-1.83.36-4.42 1.23-6.21 3.42-1.49 1.82-2.12 4.2-1.92 7.1.34-.05.69-.02 1.01.08-.22-2.7.33-4.9 1.68-6.54Zm4.6-5.24v-.85c0-.41.25-.78.63-.91.06-.02.13-.05.2-.08-2.73-1.63-5.39-2.16-7.93-1.55-2.46.59-4.27 2.13-5.4 3.39.15.78.35 1.59.63 2.44.64-.94 2.46-3.22 5.25-3.89 2.06-.49 4.29 0 6.62 1.45ZM8.94 7.5c-.08.43-.16.91-.21 1.46 2.43-2.11 7.03-5.05 12.46-3.32-.02-.39-.1-.77-.25-1.12-5.04-1.36-9.34.91-12 2.98Zm14.98-1.67c-.36.13-.69.35-.95.65.76.34 1.62.87 2.09 1.71.35.62.43 1.34.27 2.16a5.2 5.2 0 0 0 1.02-1.48c-.08-.41-.21-.8-.42-1.17-.48-.86-1.25-1.45-2.01-1.86Zm8.51 8.7c-1.28-1.21-2.63-1.88-4.03-1.98-1.4-.09-2.54.41-3.3.87-.09.36-.07.74.08 1.09 0 .01.01.03.02.05.54-.41 1.7-1.12 3.14-1.01 1.39.1 2.76.91 4.06 2.39.05-.45.07-.92.03-1.41m-3.98.82c-.97-.3-1.96-.25-2.96.12.08.31.14.65.18 1.02.85-.36 1.68-.43 2.48-.18 1.46.44 2.56 1.77 3.21 2.75.18-.33.34-.68.49-1.05-.76-1.03-1.91-2.21-3.41-2.66Z"/></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
qml/js/emoji/1fadb.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#4e932b" d="M28 5C12 5 2.03 33.73 2.03 33.73c.01.24.21.42.45.4l8.78-5.46s.36-.61.83-1.4c.75.3 1.57.45 2.43.4a5.789 5.789 0 0 0 5.44-5.08 6.373 6.373 0 0 0 4.31-6.19c3.64-.44 6.18-4.23 4.61-8.08-.62-1.52.75-3.33-.89-3.33Z"/><circle cx="7.7" cy="30.6" r="3.3" fill="#9bd57f"/><path fill="#4e932b" d="M7 25s-2 4 1.7 6c.26.14 2.46-1.81 2.38-1.4S7.4 25.11 7.4 25.11z"/><circle cx="12.16" cy="26.74" r="5.16" fill="#9bd57f"/><path fill="#4e932b" d="M11 20s-4 2 1 8l4 1z"/><circle cx="15.5" cy="21.5" r="5.5" fill="#9bd57f"/><path fill="#4e932b" d="M13.8 15.12S12.48 21.99 19 24l1.42-1.14-6.63-7.74Z"/><circle cx="19.75" cy="16.33" r="5.58" fill="#9bd57f"/><path fill="#4e932b" d="M19.27 9.73s-1.49 5.78 2.9 8.38l5.69-2.55-8.6-5.83Z"/><circle cx="24.44" cy="11.46" r="4.96" fill="#9bd57f"/><path fill="#69b546" d="M2.35 34.03C4.2 33.16 19 22 29.25 8.47l1.71 1S21 38 2.39 34.23c-.1-.02-.13-.15-.04-.2Z"/><path fill="#4e932b" d="M32.64 3.18c2.46.88 2.24-1.04 1.69-1.62-.81-.84-2.22-.94-3.3-.07-.35.28-.83.89-1.32 1.59-.2.29-.57.38-.89.25-2.85-1.16-5.8-1.23-9.4.4 0 0 2.41 3.35 7.37 3.46 0 0-3.37 3.24-.99 8.24 0 0 3.35-2.41 3.42-5.71 0 0 1.6 2.51 4.91 2.58 0 0-.61-5.8-3.37-7.95-.18-.14-.16-.43.04-.56.74-.47 1.43-.77 1.87-.61Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
qml/js/emoji/1fae8.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><defs><clipPath id="a"><path fill="none" d="M.997 18 17.996 1.001l16.999 17-17 16.998z"/></clipPath></defs><g clip-path="url(#a)"><path fill="#ffcb26" d="M26.5 26.5c-4.69 4.69-12.31 4.69-17 0-4.69-4.69-4.69-12.31 0-17s12.31-4.69 17 0c4.69 4.69 4.69 12.31 0 17"/><path fill="#b68600" d="M14.93 13.99c.65.65 1.92.44 2.83-.47.91-.91 1.12-2.18.47-2.83s-1.92-.44-2.83.47-1.12 2.18-.47 2.83Z"/><path fill="#b68600" d="M16.35 15.4c.65.65 1.92.44 2.83-.47.91-.91 1.12-2.18.47-2.83s-1.92-.44-2.83.47-1.12 2.18-.47 2.83m5.66 5.67c.65.65 1.92.44 2.83-.47s1.12-2.18.47-2.83-1.92-.44-2.83.47-1.12 2.18-.47 2.83Z"/><path fill="#b68600" d="M20.6 19.65c.65.65 1.92.44 2.83-.47s1.12-2.18.47-2.83-1.92-.44-2.83.47-1.12 2.18-.47 2.83"/><path fill="#694400" d="M12.33 23.67c1.04 1.04 2.95.83 4.25-.47s1.52-3.21.47-4.25-2.95-.83-4.25.47-1.52 3.21-.47 4.25Z"/></g><path fill="#4facf0" d="M21.89 33.66c-.46 0-.88-.32-.98-.79-.11-.54.23-1.07.77-1.19 8.4-1.78 9.92-9.63 9.99-9.96.1-.54.62-.9 1.16-.81s.9.62.81 1.16c-.02.09-1.79 9.5-11.54 11.57-.07.01-.14.02-.21.02Zm5.31 2.33c-.46 0-.88-.32-.98-.79-.11-.54.23-1.07.77-1.19 5.89-1.25 6.97-6.75 7.01-6.99.1-.54.62-.9 1.16-.8.54.1.9.61.81 1.16-.05.29-1.33 7.06-8.57 8.59-.07.01-.14.02-.21.02ZM3.34 15.1c-.07 0-.14 0-.21-.02a.998.998 0 0 1-.77-1.19C4.43 4.15 13.83 2.37 13.93 2.36a1 1 0 1 1 .35 1.97c-.35.06-8.18 1.6-9.96 9.99-.1.47-.52.79-.98.79ZM1.01 9.8c-.07 0-.14 0-.21-.02a.998.998 0 0 1-.77-1.19C1.56 1.36 8.34.08 8.62.03A1 1 0 1 1 8.97 2c-.26.05-5.74 1.14-6.99 7.01-.1.47-.51.79-.98.79Z"/><path fill="#694400" d="M15.64 14.69c.65.65 1.92.44 2.83-.47.91-.91 1.12-2.18.47-2.83s-1.92-.44-2.83.47-1.12 2.18-.47 2.83Zm5.67 5.67c.65.65 1.92.44 2.83-.47s1.12-2.18.47-2.83-1.92-.44-2.83.47-1.12 2.18-.47 2.83"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#E0AA94" d="M16.5 31c-.64 0-1.28-.24-1.77-.73C9.28 24.82 6.79 5.49 6.52 3.3 6.35 1.93 7.33.68 8.7.52c1.38-.17 2.62.81 2.78 2.18.93 7.6 3.59 20.84 6.79 24.04a2.499 2.499 0 0 1-1.77 4.27Z"/><path fill="#F7DECE" d="m28.9 25.33-8.75 1.15c-2.15.51-3.94-1.06-5.01-2.8l-.17-.38c-1.3-2.86-2.22-5.87-2.73-8.97L10.32 2.64a2.392 2.392 0 0 0-4.77.31l.22 12.21c0 .07-.02.14-.05.21-.78 1.3-1.35 3.72.04 7.88-.15.77-.44 1.48-.92 2.69-1.28 4.82 2.35 9.55 7.34 9.55h16.51a2.76 2.76 0 0 0 2.76-2.76v-5.16c0-1.35-1.19-2.4-2.54-2.22Z"/><path fill="#E0AA94" d="M12.21 26.47c-.77-1.68-1.11-3.93-1.47-5.82-.22-1.26-.42-2.55-.6-3.81-.05-1.6-1.43-2.97-3.04-2.74-.47.01-.92.11-1.35.26l.02.86c1.24-.57 2.76-.43 3.19 1.06.44 3.71.43 7.3 1.67 10.9.47 1.09 2.09.36 1.59-.71ZM26.55 34H13.33c-1.55 0-3.09-.15-4.61-.46l-2.04-.41a7.564 7.564 0 0 0 5.49 2.35h16.51c1.25 0 2.3-.84 2.64-1.98-1.57.32-3.16.5-4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 958 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#D2A077" d="M16.5 31c-.64 0-1.28-.24-1.77-.73C9.28 24.82 6.79 5.49 6.52 3.3 6.35 1.93 7.33.68 8.7.52c1.38-.17 2.62.81 2.78 2.18.93 7.6 3.59 20.84 6.79 24.04a2.499 2.499 0 0 1-1.77 4.27Z"/><path fill="#F3D2A2" d="m28.9 25.33-8.75 1.15c-2.15.51-3.94-1.06-5.01-2.8l-.17-.38c-1.3-2.86-2.22-5.87-2.73-8.97L10.32 2.64a2.392 2.392 0 0 0-4.77.31l.22 12.21c0 .07-.02.14-.05.21-.78 1.3-1.35 3.72.04 7.88-.15.77-.44 1.48-.92 2.69-1.28 4.82 2.35 9.55 7.34 9.55h16.51a2.76 2.76 0 0 0 2.76-2.76v-5.16c0-1.35-1.19-2.4-2.54-2.22Z"/><path fill="#D2A077" d="M12.21 26.47c-.77-1.68-1.11-3.93-1.47-5.82-.22-1.26-.42-2.55-.6-3.81-.05-1.6-1.43-2.97-3.04-2.74-.47.01-.92.11-1.35.26l.02.86c1.24-.57 2.76-.43 3.19 1.06.44 3.71.43 7.3 1.67 10.9.47 1.09 2.09.36 1.59-.71ZM26.55 34H13.33c-1.55 0-3.09-.15-4.61-.46l-2.04-.41a7.564 7.564 0 0 0 5.49 2.35h16.51c1.25 0 2.3-.84 2.64-1.98-1.57.32-3.16.5-4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 958 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#B78B60" d="M16.5 31c-.64 0-1.28-.24-1.77-.73C9.28 24.82 6.79 5.49 6.52 3.3 6.35 1.93 7.33.68 8.7.52c1.38-.17 2.62.81 2.78 2.18.93 7.6 3.59 20.84 6.79 24.04a2.499 2.499 0 0 1-1.77 4.27Z"/><path fill="#D5AB88" d="m28.9 25.33-8.75 1.15c-2.15.51-3.94-1.06-5.01-2.8l-.17-.38c-1.3-2.86-2.22-5.87-2.73-8.97L10.32 2.64a2.392 2.392 0 0 0-4.77.31l.22 12.21c0 .07-.02.14-.05.21-.78 1.3-1.35 3.72.04 7.88-.15.77-.44 1.48-.92 2.69-1.28 4.82 2.35 9.55 7.34 9.55h16.51a2.76 2.76 0 0 0 2.76-2.76v-5.16c0-1.35-1.19-2.4-2.54-2.22Z"/><path fill="#B78B60" d="M12.21 26.47c-.77-1.68-1.11-3.93-1.47-5.82-.22-1.26-.42-2.55-.6-3.81-.05-1.6-1.43-2.97-3.04-2.74-.47.01-.92.11-1.35.26l.02.86c1.24-.57 2.76-.43 3.19 1.06.44 3.71.43 7.3 1.67 10.9.47 1.09 2.09.36 1.59-.71ZM26.55 34H13.33c-1.55 0-3.09-.15-4.61-.46l-2.04-.41a7.564 7.564 0 0 0 5.49 2.35h16.51c1.25 0 2.3-.84 2.64-1.98-1.57.32-3.16.5-4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 958 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path xmlns="http://www.w3.org/2000/svg" fill="#90603E" d="M16.5 31c-.64 0-1.28-.24-1.77-.73C9.28 24.82 6.79 5.49 6.52 3.3 6.35 1.93 7.33.68 8.7.52c1.38-.17 2.62.81 2.78 2.18.93 7.6 3.59 20.84 6.79 24.04a2.499 2.499 0 0 1-1.77 4.27Z"/><path fill="#AF7E57" d="m28.9 25.33-8.75 1.15c-2.15.51-3.94-1.06-5.01-2.8l-.17-.38c-1.3-2.86-2.22-5.87-2.73-8.97L10.32 2.64a2.392 2.392 0 0 0-4.77.31l.22 12.21c0 .07-.02.14-.05.21-.78 1.3-1.35 3.72.04 7.88-.15.77-.44 1.48-.92 2.69-1.28 4.82 2.35 9.55 7.34 9.55h16.51a2.76 2.76 0 0 0 2.76-2.76v-5.16c0-1.35-1.19-2.4-2.54-2.22Z"/><path xmlns="http://www.w3.org/2000/svg" fill="#90603E" d="M12.21 26.47c-.77-1.68-1.11-3.93-1.47-5.82-.22-1.26-.42-2.55-.6-3.81-.05-1.6-1.43-2.97-3.04-2.74-.47.01-.92.11-1.35.26l.02.86c1.24-.57 2.76-.43 3.19 1.06.44 3.71.43 7.3 1.67 10.9.47 1.09 2.09.36 1.59-.71ZM26.55 34H13.33c-1.55 0-3.09-.15-4.61-.46l-2.04-.41a7.564 7.564 0 0 0 5.49 2.35h16.51c1.25 0 2.3-.84 2.64-1.98-1.57.32-3.16.5-4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#583529" d="M16.5 31c-.64 0-1.28-.24-1.77-.73C9.28 24.82 6.79 5.49 6.52 3.3 6.35 1.93 7.33.68 8.7.52c1.38-.17 2.62.81 2.78 2.18.93 7.6 3.59 20.84 6.79 24.04a2.499 2.499 0 0 1-1.77 4.27Z"/><path fill="#7C533E" d="m28.9 25.33-8.75 1.15c-2.15.51-3.94-1.06-5.01-2.8l-.17-.38c-1.3-2.86-2.22-5.87-2.73-8.97L10.32 2.64a2.392 2.392 0 0 0-4.77.31l.22 12.21c0 .07-.02.14-.05.21-.78 1.3-1.35 3.72.04 7.88-.15.77-.44 1.48-.92 2.69-1.28 4.82 2.35 9.55 7.34 9.55h16.51a2.76 2.76 0 0 0 2.76-2.76v-5.16c0-1.35-1.19-2.4-2.54-2.22Z"/><path fill="#583529" d="M12.21 26.47c-.77-1.68-1.11-3.93-1.47-5.82-.22-1.26-.42-2.55-.6-3.81-.05-1.6-1.43-2.97-3.04-2.74-.47.01-.92.11-1.35.26l.02.86c1.24-.57 2.76-.43 3.19 1.06.44 3.71.43 7.3 1.67 10.9.47 1.09 2.09.36 1.59-.71ZM26.55 34H13.33c-1.55 0-3.09-.15-4.61-.46l-2.04-.41a7.564 7.564 0 0 0 5.49 2.35h16.51c1.25 0 2.3-.84 2.64-1.98-1.57.32-3.16.5-4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 958 B

1
qml/js/emoji/1faf7.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EF9645" d="M16.5 31c-.64 0-1.28-.24-1.77-.73C9.28 24.82 6.79 5.49 6.52 3.3 6.35 1.93 7.33.68 8.7.52c1.38-.17 2.62.81 2.78 2.18.93 7.6 3.59 20.84 6.79 24.04a2.499 2.499 0 0 1-1.77 4.27Z"/><path fill="#FFDC5D" d="m28.9 25.33-8.75 1.15c-2.15.51-3.94-1.06-5.01-2.8l-.17-.38c-1.3-2.86-2.22-5.87-2.73-8.97L10.32 2.64a2.392 2.392 0 0 0-4.77.31l.22 12.21c0 .07-.02.14-.05.21-.78 1.3-1.35 3.72.04 7.88-.15.77-.44 1.48-.92 2.69-1.28 4.82 2.35 9.55 7.34 9.55h16.51a2.76 2.76 0 0 0 2.76-2.76v-5.16c0-1.35-1.19-2.4-2.54-2.22Z"/><path fill="#EF9645" d="M12.21 26.47c-.77-1.68-1.11-3.93-1.47-5.82-.22-1.26-.42-2.55-.6-3.81-.05-1.6-1.43-2.97-3.04-2.74-.47.01-.92.11-1.35.26l.02.86c1.24-.57 2.76-.43 3.19 1.06.44 3.71.43 7.3 1.67 10.9.47 1.09 2.09.36 1.59-.71ZM26.55 34H13.33c-1.55 0-3.09-.15-4.61-.46l-2.04-.41a7.564 7.564 0 0 0 5.49 2.35h16.51c1.25 0 2.3-.84 2.64-1.98-1.57.32-3.16.5-4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 958 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#E0AA94" d="M19.5 31c.64 0 1.28-.24 1.77-.73 5.45-5.45 7.95-24.78 8.21-26.96.17-1.37-.81-2.62-2.18-2.78-1.38-.17-2.62.81-2.78 2.18-.93 7.6-3.59 20.84-6.79 24.04a2.499 2.499 0 0 0 1.77 4.27Z"/><path fill="#F7DECE" d="m7.1 25.33 8.75 1.15c2.15.51 3.94-1.06 5.01-2.8l.17-.38c1.3-2.86 2.22-5.87 2.73-8.97l1.92-11.69a2.392 2.392 0 0 1 4.77.31l-.22 12.21c0 .07.02.14.05.21.78 1.3 1.35 3.72-.04 7.88.15.77.44 1.48.92 2.69 1.28 4.82-2.35 9.55-7.34 9.55H7.33a2.76 2.76 0 0 1-2.76-2.76v-5.16c0-1.35 1.19-2.4 2.54-2.22Z"/><path fill="#E0AA94" d="M23.79 26.47c.77-1.68 1.11-3.93 1.47-5.82.22-1.26.42-2.55.6-3.81.05-1.6 1.43-2.97 3.04-2.74.47.01.92.11 1.35.26l-.02.86c-1.24-.57-2.76-.43-3.19 1.06-.44 3.71-.43 7.3-1.67 10.9-.47 1.09-2.09.36-1.59-.71ZM9.45 34h13.22c1.55 0 3.09-.15 4.61-.46l2.04-.41a7.564 7.564 0 0 1-5.49 2.35H7.33c-1.25 0-2.3-.84-2.64-1.98 1.57.32 3.16.5 4.76.5"/></svg>

After

Width:  |  Height:  |  Size: 947 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#D2A077" d="M19.5 31c.64 0 1.28-.24 1.77-.73 5.45-5.45 7.95-24.78 8.21-26.96.17-1.37-.81-2.62-2.18-2.78-1.38-.17-2.62.81-2.78 2.18-.93 7.6-3.59 20.84-6.79 24.04a2.499 2.499 0 0 0 1.77 4.27Z"/><path fill="#F3D2A2" d="m7.1 25.33 8.75 1.15c2.15.51 3.94-1.06 5.01-2.8l.17-.38c1.3-2.86 2.22-5.87 2.73-8.97l1.92-11.69a2.392 2.392 0 0 1 4.77.31l-.22 12.21c0 .07.02.14.05.21.78 1.3 1.35 3.72-.04 7.88.15.77.44 1.48.92 2.69 1.28 4.82-2.35 9.55-7.34 9.55H7.33a2.76 2.76 0 0 1-2.76-2.76v-5.16c0-1.35 1.19-2.4 2.54-2.22Z"/><path fill="#D2A077" d="M23.79 26.47c.77-1.68 1.11-3.93 1.47-5.82.22-1.26.42-2.55.6-3.81.05-1.6 1.43-2.97 3.04-2.74.47.01.92.11 1.35.26l-.02.86c-1.24-.57-2.76-.43-3.19 1.06-.44 3.71-.43 7.3-1.67 10.9-.47 1.09-2.09.36-1.59-.71ZM9.45 34h13.22c1.55 0 3.09-.15 4.61-.46l2.04-.41a7.564 7.564 0 0 1-5.49 2.35H7.33c-1.25 0-2.3-.84-2.64-1.98 1.57.32 3.16.5 4.76.5Z"/></svg>

After

Width:  |  Height:  |  Size: 948 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#B78B60" d="M19.5 31c.64 0 1.28-.24 1.77-.73 5.45-5.45 7.95-24.78 8.21-26.96.17-1.37-.81-2.62-2.18-2.78-1.38-.17-2.62.81-2.78 2.18-.93 7.6-3.59 20.84-6.79 24.04a2.499 2.499 0 0 0 1.77 4.27Z"/><path fill="#D5AB88" d="m7.1 25.33 8.75 1.15c2.15.51 3.94-1.06 5.01-2.8l.17-.38c1.3-2.86 2.22-5.87 2.73-8.97l1.92-11.69a2.392 2.392 0 0 1 4.77.31l-.22 12.21c0 .07.02.14.05.21.78 1.3 1.35 3.72-.04 7.88.15.77.44 1.48.92 2.69 1.28 4.82-2.35 9.55-7.34 9.55H7.33a2.76 2.76 0 0 1-2.76-2.76v-5.16c0-1.35 1.19-2.4 2.54-2.22Z"/><path fill="#B78B60" d="M23.79 26.47c.77-1.68 1.11-3.93 1.47-5.82.22-1.26.42-2.55.6-3.81.05-1.6 1.43-2.97 3.04-2.74.47.01.92.11 1.35.26l-.02.86c-1.24-.57-2.76-.43-3.19 1.06-.44 3.71-.43 7.3-1.67 10.9-.47 1.09-2.09.36-1.59-.71ZM9.45 34h13.22c1.55 0 3.09-.15 4.61-.46l2.04-.41a7.564 7.564 0 0 1-5.49 2.35H7.33c-1.25 0-2.3-.84-2.64-1.98 1.57.32 3.16.5 4.76.5"/></svg>

After

Width:  |  Height:  |  Size: 947 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#90603E" d="M19.5 31c.64 0 1.28-.24 1.77-.73 5.45-5.45 7.95-24.78 8.21-26.96.17-1.37-.81-2.62-2.18-2.78-1.38-.17-2.62.81-2.78 2.18-.93 7.6-3.59 20.84-6.79 24.04a2.499 2.499 0 0 0 1.77 4.27Z"/><path fill="#AF7E57" d="m7.1 25.33 8.75 1.15c2.15.51 3.94-1.06 5.01-2.8l.17-.38c1.3-2.86 2.22-5.87 2.73-8.97l1.92-11.69a2.392 2.392 0 0 1 4.77.31l-.22 12.21c0 .07.02.14.05.21.78 1.3 1.35 3.72-.04 7.88.15.77.44 1.48.92 2.69 1.28 4.82-2.35 9.55-7.34 9.55H7.33a2.76 2.76 0 0 1-2.76-2.76v-5.16c0-1.35 1.19-2.4 2.54-2.22Z"/><path fill="#90603E" d="M23.79 26.47c.77-1.68 1.11-3.93 1.47-5.82.22-1.26.42-2.55.6-3.81.05-1.6 1.43-2.97 3.04-2.74.47.01.92.11 1.35.26l-.02.86c-1.24-.57-2.76-.43-3.19 1.06-.44 3.71-.43 7.3-1.67 10.9-.47 1.09-2.09.36-1.59-.71ZM9.45 34h13.22c1.55 0 3.09-.15 4.61-.46l2.04-.41a7.564 7.564 0 0 1-5.49 2.35H7.33c-1.25 0-2.3-.84-2.64-1.98 1.57.32 3.16.5 4.76.5"/></svg>

After

Width:  |  Height:  |  Size: 947 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#583529" d="M19.5 31c.64 0 1.28-.24 1.77-.73 5.45-5.45 7.95-24.78 8.21-26.96.17-1.37-.81-2.62-2.18-2.78-1.38-.17-2.62.81-2.78 2.18-.93 7.6-3.59 20.84-6.79 24.04a2.499 2.499 0 0 0 1.77 4.27Z"/><path fill="#7C533E" d="m7.1 25.33 8.75 1.15c2.15.51 3.94-1.06 5.01-2.8l.17-.38c1.3-2.86 2.22-5.87 2.73-8.97l1.92-11.69a2.392 2.392 0 0 1 4.77.31l-.22 12.21c0 .07.02.14.05.21.78 1.3 1.35 3.72-.04 7.88.15.77.44 1.48.92 2.69 1.28 4.82-2.35 9.55-7.34 9.55H7.33a2.76 2.76 0 0 1-2.76-2.76v-5.16c0-1.35 1.19-2.4 2.54-2.22Z"/><path fill="#583529" d="M23.79 26.47c.77-1.68 1.11-3.93 1.47-5.82.22-1.26.42-2.55.6-3.81.05-1.6 1.43-2.97 3.04-2.74.47.01.92.11 1.35.26l-.02.86c-1.24-.57-2.76-.43-3.19 1.06-.44 3.71-.43 7.3-1.67 10.9-.47 1.09-2.09.36-1.59-.71ZM9.45 34h13.22c1.55 0 3.09-.15 4.61-.46l2.04-.41a7.564 7.564 0 0 1-5.49 2.35H7.33c-1.25 0-2.3-.84-2.64-1.98 1.57.32 3.16.5 4.76.5"/></svg>

After

Width:  |  Height:  |  Size: 947 B

1
qml/js/emoji/1faf8.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EF9645" d="M19.5 31c.64 0 1.28-.24 1.77-.73 5.45-5.45 7.95-24.78 8.21-26.96.17-1.37-.81-2.62-2.18-2.78-1.38-.17-2.62.81-2.78 2.18-.93 7.6-3.59 20.84-6.79 24.04a2.499 2.499 0 0 0 1.77 4.27Z"/><path fill="#FFDC5D" d="m7.1 25.33 8.75 1.15c2.15.51 3.94-1.06 5.01-2.8l.17-.38c1.3-2.86 2.22-5.87 2.73-8.97l1.92-11.69a2.392 2.392 0 0 1 4.77.31l-.22 12.21c0 .07.02.14.05.21.78 1.3 1.35 3.72-.04 7.88.15.77.44 1.48.92 2.69 1.28 4.82-2.35 9.55-7.34 9.55H7.33a2.76 2.76 0 0 1-2.76-2.76v-5.16c0-1.35 1.19-2.4 2.54-2.22Z"/><path fill="#EF9645" d="M23.79 26.47c.77-1.68 1.11-3.93 1.47-5.82.22-1.26.42-2.55.6-3.81.05-1.6 1.43-2.97 3.04-2.74.47.01.92.11 1.35.26l-.02.86c-1.24-.57-2.76-.43-3.19 1.06-.44 3.71-.43 7.3-1.67 10.9-.47 1.09-2.09.36-1.59-.71ZM9.45 34h13.22c1.55 0 3.09-.15 4.61-.46l2.04-.41a7.564 7.564 0 0 1-5.49 2.35H7.33c-1.25 0-2.3-.84-2.64-1.98 1.57.32 3.16.5 4.76.5"/></svg>

After

Width:  |  Height:  |  Size: 947 B

View file

@ -447,10 +447,15 @@ function handleLink(link) {
handleTMeLink(link, tMePrefix); handleTMeLink(link, tMePrefix);
} else if (link.indexOf(tMePrefixHttp) === 0) { } else if (link.indexOf(tMePrefixHttp) === 0) {
handleTMeLink(link, tMePrefixHttp); handleTMeLink(link, tMePrefixHttp);
} else {
Debug.log("Trying to open URL externally: " + link)
if (link.indexOf("://") === -1) {
Qt.openUrlExternally("https://" + link)
} else { } else {
Qt.openUrlExternally(link); Qt.openUrlExternally(link);
} }
} }
}
} }
function getVideoHeight(videoWidth, videoData) { function getVideoHeight(videoWidth, videoData) {
@ -512,7 +517,7 @@ function handleErrorMessage(code, message) {
} }
function getMessagesNeededForwardPermissions(messages) { function getMessagesNeededForwardPermissions(messages) {
var neededPermissions = ["can_send_messages"] var neededPermissions = ["can_send_basic_messages"]
var mediaMessageTypes = ["messageAudio", "messageDocument", "messagePhoto", "messageVideo", "messageVideoNote", "messageVoiceNote"] var mediaMessageTypes = ["messageAudio", "messageDocument", "messagePhoto", "messageVideo", "messageVideoNote", "messageVoiceNote"]
var otherMessageTypes = ["messageAnimation", "messageGame", "messageSticker"] var otherMessageTypes = ["messageAnimation", "messageGame", "messageSticker"]

File diff suppressed because one or more lines are too long

View file

@ -417,7 +417,7 @@ Page {
chatPage.messageIdToScrollTo = messageId chatPage.messageIdToScrollTo = messageId
} }
if (chatPage.messageIdToScrollTo && chatPage.messageIdToScrollTo != "") { if (chatPage.messageIdToScrollTo && chatPage.messageIdToScrollTo != "") {
var index = chatModel.getMessageIndex(chatPage.messageIdToScrollTo); var index = chatModel.getDisplayedMessageIndex(chatPage.messageIdToScrollTo);
if(index !== -1) { if(index !== -1) {
chatPage.messageIdToScrollTo = ""; chatPage.messageIdToScrollTo = "";
chatView.scrollToIndex(index); chatView.scrollToIndex(index);
@ -601,12 +601,16 @@ Page {
onSponsoredMessageReceived: { onSponsoredMessageReceived: {
chatPage.containsSponsoredMessages = true; chatPage.containsSponsoredMessages = true;
} }
onReactionsUpdated: {
availableReactions = tdLibWrapper.getChatReactions(chatInformation.id);
}
} }
Connections { Connections {
target: chatModel target: chatModel
onMessagesReceived: { onMessagesReceived: {
Debug.log("[ChatPage] Messages received, view has ", chatView.count, " messages, last known message index ", modelIndex, ", own messages were read before index ", lastReadSentIndex); var proxyIndex = chatProxyModel.mapRowFromSource(modelIndex, -1);
Debug.log("[ChatPage] Messages received, view has ", chatView.count, " messages, last known message index ", proxyIndex, "("+modelIndex+"), own messages were read before index ", lastReadSentIndex);
if (totalCount === 0) { if (totalCount === 0) {
if (chatPage.iterativeInitialization) { if (chatPage.iterativeInitialization) {
chatPage.iterativeInitialization = false; chatPage.iterativeInitialization = false;
@ -620,9 +624,9 @@ Page {
} }
chatView.lastReadSentIndex = lastReadSentIndex; chatView.lastReadSentIndex = lastReadSentIndex;
chatView.scrollToIndex(modelIndex); chatView.scrollToIndex(proxyIndex);
chatPage.loading = false; chatPage.loading = false;
if (chatOverviewItem.visible && modelIndex >= (chatView.count - 10)) { if (chatOverviewItem.visible && proxyIndex >= (chatView.count - 10)) {
chatView.inCooldown = true; chatView.inCooldown = true;
chatModel.triggerLoadMoreFuture(); chatModel.triggerLoadMoreFuture();
} }
@ -635,6 +639,8 @@ Page {
chatViewCooldownTimer.restart(); chatViewCooldownTimer.restart();
chatViewStartupReadTimer.restart(); chatViewStartupReadTimer.restart();
/*
// Double-tap for reactions is currently disabled, let's see if we'll ever need it again
var remainingDoubleTapHints = appSettings.remainingDoubleTapHints; var remainingDoubleTapHints = appSettings.remainingDoubleTapHints;
Debug.log("Remaining double tap hints: " + remainingDoubleTapHints); Debug.log("Remaining double tap hints: " + remainingDoubleTapHints);
if (remainingDoubleTapHints > 0) { if (remainingDoubleTapHints > 0) {
@ -643,6 +649,7 @@ Page {
tapHintLabel.visible = true; tapHintLabel.visible = true;
appSettings.remainingDoubleTapHints = remainingDoubleTapHints - 1; appSettings.remainingDoubleTapHints = remainingDoubleTapHints - 1;
} }
*/
} }
onNewMessageReceived: { onNewMessageReceived: {
if (( chatView.manuallyScrolledToBottom && Qt.application.state === Qt.ApplicationActive ) || message.sender_id.user_id === chatPage.myUserId) { if (( chatView.manuallyScrolledToBottom && Qt.application.state === Qt.ApplicationActive ) || message.sender_id.user_id === chatPage.myUserId) {
@ -662,10 +669,13 @@ Page {
chatView.lastReadSentIndex = lastReadSentIndex; chatView.lastReadSentIndex = lastReadSentIndex;
} }
onMessagesIncrementalUpdate: { onMessagesIncrementalUpdate: {
Debug.log("Incremental update received. View now has ", chatView.count, " messages, view is on index ", modelIndex, ", own messages were read before index ", lastReadSentIndex); var proxyIndex = chatProxyModel.mapRowFromSource(modelIndex, -1);
Debug.log("Incremental update received. View now has ", chatView.count, " messages, view is on index ", proxyIndex, "("+modelIndex+"), own messages were read before index ", lastReadSentIndex);
chatView.lastReadSentIndex = lastReadSentIndex; chatView.lastReadSentIndex = lastReadSentIndex;
if (!chatPage.isInitialized) { if (!chatPage.isInitialized) {
chatView.scrollToIndex(modelIndex); if (proxyIndex > -1) {
chatView.scrollToIndex(proxyIndex);
}
} }
if (chatView.height > chatView.contentHeight) { if (chatView.height > chatView.contentHeight) {
Debug.log("[ChatPage] Chat content quite small..."); Debug.log("[ChatPage] Chat content quite small...");
@ -741,14 +751,26 @@ Page {
onTriggered: { onTriggered: {
Debug.log("scroll position changed, message index: ", lastQueuedIndex); Debug.log("scroll position changed, message index: ", lastQueuedIndex);
Debug.log("unread count: ", chatInformation.unread_count); Debug.log("unread count: ", chatInformation.unread_count);
var messageToRead = chatModel.getMessage(lastQueuedIndex); var modelIndex = chatProxyModel.mapRowToSource(lastQueuedIndex);
var messageToRead = chatModel.getMessage(modelIndex);
if (messageToRead['@type'] === "sponsoredMessage") { if (messageToRead['@type'] === "sponsoredMessage") {
Debug.log("sponsored message to read: ", messageToRead.id); Debug.log("sponsored message to read: ", messageToRead.id);
tdLibWrapper.viewMessage(chatInformation.id, messageToRead.message_id, false); tdLibWrapper.viewMessage(chatInformation.id, messageToRead.message_id, false);
} else if (chatInformation.unread_count > 0 && lastQueuedIndex > -1) { } else if (chatInformation.unread_count > 0 && lastQueuedIndex > -1) {
if (messageToRead) {
Debug.log("message to read: ", messageToRead.id); Debug.log("message to read: ", messageToRead.id);
if (messageToRead && messageToRead.id) { var messageId = messageToRead.id;
tdLibWrapper.viewMessage(chatInformation.id, messageToRead.id, false); var type = messageToRead.content["@type"];
if (messageToRead.media_album_id !== '0') {
var albumIds = chatModel.getMessageIdsForAlbum(messageToRead.media_album_id);
if (albumIds.length > 0) {
messageId = albumIds[albumIds.length - 1];
Debug.log("message to read last album message id: ", messageId);
}
}
if (messageId) {
tdLibWrapper.viewMessage(chatInformation.id, messageId, false);
}
} }
lastQueuedIndex = -1 lastQueuedIndex = -1
} }
@ -774,7 +796,7 @@ Page {
NamedAction { NamedAction {
visible: messageOptionsDrawer.showCopyMessageToClipboardMenuItem visible: messageOptionsDrawer.showCopyMessageToClipboardMenuItem
name: qsTr("Copy Message to Clipboard") name: qsTr("Copy Message to Clipboard")
action: messageOptionsDrawer.myMessage.copyMessageToClipboard action: messageOptionsDrawer.sourceItem.copyMessageToClipboard
}, },
NamedAction { NamedAction {
visible: messageOptionsDrawer.showForwardMessageMenuItem && messageOptionsDrawer.myMessage.can_be_forwarded visible: messageOptionsDrawer.showForwardMessageMenuItem && messageOptionsDrawer.myMessage.can_be_forwarded
@ -1216,7 +1238,6 @@ Page {
readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall; readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall;
readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) ) readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) )
readonly property bool pageIsSelecting: chatPage.isSelecting readonly property bool pageIsSelecting: chatPage.isSelecting
} }
function handleScrollPositionChanged() { function handleScrollPositionChanged() {
@ -1239,6 +1260,9 @@ Page {
positionViewAtIndex(index, (mode === undefined) ? ListView.Contain : mode) positionViewAtIndex(index, (mode === undefined) ? ListView.Contain : mode)
if(index === chatView.count - 1) { if(index === chatView.count - 1) {
manuallyScrolledToBottom = true; manuallyScrolledToBottom = true;
if(!chatView.atYEnd) {
chatView.positionViewAtEnd();
}
} }
} }
} }
@ -1271,7 +1295,13 @@ Page {
} }
} }
model: chatModel BoolFilterModel {
id: chatProxyModel
sourceModel: chatModel
filterRoleName: "album_entry_filter"
filterValue: false
}
model: chatProxyModel
header: Component { header: Component {
Loader { Loader {
active: !!chatPage.botInformation active: !!chatPage.botInformation
@ -1304,7 +1334,8 @@ Page {
} }
} }
function getContentComponentHeight(contentType, content, parentWidth) { function getContentComponentHeight(contentType, content, parentWidth, albumEntries) {
var unit;
switch(contentType) { switch(contentType) {
case "messageAnimatedEmoji": case "messageAnimatedEmoji":
return content.animated_emoji.sticker.height; return content.animated_emoji.sticker.height;
@ -1320,6 +1351,10 @@ Page {
case "messageVenue": case "messageVenue":
return parentWidth * 0.66666666; // 2 / 3; return parentWidth * 0.66666666; // 2 / 3;
case "messagePhoto": case "messagePhoto":
if(albumEntries > 0) {
unit = (parentWidth * 0.66666666)
return (albumEntries % 2 !== 0 ? unit * 0.75 : 0) + unit * albumEntries * 0.25
}
var biggest = content.photo.sizes[content.photo.sizes.length - 1]; var biggest = content.photo.sizes[content.photo.sizes.length - 1];
var aspectRatio = biggest.width/biggest.height; var aspectRatio = biggest.width/biggest.height;
return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio)); return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio));
@ -1328,6 +1363,10 @@ Page {
case "messageSticker": case "messageSticker":
return content.sticker.height; return content.sticker.height;
case "messageVideo": case "messageVideo":
if(albumEntries > 0) {
unit = (parentWidth * 0.66666666)
return (albumEntries % 2 !== 0 ? unit * 0.75 : 0) + unit * albumEntries * 0.25
}
return Functions.getVideoHeight(parentWidth, content.video); return Functions.getVideoHeight(parentWidth, content.video);
case "messageVideoNote": case "messageVideoNote":
return parentWidth return parentWidth
@ -1383,10 +1422,11 @@ Page {
chatId: chatModel.chatId chatId: chatModel.chatId
myMessage: model.display myMessage: model.display
messageId: model.message_id messageId: model.message_id
messageAlbumMessageIds: model.album_message_ids
messageViewCount: model.view_count messageViewCount: model.view_count
reactions: model.reactions reactions: model.reactions
chatReactions: availableReactions chatReactions: availableReactions
messageIndex: model.index messageIndex: chatProxyModel.mapRowToSource(model.index)
hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1 hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1
canReplyToMessage: chatPage.canSendMessages canReplyToMessage: chatPage.canSendMessages
onReplyToMessage: { onReplyToMessage: {
@ -1407,9 +1447,21 @@ Page {
id: messageListViewItemSimpleComponent id: messageListViewItemSimpleComponent
MessageListViewItemSimple {} MessageListViewItemSimple {}
} }
sourceComponent: chatView.simpleDelegateMessages.indexOf(model.content_type) > -1 ? messageListViewItemSimpleComponent : messageListViewItemComponent Component {
id: messageListViewItemHiddenComponent
Item {
property var myMessage: display
property bool senderIsUser: myMessage.sender_id["@type"] === "messageSenderUser"
property var userInformation: senderIsUser ? tdLibWrapper.getUserInformation(myMessage.sender_id.user_id) : null
property bool isOwnMessage: senderIsUser && chatPage.myUserId === myMessage.sender_id.user_id
height: 1
} }
VerticalScrollDecorator {} }
sourceComponent: chatView.simpleDelegateMessages.indexOf(model.content_type) > -1
? messageListViewItemSimpleComponent
: messageListViewItemComponent
}
VerticalScrollDecorator { flickable: chatView }
ViewPlaceholder { ViewPlaceholder {
id: chatViewPlaceholder id: chatViewPlaceholder
@ -1870,7 +1922,7 @@ Page {
Image { Image {
id: emojiPicture id: emojiPicture
source: "../js/emoji/" + modelData.file_name source: "../js/emoji/" + modelData.file_name +".svg"
width: Theme.fontSizeLarge width: Theme.fontSizeLarge
height: Theme.fontSizeLarge height: Theme.fontSizeLarge
} }

View file

@ -0,0 +1,109 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
// jolla-gallery/pages/FlickableImageView.qml
/*
FullscreenContentPage
- PagedView (jolla-gallery/FlickableImageView)
- delegate: Loader
- SilicaFlickable (Silica.private/ZoomableFlickable) (Sailfish.Gallery/ImageViewer)
- PinchArea
- dragDetector(?)
- image
- Item (Sailfish.Gallery/GalleryOverlay)
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0
import "../components"
import "../components/messageContent/mediaAlbumPage"
import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions
Page {
// id
id: page
// property declarations
property alias index: pagedView.currentIndex
property alias overlayActive: overlay.active
property alias delegate: pagedView.delegate
property var messages: [];
// message.content.caption.text
palette.colorScheme: Theme.LightOnDark
clip: status !== PageStatus.Active || pageStack.dragInProgress
navigationStyle: PageNavigation.Vertical
backgroundColor: 'black'
allowedOrientations: Orientation.All
// signal declarations
// JavaScript functions
// object (parent) properties
// large property bindings
// child objects
// states
// transitions
// content
PagedView {
id: pagedView
anchors.fill: parent
model: messages
delegate: Component {
Loader {
id: loader
asynchronous: true
visible: status == Loader.Ready
width: PagedView.contentWidth
height: PagedView.contentHeight
states: [
State {
when: model.modelData.content['@type'] === 'messagePhoto'
PropertyChanges {
target: loader
source: "../components/messageContent/mediaAlbumPage/PhotoComponent.qml"
}
},
State {
when: model.modelData.content['@type'] === 'messageVideo'
PropertyChanges {
target: loader
source: "../components/messageContent/mediaAlbumPage/VideoComponent.qml"
}
}
]
}
}
}
// overlay
FullscreenOverlay {
id: overlay
pageCount: messages.length
currentIndex: page.index
message: messages[currentIndex]
//
}
}

View file

@ -3,12 +3,13 @@
# Generated by: spectacle version 0.32 # Generated by: spectacle version 0.32
# #
Name: harbour-fernschreiber Name: org.ygriega.Fernschreiber
# >> macros # >> macros
# << macros
%define __provides_exclude_from ^%{_datadir}/.*$ %define __provides_exclude_from ^%{_datadir}/.*$
%define __requires_exclude ^lib(tdjson|ssl|crypto).*$ %define __requires_exclude ^lib(tdjson|ssl|crypto).*$
# << macros %define _binary_payload w6.xzdio
Summary: Fernschreiber is a Telegram client for Aurora OS Summary: Fernschreiber is a Telegram client for Aurora OS
Version: 0.17 Version: 0.17
@ -17,10 +18,10 @@ Group: Qt/Qt
License: LICENSE License: LICENSE
URL: http://werkwolf.eu/ URL: http://werkwolf.eu/
Source0: %{name}-%{version}.tar.bz2 Source0: %{name}-%{version}.tar.bz2
Source100: harbour-fernschreiber.yaml Source100: de.ygriega.Fernschreiber.yaml
Requires: sailfishsilica-qt5 >= 0.10.9 Requires: sailfishsilica-qt5 >= 0.10.9
#Requires: nemo-qml-plugin-contacts-qt5 #Requires: nemo-qml-plugin-contacts-qt5
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2 BuildRequires: pkgconfig(auroraapp) >= 1.0.2
BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml) BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick) BuildRequires: pkgconfig(Qt5Quick)
@ -38,7 +39,8 @@ Fernschreiber is a Telegram client for Sailfish OS
%prep %prep
%setup -q -n %{name}-%{version} #%setup -q -n %{name}-%{version}
%autosetup
# >> setup # >> setup
# << setup # << setup
@ -58,8 +60,8 @@ make %{?_smp_mflags}
rm -rf %{buildroot} rm -rf %{buildroot}
# >> install pre # >> install pre
# << install pre # << install pre
#%qmake5_install %qmake5_install
make INSTALL_ROOT=%{buildroot} install #make INSTALL_ROOT=%{buildroot} install
# >> install post # >> install post
# << install post # << install post
@ -70,7 +72,8 @@ desktop-file-install --delete-original \
%files %files
%defattr(-,root,root,-) %defattr(-,root,root,-)
%{_bindir} %{_bindir}/%{name}
%defattr(644,root,root,-)
%{_datadir}/%{name} %{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop %{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png %{_datadir}/icons/hicolor/*/apps/%{name}.png

View file

@ -42,9 +42,10 @@ namespace {
const QString KEY_FOCUS_TEXTAREA_ON_CHAT_OPEN("focusTextAreaOnChatOpen"); const QString KEY_FOCUS_TEXTAREA_ON_CHAT_OPEN("focusTextAreaOnChatOpen");
const QString KEY_SPONSORED_MESS("sponsoredMess"); const QString KEY_SPONSORED_MESS("sponsoredMess");
const QString KEY_HIGHLIGHT_UNREADCONVS("highlightUnreadConversations"); const QString KEY_HIGHLIGHT_UNREADCONVS("highlightUnreadConversations");
const QString KEY_SHOW_REACTION_BUTTON("showReactionButton");
} }
AppSettings::AppSettings(QObject *parent) : QObject(parent), settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/de.ygriega/fernschreiber/settings.conf", QSettings::NativeFormat) AppSettings::AppSettings(QObject *parent) : QObject(parent), settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/org.ygriega/Fernschreiber/settings.conf", QSettings::NativeFormat)
{ {
} }
@ -303,7 +304,7 @@ void AppSettings::setDelayMessageRead(bool enable)
bool AppSettings::highlightUnreadConversations() const bool AppSettings::highlightUnreadConversations() const
{ {
return settings.value(KEY_HIGHLIGHT_UNREADCONVS, true).toBool(); return settings.value(KEY_HIGHLIGHT_UNREADCONVS, false).toBool();
} }
void AppSettings::setHighlightUnreadConversations(bool enable) void AppSettings::setHighlightUnreadConversations(bool enable)
@ -329,6 +330,20 @@ void AppSettings::setFocusTextAreaOnChatOpen(bool focusTextAreaOnChatOpen)
} }
} }
bool AppSettings::showReactionButton() const
{
return settings.value(KEY_SHOW_REACTION_BUTTON, true).toBool();
}
void AppSettings::setShowReactionButton(bool enable)
{
if (showReactionButton() != enable) {
LOG(KEY_SHOW_REACTION_BUTTON << enable);
settings.setValue(KEY_SHOW_REACTION_BUTTON, enable);
emit showReactionButtonChanged();
}
}
AppSettings::SponsoredMess AppSettings::getSponsoredMess() const AppSettings::SponsoredMess AppSettings::getSponsoredMess() const
{ {
return (SponsoredMess) settings.value(KEY_SPONSORED_MESS, (int) return (SponsoredMess) settings.value(KEY_SPONSORED_MESS, (int)

View file

@ -43,8 +43,9 @@ class AppSettings : public QObject {
Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged) Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged)
Q_PROPERTY(bool delayMessageRead READ delayMessageRead WRITE setDelayMessageRead NOTIFY delayMessageReadChanged) Q_PROPERTY(bool delayMessageRead READ delayMessageRead WRITE setDelayMessageRead NOTIFY delayMessageReadChanged)
Q_PROPERTY(bool focusTextAreaOnChatOpen READ getFocusTextAreaOnChatOpen WRITE setFocusTextAreaOnChatOpen NOTIFY focusTextAreaOnChatOpenChanged) Q_PROPERTY(bool focusTextAreaOnChatOpen READ getFocusTextAreaOnChatOpen WRITE setFocusTextAreaOnChatOpen NOTIFY focusTextAreaOnChatOpenChanged)
Q_PROPERTY(SponsoredMess sponsoredMess READ getSponsoredMess WRITE setSponsoredMess NOTIFY sponsoredMessChanged)
Q_PROPERTY(bool highlightUnreadConversations READ highlightUnreadConversations WRITE setHighlightUnreadConversations NOTIFY highlightUnreadConversationsChanged) Q_PROPERTY(bool highlightUnreadConversations READ highlightUnreadConversations WRITE setHighlightUnreadConversations NOTIFY highlightUnreadConversationsChanged)
Q_PROPERTY(bool showReactionButton READ showReactionButton WRITE setShowReactionButton NOTIFY showReactionButtonChanged)
Q_PROPERTY(SponsoredMess sponsoredMess READ getSponsoredMess WRITE setSponsoredMess NOTIFY sponsoredMessChanged)
public: public:
enum SponsoredMess { enum SponsoredMess {
@ -121,12 +122,15 @@ public:
bool getFocusTextAreaOnChatOpen() const; bool getFocusTextAreaOnChatOpen() const;
void setFocusTextAreaOnChatOpen(bool focusTextAreaOnChatOpen); void setFocusTextAreaOnChatOpen(bool focusTextAreaOnChatOpen);
SponsoredMess getSponsoredMess() const;
void setSponsoredMess(SponsoredMess sponsoredMess);
bool highlightUnreadConversations() const; bool highlightUnreadConversations() const;
void setHighlightUnreadConversations(bool enable); void setHighlightUnreadConversations(bool enable);
bool showReactionButton() const;
void setShowReactionButton(bool enable);
SponsoredMess getSponsoredMess() const;
void setSponsoredMess(SponsoredMess sponsoredMess);
signals: signals:
void sendByEnterChanged(); void sendByEnterChanged();
void focusTextAreaAfterSendChanged(); void focusTextAreaAfterSendChanged();
@ -147,8 +151,9 @@ signals:
void onlineOnlyModeChanged(); void onlineOnlyModeChanged();
void delayMessageReadChanged(); void delayMessageReadChanged();
void focusTextAreaOnChatOpenChanged(); void focusTextAreaOnChatOpenChanged();
void sponsoredMessChanged();
void highlightUnreadConversationsChanged(); void highlightUnreadConversationsChanged();
void showReactionButtonChanged();
void sponsoredMessChanged();
private: private:
QSettings settings; QSettings settings;

153
src/boolfiltermodel.cpp Normal file
View file

@ -0,0 +1,153 @@
/*
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boolfiltermodel.h"
#define DEBUG_MODULE BoolFilterModel
#include "debuglog.h"
BoolFilterModel::BoolFilterModel(QObject *parent) : QSortFilterProxyModel(parent)
{
setDynamicSortFilter(true);
// setFilterCaseSensitivity(Qt::CaseInsensitive);
// setFilterFixedString(QString());
filterValue = true;
}
void BoolFilterModel::setSource(QObject *model)
{
setSourceModel(qobject_cast<QAbstractItemModel*>(model));
}
void BoolFilterModel::setSourceModel(QAbstractItemModel *model)
{
if (sourceModel() != model) {
LOG(model);
QSortFilterProxyModel::setSourceModel(model);
updateFilterRole();
emit sourceChanged();
}
}
QString BoolFilterModel::getFilterRoleName() const
{
return filterRoleName;
}
void BoolFilterModel::setFilterRoleName(QString role)
{
if (filterRoleName != role) {
filterRoleName = role;
LOG(role);
updateFilterRole();
emit filterRoleNameChanged();
}
}
bool BoolFilterModel::getFilterValue() const
{
return filterValue;
}
void BoolFilterModel::setFilterValue(bool value)
{
if(value != filterValue) {
filterValue = value;
invalidateFilter();
}
}
int BoolFilterModel::mapRowFromSource(int i, int fallbackDirection)
{
QModelIndex myIndex = mapFromSource(sourceModel()->index(i, 0));
LOG("mapping index" << i << "to source model:" << myIndex.row() << "valid?" << myIndex.isValid());
if(myIndex.isValid()) {
return myIndex.row();
}
if(fallbackDirection > 0) {
int max = sourceModel()->rowCount();
i += 1;
while (i < max) {
myIndex = mapFromSource(sourceModel()->index(i, 0));
LOG("fallback ++ " << i << "to source model:" << myIndex.row() << "valid?" << myIndex.isValid());
if(myIndex.isValid()) {
return myIndex.row();
}
i += 1;
}
} else if(fallbackDirection < 0) {
i -= 1;
while (i > -1) {
myIndex = mapFromSource(sourceModel()->index(i, 0));
LOG("fallback -- " << i << "to source model:" << myIndex.row() << "valid?" << myIndex.isValid());
if(myIndex.isValid()) {
return myIndex.row();
}
i -= 1;
}
}
return myIndex.row(); // may still be -1
}
int BoolFilterModel::mapRowToSource(int i)
{
QModelIndex sourceIndex = mapToSource(index(i, 0));
return sourceIndex.row();
}
bool BoolFilterModel::filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const
{
// sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(); //.toString().contains( /*string for column 0*/ ))
// LOG("Filter Role " << filterRole());
// QModelIndex index = this->sourceModel()->index(sourceRow,1,sourceParent);
// sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool();
// LOG("Filter index DATA"<< sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole())); //<< index << index.isValid());
// LOG("Filter parent " << sourceParent << sourceParent.isValid());
// LOG("Filter Model Value" << sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool());
// LOG("Filter Model filterValue" << filterValue);
// LOG("Filter Model result" << (sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool() == filterValue));
// LOG("Filter Model MESSAGE" << sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data());
return sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool() == filterValue;
}
int BoolFilterModel::findRole(QAbstractItemModel *model, QString role)
{
if (model && !role.isEmpty()) {
const QByteArray roleName(role.toUtf8());
const QHash<int,QByteArray> roleMap(model->roleNames());
const QList<int> roles(roleMap.keys());
const int n = roles.count();
for (int i = 0; i < n; i++) {
const QByteArray name(roleMap.value(roles.at(i)));
if (name == roleName) {
LOG(role << roles.at(i));
return roles.at(i);
}
}
LOG("Unknown role" << role);
}
return -1;
}
void BoolFilterModel::updateFilterRole()
{
const int role = findRole(sourceModel(), filterRoleName);
setFilterRole((role >= 0) ? role : Qt::DisplayRole);
}

63
src/boolfiltermodel.h Normal file
View file

@ -0,0 +1,63 @@
/*
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BOOLFILTERMODEL_H
#define BOOLFILTERMODEL_H
#include <QSortFilterProxyModel>
class BoolFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(QString filterRoleName READ getFilterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged)
Q_PROPERTY(bool filterValue READ getFilterValue WRITE setFilterValue NOTIFY filterValueChanged)
Q_PROPERTY(QObject* sourceModel READ sourceModel WRITE setSource NOTIFY sourceChanged)
public:
BoolFilterModel(QObject *parent = Q_NULLPTR);
void setSource(QObject* model);
void setSourceModel(QAbstractItemModel *model) Q_DECL_OVERRIDE;
QString getFilterRoleName() const;
void setFilterRoleName(QString role);
bool getFilterValue() const;
void setFilterValue(bool value);
Q_INVOKABLE int mapRowFromSource(int i, int fallbackDirection);
Q_INVOKABLE int mapRowToSource(int i);
signals:
void sourceChanged();
void filterRoleNameChanged();
void filterValueChanged();
private slots:
void updateFilterRole();
private:
static int findRole(QAbstractItemModel *model, QString role);
private:
QString filterRoleName;
bool filterValue;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
#endif // BOOLFILTERMODEL_H

View file

@ -91,6 +91,7 @@ public:
QVector<int> updateLastMessage(const QVariantMap &message); QVector<int> updateLastMessage(const QVariantMap &message);
QVector<int> updateGroup(const TDLibWrapper::Group *group); QVector<int> updateGroup(const TDLibWrapper::Group *group);
QVector<int> updateSecretChat(const QVariantMap &secretChatDetails); QVector<int> updateSecretChat(const QVariantMap &secretChatDetails);
ChatData* clone();
TDLibWrapper *tdLibWrapper; TDLibWrapper *tdLibWrapper;
public: public:
@ -384,6 +385,24 @@ QVector<int> ChatListModel::ChatData::updateSecretChat(const QVariantMap &secret
return changedRoles; return changedRoles;
} }
ChatListModel::ChatData* ChatListModel::ChatData::clone() {
QVariantMap clonedChatData;
QList<QString> keys = chatData.keys();
for(int i = 0; i < keys.count(); i++) {
clonedChatData.insert(keys[i], QVariant(chatData[keys[i]]));
}
ChatData* res = new ChatData(tdLibWrapper, clonedChatData);
res->chatId = chatId;
res->order = order;
res->groupId = groupId;
res->verified = verified;
res->chatType = chatType;
res->memberStatus = memberStatus;
res->secretChatState = secretChatState;
return res;
}
ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSettings) : showHiddenChats(false) ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSettings) : showHiddenChats(false)
{ {
this->tdLibWrapper = tdLibWrapper; this->tdLibWrapper = tdLibWrapper;
@ -426,6 +445,15 @@ ChatListModel::~ChatListModel()
qDeleteAll(hiddenChats.values()); qDeleteAll(hiddenChats.values());
} }
ChatListModel* ChatListModel::clone() {
ChatListModel* res = new ChatListModel(tdLibWrapper, appSettings);
res->relativeTimeRefreshTimer->stop();
for(int i = 0; i < chatList.count(); i++) {
res->chatList.append(chatList.at(i)->clone());
}
return res;
}
void ChatListModel::reset() void ChatListModel::reset()
{ {
chatList.clear(); chatList.clear();

View file

@ -75,6 +75,7 @@ public:
bool showAllChats() const; bool showAllChats() const;
void setShowAllChats(bool showAll); void setShowAllChats(bool showAll);
ChatListModel* clone();
private slots: private slots:
void handleChatDiscovered(const QString &chatId, const QVariantMap &chatInformation); void handleChatDiscovered(const QString &chatId, const QVariantMap &chatInformation);

View file

@ -30,6 +30,7 @@ namespace {
const QString ID("id"); const QString ID("id");
const QString CONTENT("content"); const QString CONTENT("content");
const QString CHAT_ID("chat_id"); const QString CHAT_ID("chat_id");
const QString DATE("date");
const QString PHOTO("photo"); const QString PHOTO("photo");
const QString SMALL("small"); const QString SMALL("small");
const QString UNREAD_COUNT("unread_count"); const QString UNREAD_COUNT("unread_count");
@ -48,6 +49,7 @@ namespace {
// "view_count": 47 // "view_count": 47
// } // }
const QString TYPE_MESSAGE_INTERACTION_INFO("messageInteractionInfo"); const QString TYPE_MESSAGE_INTERACTION_INFO("messageInteractionInfo");
const QString MEDIA_ALBUM_ID("media_album_id");
const QString INTERACTION_INFO("interaction_info"); const QString INTERACTION_INFO("interaction_info");
const QString VIEW_COUNT("view_count"); const QString VIEW_COUNT("view_count");
const QString REACTIONS("reactions"); const QString REACTIONS("reactions");
@ -63,7 +65,9 @@ public:
RoleMessageId, RoleMessageId,
RoleMessageContentType, RoleMessageContentType,
RoleMessageViewCount, RoleMessageViewCount,
RoleMessageReactions RoleMessageReactions,
RoleMessageAlbumEntryFilter,
RoleMessageAlbumMessageIds,
}; };
enum RoleFlag { enum RoleFlag {
@ -71,7 +75,9 @@ public:
RoleFlagMessageId = 0x02, RoleFlagMessageId = 0x02,
RoleFlagMessageContentType = 0x04, RoleFlagMessageContentType = 0x04,
RoleFlagMessageViewCount = 0x08, RoleFlagMessageViewCount = 0x08,
RoleFlagMessageReactions = 0x16 RoleFlagMessageReactions = 0x16,
RoleFlagMessageAlbumEntryFilter = 0x32,
RoleFlagMessageAlbumMessageIds = 0x64
}; };
MessageData(const QVariantMap &data, qlonglong msgid); MessageData(const QVariantMap &data, qlonglong msgid);
@ -86,12 +92,16 @@ public:
uint updateViewCount(const QVariantMap &interactionInfo); uint updateViewCount(const QVariantMap &interactionInfo);
uint updateInteractionInfo(const QVariantMap &interactionInfo); uint updateInteractionInfo(const QVariantMap &interactionInfo);
uint updateReactions(const QVariantMap &interactionInfo); uint updateReactions(const QVariantMap &interactionInfo);
uint updateAlbumEntryFilter(const bool isAlbumChild);
uint updateAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds);
QVector<int> diff(const MessageData *message) const; QVector<int> diff(const MessageData *message) const;
QVector<int> setMessageData(const QVariantMap &data); QVector<int> setMessageData(const QVariantMap &data);
QVector<int> setContent(const QVariantMap &content); QVector<int> setContent(const QVariantMap &content);
QVector<int> setReplyMarkup(const QVariantMap &replyMarkup); QVector<int> setReplyMarkup(const QVariantMap &replyMarkup);
QVector<int> setInteractionInfo(const QVariantMap &interactionInfo); QVector<int> setInteractionInfo(const QVariantMap &interactionInfo);
QVector<int> setAlbumEntryFilter(bool isAlbumChild);
QVector<int> setAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds);
int senderUserId() const; int senderUserId() const;
qlonglong senderChatId() const; qlonglong senderChatId() const;
@ -104,6 +114,8 @@ public:
QString messageContentType; QString messageContentType;
int viewCount; int viewCount;
QVariantList reactions; QVariantList reactions;
bool albumEntryFilter;
QVariantList albumMessageIds;
}; };
ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) : ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) :
@ -112,7 +124,9 @@ ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) :
messageType(data.value(_TYPE).toString()), messageType(data.value(_TYPE).toString()),
messageContentType(data.value(CONTENT).toMap().value(_TYPE).toString()), messageContentType(data.value(CONTENT).toMap().value(_TYPE).toString()),
viewCount(data.value(INTERACTION_INFO).toMap().value(VIEW_COUNT).toInt()), viewCount(data.value(INTERACTION_INFO).toMap().value(VIEW_COUNT).toInt()),
reactions(data.value(INTERACTION_INFO).toMap().value(REACTIONS).toList()) reactions(data.value(INTERACTION_INFO).toMap().value(REACTIONS).toList()),
albumEntryFilter(false),
albumMessageIds(QVariantList())
{ {
} }
@ -134,6 +148,12 @@ QVector<int> ChatModel::MessageData::flagsToRoles(uint flags)
if (flags & RoleFlagMessageReactions) { if (flags & RoleFlagMessageReactions) {
roles.append(RoleMessageReactions); roles.append(RoleMessageReactions);
} }
if (flags & RoleFlagMessageAlbumEntryFilter) {
roles.append(RoleMessageAlbumEntryFilter);
}
if (flags & RoleFlagMessageAlbumMessageIds) {
roles.append(RoleMessageAlbumMessageIds);
}
return roles; return roles;
} }
@ -169,6 +189,12 @@ QVector<int> ChatModel::MessageData::diff(const MessageData *message) const
if (message->reactions != reactions) { if (message->reactions != reactions) {
roles.append(RoleMessageReactions); roles.append(RoleMessageReactions);
} }
if (message->albumEntryFilter != albumEntryFilter) {
roles.append(RoleMessageAlbumEntryFilter);
}
if (message->albumMessageIds != albumMessageIds) {
roles.append(RoleMessageAlbumMessageIds);
}
} }
return roles; return roles;
} }
@ -237,6 +263,37 @@ uint ChatModel::MessageData::updateReactions(const QVariantMap &interactionInfo)
return (reactions == oldReactions) ? 0 : RoleFlagMessageReactions; return (reactions == oldReactions) ? 0 : RoleFlagMessageReactions;
} }
uint ChatModel::MessageData::updateAlbumEntryFilter(const bool isAlbumChild)
{
LOG("Updating album filter... for id " << messageId << " value:" << isAlbumChild << "previously" << albumEntryFilter);
const bool oldAlbumFiltered = albumEntryFilter;
albumEntryFilter = isAlbumChild;
return (isAlbumChild == oldAlbumFiltered) ? 0 : RoleFlagMessageAlbumEntryFilter;
}
QVector<int> ChatModel::MessageData::setAlbumEntryFilter(bool isAlbumChild)
{
LOG("setAlbumEntryFilter");
return flagsToRoles(updateAlbumEntryFilter(isAlbumChild));
}
uint ChatModel::MessageData::updateAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds)
{
LOG("Updating albumMessageIds... id" << messageId);
LOG(" Updating albumMessageIds..." << newAlbumMessageIds << "previously" << albumMessageIds << "same?" << (newAlbumMessageIds == albumMessageIds));
const QVariantList oldAlbumMessageIds = albumMessageIds;
albumMessageIds = newAlbumMessageIds;
LOG(" Updating albumMessageIds... same again?" << (newAlbumMessageIds == oldAlbumMessageIds));
return (newAlbumMessageIds == oldAlbumMessageIds) ? 0 : RoleFlagMessageAlbumMessageIds;
}
QVector<int> ChatModel::MessageData::setAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds)
{
return flagsToRoles(updateAlbumEntryMessageIds(newAlbumMessageIds));
}
QVector<int> ChatModel::MessageData::setInteractionInfo(const QVariantMap &info) QVector<int> ChatModel::MessageData::setInteractionInfo(const QVariantMap &info)
{ {
return flagsToRoles(updateInteractionInfo(info)); return flagsToRoles(updateInteractionInfo(info));
@ -295,6 +352,8 @@ QHash<int,QByteArray> ChatModel::roleNames() const
roles.insert(MessageData::RoleMessageContentType, "content_type"); roles.insert(MessageData::RoleMessageContentType, "content_type");
roles.insert(MessageData::RoleMessageViewCount, "view_count"); roles.insert(MessageData::RoleMessageViewCount, "view_count");
roles.insert(MessageData::RoleMessageReactions, "reactions"); roles.insert(MessageData::RoleMessageReactions, "reactions");
roles.insert(MessageData::RoleMessageAlbumEntryFilter, "album_entry_filter");
roles.insert(MessageData::RoleMessageAlbumMessageIds, "album_message_ids");
return roles; return roles;
} }
@ -314,6 +373,8 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const
case MessageData::RoleMessageContentType: return message->messageContentType; case MessageData::RoleMessageContentType: return message->messageContentType;
case MessageData::RoleMessageViewCount: return message->viewCount; case MessageData::RoleMessageViewCount: return message->viewCount;
case MessageData::RoleMessageReactions: return message->reactions; case MessageData::RoleMessageReactions: return message->reactions;
case MessageData::RoleMessageAlbumEntryFilter: return message->albumEntryFilter;
case MessageData::RoleMessageAlbumMessageIds: return message->albumMessageIds;
} }
} }
return QVariant(); return QVariant();
@ -331,6 +392,7 @@ void ChatModel::clear(bool contentOnly)
qDeleteAll(messages); qDeleteAll(messages);
messages.clear(); messages.clear();
messageIndexMap.clear(); messageIndexMap.clear();
albumMessageMap.clear();
endResetModel(); endResetModel();
} }
@ -356,6 +418,7 @@ void ChatModel::initialize(const QVariantMap &chatInformation)
this->chatId = chatId; this->chatId = chatId;
this->messages.clear(); this->messages.clear();
this->messageIndexMap.clear(); this->messageIndexMap.clear();
this->albumMessageMap.clear();
this->searchQuery.clear(); this->searchQuery.clear();
endResetModel(); endResetModel();
emit chatIdChanged(); emit chatIdChanged();
@ -409,17 +472,58 @@ QVariantMap ChatModel::getMessage(int index)
return QVariantMap(); return QVariantMap();
} }
int ChatModel::getMessageIndex(qlonglong messageId) int ChatModel::getDisplayedMessageIndex(qlonglong messageId)
{ {
if (messages.size() == 0) { if (messages.size() == 0) {
return -1; return -1;
} }
if (messageIndexMap.contains(messageId)) { if (messageIndexMap.contains(messageId)) {
return messageIndexMap.value(messageId); int rawIndex = messageIndexMap.value(messageId);
// We need to substract number of albums which are shown before this item, because that's the index it's displayed on screen at.
int realIndex = rawIndex;
for(int i = 0; i < rawIndex; i++) {
MessageData *message = messages.at(i);
if(message->albumMessageIds.count() > 0) {
realIndex -= (message->albumMessageIds.count() - 1);
}
}
if(realIndex < -1)
return -1;
return realIndex;
} }
return -1; return -1;
} }
QVariantList ChatModel::getMessageIdsForAlbum(qlonglong albumId)
{
QVariantList foundMessages;
if(albumMessageMap.contains(albumId)) { // there should be only one in here
QHash< qlonglong, QVariantList >::iterator i = albumMessageMap.find(albumId);
return i.value();
}
return foundMessages;
}
QVariantList ChatModel::getMessagesForAlbum(qlonglong albumId, int startAt)
{
LOG("getMessagesForAlbumId" << albumId);
QVariantList messageIds = getMessageIdsForAlbum(albumId);
int count = messageIds.size();
if ( count == 0) {
return messageIds;
}
QVariantList foundMessages;
for (int messageNum = startAt; messageNum < count; ++messageNum) {
const int position = messageIndexMap.value(messageIds.at(messageNum).toLongLong(), -1);
if(position >= 0 && position < messages.size()) {
foundMessages.append(messages.at(position)->messageData);
} else {
LOG("Not found in messages: #"<< messageNum);
}
}
return foundMessages;
}
int ChatModel::getLastReadMessageIndex() int ChatModel::getLastReadMessageIndex()
{ {
LOG("Obtaining last read message index"); LOG("Obtaining last read message index");
@ -477,7 +581,8 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCo
const qlonglong messageId = messageData.value(ID).toLongLong(); const qlonglong messageId = messageData.value(ID).toLongLong();
if (messageId && messageData.value(CHAT_ID).toLongLong() == chatId && !messageIndexMap.contains(messageId)) { if (messageId && messageData.value(CHAT_ID).toLongLong() == chatId && !messageIndexMap.contains(messageId)) {
LOG("New message will be added:" << messageId); LOG("New message will be added:" << messageId);
messagesToBeAdded.append(new MessageData(messageData, messageId)); MessageData* message = new MessageData(messageData, messageId);
messagesToBeAdded.append(message);
} }
} }
@ -485,6 +590,7 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCo
if (!messagesToBeAdded.isEmpty()) { if (!messagesToBeAdded.isEmpty()) {
insertMessages(messagesToBeAdded); insertMessages(messagesToBeAdded);
setMessagesAlbum(messagesToBeAdded);
} }
// First call only returns a few messages, we need to get a little more than that... // First call only returns a few messages, we need to get a little more than that...
@ -540,6 +646,7 @@ void ChatModel::handleNewMessageReceived(qlonglong chatId, const QVariantMap &me
QList<MessageData*> messagesToBeAdded; QList<MessageData*> messagesToBeAdded;
messagesToBeAdded.append(new MessageData(message, messageId)); messagesToBeAdded.append(new MessageData(message, messageId));
insertMessages(messagesToBeAdded); insertMessages(messagesToBeAdded);
setMessagesAlbum(messagesToBeAdded);
emit newMessageReceived(message); emit newMessageReceived(message);
} else { } else {
LOG("New message in this chat, but not relevant as less recent messages need to be loaded first!"); LOG("New message in this chat, but not relevant as less recent messages need to be loaded first!");
@ -591,6 +698,7 @@ void ChatModel::handleMessageSendSucceeded(qlonglong messageId, qlonglong oldMes
messages.replace(pos, newMessage); messages.replace(pos, newMessage);
messageIndexMap.remove(oldMessageId); messageIndexMap.remove(oldMessageId);
messageIndexMap.insert(messageId, pos); messageIndexMap.insert(messageId, pos);
// TODO when we support sending album messages, handle ID change in albumMessageMap
const QVector<int> changedRoles(newMessage->diff(oldMessage)); const QVector<int> changedRoles(newMessage->diff(oldMessage));
delete oldMessage; delete oldMessage;
LOG("Message was replaced at index" << pos); LOG("Message was replaced at index" << pos);
@ -635,7 +743,8 @@ void ChatModel::handleMessageContentUpdated(qlonglong chatId, qlonglong messageI
LOG("We know the message that was updated" << messageId); LOG("We know the message that was updated" << messageId);
const int pos = messageIndexMap.value(messageId, -1); const int pos = messageIndexMap.value(messageId, -1);
if (pos >= 0) { if (pos >= 0) {
const QVector<int> changedRoles(messages.at(pos)->setContent(newContent)); MessageData* messageData = messages.at(pos);
const QVector<int> changedRoles(messageData->setContent(newContent));
LOG("Message was updated at index" << pos); LOG("Message was updated at index" << pos);
const QModelIndex messageIndex(index(pos)); const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex, changedRoles); emit dataChanged(messageIndex, messageIndex, changedRoles);
@ -664,7 +773,8 @@ void ChatModel::handleMessageEditedUpdated(qlonglong chatId, qlonglong messageId
LOG("We know the message that was updated" << messageId); LOG("We know the message that was updated" << messageId);
const int pos = messageIndexMap.value(messageId, -1); const int pos = messageIndexMap.value(messageId, -1);
if (pos >= 0) { if (pos >= 0) {
const QVector<int> changedRoles(messages.at(pos)->setReplyMarkup(replyMarkup)); MessageData* messageData = messages.at(pos);
const QVector<int> changedRoles(messageData->setReplyMarkup(replyMarkup));
LOG("Message was edited at index" << pos); LOG("Message was edited at index" << pos);
const QModelIndex messageIndex(index(pos)); const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex, changedRoles); emit dataChanged(messageIndex, messageIndex, changedRoles);
@ -709,18 +819,31 @@ void ChatModel::handleMessagesDeleted(qlonglong chatId, const QList<qlonglong> &
} }
} }
void ChatModel::removeRange(int firstDeleted, int lastDeleted) void ChatModel::removeRange(int firstDeleted, int lastDeleted)
{ {
if (firstDeleted >= 0 && firstDeleted <= lastDeleted) { if (firstDeleted >= 0 && firstDeleted <= lastDeleted) {
LOG("Removing range" << firstDeleted << "..." << lastDeleted << "| current messages size" << messages.size()); LOG("Removing range" << firstDeleted << "..." << lastDeleted << "| current messages size" << messages.size());
beginRemoveRows(QModelIndex(), firstDeleted, lastDeleted); beginRemoveRows(QModelIndex(), firstDeleted, lastDeleted);
QList<qlonglong> rescanAlbumIds;
for (int i = firstDeleted; i <= lastDeleted; i++) { for (int i = firstDeleted; i <= lastDeleted; i++) {
MessageData *message = messages.at(i); MessageData *message = messages.at(i);
messageIndexMap.remove(message->messageId); messageIndexMap.remove(message->messageId);
qlonglong albumId = message->messageData.value(MEDIA_ALBUM_ID).toLongLong();
if(albumId != 0 && albumMessageMap.contains(albumId)) {
rescanAlbumIds.append(albumId);
}
delete message; delete message;
} }
messages.erase(messages.begin() + firstDeleted, messages.begin() + (lastDeleted + 1)); messages.erase(messages.begin() + firstDeleted, messages.begin() + (lastDeleted + 1));
// rebuild following messageIndexMap
for(int i = firstDeleted; i < messages.size(); ++i) {
messageIndexMap.insert(messages.at(i)->messageId, i);
}
endRemoveRows(); endRemoveRows();
updateAlbumMessages(rescanAlbumIds, true);
} }
} }
@ -757,7 +880,7 @@ void ChatModel::appendMessages(const QList<MessageData*> newMessages)
beginInsertRows(QModelIndex(), oldSize, oldSize + count - 1); beginInsertRows(QModelIndex(), oldSize, oldSize + count - 1);
messages.append(newMessages); messages.append(newMessages);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
// Appens new indeces to the map // Append new indices to the map
messageIndexMap.insert(newMessages.at(i)->messageId, oldSize + i); messageIndexMap.insert(newMessages.at(i)->messageId, oldSize + i);
} }
endInsertRows(); endInsertRows();
@ -785,6 +908,90 @@ void ChatModel::prependMessages(const QList<MessageData*> newMessages)
endInsertRows(); endInsertRows();
} }
void ChatModel::updateAlbumMessages(qlonglong albumId, bool checkDeleted)
{
if(albumMessageMap.contains(albumId)) {
const QVariantList empty;
QHash< qlonglong, QVariantList >::iterator album = albumMessageMap.find(albumId);
QVariantList messageIds = album.value();
std::sort(messageIds.begin(), messageIds.end());
int count;
// first: clear deleted messageIds:
if(checkDeleted) {
QVariantList::iterator it = messageIds.begin();
while (it != messageIds.end()) {
if (!messageIndexMap.contains(it->toLongLong())) {
it = messageIds.erase(it);
}
else {
++it;
}
}
}
// second: remaining ones still exist
count = messageIds.size();
if(count == 0) {
albumMessageMap.remove(albumId);
} else {
for (int i = 0; i < count; i++) {
const int position = messageIndexMap.value(messageIds.at(i).toLongLong(), -1);
if(position > -1) {
// set list for first entry, empty for all others
QVector<int> changedRolesFilter;
QVector<int> changedRolesIds;
QModelIndex messageIndex(index(position));
if(i == 0) {
changedRolesFilter = messages.at(position)->setAlbumEntryFilter(false);
changedRolesIds = messages.at(position)->setAlbumEntryMessageIds(messageIds);
} else {
changedRolesFilter = messages.at(position)->setAlbumEntryFilter(true);
changedRolesIds = messages.at(position)->setAlbumEntryMessageIds(empty);
}
emit dataChanged(messageIndex, messageIndex, changedRolesIds);
emit dataChanged(messageIndex, messageIndex, changedRolesFilter);
}
}
}
albumMessageMap.insert(albumId, messageIds);
}
}
void ChatModel::updateAlbumMessages(QList<qlonglong> albumIds, bool checkDeleted)
{
const int albumsCount = albumIds.size();
for (int i = 0; i < albumsCount; i++) {
updateAlbumMessages(albumIds.at(i), checkDeleted);
}
}
void ChatModel::setMessagesAlbum(const QList<MessageData *> newMessages)
{
const int count = newMessages.size();
for (int i = 0; i < count; i++) {
setMessagesAlbum(newMessages.at(i));
}
}
void ChatModel::setMessagesAlbum(MessageData *message)
{
qlonglong albumId = message->messageData.value(MEDIA_ALBUM_ID).toLongLong();
if (albumId > 0 && (message->messageContentType != "messagePhoto" || message->messageContentType != "messageVideo")) {
qlonglong messageId = message->messageId;
if(albumMessageMap.contains(albumId)) {
// find message id within album:
QHash< qlonglong, QVariantList >::iterator i = albumMessageMap.find(albumId);
if(!i.value().contains(messageId)) {
i.value().append(messageId);
}
} else { // new album id
albumMessageMap.insert(albumId, QVariantList() << messageId);
}
updateAlbumMessages(albumId, false);
}
}
QVariantMap ChatModel::enhanceMessage(const QVariantMap &message) QVariantMap ChatModel::enhanceMessage(const QVariantMap &message)
{ {
QVariantMap enhancedMessage = message; QVariantMap enhancedMessage = message;

View file

@ -44,10 +44,12 @@ public:
Q_INVOKABLE void triggerLoadMoreFuture(); Q_INVOKABLE void triggerLoadMoreFuture();
Q_INVOKABLE QVariantMap getChatInformation(); Q_INVOKABLE QVariantMap getChatInformation();
Q_INVOKABLE QVariantMap getMessage(int index); Q_INVOKABLE QVariantMap getMessage(int index);
Q_INVOKABLE QVariantList getMessageIdsForAlbum(qlonglong albumId);
Q_INVOKABLE QVariantList getMessagesForAlbum(qlonglong albumId, int startAt);
Q_INVOKABLE int getLastReadMessageIndex(); Q_INVOKABLE int getLastReadMessageIndex();
Q_INVOKABLE void setSearchQuery(const QString newSearchQuery); Q_INVOKABLE void setSearchQuery(const QString newSearchQuery);
Q_INVOKABLE int getMessageIndex(qlonglong messageId); Q_INVOKABLE int getDisplayedMessageIndex(qlonglong messageId);
QVariantMap smallPhoto() const; QVariantMap smallPhoto() const;
qlonglong getChatId() const; qlonglong getChatId() const;
@ -85,6 +87,10 @@ private:
void insertMessages(const QList<MessageData*> newMessages); void insertMessages(const QList<MessageData*> newMessages);
void appendMessages(const QList<MessageData*> newMessages); void appendMessages(const QList<MessageData*> newMessages);
void prependMessages(const QList<MessageData*> newMessages); void prependMessages(const QList<MessageData*> newMessages);
void updateAlbumMessages(qlonglong albumId, bool checkDeleted);
void updateAlbumMessages(QList<qlonglong> albumIds, bool checkDeleted);
void setMessagesAlbum(const QList<MessageData*> newMessages);
void setMessagesAlbum(MessageData *message);
QVariantMap enhanceMessage(const QVariantMap &message); QVariantMap enhanceMessage(const QVariantMap &message);
int calculateLastKnownMessageId(); int calculateLastKnownMessageId();
int calculateLastReadSentMessageId(); int calculateLastReadSentMessageId();
@ -95,6 +101,7 @@ private:
TDLibWrapper *tdLibWrapper; TDLibWrapper *tdLibWrapper;
QList<MessageData*> messages; QList<MessageData*> messages;
QHash<qlonglong,int> messageIndexMap; QHash<qlonglong,int> messageIndexMap;
QHash<qlonglong, QVariantList> albumMessageMap;
QVariantMap chatInformation; QVariantMap chatInformation;
qlonglong chatId; qlonglong chatId;
bool inReload; bool inReload;

View file

@ -33,7 +33,9 @@ ChatPermissionFilterModel::ChatPermissionFilterModel(QObject *parent) : QSortFil
void ChatPermissionFilterModel::setSource(QObject *model) void ChatPermissionFilterModel::setSource(QObject *model)
{ {
setSourceModel(qobject_cast<ChatListModel*>(model)); ChatListModel* chatListModel = qobject_cast<ChatListModel*>(model);
ChatListModel* chatListModelClone = chatListModel->clone();
setSourceModel(chatListModelClone);
} }
void ChatPermissionFilterModel::setSourceModel(QAbstractItemModel *model) void ChatPermissionFilterModel::setSourceModel(QAbstractItemModel *model)
@ -45,6 +47,13 @@ void ChatPermissionFilterModel::setSourceModel(QAbstractItemModel *model)
} }
} }
ChatPermissionFilterModel::~ChatPermissionFilterModel() {
QAbstractItemModel* _sourceModel = sourceModel();
if(_sourceModel != nullptr) {
delete _sourceModel;
}
}
TDLibWrapper *ChatPermissionFilterModel::getTDLibWrapper() const TDLibWrapper *ChatPermissionFilterModel::getTDLibWrapper() const
{ {
return tdLibWrapper; return tdLibWrapper;

View file

@ -31,7 +31,7 @@ class ChatPermissionFilterModel : public QSortFilterProxyModel
public: public:
ChatPermissionFilterModel(QObject *parent = Q_NULLPTR); ChatPermissionFilterModel(QObject *parent = Q_NULLPTR);
~ChatPermissionFilterModel() override;
TDLibWrapper *getTDLibWrapper() const; TDLibWrapper *getTDLibWrapper() const;
void setTDLibWrapper(QObject* obj); void setTDLibWrapper(QObject* obj);

View file

@ -26,7 +26,7 @@ class DBusAdaptor : public QDBusAbstractAdaptor
{ {
Q_OBJECT Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "de.ygriega.fernschreiber") Q_CLASSINFO("D-Bus Interface", "org.ygriega.Fernschreiber")
public: public:
DBusAdaptor(QObject *parent); DBusAdaptor(QObject *parent);

View file

@ -25,8 +25,8 @@
#include "dbusadaptor.h" #include "dbusadaptor.h"
const QString INTERFACE_NAME = "de.ygriega.fernschreiber"; const QString INTERFACE_NAME = "org.ygriega.Fernschreiber";
const QString PATH_NAME = "/de/ygriega/fernschreiber"; const QString PATH_NAME = "/org/ygriega/Fernschreiber";
class DBusInterface : public QObject class DBusInterface : public QObject
{ {

View file

@ -21,7 +21,7 @@
#include <QtQuick> #include <QtQuick>
#endif #endif
#include <sailfishapp.h> #include <auroraapp.h>
#include <QScopedPointer> #include <QScopedPointer>
#include <QQuickView> #include <QQuickView>
#include <QtQml> #include <QtQml>
@ -51,6 +51,7 @@
#include "processlauncher.h" #include "processlauncher.h"
#include "stickermanager.h" #include "stickermanager.h"
#include "textfiltermodel.h" #include "textfiltermodel.h"
#include "boolfiltermodel.h"
#include "tgsplugin.h" #include "tgsplugin.h"
#include "fernschreiberutils.h" #include "fernschreiberutils.h"
#include "knownusersmodel.h" #include "knownusersmodel.h"
@ -72,7 +73,7 @@ void migrateSettings() {
int sailfishOSMinorVersion = sailfishOSVersion.value(1).toInt(); int sailfishOSMinorVersion = sailfishOSVersion.value(1).toInt();
if ((sailfishOSMajorVersion == 4 && sailfishOSMinorVersion >= 4) || sailfishOSMajorVersion > 4) { if ((sailfishOSMajorVersion == 4 && sailfishOSMinorVersion >= 4) || sailfishOSMajorVersion > 4) {
LOG("Checking if we need to migrate settings..."); LOG("Checking if we need to migrate settings...");
QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/de.ygriega/fernschreiber/settings.conf", QSettings::NativeFormat); QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/org.ygriega/Fernschreiber/settings.conf", QSettings::NativeFormat);
if (settings.contains("migrated")) { if (settings.contains("migrated")) {
return; return;
} }
@ -119,8 +120,8 @@ int main(int argc, char *argv[])
{ {
QLoggingCategory::setFilterRules(DEFAULT_LOG_FILTER); QLoggingCategory::setFilterRules(DEFAULT_LOG_FILTER);
QScopedPointer<QGuiApplication> app(SailfishApp::application(argc, argv)); QScopedPointer<QGuiApplication> app(Aurora::Application::application(argc, argv));
QScopedPointer<QQuickView> view(SailfishApp::createView()); QScopedPointer<QQuickView> view(Aurora::Application::createView());
QQmlContext *context = view.data()->rootContext(); QQmlContext *context = view.data()->rootContext();
@ -130,6 +131,7 @@ int main(int argc, char *argv[])
qmlRegisterType<TDLibFile>(uri, 1, 0, "TDLibFile"); qmlRegisterType<TDLibFile>(uri, 1, 0, "TDLibFile");
qmlRegisterType<NamedAction>(uri, 1, 0, "NamedAction"); qmlRegisterType<NamedAction>(uri, 1, 0, "NamedAction");
qmlRegisterType<TextFilterModel>(uri, 1, 0, "TextFilterModel"); qmlRegisterType<TextFilterModel>(uri, 1, 0, "TextFilterModel");
qmlRegisterType<BoolFilterModel>(uri, 1, 0, "BoolFilterModel");
qmlRegisterType<ChatPermissionFilterModel>(uri, 1, 0, "ChatPermissionFilterModel"); qmlRegisterType<ChatPermissionFilterModel>(uri, 1, 0, "ChatPermissionFilterModel");
qmlRegisterSingletonType<DebugLogJS>(uri, 1, 0, "DebugLog", DebugLogJS::createSingleton); qmlRegisterSingletonType<DebugLogJS>(uri, 1, 0, "DebugLog", DebugLogJS::createSingleton);
@ -180,7 +182,7 @@ int main(int argc, char *argv[])
contactsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); contactsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
context->setContextProperty("contactsProxyModel", &contactsProxyModel); context->setContextProperty("contactsProxyModel", &contactsProxyModel);
view->setSource(SailfishApp::pathTo("qml/harbour-fernschreiber.qml")); view->setSource(Aurora::Application::pathTo("qml/harbour-fernschreiber.qml"));
view->show(); view->show();
return app->exec(); return app->exec();
} }

View file

@ -20,7 +20,7 @@
#include "notificationmanager.h" #include "notificationmanager.h"
#include "fernschreiberutils.h" #include "fernschreiberutils.h"
#include "chatmodel.h" #include "chatmodel.h"
#include <sailfishapp.h> #include <auroraapp.h>
#include <QListIterator> #include <QListIterator>
#include <QUrl> #include <QUrl>
#include <QDateTime> #include <QDateTime>
@ -126,7 +126,7 @@ NotificationManager::NotificationGroup::~NotificationGroup()
} }
NotificationManager::NotificationManager(TDLibWrapper *tdLibWrapper, AppSettings *appSettings, MceInterface *mceInterface, ChatModel *chatModel) : NotificationManager::NotificationManager(TDLibWrapper *tdLibWrapper, AppSettings *appSettings, MceInterface *mceInterface, ChatModel *chatModel) :
appIconFile(SailfishApp::pathTo("images/fernschreiber-notification.png").toLocalFile()) appIconFile(Aurora::Application::pathTo("images/fernschreiber-notification.png").toLocalFile())
{ {
LOG("Initializing..."); LOG("Initializing...");
this->tdLibWrapper = tdLibWrapper; this->tdLibWrapper = tdLibWrapper;
@ -335,7 +335,7 @@ void NotificationManager::publishNotification(const NotificationGroup *notificat
remoteActionArguments.append(QString::number(notificationGroup->chatId)); remoteActionArguments.append(QString::number(notificationGroup->chatId));
remoteActionArguments.append(messageMap.value(ID).toString()); remoteActionArguments.append(messageMap.value(ID).toString());
nemoNotification->setRemoteAction(Notification::remoteAction("default", "openMessage", nemoNotification->setRemoteAction(Notification::remoteAction("default", "openMessage",
"de.ygriega.fernschreiber", "/de/ygriega/fernschreiber", "de.ygriega.fernschreiber", "org.ygriega.Fernschreiber", "/org/ygriega/Fernschreiber", "org.ygriega.Fernschreiber",
"openMessage", remoteActionArguments)); "openMessage", remoteActionArguments));
} }

View file

@ -81,6 +81,8 @@ namespace {
const QString TYPE_ANIMATED_EMOJI("animatedEmoji"); const QString TYPE_ANIMATED_EMOJI("animatedEmoji");
const QString TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE("inputMessageReplyToMessage"); const QString TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE("inputMessageReplyToMessage");
const QString TYPE_DRAFT_MESSAGE("draftMessage"); const QString TYPE_DRAFT_MESSAGE("draftMessage");
const double POWERSAVING_TDLIB_REQUEST_INTERVAL = 100;
} }
static QString getChatPositionOrder(const QVariantMap &position) static QString getChatPositionOrder(const QVariantMap &position)
@ -191,9 +193,15 @@ void TDLibReceiver::setActive(bool active)
} else { } else {
LOG("Deactivating receiver loop, this may take a while..."); LOG("Deactivating receiver loop, this may take a while...");
} }
this->powerSavingMode = false;
this->isActive = active; this->isActive = active;
} }
void TDLibReceiver::setPowerSavingMode(bool powerSavingMode)
{
this->powerSavingMode = powerSavingMode;
}
void TDLibReceiver::receiverLoop() void TDLibReceiver::receiverLoop()
{ {
LOG("Starting receiver loop"); LOG("Starting receiver loop");
@ -205,6 +213,9 @@ void TDLibReceiver::receiverLoop()
VERBOSE("Raw result:" << receivedJsonDocument.toJson(QJsonDocument::Indented).constData()); VERBOSE("Raw result:" << receivedJsonDocument.toJson(QJsonDocument::Indented).constData());
processReceivedDocument(receivedJsonDocument); processReceivedDocument(receivedJsonDocument);
} }
if(this->powerSavingMode) {
msleep(POWERSAVING_TDLIB_REQUEST_INTERVAL);
}
} }
LOG("Stopping receiver loop"); LOG("Stopping receiver loop");
} }

View file

@ -35,6 +35,7 @@ class TDLibReceiver : public QThread
public: public:
explicit TDLibReceiver(void *tdLibClient, QObject *parent = nullptr); explicit TDLibReceiver(void *tdLibClient, QObject *parent = nullptr);
void setActive(bool active); void setActive(bool active);
void setPowerSavingMode(bool active);
signals: signals:
void versionDetected(const QString &version); void versionDetected(const QString &version);
@ -115,6 +116,7 @@ private:
QHash<QString, Handler> handlers; QHash<QString, Handler> handlers;
void *tdLibClient; void *tdLibClient;
bool isActive; bool isActive;
bool powerSavingMode;
private: private:
static const QVariantList cleanupList(const QVariantList& list, bool *updated = Q_NULLPTR); static const QVariantList cleanupList(const QVariantList& list, bool *updated = Q_NULLPTR);

View file

@ -22,6 +22,7 @@
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QGuiApplication>
#include <QLocale> #include <QLocale>
#include <QProcess> #include <QProcess>
#include <QSysInfo> #include <QSysInfo>
@ -102,7 +103,7 @@ TDLibWrapper::TDLibWrapper(AppSettings *settings, MceInterface *mce, QObject *pa
connect(this->appSettings, SIGNAL(useOpenWithChanged()), this, SLOT(handleOpenWithChanged())); connect(this->appSettings, SIGNAL(useOpenWithChanged()), this, SLOT(handleOpenWithChanged()));
connect(this->appSettings, SIGNAL(storageOptimizerChanged()), this, SLOT(handleStorageOptimizerChanged())); connect(this->appSettings, SIGNAL(storageOptimizerChanged()), this, SLOT(handleStorageOptimizerChanged()));
connect(qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(handleApplicationStateChanged(Qt::ApplicationState)));
connect(networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), this, SLOT(handleNetworkConfigurationChanged(QNetworkConfiguration))); connect(networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), this, SLOT(handleNetworkConfigurationChanged(QNetworkConfiguration)));
this->setLogVerbosityLevel(); this->setLogVerbosityLevel();
@ -1464,9 +1465,8 @@ void TDLibWrapper::getPageSource(const QString &address)
connect(reply, SIGNAL(finished()), this, SLOT(handleGetPageSourceFinished())); connect(reply, SIGNAL(finished()), this, SLOT(handleGetPageSourceFinished()));
} }
void TDLibWrapper::setMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction) void TDLibWrapper::addMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction)
{ {
LOG("Set message reaction" << chatId << messageId << reaction);
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(CHAT_ID, chatId); requestObject.insert(CHAT_ID, chatId);
requestObject.insert(MESSAGE_ID, messageId); requestObject.insert(MESSAGE_ID, messageId);
@ -1481,9 +1481,35 @@ void TDLibWrapper::setMessageReaction(qlonglong chatId, qlonglong messageId, con
reactionType.insert(EMOJI, reaction); reactionType.insert(EMOJI, reaction);
requestObject.insert(REACTION_TYPE, reactionType); requestObject.insert(REACTION_TYPE, reactionType);
requestObject.insert(_TYPE, "addMessageReaction"); requestObject.insert(_TYPE, "addMessageReaction");
LOG("Add message reaction" << chatId << messageId << reaction);
} else { } else {
requestObject.insert("reaction", reaction); requestObject.insert("reaction", reaction);
requestObject.insert(_TYPE, "setMessageReaction"); requestObject.insert(_TYPE, "setMessageReaction");
LOG("Toggle message reaction" << chatId << messageId << reaction);
}
this->sendRequest(requestObject);
}
void TDLibWrapper::removeMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction)
{
QVariantMap requestObject;
requestObject.insert(CHAT_ID, chatId);
requestObject.insert(MESSAGE_ID, messageId);
if (versionNumber > VERSION_NUMBER(1,8,5)) {
// "reaction_type": {
// "@type": "reactionTypeEmoji",
// "emoji": "..."
// }
QVariantMap reactionType;
reactionType.insert(_TYPE, REACTION_TYPE_EMOJI);
reactionType.insert(EMOJI, reaction);
requestObject.insert(REACTION_TYPE, reactionType);
requestObject.insert(_TYPE, "removeMessageReaction");
LOG("Remove message reaction" << chatId << messageId << reaction);
} else {
requestObject.insert("reaction", reaction);
requestObject.insert(_TYPE, "setMessageReaction");
LOG("Toggle message reaction" << chatId << messageId << reaction);
} }
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
@ -1548,17 +1574,17 @@ QVariantMap TDLibWrapper::getUserInformation()
QVariantMap TDLibWrapper::getUserInformation(const QString &userId) QVariantMap TDLibWrapper::getUserInformation(const QString &userId)
{ {
// LOG("Returning user information for ID" << userId); // LOG("Returning user information for ID" << userId);
return this->allUsers.value(userId).toMap(); return this->usersById.value(userId).toMap();
} }
bool TDLibWrapper::hasUserInformation(const QString &userId) bool TDLibWrapper::hasUserInformation(const QString &userId)
{ {
return this->allUsers.contains(userId); return this->usersById.contains(userId);
} }
QVariantMap TDLibWrapper::getUserInformationByName(const QString &userName) QVariantMap TDLibWrapper::getUserInformationByName(const QString &userName)
{ {
return this->allUserNames.value(userName).toMap(); return this->usersByName.value(userName).toMap();
} }
TDLibWrapper::UserPrivacySettingRule TDLibWrapper::getUserPrivacySettingRule(TDLibWrapper::UserPrivacySetting userPrivacySetting) TDLibWrapper::UserPrivacySettingRule TDLibWrapper::getUserPrivacySettingRule(TDLibWrapper::UserPrivacySetting userPrivacySetting)
@ -1787,8 +1813,8 @@ void TDLibWrapper::handleAuthorizationStateChanged(const QString &authorizationS
LOG("Reloading TD Lib..."); LOG("Reloading TD Lib...");
this->basicGroups.clear(); this->basicGroups.clear();
this->superGroups.clear(); this->superGroups.clear();
this->allUsers.clear(); this->usersById.clear();
this->allUserNames.clear(); this->usersByName.clear();
this->tdLibReceiver->setActive(false); this->tdLibReceiver->setActive(false);
while (this->tdLibReceiver->isRunning()) { while (this->tdLibReceiver->isRunning()) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
@ -1839,18 +1865,17 @@ void TDLibWrapper::handleConnectionStateChanged(const QString &connectionState)
emit connectionStateChanged(this->connectionState); emit connectionStateChanged(this->connectionState);
} }
void TDLibWrapper::handleUserUpdated(const QVariantMap &userInformation) void TDLibWrapper::handleUserUpdated(const QVariantMap &updatedUserInformation)
{ {
QString updatedUserId = userInformation.value(ID).toString(); QString updatedUserId = updatedUserInformation.value(ID).toString();
if (updatedUserId == this->options.value("my_id").toString()) { if (updatedUserId == this->options.value("my_id").toString()) {
LOG("Own user information updated :)"); LOG("Own user information updated :)");
this->userInformation = userInformation; this->userInformation = updatedUserInformation;
emit ownUserUpdated(userInformation); emit ownUserUpdated(updatedUserInformation);
} }
LOG("User information updated:" << userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString() << userInformation.value(FIRST_NAME).toString() << userInformation.value(LAST_NAME).toString()); LOG("User information updated:" << updatedUserInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString() << updatedUserInformation.value(FIRST_NAME).toString() << updatedUserInformation.value(LAST_NAME).toString());
this->allUsers.insert(updatedUserId, userInformation); updateUserInformation(updatedUserId, updatedUserInformation);
this->allUserNames.insert(userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString(), userInformation); emit userUpdated(updatedUserId, updatedUserInformation);
emit userUpdated(updatedUserId, userInformation);
} }
void TDLibWrapper::handleUserStatusUpdated(const QString &userId, const QVariantMap &userStatusInformation) void TDLibWrapper::handleUserStatusUpdated(const QString &userId, const QVariantMap &userStatusInformation)
@ -1859,14 +1884,22 @@ void TDLibWrapper::handleUserStatusUpdated(const QString &userId, const QVariant
LOG("Own user status information updated :)"); LOG("Own user status information updated :)");
this->userInformation.insert(STATUS, userStatusInformation); this->userInformation.insert(STATUS, userStatusInformation);
} }
QVariantMap updatedUserInformation = this->usersById.value(userId).toMap();
if(updatedUserInformation[STATUS] == userStatusInformation) {
return;
}
LOG("User status information updated:" << userId << userStatusInformation.value(_TYPE).toString()); LOG("User status information updated:" << userId << userStatusInformation.value(_TYPE).toString());
QVariantMap updatedUserInformation = this->allUsers.value(userId).toMap();
updatedUserInformation.insert(STATUS, userStatusInformation); updatedUserInformation.insert(STATUS, userStatusInformation);
this->allUsers.insert(userId, updatedUserInformation); updateUserInformation(userId, updatedUserInformation);
this->allUserNames.insert(userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString(), userInformation);
emit userUpdated(userId, updatedUserInformation); emit userUpdated(userId, updatedUserInformation);
} }
void TDLibWrapper::updateUserInformation(const QString &userId, const QVariantMap &userInformation)
{
this->usersById.insert(userId, userInformation);
this->usersByName.insert(userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString(), userInformation);
}
void TDLibWrapper::handleFileUpdated(const QVariantMap &fileInformation) void TDLibWrapper::handleFileUpdated(const QVariantMap &fileInformation)
{ {
emit fileUpdated(fileInformation.value(ID).toInt(), fileInformation); emit fileUpdated(fileInformation.value(ID).toInt(), fileInformation);
@ -2087,6 +2120,7 @@ void TDLibWrapper::handleActiveEmojiReactionsUpdated(const QStringList& emojis)
if (activeEmojiReactions != emojis) { if (activeEmojiReactions != emojis) {
activeEmojiReactions = emojis; activeEmojiReactions = emojis;
LOG(emojis.count() << "reaction(s) available"); LOG(emojis.count() << "reaction(s) available");
emit reactionsUpdated();
} }
} }
@ -2178,6 +2212,10 @@ void TDLibWrapper::handleGetPageSourceFinished()
} }
} }
void TDLibWrapper::handleApplicationStateChanged(Qt::ApplicationState state) {
this->tdLibReceiver->setPowerSavingMode(state != Qt::ApplicationState::ApplicationActive);
}
QVariantMap& TDLibWrapper::fillTdlibParameters(QVariantMap& parameters) QVariantMap& TDLibWrapper::fillTdlibParameters(QVariantMap& parameters)
{ {
parameters.insert("api_id", TDLIB_API_ID); parameters.insert("api_id", TDLIB_API_ID);
@ -2310,9 +2348,9 @@ void TDLibWrapper::initializeOpenWith()
} else { } else {
fileOut << QString("MimeType=x-url-handler/t.me;x-scheme-handler/tg;").toUtf8() << "\n"; fileOut << QString("MimeType=x-url-handler/t.me;x-scheme-handler/tg;").toUtf8() << "\n";
} }
fileOut << QString("X-Maemo-Service=de.ygriega.fernschreiber").toUtf8() << "\n"; fileOut << QString("X-Maemo-Service=org.ygriega.Fernschreiber").toUtf8() << "\n";
fileOut << QString("X-Maemo-Object-Path=/de/ygriega/fernschreiber").toUtf8() << "\n"; fileOut << QString("X-Maemo-Object-Path=/org/ygriega/Fernschreiber").toUtf8() << "\n";
fileOut << QString("X-Maemo-Method=de.ygriega.fernschreiber.openUrl").toUtf8() << "\n"; fileOut << QString("X-Maemo-Method=org.ygriega.Fernschreiber.openUrl").toUtf8() << "\n";
fileOut << QString("Hidden=true;").toUtf8() << "\n"; fileOut << QString("Hidden=true;").toUtf8() << "\n";
fileOut.flush(); fileOut.flush();
desktopFile.close(); desktopFile.close();
@ -2325,7 +2363,7 @@ void TDLibWrapper::initializeOpenWith()
LOG("Creating D-Bus directory" << dbusPathName); LOG("Creating D-Bus directory" << dbusPathName);
dbusPath.mkpath(dbusPathName); dbusPath.mkpath(dbusPathName);
} }
QString dbusServiceFileName = dbusPathName + "/de.ygriega.fernschreiber.service"; QString dbusServiceFileName = dbusPathName + "/org.ygriega.Fernschreiber.service";
QFile dbusServiceFile(dbusServiceFileName); QFile dbusServiceFile(dbusServiceFileName);
if (dbusServiceFile.exists()) { if (dbusServiceFile.exists()) {
LOG("D-BUS service file existing, removing to ensure proper re-creation..."); LOG("D-BUS service file existing, removing to ensure proper re-creation...");
@ -2336,8 +2374,8 @@ void TDLibWrapper::initializeOpenWith()
QTextStream fileOut(&dbusServiceFile); QTextStream fileOut(&dbusServiceFile);
fileOut.setCodec("UTF-8"); fileOut.setCodec("UTF-8");
fileOut << QString("[D-BUS Service]").toUtf8() << "\n"; fileOut << QString("[D-BUS Service]").toUtf8() << "\n";
fileOut << QString("Name=de.ygriega.fernschreiber").toUtf8() << "\n"; fileOut << QString("Name=org.ygriega.Fernschreiber").toUtf8() << "\n";
fileOut << QString("Exec=sailjail -- /usr/bin/harbour-fernschreiber").toUtf8() << "\n"; fileOut << QString("Exec=sailjail -- /usr/bin/org.ygriega.Fernschreiberr").toUtf8() << "\n";
fileOut.flush(); fileOut.flush();
dbusServiceFile.close(); dbusServiceFile.close();
} }

View file

@ -249,7 +249,8 @@ public:
Q_INVOKABLE void terminateSession(const QString &sessionId); Q_INVOKABLE void terminateSession(const QString &sessionId);
Q_INVOKABLE void getMessageAvailableReactions(qlonglong chatId, qlonglong messageId); Q_INVOKABLE void getMessageAvailableReactions(qlonglong chatId, qlonglong messageId);
Q_INVOKABLE void getPageSource(const QString &address); Q_INVOKABLE void getPageSource(const QString &address);
Q_INVOKABLE void setMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction); Q_INVOKABLE void addMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction);
Q_INVOKABLE void removeMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction);
Q_INVOKABLE void setNetworkType(NetworkType networkType); Q_INVOKABLE void setNetworkType(NetworkType networkType);
Q_INVOKABLE void setInactiveSessionTtl(int days); Q_INVOKABLE void setInactiveSessionTtl(int days);
@ -340,13 +341,14 @@ signals:
void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount); void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount);
void chatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount); void chatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount);
void tgUrlFound(const QString &tgUrl); void tgUrlFound(const QString &tgUrl);
void reactionsUpdated();
public slots: public slots:
void handleVersionDetected(const QString &version); void handleVersionDetected(const QString &version);
void handleAuthorizationStateChanged(const QString &authorizationState, const QVariantMap authorizationStateData); void handleAuthorizationStateChanged(const QString &authorizationState, const QVariantMap authorizationStateData);
void handleOptionUpdated(const QString &optionName, const QVariant &optionValue); void handleOptionUpdated(const QString &optionName, const QVariant &optionValue);
void handleConnectionStateChanged(const QString &connectionState); void handleConnectionStateChanged(const QString &connectionState);
void handleUserUpdated(const QVariantMap &userInformation); void handleUserUpdated(const QVariantMap &updatedUserInformation);
void handleUserStatusUpdated(const QString &userId, const QVariantMap &userStatusInformation); void handleUserStatusUpdated(const QString &userId, const QVariantMap &userStatusInformation);
void handleFileUpdated(const QVariantMap &fileInformation); void handleFileUpdated(const QVariantMap &fileInformation);
void handleNewChatDiscovered(const QVariantMap &chatInformation); void handleNewChatDiscovered(const QVariantMap &chatInformation);
@ -371,6 +373,7 @@ public slots:
void handleNetworkConfigurationChanged(const QNetworkConfiguration &config); void handleNetworkConfigurationChanged(const QNetworkConfiguration &config);
void handleActiveEmojiReactionsUpdated(const QStringList& emojis); void handleActiveEmojiReactionsUpdated(const QStringList& emojis);
void handleGetPageSourceFinished(); void handleGetPageSourceFinished();
void handleApplicationStateChanged(Qt::ApplicationState state);
private: private:
void setOption(const QString &name, const QString &type, const QVariant &value); void setOption(const QString &name, const QString &type, const QVariant &value);
@ -381,6 +384,7 @@ private:
const Group *updateGroup(qlonglong groupId, const QVariantMap &groupInfo, QHash<qlonglong,Group*> *groups); const Group *updateGroup(qlonglong groupId, const QVariantMap &groupInfo, QHash<qlonglong,Group*> *groups);
QVariantMap newSendMessageRequest(qlonglong chatId, qlonglong replyToMessageId); QVariantMap newSendMessageRequest(qlonglong chatId, qlonglong replyToMessageId);
void initializeTDLibReceiver(); void initializeTDLibReceiver();
void updateUserInformation(const QString &userId, const QVariantMap &userInformation);
private: private:
void *tdLibClient; void *tdLibClient;
@ -397,8 +401,8 @@ private:
QVariantMap options; QVariantMap options;
QVariantMap userInformation; QVariantMap userInformation;
QMap<UserPrivacySetting, UserPrivacySettingRule> userPrivacySettingRules; QMap<UserPrivacySetting, UserPrivacySettingRule> userPrivacySettingRules;
QVariantMap allUsers; QVariantMap usersById;
QVariantMap allUserNames; QVariantMap usersByName;
QVariantMap chats; QVariantMap chats;
QMap<qlonglong, QVariantMap> secretChats; QMap<qlonglong, QVariantMap> secretChats;
QVariantMap unreadMessageInformation; QVariantMap unreadMessageInformation;

View file

@ -499,6 +499,13 @@
<translation>Sie haben noch keine Chats.</translation> <translation>Sie haben noch keine Chats.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Konnte Ihre Kontakte nicht mit Telegram synchronisieren.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -896,6 +903,17 @@
<translation>hat eine Videonachricht geschickt</translation> <translation>hat eine Videonachricht geschickt</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>%Ln Nachricht weiterleiten</numerusform>
<numerusform>%Ln Nachrichten weiterleiten</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -499,6 +499,13 @@
<translation>You don&apos;t have any chats yet.</translation> <translation>You don&apos;t have any chats yet.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished">Could not synchronize your contacts with Telegram.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -898,6 +905,17 @@ messages</numerusform>
<translation>sent a video note</translation> <translation>sent a video note</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Forward %Ln message</numerusform>
<numerusform>Forward %Ln messages</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -45,7 +45,7 @@
</message> </message>
<message> <message>
<source>About Telegram</source> <source>About Telegram</source>
<translation>Telegram</translation> <translation>Telegrama</translation>
</message> </message>
<message> <message>
<source>This product uses the Telegram API but is not endorsed or certified by Telegram.</source> <source>This product uses the Telegram API but is not endorsed or certified by Telegram.</source>
@ -173,7 +173,7 @@
</message> </message>
<message> <message>
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Crear conversación secreta</translation> <translation>Crear Charla Secreta</translation>
</message> </message>
<message> <message>
<source>Unmute Chat</source> <source>Unmute Chat</source>
@ -193,7 +193,7 @@
<message> <message>
<source>Loading common chats</source> <source>Loading common chats</source>
<comment>chats you have in common with a user</comment> <comment>chats you have in common with a user</comment>
<translation>Cargando conversaciones comunes</translation> <translation>Cargando charlas comunes</translation>
</message> </message>
<message> <message>
<source>Loading group members</source> <source>Loading group members</source>
@ -274,11 +274,11 @@
</message> </message>
<message> <message>
<source>Unpin chat</source> <source>Unpin chat</source>
<translation>Desanclar conversación</translation> <translation>Desanclar charla</translation>
</message> </message>
<message> <message>
<source>Pin chat</source> <source>Pin chat</source>
<translation>Anclar conversación</translation> <translation>Anclar charla</translation>
</message> </message>
<message> <message>
<source>Unmute chat</source> <source>Unmute chat</source>
@ -339,7 +339,7 @@
</message> </message>
<message> <message>
<source>This chat is empty.</source> <source>This chat is empty.</source>
<translation>Esta conversación está vacía.</translation> <translation>Esta charla está vacía.</translation>
</message> </message>
<message> <message>
<source>Leave Chat</source> <source>Leave Chat</source>
@ -351,7 +351,7 @@
</message> </message>
<message> <message>
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Saliendo de conversación</translation> <translation>Saliendo de charla</translation>
</message> </message>
<message> <message>
<source>You joined the chat %1</source> <source>You joined the chat %1</source>
@ -405,15 +405,15 @@
</message> </message>
<message> <message>
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source> <source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
<translation>Esta conversación secreta no está lista. El contacto no está conectado.</translation> <translation>Esta charla secreta no está lista. El contacto no está conectado.</translation>
</message> </message>
<message> <message>
<source>Closing chat</source> <source>Closing chat</source>
<translation>Cerrando la conversación</translation> <translation>Cerrando charla</translation>
</message> </message>
<message> <message>
<source>Close Chat</source> <source>Close Chat</source>
<translation>Cerrar conversación</translation> <translation>Cerrar charla</translation>
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
@ -473,11 +473,11 @@
</message> </message>
<message> <message>
<source>Deleting chat</source> <source>Deleting chat</source>
<translation>Borrando la conversación</translation> <translation>Borrando charla</translation>
</message> </message>
<message> <message>
<source>Delete Chat</source> <source>Delete Chat</source>
<translation>Borrar conversación</translation> <translation>Borrar Charla</translation>
</message> </message>
<message> <message>
<source>Deleted User</source> <source>Deleted User</source>
@ -492,11 +492,18 @@
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
<message> <message>
<source>Select Chat</source> <source>Select Chat</source>
<translation>Seleccionar conversación</translation> <translation>Seleccionar Charla</translation>
</message> </message>
<message> <message>
<source>You don&apos;t have any chats yet.</source> <source>You don&apos;t have any chats yet.</source>
<translation>No hay conversaciones.</translation> <translation>No hay charlas.</translation>
</message>
</context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>No se puede sincronizar los contactos con Telegrama.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -529,7 +536,7 @@
<source>chats</source> <source>chats</source>
<translation> <translation>
<numerusform>conversación</numerusform> <numerusform>conversación</numerusform>
<numerusform>conversaciones</numerusform> <numerusform>charlas</numerusform>
</translation> </translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
@ -570,7 +577,7 @@
<message> <message>
<source>Change Chat Info</source> <source>Change Chat Info</source>
<comment>member permission</comment> <comment>member permission</comment>
<translation>Cambiar detalles de conversación</translation> <translation>Cambiar detalles de Charla</translation>
</message> </message>
<message> <message>
<source>Invite Users</source> <source>Invite Users</source>
@ -605,7 +612,7 @@
</message> </message>
<message> <message>
<source>Set how long every chat member has to wait between Messages</source> <source>Set how long every chat member has to wait between Messages</source>
<translation>Establecer cuánto tiempo debe esperar cada miembro de conversación entre mensajes</translation> <translation>Establecer cuánto tiempo debe esperar cada miembro de charla entre Mensajes</translation>
</message> </message>
</context> </context>
<context> <context>
@ -662,7 +669,7 @@
<message> <message>
<source>have registered with Telegram</source> <source>have registered with Telegram</source>
<comment>myself</comment> <comment>myself</comment>
<translation>registrado a Telegram</translation> <translation>registrado a Telegrama</translation>
</message> </message>
<message> <message>
<source>has registered with Telegram</source> <source>has registered with Telegram</source>
@ -689,11 +696,11 @@
<message> <message>
<source>left this chat</source> <source>left this chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation>dejó esta conversación</translation> <translation>dejó esta charla</translation>
</message> </message>
<message> <message>
<source>left this chat</source> <source>left this chat</source>
<translation>dejó esta conversación</translation> <translation>dejó esta charla</translation>
</message> </message>
<message> <message>
<source>sent a voice note</source> <source>sent a voice note</source>
@ -748,7 +755,7 @@
<message> <message>
<source>changed the chat photo</source> <source>changed the chat photo</source>
<comment>myself</comment> <comment>myself</comment>
<translation>cambió la foto de la conversación</translation> <translation>cambió foto de charla</translation>
</message> </message>
<message> <message>
<source>changed the chat photo</source> <source>changed the chat photo</source>
@ -757,7 +764,7 @@
<message> <message>
<source>deleted the chat photo</source> <source>deleted the chat photo</source>
<comment>myself</comment> <comment>myself</comment>
<translation>borró foto de conversación</translation> <translation>borró foto de charla</translation>
</message> </message>
<message> <message>
<source>deleted the chat photo</source> <source>deleted the chat photo</source>
@ -766,11 +773,11 @@
<message> <message>
<source>changed the secret chat TTL setting</source> <source>changed the secret chat TTL setting</source>
<comment>myself</comment> <comment>myself</comment>
<translation>cambió ajustes TTL en conversación secreta</translation> <translation>cambió ajustes TTL en charla secreta</translation>
</message> </message>
<message> <message>
<source>changed the secret chat TTL setting</source> <source>changed the secret chat TTL setting</source>
<translation>cambió ajustes TTL en conversación secreta</translation> <translation>cambió ajustes TTL en charla secreta</translation>
</message> </message>
<message> <message>
<source>upgraded this group to a supergroup</source> <source>upgraded this group to a supergroup</source>
@ -789,11 +796,11 @@
<message> <message>
<source>created a screenshot in this chat</source> <source>created a screenshot in this chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation>creó pantallazo a esta conversación</translation> <translation>creó pantallazo a esta charla</translation>
</message> </message>
<message> <message>
<source>created a screenshot in this chat</source> <source>created a screenshot in this chat</source>
<translation>creó pantallazo a esta conversación</translation> <translation>creó pantallazo a esta charla</translation>
</message> </message>
<message> <message>
<source>sent an unsupported message</source> <source>sent an unsupported message</source>
@ -846,21 +853,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation>ha añadido %1 a conversación</translation> <translation>ha añadido %1 a charla</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation>ha quitado %1 de conversación</translation> <translation>ha quitado %1 de charla</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation>ha añadido %1 a conversación</translation> <translation>ha añadido %1 a charla</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation>ha quitado %1 de conversación</translation> <translation>ha quitado %1 de charla</translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<source>scored %Ln points</source> <source>scored %Ln points</source>
@ -880,11 +887,11 @@
<message> <message>
<source>sent a game</source> <source>sent a game</source>
<comment>myself</comment> <comment>myself</comment>
<translation>envió juego</translation> <translation>envió un juego</translation>
</message> </message>
<message> <message>
<source>sent a game</source> <source>sent a game</source>
<translation>envió juego</translation> <translation>envió un juego</translation>
</message> </message>
<message> <message>
<source>sent a video note</source> <source>sent a video note</source>
@ -896,6 +903,17 @@
<translation>envió nota de video</translation> <translation>envió nota de video</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Reenviar %Ln mensaje</numerusform>
<numerusform>Reenviar %Ln mensajes</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>
@ -1204,7 +1222,7 @@
</message> </message>
<message> <message>
<source>Loading chat list...</source> <source>Loading chat list...</source>
<translation>cargando lista de conversación...</translation> <translation>cargando lista de charla...</translation>
</message> </message>
<message> <message>
<source>Settings</source> <source>Settings</source>
@ -1212,11 +1230,11 @@
</message> </message>
<message> <message>
<source>You don&apos;t have any chats yet.</source> <source>You don&apos;t have any chats yet.</source>
<translation>No hay conversaciones.</translation> <translation>No hay charlas.</translation>
</message> </message>
<message> <message>
<source>New Chat</source> <source>New Chat</source>
<translation>Crear conversación</translation> <translation>Crear Charla</translation>
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
@ -1482,7 +1500,7 @@
</message> </message>
<message> <message>
<source>Show stickers as emojis</source> <source>Show stickers as emojis</source>
<translation>Mostrar pegatinas como emoticonos</translation> <translation>Mostrar pegatinas como Emoticonos</translation>
</message> </message>
<message> <message>
<source>Only display emojis instead of the actual stickers</source> <source>Only display emojis instead of the actual stickers</source>
@ -1490,7 +1508,7 @@
</message> </message>
<message> <message>
<source>Show stickers as images</source> <source>Show stickers as images</source>
<translation>Mostrar pegatinas como imágenes</translation> <translation>Mostrar pegatinas como Imágenes</translation>
</message> </message>
<message> <message>
<source>Show background for stickers and align them centrally like images</source> <source>Show background for stickers and align them centrally like images</source>
@ -1498,7 +1516,7 @@
</message> </message>
<message> <message>
<source>Animate stickers</source> <source>Animate stickers</source>
<translation>Mostrar pegatinas animadas</translation> <translation>Mostrar pegatinas Animadas</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1517,15 +1535,15 @@
</message> </message>
<message> <message>
<source>Focus text input on chat open</source> <source>Focus text input on chat open</source>
<translation>Enfocar entrada de texto a conversación</translation> <translation>Enfocar entrada de texto de Charla</translation>
</message> </message>
<message> <message>
<source>Focus the text input area when entering a chat</source> <source>Focus the text input area when entering a chat</source>
<translation>Enfoca área de entrada de texto al ingresar a conversación</translation> <translation>Enfoca área de entrada de texto al ingresar a charla</translation>
</message> </message>
<message> <message>
<source>Focus text input area after send</source> <source>Focus text input area after send</source>
<translation>Enfocar área de entrada de texto</translation> <translation>Enfocar área de entrada de Texto</translation>
</message> </message>
<message> <message>
<source>Focus the text input area after sending a message</source> <source>Focus the text input area after sending a message</source>
@ -1533,11 +1551,11 @@
</message> </message>
<message> <message>
<source>Delay before marking messages as read</source> <source>Delay before marking messages as read</source>
<translation>Marcar mensajes como leídos</translation> <translation>Marcar mensajes como Leídos</translation>
</message> </message>
<message> <message>
<source>Fernschreiber will wait a bit before messages are marked as read</source> <source>Fernschreiber will wait a bit before messages are marked as read</source>
<translation>Si esta habilitado, apl espera un segundo hasta que mensaje que está en pantalla se marque como leído. Si deshabilita esta función, mensajes se marcarán inmediatamente como leído una vez que esté en pantalla sin desplazarse a mensaje</translation> <translation>Si esta habilitado, Apl espera un segundo hasta que mensaje que está en monitor se marque como leído. Si deshabilita esta función, mensajes se marcarán inmediatamente como leído una vez que esté en monitor sin desplazarse a mensaje</translation>
</message> </message>
<message> <message>
<source>Open-with menu integration</source> <source>Open-with menu integration</source>
@ -1569,19 +1587,19 @@
</message> </message>
<message> <message>
<source>Notification turns on the display</source> <source>Notification turns on the display</source>
<translation>Mostrar notificación por pantalla</translation> <translation>Mostrar notificación por Monitor</translation>
</message> </message>
<message> <message>
<source>Enable notification sounds</source> <source>Enable notification sounds</source>
<translation>Habilitar sonidos notificación</translation> <translation>Habilitar sonidos de Notificación</translation>
</message> </message>
<message> <message>
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Cuando sonidos están habilitados, Ferni utilizará sonido de notificación actual de Sailfish OS para los grupos, que se puede ajustar a configuración del sistema.</translation> <translation>Cuando los sonidos están habilitados, Ferni utilizará sonido de notificación actual de Sailfish OS para los grupos, que se puede ajustar a configuración del sistema.</translation>
</message> </message>
<message> <message>
<source>Always append message preview to notifications</source> <source>Always append message preview to notifications</source>
<translation>Agregar vista previa mensaje a notificaciones</translation> <translation>Vista previa de mensaje en Notificaciones</translation>
</message> </message>
<message> <message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source> <source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
@ -1589,15 +1607,15 @@
</message> </message>
<message> <message>
<source>Highlight unread messages</source> <source>Highlight unread messages</source>
<translation>Resaltar mensajes no leídos</translation> <translation>Resaltar mensajes no Leídos</translation>
</message> </message>
<message> <message>
<source>Highlight Conversations with unread messages</source> <source>Highlight Conversations with unread messages</source>
<translation>Resaltar conversaciones a mensajes no leídos</translation> <translation>Resalta la charla en mensajes no leídos</translation>
</message> </message>
<message> <message>
<source>Hide content in notifications</source> <source>Hide content in notifications</source>
<translation>Ocultar contenido a notificaciones</translation> <translation>Ocultar contenido de notificaciones</translation>
</message> </message>
<message> <message>
<source>Go to quoted message</source> <source>Go to quoted message</source>
@ -1623,7 +1641,7 @@
</message> </message>
<message> <message>
<source>Allow chat invites</source> <source>Allow chat invites</source>
<translation>Permitir invitaciones de grupo</translation> <translation>Permitir invitaciones de Grupo</translation>
</message> </message>
<message> <message>
<source>Privacy setting for managing whether you can be invited to chats.</source> <source>Privacy setting for managing whether you can be invited to chats.</source>
@ -1643,7 +1661,7 @@
</message> </message>
<message> <message>
<source>Allow finding by phone number</source> <source>Allow finding by phone number</source>
<translation>Permitir buscarme por número</translation> <translation>Permitir buscarme por Número</translation>
</message> </message>
<message> <message>
<source>Privacy setting for managing whether you can be found by your phone number.</source> <source>Privacy setting for managing whether you can be found by your phone number.</source>
@ -1651,7 +1669,7 @@
</message> </message>
<message> <message>
<source>Show link in forwarded messages</source> <source>Show link in forwarded messages</source>
<translation>Mostrar enlace a mensajes reenviados</translation> <translation>Mostrar enlace a mensajes Reenviados</translation>
</message> </message>
<message> <message>
<source>Privacy setting for managing whether a link to your account is included in forwarded messages.</source> <source>Privacy setting for managing whether a link to your account is included in forwarded messages.</source>
@ -1659,7 +1677,7 @@
</message> </message>
<message> <message>
<source>Show phone number</source> <source>Show phone number</source>
<translation>Mostrar número telefónico</translation> <translation>Mostrar número Telefónico</translation>
</message> </message>
<message> <message>
<source>Privacy setting for managing whether your phone number is visible.</source> <source>Privacy setting for managing whether your phone number is visible.</source>
@ -1667,7 +1685,7 @@
</message> </message>
<message> <message>
<source>Show profile photo</source> <source>Show profile photo</source>
<translation>Mostrar foto de perfil</translation> <translation>Mostrar foto de Perfil</translation>
</message> </message>
<message> <message>
<source>Privacy setting for managing whether your profile photo is visible.</source> <source>Privacy setting for managing whether your profile photo is visible.</source>
@ -1675,7 +1693,7 @@
</message> </message>
<message> <message>
<source>Show status</source> <source>Show status</source>
<translation>Mostrar estado</translation> <translation>Mostrar Estado</translation>
</message> </message>
<message> <message>
<source>Privacy setting for managing whether your online status is visible.</source> <source>Privacy setting for managing whether your online status is visible.</source>
@ -1683,7 +1701,7 @@
</message> </message>
<message> <message>
<source>Allow sending Location to inline bots</source> <source>Allow sending Location to inline bots</source>
<translation>Enviar ubicación de Robot enlínea</translation> <translation>Enviar ubicación de Robot Enlínea</translation>
</message> </message>
<message> <message>
<source>Some inline bots request location data when using them</source> <source>Some inline bots request location data when using them</source>
@ -1760,15 +1778,15 @@
</message> </message>
<message> <message>
<source>Enable online-only mode</source> <source>Enable online-only mode</source>
<translation>Modo solo enlínea</translation> <translation>Modo solo Enlínea</translation>
</message> </message>
<message> <message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>Deshabilita almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Ferni para efecto.</translation> <translation>Deshabilita el almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Ferni para efecto.</translation>
</message> </message>
<message> <message>
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Optimizador de almacenamiento</translation> <translation>Optimizar Almacenamiento</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1838,7 +1856,7 @@
</message> </message>
<message> <message>
<source>Synchronize Contacts with Telegram</source> <source>Synchronize Contacts with Telegram</source>
<translation>Sincronizar Telegram</translation> <translation>Sincronizar Telegrama</translation>
</message> </message>
</context> </context>
<context> <context>
@ -2219,12 +2237,12 @@
<message> <message>
<source>changed the secret chat TTL setting</source> <source>changed the secret chat TTL setting</source>
<comment>myself; TTL = Time To Live</comment> <comment>myself; TTL = Time To Live</comment>
<translation>cambió ajustes de TTL de conversación secreta</translation> <translation>cambió ajustes de TTL de charla secreta</translation>
</message> </message>
<message> <message>
<source>changed the secret chat TTL setting</source> <source>changed the secret chat TTL setting</source>
<comment>TTL = Time To Live</comment> <comment>TTL = Time To Live</comment>
<translation>cambió ajustes de TTL de conversación secreta</translation> <translation>cambió ajustes de TTL de charla secreta</translation>
</message> </message>
<message> <message>
<source>upgraded this group to a supergroup</source> <source>upgraded this group to a supergroup</source>
@ -2243,11 +2261,11 @@
<message> <message>
<source>created a screenshot in this chat</source> <source>created a screenshot in this chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation>creó pantallazo a conversación</translation> <translation>creó pantallazo de charla</translation>
</message> </message>
<message> <message>
<source>created a screenshot in this chat</source> <source>created a screenshot in this chat</source>
<translation>creó pantallazo a conversación</translation> <translation>creó pantallazo a charla</translation>
</message> </message>
<message> <message>
<source>sent an unsupported message</source> <source>sent an unsupported message</source>

View file

@ -499,6 +499,13 @@
<translation>Sinulla ei ole vielä keskusteluja.</translation> <translation>Sinulla ei ole vielä keskusteluja.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished">Yhteystietojasi ei voitu synkronoida Telegramin kanssa.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -897,6 +904,17 @@
<translation>lähetti videoviestin</translation> <translation>lähetti videoviestin</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Välitä %Ln viesti</numerusform>
<numerusform>Välitä %Ln viestiä</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -499,6 +499,13 @@
<translation>Vous n&apos;avez aucune conversation.</translation> <translation>Vous n&apos;avez aucune conversation.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Impossible de synchroniser vos contacts avec Telegram.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -896,6 +903,17 @@
<translation>a envoyé une note vidéo</translation> <translation>a envoyé une note vidéo</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Transférer %Ln message</numerusform>
<numerusform>Transférer %Ln messages</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -499,6 +499,13 @@
<translation>Nessuna chat presente</translation> <translation>Nessuna chat presente</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished">Sincronizzazione contatti con Telegram non riuscita</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -896,6 +903,17 @@
<translation>ha inviato un videomessaggio</translation> <translation>ha inviato un videomessaggio</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Inoltra %Ln messaggio</numerusform>
<numerusform>Inoltra %Ln messaggi</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -509,6 +509,13 @@
<translation>Nie masz jeszcze żadnych czatów.</translation> <translation>Nie masz jeszcze żadnych czatów.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Nie można zsynchonizaować kontaktów z Telegramem.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -910,6 +917,18 @@
<translation>wysłał notatkę video</translation> <translation>wysłał notatkę video</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Przekaż %Ln wiadomość</numerusform>
<numerusform>Przekaż %Ln wiadomości</numerusform>
<numerusform>Przekaż %Ln wiadomości</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -509,6 +509,13 @@
<translation>Тут пока ничего нет</translation> <translation>Тут пока ничего нет</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Невозможно синхронизировать ваши контакты с Телеграм.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -913,6 +920,18 @@
<translation>отправил(а) видео заметку</translation> <translation>отправил(а) видео заметку</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Перенаправить %Ln сообщение</numerusform>
<numerusform>Перенаправить %Ln сообщения</numerusform>
<numerusform>Перенаправить %Ln сообщений</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

View file

@ -495,7 +495,7 @@
</message> </message>
<message> <message>
<source>Double-tap on a message to choose a reaction</source> <source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation> <translation>Dvojitým klepnutím na správu vybrať reakciu</translation>
</message> </message>
</context> </context>
<context> <context>
@ -509,6 +509,13 @@
<translation>Nemáte žiadne čety.</translation> <translation>Nemáte žiadne čety.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Nemožno synchonizovať kontakty s Telegramom.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -910,6 +917,18 @@
<translation>poslal video-poznámku</translation> <translation>poslal video-poznámku</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Postúpená %Ln správa</numerusform>
<numerusform>Postúpené %Ln správy</numerusform>
<numerusform>Postúpených %Ln správ</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>
@ -1580,7 +1599,7 @@
</message> </message>
<message> <message>
<source>Use non-graphical feedback (sound, vibration) for notifications</source> <source>Use non-graphical feedback (sound, vibration) for notifications</source>
<translation>Pre upozornenia použiť negrafickú reakciu (zvuk, vibrovanie)</translation> <translation>Pre oznamy použiť negrafickú reakciu (zvuk, vibrovanie)</translation>
</message> </message>
<message> <message>
<source>All events</source> <source>All events</source>
@ -1600,39 +1619,39 @@
</message> </message>
<message> <message>
<source>Enable notification sounds</source> <source>Enable notification sounds</source>
<translation>Povoliť zvukové upozornenia</translation> <translation>Povoliť zvukové oznamy</translation>
</message> </message>
<message> <message>
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Keď povolené zvukové upozornenia, Fernschreiber použije aktuálne zvukové upozornenia Sailfish OS pre čety, ktoré môžu byť upravené v nastaveniach systému.</translation> <translation>Keď povolené zvukové oznamy, Fernschreiber použije aktuálne zvukové oznamy Sailfish OS pre čety, ktoré môžu byť upravené v nastaveniach systému.</translation>
</message> </message>
<message> <message>
<source>Always append message preview to notifications</source> <source>Always append message preview to notifications</source>
<translation type="unfinished"></translation> <translation>K upozorneniam vždy pripojiť ukážku správy</translation>
</message> </message>
<message> <message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source> <source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation> <translation>Okrem zobrazenia počtu neprečítaných správ pripojiť k upozorneniam aj najnovšiu správu.</translation>
</message> </message>
<message> <message>
<source>Highlight unread messages</source> <source>Highlight unread messages</source>
<translation type="unfinished"></translation> <translation>Zvýrazniť neprečítané správy</translation>
</message> </message>
<message> <message>
<source>Highlight Conversations with unread messages</source> <source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation> <translation>Zvýrazniť konverzácie s neprečítanými správami</translation>
</message> </message>
<message> <message>
<source>Hide content in notifications</source> <source>Hide content in notifications</source>
<translation type="unfinished"></translation> <translation>V upozorneniach skryť obsah</translation>
</message> </message>
<message> <message>
<source>Go to quoted message</source> <source>Go to quoted message</source>
<translation type="unfinished"></translation> <translation>Prejsť na citovanú správu</translation>
</message> </message>
<message> <message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source> <source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation> <translation>Citovanú správu otvoriť v čete namiesto v náhľade.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1745,39 +1764,39 @@
</message> </message>
<message numerus="yes"> <message numerus="yes">
<source>%1 day(s)</source> <source>%1 day(s)</source>
<translation type="unfinished"> <translation>
<numerusform></numerusform> <numerusform>%1 deň</numerusform>
<numerusform></numerusform> <numerusform>%1 dni</numerusform>
<numerusform></numerusform> <numerusform>%1 dní</numerusform>
</translation> </translation>
</message> </message>
<message> <message>
<source>1 week</source> <source>1 week</source>
<translation type="unfinished"></translation> <translation>1 týždeň</translation>
</message> </message>
<message> <message>
<source>1 month</source> <source>1 month</source>
<translation type="unfinished"></translation> <translation>1 mesiac</translation>
</message> </message>
<message> <message>
<source>3 months</source> <source>3 months</source>
<translation type="unfinished"></translation> <translation>3 mesiace</translation>
</message> </message>
<message> <message>
<source>6 months</source> <source>6 months</source>
<translation type="unfinished"></translation> <translation>6 mesiacov</translation>
</message> </message>
<message> <message>
<source>1 year</source> <source>1 year</source>
<translation type="unfinished"></translation> <translation>1 rok</translation>
</message> </message>
<message> <message>
<source>Session Timeout</source> <source>Session Timeout</source>
<translation type="unfinished"></translation> <translation>Časový limit relácie</translation>
</message> </message>
<message> <message>
<source>Inactive sessions will be terminated after this timeframe</source> <source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation> <translation>Neaktívne relácie budú po tomto časovom rámci ukončené</translation>
</message> </message>
</context> </context>
<context> <context>

View file

@ -185,7 +185,7 @@
</message> </message>
<message> <message>
<source>ID has been copied to the clipboard.</source> <source>ID has been copied to the clipboard.</source>
<translation type="unfinished"></translation> <translation>ID har kopierats till urklipp.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -485,7 +485,7 @@
</message> </message>
<message> <message>
<source>Double-tap on a message to choose a reaction</source> <source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation> <translation>Dubbeltryck ett meddelande för att välja en reaktion</translation>
</message> </message>
</context> </context>
<context> <context>
@ -499,6 +499,13 @@
<translation>Du har inga chattar än.</translation> <translation>Du har inga chattar än.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Kunde inte synkronisera dina kontakter med Telegram.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -896,6 +903,17 @@
<translation>skickade ett videomeddelande</translation> <translation>skickade ett videomeddelande</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Vidarebefordra %Ln meddelande</numerusform>
<numerusform>Vidarebefordra %Ln meddelanden</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>
@ -1163,7 +1181,7 @@
</message> </message>
<message> <message>
<source>No contacts found.</source> <source>No contacts found.</source>
<translation type="unfinished"></translation> <translation>Inga kontakter hittades.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1581,31 +1599,31 @@
</message> </message>
<message> <message>
<source>Always append message preview to notifications</source> <source>Always append message preview to notifications</source>
<translation type="unfinished"></translation> <translation>Visa alltid förhandsgranskning av meddelanden i aviseringar</translation>
</message> </message>
<message> <message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source> <source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation> <translation>Förutom att visa antalet olästa meddelanden kommer det senaste meddelandet också att visas i aviseringarna.</translation>
</message> </message>
<message> <message>
<source>Highlight unread messages</source> <source>Highlight unread messages</source>
<translation type="unfinished"></translation> <translation>Färgmarkera olästa meddelanden</translation>
</message> </message>
<message> <message>
<source>Highlight Conversations with unread messages</source> <source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation> <translation>Färgmarkera konversationer med olästa meddelanden</translation>
</message> </message>
<message> <message>
<source>Hide content in notifications</source> <source>Hide content in notifications</source>
<translation type="unfinished"></translation> <translation>Dölj innehåll i aviseringar</translation>
</message> </message>
<message> <message>
<source>Go to quoted message</source> <source>Go to quoted message</source>
<translation type="unfinished"></translation> <translation> till citerat meddelande</translation>
</message> </message>
<message> <message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source> <source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation> <translation>Vid tryck ett citerat meddelande öppnas det i chatten istället för att visas i ett överlägg.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1718,38 +1736,38 @@
</message> </message>
<message numerus="yes"> <message numerus="yes">
<source>%1 day(s)</source> <source>%1 day(s)</source>
<translation type="unfinished"> <translation>
<numerusform></numerusform> <numerusform>%1 dag</numerusform>
<numerusform></numerusform> <numerusform>%1 dagar</numerusform>
</translation> </translation>
</message> </message>
<message> <message>
<source>1 week</source> <source>1 week</source>
<translation type="unfinished"></translation> <translation>1 vecka</translation>
</message> </message>
<message> <message>
<source>1 month</source> <source>1 month</source>
<translation type="unfinished"></translation> <translation>1 månad</translation>
</message> </message>
<message> <message>
<source>3 months</source> <source>3 months</source>
<translation type="unfinished"></translation> <translation>3 månader</translation>
</message> </message>
<message> <message>
<source>6 months</source> <source>6 months</source>
<translation type="unfinished"></translation> <translation>6 månader</translation>
</message> </message>
<message> <message>
<source>1 year</source> <source>1 year</source>
<translation type="unfinished"></translation> <translation>1 år</translation>
</message> </message>
<message> <message>
<source>Session Timeout</source> <source>Session Timeout</source>
<translation type="unfinished"></translation> <translation>Tidsgräns för session</translation>
</message> </message>
<message> <message>
<source>Inactive sessions will be terminated after this timeframe</source> <source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation> <translation>Inaktiva sessioner avslutas efter den här tidsramen</translation>
</message> </message>
</context> </context>
<context> <context>

View file

@ -499,6 +499,13 @@
<translation>You don&apos;t have any chats yet.</translation> <translation>You don&apos;t have any chats yet.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -896,6 +903,17 @@
<translation type="unfinished">sent a video note</translation> <translation type="unfinished">sent a video note</translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation>
<numerusform>Forward %Ln message</numerusform>
<numerusform>Forward %Ln messages</numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>

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

@ -882,6 +882,16 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>FullscreenOverlay</name>
<message numerus="yes">
<source>Forward %Ln messages</source>
<comment>dialog header</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
</context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
<message> <message>
@ -1580,6 +1590,14 @@
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source> <source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Show reaction button on tap</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The reaction button may appear when you tap the message bubble, to make access to the reactions even easier.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>

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

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