Merge branch 'master' into logout

This commit is contained in:
Santhosh Manikandan S 2021-01-11 23:57:31 +05:30
commit 756a96e92c
45 changed files with 2701 additions and 685 deletions

View file

@ -16,7 +16,7 @@ CONFIG += sailfishapp sailfishapp_i18n
PKGCONFIG += nemonotifications-qt5 zlib PKGCONFIG += nemonotifications-qt5 zlib
QT += core dbus sql QT += core dbus sql multimedia positioning
DEFINES += QT_STATICPLUGIN DEFINES += QT_STATICPLUGIN
@ -58,6 +58,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/ReplyMarkupButtons.qml \ qml/components/ReplyMarkupButtons.qml \
qml/components/StickerPicker.qml \ qml/components/StickerPicker.qml \
qml/components/PhotoTextsListItem.qml \ qml/components/PhotoTextsListItem.qml \
qml/components/VoiceNoteOverlay.qml \
qml/components/WebPagePreview.qml \ qml/components/WebPagePreview.qml \
qml/components/chatInformationPage/ChatInformationEditArea.qml \ qml/components/chatInformationPage/ChatInformationEditArea.qml \
qml/components/chatInformationPage/ChatInformationPageContent.qml \ qml/components/chatInformationPage/ChatInformationPageContent.qml \

26
images/icon-s-pin.svg Normal file
View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
style="enable-background:new 0 0 32 32;"
viewBox="0 0 32 32"
height="32px"
width="32px"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata17"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs15" /><path
id="path4567-6-9-2-9-0"
d="M 14.774588,14.651001 2.1829034,28.267313 c -1.0611451,1.031581 0.5159425,2.608675 1.5475102,1.547514 L 17.354282,17.224652 a 8.3460333,8.3460333 0 0 1 -1.438704,-1.136457 8.3460333,8.3460333 0 0 1 -1.14099,-1.437194 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /><path
id="path4569-2-1-7-6-6"
d="m 27.72494,4.2773215 a 8.3460333,8.3460333 0 0 1 -2e-6,11.8030745 8.3460333,8.3460333 0 0 1 -11.803072,0 8.3460333,8.3460333 0 0 1 0,-11.8030745 8.3460333,8.3460333 0 0 1 11.803074,0 z"
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -444,7 +444,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: positionText.top anchors.bottom: positionText.top
minimumValue: 0 minimumValue: 0
maximumValue: messageAudio.duration ? messageAudio.duration : 0 maximumValue: messageAudio.duration ? messageAudio.duration : 0.1
stepSize: 1 stepSize: 1
value: messageAudio.position value: messageAudio.position
enabled: messageAudio.seekable enabled: messageAudio.seekable

View file

@ -9,13 +9,14 @@ PhotoTextsListItem {
id: listItem id: listItem
pictureThumbnail { pictureThumbnail {
photoData: photo_small || ({}) photoData: photo_small || ({})
highlighted: listItem.highlighted && !listItem.menuOpen
} }
property int ownUserId property int ownUserId
property bool showDraft: !!draft_message_text && draft_message_date > last_message_date property bool showDraft: !!draft_message_text && draft_message_date > last_message_date
property string previewText: showDraft ? draft_message_text : last_message_text property string previewText: showDraft ? draft_message_text : last_message_text
// chat title // chat title
primaryText.text: title ? Emoji.emojify(title + ( display.notification_settings.mute_for > 0 ? " 🔇" : "" ), Theme.fontSizeMedium) : qsTr("Unknown") primaryText.text: title ? Emoji.emojify(title, Theme.fontSizeMedium) : qsTr("Unknown")
// last user // last user
prologSecondaryText.text: showDraft ? "<i>"+qsTr("Draft")+"</i>" : (is_channel ? "" : ( last_message_sender_id ? ( last_message_sender_id !== ownUserId ? Emoji.emojify(Functions.getUserName(tdLibWrapper.getUserInformation(last_message_sender_id)), primaryText.font.pixelSize) : qsTr("You") ) : "" )) prologSecondaryText.text: showDraft ? "<i>"+qsTr("Draft")+"</i>" : (is_channel ? "" : ( last_message_sender_id ? ( last_message_sender_id !== ownUserId ? Emoji.emojify(Functions.getUserName(tdLibWrapper.getUserInformation(last_message_sender_id)), primaryText.font.pixelSize) : qsTr("You") ) : "" ))
// last message // last message
@ -25,6 +26,8 @@ PhotoTextsListItem {
unreadCount: unread_count unreadCount: unread_count
isSecret: ( chat_type === TelegramAPI.ChatTypeSecret ) isSecret: ( chat_type === TelegramAPI.ChatTypeSecret )
isMarkedAsUnread: is_marked_as_unread isMarkedAsUnread: is_marked_as_unread
isPinned: is_pinned
isMuted: display.notification_settings.mute_for > 0
openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId
@ -54,19 +57,18 @@ PhotoTextsListItem {
} }
MenuItem { MenuItem {
visible: unread_count === 0 && !is_marked_as_unread visible: unread_count === 0
onClicked: { onClicked: {
tdLibWrapper.toggleChatIsMarkedAsUnread(chat_id, true); tdLibWrapper.toggleChatIsMarkedAsUnread(chat_id, !is_marked_as_unread);
} }
text: qsTr("Mark chat as unread") text: is_marked_as_unread ? qsTr("Mark chat as read") : qsTr("Mark chat as unread")
} }
MenuItem { MenuItem {
visible: unread_count === 0 && is_marked_as_unread
onClicked: { onClicked: {
tdLibWrapper.toggleChatIsMarkedAsUnread(chat_id, false); tdLibWrapper.toggleChatIsPinned(chat_id, !is_pinned);
} }
text: qsTr("Mark chat as read") text: is_pinned ? qsTr("Unpin chat") : qsTr("Pin chat")
} }
MenuItem { MenuItem {
@ -81,7 +83,7 @@ PhotoTextsListItem {
newNotificationSettings.use_default_mute_for = false; newNotificationSettings.use_default_mute_for = false;
tdLibWrapper.setChatNotificationSettings(chat_id, newNotificationSettings); tdLibWrapper.setChatNotificationSettings(chat_id, newNotificationSettings);
} }
text: display.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat") text: display.notification_settings.mute_for > 0 ? qsTr("Unmute chat") : qsTr("Mute chat")
} }
MenuItem { MenuItem {

View file

@ -41,9 +41,9 @@ Item {
if (documentData) { if (documentData) {
if (documentData.document.local.is_downloading_completed) { if (documentData.document.local.is_downloading_completed) {
downloadDocumentButton.visible = false; downloadDocumentButton.visible = false;
openDocumentButton.visible = true; openDocumentArea.visible = true;
} else { } else {
openDocumentButton.visible = false; openDocumentArea.visible = false;
downloadDocumentButton.visible = true; downloadDocumentButton.visible = true;
} }
} }
@ -57,7 +57,7 @@ Item {
downloadingProgressBar.visible = false; downloadingProgressBar.visible = false;
documentData.document = fileInformation; documentData.document = fileInformation;
downloadDocumentButton.visible = false; downloadDocumentButton.visible = false;
openDocumentButton.visible = true; openDocumentArea.visible = true;
if (documentPreviewItem.openRequested) { if (documentPreviewItem.openRequested) {
documentPreviewItem.openRequested = false; documentPreviewItem.openRequested = false;
tdLibWrapper.openFileOnDevice(documentData.document.local.path); tdLibWrapper.openFileOnDevice(documentData.document.local.path);
@ -95,12 +95,21 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
} }
Column {
id: openDocumentArea
visible: false
spacing: Theme.paddingMedium
width: parent.width
onVisibleChanged: {
visible ? (documentPreviewItem.height = openDocumentArea.height) : (documentPreviewItem.height = Theme.itemSizeLarge);
}
Button { Button {
id: openDocumentButton id: openDocumentButton
preferredWidth: Theme.buttonWidthMedium preferredWidth: Theme.buttonWidthMedium
anchors.centerIn: parent anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Open Document") text: qsTr("Open Document")
visible: false
highlighted: documentPreviewItem.highlighted || down highlighted: documentPreviewItem.highlighted || down
onClicked: { onClicked: {
documentPreviewItem.openRequested = true; documentPreviewItem.openRequested = true;
@ -108,4 +117,18 @@ Item {
} }
} }
Button {
id: copyDocumentButton
preferredWidth: Theme.buttonWidthMedium
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Copy Document to Downloads")
highlighted: documentPreviewItem.highlighted || down
onClicked: {
tdLibWrapper.copyFileToDownloads(documentData.document.local.path);
}
}
}
} }

View file

@ -27,6 +27,7 @@ ListItem {
contentHeight: messageBackground.height + Theme.paddingMedium contentHeight: messageBackground.height + Theme.paddingMedium
property var chatId property var chatId
property var messageId property var messageId
property int messageIndex
property var myMessage property var myMessage
property bool canReplyToMessage property bool canReplyToMessage
readonly property bool isAnonymous: myMessage.sender["@type"] === "messageSenderChat" readonly property bool isAnonymous: myMessage.sender["@type"] === "messageSenderChat"
@ -159,13 +160,6 @@ ListItem {
Debug.log("[ChatModel] Messages in this chat were read, new last read: ", lastReadSentIndex, ", updating description for index ", index, ", status: ", (index <= lastReadSentIndex)); Debug.log("[ChatModel] Messages in this chat were read, new last read: ", lastReadSentIndex, ", updating description for index ", index, ", status: ", (index <= lastReadSentIndex));
messageDateText.text = getMessageStatusText(myMessage, index, lastReadSentIndex, messageDateText.useElapsed); messageDateText.text = getMessageStatusText(myMessage, index, lastReadSentIndex, messageDateText.useElapsed);
} }
onMessageUpdated: {
if (index === modelIndex) {
Debug.log("[ChatModel] This message was updated, index ", index, ", updating content...");
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
messageText.text = Emoji.emojify(Functions.getMessageText(myMessage, false, page.myUserId, false), messageText.font.pixelSize);
}
}
} }
Connections { Connections {
@ -190,6 +184,15 @@ ListItem {
} }
} }
onMyMessageChanged: {
Debug.log("[ChatModel] This message was updated, index", messageIndex, ", updating content...")
messageDateText.text = getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed)
messageText.text = Emoji.emojify(Functions.getMessageText(myMessage, false, page.myUserId, false), messageText.font.pixelSize)
if (webPagePreviewLoader.item) {
webPagePreviewLoader.item.webPageData = myMessage.content.web_page
}
}
Timer { Timer {
id: delegateComponentLoadingTimer id: delegateComponentLoadingTimer
interval: 500 interval: 500

View file

@ -14,6 +14,8 @@ ListItem {
property bool isSecret: false property bool isSecret: false
property bool isVerified: false property bool isVerified: false
property bool isMarkedAsUnread: false property bool isMarkedAsUnread: false
property bool isPinned: false
property bool isMuted: false
property alias pictureThumbnail: pictureThumbnail property alias pictureThumbnail: pictureThumbnail
contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium
@ -33,15 +35,14 @@ ListItem {
height: contentColumn.height height: contentColumn.height
spacing: Theme.paddingMedium spacing: Theme.paddingMedium
Column { ShaderEffectSource {
id: pictureColumn id: pictureItem
width: contentColumn.height - Theme.paddingSmall width: contentColumn.height - Theme.paddingSmall
height: contentColumn.height - Theme.paddingSmall height: contentColumn.height - Theme.paddingSmall
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
sourceItem: Item {
Item { width: pictureItem.width
width: parent.width height: pictureItem.width
height: parent.width
ProfileThumbnail { ProfileThumbnail {
id: pictureThumbnail id: pictureThumbnail
@ -50,11 +51,30 @@ ListItem {
height: parent.width height: parent.width
} }
Rectangle {
id: chatPinnedBackground
color: Theme.highlightBackgroundColor
width: Theme.fontSizeLarge
height: Theme.fontSizeLarge
anchors.top: parent.top
radius: parent.width / 2
visible: chatListViewItem.isPinned
}
Image {
source: "../../images/icon-s-pin.svg"
height: Theme.fontSizeSmall
width: Theme.fontSizeSmall
sourceSize: Qt.size(Theme.iconSizeSmall, Theme.iconSizeSmall)
anchors.centerIn: chatPinnedBackground
visible: chatListViewItem.isPinned
}
Rectangle { Rectangle {
id: chatSecretBackground id: chatSecretBackground
color: Theme.overlayBackgroundColor color: Theme.highlightBackgroundColor
width: Theme.fontSizeExtraLarge width: Theme.fontSizeLarge
height: Theme.fontSizeExtraLarge height: Theme.fontSizeLarge
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
radius: parent.width / 2 radius: parent.width / 2
visible: chatListViewItem.isSecret visible: chatListViewItem.isSecret
@ -62,8 +82,8 @@ ListItem {
Image { Image {
source: "image://theme/icon-s-secure" source: "image://theme/icon-s-secure"
height: Theme.fontSizeMedium height: Theme.fontSizeSmall
width: Theme.fontSizeMedium width: Theme.fontSizeSmall
anchors.centerIn: chatSecretBackground anchors.centerIn: chatSecretBackground
visible: chatListViewItem.isSecret visible: chatListViewItem.isSecret
} }
@ -93,7 +113,7 @@ ListItem {
Column { Column {
id: contentColumn id: contentColumn
width: mainColumn.width - pictureColumn.width - mainRow.spacing width: mainColumn.width - pictureItem.width - mainRow.spacing
spacing: Theme.paddingSmall spacing: Theme.paddingSmall
Row { Row {
@ -106,15 +126,26 @@ ListItem {
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
truncationMode: TruncationMode.Fade truncationMode: TruncationMode.Fade
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: Math.min(contentColumn.width - (verifiedImage.visible ? (verifiedImage.width + primaryTextRow.spacing) : 0), implicitWidth) width: Math.min(contentColumn.width - (verifiedImage.visible ? (verifiedImage.width + primaryTextRow.spacing) : 0) - (mutedImage.visible ? (mutedImage.width + primaryTextRow.spacing) : 0), implicitWidth)
} }
Image { Image {
id: verifiedImage id: verifiedImage
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
source: chatListViewItem.isVerified ? "../../images/icon-verified.svg" : "" source: chatListViewItem.isVerified ? "../../images/icon-verified.svg" : ""
sourceSize.width: Theme.iconSizeExtraSmall sourceSize: Qt.size(Theme.iconSizeExtraSmall, Theme.iconSizeExtraSmall)
width: Theme.iconSizeExtraSmall width: Theme.iconSizeSmall
height: Theme.iconSizeSmall
visible: status === Image.Ready
}
Image {
id: mutedImage
anchors.verticalCenter: parent.verticalCenter
source: chatListViewItem.isMuted ? "../js/emoji/1f507.svg" : ""
sourceSize: Qt.size(Theme.iconSizeExtraSmall, Theme.iconSizeExtraSmall)
width: Theme.iconSizeSmall
height: Theme.iconSizeSmall
visible: status === Image.Ready visible: status === Image.Ready
} }
} }

View file

@ -22,7 +22,6 @@ import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0 import WerkWolf.Fernschreiber 1.0
Item { Item {
id: profileThumbnail id: profileThumbnail
property alias photoData: file.fileInformation property alias photoData: file.fileInformation
@ -30,6 +29,10 @@ Item {
property int radius: width / 2 property int radius: width / 2
property int imageStatus: -1 property int imageStatus: -1
property bool optimizeImageSize: true property bool optimizeImageSize: true
property bool highlighted
layer.enabled: highlighted
layer.effect: PressEffect { source: profileThumbnail }
function getReplacementString() { function getReplacementString() {
if (replacementStringHint.length > 2) { if (replacementStringHint.length > 2) {

View file

@ -27,7 +27,7 @@ Item {
property ListItem messageListItem property ListItem messageListItem
property MessageOverlayFlickable overlayFlickable property MessageOverlayFlickable overlayFlickable
property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage property var rawMessage: messageListItem ? messageListItem.myMessage : ( overlayFlickable ? overlayFlickable.overlayMessage : undefined )
property var videoData: ( rawMessage.content['@type'] === "messageVideo" ) ? rawMessage.content.video : ( ( rawMessage.content['@type'] === "messageAnimation" ) ? rawMessage.content.animation : rawMessage.content.video_note ) property var videoData: ( rawMessage.content['@type'] === "messageVideo" ) ? rawMessage.content.video : ( ( rawMessage.content['@type'] === "messageAnimation" ) ? rawMessage.content.animation : rawMessage.content.video_note )
property string videoUrl; property string videoUrl;
@ -89,7 +89,7 @@ Item {
videoMessageComponent.videoType = videoMessageComponent.isVideoNote ? "video" : videoData['@type']; videoMessageComponent.videoType = videoMessageComponent.isVideoNote ? "video" : videoData['@type'];
videoFileId = videoData[videoType].id; videoFileId = videoData[videoType].id;
if (rawMessage.content['@type'] === "messageAnimation") { if (typeof rawMessage !== "undefined" && rawMessage.content['@type'] === "messageAnimation") {
playButton.visible = true; playButton.visible = true;
fullscreenButton.visible = !videoMessageComponent.fullscreen; fullscreenButton.visible = !videoMessageComponent.fullscreen;
handlePlay(); handlePlay();
@ -294,21 +294,6 @@ Item {
} }
} }
Connections {
target: videoMessageComponent
onClicked: {
if (messageVideo.playbackState === MediaPlayer.PlayingState) {
enableScreensaver();
messageVideo.pause();
timeLeftItem.visible = true;
} else {
disableScreensaver();
messageVideo.play();
timeLeftTimer.start();
}
}
}
Video { Video {
id: messageVideo id: messageVideo
@ -367,7 +352,7 @@ Item {
height: parent.height height: parent.height
source: videoUrl source: videoUrl
layer.enabled: videoMessageComponent.highlighted layer.enabled: videoMessageComponent.highlighted
layer.effect: PressEffect { source: singleImage } layer.effect: PressEffect { source: messageVideo }
onStopped: { onStopped: {
enableScreensaver(); enableScreensaver();
messageVideo.visible = false; messageVideo.visible = false;
@ -376,6 +361,21 @@ Item {
videoComponentLoader.active = false; videoComponentLoader.active = false;
fullscreenItem.visible = !videoMessageComponent.fullscreen; fullscreenItem.visible = !videoMessageComponent.fullscreen;
} }
MouseArea {
anchors.fill: parent
onClicked: {
if (messageVideo.playbackState === MediaPlayer.PlayingState) {
enableScreensaver();
messageVideo.pause();
timeLeftItem.visible = true;
} else {
disableScreensaver();
messageVideo.play();
timeLeftTimer.start();
}
}
}
} }
BusyIndicator { BusyIndicator {
@ -482,7 +482,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: positionText.top anchors.bottom: positionText.top
minimumValue: 0 minimumValue: 0
maximumValue: messageVideo.duration ? messageVideo.duration : 0 maximumValue: messageVideo.duration ? messageVideo.duration : 0.1
highlighted: videoMessageComponent.highlighted || down highlighted: videoMessageComponent.highlighted || down
stepSize: 1 stepSize: 1
@ -514,7 +514,6 @@ Item {
} }
} }
} }

View file

@ -0,0 +1,222 @@
/*
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fernschreiber is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0
import "../components"
import "../js/twemoji.js" as Emoji
import "../js/debug.js" as Debug
Item {
id: voiceNoteOverlayItem
anchors.fill: parent
property int recordingState: fernschreiberUtils.getVoiceNoteRecordingState();
property int recordingDuration: 0;
property bool recordingDone: false;
function handleRecordingState() {
switch (recordingState) {
case FernschreiberUtilities.Unavailable:
recordingStateLabel.text = qsTr("Unavailable");
break;
case FernschreiberUtilities.Ready:
recordingStateLabel.text = qsTr("Ready");
break;
case FernschreiberUtilities.Starting:
recordingStateLabel.text = qsTr("Starting");
break;
case FernschreiberUtilities.Recording:
recordingStateLabel.text = qsTr("Recording");
break;
case FernschreiberUtilities.Stopping:
recordingStateLabel.text = qsTr("Stopping");
break;
}
}
function getTwoDigitString(numberToBeConverted) {
var numberString = "00";
if (numberToBeConverted > 0 && numberToBeConverted < 10) {
numberString = "0" + String(numberToBeConverted);
}
if (numberToBeConverted >= 10) {
numberString = String(numberToBeConverted);
}
return numberString;
}
function handleRecordingDuration() {
var minutes = Math.floor(recordingDuration / 60);
var seconds = recordingDuration % 60;
recordingDurationLabel.text = getTwoDigitString(minutes) + ":" + getTwoDigitString(seconds);
}
Component.onCompleted: {
handleRecordingState();
handleRecordingDuration();
}
Connections {
target: fernschreiberUtils
onVoiceNoteDurationChanged: {
Debug.log("New duration received: " + duration);
recordingDuration = Math.round(duration / 1000);
handleRecordingDuration();
}
onVoiceNoteRecordingStateChanged: {
Debug.log("New state received: " + state);
recordingState = state;
handleRecordingState();
}
}
Rectangle {
id: stickerPickerOverlayBackground
anchors.fill: parent
color: Theme.overlayBackgroundColor
opacity: Theme.opacityHigh
}
Flickable {
id: voiceNoteFlickable
anchors.fill: parent
anchors.margins: Theme.paddingMedium
Behavior on opacity { NumberAnimation {} }
contentHeight: voiceNoteColumn.height
clip: true
Column {
id: voiceNoteColumn
spacing: Theme.paddingMedium
width: voiceNoteFlickable.width
InfoLabel {
text: qsTr("Record a Voice Note")
}
Label {
wrapMode: Text.Wrap
width: parent.width - ( 2 * Theme.horizontalPageMargin )
horizontalAlignment: Text.AlignHCenter
text: qsTr("Press the button to start recording")
font.pixelSize: Theme.fontSizeMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Item {
width: Theme.iconSizeExtraLarge
height: Theme.iconSizeExtraLarge
anchors {
horizontalCenter: parent.horizontalCenter
}
Rectangle {
color: Theme.primaryColor
opacity: Theme.opacityOverlay
width: Theme.iconSizeExtraLarge
height: Theme.iconSizeExtraLarge
anchors.centerIn: parent
radius: width / 2
}
Rectangle {
id: recordButton
color: "red"
width: Theme.iconSizeExtraLarge * 0.6
height: Theme.iconSizeExtraLarge * 0.6
anchors.centerIn: parent
radius: width / 2
MouseArea {
anchors.fill: parent
onClicked: {
recordButton.visible = false;
recordingDone = false;
recordingDuration = 0;
handleRecordingDuration();
fernschreiberUtils.startRecordingVoiceNote();
}
}
}
Rectangle {
id: stopButton
visible: !recordButton.visible
color: Theme.overlayBackgroundColor
width: Theme.iconSizeExtraLarge * 0.4
height: Theme.iconSizeExtraLarge * 0.4
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked: {
recordButton.visible = true;
fernschreiberUtils.stopRecordingVoiceNote();
recordingDone = true;
}
}
}
}
Label {
id: recordingStateLabel
wrapMode: Text.Wrap
width: parent.width - ( 2 * Theme.horizontalPageMargin )
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
id: recordingDurationLabel
wrapMode: Text.Wrap
width: parent.width - ( 2 * Theme.horizontalPageMargin )
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Button {
visible: recordingDone
anchors {
horizontalCenter: parent.horizontalCenter
}
text: qsTr("Use recording")
onClicked: {
attachmentOptionsFlickable.isNeeded = false;
attachmentPreviewRow.isVoiceNote = true;
attachmentPreviewRow.attachmentDescription = qsTr("Voice Note (%1)").arg(recordingDurationLabel.text);
attachmentPreviewRow.visible = true;
controlSendButton();
voiceNoteOverlayLoader.active = false;
}
}
}
}
}

View file

@ -33,7 +33,11 @@ Column {
spacing: Theme.paddingSmall spacing: Theme.paddingSmall
Component.onCompleted: { Component.onCompleted: updatePhoto()
onWebPageDataChanged: updatePhoto()
function updatePhoto() {
if (webPageData) { if (webPageData) {
if (webPageData.photo) { if (webPageData.photo) {
// Check first which size fits best... // Check first which size fits best...
@ -134,9 +138,10 @@ Column {
} }
BackgroundImage { BackgroundImage {
id: backgroundImage
visible: hasImage && singleImage.status !== Image.Ready visible: hasImage && singleImage.status !== Image.Ready
layer.enabled: webPagePreviewColumn.highlighted layer.enabled: webPagePreviewColumn.highlighted
layer.effect: PressEffect { source: singleImage } layer.effect: PressEffect { source: backgroundImage }
} }
} }

View file

@ -59,7 +59,7 @@ Page {
} }
Label { Label {
text: "Fernschreiber 0.6" text: "Fernschreiber 0.7"
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeExtraLarge font.pixelSize: Theme.fontSizeExtraLarge
anchors { anchors {
@ -178,7 +178,7 @@ Page {
} }
ProfileThumbnail { ProfileThumbnail {
photoData: aboutPage.userInformation.profile_photo.small photoData: ((typeof aboutPage.userInformation.profile_photo !== "undefined") ? aboutPage.userInformation.profile_photo.small : {})
width: Theme.itemSizeExtraLarge width: Theme.itemSizeExtraLarge
height: Theme.itemSizeExtraLarge height: Theme.itemSizeExtraLarge
replacementStringHint: aboutPage.userInformation.first_name + " " + aboutPage.userInformation.last_name replacementStringHint: aboutPage.userInformation.first_name + " " + aboutPage.userInformation.last_name

View file

@ -211,14 +211,21 @@ Page {
attachmentPreviewRow.isPicture = false; attachmentPreviewRow.isPicture = false;
attachmentPreviewRow.isVideo = false; attachmentPreviewRow.isVideo = false;
attachmentPreviewRow.isDocument = false; attachmentPreviewRow.isDocument = false;
attachmentPreviewRow.isVoiceNote = false;
attachmentPreviewRow.isLocation = false;
attachmentPreviewRow.fileProperties = {}; attachmentPreviewRow.fileProperties = {};
attachmentPreviewRow.locationData = {};
attachmentPreviewRow.attachmentDescription = "";
fernschreiberUtils.stopGeoLocationUpdates();
} }
function controlSendButton() { function controlSendButton() {
if (newMessageTextField.text.length !== 0 if (newMessageTextField.text.length !== 0
|| attachmentPreviewRow.isPicture || attachmentPreviewRow.isPicture
|| attachmentPreviewRow.isDocument || attachmentPreviewRow.isDocument
|| attachmentPreviewRow.isVideo) { || attachmentPreviewRow.isVideo
|| attachmentPreviewRow.isVoiceNote
|| attachmentPreviewRow.isLocation) {
newMessageSendButton.enabled = true; newMessageSendButton.enabled = true;
} else { } else {
newMessageSendButton.enabled = false; newMessageSendButton.enabled = false;
@ -239,14 +246,25 @@ Page {
if (attachmentPreviewRow.isDocument) { if (attachmentPreviewRow.isDocument) {
tdLibWrapper.sendDocumentMessage(chatInformation.id, attachmentPreviewRow.fileProperties.filePath, newMessageTextField.text, newMessageColumn.replyToMessageId); tdLibWrapper.sendDocumentMessage(chatInformation.id, attachmentPreviewRow.fileProperties.filePath, newMessageTextField.text, newMessageColumn.replyToMessageId);
} }
if (attachmentPreviewRow.isVoiceNote) {
tdLibWrapper.sendVoiceNoteMessage(chatInformation.id, fernschreiberUtils.voiceNotePath(), newMessageTextField.text, newMessageColumn.replyToMessageId);
}
if (attachmentPreviewRow.isLocation) {
tdLibWrapper.sendLocationMessage(chatInformation.id, attachmentPreviewRow.locationData.latitude, attachmentPreviewRow.locationData.longitude, attachmentPreviewRow.locationData.horizontalAccuracy, newMessageColumn.replyToMessageId);
}
clearAttachmentPreviewRow(); clearAttachmentPreviewRow();
} else { } else {
tdLibWrapper.sendTextMessage(chatInformation.id, newMessageTextField.text, newMessageColumn.replyToMessageId); tdLibWrapper.sendTextMessage(chatInformation.id, newMessageTextField.text, newMessageColumn.replyToMessageId);
} }
if(appSettings.focusTextAreaAfterSend) {
lostFocusTimer.start();
}
} }
controlSendButton(); controlSendButton();
newMessageInReplyToRow.inReplyToMessage = null; newMessageInReplyToRow.inReplyToMessage = null;
newMessageColumn.editMessageId = "0"; newMessageColumn.editMessageId = "0";
fernschreiberUtils.stopGeoLocationUpdates();
} }
function getWordBoundaries(text, cursorPosition) { function getWordBoundaries(text, cursorPosition) {
@ -376,6 +394,7 @@ Page {
if (chatPage.canSendMessages) { if (chatPage.canSendMessages) {
tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text); tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text);
} }
fernschreiberUtils.stopGeoLocationUpdates();
tdLibWrapper.closeChat(chatInformation.id); tdLibWrapper.closeChat(chatInformation.id);
} }
@ -455,7 +474,7 @@ Page {
Debug.log("[ChatPage] Received pinned message"); Debug.log("[ChatPage] Received pinned message");
pinnedMessageItem.pinnedMessage = message; pinnedMessageItem.pinnedMessage = message;
} }
if (messageId === chatInformation.draft_message.reply_to_message_id) { if (chatInformation.draft_message && messageId === chatInformation.draft_message.reply_to_message_id) {
newMessageInReplyToRow.inReplyToMessage = message; newMessageInReplyToRow.inReplyToMessage = message;
} }
} }
@ -623,7 +642,7 @@ Page {
contentWidth: width contentWidth: width
PullDownMenu { PullDownMenu {
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !messageOverlayLoader.active visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !voiceNoteOverlayLoader.active && !messageOverlayLoader.active
MenuItem { MenuItem {
id: closeSecretChatMenuItem id: closeSecretChatMenuItem
visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed" visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
@ -730,7 +749,7 @@ Page {
Rectangle { Rectangle {
id: chatSecretBackground id: chatSecretBackground
color: Theme.overlayBackgroundColor color: Theme.highlightBackgroundColor
width: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium width: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
height: width height: width
anchors.left: parent.left anchors.left: parent.left
@ -876,7 +895,7 @@ Page {
id: chatView id: chatView
visible: !blurred visible: !blurred
property bool blurred: messageOverlayLoader.item property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item
anchors.fill: parent anchors.fill: parent
opacity: chatPage.loading ? 0 : 1 opacity: chatPage.loading ? 0 : 1
@ -1010,6 +1029,7 @@ Page {
chatId: chatModel.chatId chatId: chatModel.chatId
myMessage: model.display myMessage: model.display
messageId: model.message_id messageId: model.message_id
messageIndex: model.index
extraContentComponentName: chatView.contentComponentNames[model.content_type] || "" extraContentComponentName: chatView.contentComponentNames[model.content_type] || ""
canReplyToMessage: chatPage.canSendMessages canReplyToMessage: chatPage.canSendMessages
onReplyToMessage: { onReplyToMessage: {
@ -1110,7 +1130,7 @@ Page {
Debug.log("Sticker picked: " + stickerId); Debug.log("Sticker picked: " + stickerId);
tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId); tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId);
stickerPickerLoader.active = false; stickerPickerLoader.active = false;
attachmentOptionsRow.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
} }
} }
@ -1134,6 +1154,20 @@ Page {
} }
} }
Loader {
id: voiceNoteOverlayLoader
active: false
asynchronous: true
width: parent.width
height: active ? parent.height : 0
source: "../components/VoiceNoteOverlay.qml"
onActiveChanged: {
if (!active) {
fernschreiberUtils.stopRecordingVoiceNote();
}
}
}
} }
Column { Column {
@ -1141,7 +1175,6 @@ Page {
spacing: Theme.paddingSmall spacing: Theme.paddingSmall
topPadding: Theme.paddingSmall topPadding: Theme.paddingSmall
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
clip: true
visible: height > 0 visible: height > 0
width: parent.width - ( 2 * Theme.horizontalPageMargin ) width: parent.width - ( 2 * Theme.horizontalPageMargin )
height: isNeeded ? implicitHeight : 0 height: isNeeded ? implicitHeight : 0
@ -1173,18 +1206,41 @@ Page {
visible: false visible: false
} }
Flickable {
id: attachmentOptionsFlickable
property bool isNeeded: false
width: chatPage.width
x: -Theme.horizontalPageMargin
height: isNeeded ? attachmentOptionsRow.height : 0
Behavior on height { SmoothedAnimation { duration: 200 } }
visible: height > 0
contentHeight: attachmentOptionsRow.height
contentWidth: Math.max(width, attachmentOptionsRow.width)
property bool fadeRight: (attachmentOptionsRow.width-contentX) > width
property bool fadeLeft: !fadeRight && contentX > 0
layer.enabled: fadeRight || fadeLeft
layer.effect: OpacityRampEffectBase {
direction: attachmentOptionsFlickable.fadeRight ? OpacityRamp.LeftToRight : OpacityRamp.RightToLeft
source: attachmentOptionsFlickable
slope: 1 + 6 * (chatPage.width) / Screen.width
offset: 1 - 1 / slope
}
Row { Row {
id: attachmentOptionsRow id: attachmentOptionsRow
property bool isNeeded: false
visible: height > 0 height: attachImageIconButton.height
height: isNeeded ? implicitHeight : 0
anchors.right: parent.right anchors.right: parent.right
width: parent.width
layoutDirection: Qt.RightToLeft layoutDirection: Qt.RightToLeft
spacing: Theme.paddingMedium spacing: Theme.paddingMedium
clip: true leftPadding: Theme.horizontalPageMargin
Behavior on height { SmoothedAnimation { duration: 200 } } rightPadding: Theme.horizontalPageMargin
IconButton { IconButton {
id: attachImageIconButton
visible: chatPage.hasSendPrivilege("can_send_media_messages") visible: chatPage.hasSendPrivilege("can_send_media_messages")
icon.source: "image://theme/icon-m-image" icon.source: "image://theme/icon-m-image"
onClicked: { onClicked: {
@ -1192,7 +1248,7 @@ Page {
allowedOrientations: chatPage.allowedOrientations allowedOrientations: chatPage.allowedOrientations
}) })
picker.selectedContentPropertiesChanged.connect(function(){ picker.selectedContentPropertiesChanged.connect(function(){
attachmentOptionsRow.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
Debug.log("Selected document: ", picker.selectedContentProperties.filePath ); Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties; attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isPicture = true; attachmentPreviewRow.isPicture = true;
@ -1209,7 +1265,7 @@ Page {
allowedOrientations: chatPage.allowedOrientations allowedOrientations: chatPage.allowedOrientations
}) })
picker.selectedContentPropertiesChanged.connect(function(){ picker.selectedContentPropertiesChanged.connect(function(){
attachmentOptionsRow.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
Debug.log("Selected video: ", picker.selectedContentProperties.filePath ); Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties; attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isVideo = true; attachmentPreviewRow.isVideo = true;
@ -1218,6 +1274,19 @@ Page {
}) })
} }
} }
IconButton {
visible: chatPage.hasSendPrivilege("can_send_media_messages")
icon.source: "image://theme/icon-m-mic"
icon.sourceSize {
width: Theme.iconSizeMedium
height: Theme.iconSizeMedium
}
highlighted: down || voiceNoteOverlayLoader.active
onClicked: {
voiceNoteOverlayLoader.active = !voiceNoteOverlayLoader.active;
stickerPickerLoader.active = false;
}
}
IconButton { IconButton {
visible: chatPage.hasSendPrivilege("can_send_media_messages") visible: chatPage.hasSendPrivilege("can_send_media_messages")
icon.source: "image://theme/icon-m-document" icon.source: "image://theme/icon-m-document"
@ -1226,7 +1295,7 @@ Page {
allowedOrientations: chatPage.allowedOrientations allowedOrientations: chatPage.allowedOrientations
}) })
picker.selectedContentPropertiesChanged.connect(function(){ picker.selectedContentPropertiesChanged.connect(function(){
attachmentOptionsRow.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
Debug.log("Selected document: ", picker.selectedContentProperties.filePath ); Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties; attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isDocument = true; attachmentPreviewRow.isDocument = true;
@ -1245,6 +1314,7 @@ Page {
highlighted: down || stickerPickerLoader.active highlighted: down || stickerPickerLoader.active
onClicked: { onClicked: {
stickerPickerLoader.active = !stickerPickerLoader.active; stickerPickerLoader.active = !stickerPickerLoader.active;
voiceNoteOverlayLoader.active = false;
} }
} }
IconButton { IconButton {
@ -1252,11 +1322,30 @@ Page {
icon.source: "image://theme/icon-m-question" icon.source: "image://theme/icon-m-question"
onClicked: { onClicked: {
pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title}); pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
attachmentOptionsRow.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
}
}
IconButton {
visible: fernschreiberUtils.supportsGeoLocation() && newMessageTextField.text === ""
icon.source: "image://theme/icon-m-location"
icon.sourceSize {
width: Theme.iconSizeMedium
height: Theme.iconSizeMedium
}
onClicked: {
fernschreiberUtils.startGeoLocationUpdates();
attachmentOptionsFlickable.isNeeded = false;
attachmentPreviewRow.isLocation = true;
attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position...");
attachmentPreviewRow.visible = true;
controlSendButton();
} }
} }
} }
}
Row { Row {
id: attachmentPreviewRow id: attachmentPreviewRow
visible: false visible: false
@ -1268,7 +1357,21 @@ Page {
property bool isPicture: false; property bool isPicture: false;
property bool isVideo: false; property bool isVideo: false;
property bool isDocument: false; property bool isDocument: false;
property bool isVoiceNote: false;
property bool isLocation: false;
property var locationData: ({});
property var fileProperties:({}); property var fileProperties:({});
property string attachmentDescription: "";
Connections {
target: fernschreiberUtils
onNewPositionInformation: {
attachmentPreviewRow.locationData = positionInformation;
if (attachmentPreviewRow.isLocation) {
attachmentPreviewRow.attachmentDescription = qsTr("Location (%1/%2)").arg(attachmentPreviewRow.locationData.latitude).arg(attachmentPreviewRow.locationData.longitude);
}
}
}
IconButton { IconButton {
id: removeAttachmentsIconButton id: removeAttachmentsIconButton
@ -1295,13 +1398,13 @@ Page {
Label { Label {
id: attachmentPreviewText id: attachmentPreviewText
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
text: typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.fileName || "" : ""; text: ( attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation ) ? attachmentPreviewRow.attachmentDescription : ( typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.fileName || "" : "" );
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1 maximumLineCount: 1
truncationMode: TruncationMode.Fade truncationMode: TruncationMode.Fade
color: Theme.secondaryColor color: Theme.secondaryColor
visible: attachmentPreviewRow.isDocument visible: attachmentPreviewRow.isDocument || attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation
} }
} }
@ -1503,13 +1606,16 @@ Page {
labelVisible: false labelVisible: false
textLeftMargin: 0 textLeftMargin: 0
textTopMargin: 0 textTopMargin: 0
enabled: !attachmentPreviewRow.isLocation
EnterKey.onClicked: { EnterKey.onClicked: {
if (appSettings.sendByEnter) { if (appSettings.sendByEnter) {
sendMessage(); sendMessage();
newMessageTextField.text = ""; newMessageTextField.text = "";
if(!appSettings.focusTextAreaAfterSend) {
newMessageTextField.focus = false; newMessageTextField.focus = false;
} }
} }
}
EnterKey.enabled: !appSettings.sendByEnter || text.length EnterKey.enabled: !appSettings.sendByEnter || text.length
EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter" EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
@ -1522,16 +1628,17 @@ Page {
IconButton { IconButton {
id: attachmentIconButton id: attachmentIconButton
icon.source: "image://theme/icon-m-attach?" + (attachmentOptionsRow.isNeeded ? Theme.highlightColor : Theme.primaryColor) icon.source: "image://theme/icon-m-attach?" + (attachmentOptionsFlickable.isNeeded ? Theme.highlightColor : Theme.primaryColor)
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall anchors.bottomMargin: Theme.paddingSmall
enabled: !attachmentPreviewRow.visible enabled: !attachmentPreviewRow.visible
onClicked: { onClicked: {
if (attachmentOptionsRow.isNeeded) { if (attachmentOptionsFlickable.isNeeded) {
attachmentOptionsRow.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
stickerPickerLoader.active = false; stickerPickerLoader.active = false;
voiceNoteOverlayLoader.active = false;
} else { } else {
attachmentOptionsRow.isNeeded = true; attachmentOptionsFlickable.isNeeded = true;
} }
} }
} }
@ -1546,6 +1653,7 @@ Page {
onClicked: { onClicked: {
sendMessage(); sendMessage();
newMessageTextField.text = ""; newMessageTextField.text = "";
if(!appSettings.focusTextAreaAfterSend) {
newMessageTextField.focus = false; newMessageTextField.focus = false;
} }
} }
@ -1553,6 +1661,7 @@ Page {
} }
} }
} }
}
Loader { Loader {
id: selectedMessagesActions id: selectedMessagesActions

View file

@ -67,7 +67,7 @@ CoverBackground {
coverPage.authenticated = (tdLibWrapper.getAuthorizationState() === TelegramAPI.AuthorizationReady); coverPage.authenticated = (tdLibWrapper.getAuthorizationState() === TelegramAPI.AuthorizationReady);
coverPage.connectionState = tdLibWrapper.getConnectionState(); coverPage.connectionState = tdLibWrapper.getConnectionState();
coverPage.unreadMessages = tdLibWrapper.getUnreadMessageInformation().unread_count || 0; coverPage.unreadMessages = tdLibWrapper.getUnreadMessageInformation().unread_count || 0;
coverPage.unreadChats = tdLibWrapper.getUnreadChatInformation().unread_count; coverPage.unreadChats = tdLibWrapper.getUnreadChatInformation().unread_count || 0;
setUnreadInfoText(); setUnreadInfoText();
} }
@ -91,6 +91,15 @@ CoverBackground {
} }
} }
Connections {
target: chatListModel
onUnreadStateChanged: {
coverPage.unreadMessages = unreadMessagesCount;
coverPage.unreadChats = unreadChatsCount;
setUnreadInfoText();
}
}
BackgroundImage { BackgroundImage {
id: backgroundImage id: backgroundImage
width: parent.height - Theme.paddingLarge width: parent.height - Theme.paddingLarge

View file

@ -67,6 +67,13 @@ Page {
overviewPage.chatListCreated = true; overviewPage.chatListCreated = true;
chatListView.scrollToTop(); chatListView.scrollToTop();
updateSecondaryContentTimer.start(); updateSecondaryContentTimer.start();
var remainingInteractionHints = appSettings.remainingInteractionHints;
Debug.log("Remaining interaction hints: " + remainingInteractionHints);
if (remainingInteractionHints > 0) {
interactionHintTimer.start();
titleInteractionHint.opacity = 1.0;
appSettings.remainingInteractionHints = remainingInteractionHints - 1;
}
} }
} }
@ -81,6 +88,7 @@ Page {
id: updateSecondaryContentTimer id: updateSecondaryContentTimer
interval: 600 interval: 600
onTriggered: { onTriggered: {
chatListModel.calculateUnreadState();
tdLibWrapper.getRecentStickers(); tdLibWrapper.getRecentStickers();
tdLibWrapper.getInstalledStickerSets(); tdLibWrapper.getInstalledStickerSets();
tdLibWrapper.getContacts(); tdLibWrapper.getContacts();
@ -178,9 +186,8 @@ Page {
function resetFocus() { function resetFocus() {
if (chatSearchField.text === "") { if (chatSearchField.text === "") {
chatSearchField.visible = false; chatSearchField.opacity = 0.0;
pageHeader.visible = true; pageHeader.opacity = 1.0;
searchChatButton.visible = overviewPage.connectionState === TelegramAPI.ConnectionReady;
} }
chatSearchField.focus = false; chatSearchField.focus = false;
overviewPage.focus = true; overviewPage.focus = true;
@ -202,11 +209,15 @@ Page {
onChatLastMessageUpdated: { onChatLastMessageUpdated: {
if (!overviewPage.chatListCreated) { if (!overviewPage.chatListCreated) {
chatListCreatedTimer.restart(); chatListCreatedTimer.restart();
} else {
chatListModel.calculateUnreadState();
} }
} }
onChatOrderUpdated: { onChatOrderUpdated: {
if (!overviewPage.chatListCreated) { if (!overviewPage.chatListCreated) {
chatListCreatedTimer.restart(); chatListCreatedTimer.restart();
} else {
chatListModel.calculateUnreadState();
} }
} }
onChatsReceived: { onChatsReceived: {
@ -268,9 +279,12 @@ Page {
} }
} }
Row { PageHeader {
id: headerRow id: pageHeader
width: parent.width - Theme.horizontalPageMargin title: qsTr("Fernschreiber")
leftMargin: Theme.itemSizeMedium
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
GlassItem { GlassItem {
id: pageStatus id: pageStatus
@ -282,39 +296,23 @@ Page {
cache: false cache: false
} }
PageHeader { MouseArea {
id: pageHeader anchors.fill: parent
title: qsTr("Fernschreiber")
width: visible ? ( parent.width - pageStatus.width - searchChatButton.width ) : 0
opacity: visible ? 1 : 0
Behavior on opacity { FadeAnimation {} }
}
IconButton {
id: searchChatButton
width: visible ? height : 0
opacity: visible ? 1 : 0
Behavior on opacity { NumberAnimation {} }
anchors.verticalCenter: parent.verticalCenter
icon {
source: "image://theme/icon-m-search?" + Theme.highlightColor
asynchronous: true
}
visible: overviewPage.connectionState === TelegramAPI.ConnectionReady
onClicked: { onClicked: {
chatSearchField.focus = true; chatSearchField.focus = true;
chatSearchField.visible = true; chatSearchField.opacity = 1.0;
pageHeader.visible = false; pageHeader.opacity = 0.0;
searchChatButton.visible = false;
} }
} }
}
SearchField { SearchField {
id: chatSearchField id: chatSearchField
visible: false visible: opacity > 0
opacity: visible ? 1 : 0 opacity: 0
Behavior on opacity { FadeAnimation {} } Behavior on opacity { FadeAnimation {} }
width: visible ? ( parent.width - pageStatus.width ) : 0 width: parent.width
height: pageHeader.height height: pageHeader.height
placeholderText: qsTr("Filter your chats...") placeholderText: qsTr("Filter your chats...")
canHide: text === "" canHide: text === ""
@ -333,12 +331,10 @@ Page {
} }
} }
}
SilicaListView { SilicaListView {
id: chatListView id: chatListView
anchors { anchors {
top: headerRow.bottom top: pageHeader.bottom
bottom: parent.bottom bottom: parent.bottom
left: parent.left left: parent.left
right: parent.right right: parent.right
@ -360,7 +356,8 @@ Page {
ViewPlaceholder { ViewPlaceholder {
enabled: chatListView.count === 0 enabled: chatListView.count === 0
text: qsTr("You don't have any chats yet.") text: chatSearchField.text === "" ? qsTr("You don't have any chats yet.") : qsTr("No matching chats found.")
hintText: qsTr("You can search public chats or create a new chat via the pull-down menu.")
} }
VerticalScrollDecorator {} VerticalScrollDecorator {}
@ -403,4 +400,24 @@ Page {
} }
} }
} }
Timer {
id: interactionHintTimer
running: false
interval: 4000
onTriggered: {
titleInteractionHint.opacity = 0.0;
}
}
InteractionHintLabel {
id: titleInteractionHint
text: qsTr("Tap on the title bar to filter your chats")
visible: opacity > 0
invert: true
anchors.fill: parent
Behavior on opacity { FadeAnimation {} }
opacity: 0
}
} }

View file

@ -175,13 +175,13 @@ Page {
onBasicGroupFullInfoUpdated: { onBasicGroupFullInfoUpdated: {
if (foundChatListDelegate.isBasicGroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.basic_group_id.toString()) { if (foundChatListDelegate.isBasicGroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.basic_group_id.toString()) {
foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.members.length).toLocaleString(Qt.locale(), "f", 0)); foundChatListItem.secondaryText.text = qsTr("%1 members", "", groupFullInfo.members.length).arg(Number(groupFullInfo.members.length).toLocaleString(Qt.locale(), "f", 0));
foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/"); foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
} }
} }
onBasicGroupFullInfoReceived: { onBasicGroupFullInfoReceived: {
if (foundChatListDelegate.isBasicGroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.basic_group_id.toString()) { if (foundChatListDelegate.isBasicGroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.basic_group_id.toString()) {
foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.members.length).toLocaleString(Qt.locale(), "f", 0)); foundChatListItem.secondaryText.text = qsTr("%1 members", "", groupFullInfo.members.length).arg(Number(groupFullInfo.members.length).toLocaleString(Qt.locale(), "f", 0));
foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/"); foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
} }
} }
@ -189,9 +189,9 @@ Page {
onSupergroupFullInfoUpdated: { onSupergroupFullInfoUpdated: {
if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) { if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) {
if (foundChatListDelegate.relatedInformation.is_channel) { if (foundChatListDelegate.relatedInformation.is_channel) {
foundChatListItem.secondaryText.text = qsTr("%1 subscribers").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0)); foundChatListItem.secondaryText.text = qsTr("%1 subscribers", "", groupFullInfo.member_count).arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
} else { } else {
foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0)); foundChatListItem.secondaryText.text = qsTr("%1 members", "", groupFullInfo.member_count).arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
} }
foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/"); foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
} }
@ -199,9 +199,9 @@ Page {
onSupergroupFullInfoReceived: { onSupergroupFullInfoReceived: {
if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) { if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) {
if (foundChatListDelegate.relatedInformation.is_channel) { if (foundChatListDelegate.relatedInformation.is_channel) {
foundChatListItem.secondaryText.text = qsTr("%1 subscribers").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0)); foundChatListItem.secondaryText.text = qsTr("%1 subscribers", "", groupFullInfo.member_count).arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
} else { } else {
foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0)); foundChatListItem.secondaryText.text = qsTr("%1 members", "", groupFullInfo.member_count).arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
} }
foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/"); foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
} }

View file

@ -52,6 +52,16 @@ Page {
} }
} }
TextSwitch {
checked: appSettings.focusTextAreaAfterSend
text: qsTr("Focus text input area after send")
description: qsTr("Focus the text input area after sending a message")
automaticCheck: false
onClicked: {
appSettings.focusTextAreaAfterSend = !checked
}
}
TextSwitch { TextSwitch {
checked: appSettings.useOpenWith checked: appSettings.useOpenWith
text: qsTr("Open-with menu integration") text: qsTr("Open-with menu integration")
@ -162,6 +172,16 @@ Page {
} }
} }
TextSwitch {
checked: appSettings.onlineOnlyMode
text: qsTr("Enable online-only mode")
description: qsTr("Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.")
automaticCheck: false
onClicked: {
appSettings.onlineOnlyMode = !checked
}
}
Item { Item {
width: 1 width: 1
height: Theme.paddingLarge // Some space at the bottom height: Theme.paddingLarge // Some space at the bottom

View file

@ -12,6 +12,38 @@
# * date Author's Name <author's email> version-release # * date Author's Name <author's email> version-release
# - Summary of changes # - Summary of changes
* Wed Jan 6 2021 Sebastian J. Wolf <sebastian@ygriega.de> 0.6.1
- Show indicator for pinned chats
- Use highlightBackgroundColor and align size for all chat attributes (unread messages, secret chat, pinned chat)
- Fix: Chat list order of sequence was wrong in some circumstances
- Fix: Make video (fullscreen) page work again
- Fix: Add build requirements for Qt Multimedia and Qt Positioning
- Updated translations for Finnish and Swedish - thanks to jorm1s and eson57
* Tue Jan 5 2021 Sebastian J. Wolf <sebastian@ygriega.de> 0.6
- Filter chat list (tap on title bar to open search field) - thanks to the entire dev team for the great discussion and contributions to the layout :)
- Search for public chats (see pulley menu on overview page)
- Search for chat messages (see pulley menu on chat page)
- Support sending voice notes
- Support sending locations
- Add message draft support - thanks to jgibbon
- Basic bot messages support (reply markup) - thanks to jgibbon
- Add 'Mark chat as read/unread' option
- Add download option to audio messages (voice notes, music...)
- Introduce proper text if other people added/removed a person from a chat
- Tweaks to poll layout - thanks to monich
- New option to keep message input focused after message was sent - thanks to jgibbon
- Send message button now removed if Send-message-by-enter option is enabled (and no attachment is set) - thanks to santhoshmanikandan
- Performance and code optimizations (architecture, startup, JS components) - thanks to monich and jgibbon
- Improve readability in light ambiences
- Upgrade to TDLib 1.7
- Fix: Only show reply to option for messages that can be replied to - thanks to monich
- Fix: Emoji positioning in multi-line texts - thanks to monich
- Fix: Left-over @-mention notifications when all messages in chat are read
- Fix: Occasional crashes on opening poll context menu - thanks to monich
- Fix: Don't display in-reply-to section if message wasn't found
- Several translations updated - thanks to all contributors
* Fri Dec 4 2020 Sebastian J. Wolf <sebastian@ygriega.de> 0.5.1 * Fri Dec 4 2020 Sebastian J. Wolf <sebastian@ygriega.de> 0.5.1
- Add verification badge to verified chats - thanks to monich - Add verification badge to verified chats - thanks to monich
- Improve preview of wide images - thanks to monich - Improve preview of wide images - thanks to monich

View file

@ -11,7 +11,7 @@ Name: harbour-fernschreiber
# << macros # << macros
Summary: Fernschreiber is a Telegram client for Sailfish OS Summary: Fernschreiber is a Telegram client for Sailfish OS
Version: 0.6 Version: 0.7
Release: 1 Release: 1
Group: Qt/Qt Group: Qt/Qt
License: LICENSE License: LICENSE
@ -25,6 +25,8 @@ BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick) BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: pkgconfig(Qt5DBus) BuildRequires: pkgconfig(Qt5DBus)
BuildRequires: pkgconfig(Qt5Sql) BuildRequires: pkgconfig(Qt5Sql)
BuildRequires: pkgconfig(Qt5Multimedia)
BuildRequires: pkgconfig(Qt5Positioning)
BuildRequires: pkgconfig(nemonotifications-qt5) BuildRequires: pkgconfig(nemonotifications-qt5)
BuildRequires: pkgconfig(ngf-qt5) BuildRequires: pkgconfig(ngf-qt5)
BuildRequires: desktop-file-utils BuildRequires: desktop-file-utils

View file

@ -1,6 +1,6 @@
Name: harbour-fernschreiber Name: harbour-fernschreiber
Summary: Fernschreiber is a Telegram client for Sailfish OS Summary: Fernschreiber is a Telegram client for Sailfish OS
Version: 0.6 Version: 0.7
Release: 1 Release: 1
# The contents of the Group field should be one of the groups listed here: # The contents of the Group field should be one of the groups listed here:
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS # https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
@ -25,6 +25,8 @@ PkgConfigBR:
- Qt5Quick - Qt5Quick
- Qt5DBus - Qt5DBus
- Qt5Sql - Qt5Sql
- Qt5Multimedia
- Qt5Positioning
- nemonotifications-qt5 - nemonotifications-qt5
- ngf-qt5 - ngf-qt5

View file

@ -22,12 +22,15 @@
namespace { namespace {
const QString KEY_SEND_BY_ENTER("sendByEnter"); const QString KEY_SEND_BY_ENTER("sendByEnter");
const QString KEY_FOCUS_TEXTAREA_AFTER_SEND("focusTextAreaAfterSend");
const QString KEY_USE_OPEN_WITH("useOpenWith"); const QString KEY_USE_OPEN_WITH("useOpenWith");
const QString KEY_SHOW_STICKERS_AS_IMAGES("showStickersAsImages"); const QString KEY_SHOW_STICKERS_AS_IMAGES("showStickersAsImages");
const QString KEY_ANIMATE_STICKERS("animateStickers"); const QString KEY_ANIMATE_STICKERS("animateStickers");
const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn"); const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn");
const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback"); const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback");
const QString KEY_STORAGE_OPTIMIZER("storageOptimizer"); const QString KEY_STORAGE_OPTIMIZER("storageOptimizer");
const QString KEY_REMAINING_INTERACTION_HINTS("remainingInteractionHints");
const QString KEY_ONLINE_ONLY_MODE("onlineOnlyMode");
} }
AppSettings::AppSettings(QObject *parent) : QObject(parent), settings("harbour-fernschreiber", "settings") AppSettings::AppSettings(QObject *parent) : QObject(parent), settings("harbour-fernschreiber", "settings")
@ -48,6 +51,20 @@ void AppSettings::setSendByEnter(bool sendByEnter)
} }
} }
bool AppSettings::getFocusTextAreaAfterSend() const
{
return settings.value(KEY_FOCUS_TEXTAREA_AFTER_SEND, false).toBool();
}
void AppSettings::setFocusTextAreaAfterSend(bool focusTextAreaAfterSend)
{
if (getFocusTextAreaAfterSend() != focusTextAreaAfterSend) {
LOG(KEY_FOCUS_TEXTAREA_AFTER_SEND << focusTextAreaAfterSend);
settings.setValue(KEY_FOCUS_TEXTAREA_AFTER_SEND, focusTextAreaAfterSend);
emit focusTextAreaAfterSendChanged();
}
}
bool AppSettings::getUseOpenWith() const bool AppSettings::getUseOpenWith() const
{ {
return settings.value(KEY_USE_OPEN_WITH, true).toBool(); return settings.value(KEY_USE_OPEN_WITH, true).toBool();
@ -131,3 +148,31 @@ void AppSettings::setStorageOptimizer(bool enable)
emit storageOptimizerChanged(); emit storageOptimizerChanged();
} }
} }
int AppSettings::remainingInteractionHints() const
{
return settings.value(KEY_REMAINING_INTERACTION_HINTS, 3).toInt();
}
void AppSettings::setRemainingInteractionHints(int remainingHints)
{
if (remainingInteractionHints() != remainingHints) {
LOG(KEY_REMAINING_INTERACTION_HINTS << remainingHints);
settings.setValue(KEY_REMAINING_INTERACTION_HINTS, remainingHints);
emit remainingInteractionHintsChanged();
}
}
bool AppSettings::onlineOnlyMode() const
{
return settings.value(KEY_ONLINE_ONLY_MODE, false).toBool();
}
void AppSettings::setOnlineOnlyMode(bool enable)
{
if (onlineOnlyMode() != enable) {
LOG(KEY_ONLINE_ONLY_MODE << enable);
settings.setValue(KEY_ONLINE_ONLY_MODE, enable);
emit onlineOnlyModeChanged();
}
}

View file

@ -24,12 +24,15 @@
class AppSettings : public QObject { class AppSettings : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool sendByEnter READ getSendByEnter WRITE setSendByEnter NOTIFY sendByEnterChanged) Q_PROPERTY(bool sendByEnter READ getSendByEnter WRITE setSendByEnter NOTIFY sendByEnterChanged)
Q_PROPERTY(bool focusTextAreaAfterSend READ getFocusTextAreaAfterSend WRITE setFocusTextAreaAfterSend NOTIFY focusTextAreaAfterSendChanged)
Q_PROPERTY(bool useOpenWith READ getUseOpenWith WRITE setUseOpenWith NOTIFY useOpenWithChanged) Q_PROPERTY(bool useOpenWith READ getUseOpenWith WRITE setUseOpenWith NOTIFY useOpenWithChanged)
Q_PROPERTY(bool showStickersAsImages READ showStickersAsImages WRITE setShowStickersAsImages NOTIFY showStickersAsImagesChanged) Q_PROPERTY(bool showStickersAsImages READ showStickersAsImages WRITE setShowStickersAsImages NOTIFY showStickersAsImagesChanged)
Q_PROPERTY(bool animateStickers READ animateStickers WRITE setAnimateStickers NOTIFY animateStickersChanged) Q_PROPERTY(bool animateStickers READ animateStickers WRITE setAnimateStickers NOTIFY animateStickersChanged)
Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged) Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged)
Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged) Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged)
Q_PROPERTY(bool storageOptimizer READ storageOptimizer WRITE setStorageOptimizer NOTIFY storageOptimizerChanged) Q_PROPERTY(bool storageOptimizer READ storageOptimizer WRITE setStorageOptimizer NOTIFY storageOptimizerChanged)
Q_PROPERTY(int remainingInteractionHints READ remainingInteractionHints WRITE setRemainingInteractionHints NOTIFY remainingInteractionHintsChanged)
Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged)
public: public:
enum NotificationFeedback { enum NotificationFeedback {
@ -45,6 +48,9 @@ public:
bool getSendByEnter() const; bool getSendByEnter() const;
void setSendByEnter(bool sendByEnter); void setSendByEnter(bool sendByEnter);
bool getFocusTextAreaAfterSend() const;
void setFocusTextAreaAfterSend(bool focusTextAreaAfterSend);
bool getUseOpenWith() const; bool getUseOpenWith() const;
void setUseOpenWith(bool useOpenWith); void setUseOpenWith(bool useOpenWith);
@ -63,14 +69,23 @@ public:
bool storageOptimizer() const; bool storageOptimizer() const;
void setStorageOptimizer(bool enable); void setStorageOptimizer(bool enable);
int remainingInteractionHints() const;
void setRemainingInteractionHints(int remainingHints);
bool onlineOnlyMode() const;
void setOnlineOnlyMode(bool enable);
signals: signals:
void sendByEnterChanged(); void sendByEnterChanged();
void focusTextAreaAfterSendChanged();
void useOpenWithChanged(); void useOpenWithChanged();
void showStickersAsImagesChanged(); void showStickersAsImagesChanged();
void animateStickersChanged(); void animateStickersChanged();
void notificationTurnsDisplayOnChanged(); void notificationTurnsDisplayOnChanged();
void notificationFeedbackChanged(); void notificationFeedbackChanged();
void storageOptimizerChanged(); void storageOptimizerChanged();
void remainingInteractionHintsChanged();
void onlineOnlyModeChanged();
private: private:
QSettings settings; QSettings settings;

View file

@ -19,6 +19,7 @@
#include "chatlistmodel.h" #include "chatlistmodel.h"
#include "fernschreiberutils.h" #include "fernschreiberutils.h"
#include <QListIterator>
#define DEBUG_MODULE ChatListModel #define DEBUG_MODULE ChatListModel
#include "debuglog.h" #include "debuglog.h"
@ -48,6 +49,7 @@ namespace {
const QString IS_CHANNEL("is_channel"); const QString IS_CHANNEL("is_channel");
const QString IS_VERIFIED("is_verified"); const QString IS_VERIFIED("is_verified");
const QString IS_MARKED_AS_UNREAD("is_marked_as_unread"); const QString IS_MARKED_AS_UNREAD("is_marked_as_unread");
const QString IS_PINNED("is_pinned");
const QString PINNED_MESSAGE_ID("pinned_message_id"); const QString PINNED_MESSAGE_ID("pinned_message_id");
const QString _TYPE("@type"); const QString _TYPE("@type");
const QString SECRET_CHAT_ID("secret_chat_id"); const QString SECRET_CHAT_ID("secret_chat_id");
@ -77,6 +79,7 @@ public:
bool isChannel() const; bool isChannel() const;
bool isHidden() const; bool isHidden() const;
bool isMarkedAsUnread() const; bool isMarkedAsUnread() const;
bool isPinned() const;
bool updateUnreadCount(int unreadCount); bool updateUnreadCount(int unreadCount);
bool updateLastReadInboxMessageId(qlonglong messageId); bool updateLastReadInboxMessageId(qlonglong messageId);
QVector<int> updateLastMessage(const QVariantMap &message); QVector<int> updateLastMessage(const QVariantMap &message);
@ -272,6 +275,11 @@ bool ChatListModel::ChatData::isMarkedAsUnread() const
return chatData.value(IS_MARKED_AS_UNREAD).toBool(); return chatData.value(IS_MARKED_AS_UNREAD).toBool();
} }
bool ChatListModel::ChatData::isPinned() const
{
return chatData.value(IS_PINNED).toBool();
}
bool ChatListModel::ChatData::updateUnreadCount(int count) bool ChatListModel::ChatData::updateUnreadCount(int count)
{ {
const int prevUnreadCount(unreadCount()); const int prevUnreadCount(unreadCount());
@ -346,9 +354,10 @@ QVector<int> ChatListModel::ChatData::updateSecretChat(const QVariantMap &secret
return changedRoles; return changedRoles;
} }
ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false) ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSettings) : showHiddenChats(false)
{ {
this->tdLibWrapper = tdLibWrapper; this->tdLibWrapper = tdLibWrapper;
this->appSettings = appSettings;
connect(tdLibWrapper, SIGNAL(newChatDiscovered(QString, QVariantMap)), this, SLOT(handleChatDiscovered(QString, QVariantMap))); connect(tdLibWrapper, SIGNAL(newChatDiscovered(QString, QVariantMap)), this, SLOT(handleChatDiscovered(QString, QVariantMap)));
connect(tdLibWrapper, SIGNAL(chatLastMessageUpdated(QString, QString, QVariantMap)), this, SLOT(handleChatLastMessageUpdated(QString, QString, QVariantMap))); connect(tdLibWrapper, SIGNAL(chatLastMessageUpdated(QString, QString, QVariantMap)), this, SLOT(handleChatLastMessageUpdated(QString, QString, QVariantMap)));
connect(tdLibWrapper, SIGNAL(chatOrderUpdated(QString, QString)), this, SLOT(handleChatOrderUpdated(QString, QString))); connect(tdLibWrapper, SIGNAL(chatOrderUpdated(QString, QString)), this, SLOT(handleChatOrderUpdated(QString, QString)));
@ -364,6 +373,7 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false
connect(tdLibWrapper, SIGNAL(secretChatReceived(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap))); connect(tdLibWrapper, SIGNAL(secretChatReceived(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap)));
connect(tdLibWrapper, SIGNAL(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString))); connect(tdLibWrapper, SIGNAL(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString)));
connect(tdLibWrapper, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)), this, SLOT(handleChatIsMarkedAsUnreadUpdated(qlonglong, bool))); connect(tdLibWrapper, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)), this, SLOT(handleChatIsMarkedAsUnreadUpdated(qlonglong, bool)));
connect(tdLibWrapper, SIGNAL(chatPinnedUpdated(qlonglong, bool)), this, SLOT(handleChatPinnedUpdated(qlonglong, bool)));
connect(tdLibWrapper, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SLOT(handleChatDraftMessageUpdated(qlonglong, QVariantMap, QString))); connect(tdLibWrapper, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SLOT(handleChatDraftMessageUpdated(qlonglong, QVariantMap, QString)));
// Don't start the timer until we have at least one chat // Don't start the timer until we have at least one chat
@ -405,6 +415,7 @@ QHash<int,QByteArray> ChatListModel::roleNames() const
roles.insert(ChatListModel::RoleIsVerified, "is_verified"); roles.insert(ChatListModel::RoleIsVerified, "is_verified");
roles.insert(ChatListModel::RoleIsChannel, "is_channel"); roles.insert(ChatListModel::RoleIsChannel, "is_channel");
roles.insert(ChatListModel::RoleIsMarkedAsUnread, "is_marked_as_unread"); roles.insert(ChatListModel::RoleIsMarkedAsUnread, "is_marked_as_unread");
roles.insert(ChatListModel::RoleIsPinned, "is_pinned");
roles.insert(ChatListModel::RoleFilter, "filter"); roles.insert(ChatListModel::RoleFilter, "filter");
roles.insert(ChatListModel::RoleDraftMessageDate, "draft_message_date"); roles.insert(ChatListModel::RoleDraftMessageDate, "draft_message_date");
roles.insert(ChatListModel::RoleDraftMessageText, "draft_message_text"); roles.insert(ChatListModel::RoleDraftMessageText, "draft_message_text");
@ -438,6 +449,7 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const
case ChatListModel::RoleIsVerified: return data->verified; case ChatListModel::RoleIsVerified: return data->verified;
case ChatListModel::RoleIsChannel: return data->isChannel(); case ChatListModel::RoleIsChannel: return data->isChannel();
case ChatListModel::RoleIsMarkedAsUnread: return data->isMarkedAsUnread(); case ChatListModel::RoleIsMarkedAsUnread: return data->isMarkedAsUnread();
case ChatListModel::RoleIsPinned: return data->isPinned();
case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed(); case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed();
case ChatListModel::RoleDraftMessageText: return data->draftMessageText(); case ChatListModel::RoleDraftMessageText: return data->draftMessageText();
case ChatListModel::RoleDraftMessageDate: return data->draftMessageDate(); case ChatListModel::RoleDraftMessageDate: return data->draftMessageDate();
@ -515,6 +527,26 @@ int ChatListModel::updateChatOrder(int chatIndex)
return newIndex; return newIndex;
} }
void ChatListModel::calculateUnreadState()
{
if (this->appSettings->onlineOnlyMode()) {
LOG("Online-only mode: Calculating unread state on my own...");
int unreadMessages = 0;
int unreadChats = 0;
QListIterator<ChatData*> chatIterator(this->chatList);
while (chatIterator.hasNext()) {
ChatData *currentChat = chatIterator.next();
int unreadCount = currentChat->unreadCount();
if (unreadCount > 0) {
unreadChats++;
unreadMessages += unreadCount;
}
}
LOG("Online-only mode: New unread state:" << unreadMessages << unreadChats);
emit unreadStateChanged(unreadMessages, unreadChats);
}
}
void ChatListModel::addVisibleChat(ChatData *chat) void ChatListModel::addVisibleChat(ChatData *chat)
{ {
const int n = chatList.size(); const int n = chatList.size();
@ -715,6 +747,7 @@ void ChatListModel::handleChatReadInboxUpdated(const QString &id, const QString
} }
const QModelIndex modelIndex(index(chatIndex)); const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles); emit dataChanged(modelIndex, modelIndex, changedRoles);
this->calculateUnreadState();
} else { } else {
ChatData *chat = hiddenChats.value(chatId); ChatData *chat = hiddenChats.value(chatId);
if (chat) { if (chat) {
@ -856,6 +889,26 @@ void ChatListModel::handleChatTitleUpdated(const QString &chatId, const QString
} }
} }
void ChatListModel::handleChatPinnedUpdated(qlonglong chatId, bool chatIsPinned)
{
if (chatIndexMap.contains(chatId)) {
LOG("Updating chat is pinned for" << chatId << chatIsPinned);
const int chatIndex = chatIndexMap.value(chatId);
ChatData *chat = chatList.at(chatIndex);
chat->chatData.insert(IS_PINNED, chatIsPinned);
QVector<int> changedRoles;
changedRoles.append(ChatListModel::RoleIsPinned);
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
} else {
ChatData *chat = hiddenChats.value(chatId);
if (chat) {
LOG("Updating chat is pinned for hidden chat" << chatId);
chat->chatData.insert(IS_PINNED, chatIsPinned);
}
}
}
void ChatListModel::handleChatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread) void ChatListModel::handleChatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread)
{ {
if (chatIndexMap.contains(chatId)) { if (chatIndexMap.contains(chatId)) {

View file

@ -22,6 +22,7 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include "tdlibwrapper.h" #include "tdlibwrapper.h"
#include "appsettings.h"
class ChatListModel : public QAbstractListModel class ChatListModel : public QAbstractListModel
{ {
@ -47,12 +48,13 @@ public:
RoleIsVerified, RoleIsVerified,
RoleIsChannel, RoleIsChannel,
RoleIsMarkedAsUnread, RoleIsMarkedAsUnread,
RoleIsPinned,
RoleFilter, RoleFilter,
RoleDraftMessageText, RoleDraftMessageText,
RoleDraftMessageDate RoleDraftMessageDate
}; };
ChatListModel(TDLibWrapper *tdLibWrapper); ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSettings);
~ChatListModel() override; ~ChatListModel() override;
virtual QHash<int,QByteArray> roleNames() const override; virtual QHash<int,QByteArray> roleNames() const override;
@ -64,6 +66,7 @@ public:
Q_INVOKABLE QVariantMap getById(qlonglong chatId); Q_INVOKABLE QVariantMap getById(qlonglong chatId);
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
Q_INVOKABLE void calculateUnreadState();
bool showAllChats() const; bool showAllChats() const;
void setShowAllChats(bool showAll); void setShowAllChats(bool showAll);
@ -81,6 +84,7 @@ private slots:
void handleGroupUpdated(qlonglong groupId); void handleGroupUpdated(qlonglong groupId);
void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat); void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
void handleChatTitleUpdated(const QString &chatId, const QString &title); void handleChatTitleUpdated(const QString &chatId, const QString &title);
void handleChatPinnedUpdated(qlonglong chatId, bool chatIsPinned);
void handleChatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread); void handleChatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
void handleChatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order); void handleChatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order);
void handleRelativeTimeRefreshTimer(); void handleRelativeTimeRefreshTimer();
@ -89,6 +93,7 @@ signals:
void showAllChatsChanged(); void showAllChatsChanged();
void chatChanged(const qlonglong &changedChatId); void chatChanged(const qlonglong &changedChatId);
void chatJoined(const qlonglong &chatId, const QString &chatTitle); void chatJoined(const qlonglong &chatId, const QString &chatTitle);
void unreadStateChanged(int unreadMessagesCount, int unreadChatsCount);
private: private:
class ChatData; class ChatData;
@ -99,6 +104,7 @@ private:
private: private:
TDLibWrapper *tdLibWrapper; TDLibWrapper *tdLibWrapper;
AppSettings *appSettings;
QTimer *relativeTimeRefreshTimer; QTimer *relativeTimeRefreshTimer;
QList<ChatData*> chatList; QList<ChatData*> chatList;
QHash<qlonglong,int> chatIndexMap; QHash<qlonglong,int> chatIndexMap;

View file

@ -54,8 +54,10 @@ public:
MessageData(const QVariantMap &data, qlonglong msgid); MessageData(const QVariantMap &data, qlonglong msgid);
static bool lessThan(const MessageData *message1, const MessageData *message2); static bool lessThan(const MessageData *message1, const MessageData *message2);
void setContent(const QVariantMap &content); QVector<int> diff(const MessageData *message) const;
void setReplyMarkup(const QVariantMap &replyMarkup); QVector<int> setMessageData(const QVariantMap &data);
QVector<int> setContent(const QVariantMap &content);
QVector<int> setReplyMarkup(const QVariantMap &replyMarkup);
int senderUserId() const; int senderUserId() const;
qlonglong senderChatId() const; qlonglong senderChatId() const;
bool senderIsChat() const; bool senderIsChat() const;
@ -63,7 +65,7 @@ public:
public: public:
QVariantMap messageData; QVariantMap messageData;
const qlonglong messageId; const qlonglong messageId;
const QString messageContentType; QString messageContentType;
}; };
ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) : ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) :
@ -88,13 +90,48 @@ bool ChatModel::MessageData::senderIsChat() const
return messageData.value(SENDER).toMap().value(_TYPE).toString() == "messageSenderChat"; return messageData.value(SENDER).toMap().value(_TYPE).toString() == "messageSenderChat";
} }
void ChatModel::MessageData::setContent(const QVariantMap &content) QVector<int> ChatModel::MessageData::diff(const MessageData *message) const
{ {
messageData.insert(CONTENT, content); QVector<int> roles;
if (message != this) {
roles.append(RoleDisplay);
if (message->messageId != messageId) {
roles.append(RoleMessageId);
} }
void ChatModel::MessageData::setReplyMarkup(const QVariantMap &replyMarkup) if (message->messageContentType != messageContentType) {
roles.append(RoleMessageContentType);
}
}
return roles;
}
QVector<int> ChatModel::MessageData::setMessageData(const QVariantMap &data)
{
messageData = data;
QVector<int> changedRoles;
changedRoles.append(RoleDisplay);
return changedRoles;
}
QVector<int> ChatModel::MessageData::setContent(const QVariantMap &content)
{
const QString oldContentType(messageContentType);
messageData.insert(CONTENT, content);
messageContentType = content.value(_TYPE).toString();
QVector<int> changedRoles;
if (oldContentType != messageContentType) {
changedRoles.append(RoleMessageContentType);
}
changedRoles.append(RoleDisplay);
return changedRoles;
}
QVector<int> ChatModel::MessageData::setReplyMarkup(const QVariantMap &replyMarkup)
{ {
messageData.insert(REPLY_MARKUP, replyMarkup); messageData.insert(REPLY_MARKUP, replyMarkup);
QVector<int> changedRoles;
changedRoles.append(RoleDisplay);
return changedRoles;
} }
bool ChatModel::MessageData::lessThan(const MessageData *message1, const MessageData *message2) bool ChatModel::MessageData::lessThan(const MessageData *message1, const MessageData *message2)
@ -105,7 +142,8 @@ bool ChatModel::MessageData::lessThan(const MessageData *message1, const Message
ChatModel::ChatModel(TDLibWrapper *tdLibWrapper) : ChatModel::ChatModel(TDLibWrapper *tdLibWrapper) :
chatId(0), chatId(0),
inReload(false), inReload(false),
inIncrementalUpdate(false) inIncrementalUpdate(false),
searchModeActive(false)
{ {
this->tdLibWrapper = tdLibWrapper; this->tdLibWrapper = tdLibWrapper;
connect(this->tdLibWrapper, SIGNAL(messagesReceived(QVariantList, int)), this, SLOT(handleMessagesReceived(QVariantList, int))); connect(this->tdLibWrapper, SIGNAL(messagesReceived(QVariantList, int)), this, SLOT(handleMessagesReceived(QVariantList, int)));
@ -370,11 +408,10 @@ void ChatModel::handleMessageReceived(qlonglong chatId, qlonglong messageId, con
if (chatId == this->chatId && messageIndexMap.contains(messageId)) { if (chatId == this->chatId && messageIndexMap.contains(messageId)) {
LOG("Received a message that we already know, let's update it!"); LOG("Received a message that we already know, let's update it!");
const int position = messageIndexMap.value(messageId); const int position = messageIndexMap.value(messageId);
MessageData *messageData = messages.at(position); const QVector<int> changedRoles(messages.at(position)->setMessageData(message));
messageData->messageData = message;
LOG("Message was updated at index" << position); LOG("Message was updated at index" << position);
const QModelIndex messageIndex(index(position)); const QModelIndex messageIndex(index(position));
emit dataChanged(messageIndex, messageIndex); emit dataChanged(messageIndex, messageIndex, changedRoles);
} }
} }
@ -405,11 +442,14 @@ void ChatModel::handleMessageSendSucceeded(qlonglong messageId, qlonglong oldMes
if (this->messageIndexMap.contains(oldMessageId)) { if (this->messageIndexMap.contains(oldMessageId)) {
LOG("Message was successfully sent" << oldMessageId); LOG("Message was successfully sent" << oldMessageId);
const int pos = messageIndexMap.take(oldMessageId); const int pos = messageIndexMap.take(oldMessageId);
delete messages.at(pos); MessageData* oldMessage = messages.at(pos);
messages.replace(pos, new MessageData(message, messageId)); MessageData* newMessage = new MessageData(message, messageId);
messages.replace(pos, newMessage);
const QVector<int> changedRoles(newMessage->diff(oldMessage));
delete oldMessage;
LOG("Message was replaced at index" << pos); LOG("Message was replaced at index" << pos);
const QModelIndex messageIndex(index(pos)); const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex); emit dataChanged(messageIndex, messageIndex, changedRoles);
emit lastReadSentMessageUpdated(calculateLastReadSentMessageId()); emit lastReadSentMessageUpdated(calculateLastReadSentMessageId());
} }
} }
@ -448,10 +488,10 @@ void ChatModel::handleMessageContentUpdated(qlonglong chatId, qlonglong messageI
LOG("We know the message that was updated" << messageId); LOG("We know the message that was updated" << messageId);
const int pos = messageIndexMap.value(messageId, -1); const int pos = messageIndexMap.value(messageId, -1);
if (pos >= 0) { if (pos >= 0) {
messages.at(pos)->setContent(newContent); const QVector<int> changedRoles(messages.at(pos)->setContent(newContent));
LOG("Message was replaced at index" << pos); LOG("Message was updated at index" << pos);
const QModelIndex messageIndex(index(pos)); const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex); emit dataChanged(messageIndex, messageIndex, changedRoles);
emit messageUpdated(pos); emit messageUpdated(pos);
} }
} }
@ -464,10 +504,10 @@ void ChatModel::handleMessageEditedUpdated(qlonglong chatId, qlonglong messageId
LOG("We know the message that was updated" << messageId); LOG("We know the message that was updated" << messageId);
const int pos = messageIndexMap.value(messageId, -1); const int pos = messageIndexMap.value(messageId, -1);
if (pos >= 0) { if (pos >= 0) {
messages.at(pos)->setReplyMarkup(replyMarkup); const QVector<int> changedRoles(messages.at(pos)->setReplyMarkup(replyMarkup));
LOG("Message was edited at index" << pos); LOG("Message was edited at index" << pos);
const QModelIndex messageIndex(index(pos)); const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex); emit dataChanged(messageIndex, messageIndex, changedRoles);
emit messageUpdated(pos); emit messageUpdated(pos);
} }
} }

View file

@ -1,11 +1,74 @@
#include "fernschreiberutils.h" /*
Copyright (C) 2020-21 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/>.
*/
#include "fernschreiberutils.h"
#include <QMap> #include <QMap>
#include <QVariant> #include <QVariant>
#include <QAudioEncoderSettings>
#include <QStandardPaths>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QUrl>
#include <QDateTime>
#include <QGeoCoordinate>
#include <QGeoLocation>
#define DEBUG_MODULE FernschreiberUtils
#include "debuglog.h"
FernschreiberUtils::FernschreiberUtils(QObject *parent) : QObject(parent) FernschreiberUtils::FernschreiberUtils(QObject *parent) : QObject(parent)
{ {
LOG("Initializing audio recorder...");
QString temporaryDirectoryPath = this->getTemporaryDirectoryPath();
QDir temporaryDirectory(temporaryDirectoryPath);
if (!temporaryDirectory.exists()) {
temporaryDirectory.mkpath(temporaryDirectoryPath);
}
QAudioEncoderSettings encoderSettings;
encoderSettings.setCodec("audio/vorbis");
encoderSettings.setChannelCount(1);
encoderSettings.setQuality(QMultimedia::LowQuality);
this->audioRecorder.setEncodingSettings(encoderSettings);
this->audioRecorder.setContainerFormat("ogg");
QMediaRecorder::Status audioRecorderStatus = this->audioRecorder.status();
this->handleAudioRecorderStatusChanged(audioRecorderStatus);
connect(&audioRecorder, SIGNAL(durationChanged(qlonglong)), this, SIGNAL(voiceNoteDurationChanged(qlonglong)));
connect(&audioRecorder, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SLOT(handleAudioRecorderStatusChanged(QMediaRecorder::Status)));
this->geoPositionInfoSource = QGeoPositionInfoSource::createDefaultSource(this);
if (this->geoPositionInfoSource) {
LOG("Geolocation successfully initialized...");
this->geoPositionInfoSource->setUpdateInterval(5000);
connect(geoPositionInfoSource, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(handleGeoPositionUpdated(QGeoPositionInfo)));
} else {
LOG("Unable to initialize geolocation!");
}
}
FernschreiberUtils::~FernschreiberUtils()
{
this->cleanUp();
} }
QString FernschreiberUtils::getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender) QString FernschreiberUtils::getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender)
@ -128,3 +191,117 @@ QString FernschreiberUtils::getUserName(const QVariantMap &userInformation)
const QString lastName = userInformation.value("last_name").toString(); const QString lastName = userInformation.value("last_name").toString();
return QString(firstName + " " + lastName).trimmed(); return QString(firstName + " " + lastName).trimmed();
} }
void FernschreiberUtils::startRecordingVoiceNote()
{
LOG("Start recording voice note...");
QDateTime thisIsNow = QDateTime::currentDateTime();
this->audioRecorder.setOutputLocation(QUrl::fromLocalFile(this->getTemporaryDirectoryPath() + "/voicenote-" + thisIsNow.toString("yyyy-MM-dd-HH-mm-ss") + ".ogg"));
this->audioRecorder.setVolume(1);
this->audioRecorder.record();
}
void FernschreiberUtils::stopRecordingVoiceNote()
{
LOG("Stop recording voice note...");
this->audioRecorder.stop();
}
QString FernschreiberUtils::voiceNotePath()
{
return this->audioRecorder.outputLocation().toLocalFile();
}
FernschreiberUtils::VoiceNoteRecordingState FernschreiberUtils::getVoiceNoteRecordingState()
{
return this->voiceNoteRecordingState;
}
void FernschreiberUtils::startGeoLocationUpdates()
{
if (this->geoPositionInfoSource) {
this->geoPositionInfoSource->startUpdates();
}
}
void FernschreiberUtils::stopGeoLocationUpdates()
{
if (this->geoPositionInfoSource) {
this->geoPositionInfoSource->stopUpdates();
}
}
bool FernschreiberUtils::supportsGeoLocation()
{
return this->geoPositionInfoSource;
}
void FernschreiberUtils::handleAudioRecorderStatusChanged(QMediaRecorder::Status status)
{
LOG("Audio recorder status changed:" << status);
switch (status) {
case QMediaRecorder::UnavailableStatus:
case QMediaRecorder::UnloadedStatus:
case QMediaRecorder::LoadingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Unavailable;
break;
case QMediaRecorder::LoadedStatus:
case QMediaRecorder::PausedStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Ready;
break;
case QMediaRecorder::StartingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Starting;
break;
case QMediaRecorder::FinalizingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Stopping;
break;
case QMediaRecorder::RecordingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Recording;
break;
}
emit voiceNoteRecordingStateChanged(this->voiceNoteRecordingState);
}
void FernschreiberUtils::handleGeoPositionUpdated(const QGeoPositionInfo &info)
{
LOG("Geo position was updated");
QVariantMap positionInformation;
if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) {
positionInformation.insert("horizontalAccuracy", info.attribute(QGeoPositionInfo::HorizontalAccuracy));
} else {
positionInformation.insert("horizontalAccuracy", 0);
}
if (info.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
positionInformation.insert("verticalAccuracy", info.attribute(QGeoPositionInfo::VerticalAccuracy));
} else {
positionInformation.insert("verticalAccuracy", 0);
}
QGeoCoordinate geoCoordinate = info.coordinate();
positionInformation.insert("latitude", geoCoordinate.latitude());
positionInformation.insert("longitude", geoCoordinate.longitude());
emit newPositionInformation(positionInformation);
}
void FernschreiberUtils::cleanUp()
{
if (this->geoPositionInfoSource) {
this->geoPositionInfoSource->stopUpdates();
}
QString temporaryDirectoryPath = this->getTemporaryDirectoryPath();
QDirIterator temporaryDirectoryIterator(temporaryDirectoryPath, QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks, QDirIterator::Subdirectories);
while (temporaryDirectoryIterator.hasNext()) {
QString nextFilePath = temporaryDirectoryIterator.next();
if (QFile::remove(nextFilePath)) {
LOG("Temporary file removed " << nextFilePath);
} else {
LOG("Error removing temporary file " << nextFilePath);
}
}
}
QString FernschreiberUtils::getTemporaryDirectoryPath()
{
return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + + "/harbour-fernschreiber";
}

View file

@ -1,7 +1,29 @@
/*
Copyright (C) 2020-21 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/>.
*/
#ifndef FERNSCHREIBERUTILS_H #ifndef FERNSCHREIBERUTILS_H
#define FERNSCHREIBERUTILS_H #define FERNSCHREIBERUTILS_H
#include <QObject> #include <QObject>
#include <QAudioRecorder>
#include <QGeoPositionInfo>
#include <QGeoPositionInfoSource>
#include "tdlibwrapper.h" #include "tdlibwrapper.h"
class FernschreiberUtils : public QObject class FernschreiberUtils : public QObject
@ -9,13 +31,46 @@ class FernschreiberUtils : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit FernschreiberUtils(QObject *parent = nullptr); explicit FernschreiberUtils(QObject *parent = nullptr);
~FernschreiberUtils();
enum VoiceNoteRecordingState {
Unavailable,
Ready,
Starting,
Recording,
Stopping
};
Q_ENUM(VoiceNoteRecordingState)
static QString getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender); static QString getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender);
static QString getUserName(const QVariantMap &userInformation); static QString getUserName(const QVariantMap &userInformation);
signals: Q_INVOKABLE void startRecordingVoiceNote();
Q_INVOKABLE void stopRecordingVoiceNote();
Q_INVOKABLE QString voiceNotePath();
Q_INVOKABLE VoiceNoteRecordingState getVoiceNoteRecordingState();
Q_INVOKABLE void startGeoLocationUpdates();
Q_INVOKABLE void stopGeoLocationUpdates();
Q_INVOKABLE bool supportsGeoLocation();
signals:
void voiceNoteDurationChanged(qlonglong duration);
void voiceNoteRecordingStateChanged(VoiceNoteRecordingState state);
void newPositionInformation(const QVariantMap &positionInformation);
private slots:
void handleAudioRecorderStatusChanged(QMediaRecorder::Status status);
void handleGeoPositionUpdated(const QGeoPositionInfo &info);
private:
QAudioRecorder audioRecorder;
VoiceNoteRecordingState voiceNoteRecordingState;
QGeoPositionInfoSource *geoPositionInfoSource;
void cleanUp();
QString getTemporaryDirectoryPath();
public slots:
}; };
#endif // FERNSCHREIBERUTILS_H #endif // FERNSCHREIBERUTILS_H

View file

@ -82,11 +82,12 @@ int main(int argc, char *argv[])
FernschreiberUtils *fernschreiberUtils = new FernschreiberUtils(view.data()); FernschreiberUtils *fernschreiberUtils = new FernschreiberUtils(view.data());
context->setContextProperty("fernschreiberUtils", fernschreiberUtils); context->setContextProperty("fernschreiberUtils", fernschreiberUtils);
qmlRegisterUncreatableType<FernschreiberUtils>(uri, 1, 0, "FernschreiberUtilities", QString());
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor(); DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
context->setContextProperty("dBusAdaptor", dBusAdaptor); context->setContextProperty("dBusAdaptor", dBusAdaptor);
ChatListModel chatListModel(tdLibWrapper); ChatListModel chatListModel(tdLibWrapper, appSettings);
context->setContextProperty("chatListModel", &chatListModel); context->setContextProperty("chatListModel", &chatListModel);
QSortFilterProxyModel chatListProxyModel(view.data()); QSortFilterProxyModel chatListProxyModel(view.data());
chatListProxyModel.setSourceModel(&chatListModel); chatListProxyModel.setSourceModel(&chatListModel);

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2020 Slava Monich at al. Copyright (C) 2020-2021 Slava Monich at al.
This file is part of Fernschreiber. This file is part of Fernschreiber.
@ -107,6 +107,7 @@ void TDLibFile::init()
queuedSignals = 0; queuedSignals = 0;
firstQueuedSignal = SignalCount; firstQueuedSignal = SignalCount;
autoLoad = false; autoLoad = false;
downloadHoldOffTimer = 0;
id = 0; id = 0;
expected_size = 0; expected_size = 0;
size = 0; size = 0;
@ -174,7 +175,7 @@ void TDLibFile::updateTDLibWrapper(TDLibWrapper* tdlib)
if (tdlib) { if (tdlib) {
connect(tdlib, SIGNAL(fileUpdated(int,QVariantMap)), SLOT(handleFileUpdated(int,QVariantMap))); connect(tdlib, SIGNAL(fileUpdated(int,QVariantMap)), SLOT(handleFileUpdated(int,QVariantMap)));
if (autoLoad) { if (autoLoad) {
load(); downloadFile();
} }
} }
queueSignal(SignalTdLibChanged); queueSignal(SignalTdLibChanged);
@ -187,7 +188,7 @@ void TDLibFile::setAutoLoad(bool enableAutoLoad)
autoLoad = enableAutoLoad; autoLoad = enableAutoLoad;
queueSignal(SignalAutoLoadChanged); queueSignal(SignalAutoLoadChanged);
if (autoLoad) { if (autoLoad) {
load(); downloadFile();
} }
emitQueuedSignals(); emitQueuedSignals();
} }
@ -195,7 +196,19 @@ void TDLibFile::setAutoLoad(bool enableAutoLoad)
bool TDLibFile::load() bool TDLibFile::load()
{ {
if (id && tdLibWrapper && !is_downloading_active && !is_downloading_completed && can_be_downloaded) { // Manual load ignores hold-off timer
if (downloadHoldOffTimer) {
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
}
return downloadFile();
}
bool TDLibFile::downloadFile()
{
if (id && tdLibWrapper && !downloadHoldOffTimer &&
!is_downloading_active && !is_downloading_completed && can_be_downloaded) {
downloadHoldOffTimer = startTimer(DownloadHoldOffMs);
tdLibWrapper->downloadFile(id); tdLibWrapper->downloadFile(id);
return true; return true;
} }
@ -205,12 +218,26 @@ bool TDLibFile::load()
void TDLibFile::setFileInfo(const QVariantMap &fileInfo) void TDLibFile::setFileInfo(const QVariantMap &fileInfo)
{ {
updateFileInfo(fileInfo); updateFileInfo(fileInfo);
if (is_downloading_completed && downloadHoldOffTimer) {
// Don't need this timer anymore
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
}
if (autoLoad) { if (autoLoad) {
load(); downloadFile();
} }
emitQueuedSignals(); emitQueuedSignals();
} }
void TDLibFile::timerEvent(QTimerEvent *)
{
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
if (autoLoad) {
downloadFile();
}
}
void TDLibFile::handleFileUpdated(int fileId, const QVariantMap &fileInfo) void TDLibFile::handleFileUpdated(int fileId, const QVariantMap &fileInfo)
{ {
if (id == fileId) { if (id == fileId) {
@ -256,6 +283,7 @@ void TDLibFile::updateFileInfo(const QVariantMap &file)
bool b, fileChanged = false; bool b, fileChanged = false;
int i = file.value(ID).toInt(); int i = file.value(ID).toInt();
if (id != i) { if (id != i) {
LOG("File id has changed" << id << "=>" << i);
id = i; id = i;
fileChanged = true; fileChanged = true;
queueSignal(SignalIdChanged); queueSignal(SignalIdChanged);

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2020 Slava Monich at al. Copyright (C) 2020-2021 Slava Monich at al.
This file is part of Fernschreiber. This file is part of Fernschreiber.
@ -45,6 +45,8 @@ class TDLibFile : public QObject
Q_PROPERTY(bool isUploadingActive READ isUploadingActive NOTIFY uploadingActiveChanged) Q_PROPERTY(bool isUploadingActive READ isUploadingActive NOTIFY uploadingActiveChanged)
Q_PROPERTY(bool isUploadingCompleted READ isUploadingCompleted NOTIFY uploadingCompletedChanged) Q_PROPERTY(bool isUploadingCompleted READ isUploadingCompleted NOTIFY uploadingCompletedChanged)
enum { DownloadHoldOffMs = 1000 };
public: public:
TDLibFile(); TDLibFile();
TDLibFile(TDLibWrapper *tdlib); TDLibFile(TDLibWrapper *tdlib);
@ -102,10 +104,14 @@ signals:
private slots: private slots:
void handleFileUpdated(int fileId, const QVariantMap &fileInfo); void handleFileUpdated(int fileId, const QVariantMap &fileInfo);
protected:
void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
private: private:
void init(); void init();
void updateTDLibWrapper(TDLibWrapper* tdlib); void updateTDLibWrapper(TDLibWrapper* tdlib);
void updateFileInfo(const QVariantMap &fileInfo); void updateFileInfo(const QVariantMap &fileInfo);
bool downloadFile();
void queueSignal(uint signal); void queueSignal(uint signal);
void emitQueuedSignals(); void emitQueuedSignals();
@ -114,6 +120,7 @@ private:
uint queuedSignals; uint queuedSignals;
uint firstQueuedSignal; uint firstQueuedSignal;
bool autoLoad; bool autoLoad;
int downloadHoldOffTimer;
// file // file
QVariantMap infoMap; QVariantMap infoMap;
int id; int id;

View file

@ -38,6 +38,7 @@ namespace {
const QString POSITIONS("positions"); const QString POSITIONS("positions");
const QString PHOTO("photo"); const QString PHOTO("photo");
const QString ORDER("order"); const QString ORDER("order");
const QString IS_PINNED("is_pinned");
const QString BASIC_GROUP("basic_group"); const QString BASIC_GROUP("basic_group");
const QString SUPERGROUP("supergroup"); const QString SUPERGROUP("supergroup");
const QString LAST_MESSAGE("last_message"); const QString LAST_MESSAGE("last_message");
@ -287,9 +288,20 @@ void TDLibReceiver::processUpdateChatOrder(const QVariantMap &receivedInformatio
void TDLibReceiver::processUpdateChatPosition(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateChatPosition(const QVariantMap &receivedInformation)
{ {
const QString chat_id(receivedInformation.value(CHAT_ID).toString()); const QString chat_id(receivedInformation.value(CHAT_ID).toString());
const QString order(receivedInformation.value(POSITION).toMap().value(ORDER).toString()); QVariantMap positionMap = receivedInformation.value(POSITION).toMap();
LOG("Chat position updated for ID" << chat_id << "new order" << order);
QString updateForChatList = positionMap.value(LIST).toMap().value(TYPE).toString();
const QString order(positionMap.value(ORDER).toString());
bool is_pinned = positionMap.value(IS_PINNED).toBool();
// We are only processing main chat list updates at the moment...
if (updateForChatList == "chatListMain") {
LOG("Chat position updated for ID" << chat_id << "new order" << order << "is pinned" << is_pinned);
emit chatOrderUpdated(chat_id, order); emit chatOrderUpdated(chat_id, order);
emit chatPinnedUpdated(chat_id.toLongLong(), is_pinned);
} else {
LOG("Received chat position update for uninteresting list" << updateForChatList << "ID" << chat_id << "new order" << order << "is pinned" << is_pinned);
}
} }
void TDLibReceiver::processUpdateChatReadInbox(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateChatReadInbox(const QVariantMap &receivedInformation)

View file

@ -49,6 +49,7 @@ signals:
void unreadChatCountUpdated(const QVariantMap &chatCountInformation); void unreadChatCountUpdated(const QVariantMap &chatCountInformation);
void chatLastMessageUpdated(const QString &chatId, const QString &order, const QVariantMap &lastMessage); void chatLastMessageUpdated(const QString &chatId, const QString &order, const QVariantMap &lastMessage);
void chatOrderUpdated(const QString &chatId, const QString &order); void chatOrderUpdated(const QString &chatId, const QString &order);
void chatPinnedUpdated(qlonglong chatId, bool isPinned);
void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount); void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount);
void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId); void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId);
void basicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation); void basicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation);

View file

@ -47,8 +47,10 @@ namespace {
const QString USERNAME("username"); const QString USERNAME("username");
const QString THREAD_ID("thread_id"); const QString THREAD_ID("thread_id");
const QString VALUE("value"); const QString VALUE("value");
const QString CHAT_LIST_TYPE("chat_list_type");
const QString _TYPE("@type"); const QString _TYPE("@type");
const QString _EXTRA("@extra"); const QString _EXTRA("@extra");
const QString CHAT_LIST_MAIN("chatListMain");
} }
TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface, QObject *parent) : QObject(parent), joinChatRequested(false) TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface, QObject *parent) : QObject(parent), joinChatRequested(false)
@ -145,6 +147,7 @@ void TDLibWrapper::initializeTDLibReciever() {
connect(this->tdLibReceiver, SIGNAL(chatPermissionsUpdated(QString, QVariantMap)), this, SIGNAL(chatPermissionsUpdated(QString, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(chatPermissionsUpdated(QString, QVariantMap)), this, SIGNAL(chatPermissionsUpdated(QString, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)), this, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)), this, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(chatTitleUpdated(QString, QString)), this, SIGNAL(chatTitleUpdated(QString, QString))); connect(this->tdLibReceiver, SIGNAL(chatTitleUpdated(QString, QString)), this, SIGNAL(chatTitleUpdated(QString, QString)));
connect(this->tdLibReceiver, SIGNAL(chatPinnedUpdated(qlonglong, bool)), this, SIGNAL(chatPinnedUpdated(qlonglong, bool)));
connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong))); connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)));
connect(this->tdLibReceiver, SIGNAL(messageIsPinnedUpdated(qlonglong, qlonglong, bool)), this, SLOT(handleMessageIsPinnedUpdated(qlonglong, qlonglong, bool))); connect(this->tdLibReceiver, SIGNAL(messageIsPinnedUpdated(qlonglong, qlonglong, bool)), this, SLOT(handleMessageIsPinnedUpdated(qlonglong, qlonglong, bool)));
connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int))); connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int)));
@ -491,6 +494,56 @@ void TDLibWrapper::sendDocumentMessage(const QString &chatId, const QString &fil
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendVoiceNoteMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId)
{
LOG("Sending voice note message" << chatId << filePath << message << replyToMessageId);
QVariantMap requestObject;
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageVoiceNote");
QVariantMap formattedText;
formattedText.insert("text", message);
formattedText.insert(_TYPE, "formattedText");
inputMessageContent.insert("caption", formattedText);
QVariantMap documentInputFile;
documentInputFile.insert(_TYPE, "inputFileLocal");
documentInputFile.insert("path", filePath);
inputMessageContent.insert("voice_note", documentInputFile);
requestObject.insert("input_message_content", inputMessageContent);
this->sendRequest(requestObject);
}
void TDLibWrapper::sendLocationMessage(const QString &chatId, double latitude, double longitude, double horizontalAccuracy, const QString &replyToMessageId)
{
LOG("Sending location message" << chatId << latitude << longitude << horizontalAccuracy << replyToMessageId);
QVariantMap requestObject;
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageLocation");
QVariantMap location;
location.insert("latitude", latitude);
location.insert("longitude", longitude);
location.insert("horizontal_accuracy", horizontalAccuracy);
location.insert(_TYPE, "location");
inputMessageContent.insert("location", location);
inputMessageContent.insert("live_period", 0);
inputMessageContent.insert("heading", 0);
inputMessageContent.insert("proximity_alert_radius", 0);
requestObject.insert("input_message_content", inputMessageContent);
this->sendRequest(requestObject);
}
void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId) void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId)
{ {
LOG("Sending sticker message" << chatId << fileId << replyToMessageId); LOG("Sending sticker message" << chatId << fileId << replyToMessageId);
@ -1002,6 +1055,20 @@ void TDLibWrapper::toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsU
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::toggleChatIsPinned(qlonglong chatId, bool isPinned)
{
LOG("Toggle chat is pinned" << chatId << isPinned);
QVariantMap requestObject;
requestObject.insert(_TYPE, "toggleChatIsPinned");
QVariantMap chatListMap;
chatListMap.insert(_TYPE, CHAT_LIST_MAIN);
requestObject.insert("chat_list", chatListMap);
requestObject.insert(CHAT_ID, chatId);
requestObject.insert("is_pinned", isPinned);
requestObject.insert("is_marked_as_unread", isPinned);
this->sendRequest(requestObject);
}
void TDLibWrapper::setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &draft) void TDLibWrapper::setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &draft)
{ {
LOG("Set Draft Message" << chatId); LOG("Set Draft Message" << chatId);
@ -1334,7 +1401,7 @@ void TDLibWrapper::handleChatReceived(const QVariantMap &chatInformation)
void TDLibWrapper::handleUnreadMessageCountUpdated(const QVariantMap &messageCountInformation) void TDLibWrapper::handleUnreadMessageCountUpdated(const QVariantMap &messageCountInformation)
{ {
if (messageCountInformation.value("chat_list_type").toString() == "chatListMain") { if (messageCountInformation.value(CHAT_LIST_TYPE).toString() == CHAT_LIST_MAIN) {
this->unreadMessageInformation = messageCountInformation; this->unreadMessageInformation = messageCountInformation;
emit unreadMessageCountUpdated(messageCountInformation); emit unreadMessageCountUpdated(messageCountInformation);
} }
@ -1342,7 +1409,7 @@ void TDLibWrapper::handleUnreadMessageCountUpdated(const QVariantMap &messageCou
void TDLibWrapper::handleUnreadChatCountUpdated(const QVariantMap &chatCountInformation) void TDLibWrapper::handleUnreadChatCountUpdated(const QVariantMap &chatCountInformation)
{ {
if (chatCountInformation.value("chat_list_type").toString() == "chatListMain") { if (chatCountInformation.value(CHAT_LIST_TYPE).toString() == CHAT_LIST_MAIN) {
this->unreadChatInformation = chatCountInformation; this->unreadChatInformation = chatCountInformation;
emit unreadChatCountUpdated(chatCountInformation); emit unreadChatCountUpdated(chatCountInformation);
} }
@ -1449,15 +1516,16 @@ void TDLibWrapper::setInitialParameters()
initialParameters.insert("api_id", TDLIB_API_ID); initialParameters.insert("api_id", TDLIB_API_ID);
initialParameters.insert("api_hash", TDLIB_API_HASH); initialParameters.insert("api_hash", TDLIB_API_HASH);
initialParameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib"); initialParameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib");
initialParameters.insert("use_file_database", true); bool onlineOnlyMode = this->appSettings->onlineOnlyMode();
initialParameters.insert("use_chat_info_database", true); initialParameters.insert("use_file_database", !onlineOnlyMode);
initialParameters.insert("use_message_database", true); initialParameters.insert("use_chat_info_database", !onlineOnlyMode);
initialParameters.insert("use_message_database", !onlineOnlyMode);
initialParameters.insert("use_secret_chats", true); initialParameters.insert("use_secret_chats", true);
initialParameters.insert("system_language_code", QLocale::system().name()); initialParameters.insert("system_language_code", QLocale::system().name());
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat); QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString()); initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
initialParameters.insert("system_version", QSysInfo::prettyProductName()); initialParameters.insert("system_version", QSysInfo::prettyProductName());
initialParameters.insert("application_version", "0.6"); initialParameters.insert("application_version", "0.7");
initialParameters.insert("enable_storage_optimizer", appSettings->storageOptimizer()); initialParameters.insert("enable_storage_optimizer", appSettings->storageOptimizer());
// initialParameters.insert("use_test_dc", true); // initialParameters.insert("use_test_dc", true);
requestObject.insert("parameters", initialParameters); requestObject.insert("parameters", initialParameters);

View file

@ -142,6 +142,8 @@ public:
Q_INVOKABLE void sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
Q_INVOKABLE void sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
Q_INVOKABLE void sendDocumentMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendDocumentMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
Q_INVOKABLE void sendVoiceNoteMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
Q_INVOKABLE void sendLocationMessage(const QString &chatId, double latitude, double longitude, double horizontalAccuracy, const QString &replyToMessageId = "0");
Q_INVOKABLE void sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId = "0");
Q_INVOKABLE void sendPollMessage(const QString &chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendPollMessage(const QString &chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &replyToMessageId = "0");
Q_INVOKABLE void forwardMessages(const QString &chatId, const QString &fromChatId, const QVariantList &messageIds, bool sendCopy, bool removeCaption); Q_INVOKABLE void forwardMessages(const QString &chatId, const QString &fromChatId, const QVariantList &messageIds, bool sendCopy, bool removeCaption);
@ -186,6 +188,7 @@ public:
Q_INVOKABLE void searchPublicChats(const QString &query); Q_INVOKABLE void searchPublicChats(const QString &query);
Q_INVOKABLE void readAllChatMentions(qlonglong chatId); Q_INVOKABLE void readAllChatMentions(qlonglong chatId);
Q_INVOKABLE void toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread); Q_INVOKABLE void toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread);
Q_INVOKABLE void toggleChatIsPinned(qlonglong chatId, bool isPinned);
Q_INVOKABLE void setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &draft); Q_INVOKABLE void setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &draft);
// Others (candidates for extraction ;)) // Others (candidates for extraction ;))
@ -211,6 +214,7 @@ signals:
void unreadChatCountUpdated(const QVariantMap &chatCountInformation); void unreadChatCountUpdated(const QVariantMap &chatCountInformation);
void chatLastMessageUpdated(const QString &chatId, const QString &order, const QVariantMap &lastMessage); void chatLastMessageUpdated(const QString &chatId, const QString &order, const QVariantMap &lastMessage);
void chatOrderUpdated(const QString &chatId, const QString &order); void chatOrderUpdated(const QString &chatId, const QString &order);
void chatPinnedUpdated(qlonglong chatId, bool isPinned);
void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount); void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount);
void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId); void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId);
void userUpdated(const QString &userId, const QVariantMap &userInformation); void userUpdated(const QString &userId, const QVariantMap &userInformation);

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Verlasse Chat</translation> <translation>Verlasse Chat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Stummschaltung des Chats aufheben</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Chat stummschalten</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Unbekannt</translation> <translation>Unbekannt</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Neuer geheimer Chat</translation> <translation>Neuer geheimer Chat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Stummschaltung des Chats aufheben</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Chat stummschalten</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source> <source>You</source>
<translation>Sie</translation> <translation>Sie</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Stummschaltung des Chats aufheben</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Chat stummschalten</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Benutzerinfos</translation> <translation>Benutzerinfos</translation>
@ -284,6 +276,22 @@
<source>Draft</source> <source>Draft</source>
<translation>Entwurf</translation> <translation>Entwurf</translation>
</message> </message>
<message>
<source>Unpin chat</source>
<translation>Chat losheften</translation>
</message>
<message>
<source>Pin chat</source>
<translation>Chat anheften</translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Stummschaltung des Chats aufheben</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Chat stummschalten</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source> <source>Search in chat...</source>
<translation>Im Chat suchen...</translation> <translation>Im Chat suchen...</translation>
</message> </message>
<message>
<source>Location: Obtaining position...</source>
<translation>Standort: Erlange Position...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Standort (%1/%2)</translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Dokument öffnen</translation> <translation>Dokument öffnen</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation>Dokument zu Downloads kopieren</translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -1085,8 +1105,16 @@
<translation>Download fehlgeschlagen.</translation> <translation>Download fehlgeschlagen.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Tap on the title bar to filter your chats</source>
<translation type="unfinished">Verbinde zum Netzwerk...</translation> <translation>Tippen Sie auf die Titelleiste, um Ihre Chats zu filtern</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>Keine passenden Chats gefunden.</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Sie können über das Pull-Down-Menü öffentliche Chats finden oder einen Neuen erstellen.</translation>
</message> </message>
<message> <message>
<source>Logging out</source> <source>Logging out</source>
@ -1308,13 +1336,19 @@
<source>Channel</source> <source>Channel</source>
<translation>Kanal</translation> <translation>Kanal</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 Mitglied</translation> <translation>
<numerusform>%1 Mitglied</numerusform>
<numerusform>%1 Mitglieder</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 Abonnent</translation> <translation>
<numerusform>%1 Abonnent</numerusform>
<numerusform>%1 Abonnenten</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Speicheroptimierer einschalten</translation> <translation>Speicheroptimierer einschalten</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Texteingabe nach Senden fokussieren</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Fokussiert die Texteingabe nach Senden einer Nachricht</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation>Nur-Online-Modus einschalten</translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>Schaltet das Offline-Caching aus. Bestimmte Features können in diesem Modus eingeschränkt sein oder fehlen. Änderungen erfordern einen Neustart von Fernschreiber, um in Kraft zu treten.</translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Download fehlgeschlagen.</translation> <translation>Download fehlgeschlagen.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Eine Sprachnachricht aufzeichnen</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Drücken Sie den Knopf, um die Aufzeichnung zu starten</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Nicht verfügbar</translation>
</message>
<message>
<source>Ready</source>
<translation>Bereit</translation>
</message>
<message>
<source>Starting</source>
<translation>Startet</translation>
</message>
<message>
<source>Recording</source>
<translation>Zeichnet auf</translation>
</message>
<message>
<source>Stopping</source>
<translation>Stoppt</translation>
</message>
<message>
<source>Use recording</source>
<translation>Aufzeichnung verwenden</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Sprachnachricht (%1)</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Leaving chat</translation> <translation>Leaving chat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Unknown</translation> <translation>Unknown</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>New Secret Chat</translation> <translation>New Secret Chat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source> <source>You</source>
<translation>You</translation> <translation>You</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>User Info</translation> <translation>User Info</translation>
@ -284,6 +276,22 @@
<source>Draft</source> <source>Draft</source>
<translation>Draft</translation> <translation>Draft</translation>
</message> </message>
<message>
<source>Unpin chat</source>
<translation>Unpin chat</translation>
</message>
<message>
<source>Pin chat</source>
<translation>Pin chat</translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Unmute chat</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Mute chat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source> <source>Search in chat...</source>
<translation>Search in chat...</translation> <translation>Search in chat...</translation>
</message> </message>
<message>
<source>Location: Obtaining position...</source>
<translation>Location: Obtaining position...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Location (%1/%2)</translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Open Document</translation> <translation>Open Document</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation>Copy Document to Downloads</translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -1085,12 +1105,20 @@
<translation>Download failed.</translation> <translation>Download failed.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Tap on the title bar to filter your chats</source>
<translation type="unfinished">Connecting to network...</translation> <translation>Tap on the title bar to filter your chats</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>No matching chats found.</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>You can search public chats or create a new chat via the pull-down menu.</translation>
</message> </message>
<message> <message>
<source>Logging out</source> <source>Logging out</source>
<translation type="unfinished"></translation> <translation >Logging out</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1308,13 +1336,19 @@
<source>Channel</source> <source>Channel</source>
<translation>Channel</translation> <translation>Channel</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 member</translation> <translation>
<numerusform>%1 member</numerusform>
<numerusform>%1 members</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 subscriber</translation> <translation>
<numerusform>%1 subscriber</numerusform>
<numerusform>%1 subscribers</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Enable storage optimizer</translation> <translation>Enable storage optimizer</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Focus text input area after send</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Focus the text input area after sending a message</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation>Enable online-only mode</translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Download failed.</translation> <translation>Download failed.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Record a Voice Note</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Press the button to start recording</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Unavailable</translation>
</message>
<message>
<source>Starting</source>
<translation>Starting</translation>
</message>
<message>
<source>Recording</source>
<translation>Recording</translation>
</message>
<message>
<source>Stopping</source>
<translation>Stopping</translation>
</message>
<message>
<source>Use recording</source>
<translation>Use recording</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Voice Note (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Ready</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1815,21 +1904,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished">has added %1 to the chat</translation> <translation>has added %1 to the chat</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished">has removed %1 from the chat</translation> <translation>has removed %1 from the chat</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished">have added %1 to the chat</translation> <translation>have added %1 to the chat</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished">have removed %1 from the chat</translation> <translation>have removed %1 from the chat</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -129,14 +129,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Saliendo de la charla</translation> <translation>Saliendo de la charla</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>No notificar</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Desconocido</translation> <translation>Desconocido</translation>
@ -188,6 +180,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Charla secreta</translation> <translation>Charla secreta</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>No notificar</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -245,14 +245,6 @@
<source>You</source> <source>You</source>
<translation>Usted</translation> <translation>Usted</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>No notificar</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Usuario</translation> <translation>Usuario</translation>
@ -271,16 +263,32 @@
</message> </message>
<message> <message>
<source>Mark chat as unread</source> <source>Mark chat as unread</source>
<translation type="unfinished"></translation> <translation>Marcar como no leído</translation>
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>Borrador</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation>Marcar como leído</translation>
</message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute chat</source>
<translation>No notificar</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -403,11 +411,19 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation type="unfinished"></translation> <translation>Buscar en charla</translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation>Buscar</translation>
</message>
<message>
<source>Location: Obtaining position...</source>
<translation>Ubicación: Recibiendo posición ...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Ubicación (%1/%2)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -474,6 +490,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Abrir Documento</translation> <translation>Abrir Documento</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -785,21 +805,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>ha añadido %1 a charla</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>ha quitado %1 de charla</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>ha añadido %1 a la charla</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>ha quitado %1 de charla</translation>
</message> </message>
</context> </context>
<context> <context>
@ -836,7 +856,7 @@
</message> </message>
<message> <message>
<source>Please enter your phone number to continue.</source> <source>Please enter your phone number to continue.</source>
<translation>Marcar número de teléfono para continuar.</translation> <translation>Marcar el número de teléfono para continuar.</translation>
</message> </message>
<message> <message>
<source>Continue</source> <source>Continue</source>
@ -884,7 +904,7 @@
</message> </message>
<message> <message>
<source>Use the international format, e.g. %1</source> <source>Use the international format, e.g. %1</source>
<translation>Usar el formato internacional %1</translation> <translation>Usa el formato internacional %1</translation>
</message> </message>
<message> <message>
<source>About Fernschreiber</source> <source>About Fernschreiber</source>
@ -910,7 +930,7 @@
</message> </message>
<message> <message>
<source>Copy Message to Clipboard</source> <source>Copy Message to Clipboard</source>
<translation>Copiar mensaje</translation> <translation>Copiar</translation>
</message> </message>
<message> <message>
<source>Message deleted</source> <source>Message deleted</source>
@ -930,7 +950,7 @@
</message> </message>
<message> <message>
<source>Select Message</source> <source>Select Message</source>
<translation>Seleccionar mensaje</translation> <translation>Seleccionar</translation>
</message> </message>
<message> <message>
<source>Pin Message</source> <source>Pin Message</source>
@ -938,11 +958,11 @@
</message> </message>
<message> <message>
<source>Message unpinned</source> <source>Message unpinned</source>
<translation type="unfinished">Desanclar mensaje</translation> <translation>Mensaje desanclado</translation>
</message> </message>
<message> <message>
<source>Unpin Message</source> <source>Unpin Message</source>
<translation type="unfinished"></translation> <translation>Desanclar mensaje</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1059,19 +1079,31 @@
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
<translation type="unfinished"></translation> <translation>Filtrar las charlas...</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Buscar charlas</translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
<translation type="unfinished">Bajada de %1 exitosa.</translation> <translation>Bajada de %1 exitosa.</translation>
</message> </message>
<message> <message>
<source>Download failed.</source> <source>Download failed.</source>
<translation type="unfinished">Error al bajar</translation> <translation>Error al bajar</translation>
</message>
<message>
<source>Tap on the title bar to filter your chats</source>
<translation>Tocar la barra de título para filtrar las charlas</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>No hay coincidencias.</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Puede buscar charlas públicas o crear un nueva charla a través de la polea de opciones.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Connecting to network...</source>
@ -1094,7 +1126,7 @@
</message> </message>
<message> <message>
<source>Message unpinned</source> <source>Message unpinned</source>
<translation>Desanclar mensaje</translation> <translation>Mensaje desanclado</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1271,43 +1303,47 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation type="unfinished"></translation> <translation>No se han encontrado charlas.</translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
<translation type="unfinished"></translation> <translation>Buscando charlas...</translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation type="unfinished">Privado</translation> <translation>Privado</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation>Grupo</translation>
</message> </message>
<message> <message>
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation>Canal</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 miembros</translation> <translation>
<numerusform>%1 miembros</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 suscriptores</translation> <translation>
<numerusform>%1 suscriptores</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Buscar charla</translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
<translation type="unfinished"></translation> <translation>A b c</translation>
</message> </message>
<message> <message>
<source>Enter your query to start searching (at least 5 characters needed)</source> <source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation> <translation>Para iniciar la búsqueda se necesitan al menos 5 caracteres</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1342,15 +1378,15 @@
</message> </message>
<message> <message>
<source>Notification feedback</source> <source>Notification feedback</source>
<translation>Notificaciones</translation> <translation>Notificar en </translation>
</message> </message>
<message> <message>
<source>All events</source> <source>All events</source>
<translation>Todos los eventos</translation> <translation>Eventos</translation>
</message> </message>
<message> <message>
<source>Only new events</source> <source>Only new events</source>
<translation>Sólo nuevos eventos</translation> <translation>Nuevos eventos</translation>
</message> </message>
<message> <message>
<source>None</source> <source>None</source>
@ -1384,6 +1420,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Optimizador de almacenamiento</translation> <translation>Optimizador de almacenamiento</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Enfocar área de entrada de texto</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Enfoca el área de entrada de texto después de enviar un mensaje</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1411,6 +1463,45 @@
<translation>Error al bajar</translation> <translation>Error al bajar</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Nota de voz</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Presionar el botón para iniciar a grabar</translation>
</message>
<message>
<source>Unavailable</source>
<translation>No diponible</translation>
</message>
<message>
<source>Starting</source>
<translation>Iniciando</translation>
</message>
<message>
<source>Recording</source>
<translation>Grabando</translation>
</message>
<message>
<source>Stopping</source>
<translation>Deteniendo</translation>
</message>
<message>
<source>Use recording</source>
<translation>Usar grabación</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Nota de voz (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Listo</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1796,21 +1887,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>ha añadido %1 a la charla</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>ha quitado %1 de la charla</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>ha añadido %1 a la charla</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>ha añadido %1 de la charla</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Poistutaan keskustelusta</translation> <translation>Poistutaan keskustelusta</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Tuntematon</translation> <translation>Tuntematon</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Uusi salattu keskustelu</translation> <translation>Uusi salattu keskustelu</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source> <source>You</source>
<translation>Sinä</translation> <translation>Sinä</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Käyttäjän tiedot</translation> <translation>Käyttäjän tiedot</translation>
@ -274,16 +266,32 @@
</message> </message>
<message> <message>
<source>Mark chat as unread</source> <source>Mark chat as unread</source>
<translation type="unfinished"></translation> <translation>Merkitse keskustelu lukemattomaksi</translation>
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>Luonnos</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation>Merkitse keskustelu luetuksi</translation>
</message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -413,11 +421,19 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation type="unfinished"></translation> <translation>Etsi keskustelusta</translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation>Etsi keskustelusta...</translation>
</message>
<message>
<source>Location: Obtaining position...</source>
<translation>Sijainti: Paikannetaan...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Sijainti (%1/%2)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -485,6 +501,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Avaa dokumentti</translation> <translation>Avaa dokumentti</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -796,21 +816,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>lisäsi käyttäjän %1 keskusteluun</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>posit käyttäjän %1 keskustelusta</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>lisäsit käyttäjän %1 keskusteluun</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>poistit käyttäjän %1 keskustelusta</translation>
</message> </message>
</context> </context>
<context> <context>
@ -949,11 +969,11 @@
</message> </message>
<message> <message>
<source>Message unpinned</source> <source>Message unpinned</source>
<translation type="unfinished">Viestin kiinnitys poistettu</translation> <translation>Viestin kiinnitys poistettu</translation>
</message> </message>
<message> <message>
<source>Unpin Message</source> <source>Unpin Message</source>
<translation type="unfinished"></translation> <translation>Poista viestin kiinnitys</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1071,19 +1091,31 @@
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
<translation type="unfinished"></translation> <translation>Suodata keskustelujasi...</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Etsi keskusteluista</translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
<translation type="unfinished"></translation> <translation>Tiedoston %1 lataus onnistui.</translation>
</message> </message>
<message> <message>
<source>Download failed.</source> <source>Download failed.</source>
<translation type="unfinished">Lataus epäonnistui.</translation> <translation>Lataus epäonnistui.</translation>
</message>
<message>
<source>Tap on the title bar to filter your chats</source>
<translation>Kosketa otsikkopalkkia suodattaaksesi keskusteluja</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>Hakua vastaavia keskusteluja ei löytynyt,</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Voit etsiä julkisia keskusteluja tai luoda uuden keskustelun alasvetovalikosta.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Connecting to network...</source>
@ -1291,43 +1323,49 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation type="unfinished"></translation> <translation>Keskusteluja ei löytynyt</translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
<translation type="unfinished"></translation> <translation>Etsitään keskusteluja...</translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation type="unfinished">Yksityinen keskustelu</translation> <translation>Yksityinen keskustelu</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation>Ryhmä</translation>
</message> </message>
<message> <message>
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation>Kanava</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 jäsen</translation> <translation>
<numerusform>%1 jäsen</numerusform>
<numerusform>%1 jäsentä</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 tilaaja</translation> <translation>
<numerusform>%1 tilaaja</numerusform>
<numerusform>%1 tilaajaa</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Etsi keskusteluja</translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
<translation type="unfinished"></translation> <translation>Etsi keskustelua...</translation>
</message> </message>
<message> <message>
<source>Enter your query to start searching (at least 5 characters needed)</source> <source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation> <translation>Syötä hakusanasi etsiäksesi (vähintään 5 merkkiä tarvitaan)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1404,6 +1442,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Käytä tallennustilan optimointia</translation> <translation>Käytä tallennustilan optimointia</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Kohdista tekstinsyöttökenttä lähetyksen jälkeen</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Kohdista tekstinsyöttökenttä viestin lähetyksen jälkeen</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1431,6 +1485,45 @@
<translation>Lataus epäonnistui.</translation> <translation>Lataus epäonnistui.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Nauhoita ääniviesti</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Paina nappia aloittaaksesi nauhoituksen</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Ei saatavilla</translation>
</message>
<message>
<source>Starting</source>
<translation>Aloitetaan</translation>
</message>
<message>
<source>Recording</source>
<translation>Nauhoitetaan</translation>
</message>
<message>
<source>Stopping</source>
<translation>Lopetetaan</translation>
</message>
<message>
<source>Use recording</source>
<translation>Käytä nauhoitusta</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Ääniviesti (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Valmis</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1816,21 +1909,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>lisäsi käyttäjä %1 keskusteluun</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>posit käyttäjän %1 keskustelusta</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>lisäsit käyttäjän %1 keskusteluun</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>poistit käyttäjän %1 keskustelusta</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -129,14 +129,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation type="unfinished">Csevegés némítás feloldása</translation>
</message>
<message>
<source>Mute Chat</source>
<translation type="unfinished">Csevegés némítása</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation type="unfinished">Ismeretlen</translation> <translation type="unfinished">Ismeretlen</translation>
@ -188,6 +180,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Csevegés némítás feloldása</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Csevegés némítása</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -245,14 +245,6 @@
<source>You</source> <source>You</source>
<translation type="unfinished">Te</translation> <translation type="unfinished">Te</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation type="unfinished">Csevegés némítás feloldása</translation>
</message>
<message>
<source>Mute Chat</source>
<translation type="unfinished">Csevegés némítása</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -281,6 +273,22 @@
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Csevegés némítás feloldása</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Csevegés némítása</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -409,6 +417,14 @@
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Location: Obtaining position...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -474,6 +490,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Dokumentum megyitása</translation> <translation>Dokumentum megyitása</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -1074,8 +1094,16 @@
<translation type="unfinished">A letöltés nem sikerült.</translation> <translation type="unfinished">A letöltés nem sikerült.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Tap on the title bar to filter your chats</source>
<translation type="unfinished">Csatlakozás a hálózathoz...</translation> <translation type="unfinished"></translation>
</message>
<message>
<source>No matching chats found.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Logging out</source> <source>Logging out</source>
@ -1289,13 +1317,17 @@
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 tag</translation> <translation type="unfinished">
<numerusform>%1 tag</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 feliratkozott</translation> <translation type="unfinished">
<numerusform>%1 feliratkozott</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
@ -1384,6 +1416,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1411,6 +1459,45 @@
<translation>A letöltés nem sikerült.</translation> <translation>A letöltés nem sikerült.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ready</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Lascia chat</translation> <translation>Lascia chat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Silenzia chat</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Sconosciuto</translation> <translation>Sconosciuto</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Nuova chat segreta</translation> <translation>Nuova chat segreta</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Silenzia chat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source> <source>You</source>
<translation>Tu</translation> <translation>Tu</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Silenzia chat</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Info utente</translation> <translation>Info utente</translation>
@ -274,15 +266,31 @@
</message> </message>
<message> <message>
<source>Mark chat as unread</source> <source>Mark chat as unread</source>
<translation type="unfinished"></translation> <translation>Segna chat come non letta</translation>
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>Bozza</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation type="unfinished"></translation> <translation>Segna chat come letta</translation>
</message>
<message>
<source>Unpin chat</source>
<translation>Togli chat in evidenza</translation>
</message>
<message>
<source>Pin chat</source>
<translation>Metti chat in evidenza</translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Silenzia chat</translation>
</message> </message>
</context> </context>
<context> <context>
@ -413,11 +421,19 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation type="unfinished"></translation> <translation>Cerca nella chat</translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation>Cerca nella chat...</translation>
</message>
<message>
<source>Location: Obtaining position...</source>
<translation>Posizione: ottengo posizione...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Posizione(%1/%2)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -484,6 +500,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Apri documento</translation> <translation>Apri documento</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -795,21 +815,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>ha aggiunto %1 alla chat</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>ha rimosso %1 dalla chat</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>hai aggiunto %1 alla chat</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>hai rimosso %1 dalla chat</translation>
</message> </message>
</context> </context>
<context> <context>
@ -948,11 +968,11 @@
</message> </message>
<message> <message>
<source>Message unpinned</source> <source>Message unpinned</source>
<translation type="unfinished">Messaggio non più in evidenza</translation> <translation>Messaggio non più in evidenza</translation>
</message> </message>
<message> <message>
<source>Unpin Message</source> <source>Unpin Message</source>
<translation type="unfinished"></translation> <translation>Togli messaggio in evidenza</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1070,19 +1090,31 @@
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
<translation type="unfinished"></translation> <translation>Filtra le chat...</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Ricerca chat</translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
<translation type="unfinished">Download di %1 completato.</translation> <translation>Download di %1 completato.</translation>
</message> </message>
<message> <message>
<source>Download failed.</source> <source>Download failed.</source>
<translation type="unfinished">Download non riuscito.</translation> <translation>Download non riuscito.</translation>
</message>
<message>
<source>Tap on the title bar to filter your chats</source>
<translation>Clicca sulla barra del titolo per filtrare le tue chat</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>Nessuna chat corrispondente.</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Puoi creare una nuova chat o cercare chat pubbliche dal menu a trascinamento.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Connecting to network...</source>
@ -1290,43 +1322,49 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation type="unfinished"></translation> <translation>Nessuna chat trovata.</translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
<translation type="unfinished"></translation> <translation>Ricerca chat...</translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation type="unfinished">Chat privata</translation> <translation>Chat privata</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation>Gruppo</translation>
</message> </message>
<message> <message>
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation>Canale</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 membro</translation> <translation>
<numerusform>%1 membro</numerusform>
<numerusform>%1 membri</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 abbonato</translation> <translation>
<numerusform>%1 abbonato</numerusform>
<numerusform>%1 abbonati</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Cerca chat</translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
<translation type="unfinished"></translation> <translation>Cerca chat...</translation>
</message> </message>
<message> <message>
<source>Enter your query to start searching (at least 5 characters needed)</source> <source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation> <translation>Scrivi il testo che vuoi cercare (almeno 5 caratteri)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1403,6 +1441,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Abilita ottimizzazione memoria</translation> <translation>Abilita ottimizzazione memoria</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Tastiera in primo piano dopo invio</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Mantieni la tastiera in primo piano dopo aver inviato un messaggio</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1430,6 +1484,45 @@
<translation>Download non riuscito.</translation> <translation>Download non riuscito.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Registra una nota vocale</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Premi il pulsante per iniziare la registrazione</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Non disponibile</translation>
</message>
<message>
<source>Starting</source>
<translation>Inizia</translation>
</message>
<message>
<source>Recording</source>
<translation>In registrazione</translation>
</message>
<message>
<source>Stopping</source>
<translation>Stop</translation>
</message>
<message>
<source>Use recording</source>
<translation>Usa registrazione</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Nota vocale (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Pronto</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1815,21 +1908,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>ha aggiunto %1 alla chat</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>ha rimosso %1 dalla chat</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>hai aggiunto %1 alla chat</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>hai rimosso %1 dalla chat</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -105,14 +105,6 @@
</context> </context>
<context> <context>
<name>ChatInformationPageContent</name> <name>ChatInformationPageContent</name>
<message>
<source>Unmute Chat</source>
<translation>Wyłącz wyciszenie czatu</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Wycisz czat</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Nieznany</translation> <translation>Nieznany</translation>
@ -194,6 +186,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Nowy tajny czat</translation> <translation>Nowy tajny czat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Wyłącz wyciszenie czatu</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Wycisz czat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -251,14 +251,6 @@
<source>You</source> <source>You</source>
<translation>Ty</translation> <translation>Ty</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Wyłącz wyciszenie czatu</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Wycisz czat</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Informacje o użytkowniku</translation> <translation>Informacje o użytkowniku</translation>
@ -277,16 +269,32 @@
</message> </message>
<message> <message>
<source>Mark chat as unread</source> <source>Mark chat as unread</source>
<translation type="unfinished"></translation> <translation>Oznacz czat jako nieprzeczytany</translation>
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>Kopia robocza</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation>Oznacz czat jako przeczytany</translation>
</message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Wyłącz wyciszenie czatu</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Wycisz czat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -423,11 +431,19 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation type="unfinished"></translation> <translation>Wyszukaj w czacie</translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation>Wyszukaj w czacie</translation>
</message>
<message>
<source>Location: Obtaining position...</source>
<translation>Lokalizacja: Uzyskanie pozycji ...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Lokalizacja (%1/%2)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -494,6 +510,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Otwórz dokument</translation> <translation>Otwórz dokument</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -805,21 +825,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>dodał %1 do czatu</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>usunął %1 z czatu</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>dodsałem %1 do czatu</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>usunąłem %1 z czatu</translation>
</message> </message>
</context> </context>
<context> <context>
@ -958,11 +978,11 @@
</message> </message>
<message> <message>
<source>Message unpinned</source> <source>Message unpinned</source>
<translation type="unfinished">Wiadomość opięta</translation> <translation>Wiadomość odpięta</translation>
</message> </message>
<message> <message>
<source>Unpin Message</source> <source>Unpin Message</source>
<translation type="unfinished"></translation> <translation>Odepnij wiadomość</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1081,19 +1101,31 @@
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
<translation type="unfinished"></translation> <translation>Filtruj swoje czaty...</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Wyszukaj czaty</translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
<translation type="unfinished"></translation> <translation>Pobieranie %1 zakończone sukcesem</translation>
</message> </message>
<message> <message>
<source>Download failed.</source> <source>Download failed.</source>
<translation type="unfinished"></translation> <translation>Nieudane pobranie</translation>
</message>
<message>
<source>Tap on the title bar to filter your chats</source>
<translation>Dotknij paska tytułowego, aby filtrować swoje czaty</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>Brak pasujących czatów</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Możesz przeszukiwać czaty publiczne lub utworzyć nowy czat za pomocą menu rozwijanego z góry.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Connecting to network...</source>
@ -1309,43 +1341,51 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation type="unfinished"></translation> <translation>Brak pasujących czatów</translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
<translation type="unfinished"></translation> <translation>Wyszukiwanie czatów...</translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation type="unfinished">Prywatny czat</translation> <translation>Prywatny czat</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation>Grupa</translation>
</message> </message>
<message> <message>
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation>Kanał</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 członek</translation> <translation>
<numerusform>%1 członek</numerusform>
<numerusform>%1 członków</numerusform>
<numerusform>%1 członków</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 subskrybent</translation> <translation>
<numerusform>%1 subskrybent</numerusform>
<numerusform>%1 subskrybentów</numerusform>
<numerusform>%1 subskrybentów</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Wyszukaj czaty</translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
<translation type="unfinished"></translation> <translation>Wyszukaj czat...</translation>
</message> </message>
<message> <message>
<source>Enter your query to start searching (at least 5 characters needed)</source> <source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation> <translation>Wprowadź zapytanie aby zacząć wyszukiwanie (minimum 5 znaków)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1422,6 +1462,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Włącz optymalizację pamięci</translation> <translation>Włącz optymalizację pamięci</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Po wysłaniu zaznacz pole wprowadzania tekstu</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Po wysłaniu wiadomości zaznacz pole wprowadzania tekstu</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1449,6 +1505,45 @@
<translation>Nieudane pobieranie</translation> <translation>Nieudane pobieranie</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Nagraj notatkę głosową</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Naciśnij przycisk, aby zacząć nagrywać</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Niedostepne</translation>
</message>
<message>
<source>Starting</source>
<translation>Uruchamianie</translation>
</message>
<message>
<source>Recording</source>
<translation>Nagrywanie</translation>
</message>
<message>
<source>Stopping</source>
<translation>Zatrzymywanie</translation>
</message>
<message>
<source>Use recording</source>
<translation>Użyj nagrywania</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Notatka głosowa (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Gotowy</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1834,21 +1929,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation>dodał %1 do czatu</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation>usunął %1 z czatu</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>dodałem %1 do czatu</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation>usunąłem %1 z czatu</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -123,59 +123,51 @@
</message> </message>
<message> <message>
<source>Leave Chat</source> <source>Leave Chat</source>
<translation type="unfinished">Выйти из чата</translation> <translation>Выйти из чата</translation>
</message> </message>
<message> <message>
<source>Join Chat</source> <source>Join Chat</source>
<translation type="unfinished">Зайти в чат</translation> <translation>Зайти в чат</translation>
</message> </message>
<message> <message>
<source>Leaving chat</source> <source>Leaving chat</source>
<translation type="unfinished">Выход из чата</translation> <translation>Выход из чата</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation type="unfinished">Включить уведомления</translation>
</message>
<message>
<source>Mute Chat</source>
<translation type="unfinished">Выключить уведомления</translation>
</message> </message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation type="unfinished">Неизвестный</translation> <translation>Неизвестный</translation>
</message> </message>
<message> <message>
<source>Chat Title</source> <source>Chat Title</source>
<comment>group title header</comment> <comment>group title header</comment>
<translation type="unfinished">Заголовок чата</translation> <translation>Заголовок чата</translation>
</message> </message>
<message> <message>
<source>Enter 1-128 characters</source> <source>Enter 1-128 characters</source>
<translation type="unfinished">Введите 1-128 символов</translation> <translation>Введите 1-128 символов</translation>
</message> </message>
<message> <message>
<source>There is no information text available, yet.</source> <source>There is no information text available, yet.</source>
<translation type="unfinished">Информация отсутствует</translation> <translation>Информация отсутствует</translation>
</message> </message>
<message> <message>
<source>Info</source> <source>Info</source>
<comment>group or user infotext header</comment> <comment>group or user infotext header</comment>
<translation type="unfinished">Информация</translation> <translation>Информация</translation>
</message> </message>
<message> <message>
<source>Phone Number</source> <source>Phone Number</source>
<comment>user phone number header</comment> <comment>user phone number header</comment>
<translation type="unfinished">Номер телефона</translation> <translation>Номер телефона</translation>
</message> </message>
<message> <message>
<source>Invite Link</source> <source>Invite Link</source>
<comment>header</comment> <comment>header</comment>
<translation type="unfinished">Ссылка для приглашения</translation> <translation>Ссылка для приглашения</translation>
</message> </message>
<message> <message>
<source>The Invite Link has been copied to the clipboard.</source> <source>The Invite Link has been copied to the clipboard.</source>
<translation type="unfinished">Ссылка для приглашения скопирована в буффер обмена</translation> <translation>Ссылка для приглашения скопирована в буффер обмена</translation>
</message> </message>
<message> <message>
<source>%1, %2</source> <source>%1, %2</source>
@ -194,6 +186,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Новый секретный чат</translation> <translation>Новый секретный чат</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Включить уведомления</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Выключить уведомления</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -228,17 +228,17 @@
<message> <message>
<source>Groups</source> <source>Groups</source>
<comment>Button: groups in common (short)</comment> <comment>Button: groups in common (short)</comment>
<translation type="unfinished">Группы</translation> <translation>Группы</translation>
</message> </message>
<message> <message>
<source>Members</source> <source>Members</source>
<comment>Button: Group Members</comment> <comment>Button: Group Members</comment>
<translation type="unfinished">Участники группы</translation> <translation>Участники группы</translation>
</message> </message>
<message> <message>
<source>Settings</source> <source>Settings</source>
<comment>Button: Chat Settings</comment> <comment>Button: Chat Settings</comment>
<translation type="unfinished">Настройки</translation> <translation>Настройки</translation>
</message> </message>
</context> </context>
<context> <context>
@ -251,14 +251,6 @@
<source>You</source> <source>You</source>
<translation>Вы</translation> <translation>Вы</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Включить уведомления</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Выключить уведомления</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Информация о пользователе</translation> <translation>Информация о пользователе</translation>
@ -277,16 +269,32 @@
</message> </message>
<message> <message>
<source>Mark chat as unread</source> <source>Mark chat as unread</source>
<translation type="unfinished"></translation> <translation>Отметить чат как непрочитанный</translation>
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>Черновик</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation>Отметить чат как прочитанный</translation>
</message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Включить уведомления</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Выключить уведомления</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -423,11 +431,19 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation type="unfinished"></translation> <translation>Найти в Чате</translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation>Поиск...</translation>
</message>
<message>
<source>Location: Obtaining position...</source>
<translation type="unfinished">Определение координат...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation type="unfinished">Местоположение (%1/%2)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -438,18 +454,18 @@
</message> </message>
<message> <message>
<source>You don&apos;t have any chats yet.</source> <source>You don&apos;t have any chats yet.</source>
<translation type="unfinished">Тут пока ничего нет</translation> <translation>Тут пока ничего нет</translation>
</message> </message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
<source>unread message</source> <source>unread message</source>
<translation>непрочитанное сообщение</translation> <translation>сообщение</translation>
</message> </message>
<message> <message>
<source>unread messages</source> <source>unread messages</source>
<translation>непрочитанных сообщений</translation> <translation>сообщений</translation>
</message> </message>
<message> <message>
<source>in</source> <source>in</source>
@ -469,7 +485,7 @@
</message> </message>
<message> <message>
<source>Connected</source> <source>Connected</source>
<translation>Подключен</translation> <translation>В сети</translation>
</message> </message>
<message> <message>
<source>Updating content...</source> <source>Updating content...</source>
@ -477,11 +493,11 @@
</message> </message>
<message> <message>
<source>chat</source> <source>chat</source>
<translation>чат</translation> <translation>чате</translation>
</message> </message>
<message> <message>
<source>chats</source> <source>chats</source>
<translation>чаты</translation> <translation>чатах</translation>
</message> </message>
</context> </context>
<context> <context>
@ -494,6 +510,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Открыть документ</translation> <translation>Открыть документ</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -805,21 +825,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation type="unfinished">%1 добавлен в чат</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation type="unfinished">%1 удалён из чата</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished">%1 добавлены в чат</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished">%1 удалены из чата</translation>
</message> </message>
</context> </context>
<context> <context>
@ -958,11 +978,11 @@
</message> </message>
<message> <message>
<source>Message unpinned</source> <source>Message unpinned</source>
<translation type="unfinished">Сообщение откреплено</translation> <translation>Сообщение откреплено</translation>
</message> </message>
<message> <message>
<source>Unpin Message</source> <source>Unpin Message</source>
<translation type="unfinished"></translation> <translation type="unfinished">Открепить сообщение</translation>
</message> </message>
</context> </context>
<context> <context>
@ -991,7 +1011,7 @@
</message> </message>
<message> <message>
<source>You don&apos;t have any contacts.</source> <source>You don&apos;t have any contacts.</source>
<translation type="unfinished">У вас нет никаких контактов.</translation> <translation>У вас нет никаких контактов.</translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
@ -1081,19 +1101,31 @@
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
<translation type="unfinished"></translation> <translation>Выбрать чат...</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Найти Чаты</translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
<translation type="unfinished">Успешно скачано %1.</translation> <translation>Успешно скачано %1.</translation>
</message> </message>
<message> <message>
<source>Download failed.</source> <source>Download failed.</source>
<translation type="unfinished">Ошибка скачивания.</translation> <translation>Ошибка скачивания.</translation>
</message>
<message>
<source>Tap on the title bar to filter your chats</source>
<translation>Коснитесь строки заголовка, чтобы отфильтровать ваши чаты</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation type="unfinished">Совпадающих чатов не найдено.</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Вы можете искать публичные чаты или создать новый чат с помощью выпадающего меню</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Connecting to network...</source>
@ -1309,43 +1341,51 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation type="unfinished"></translation> <translation>Чаты не найдены</translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
<translation type="unfinished"></translation> <translation>Идёт поиск чатов...</translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation type="unfinished">Приватный Чат</translation> <translation>Приватный Чат</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation>Группа</translation>
</message> </message>
<message> <message>
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation>Канал</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 участников</translation> <translation type="unfinished">
<numerusform>%1 участников</numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 подписчиков</translation> <translation type="unfinished">
<numerusform>%1 подписчиков</numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation>Найти Чаты</translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
<translation type="unfinished"></translation> <translation>Поиск...</translation>
</message> </message>
<message> <message>
<source>Enter your query to start searching (at least 5 characters needed)</source> <source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation> <translation>Введите не менее 5 символов, чтобы начать поиск</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1422,6 +1462,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Включить оптимизацию хранилища</translation> <translation>Включить оптимизацию хранилища</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Приоритет фокусировки при разговоре в чате</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Сфокусироваться на поле ввода текста после отправки сообщения</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1449,6 +1505,45 @@
<translation>Ошибка скачивания.</translation> <translation>Ошибка скачивания.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Голосовое сообщение</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Коснитесь кнопки для записи</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Недоступно</translation>
</message>
<message>
<source>Starting</source>
<translation>Пуск</translation>
</message>
<message>
<source>Recording</source>
<translation>Запись</translation>
</message>
<message>
<source>Stopping</source>
<translation>Стоп</translation>
</message>
<message>
<source>Use recording</source>
<translation>Использовать запись</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Аудиозаметка (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Готов</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1834,21 +1929,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation type="unfinished">%1 добавлен в чат</translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation type="unfinished">%1 удалён из чата</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished">%1 добавлены в чат</translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished">%1 удалены из чата</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Lämnar chatten</translation> <translation>Lämnar chatten</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Slå chatten</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Stäng av chatten</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Okänd</translation> <translation>Okänd</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation>Ny hemlig chatt</translation> <translation>Ny hemlig chatt</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Slå chatten</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Stäng av chatten</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source> <source>You</source>
<translation>Du</translation> <translation>Du</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Slå chatten</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Stäng av chatten</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>Användarinfo</translation> <translation>Användarinfo</translation>
@ -278,12 +270,28 @@
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>Utkast</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation>Markera chatten som läst</translation> <translation>Markera chatten som läst</translation>
</message> </message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation>Slå chatten</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Stäng av chatten</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source> <source>Search in chat...</source>
<translation>Sök i chatten...</translation> <translation>Sök i chatten...</translation>
</message> </message>
<message>
<source>Location: Obtaining position...</source>
<translation>Plats: Hämtar position...</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation>Plats (%1/%2)</translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Öppna dokument</translation> <translation>Öppna dokument</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -1085,8 +1105,16 @@
<translation>Nerladdning misslyckades.</translation> <translation>Nerladdning misslyckades.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Tap on the title bar to filter your chats</source>
<translation type="unfinished">Ansluter till nätverket...</translation> <translation>Tryck titelfältet för att filtrera dina chattar</translation>
</message>
<message>
<source>No matching chats found.</source>
<translation>Ingen passande chatt hittades.</translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation>Du kan söka efter allmänna chattar eller skapa en ny chatt via toppmenyn.</translation>
</message> </message>
<message> <message>
<source>Logging out</source> <source>Logging out</source>
@ -1308,13 +1336,19 @@
<source>Channel</source> <source>Channel</source>
<translation>Kanal</translation> <translation>Kanal</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation>%1 medlemmar</translation> <translation>
<numerusform>%1 medlem</numerusform>
<numerusform>%1 medlemmar</numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation>%1 prenumeranter</translation> <translation>
<numerusform>%1 prenumerant</numerusform>
<numerusform>%1 prenumeranter</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation>Aktivera lagringsoptimering</translation> <translation>Aktivera lagringsoptimering</translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation>Fokusera textinmatningsfältet efter sändning</translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation>Fokusera textinmatningsfältet efter att ett meddelande skickats</translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Nerladdning misslyckades.</translation> <translation>Nerladdning misslyckades.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation>Spela in ett röstmeddelande</translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation>Tryck knappen för att starta inspelning</translation>
</message>
<message>
<source>Unavailable</source>
<translation>Ej tillgänglig</translation>
</message>
<message>
<source>Starting</source>
<translation>Startar</translation>
</message>
<message>
<source>Recording</source>
<translation>Spelar in</translation>
</message>
<message>
<source>Stopping</source>
<translation>Stoppar</translation>
</message>
<message>
<source>Use recording</source>
<translation>Använd inspelning</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation>Röstmeddelande (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation>Klar</translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>

View file

@ -129,14 +129,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation></translation>
</message>
<message>
<source>Mute Chat</source>
<translation></translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation></translation> <translation></translation>
@ -188,6 +180,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation></translation>
</message>
<message>
<source>Mute Chat</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -245,14 +245,6 @@
<source>You</source> <source>You</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation></translation>
</message>
<message>
<source>Mute Chat</source>
<translation></translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation></translation> <translation></translation>
@ -271,16 +263,32 @@
</message> </message>
<message> <message>
<source>Mark chat as unread</source> <source>Mark chat as unread</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation>稿</translation>
</message> </message>
<message> <message>
<source>Mark chat as read</source> <source>Mark chat as read</source>
<translation></translation>
</message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation></translation>
</message>
<message>
<source>Mute chat</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -403,11 +411,19 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation></translation>
</message>
<message>
<source>Location: Obtaining position...</source>
<translation>位置:正在获取位置</translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation> (%1/%2)</translation>
</message> </message>
</context> </context>
<context> <context>
@ -425,7 +441,8 @@
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
<source>unread message</source> <source>unread message</source>
<translation></translation> <translation>
</translation>
</message> </message>
<message> <message>
<source>unread messages</source> <source>unread messages</source>
@ -474,6 +491,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -574,7 +595,7 @@
</message> </message>
<message> <message>
<source>sent a voice note</source> <source>sent a voice note</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<source>sent a document</source> <source>sent a document</source>
@ -785,21 +806,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation> %1 </translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation> %1</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation> %1 </translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation> %1</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1059,11 +1080,11 @@
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
@ -1074,8 +1095,16 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Tap on the title bar to filter your chats</source>
<translation type="unfinished"></translation> <translation></translation>
</message>
<message>
<source>No matching chats found.</source>
<translation></translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation></translation>
</message> </message>
<message> <message>
<source>Logging out</source> <source>Logging out</source>
@ -1271,43 +1300,47 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 </translation> <translation>
<numerusform>%1 </numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 </translation> <translation>
<numerusform>%1 </numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Enter your query to start searching (at least 5 characters needed)</source> <source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation> <translation>5</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1384,6 +1417,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation></translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation></translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1411,6 +1460,45 @@
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation></translation>
</message>
<message>
<source>Unavailable</source>
<translation></translation>
</message>
<message>
<source>Starting</source>
<translation></translation>
</message>
<message>
<source>Recording</source>
<translation></translation>
</message>
<message>
<source>Stopping</source>
<translation></translation>
</message>
<message>
<source>Use recording</source>
<translation>使</translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation> (%1)</translation>
</message>
<message>
<source>Ready</source>
<translation></translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>
@ -1788,7 +1876,7 @@
</message> </message>
<message> <message>
<source>Closed!</source> <source>Closed!</source>
<translation>!</translation> <translation></translation>
</message> </message>
<message> <message>
<source>Pending acknowledgement</source> <source>Pending acknowledgement</source>
@ -1796,21 +1884,21 @@
</message> </message>
<message> <message>
<source>has added %1 to the chat</source> <source>has added %1 to the chat</source>
<translation type="unfinished"></translation> <translation> %1 </translation>
</message> </message>
<message> <message>
<source>has removed %1 from the chat</source> <source>has removed %1 from the chat</source>
<translation type="unfinished"></translation> <translation> %1</translation>
</message> </message>
<message> <message>
<source>have added %1 to the chat</source> <source>have added %1 to the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation> %1 </translation>
</message> </message>
<message> <message>
<source>have removed %1 from the chat</source> <source>have removed %1 from the chat</source>
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation> %1</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source> <source>Leaving chat</source>
<translation>Leaving chat</translation> <translation>Leaving chat</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Unknown</translation> <translation>Unknown</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source> <source>New Secret Chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation type="unfinished">Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation type="unfinished">Mute Chat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatInformationTabItemMembersGroups</name> <name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source> <source>You</source>
<translation>You</translation> <translation>You</translation>
</message> </message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message> <message>
<source>User Info</source> <source>User Info</source>
<translation>User Info</translation> <translation>User Info</translation>
@ -284,6 +276,22 @@
<source>Draft</source> <source>Draft</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Unpin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pin chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unmute chat</source>
<translation type="unfinished">Unmute chat</translation>
</message>
<message>
<source>Mute chat</source>
<translation type="unfinished">Mute chat</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source> <source>Search in chat...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Location: Obtaining position...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Location (%1/%2)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source> <source>Open Document</source>
<translation>Open Document</translation> <translation>Open Document</translation>
</message> </message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupChatPermissionsColumn</name> <name>EditGroupChatPermissionsColumn</name>
@ -1085,8 +1105,16 @@
<translation type="unfinished">Download failed.</translation> <translation type="unfinished">Download failed.</translation>
</message> </message>
<message> <message>
<source>Connecting to network...</source> <source>Tap on the title bar to filter your chats</source>
<translation type="unfinished">Connecting to network...</translation> <translation type="unfinished"></translation>
</message>
<message>
<source>No matching chats found.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>You can search public chats or create a new chat via the pull-down menu.</source>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Logging out</source> <source>Logging out</source>
@ -1308,13 +1336,19 @@
<source>Channel</source> <source>Channel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 members</source> <source>%1 members</source>
<translation type="unfinished">%1 member</translation> <translation type="unfinished">
<numerusform>%1 member</numerusform>
<numerusform></numerusform>
</translation>
</message> </message>
<message> <message numerus="yes">
<source>%1 subscribers</source> <source>%1 subscribers</source>
<translation type="unfinished">%1 subscriber</translation> <translation type="unfinished">
<numerusform>%1 subscriber</numerusform>
<numerusform></numerusform>
</translation>
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source> <source>Enable storage optimizer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Focus text input area after send</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Focus the text input area after sending a message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable online-only mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Download failed.</translation> <translation>Download failed.</translation>
</message> </message>
</context> </context>
<context>
<name>VoiceNoteOverlay</name>
<message>
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Voice Note (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ready</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>WebPagePreview</name> <name>WebPagePreview</name>
<message> <message>