Compare commits

...

76 commits

Author SHA1 Message Date
Sebastian Wolf
0236586e44 Switch to gh tool for release creation, see https://github.com/actions/runner-images/issues/8362 2023-12-03 23:52:21 +03:00
Sebastian Wolf
e13c7ae68c Prepare 0.17 release 2023-12-03 23:52:19 +03:00
Sebastian Wolf
1d68123ea1 Restore content width in landscape as per discussion in #540 2023-12-03 23:52:06 +03:00
Slava Monich
cf7b706582 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 23:50:55 +03:00
free software
c42d030d02 Update harbour-fernschreiber-es.ts (#542) 2023-12-03 23:50:55 +03:00
Sebastian Wolf
edfce9b492 Highlight message that was jumped to 2023-12-03 23:50:55 +03:00
mbarashkov
e5d1ecd9f3 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 23:50:23 +03:00
Sebastian Wolf
42a7813776 Not only tablets have widescreen ;) 2023-12-03 23:50:21 +03:00
mbarashkov
627faba0db 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-03 23:49:14 +03:00
Slava Monich
a4ebaf52f5 Fix broken signal-slot connection (#541)
QObject::connect: No such signal TDLibWrapper::chatAvailableReactionsUpdated(qlonglong, QString) in src/chatlistmodel.cpp:410
2023-12-03 23:46:48 +03:00
Sebastian Wolf
660fe527f2 Handle event updateChatAvailableReactions 2023-12-03 23:46:46 +03:00
Sebastian Wolf
1a0ed7f298 Only one star per list, restore users in poll results 2023-12-03 23:46:30 +03:00
062f5d3811 Merge pull request 'upstream_changes_251123' (#14) from upstream_changes_251123 into master
Reviewed-on: medvedych/harbour-fernschreiber#14
2023-11-25 13:50:00 +03:00
034c70542f Update build number 2023-11-25 13:49:08 +03:00
Patrick Hervieux
ef244c3319 Update French translation (#532)
Co-authored-by: Patrick Hervieux <patrick.hervieux+git@pherjung.ch>
2023-11-25 13:34:26 +03:00
free software
556505894c Update harbour-fernschreiber-es.ts (#531) 2023-11-25 13:34:11 +03:00
Sebastian Wolf
7c63ad66f9 Interaction hint for new reactions behavior 2023-11-25 13:33:45 +03:00
Sebastian Wolf
26772a48eb Show reactions on double-click 2023-11-25 13:33:13 +03:00
Slava Monich
2ded14deda 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-25 13:31:50 +03:00
free software
465c082328 Update harbour-fernschreiber-es.ts (#529)
* Update harbour-fernschreiber-es.ts

* Update harbour-fernschreiber-es.ts
2023-11-25 13:31:35 +03:00
Slava Monich
25e6660e8e Updated Russian translation (#528) 2023-11-25 13:31:25 +03:00
f58afe92cb Merge pull request 'upstream_changes' (#13) from upstream_changes into master
Reviewed-on: medvedych/harbour-fernschreiber#13
2023-11-21 14:24:05 +03:00
2b3ca0dff9 Update gitignore 2023-11-21 14:23:24 +03:00
Sebastian Wolf
1e88a31f90 Display reactions on last message again 2023-11-21 14:18:20 +03:00
Sebastian Wolf
026d32c92a Restore search in chats 2023-11-21 14:18:20 +03:00
Sebastian Wolf
a28b9df6b5 Minor tweaks for the new session timout setting 2023-11-21 14:18:12 +03:00
d2bde99e1f Merge pull request 'Revert executable rename' (#12) from revert_executable_rename into master
Reviewed-on: medvedych/harbour-fernschreiber#12
2023-11-20 22:31:56 +03:00
f8ffedb5db Revert executable rename 2023-11-20 19:31:56 +00:00
4b7d17c02f Merge pull request 'upstream_changes' (#11) from upstream_changes into master
Reviewed-on: medvedych/harbour-fernschreiber#11
2023-11-20 00:54:47 +03:00
0ae98f96b5 Release version ++ 2023-11-19 21:54:39 +00:00
Slava Monich
fb3d314ee2 Added UI for configuring session inactivity timeout (#527) 2023-11-20 00:50:41 +03:00
Sebastian Wolf
b032b32db1 More places affected by new username handling 2023-11-20 00:50:37 +03:00
Sebastian Wolf
63f4b37655 Hide unsupported emojis for reactions 2023-11-20 00:49:35 +03:00
Sebastian Wolf
bdc0423bf3 Make replies backward-compatible again 2023-11-20 00:49:12 +03:00
749f05816c Merge pull request 'adapt_to_changes_in_tdlib' (#10) from adapt_to_changes_in_tdlib into master
Reviewed-on: medvedych/harbour-fernschreiber#10
2023-11-19 16:35:16 +03:00
06ebe95309 Fix some validator warnings 2023-11-19 16:35:45 +03:00
Sebastian Wolf
40ec4b0968 Fix chat permissions handling 2023-11-19 16:16:46 +03:00
Slava Monich
c4c9dc83c0 Tweaked notification feedback settings UI (#526) 2023-11-19 16:16:36 +03:00
9a37db94ae Update tdlib headers 2023-11-19 16:11:29 +03:00
Sebastian Wolf
0ba3a8cd7f Trouble with usernames 2023-11-19 12:23:42 +03:00
Sebastian Wolf
0aeaf50c92 Some minor adjustments 2023-11-19 12:23:23 +03:00
Johannes Bachmann
a9947ff9f7 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-19 12:22:22 +03:00
Peter G
c7324c020b 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-19 12:22:13 +03:00
Peter G
46419b0960 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-19 12:22:04 +03:00
Slava Monich
c1c8729023 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-19 12:21:17 +03:00
Sebastian Wolf
9bcc9ab690 Only expect chat partner information in private chats 2023-11-19 12:01:42 +03:00
Slava Monich
0b6a2db2f1 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-19 12:01:27 +03:00
61a04b034a Merge pull request 'improve_unread_ballons' (#9) from sprainbrains/harbour-fernschreiber:improve_unread_ballons into master
Reviewed-on: medvedych/harbour-fernschreiber#9
2023-11-18 22:38:58 +03:00
jgibbon
ca42a5e7e0 Add specific unread info for higher counts of unread messages 2023-11-18 22:38:44 +03:00
400cda8dcd Merge pull request 'origin_merged' (#8) from sprainbrains/harbour-fernschreiber:origin_merged into master
Reviewed-on: medvedych/harbour-fernschreiber#8
2023-11-18 22:36:19 +03:00
fe50ead4ee Merged 2023-10-14 00:21:35 +03:00
f1717cbd29 Merge remote-tracking branch 'origin_sailfish/master' into origin_merged 2023-10-14 00:19:37 +03:00
Sebastian Wolf
5394fde136
Update chat list more reliably 2023-08-08 22:50:04 +02:00
1ad324aa23 Merge pull request 'Fixed size of background circle for Unread messages' (#7) from sprainbrains/harbour-fernschreiber:unread_message_background_fix into master
Reviewed-on: medvedych/harbour-fernschreiber#7
2023-08-07 14:17:38 +03:00
59b99a0a28 Fixed size of background circle for Unread messages 2023-08-03 15:02:34 +03:00
12dbac7480 build number ++ 2023-07-12 01:31:25 +03:00
258466beeb Merge pull request 'remove own id from messages title' (#6) from remove_title_on_own_messages_4 into master
Reviewed-on: medvedych/harbour-fernschreiber#6
2023-07-12 01:28:18 +03:00
656e8ccfe7 left margin for own messages in private chats 2023-07-12 01:27:18 +03:00
22930628ae wide message without left margin 2023-07-12 01:12:53 +03:00
0d26167ee3 remove left margin on own messages 2023-07-10 01:29:00 +03:00
723105382d remove own id from messages title 2023-07-09 15:16:06 +03:00
c9773fb5ab Merge pull request 'fix_own_message_ui_3' (#4) from fix_own_message_ui_3 into master
Reviewed-on: medvedych/harbour-fernschreiber#4
2023-07-09 14:43:42 +03:00
51227c1323 forgot desktop file 2023-07-09 14:43:18 +03:00
aac7fd7328 remowed right align on own messages 2023-07-09 14:43:18 +03:00
f3dd33c4ca removed sailfish contact sync support 2023-07-09 14:43:18 +03:00
Sebastian Wolf
83f0c54f8b Switch to icon-m-video as placeholder 2023-07-09 14:43:18 +03:00
71d1831ed3 Merge pull request 'aurora_build' (#2) from aurora_build into master
Reviewed-on: medvedych/harbour-fernschreiber#2
2023-07-08 10:32:41 +03:00
Sebastian Wolf
29621b739a
Switch to icon-m-video as placeholder 2023-06-20 09:46:01 +02:00
Denis Fedoseev
0b91948141 project build settings were modified 2023-04-04 10:54:18 +03:00
Denis Fedoseev
9d37635500 мелкие фиксы для валидатора 2023-04-04 10:53:39 +03:00
okruhliak
df6322c712 Update harbour-fernschreiber-sk.ts 2023-02-27 21:27:26 +01:00
free software
5de2e94f32 Update harbour-fernschreiber-es.ts 2023-02-27 11:57:52 +01:00
Rustem Abzalov
85732c6fbc Update harbour-fernschreiber-ru.ts 2023-02-27 11:56:18 +01:00
Sebastian Wolf
a7ab0ed33a
Prepare support for contact sync with SFOS 4.5 2023-02-05 20:17:06 +01:00
Slava Monich
f152bbeb5b
Always show user or group id on the info page (#511)
And copy it to the clipboard on tap.
2023-02-05 15:52:06 +01:00
Peter G
b469135877
improve message when search yields no results (#507)
Co-authored-by: nephros <nemo@pgxperiiia10>
2023-02-05 15:41:04 +01:00
174 changed files with 19936 additions and 2762 deletions

View file

@ -84,7 +84,7 @@ jobs:
assets+=("-a" "$asset")
done
tag_name="${GITHUB_REF##*/}"
hub release create "${assets[@]}" -m "$tag_name" "$tag_name"
gh release create "$tag_name" "${assets[@]}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -97,6 +97,6 @@ jobs:
assets+=("-a" "$asset")
done
tag_name="${GITHUB_REF##*/}"
hub release create -p "${assets[@]}" -m "$tag_name" -m "This is a pre-release for testing purposes only. It may or may not be unstable." -m "Join the Telegram group to help out: https://github.com/Wunderfitz/harbour-fernschreiber/issues/162" "$tag_name"
gh release create "$tag_name" -p -n "This is a pre-release for testing purposes only. It may or may not be unstable." "${assets[@]}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View file

@ -53,3 +53,6 @@ compile_commands.json
# TDLib API Secrets
tdlibsecrets.h
#Convinience scripts
*.sh

View file

@ -14,7 +14,11 @@ Fernschreiber wouldn't be the same without all the people helping in making it b
- Chat info page, performance improvements to chat page, location support, app initialization/registration with Telegram, project dependencies, emoji handling, qml/js optimizations, multi-message actions, i18n fixes, chat permission handling, code reviews, logging categories, bot support, github build: [jgibbon](https://github.com/jgibbon)
- Copy message to clipboard: [Christian Stemmle](https://github.com/chstem)
- Hide send message button if send-by-enter is switched on, focus text input on entering a chat: [santhoshmanikandan](https://github.com/santhoshmanikandan)
- Integration of logout and sesison options to settings page: [Peter G.](https://github.com/nephros)
- Integration of logout and sesison options to settings page, search results optimization, highlight unread conversations: [Peter G.](https://github.com/nephros)
- Option to always append last message in notifications: [Johannes Bachmann](https://github.com/dscheinah)
- Option to jump to quoted message, widescreen UI adjustments: [Mikhail Barashkov](https://github.com/mbarashkov)
This list might not be complete. In case I forgot something/somebody, please let me know or create a PR, thanks! :)
### Logo/Icon
- Designed by [Matteo](https://github.com/iamnomeutente), adjustments by [Slava Monich](https://github.com/monich)
@ -48,7 +52,7 @@ const char TDLIB_API_HASH[] = "1234567890abcdef1234567890abcdef";
You get the Telegram API ID and hash as soon as you've registered your own application on [https://my.telegram.org](https://my.telegram.org).
Moreover, you need to have a compiled version of [TDLib 1.8.3](https://github.com/tdlib/td) or higher in the sub-directory `tdlib`. This sub-directory must contain another sub-directory that fits to the target device architecture (e.g. armv7hl, i486). Within this directory, there needs to be a folder called `lib` that contains at least `libtdjson.so`. For armv7hl the relative path would consequently be `tdlib/armv7hl/lib`.
Moreover, you need to have a compiled version of [TDLib 1.8.21](https://github.com/tdlib/td) or higher in the sub-directory `tdlib`. This sub-directory must contain another sub-directory that fits to the target device architecture (e.g. armv7hl, i486). Within this directory, there needs to be a folder called `lib` that contains at least `libtdjson.so`. For armv7hl the relative path would consequently be `tdlib/armv7hl/lib`.
You may just want to download the [tdlib.zip from our fork](https://github.com/Wunderfitz/td/releases) to just use the exact version of the latest official Fernschreiber release. To use it, you need to extract it into your local `tdlib/` folder as described above. If so, you're done and can compile Fernschreiber using the Sailfish SDK. If you want to build TDLib for yourself, please keep on reading.

View file

@ -1,6 +1,6 @@
[Desktop Entry]
Type=Application
X-Nemo-Application-Type=generic
X-Nemo-Application-Type=silica-qt5
Icon=harbour-fernschreiber
Exec=harbour-fernschreiber
Name=Fernschreiber

View file

@ -46,6 +46,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/AudioPreview.qml \
qml/components/BackgroundImage.qml \
qml/components/ChatListViewItem.qml \
qml/components/ContactSync.qml \
qml/components/DocumentPreview.qml \
qml/components/GamePreview.qml \
qml/components/ImagePreview.qml \
@ -139,7 +140,6 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/pages/VideoPage.qml \
rpm/harbour-fernschreiber.changes \
rpm/harbour-fernschreiber.spec \
rpm/harbour-fernschreiber.yaml \
translations/*.ts \
harbour-fernschreiber.desktop

View file

@ -0,0 +1,44 @@
/*
Copyright (C) 2021 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.0
//import org.nemomobile.contacts 1.0
Item {
// signal syncError();
// function synchronize() {
// if (peopleModel.count === 0) {
// appNotification.show(qsTr("Could not synchronize your contacts with Telegram."));
// syncError();
// } else {
// contactsModel.startImportingContacts();
// for (var i = 0; i < peopleModel.count; i++ ) {
// contactsModel.importContact(peopleModel.get(i));
// }
// contactsModel.stopImportingContacts();
// }
// }
// PeopleModel {
// id: peopleModel
// requiredProperty: PeopleModel.PhoneNumberRequired
// }
}

View file

@ -40,7 +40,7 @@ Loader {
property string chatId
property string userName
property bool userNameIsValid: userName !== "" && inlineBotInformation && userName.toLowerCase() === inlineBotInformation.username.toLowerCase()
property bool userNameIsValid: userName !== "" && inlineBotInformation && userName.toLowerCase() === inlineBotInformation.usernames.editable_username.toLowerCase()
property string query
property int currentOffset: 0
property string responseExtra: chatId+"|"+userName+"|"+query+"|"+currentOffset

View file

@ -38,7 +38,7 @@ ListItem {
readonly property var userInformation: tdLibWrapper.getUserInformation(myMessage.sender_id.user_id)
property QtObject precalculatedValues: ListView.view.precalculatedValues
readonly property color textColor: isOwnMessage ? Theme.highlightColor : Theme.primaryColor
readonly property int textAlign: isOwnMessage ? Text.AlignRight : Text.AlignLeft
readonly property int textAlign: Text.AlignLeft
readonly property Page page: precalculatedValues.page
readonly property bool isSelected: messageListItem.precalculatedValues.pageIsSelecting && page.selectedMessages.some(function(existingMessage) {
return existingMessage.id === messageId
@ -47,6 +47,7 @@ ListItem {
readonly property bool canDeleteMessage: myMessage.can_be_deleted_for_all_users || (myMessage.can_be_deleted_only_for_self && myMessage.chat_id === page.myUserId)
property bool hasContentComponent
property bool additionalOptionsOpened
property bool wasNavigatedTo: false
readonly property var additionalItemsModel: (extraContentLoader.item && ("extraContextMenuItems" in extraContentLoader.item)) ?
extraContentLoader.item.extraContextMenuItems : 0
@ -64,9 +65,10 @@ ListItem {
readonly property bool showForwardMessageMenuItem: (baseContextMenuItemCount + 2) <= maxContextMenuItemCount
// And don't count "More Options..." for "Delete Message" if "Delete Message" is the only extra option
readonly property bool haveSpaceForDeleteMessageMenuItem: (baseContextMenuItemCount + 3 - (deleteMessageIsOnlyExtraOption ? 1 : 0)) <= maxContextMenuItemCount
property var chatReactions
property var messageReactions
highlighted: (down || isSelected || additionalOptionsOpened) && !menuOpen
highlighted: (down || isSelected || additionalOptionsOpened || wasNavigatedTo) && !menuOpen
openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting
signal replyToMessage()
@ -94,20 +96,43 @@ ListItem {
}
}
function getInteractionText(viewCount, reactions) {
function getInteractionText(viewCount, reactions, size, highlightColor) {
var interactionText = "";
if (viewCount > 0) {
interactionText = Emoji.emojify("👁️", Theme.fontSizeTiny) + Functions.getShortenedCount(viewCount);
interactionText = Emoji.emojify("👁️ ", size) + Functions.getShortenedCount(viewCount);
}
for (var i = 0; i < reactions.length; i++) {
interactionText += ( "&nbsp;" + Emoji.emojify(reactions[i].reaction, Theme.fontSizeTiny) );
if (!chatPage.isPrivateChat) {
interactionText += ( " " + Functions.getShortenedCount(reactions[i].total_count) );
var reaction = reactions[i]
var reactionText = reaction.reaction ? reaction.reaction : (reaction.type && reaction.type.emoji) ? reaction.type.emoji : ""
if (reactionText) {
interactionText += ( "&nbsp;" + Emoji.emojify(reactionText, size) );
if (!chatPage.isPrivateChat) {
var count = Functions.getShortenedCount(reaction.total_count)
interactionText += " "
interactionText += (reaction.is_chosen ? ( "<font color='" + highlightColor + "'><b>" + count + "</b></font>" ) : count)
}
}
}
return interactionText;
}
function openReactions() {
if (messageListItem.chatReactions) {
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);
}
selectReactionBubble.visible = false;
}
function getContentWidthMultiplier() {
return Functions.isWidescreen(appWindow) ? 0.4 : 1.0
}
onClicked: {
if (messageListItem.precalculatedValues.pageIsSelecting) {
page.toggleMessageSelection(myMessage);
@ -125,12 +150,26 @@ ListItem {
if (messageListItem.messageReactions) {
messageListItem.messageReactions = null;
selectReactionBubble.visible = false;
} else {
tdLibWrapper.getMessageAvailableReactions(messageListItem.chatId, messageListItem.messageId);
selectReactionBubble.visible = !selectReactionBubble.visible;
elementSelected(index);
}
}
}
onDoubleClicked: {
if (messageListItem.chatReactions) {
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: {
if (openMenuOnPressAndHold) {
openContextMenu()
@ -154,6 +193,25 @@ ListItem {
}
}
Connections {
target: chatPage
onResetElements: {
messageListItem.messageReactions = null;
selectReactionBubble.visible = false;
}
onElementSelected: {
if (elementIndex !== index) {
selectReactionBubble.visible = false;
}
}
onNavigatedTo: {
if (targetIndex === index) {
messageListItem.wasNavigatedTo = true;
restoreNormalityTimer.start();
}
}
}
Loader {
id: contextMenuLoader
active: false
@ -258,6 +316,9 @@ ListItem {
messageListItem.messageReactions = null;
}
}
onReactionsUpdated: {
chatReactions = tdLibWrapper.getChatReactions(page.chatInformation.id);
}
}
Timer {
@ -270,16 +331,34 @@ ListItem {
interval: 200
triggeredOnStart: false
onTriggered: {
Debug.log("Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index)
if (requestedIndex === index) {
chatView.highlightMoveDuration = -1;
chatView.highlightResizeDuration = -1;
chatView.scrollToIndex(requestedIndex);
chatView.highlightMoveDuration = 0;
chatView.highlightResizeDuration = 0;
var p = chatView.contentItem.mapFromItem(reactionsColumn, 0, 0)
if (chatView.contentY > p.y || p.y + reactionsColumn.height > chatView.contentY + chatView.height) {
Debug.log("Moving reactions for item at", requestedIndex, "info the view")
chatView.highlightMoveDuration = -1
chatView.highlightResizeDuration = -1
chatView.scrollToIndex(requestedIndex, height <= chatView.height ? ListView.Contain : ListView.End)
chatView.highlightMoveDuration = 0
chatView.highlightResizeDuration = 0
}
}
}
}
Timer {
id: restoreNormalityTimer
repeat: false
running: false
interval: 1000
triggeredOnStart: false
onTriggered: {
Debug.log("Restore normality for index " + index);
messageListItem.wasNavigatedTo = false;
}
}
Component.onCompleted: {
delegateComponentLoadingTimer.start();
if (myMessage.reply_to_message_id) {
@ -322,8 +401,10 @@ ListItem {
id: messageTextRow
spacing: Theme.paddingSmall
width: precalculatedValues.entryWidth
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: Functions.isWidescreen(appWindow) ? undefined : parent.horizontalCenter
anchors.left: Functions.isWidescreen(appWindow) ? parent.left : undefined
y: Theme.paddingSmall
anchors.leftMargin: Functions.isWidescreen(appWindow) ? Theme.paddingMedium : undefined
Loader {
id: profileThumbnailLoader
@ -363,13 +444,13 @@ ListItem {
anchors {
left: parent.left
leftMargin: messageListItem.isOwnMessage ? precalculatedValues.pageMarginDouble : 0
leftMargin: page.isPrivateChat ? (messageListItem.isOwnMessage ? precalculatedValues.pageMarginDouble : 0) : 0 //левый марджин для собственных сообщений в приватных чатах. В остальных на полную ширину
verticalCenter: parent.verticalCenter
}
height: messageTextColumn.height + precalculatedValues.paddingMediumDouble
width: precalculatedValues.backgroundWidth
property bool isUnread: index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"
color: Theme.colorScheme === Theme.LightOnDark ? (isUnread ? Theme.secondaryHighlightColor : Theme.secondaryColor) : (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
opacity: isUnread ? 0.5 : 0.2
visible: appSettings.showStickersAsImages || (myMessage.content['@type'] !== "messageSticker" && myMessage.content['@type'] !== "messageAnimatedEmoji")
@ -398,7 +479,7 @@ ListItem {
truncationMode: TruncationMode.Fade
textFormat: Text.StyledText
horizontalAlignment: messageListItem.textAlign
visible: precalculatedValues.showUserInfo || myMessage['@type'] === "sponsoredMessage"
visible: messageListItem.isOwnMessage ? false : (precalculatedValues.showUserInfo || myMessage['@type'] === "sponsoredMessage")
MouseArea {
anchors.fill: parent
enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous)
@ -440,8 +521,12 @@ ListItem {
page.toggleMessageSelection(myMessage)
} else {
messageOptionsDrawer.open = false
messageOverlayLoader.overlayMessage = messageInReplyToRow.inReplyToMessage
messageOverlayLoader.active = true
if(appSettings.goToQuotedMessage) {
chatPage.showMessage(messageInReplyToRow.inReplyToMessage.id, true)
} else {
messageOverlayLoader.active = true
messageOverlayLoader.overlayMessage = messageInReplyToRow.inReplyToMessage
}
}
}
onPressAndHold: {
@ -467,11 +552,12 @@ ListItem {
width: parent.width
Component.onCompleted: {
if (myMessage.forward_info.origin["@type"] === "messageForwardOriginChannel") {
var originType = myMessage.forward_info.origin["@type"]
if (originType === "messageOriginChannel" || originType === "messageForwardOriginChannel") {
var otherChatInformation = tdLibWrapper.getChat(myMessage.forward_info.origin.chat_id);
forwardedThumbnail.photoData = (typeof otherChatInformation.photo !== "undefined") ? otherChatInformation.photo.small : {};
forwardedChannelText.text = Emoji.emojify(otherChatInformation.title, Theme.fontSizeExtraSmall);
} else if (myMessage.forward_info.origin["@type"] === "messageForwardOriginUser") {
} else if (originType === "messageOriginUser" || originType === "messageForwardOriginUser") {
var otherUserInformation = tdLibWrapper.getUserInformation(myMessage.forward_info.origin.sender_user_id);
forwardedThumbnail.photoData = (typeof otherUserInformation.profile_photo !== "undefined") ? otherUserInformation.profile_photo.small : {};
forwardedChannelText.text = Emoji.emojify(Functions.getUserName(otherUserInformation), Theme.fontSizeExtraSmall);
@ -552,7 +638,7 @@ ListItem {
id: webPagePreviewLoader
active: false
asynchronous: true
width: parent.width
width: parent.width * getContentWidthMultiplier()
height: (status === Loader.Ready) ? item.implicitHeight : myMessage.content.web_page ? precalculatedValues.webPagePreviewHeight : 0
sourceComponent: Component {
@ -566,7 +652,7 @@ ListItem {
Loader {
id: extraContentLoader
width: parent.width
width: parent.width * getContentWidthMultiplier()
asynchronous: true
height: item ? item.height : (messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width) : 0)
}
@ -625,7 +711,7 @@ ListItem {
height: ( ( chatPage.isChannel && messageViewCount > 0 ) || reactions.length > 0 ) ? ( Theme.fontSizeExtraSmall + Theme.paddingSmall ) : 0
sourceComponent: Component {
Label {
text: getInteractionText(messageViewCount, reactions)
text: getInteractionText(messageViewCount, reactions, font.pixelSize, Theme.highlightColor)
width: parent.width
font.pixelSize: Theme.fontSizeTiny
color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor
@ -677,7 +763,7 @@ ListItem {
Image {
id: emojiPicture
source: Emoji.getEmojiPath(modelData)
width: Theme.fontSizeLarge
width: status === Image.Ready ? Theme.fontSizeLarge : 0
height: Theme.fontSizeLarge
}

View file

@ -40,9 +40,11 @@ Flickable {
function getOriginalAuthor(forwardInformation, fontSize) {
switch (forwardInformation.origin["@type"]) {
case "messageOriginChannel":
case "messageForwardOriginChannel":
var otherChatInformation = tdLibWrapper.getChat(forwardInformation.origin.chat_id);
return Emoji.emojify(otherChatInformation.title, fontSize);
case "messageOriginUser":
case "messageForwardOriginUser":
var otherUserInformation = tdLibWrapper.getUserInformation(forwardInformation.origin.sender_id.user_id);
return Emoji.emojify(Functions.getUserName(otherUserInformation), fontSize);

View file

@ -31,12 +31,12 @@ Loader {
property var botUserInformation: tdLibWrapper.getUserInformation(message.via_bot_user_id)
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeExtraSmall
text: qsTr("via %1", "message posted via bot user").arg("<a style=\"text-decoration: none; font-weight: bold; color:"+Theme.primaryColor+"\" href=\"userId://" + message.via_bot_user_id + "\">@" + Emoji.emojify(botUserInformation.username, font.pixelSize)+"</a>")
text: qsTr("via %1", "message posted via bot user").arg("<a style=\"text-decoration: none; font-weight: bold; color:"+Theme.primaryColor+"\" href=\"userId://" + message.via_bot_user_id + "\">@" + Emoji.emojify(botUserInformation.usernames.editable_username, font.pixelSize)+"</a>")
textFormat: Text.RichText
truncationMode: TruncationMode.Fade
onLinkActivated: {
if(link === "userId://" + message.via_bot_user_id && botUserInformation.type.is_inline) {
newMessageTextField.text = "@"+botUserInformation.username+" "
newMessageTextField.text = "@"+botUserInformation.usernames.editable_username+" "
newMessageTextField.cursorPosition = newMessageTextField.text.length
lostFocusTimer.start();
}

View file

@ -1,6 +1,7 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0
import "../js/functions.js" as Functions
ListItem {
id: chatListViewItem
@ -87,7 +88,7 @@ ListItem {
Rectangle {
id: chatUnreadMessagesCountBackground
color: isMuted ? ((Theme.colorScheme === Theme.DarkOnLight) ? "lightgray" : "dimgray") : Theme.highlightBackgroundColor
width: Theme.fontSizeLarge
width: chatUnreadMessagesCount.width + Theme.fontSizeLarge / 2
height: Theme.fontSizeLarge
anchors.right: parent.right
anchors.bottom: parent.bottom
@ -103,31 +104,42 @@ ListItem {
anchors.centerIn: chatUnreadMessagesCountBackground
visible: chatListViewItem.unreadCount > 0
opacity: isMuted ? Theme.opacityHigh : 1.0
text: chatListViewItem.unreadCount > 99 ? "99+" : chatListViewItem.unreadCount
text: Functions.formatUnreadCount(chatListViewItem.unreadCount)
}
Rectangle {
id: chatUnreadReactionCountBackground
color: isMuted ? ((Theme.colorScheme === Theme.DarkOnLight) ? "lightgray" : "dimgray") : Theme.highlightBackgroundColor
width: Theme.fontSizeLarge
height: Theme.fontSizeLarge
anchors.right: parent.right
anchors.top: parent.top
radius: parent.width / 2
visible: chatListViewItem.unreadReactionCount > 0
}
visible: chatListViewItem.unreadReactionCount > 0 || chatListViewItem.unreadMentionCount > 0
Icon {
source: "image://theme/icon-s-favorite"
height: Theme.iconSizeExtraSmall
width: Theme.iconSizeExtraSmall
highlighted: chatListViewItem.highlighted
anchors.centerIn: chatUnreadReactionCountBackground
visible: chatListViewItem.unreadReactionCount > 0
}
Icon {
source: "image://theme/icon-s-favorite"
height: Theme.iconSizeExtraSmall
width: Theme.iconSizeExtraSmall
highlighted: chatListViewItem.highlighted
anchors.centerIn: parent
visible: chatListViewItem.unreadReactionCount > 0 && !chatListViewItem.unreadMentionCount
}
Text {
font {
pixelSize: Theme.iconSizeExtraSmall
bold: true
}
color: Theme.primaryColor
anchors.centerIn: parent
visible: chatListViewItem.unreadMentionCount > 0
opacity: isMuted ? Theme.opacityHigh : 1.0
text: "@"
}
}
}
}
Column {
id: contentColumn
anchors {
@ -150,6 +162,9 @@ ListItem {
truncationMode: TruncationMode.Fade
anchors.verticalCenter: parent.verticalCenter
width: Math.min(contentColumn.width - (verifiedImage.visible ? (verifiedImage.width + primaryTextRow.spacing) : 0) - (mutedImage.visible ? (mutedImage.width + primaryTextRow.spacing) : 0), implicitWidth)
font.bold: appSettings.highlightUnreadConversations && ( !chatListViewItem.isMuted && (chatListViewItem.unreadCount > 0 || chatListViewItem.isMarkedAsUnread) )
font.italic: appSettings.highlightUnreadConversations && (chatListViewItem.unreadReactionCount > 0)
color: (appSettings.highlightUnreadConversations && (chatListViewItem.unreadCount > 0)) ? Theme.highlightColor : Theme.primaryColor
}
Image {

View file

@ -60,12 +60,12 @@ Column {
},
inlineKeyboardButtonTypeSwitchInline: function() {
if(modelData.type.in_current_chat) {
chatPage.setMessageText("@" + userInformation.username + " "+(modelData.type.query || ""))
chatPage.setMessageText("@" + userInformation.usernames.editable_username + " "+(modelData.type.query || ""))
} else {
pageStack.push(Qt.resolvedUrl("../pages/ChatSelectionPage.qml"), {
myUserId: chatPage.myUserId,
payload: { neededPermissions: ["can_send_other_messages"], text:"@" + userInformation.username + " "+(modelData.type.query || "")},
payload: { neededPermissions: ["can_send_other_messages"], text:"@" + userInformation.usernames.editable_username + " "+(modelData.type.query || "")},
state: "fillTextArea"
})
}

View file

@ -300,7 +300,8 @@ SilicaFlickable {
}
leftMargin: imageContainer.getEased((imageContainer.minDimension + Theme.paddingMedium), 0, imageContainer.tweenFactor) + Theme.horizontalPageMargin
title: chatInformationPage.chatInformation.title !== "" ? Emoji.emojify(chatInformationPage.chatInformation.title, Theme.fontSizeLarge) : qsTr("Unknown")
description: (chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? ("@"+(chatInformationPage.privateChatUserInformation.username || chatInformationPage.chatPartnerGroupId)) : ""
description: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.privateChatUserInformation.usernames.editable_username)
? ("@"+chatInformationPage.privateChatUserInformation.usernames.editable_username) : ""
}
SilicaFlickable {
@ -363,6 +364,27 @@ SilicaFlickable {
height: imageContainer.hasImage ? imageContainer.maxDimension : 0
}
Label {
id: copyIdText
x: Math.max(headerItem.x + imageContainer.x - groupInfoItem.x + (imageContainer.width - width)/2, 0)
text: chatInformationPage.chatPartnerGroupId
font.pixelSize: Theme.fontSizeSmall
color: copyIdMouseArea.pressed ? Theme.secondaryHighlightColor : Theme.highlightColor
visible: text !== ""
MouseArea {
id: copyIdMouseArea
anchors {
fill: parent
margins: -Theme.paddingLarge
}
onClicked: {
Clipboard.text = copyIdText.text
appNotification.show(qsTr("ID has been copied to the clipboard."));
}
}
}
InformationEditArea {
visible: canEdit
canEdit: !(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.groupInformation.status && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator")

View file

@ -79,7 +79,7 @@ ChatInformationTabItemBase {
// chat title
primaryText.text: Emoji.emojify(Functions.getUserName(user), primaryText.font.pixelSize)
// last user
prologSecondaryText.text: "@"+(user.username !== "" ? user.username : member_id.user_id) + (member_id.user_id === chatInformationPage.myUserId ? " " + qsTr("You") : "")
prologSecondaryText.text: "@"+(user.username ? user.username : member_id.user_id) + (member_id.user_id === chatInformationPage.myUserId ? " " + qsTr("You") : "")
secondaryText {
horizontalAlignment: Text.AlignRight
property string statusText: Functions.getChatMemberStatusText(model.status["@type"])
@ -180,6 +180,9 @@ ChatInformationTabItemBase {
for(var memberIndex in members) {
var memberData = members[memberIndex];
var userInfo = tdLibWrapper.getUserInformation(memberData.member_id.user_id) || {user:{}, bot_info:{}};
if (!userInfo.username && userInfo.usernames && userInfo.usernames.active_usernames) {
userInfo.username = userInfo.usernames.active_usernames[0]
}
memberData.user = userInfo;
memberData.bot_info = memberData.bot_info || {};
pageContent.membersList.append(memberData);

View file

@ -27,7 +27,7 @@ MessageContentBase {
property var stickerData: messageListItem ? messageListItem.myMessage.content.sticker : overlayFlickable.overlayMessage.content.sticker;
readonly property bool asEmoji: appSettings.showStickersAsEmojis
readonly property bool animated: stickerData.type["@type"] === "stickerTypeAnimated" && appSettings.animateStickers
readonly property bool animated: stickerData.format["@type"] === "stickerFormatTgs" && appSettings.animateStickers
readonly property bool stickerVisible: staticStickerLoader.item ? staticStickerLoader.item.visible :
animatedStickerLoader.item ? animatedStickerLoader.item.visible : false
readonly property bool isOwnSticker : messageListItem ? messageListItem.isOwnMessage : overlayFlickable.isOwnMessage

View file

@ -95,7 +95,7 @@ MessageContentBase {
tdLibWrapper.downloadFile(previewFileId);
}
} else {
placeholderImage.source = "image://theme/icon-l-video?white";
placeholderImage.source = "image://theme/icon-m-video?white";
placeholderImage.width = Theme.itemSizeLarge
placeholderImage.height = Theme.itemSizeLarge
}

View file

@ -19,9 +19,10 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import "../../js/functions.js" as Functions
Grid {
width: parent.width - ( 2 * x )
columns: (appWindow.deviceOrientation & Orientation.LandscapeMask) || Screen.sizeCategory === Screen.Large || Screen.sizeCategory === Screen.ExtraLarge ? 2 : 1
columns: Functions.isWidescreen(appWindow) ? 2 : 1
readonly property real columnWidth: width/columns
}

View file

@ -70,6 +70,17 @@ AccordionItem {
}
}
TextSwitch {
width: parent.columnWidth
checked: appSettings.highlightUnreadConversations
text: qsTr("Highlight unread messages")
description: qsTr("Highlight Conversations with unread messages")
automaticCheck: false
onClicked: {
appSettings.highlightUnreadConversations = !checked
}
}
TextSwitch {
width: parent.columnWidth
checked: appSettings.useOpenWith
@ -81,6 +92,28 @@ AccordionItem {
}
}
TextSwitch {
width: parent.columnWidth
checked: appSettings.notificationAlwaysShowPreview
text: qsTr("Always append message preview to notifications")
description: qsTr("In addition to showing the number of unread messages, the latest message will also be appended to notifications.")
automaticCheck: false
onClicked: {
appSettings.notificationAlwaysShowPreview = !checked
}
}
TextSwitch {
width: parent.columnWidth
checked: appSettings.goToQuotedMessage
text: qsTr("Go to quoted message")
description: qsTr("When tapping a quoted message, open it in chat instead of showing it in an overlay.")
automaticCheck: false
onClicked: {
appSettings.goToQuotedMessage = !checked
}
}
ComboBox {
id: feedbackComboBox
width: parent.columnWidth
@ -135,35 +168,53 @@ AccordionItem {
}
}
TextSwitch {
width: parent.columnWidth
checked: appSettings.notificationTurnsDisplayOn && enabled
text: qsTr("Notification turns on the display")
enabled: appSettings.notificationFeedback !== AppSettings.NotificationFeedbackNone
height: enabled ? implicitHeight: 0
clip: height < implicitHeight
visible: height > 0
automaticCheck: false
onClicked: {
appSettings.notificationTurnsDisplayOn = !checked
}
Behavior on height { SmoothedAnimation { duration: 200 } }
Item {
// Occupies one grid cell so that the column ends up under the combo box
// in the landscape layout
visible: parent.columns === 2
width: 1
height: 1
}
TextSwitch {
width: parent.columnWidth
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.")
Column {
enabled: appSettings.notificationFeedback !== AppSettings.NotificationFeedbackNone
width: parent.columnWidth
height: enabled ? implicitHeight: 0
clip: height < implicitHeight
visible: height > 0
automaticCheck: false
onClicked: {
appSettings.notificationSoundsEnabled = !checked
}
Behavior on height { SmoothedAnimation { duration: 200 } }
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 {
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
}
}
}
}
}

View file

@ -30,153 +30,183 @@ AccordionItem {
Column {
id: activeSessionsItem
bottomPadding: Theme.paddingMedium
property variant activeSessions;
property bool loaded : false;
property variant activeSessions
property int inactiveSessionsTtlDays
Component.onCompleted: {
if (!activeSessions) {
tdLibWrapper.getActiveSessions();
} else {
activeSessionsItem.loaded = true;
}
}
Connections {
target: tdLibWrapper
onSessionsReceived: {
activeSessionsItem.activeSessions = sessions;
activeSessionsItem.loaded = true;
activeSessionsItem.activeSessions = sessions
activeSessionsItem.inactiveSessionsTtlDays = inactive_session_ttl_days
}
onOkReceived: {
if (request === "terminateSession") {
appNotification.show(qsTr("Session was terminated"));
activeSessionsItem.loaded = false;
tdLibWrapper.getActiveSessions();
}
}
}
Loader {
id: sessionInformationLoader
active: tdLibWrapper.authorizationState === TelegramAPI.AuthorizationReady
width: parent.width
sourceComponent: Component {
SilicaListView {
id: activeSessionsListView
width: parent.width
height: contentHeight
model: activeSessionsItem.activeSessions
headerPositioning: ListView.OverlayHeader
header: Separator {
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
Column {
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: !activeSessionsListView.count && !activeSessionsItem.inactiveSessionsTtlDays
size: BusyIndicatorSize.Medium
visible: opacity > 0
height: running ? implicitHeight : 0
}
delegate: ListItem {
id: activeSessionListItem
SilicaListView {
id: activeSessionsListView
width: parent.width
contentHeight: activeSessionColumn.height + ( 2 * Theme.paddingMedium )
menu: ContextMenu {
hasContent: !modelData.is_current
onHeightChanged: {
if (parent && flickable) {
// Make sure we are inside the screen area
var bottom = parent.mapToItem(flickable, x, y).y + height
if (bottom > flickable.height) {
flickable.contentY += bottom - flickable.height
}
}
}
MenuItem {
onClicked: {
var sessionId = modelData.id;
Remorse.itemAction(activeSessionListItem, qsTr("Terminating session"), function() { tdLibWrapper.terminateSession(sessionId); });
}
text: qsTr("Terminate Session")
}
}
Column {
id: activeSessionColumn
width: parent.width - ( 2 * Theme.horizontalPageMargin )
spacing: Theme.paddingSmall
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Label {
width: parent.width
text: qsTr("This app")
font.pixelSize: Theme.fontSizeMedium
font.bold: true
visible: modelData.is_current
color: Theme.highlightColor
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: modelData.application_name + " " + modelData.application_version
font.pixelSize: Theme.fontSizeMedium
font.bold: true
color: Theme.primaryColor
maximumLineCount: 1
elide: Text.ElideRight
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: modelData.device_model + ", " + (modelData.platform + " " + modelData.system_version).trim()
font.pixelSize: Theme.fontSizeSmall
color: Theme.primaryColor
maximumLineCount: 1
truncationMode: TruncationMode.Fade
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: qsTr("IP address: %1, origin: %2").arg(modelData.ip).arg(modelData.country)
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
maximumLineCount: 1
truncationMode: TruncationMode.Fade
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: qsTr("Active since: %1, last online: %2").arg(Functions.getDateTimeTimepoint(modelData.log_in_date)).arg(Functions.getDateTimeElapsed(modelData.last_active_date))
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.primaryColor
maximumLineCount: 1
truncationMode: TruncationMode.Fade
anchors {
horizontalCenter: parent.horizontalCenter
}
}
}
Separator {
id: separator
anchors {
bottom: parent.bottom
}
height: contentHeight
model: activeSessionsItem.activeSessions
headerPositioning: ListView.OverlayHeader
header: Separator {
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
visible: activeSessionsListView.count > 0
}
delegate: ListItem {
id: activeSessionListItem
width: parent.width
contentHeight: activeSessionColumn.height + ( 2 * Theme.paddingMedium )
menu: ContextMenu {
hasContent: !modelData.is_current
onHeightChanged: {
if (parent && flickable) {
// Make sure we are inside the screen area
var bottom = parent.mapToItem(flickable, x, y).y + height
if (bottom > flickable.height) {
flickable.contentY += bottom - flickable.height
}
}
}
MenuItem {
onClicked: {
var sessionId = modelData.id;
Remorse.itemAction(activeSessionListItem, qsTr("Terminating session"), function() { tdLibWrapper.terminateSession(sessionId); });
}
text: qsTr("Terminate Session")
}
}
Column {
id: activeSessionColumn
width: parent.width - ( 2 * Theme.horizontalPageMargin )
spacing: Theme.paddingSmall
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Label {
width: parent.width
text: qsTr("This app")
font.pixelSize: Theme.fontSizeMedium
font.bold: true
visible: modelData.is_current
color: Theme.highlightColor
}
Label {
width: parent.width
text: modelData.application_name + " " + modelData.application_version
font.pixelSize: Theme.fontSizeMedium
font.bold: true
maximumLineCount: 1
elide: Text.ElideRight
}
Label {
width: parent.width
text: modelData.device_model + ", " + (modelData.platform + " " + modelData.system_version).trim()
font.pixelSize: Theme.fontSizeSmall
maximumLineCount: 1
truncationMode: TruncationMode.Fade
}
Label {
width: parent.width
text: qsTr("Active since: %1, last online: %2").arg(Functions.getDateTimeTimepoint(modelData.log_in_date)).arg(Functions.getDateTimeElapsed(modelData.last_active_date))
font.pixelSize: Theme.fontSizeExtraSmall
maximumLineCount: 1
truncationMode: TruncationMode.Fade
}
}
Separator {
anchors {
bottom: parent.bottom
}
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
}
}
}
ComboBox {
readonly property int ttl: activeSessionsItem.inactiveSessionsTtlDays
label: qsTr("Session Timeout")
description: qsTr("Inactive sessions will be terminated after this timeframe")
value: (currentItem && currentItem.text) ? currentItem.text : qsTr("%1 day(s)", "", ttl).arg(ttl)
visible: ttl > 0
menu: ContextMenu {
id: ttlMenu
MenuItem {
readonly property int days: 7
text: qsTr("1 week")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 30
text: qsTr("1 month")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 90
text: qsTr("3 months")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 180
text: qsTr("6 months")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 365
text: qsTr("1 year")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
}
Component.onCompleted: updateSelection()
onTtlChanged: updateSelection()
function updateSelection() {
var menuItems = ttlMenu.children
var n = menuItems.length
for (var i = 0; i < n; i++) {
if (menuItems[i].days === ttl) {
currentIndex = i
return
}
}
currentIndex = -1
}
}
}
}

View file

@ -35,6 +35,7 @@ AccordionItem {
readonly property var userInformation: tdLibWrapper.getUserInformation()
property bool uploadInProgress: false
property bool contactSyncEnabled: false
Component.onCompleted: {
tdLibWrapper.getUserProfilePhotos(userInformation.id, 100, 0);
@ -142,7 +143,7 @@ AccordionItem {
visible: true
canEdit: true
headerText: qsTr("Username", "user name of the logged-in profile - header")
text: userInformation.username
text: userInformation.usernames.editable_username
width: parent.columnWidth
headerLeftAligned: true
@ -151,6 +152,49 @@ AccordionItem {
}
}
Column {
id: contactSyncItem
width: parent.width
height: syncInProgress ? ( syncContactsBusyIndicator.height + Theme.paddingMedium ) : ( syncContactsButton.height + Theme.paddingMedium )
visible: accordionContent.contactSyncEnabled
property bool syncInProgress: false
Connections {
target: contactSyncLoader.item
onSyncError: {
contactSyncItem.syncInProgress = false;
}
}
Connections {
target: tdLibWrapper
onContactsImported: {
appNotification.show(qsTr("Contacts successfully synchronized with Telegram."));
}
}
Button {
id: syncContactsButton
text: qsTr("Synchronize Contacts with Telegram")
visible: !contactSyncItem.syncInProgress
anchors {
horizontalCenter: parent.horizontalCenter
}
onClicked: {
contactSyncLoader.item.synchronize();
}
}
BusyIndicator {
id: syncContactsBusyIndicator
anchors.horizontalCenter: parent.horizontalCenter
running: contactSyncItem.syncInProgress
size: BusyIndicatorSize.Small
visible: running
}
}
}
SectionHeader {
@ -247,6 +291,15 @@ AccordionItem {
}
Loader {
id: contactSyncLoader
source: "../ContactSync.qml"
active: true
onLoaded: {
accordionContent.contactSyncEnabled = true;
}
}
Component {
id: imagePickerPage
ImagePickerPage {

0
qml/js/emoji/1f6dd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

0
qml/js/emoji/1f6de.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

0
qml/js/emoji/1f6df.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

0
qml/js/emoji/1f7f0.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 221 B

0
qml/js/emoji/1f91d-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1f91d-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1f91d-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1f91d-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1f91d-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1f979.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1f9cc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

0
qml/js/emoji/1fa7b.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

0
qml/js/emoji/1fa7c.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

0
qml/js/emoji/1faa9.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

0
qml/js/emoji/1faaa.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faab.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

0
qml/js/emoji/1faac.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

0
qml/js/emoji/1fab7.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

0
qml/js/emoji/1fab8.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

0
qml/js/emoji/1fab9.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

0
qml/js/emoji/1faba.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

0
qml/js/emoji/1fac3-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac3-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac3-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac3-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac3-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac3.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac4-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac4-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac4-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac4-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac4-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac4.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
qml/js/emoji/1fac5-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fac5-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fac5-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fac5-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fac5-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fac5.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fad7.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fad8.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fad9.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
qml/js/emoji/1fae0.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1fae1.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

0
qml/js/emoji/1fae2.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 971 B

After

Width:  |  Height:  |  Size: 971 B

0
qml/js/emoji/1fae3.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

0
qml/js/emoji/1fae4.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 422 B

After

Width:  |  Height:  |  Size: 422 B

0
qml/js/emoji/1fae5.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
qml/js/emoji/1fae6.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

0
qml/js/emoji/1fae7.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

0
qml/js/emoji/1faf0-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faf0-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faf0-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faf0-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faf0-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faf0.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

0
qml/js/emoji/1faf1-1f3fb-200d-1faf2-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fb-200d-1faf2-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fb-200d-1faf2-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fb-200d-1faf2-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

0
qml/js/emoji/1faf1-1f3fc-200d-1faf2-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fc-200d-1faf2-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fc-200d-1faf2-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fc-200d-1faf2-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

0
qml/js/emoji/1faf1-1f3fd-200d-1faf2-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fd-200d-1faf2-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fd-200d-1faf2-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fd-200d-1faf2-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

0
qml/js/emoji/1faf1-1f3fe-200d-1faf2-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fe-200d-1faf2-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fe-200d-1faf2-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fe-200d-1faf2-1f3ff.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

0
qml/js/emoji/1faf1-1f3ff-200d-1faf2-1f3fb.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3ff-200d-1faf2-1f3fc.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3ff-200d-1faf2-1f3fd.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
qml/js/emoji/1faf1-1f3ff-200d-1faf2-1f3fe.svg Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

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