Compare commits
76 commits
Author | SHA1 | Date | |
---|---|---|---|
|
0236586e44 | ||
|
e13c7ae68c | ||
|
1d68123ea1 | ||
|
cf7b706582 | ||
|
c42d030d02 | ||
|
edfce9b492 | ||
|
e5d1ecd9f3 | ||
|
42a7813776 | ||
|
627faba0db | ||
|
a4ebaf52f5 | ||
|
660fe527f2 | ||
|
1a0ed7f298 | ||
062f5d3811 | |||
034c70542f | |||
|
ef244c3319 | ||
|
556505894c | ||
|
7c63ad66f9 | ||
|
26772a48eb | ||
|
2ded14deda | ||
|
465c082328 | ||
|
25e6660e8e | ||
f58afe92cb | |||
2b3ca0dff9 | |||
|
1e88a31f90 | ||
|
026d32c92a | ||
|
a28b9df6b5 | ||
d2bde99e1f | |||
f8ffedb5db | |||
4b7d17c02f | |||
0ae98f96b5 | |||
|
fb3d314ee2 | ||
|
b032b32db1 | ||
|
63f4b37655 | ||
|
bdc0423bf3 | ||
749f05816c | |||
06ebe95309 | |||
|
40ec4b0968 | ||
|
c4c9dc83c0 | ||
9a37db94ae | |||
|
0ba3a8cd7f | ||
|
0aeaf50c92 | ||
|
a9947ff9f7 | ||
|
c7324c020b | ||
|
46419b0960 | ||
|
c1c8729023 | ||
|
9bcc9ab690 | ||
|
0b6a2db2f1 | ||
61a04b034a | |||
|
ca42a5e7e0 | ||
400cda8dcd | |||
fe50ead4ee | |||
f1717cbd29 | |||
|
5394fde136 | ||
1ad324aa23 | |||
59b99a0a28 | |||
12dbac7480 | |||
258466beeb | |||
656e8ccfe7 | |||
22930628ae | |||
0d26167ee3 | |||
723105382d | |||
c9773fb5ab | |||
51227c1323 | |||
aac7fd7328 | |||
f3dd33c4ca | |||
|
83f0c54f8b | ||
71d1831ed3 | |||
|
29621b739a | ||
|
0b91948141 | ||
|
9d37635500 | ||
|
df6322c712 | ||
|
5de2e94f32 | ||
|
85732c6fbc | ||
|
a7ab0ed33a | ||
|
f152bbeb5b | ||
|
b469135877 |
4
.github/workflows/main.yml
vendored
|
@ -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
|
@ -53,3 +53,6 @@ compile_commands.json
|
|||
|
||||
# TDLib API Secrets
|
||||
tdlibsecrets.h
|
||||
|
||||
#Convinience scripts
|
||||
*.sh
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
44
qml/components/ContactSync.qml
Normal 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
|
||||
// }
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 += ( " " + 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 += ( " " + 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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
qml/js/emoji/1f6de.svg
Executable file → Normal 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
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
0
qml/js/emoji/1f7f0.svg
Executable file → Normal file
Before Width: | Height: | Size: 221 B After Width: | Height: | Size: 221 B |
0
qml/js/emoji/1f91d-1f3fb.svg
Executable file → Normal 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
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
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
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
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
0
qml/js/emoji/1f979.svg
Executable file → Normal 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
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
0
qml/js/emoji/1fa7b.svg
Executable file → Normal 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
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
0
qml/js/emoji/1faa9.svg
Executable file → Normal 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
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1faab.svg
Executable file → Normal 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
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
0
qml/js/emoji/1fab7.svg
Executable file → Normal 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
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
0
qml/js/emoji/1fab9.svg
Executable file → Normal 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
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
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
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
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
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
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
0
qml/js/emoji/1fac3.svg
Executable file → Normal 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
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
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
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
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
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
0
qml/js/emoji/1fac4.svg
Executable file → Normal 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
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
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
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
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
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
qml/js/emoji/1fac5.svg
Executable file → Normal 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
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
qml/js/emoji/1fad8.svg
Executable file → Normal 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
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
qml/js/emoji/1fae0.svg
Executable file → Normal file
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1fae1.svg
Executable file → Normal 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
Before Width: | Height: | Size: 971 B After Width: | Height: | Size: 971 B |
0
qml/js/emoji/1fae3.svg
Executable file → Normal 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
Before Width: | Height: | Size: 422 B After Width: | Height: | Size: 422 B |
0
qml/js/emoji/1fae5.svg
Executable file → Normal 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
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
0
qml/js/emoji/1fae7.svg
Executable file → Normal 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
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1faf0-1f3fc.svg
Executable file → Normal file
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1faf0-1f3fd.svg
Executable file → Normal file
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1faf0-1f3fe.svg
Executable file → Normal file
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1faf0-1f3ff.svg
Executable file → Normal file
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
0
qml/js/emoji/1faf0.svg
Executable file → Normal 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |