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
QT += core dbus sql
QT += core dbus sql multimedia positioning
DEFINES += QT_STATICPLUGIN
@ -58,6 +58,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/ReplyMarkupButtons.qml \
qml/components/StickerPicker.qml \
qml/components/PhotoTextsListItem.qml \
qml/components/VoiceNoteOverlay.qml \
qml/components/WebPagePreview.qml \
qml/components/chatInformationPage/ChatInformationEditArea.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.bottom: positionText.top
minimumValue: 0
maximumValue: messageAudio.duration ? messageAudio.duration : 0
maximumValue: messageAudio.duration ? messageAudio.duration : 0.1
stepSize: 1
value: messageAudio.position
enabled: messageAudio.seekable

View file

@ -9,13 +9,14 @@ PhotoTextsListItem {
id: listItem
pictureThumbnail {
photoData: photo_small || ({})
highlighted: listItem.highlighted && !listItem.menuOpen
}
property int ownUserId
property bool showDraft: !!draft_message_text && draft_message_date > last_message_date
property string previewText: showDraft ? draft_message_text : last_message_text
// 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
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
@ -25,6 +26,8 @@ PhotoTextsListItem {
unreadCount: unread_count
isSecret: ( chat_type === TelegramAPI.ChatTypeSecret )
isMarkedAsUnread: is_marked_as_unread
isPinned: is_pinned
isMuted: display.notification_settings.mute_for > 0
openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId
@ -54,19 +57,18 @@ PhotoTextsListItem {
}
MenuItem {
visible: unread_count === 0 && !is_marked_as_unread
visible: unread_count === 0
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 {
visible: unread_count === 0 && is_marked_as_unread
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 {
@ -81,7 +83,7 @@ PhotoTextsListItem {
newNotificationSettings.use_default_mute_for = false;
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 {

View file

@ -41,9 +41,9 @@ Item {
if (documentData) {
if (documentData.document.local.is_downloading_completed) {
downloadDocumentButton.visible = false;
openDocumentButton.visible = true;
openDocumentArea.visible = true;
} else {
openDocumentButton.visible = false;
openDocumentArea.visible = false;
downloadDocumentButton.visible = true;
}
}
@ -57,7 +57,7 @@ Item {
downloadingProgressBar.visible = false;
documentData.document = fileInformation;
downloadDocumentButton.visible = false;
openDocumentButton.visible = true;
openDocumentArea.visible = true;
if (documentPreviewItem.openRequested) {
documentPreviewItem.openRequested = false;
tdLibWrapper.openFileOnDevice(documentData.document.local.path);
@ -95,12 +95,21 @@ Item {
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 {
id: openDocumentButton
preferredWidth: Theme.buttonWidthMedium
anchors.centerIn: parent
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Open Document")
visible: false
highlighted: documentPreviewItem.highlighted || down
onClicked: {
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
property var chatId
property var messageId
property int messageIndex
property var myMessage
property bool canReplyToMessage
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));
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 {
@ -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 {
id: delegateComponentLoadingTimer
interval: 500

View file

@ -14,6 +14,8 @@ ListItem {
property bool isSecret: false
property bool isVerified: false
property bool isMarkedAsUnread: false
property bool isPinned: false
property bool isMuted: false
property alias pictureThumbnail: pictureThumbnail
contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium
@ -33,15 +35,14 @@ ListItem {
height: contentColumn.height
spacing: Theme.paddingMedium
Column {
id: pictureColumn
ShaderEffectSource {
id: pictureItem
width: contentColumn.height - Theme.paddingSmall
height: contentColumn.height - Theme.paddingSmall
anchors.verticalCenter: parent.verticalCenter
Item {
width: parent.width
height: parent.width
sourceItem: Item {
width: pictureItem.width
height: pictureItem.width
ProfileThumbnail {
id: pictureThumbnail
@ -50,11 +51,30 @@ ListItem {
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 {
id: chatSecretBackground
color: Theme.overlayBackgroundColor
width: Theme.fontSizeExtraLarge
height: Theme.fontSizeExtraLarge
color: Theme.highlightBackgroundColor
width: Theme.fontSizeLarge
height: Theme.fontSizeLarge
anchors.bottom: parent.bottom
radius: parent.width / 2
visible: chatListViewItem.isSecret
@ -62,8 +82,8 @@ ListItem {
Image {
source: "image://theme/icon-s-secure"
height: Theme.fontSizeMedium
width: Theme.fontSizeMedium
height: Theme.fontSizeSmall
width: Theme.fontSizeSmall
anchors.centerIn: chatSecretBackground
visible: chatListViewItem.isSecret
}
@ -93,7 +113,7 @@ ListItem {
Column {
id: contentColumn
width: mainColumn.width - pictureColumn.width - mainRow.spacing
width: mainColumn.width - pictureItem.width - mainRow.spacing
spacing: Theme.paddingSmall
Row {
@ -106,15 +126,26 @@ ListItem {
font.pixelSize: Theme.fontSizeMedium
truncationMode: TruncationMode.Fade
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 {
id: verifiedImage
anchors.verticalCenter: parent.verticalCenter
source: chatListViewItem.isVerified ? "../../images/icon-verified.svg" : ""
sourceSize.width: Theme.iconSizeExtraSmall
width: Theme.iconSizeExtraSmall
sourceSize: Qt.size(Theme.iconSizeExtraSmall, 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
}
}

View file

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

View file

@ -27,7 +27,7 @@ Item {
property ListItem messageListItem
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 string videoUrl;
@ -89,7 +89,7 @@ Item {
videoMessageComponent.videoType = videoMessageComponent.isVideoNote ? "video" : videoData['@type'];
videoFileId = videoData[videoType].id;
if (rawMessage.content['@type'] === "messageAnimation") {
if (typeof rawMessage !== "undefined" && rawMessage.content['@type'] === "messageAnimation") {
playButton.visible = true;
fullscreenButton.visible = !videoMessageComponent.fullscreen;
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 {
id: messageVideo
@ -367,7 +352,7 @@ Item {
height: parent.height
source: videoUrl
layer.enabled: videoMessageComponent.highlighted
layer.effect: PressEffect { source: singleImage }
layer.effect: PressEffect { source: messageVideo }
onStopped: {
enableScreensaver();
messageVideo.visible = false;
@ -376,6 +361,21 @@ Item {
videoComponentLoader.active = false;
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 {
@ -482,7 +482,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: positionText.top
minimumValue: 0
maximumValue: messageVideo.duration ? messageVideo.duration : 0
maximumValue: messageVideo.duration ? messageVideo.duration : 0.1
highlighted: videoMessageComponent.highlighted || down
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
Component.onCompleted: {
Component.onCompleted: updatePhoto()
onWebPageDataChanged: updatePhoto()
function updatePhoto() {
if (webPageData) {
if (webPageData.photo) {
// Check first which size fits best...
@ -134,9 +138,10 @@ Column {
}
BackgroundImage {
id: backgroundImage
visible: hasImage && singleImage.status !== Image.Ready
layer.enabled: webPagePreviewColumn.highlighted
layer.effect: PressEffect { source: singleImage }
layer.effect: PressEffect { source: backgroundImage }
}
}

View file

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

View file

@ -211,14 +211,21 @@ Page {
attachmentPreviewRow.isPicture = false;
attachmentPreviewRow.isVideo = false;
attachmentPreviewRow.isDocument = false;
attachmentPreviewRow.isVoiceNote = false;
attachmentPreviewRow.isLocation = false;
attachmentPreviewRow.fileProperties = {};
attachmentPreviewRow.locationData = {};
attachmentPreviewRow.attachmentDescription = "";
fernschreiberUtils.stopGeoLocationUpdates();
}
function controlSendButton() {
if (newMessageTextField.text.length !== 0
|| attachmentPreviewRow.isPicture
|| attachmentPreviewRow.isDocument
|| attachmentPreviewRow.isVideo) {
|| attachmentPreviewRow.isVideo
|| attachmentPreviewRow.isVoiceNote
|| attachmentPreviewRow.isLocation) {
newMessageSendButton.enabled = true;
} else {
newMessageSendButton.enabled = false;
@ -239,14 +246,25 @@ Page {
if (attachmentPreviewRow.isDocument) {
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();
} else {
tdLibWrapper.sendTextMessage(chatInformation.id, newMessageTextField.text, newMessageColumn.replyToMessageId);
}
if(appSettings.focusTextAreaAfterSend) {
lostFocusTimer.start();
}
}
controlSendButton();
newMessageInReplyToRow.inReplyToMessage = null;
newMessageColumn.editMessageId = "0";
fernschreiberUtils.stopGeoLocationUpdates();
}
function getWordBoundaries(text, cursorPosition) {
@ -376,6 +394,7 @@ Page {
if (chatPage.canSendMessages) {
tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text);
}
fernschreiberUtils.stopGeoLocationUpdates();
tdLibWrapper.closeChat(chatInformation.id);
}
@ -455,7 +474,7 @@ Page {
Debug.log("[ChatPage] Received pinned 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;
}
}
@ -623,7 +642,7 @@ Page {
contentWidth: width
PullDownMenu {
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !messageOverlayLoader.active
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !voiceNoteOverlayLoader.active && !messageOverlayLoader.active
MenuItem {
id: closeSecretChatMenuItem
visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
@ -730,7 +749,7 @@ Page {
Rectangle {
id: chatSecretBackground
color: Theme.overlayBackgroundColor
color: Theme.highlightBackgroundColor
width: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
height: width
anchors.left: parent.left
@ -876,7 +895,7 @@ Page {
id: chatView
visible: !blurred
property bool blurred: messageOverlayLoader.item
property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item
anchors.fill: parent
opacity: chatPage.loading ? 0 : 1
@ -1010,6 +1029,7 @@ Page {
chatId: chatModel.chatId
myMessage: model.display
messageId: model.message_id
messageIndex: model.index
extraContentComponentName: chatView.contentComponentNames[model.content_type] || ""
canReplyToMessage: chatPage.canSendMessages
onReplyToMessage: {
@ -1110,7 +1130,7 @@ Page {
Debug.log("Sticker picked: " + stickerId);
tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId);
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 {
@ -1141,7 +1175,6 @@ Page {
spacing: Theme.paddingSmall
topPadding: Theme.paddingSmall
anchors.horizontalCenter: parent.horizontalCenter
clip: true
visible: height > 0
width: parent.width - ( 2 * Theme.horizontalPageMargin )
height: isNeeded ? implicitHeight : 0
@ -1173,18 +1206,41 @@ Page {
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 {
id: attachmentOptionsRow
property bool isNeeded: false
visible: height > 0
height: isNeeded ? implicitHeight : 0
height: attachImageIconButton.height
anchors.right: parent.right
width: parent.width
layoutDirection: Qt.RightToLeft
spacing: Theme.paddingMedium
clip: true
Behavior on height { SmoothedAnimation { duration: 200 } }
leftPadding: Theme.horizontalPageMargin
rightPadding: Theme.horizontalPageMargin
IconButton {
id: attachImageIconButton
visible: chatPage.hasSendPrivilege("can_send_media_messages")
icon.source: "image://theme/icon-m-image"
onClicked: {
@ -1192,7 +1248,7 @@ Page {
allowedOrientations: chatPage.allowedOrientations
})
picker.selectedContentPropertiesChanged.connect(function(){
attachmentOptionsRow.isNeeded = false;
attachmentOptionsFlickable.isNeeded = false;
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isPicture = true;
@ -1209,7 +1265,7 @@ Page {
allowedOrientations: chatPage.allowedOrientations
})
picker.selectedContentPropertiesChanged.connect(function(){
attachmentOptionsRow.isNeeded = false;
attachmentOptionsFlickable.isNeeded = false;
Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
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 {
visible: chatPage.hasSendPrivilege("can_send_media_messages")
icon.source: "image://theme/icon-m-document"
@ -1226,7 +1295,7 @@ Page {
allowedOrientations: chatPage.allowedOrientations
})
picker.selectedContentPropertiesChanged.connect(function(){
attachmentOptionsRow.isNeeded = false;
attachmentOptionsFlickable.isNeeded = false;
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isDocument = true;
@ -1245,6 +1314,7 @@ Page {
highlighted: down || stickerPickerLoader.active
onClicked: {
stickerPickerLoader.active = !stickerPickerLoader.active;
voiceNoteOverlayLoader.active = false;
}
}
IconButton {
@ -1252,11 +1322,30 @@ Page {
icon.source: "image://theme/icon-m-question"
onClicked: {
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 {
id: attachmentPreviewRow
visible: false
@ -1268,7 +1357,21 @@ Page {
property bool isPicture: false;
property bool isVideo: false;
property bool isDocument: false;
property bool isVoiceNote: false;
property bool isLocation: false;
property var locationData: ({});
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 {
id: removeAttachmentsIconButton
@ -1295,13 +1398,13 @@ Page {
Label {
id: attachmentPreviewText
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
maximumLineCount: 1
truncationMode: TruncationMode.Fade
color: Theme.secondaryColor
visible: attachmentPreviewRow.isDocument
visible: attachmentPreviewRow.isDocument || attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation
}
}
@ -1503,13 +1606,16 @@ Page {
labelVisible: false
textLeftMargin: 0
textTopMargin: 0
enabled: !attachmentPreviewRow.isLocation
EnterKey.onClicked: {
if (appSettings.sendByEnter) {
sendMessage();
newMessageTextField.text = "";
if(!appSettings.focusTextAreaAfterSend) {
newMessageTextField.focus = false;
}
}
}
EnterKey.enabled: !appSettings.sendByEnter || text.length
EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
@ -1522,16 +1628,17 @@ Page {
IconButton {
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.bottomMargin: Theme.paddingSmall
enabled: !attachmentPreviewRow.visible
onClicked: {
if (attachmentOptionsRow.isNeeded) {
attachmentOptionsRow.isNeeded = false;
if (attachmentOptionsFlickable.isNeeded) {
attachmentOptionsFlickable.isNeeded = false;
stickerPickerLoader.active = false;
voiceNoteOverlayLoader.active = false;
} else {
attachmentOptionsRow.isNeeded = true;
attachmentOptionsFlickable.isNeeded = true;
}
}
}
@ -1546,6 +1653,7 @@ Page {
onClicked: {
sendMessage();
newMessageTextField.text = "";
if(!appSettings.focusTextAreaAfterSend) {
newMessageTextField.focus = false;
}
}
@ -1553,6 +1661,7 @@ Page {
}
}
}
}
Loader {
id: selectedMessagesActions

View file

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

View file

@ -67,6 +67,13 @@ Page {
overviewPage.chatListCreated = true;
chatListView.scrollToTop();
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
interval: 600
onTriggered: {
chatListModel.calculateUnreadState();
tdLibWrapper.getRecentStickers();
tdLibWrapper.getInstalledStickerSets();
tdLibWrapper.getContacts();
@ -178,9 +186,8 @@ Page {
function resetFocus() {
if (chatSearchField.text === "") {
chatSearchField.visible = false;
pageHeader.visible = true;
searchChatButton.visible = overviewPage.connectionState === TelegramAPI.ConnectionReady;
chatSearchField.opacity = 0.0;
pageHeader.opacity = 1.0;
}
chatSearchField.focus = false;
overviewPage.focus = true;
@ -202,11 +209,15 @@ Page {
onChatLastMessageUpdated: {
if (!overviewPage.chatListCreated) {
chatListCreatedTimer.restart();
} else {
chatListModel.calculateUnreadState();
}
}
onChatOrderUpdated: {
if (!overviewPage.chatListCreated) {
chatListCreatedTimer.restart();
} else {
chatListModel.calculateUnreadState();
}
}
onChatsReceived: {
@ -268,9 +279,12 @@ Page {
}
}
Row {
id: headerRow
width: parent.width - Theme.horizontalPageMargin
PageHeader {
id: pageHeader
title: qsTr("Fernschreiber")
leftMargin: Theme.itemSizeMedium
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
GlassItem {
id: pageStatus
@ -282,39 +296,23 @@ Page {
cache: false
}
PageHeader {
id: pageHeader
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
MouseArea {
anchors.fill: parent
onClicked: {
chatSearchField.focus = true;
chatSearchField.visible = true;
pageHeader.visible = false;
searchChatButton.visible = false;
chatSearchField.opacity = 1.0;
pageHeader.opacity = 0.0;
}
}
}
SearchField {
id: chatSearchField
visible: false
opacity: visible ? 1 : 0
visible: opacity > 0
opacity: 0
Behavior on opacity { FadeAnimation {} }
width: visible ? ( parent.width - pageStatus.width ) : 0
width: parent.width
height: pageHeader.height
placeholderText: qsTr("Filter your chats...")
canHide: text === ""
@ -333,12 +331,10 @@ Page {
}
}
}
SilicaListView {
id: chatListView
anchors {
top: headerRow.bottom
top: pageHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
@ -360,7 +356,8 @@ Page {
ViewPlaceholder {
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 {}
@ -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: {
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/");
}
}
onBasicGroupFullInfoReceived: {
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/");
}
}
@ -189,9 +189,9 @@ Page {
onSupergroupFullInfoUpdated: {
if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) {
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 {
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/");
}
@ -199,9 +199,9 @@ Page {
onSupergroupFullInfoReceived: {
if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) {
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 {
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/");
}

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 {
checked: appSettings.useOpenWith
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 {
width: 1
height: Theme.paddingLarge // Some space at the bottom

View file

@ -12,6 +12,38 @@
# * date Author's Name <author's email> version-release
# - 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
- Add verification badge to verified chats - thanks to monich
- Improve preview of wide images - thanks to monich

View file

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

View file

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

View file

@ -22,12 +22,15 @@
namespace {
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_SHOW_STICKERS_AS_IMAGES("showStickersAsImages");
const QString KEY_ANIMATE_STICKERS("animateStickers");
const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn");
const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback");
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")
@ -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
{
return settings.value(KEY_USE_OPEN_WITH, true).toBool();
@ -131,3 +148,31 @@ void AppSettings::setStorageOptimizer(bool enable)
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 {
Q_OBJECT
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 showStickersAsImages READ showStickersAsImages WRITE setShowStickersAsImages NOTIFY showStickersAsImagesChanged)
Q_PROPERTY(bool animateStickers READ animateStickers WRITE setAnimateStickers NOTIFY animateStickersChanged)
Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged)
Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged)
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:
enum NotificationFeedback {
@ -45,6 +48,9 @@ public:
bool getSendByEnter() const;
void setSendByEnter(bool sendByEnter);
bool getFocusTextAreaAfterSend() const;
void setFocusTextAreaAfterSend(bool focusTextAreaAfterSend);
bool getUseOpenWith() const;
void setUseOpenWith(bool useOpenWith);
@ -63,14 +69,23 @@ public:
bool storageOptimizer() const;
void setStorageOptimizer(bool enable);
int remainingInteractionHints() const;
void setRemainingInteractionHints(int remainingHints);
bool onlineOnlyMode() const;
void setOnlineOnlyMode(bool enable);
signals:
void sendByEnterChanged();
void focusTextAreaAfterSendChanged();
void useOpenWithChanged();
void showStickersAsImagesChanged();
void animateStickersChanged();
void notificationTurnsDisplayOnChanged();
void notificationFeedbackChanged();
void storageOptimizerChanged();
void remainingInteractionHintsChanged();
void onlineOnlyModeChanged();
private:
QSettings settings;

View file

@ -19,6 +19,7 @@
#include "chatlistmodel.h"
#include "fernschreiberutils.h"
#include <QListIterator>
#define DEBUG_MODULE ChatListModel
#include "debuglog.h"
@ -48,6 +49,7 @@ namespace {
const QString IS_CHANNEL("is_channel");
const QString IS_VERIFIED("is_verified");
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 _TYPE("@type");
const QString SECRET_CHAT_ID("secret_chat_id");
@ -77,6 +79,7 @@ public:
bool isChannel() const;
bool isHidden() const;
bool isMarkedAsUnread() const;
bool isPinned() const;
bool updateUnreadCount(int unreadCount);
bool updateLastReadInboxMessageId(qlonglong messageId);
QVector<int> updateLastMessage(const QVariantMap &message);
@ -272,6 +275,11 @@ bool ChatListModel::ChatData::isMarkedAsUnread() const
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)
{
const int prevUnreadCount(unreadCount());
@ -346,9 +354,10 @@ QVector<int> ChatListModel::ChatData::updateSecretChat(const QVariantMap &secret
return changedRoles;
}
ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false)
ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSettings) : showHiddenChats(false)
{
this->tdLibWrapper = tdLibWrapper;
this->appSettings = appSettings;
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(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(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString)));
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)));
// 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::RoleIsChannel, "is_channel");
roles.insert(ChatListModel::RoleIsMarkedAsUnread, "is_marked_as_unread");
roles.insert(ChatListModel::RoleIsPinned, "is_pinned");
roles.insert(ChatListModel::RoleFilter, "filter");
roles.insert(ChatListModel::RoleDraftMessageDate, "draft_message_date");
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::RoleIsChannel: return data->isChannel();
case ChatListModel::RoleIsMarkedAsUnread: return data->isMarkedAsUnread();
case ChatListModel::RoleIsPinned: return data->isPinned();
case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed();
case ChatListModel::RoleDraftMessageText: return data->draftMessageText();
case ChatListModel::RoleDraftMessageDate: return data->draftMessageDate();
@ -515,6 +527,26 @@ int ChatListModel::updateChatOrder(int chatIndex)
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)
{
const int n = chatList.size();
@ -715,6 +747,7 @@ void ChatListModel::handleChatReadInboxUpdated(const QString &id, const QString
}
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
this->calculateUnreadState();
} else {
ChatData *chat = hiddenChats.value(chatId);
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)
{
if (chatIndexMap.contains(chatId)) {

View file

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

View file

@ -54,8 +54,10 @@ public:
MessageData(const QVariantMap &data, qlonglong msgid);
static bool lessThan(const MessageData *message1, const MessageData *message2);
void setContent(const QVariantMap &content);
void setReplyMarkup(const QVariantMap &replyMarkup);
QVector<int> diff(const MessageData *message) const;
QVector<int> setMessageData(const QVariantMap &data);
QVector<int> setContent(const QVariantMap &content);
QVector<int> setReplyMarkup(const QVariantMap &replyMarkup);
int senderUserId() const;
qlonglong senderChatId() const;
bool senderIsChat() const;
@ -63,7 +65,7 @@ public:
public:
QVariantMap messageData;
const qlonglong messageId;
const QString messageContentType;
QString messageContentType;
};
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";
}
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);
QVector<int> changedRoles;
changedRoles.append(RoleDisplay);
return changedRoles;
}
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) :
chatId(0),
inReload(false),
inIncrementalUpdate(false)
inIncrementalUpdate(false),
searchModeActive(false)
{
this->tdLibWrapper = tdLibWrapper;
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)) {
LOG("Received a message that we already know, let's update it!");
const int position = messageIndexMap.value(messageId);
MessageData *messageData = messages.at(position);
messageData->messageData = message;
const QVector<int> changedRoles(messages.at(position)->setMessageData(message));
LOG("Message was updated at 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)) {
LOG("Message was successfully sent" << oldMessageId);
const int pos = messageIndexMap.take(oldMessageId);
delete messages.at(pos);
messages.replace(pos, new MessageData(message, messageId));
MessageData* oldMessage = messages.at(pos);
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);
const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex);
emit dataChanged(messageIndex, messageIndex, changedRoles);
emit lastReadSentMessageUpdated(calculateLastReadSentMessageId());
}
}
@ -448,10 +488,10 @@ void ChatModel::handleMessageContentUpdated(qlonglong chatId, qlonglong messageI
LOG("We know the message that was updated" << messageId);
const int pos = messageIndexMap.value(messageId, -1);
if (pos >= 0) {
messages.at(pos)->setContent(newContent);
LOG("Message was replaced at index" << pos);
const QVector<int> changedRoles(messages.at(pos)->setContent(newContent));
LOG("Message was updated at index" << pos);
const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex);
emit dataChanged(messageIndex, messageIndex, changedRoles);
emit messageUpdated(pos);
}
}
@ -464,10 +504,10 @@ void ChatModel::handleMessageEditedUpdated(qlonglong chatId, qlonglong messageId
LOG("We know the message that was updated" << messageId);
const int pos = messageIndexMap.value(messageId, -1);
if (pos >= 0) {
messages.at(pos)->setReplyMarkup(replyMarkup);
const QVector<int> changedRoles(messages.at(pos)->setReplyMarkup(replyMarkup));
LOG("Message was edited at index" << pos);
const QModelIndex messageIndex(index(pos));
emit dataChanged(messageIndex, messageIndex);
emit dataChanged(messageIndex, messageIndex, changedRoles);
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 <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)
{
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)
@ -128,3 +191,117 @@ QString FernschreiberUtils::getUserName(const QVariantMap &userInformation)
const QString lastName = userInformation.value("last_name").toString();
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
#define FERNSCHREIBERUTILS_H
#include <QObject>
#include <QAudioRecorder>
#include <QGeoPositionInfo>
#include <QGeoPositionInfoSource>
#include "tdlibwrapper.h"
class FernschreiberUtils : public QObject
@ -9,13 +31,46 @@ class FernschreiberUtils : public QObject
Q_OBJECT
public:
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 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

View file

@ -82,11 +82,12 @@ int main(int argc, char *argv[])
FernschreiberUtils *fernschreiberUtils = new FernschreiberUtils(view.data());
context->setContextProperty("fernschreiberUtils", fernschreiberUtils);
qmlRegisterUncreatableType<FernschreiberUtils>(uri, 1, 0, "FernschreiberUtilities", QString());
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
context->setContextProperty("dBusAdaptor", dBusAdaptor);
ChatListModel chatListModel(tdLibWrapper);
ChatListModel chatListModel(tdLibWrapper, appSettings);
context->setContextProperty("chatListModel", &chatListModel);
QSortFilterProxyModel chatListProxyModel(view.data());
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.
@ -107,6 +107,7 @@ void TDLibFile::init()
queuedSignals = 0;
firstQueuedSignal = SignalCount;
autoLoad = false;
downloadHoldOffTimer = 0;
id = 0;
expected_size = 0;
size = 0;
@ -174,7 +175,7 @@ void TDLibFile::updateTDLibWrapper(TDLibWrapper* tdlib)
if (tdlib) {
connect(tdlib, SIGNAL(fileUpdated(int,QVariantMap)), SLOT(handleFileUpdated(int,QVariantMap)));
if (autoLoad) {
load();
downloadFile();
}
}
queueSignal(SignalTdLibChanged);
@ -187,7 +188,7 @@ void TDLibFile::setAutoLoad(bool enableAutoLoad)
autoLoad = enableAutoLoad;
queueSignal(SignalAutoLoadChanged);
if (autoLoad) {
load();
downloadFile();
}
emitQueuedSignals();
}
@ -195,7 +196,19 @@ void TDLibFile::setAutoLoad(bool enableAutoLoad)
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);
return true;
}
@ -205,12 +218,26 @@ bool TDLibFile::load()
void TDLibFile::setFileInfo(const QVariantMap &fileInfo)
{
updateFileInfo(fileInfo);
if (is_downloading_completed && downloadHoldOffTimer) {
// Don't need this timer anymore
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
}
if (autoLoad) {
load();
downloadFile();
}
emitQueuedSignals();
}
void TDLibFile::timerEvent(QTimerEvent *)
{
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
if (autoLoad) {
downloadFile();
}
}
void TDLibFile::handleFileUpdated(int fileId, const QVariantMap &fileInfo)
{
if (id == fileId) {
@ -256,6 +283,7 @@ void TDLibFile::updateFileInfo(const QVariantMap &file)
bool b, fileChanged = false;
int i = file.value(ID).toInt();
if (id != i) {
LOG("File id has changed" << id << "=>" << i);
id = i;
fileChanged = true;
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.
@ -45,6 +45,8 @@ class TDLibFile : public QObject
Q_PROPERTY(bool isUploadingActive READ isUploadingActive NOTIFY uploadingActiveChanged)
Q_PROPERTY(bool isUploadingCompleted READ isUploadingCompleted NOTIFY uploadingCompletedChanged)
enum { DownloadHoldOffMs = 1000 };
public:
TDLibFile();
TDLibFile(TDLibWrapper *tdlib);
@ -102,10 +104,14 @@ signals:
private slots:
void handleFileUpdated(int fileId, const QVariantMap &fileInfo);
protected:
void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
private:
void init();
void updateTDLibWrapper(TDLibWrapper* tdlib);
void updateFileInfo(const QVariantMap &fileInfo);
bool downloadFile();
void queueSignal(uint signal);
void emitQueuedSignals();
@ -114,6 +120,7 @@ private:
uint queuedSignals;
uint firstQueuedSignal;
bool autoLoad;
int downloadHoldOffTimer;
// file
QVariantMap infoMap;
int id;

View file

@ -38,6 +38,7 @@ namespace {
const QString POSITIONS("positions");
const QString PHOTO("photo");
const QString ORDER("order");
const QString IS_PINNED("is_pinned");
const QString BASIC_GROUP("basic_group");
const QString SUPERGROUP("supergroup");
const QString LAST_MESSAGE("last_message");
@ -287,9 +288,20 @@ void TDLibReceiver::processUpdateChatOrder(const QVariantMap &receivedInformatio
void TDLibReceiver::processUpdateChatPosition(const QVariantMap &receivedInformation)
{
const QString chat_id(receivedInformation.value(CHAT_ID).toString());
const QString order(receivedInformation.value(POSITION).toMap().value(ORDER).toString());
LOG("Chat position updated for ID" << chat_id << "new order" << order);
QVariantMap positionMap = receivedInformation.value(POSITION).toMap();
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 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)

View file

@ -49,6 +49,7 @@ signals:
void unreadChatCountUpdated(const QVariantMap &chatCountInformation);
void chatLastMessageUpdated(const QString &chatId, const QString &order, const QVariantMap &lastMessage);
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 chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId);
void basicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation);

View file

@ -47,8 +47,10 @@ namespace {
const QString USERNAME("username");
const QString THREAD_ID("thread_id");
const QString VALUE("value");
const QString CHAT_LIST_TYPE("chat_list_type");
const QString _TYPE("@type");
const QString _EXTRA("@extra");
const QString CHAT_LIST_MAIN("chatListMain");
}
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(chatPhotoUpdated(qlonglong, QVariantMap)), this, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)));
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(messageIsPinnedUpdated(qlonglong, qlonglong, bool)), this, SLOT(handleMessageIsPinnedUpdated(qlonglong, qlonglong, bool)));
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);
}
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)
{
LOG("Sending sticker message" << chatId << fileId << replyToMessageId);
@ -1002,6 +1055,20 @@ void TDLibWrapper::toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsU
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)
{
LOG("Set Draft Message" << chatId);
@ -1334,7 +1401,7 @@ void TDLibWrapper::handleChatReceived(const QVariantMap &chatInformation)
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;
emit unreadMessageCountUpdated(messageCountInformation);
}
@ -1342,7 +1409,7 @@ void TDLibWrapper::handleUnreadMessageCountUpdated(const QVariantMap &messageCou
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;
emit unreadChatCountUpdated(chatCountInformation);
}
@ -1449,15 +1516,16 @@ void TDLibWrapper::setInitialParameters()
initialParameters.insert("api_id", TDLIB_API_ID);
initialParameters.insert("api_hash", TDLIB_API_HASH);
initialParameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib");
initialParameters.insert("use_file_database", true);
initialParameters.insert("use_chat_info_database", true);
initialParameters.insert("use_message_database", true);
bool onlineOnlyMode = this->appSettings->onlineOnlyMode();
initialParameters.insert("use_file_database", !onlineOnlyMode);
initialParameters.insert("use_chat_info_database", !onlineOnlyMode);
initialParameters.insert("use_message_database", !onlineOnlyMode);
initialParameters.insert("use_secret_chats", true);
initialParameters.insert("system_language_code", QLocale::system().name());
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
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("use_test_dc", true);
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 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 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 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);
@ -186,6 +188,7 @@ public:
Q_INVOKABLE void searchPublicChats(const QString &query);
Q_INVOKABLE void readAllChatMentions(qlonglong chatId);
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);
// Others (candidates for extraction ;))
@ -211,6 +214,7 @@ signals:
void unreadChatCountUpdated(const QVariantMap &chatCountInformation);
void chatLastMessageUpdated(const QString &chatId, const QString &order, const QVariantMap &lastMessage);
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 chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId);
void userUpdated(const QString &userId, const QVariantMap &userInformation);

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source>
<translation>Verlasse Chat</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Stummschaltung des Chats aufheben</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Chat stummschalten</translation>
</message>
<message>
<source>Unknown</source>
<translation>Unbekannt</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source>
<translation>Neuer geheimer Chat</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>
<name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source>
<translation>Sie</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Stummschaltung des Chats aufheben</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Chat stummschalten</translation>
</message>
<message>
<source>User Info</source>
<translation>Benutzerinfos</translation>
@ -284,6 +276,22 @@
<source>Draft</source>
<translation>Entwurf</translation>
</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>
<name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source>
<translation>Im Chat suchen...</translation>
</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>
<name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source>
<translation>Dokument öffnen</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation>Dokument zu Downloads kopieren</translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -1085,8 +1105,16 @@
<translation>Download fehlgeschlagen.</translation>
</message>
<message>
<source>Connecting to network...</source>
<translation type="unfinished">Verbinde zum Netzwerk...</translation>
<source>Tap on the title bar to filter your chats</source>
<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>
<source>Logging out</source>
@ -1308,13 +1336,19 @@
<source>Channel</source>
<translation>Kanal</translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 Mitglied</translation>
<translation>
<numerusform>%1 Mitglied</numerusform>
<numerusform>%1 Mitglieder</numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 Abonnent</translation>
<translation>
<numerusform>%1 Abonnent</numerusform>
<numerusform>%1 Abonnenten</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source>
<translation>Speicheroptimierer einschalten</translation>
</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>
<name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Download fehlgeschlagen.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source>
<translation>Leaving chat</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message>
<source>Unknown</source>
<translation>Unknown</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source>
<translation>New Secret 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>
<name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source>
<translation>You</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message>
<source>User Info</source>
<translation>User Info</translation>
@ -284,6 +276,22 @@
<source>Draft</source>
<translation>Draft</translation>
</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>
<name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source>
<translation>Search in chat...</translation>
</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>
<name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source>
<translation>Open Document</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation>Copy Document to Downloads</translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -1085,12 +1105,20 @@
<translation>Download failed.</translation>
</message>
<message>
<source>Connecting to network...</source>
<translation type="unfinished">Connecting to network...</translation>
<source>Tap on the title bar to filter your chats</source>
<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>
<source>Logging out</source>
<translation type="unfinished"></translation>
<translation >Logging out</translation>
</message>
</context>
<context>
@ -1308,13 +1336,19 @@
<source>Channel</source>
<translation>Channel</translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 member</translation>
<translation>
<numerusform>%1 member</numerusform>
<numerusform>%1 members</numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 subscriber</translation>
<translation>
<numerusform>%1 subscriber</numerusform>
<numerusform>%1 subscribers</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source>
<translation>Enable storage optimizer</translation>
</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>
<name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Download failed.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1815,21 +1904,21 @@
</message>
<message>
<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>
<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>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished">have added %1 to the chat</translation>
<translation>have added %1 to the chat</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished">have removed %1 from the chat</translation>
<translation>have removed %1 from the chat</translation>
</message>
</context>
</TS>

View file

@ -129,14 +129,6 @@
<source>Leaving chat</source>
<translation>Saliendo de la charla</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>No notificar</translation>
</message>
<message>
<source>Unknown</source>
<translation>Desconocido</translation>
@ -188,6 +180,14 @@
<source>New Secret Chat</source>
<translation>Charla secreta</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>No notificar</translation>
</message>
</context>
<context>
<name>ChatInformationTabItemMembersGroups</name>
@ -245,14 +245,6 @@
<source>You</source>
<translation>Usted</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Notificar</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>No notificar</translation>
</message>
<message>
<source>User Info</source>
<translation>Usuario</translation>
@ -271,16 +263,32 @@
</message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
<translation>Marcar como no leído</translation>
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>Borrador</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Marcar como leído</translation>
</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>Notificar</translation>
</message>
<message>
<source>Mute chat</source>
<translation>No notificar</translation>
</message>
</context>
<context>
<name>ChatPage</name>
@ -403,11 +411,19 @@
</message>
<message>
<source>Search in Chat</source>
<translation type="unfinished"></translation>
<translation>Buscar en charla</translation>
</message>
<message>
<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>
</context>
<context>
@ -474,6 +490,10 @@
<source>Open Document</source>
<translation>Abrir Documento</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -785,21 +805,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>ha añadido %1 a charla</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>ha quitado %1 de charla</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>ha añadido %1 a la charla</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>ha quitado %1 de charla</translation>
</message>
</context>
<context>
@ -836,7 +856,7 @@
</message>
<message>
<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>
<source>Continue</source>
@ -884,7 +904,7 @@
</message>
<message>
<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>
<source>About Fernschreiber</source>
@ -910,7 +930,7 @@
</message>
<message>
<source>Copy Message to Clipboard</source>
<translation>Copiar mensaje</translation>
<translation>Copiar</translation>
</message>
<message>
<source>Message deleted</source>
@ -930,7 +950,7 @@
</message>
<message>
<source>Select Message</source>
<translation>Seleccionar mensaje</translation>
<translation>Seleccionar</translation>
</message>
<message>
<source>Pin Message</source>
@ -938,11 +958,11 @@
</message>
<message>
<source>Message unpinned</source>
<translation type="unfinished">Desanclar mensaje</translation>
<translation>Mensaje desanclado</translation>
</message>
<message>
<source>Unpin Message</source>
<translation type="unfinished"></translation>
<translation>Desanclar mensaje</translation>
</message>
</context>
<context>
@ -1059,19 +1079,31 @@
</message>
<message>
<source>Filter your chats...</source>
<translation type="unfinished"></translation>
<translation>Filtrar las charlas...</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Buscar charlas</translation>
</message>
<message>
<source>Download of %1 successful.</source>
<translation type="unfinished">Bajada de %1 exitosa.</translation>
<translation>Bajada de %1 exitosa.</translation>
</message>
<message>
<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>
<source>Connecting to network...</source>
@ -1094,7 +1126,7 @@
</message>
<message>
<source>Message unpinned</source>
<translation>Desanclar mensaje</translation>
<translation>Mensaje desanclado</translation>
</message>
</context>
<context>
@ -1271,43 +1303,47 @@
<name>SearchChatsPage</name>
<message>
<source>No chats found.</source>
<translation type="unfinished"></translation>
<translation>No se han encontrado charlas.</translation>
</message>
<message>
<source>Searching chats...</source>
<translation type="unfinished"></translation>
<translation>Buscando charlas...</translation>
</message>
<message>
<source>Private Chat</source>
<translation type="unfinished">Privado</translation>
<translation>Privado</translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
<translation>Grupo</translation>
</message>
<message>
<source>Channel</source>
<translation type="unfinished"></translation>
<translation>Canal</translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 miembros</translation>
<translation>
<numerusform>%1 miembros</numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 suscriptores</translation>
<translation>
<numerusform>%1 suscriptores</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Buscar charla</translation>
</message>
<message>
<source>Search a chat...</source>
<translation type="unfinished"></translation>
<translation>A b c</translation>
</message>
<message>
<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>
</context>
<context>
@ -1342,15 +1378,15 @@
</message>
<message>
<source>Notification feedback</source>
<translation>Notificaciones</translation>
<translation>Notificar en </translation>
</message>
<message>
<source>All events</source>
<translation>Todos los eventos</translation>
<translation>Eventos</translation>
</message>
<message>
<source>Only new events</source>
<translation>Sólo nuevos eventos</translation>
<translation>Nuevos eventos</translation>
</message>
<message>
<source>None</source>
@ -1384,6 +1420,22 @@
<source>Enable storage optimizer</source>
<translation>Optimizador de almacenamiento</translation>
</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>
<name>StickerPicker</name>
@ -1411,6 +1463,45 @@
<translation>Error al bajar</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1796,21 +1887,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>ha añadido %1 a la charla</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>ha quitado %1 de la charla</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>ha añadido %1 a la charla</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>ha añadido %1 de la charla</translation>
</message>
</context>
</TS>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source>
<translation>Poistutaan keskustelusta</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
<message>
<source>Unknown</source>
<translation>Tuntematon</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source>
<translation>Uusi salattu keskustelu</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>
<name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source>
<translation>Sinä</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
<message>
<source>User Info</source>
<translation>Käyttäjän tiedot</translation>
@ -274,16 +266,32 @@
</message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
<translation>Merkitse keskustelu lukemattomaksi</translation>
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>Luonnos</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Merkitse keskustelu luetuksi</translation>
</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>Poista keskustelun vaimennus</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Vaimenna keskustelu</translation>
</message>
</context>
<context>
<name>ChatPage</name>
@ -413,11 +421,19 @@
</message>
<message>
<source>Search in Chat</source>
<translation type="unfinished"></translation>
<translation>Etsi keskustelusta</translation>
</message>
<message>
<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>
</context>
<context>
@ -485,6 +501,10 @@
<source>Open Document</source>
<translation>Avaa dokumentti</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -796,21 +816,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>lisäsi käyttäjän %1 keskusteluun</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>posit käyttäjän %1 keskustelusta</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>lisäsit käyttäjän %1 keskusteluun</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>poistit käyttäjän %1 keskustelusta</translation>
</message>
</context>
<context>
@ -949,11 +969,11 @@
</message>
<message>
<source>Message unpinned</source>
<translation type="unfinished">Viestin kiinnitys poistettu</translation>
<translation>Viestin kiinnitys poistettu</translation>
</message>
<message>
<source>Unpin Message</source>
<translation type="unfinished"></translation>
<translation>Poista viestin kiinnitys</translation>
</message>
</context>
<context>
@ -1071,19 +1091,31 @@
</message>
<message>
<source>Filter your chats...</source>
<translation type="unfinished"></translation>
<translation>Suodata keskustelujasi...</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Etsi keskusteluista</translation>
</message>
<message>
<source>Download of %1 successful.</source>
<translation type="unfinished"></translation>
<translation>Tiedoston %1 lataus onnistui.</translation>
</message>
<message>
<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>
<source>Connecting to network...</source>
@ -1291,43 +1323,49 @@
<name>SearchChatsPage</name>
<message>
<source>No chats found.</source>
<translation type="unfinished"></translation>
<translation>Keskusteluja ei löytynyt</translation>
</message>
<message>
<source>Searching chats...</source>
<translation type="unfinished"></translation>
<translation>Etsitään keskusteluja...</translation>
</message>
<message>
<source>Private Chat</source>
<translation type="unfinished">Yksityinen keskustelu</translation>
<translation>Yksityinen keskustelu</translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
<translation>Ryhmä</translation>
</message>
<message>
<source>Channel</source>
<translation type="unfinished"></translation>
<translation>Kanava</translation>
</message>
<message>
<message numerus="yes">
<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 numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 tilaaja</translation>
<translation>
<numerusform>%1 tilaaja</numerusform>
<numerusform>%1 tilaajaa</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Etsi keskusteluja</translation>
</message>
<message>
<source>Search a chat...</source>
<translation type="unfinished"></translation>
<translation>Etsi keskustelua...</translation>
</message>
<message>
<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>
</context>
<context>
@ -1404,6 +1442,22 @@
<source>Enable storage optimizer</source>
<translation>Käytä tallennustilan optimointia</translation>
</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>
<name>StickerPicker</name>
@ -1431,6 +1485,45 @@
<translation>Lataus epäonnistui.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1816,21 +1909,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>lisäsi käyttäjä %1 keskusteluun</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>posit käyttäjän %1 keskustelusta</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>lisäsit käyttäjän %1 keskusteluun</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>poistit käyttäjän %1 keskustelusta</translation>
</message>
</context>
</TS>

View file

@ -129,14 +129,6 @@
<source>Leaving chat</source>
<translation type="unfinished"></translation>
</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>
<source>Unknown</source>
<translation type="unfinished">Ismeretlen</translation>
@ -188,6 +180,14 @@
<source>New Secret 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>
<name>ChatInformationTabItemMembersGroups</name>
@ -245,14 +245,6 @@
<source>You</source>
<translation type="unfinished">Te</translation>
</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>
<source>User Info</source>
<translation type="unfinished"></translation>
@ -281,6 +273,22 @@
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</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>
<name>ChatPage</name>
@ -409,6 +417,14 @@
<source>Search in chat...</source>
<translation type="unfinished"></translation>
</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>
<name>ChatSelectionPage</name>
@ -474,6 +490,10 @@
<source>Open Document</source>
<translation>Dokumentum megyitása</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -1074,8 +1094,16 @@
<translation type="unfinished">A letöltés nem sikerült.</translation>
</message>
<message>
<source>Connecting to network...</source>
<translation type="unfinished">Csatlakozás a hálózathoz...</translation>
<source>Tap on the title bar to filter your chats</source>
<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>
<source>Logging out</source>
@ -1289,13 +1317,17 @@
<source>Channel</source>
<translation type="unfinished"></translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 tag</translation>
<translation type="unfinished">
<numerusform>%1 tag</numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 feliratkozott</translation>
<translation type="unfinished">
<numerusform>%1 feliratkozott</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
@ -1384,6 +1416,22 @@
<source>Enable storage optimizer</source>
<translation type="unfinished"></translation>
</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>
<name>StickerPicker</name>
@ -1411,6 +1459,45 @@
<translation>A letöltés nem sikerült.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source>
<translation>Lascia chat</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Silenzia chat</translation>
</message>
<message>
<source>Unknown</source>
<translation>Sconosciuto</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source>
<translation>Nuova chat segreta</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Silenzia chat</translation>
</message>
</context>
<context>
<name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source>
<translation>Tu</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Riattiva suoni chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Silenzia chat</translation>
</message>
<message>
<source>User Info</source>
<translation>Info utente</translation>
@ -274,15 +266,31 @@
</message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
<translation>Segna chat come non letta</translation>
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>Bozza</translation>
</message>
<message>
<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>
</context>
<context>
@ -413,11 +421,19 @@
</message>
<message>
<source>Search in Chat</source>
<translation type="unfinished"></translation>
<translation>Cerca nella chat</translation>
</message>
<message>
<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>
</context>
<context>
@ -484,6 +500,10 @@
<source>Open Document</source>
<translation>Apri documento</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -795,21 +815,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>ha aggiunto %1 alla chat</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>ha rimosso %1 dalla chat</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>hai aggiunto %1 alla chat</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>hai rimosso %1 dalla chat</translation>
</message>
</context>
<context>
@ -948,11 +968,11 @@
</message>
<message>
<source>Message unpinned</source>
<translation type="unfinished">Messaggio non più in evidenza</translation>
<translation>Messaggio non più in evidenza</translation>
</message>
<message>
<source>Unpin Message</source>
<translation type="unfinished"></translation>
<translation>Togli messaggio in evidenza</translation>
</message>
</context>
<context>
@ -1070,19 +1090,31 @@
</message>
<message>
<source>Filter your chats...</source>
<translation type="unfinished"></translation>
<translation>Filtra le chat...</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Ricerca chat</translation>
</message>
<message>
<source>Download of %1 successful.</source>
<translation type="unfinished">Download di %1 completato.</translation>
<translation>Download di %1 completato.</translation>
</message>
<message>
<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>
<source>Connecting to network...</source>
@ -1290,43 +1322,49 @@
<name>SearchChatsPage</name>
<message>
<source>No chats found.</source>
<translation type="unfinished"></translation>
<translation>Nessuna chat trovata.</translation>
</message>
<message>
<source>Searching chats...</source>
<translation type="unfinished"></translation>
<translation>Ricerca chat...</translation>
</message>
<message>
<source>Private Chat</source>
<translation type="unfinished">Chat privata</translation>
<translation>Chat privata</translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
<translation>Gruppo</translation>
</message>
<message>
<source>Channel</source>
<translation type="unfinished"></translation>
<translation>Canale</translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 membro</translation>
<translation>
<numerusform>%1 membro</numerusform>
<numerusform>%1 membri</numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 abbonato</translation>
<translation>
<numerusform>%1 abbonato</numerusform>
<numerusform>%1 abbonati</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Cerca chat</translation>
</message>
<message>
<source>Search a chat...</source>
<translation type="unfinished"></translation>
<translation>Cerca chat...</translation>
</message>
<message>
<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>
</context>
<context>
@ -1403,6 +1441,22 @@
<source>Enable storage optimizer</source>
<translation>Abilita ottimizzazione memoria</translation>
</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>
<name>StickerPicker</name>
@ -1430,6 +1484,45 @@
<translation>Download non riuscito.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1815,21 +1908,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>ha aggiunto %1 alla chat</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>ha rimosso %1 dalla chat</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>hai aggiunto %1 alla chat</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>hai rimosso %1 dalla chat</translation>
</message>
</context>
</TS>

View file

@ -105,14 +105,6 @@
</context>
<context>
<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>
<source>Unknown</source>
<translation>Nieznany</translation>
@ -194,6 +186,14 @@
<source>New Secret Chat</source>
<translation>Nowy tajny czat</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>
<name>ChatInformationTabItemMembersGroups</name>
@ -251,14 +251,6 @@
<source>You</source>
<translation>Ty</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Wyłącz wyciszenie czatu</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Wycisz czat</translation>
</message>
<message>
<source>User Info</source>
<translation>Informacje o użytkowniku</translation>
@ -277,16 +269,32 @@
</message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
<translation>Oznacz czat jako nieprzeczytany</translation>
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>Kopia robocza</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Oznacz czat jako przeczytany</translation>
</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>Wyłącz wyciszenie czatu</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Wycisz czat</translation>
</message>
</context>
<context>
<name>ChatPage</name>
@ -423,11 +431,19 @@
</message>
<message>
<source>Search in Chat</source>
<translation type="unfinished"></translation>
<translation>Wyszukaj w czacie</translation>
</message>
<message>
<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>
</context>
<context>
@ -494,6 +510,10 @@
<source>Open Document</source>
<translation>Otwórz dokument</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -805,21 +825,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>dodał %1 do czatu</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>usunął %1 z czatu</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>dodsałem %1 do czatu</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>usunąłem %1 z czatu</translation>
</message>
</context>
<context>
@ -958,11 +978,11 @@
</message>
<message>
<source>Message unpinned</source>
<translation type="unfinished">Wiadomość opięta</translation>
<translation>Wiadomość odpięta</translation>
</message>
<message>
<source>Unpin Message</source>
<translation type="unfinished"></translation>
<translation>Odepnij wiadomość</translation>
</message>
</context>
<context>
@ -1081,19 +1101,31 @@
</message>
<message>
<source>Filter your chats...</source>
<translation type="unfinished"></translation>
<translation>Filtruj swoje czaty...</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Wyszukaj czaty</translation>
</message>
<message>
<source>Download of %1 successful.</source>
<translation type="unfinished"></translation>
<translation>Pobieranie %1 zakończone sukcesem</translation>
</message>
<message>
<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>
<source>Connecting to network...</source>
@ -1309,43 +1341,51 @@
<name>SearchChatsPage</name>
<message>
<source>No chats found.</source>
<translation type="unfinished"></translation>
<translation>Brak pasujących czatów</translation>
</message>
<message>
<source>Searching chats...</source>
<translation type="unfinished"></translation>
<translation>Wyszukiwanie czatów...</translation>
</message>
<message>
<source>Private Chat</source>
<translation type="unfinished">Prywatny czat</translation>
<translation>Prywatny czat</translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
<translation>Grupa</translation>
</message>
<message>
<source>Channel</source>
<translation type="unfinished"></translation>
<translation>Kanał</translation>
</message>
<message>
<message numerus="yes">
<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 numerus="yes">
<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>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Wyszukaj czaty</translation>
</message>
<message>
<source>Search a chat...</source>
<translation type="unfinished"></translation>
<translation>Wyszukaj czat...</translation>
</message>
<message>
<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>
</context>
<context>
@ -1422,6 +1462,22 @@
<source>Enable storage optimizer</source>
<translation>Włącz optymalizację pamięci</translation>
</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>
<name>StickerPicker</name>
@ -1449,6 +1505,45 @@
<translation>Nieudane pobieranie</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1834,21 +1929,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation>dodał %1 do czatu</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation>usunął %1 z czatu</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>dodałem %1 do czatu</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation>usunąłem %1 z czatu</translation>
</message>
</context>
</TS>

View file

@ -123,59 +123,51 @@
</message>
<message>
<source>Leave Chat</source>
<translation type="unfinished">Выйти из чата</translation>
<translation>Выйти из чата</translation>
</message>
<message>
<source>Join Chat</source>
<translation type="unfinished">Зайти в чат</translation>
<translation>Зайти в чат</translation>
</message>
<message>
<source>Leaving chat</source>
<translation type="unfinished">Выход из чата</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation type="unfinished">Включить уведомления</translation>
</message>
<message>
<source>Mute Chat</source>
<translation type="unfinished">Выключить уведомления</translation>
<translation>Выход из чата</translation>
</message>
<message>
<source>Unknown</source>
<translation type="unfinished">Неизвестный</translation>
<translation>Неизвестный</translation>
</message>
<message>
<source>Chat Title</source>
<comment>group title header</comment>
<translation type="unfinished">Заголовок чата</translation>
<translation>Заголовок чата</translation>
</message>
<message>
<source>Enter 1-128 characters</source>
<translation type="unfinished">Введите 1-128 символов</translation>
<translation>Введите 1-128 символов</translation>
</message>
<message>
<source>There is no information text available, yet.</source>
<translation type="unfinished">Информация отсутствует</translation>
<translation>Информация отсутствует</translation>
</message>
<message>
<source>Info</source>
<comment>group or user infotext header</comment>
<translation type="unfinished">Информация</translation>
<translation>Информация</translation>
</message>
<message>
<source>Phone Number</source>
<comment>user phone number header</comment>
<translation type="unfinished">Номер телефона</translation>
<translation>Номер телефона</translation>
</message>
<message>
<source>Invite Link</source>
<comment>header</comment>
<translation type="unfinished">Ссылка для приглашения</translation>
<translation>Ссылка для приглашения</translation>
</message>
<message>
<source>The Invite Link has been copied to the clipboard.</source>
<translation type="unfinished">Ссылка для приглашения скопирована в буффер обмена</translation>
<translation>Ссылка для приглашения скопирована в буффер обмена</translation>
</message>
<message>
<source>%1, %2</source>
@ -194,6 +186,14 @@
<source>New Secret Chat</source>
<translation>Новый секретный чат</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Включить уведомления</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Выключить уведомления</translation>
</message>
</context>
<context>
<name>ChatInformationTabItemMembersGroups</name>
@ -228,17 +228,17 @@
<message>
<source>Groups</source>
<comment>Button: groups in common (short)</comment>
<translation type="unfinished">Группы</translation>
<translation>Группы</translation>
</message>
<message>
<source>Members</source>
<comment>Button: Group Members</comment>
<translation type="unfinished">Участники группы</translation>
<translation>Участники группы</translation>
</message>
<message>
<source>Settings</source>
<comment>Button: Chat Settings</comment>
<translation type="unfinished">Настройки</translation>
<translation>Настройки</translation>
</message>
</context>
<context>
@ -251,14 +251,6 @@
<source>You</source>
<translation>Вы</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Включить уведомления</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Выключить уведомления</translation>
</message>
<message>
<source>User Info</source>
<translation>Информация о пользователе</translation>
@ -277,16 +269,32 @@
</message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
<translation>Отметить чат как непрочитанный</translation>
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>Черновик</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Отметить чат как прочитанный</translation>
</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>Включить уведомления</translation>
</message>
<message>
<source>Mute chat</source>
<translation>Выключить уведомления</translation>
</message>
</context>
<context>
<name>ChatPage</name>
@ -423,11 +431,19 @@
</message>
<message>
<source>Search in Chat</source>
<translation type="unfinished"></translation>
<translation>Найти в Чате</translation>
</message>
<message>
<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>
</context>
<context>
@ -438,18 +454,18 @@
</message>
<message>
<source>You don&apos;t have any chats yet.</source>
<translation type="unfinished">Тут пока ничего нет</translation>
<translation>Тут пока ничего нет</translation>
</message>
</context>
<context>
<name>CoverPage</name>
<message>
<source>unread message</source>
<translation>непрочитанное сообщение</translation>
<translation>сообщение</translation>
</message>
<message>
<source>unread messages</source>
<translation>непрочитанных сообщений</translation>
<translation>сообщений</translation>
</message>
<message>
<source>in</source>
@ -469,7 +485,7 @@
</message>
<message>
<source>Connected</source>
<translation>Подключен</translation>
<translation>В сети</translation>
</message>
<message>
<source>Updating content...</source>
@ -477,11 +493,11 @@
</message>
<message>
<source>chat</source>
<translation>чат</translation>
<translation>чате</translation>
</message>
<message>
<source>chats</source>
<translation>чаты</translation>
<translation>чатах</translation>
</message>
</context>
<context>
@ -494,6 +510,10 @@
<source>Open Document</source>
<translation>Открыть документ</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -805,21 +825,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 добавлен в чат</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 удалён из чата</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 добавлены в чат</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 удалены из чата</translation>
</message>
</context>
<context>
@ -958,11 +978,11 @@
</message>
<message>
<source>Message unpinned</source>
<translation type="unfinished">Сообщение откреплено</translation>
<translation>Сообщение откреплено</translation>
</message>
<message>
<source>Unpin Message</source>
<translation type="unfinished"></translation>
<translation type="unfinished">Открепить сообщение</translation>
</message>
</context>
<context>
@ -991,7 +1011,7 @@
</message>
<message>
<source>You don&apos;t have any contacts.</source>
<translation type="unfinished">У вас нет никаких контактов.</translation>
<translation>У вас нет никаких контактов.</translation>
</message>
<message>
<source>Private Chat</source>
@ -1081,19 +1101,31 @@
</message>
<message>
<source>Filter your chats...</source>
<translation type="unfinished"></translation>
<translation>Выбрать чат...</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Найти Чаты</translation>
</message>
<message>
<source>Download of %1 successful.</source>
<translation type="unfinished">Успешно скачано %1.</translation>
<translation>Успешно скачано %1.</translation>
</message>
<message>
<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>
<source>Connecting to network...</source>
@ -1309,43 +1341,51 @@
<name>SearchChatsPage</name>
<message>
<source>No chats found.</source>
<translation type="unfinished"></translation>
<translation>Чаты не найдены</translation>
</message>
<message>
<source>Searching chats...</source>
<translation type="unfinished"></translation>
<translation>Идёт поиск чатов...</translation>
</message>
<message>
<source>Private Chat</source>
<translation type="unfinished">Приватный Чат</translation>
<translation>Приватный Чат</translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
<translation>Группа</translation>
</message>
<message>
<source>Channel</source>
<translation type="unfinished"></translation>
<translation>Канал</translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 участников</translation>
<translation type="unfinished">
<numerusform>%1 участников</numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 подписчиков</translation>
<translation type="unfinished">
<numerusform>%1 подписчиков</numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation>Найти Чаты</translation>
</message>
<message>
<source>Search a chat...</source>
<translation type="unfinished"></translation>
<translation>Поиск...</translation>
</message>
<message>
<source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation>
<translation>Введите не менее 5 символов, чтобы начать поиск</translation>
</message>
</context>
<context>
@ -1422,6 +1462,22 @@
<source>Enable storage optimizer</source>
<translation>Включить оптимизацию хранилища</translation>
</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>
<name>StickerPicker</name>
@ -1449,6 +1505,45 @@
<translation>Ошибка скачивания.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1834,21 +1929,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 добавлен в чат</translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 удалён из чата</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 добавлены в чат</translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation type="unfinished">%1 удалены из чата</translation>
</message>
</context>
</TS>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source>
<translation>Lämnar chatten</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Slå chatten</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Stäng av chatten</translation>
</message>
<message>
<source>Unknown</source>
<translation>Okänd</translation>
@ -191,6 +183,14 @@
<source>New Secret Chat</source>
<translation>Ny hemlig chatt</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>
<name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source>
<translation>Du</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Slå chatten</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Stäng av chatten</translation>
</message>
<message>
<source>User Info</source>
<translation>Användarinfo</translation>
@ -278,12 +270,28 @@
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>Utkast</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Markera chatten som läst</translation>
</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>
<name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source>
<translation>Sök i chatten...</translation>
</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>
<name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source>
<translation>Öppna dokument</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -1085,8 +1105,16 @@
<translation>Nerladdning misslyckades.</translation>
</message>
<message>
<source>Connecting to network...</source>
<translation type="unfinished">Ansluter till nätverket...</translation>
<source>Tap on the title bar to filter your chats</source>
<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>
<source>Logging out</source>
@ -1308,13 +1336,19 @@
<source>Channel</source>
<translation>Kanal</translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation>%1 medlemmar</translation>
<translation>
<numerusform>%1 medlem</numerusform>
<numerusform>%1 medlemmar</numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation>%1 prenumeranter</translation>
<translation>
<numerusform>%1 prenumerant</numerusform>
<numerusform>%1 prenumeranter</numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source>
<translation>Aktivera lagringsoptimering</translation>
</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>
<name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Nerladdning misslyckades.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>

View file

@ -129,14 +129,6 @@
<source>Leaving chat</source>
<translation></translation>
</message>
<message>
<source>Unmute Chat</source>
<translation></translation>
</message>
<message>
<source>Mute Chat</source>
<translation></translation>
</message>
<message>
<source>Unknown</source>
<translation></translation>
@ -188,6 +180,14 @@
<source>New Secret Chat</source>
<translation></translation>
</message>
<message>
<source>Unmute Chat</source>
<translation></translation>
</message>
<message>
<source>Mute Chat</source>
<translation></translation>
</message>
</context>
<context>
<name>ChatInformationTabItemMembersGroups</name>
@ -245,14 +245,6 @@
<source>You</source>
<translation></translation>
</message>
<message>
<source>Unmute Chat</source>
<translation></translation>
</message>
<message>
<source>Mute Chat</source>
<translation></translation>
</message>
<message>
<source>User Info</source>
<translation></translation>
@ -271,16 +263,32 @@
</message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Draft</source>
<translation type="unfinished"></translation>
<translation>稿</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation></translation>
</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></translation>
</message>
<message>
<source>Mute chat</source>
<translation></translation>
</message>
</context>
<context>
<name>ChatPage</name>
@ -403,11 +411,19 @@
</message>
<message>
<source>Search in Chat</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<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>
</context>
<context>
@ -425,7 +441,8 @@
<name>CoverPage</name>
<message>
<source>unread message</source>
<translation></translation>
<translation>
</translation>
</message>
<message>
<source>unread messages</source>
@ -474,6 +491,10 @@
<source>Open Document</source>
<translation></translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -574,7 +595,7 @@
</message>
<message>
<source>sent a voice note</source>
<translation></translation>
<translation></translation>
</message>
<message>
<source>sent a document</source>
@ -785,21 +806,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation> %1 </translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation> %1</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation> %1 </translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation> %1</translation>
</message>
</context>
<context>
@ -1059,11 +1080,11 @@
</message>
<message>
<source>Filter your chats...</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Download of %1 successful.</source>
@ -1074,8 +1095,16 @@
<translation></translation>
</message>
<message>
<source>Connecting to network...</source>
<translation type="unfinished"></translation>
<source>Tap on the title bar to filter your chats</source>
<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>
<source>Logging out</source>
@ -1271,43 +1300,47 @@
<name>SearchChatsPage</name>
<message>
<source>No chats found.</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Searching chats...</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Private Chat</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Channel</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 </translation>
<translation>
<numerusform>%1 </numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 </translation>
<translation>
<numerusform>%1 </numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Search a chat...</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<source>Enter your query to start searching (at least 5 characters needed)</source>
<translation type="unfinished"></translation>
<translation>5</translation>
</message>
</context>
<context>
@ -1384,6 +1417,22 @@
<source>Enable storage optimizer</source>
<translation></translation>
</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>
<name>StickerPicker</name>
@ -1411,6 +1460,45 @@
<translation></translation>
</message>
</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>
<name>WebPagePreview</name>
<message>
@ -1788,7 +1876,7 @@
</message>
<message>
<source>Closed!</source>
<translation>!</translation>
<translation></translation>
</message>
<message>
<source>Pending acknowledgement</source>
@ -1796,21 +1884,21 @@
</message>
<message>
<source>has added %1 to the chat</source>
<translation type="unfinished"></translation>
<translation> %1 </translation>
</message>
<message>
<source>has removed %1 from the chat</source>
<translation type="unfinished"></translation>
<translation> %1</translation>
</message>
<message>
<source>have added %1 to the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation> %1 </translation>
</message>
<message>
<source>have removed %1 from the chat</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
<translation> %1</translation>
</message>
</context>
</TS>

View file

@ -131,14 +131,6 @@
<source>Leaving chat</source>
<translation>Leaving chat</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message>
<source>Unknown</source>
<translation>Unknown</translation>
@ -191,6 +183,14 @@
<source>New Secret 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>
<name>ChatInformationTabItemMembersGroups</name>
@ -248,14 +248,6 @@
<source>You</source>
<translation>You</translation>
</message>
<message>
<source>Unmute Chat</source>
<translation>Unmute Chat</translation>
</message>
<message>
<source>Mute Chat</source>
<translation>Mute Chat</translation>
</message>
<message>
<source>User Info</source>
<translation>User Info</translation>
@ -284,6 +276,22 @@
<source>Draft</source>
<translation type="unfinished"></translation>
</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>
<name>ChatPage</name>
@ -419,6 +427,14 @@
<source>Search in chat...</source>
<translation type="unfinished"></translation>
</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>
<name>ChatSelectionPage</name>
@ -484,6 +500,10 @@
<source>Open Document</source>
<translation>Open Document</translation>
</message>
<message>
<source>Copy Document to Downloads</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupChatPermissionsColumn</name>
@ -1085,8 +1105,16 @@
<translation type="unfinished">Download failed.</translation>
</message>
<message>
<source>Connecting to network...</source>
<translation type="unfinished">Connecting to network...</translation>
<source>Tap on the title bar to filter your chats</source>
<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>
<source>Logging out</source>
@ -1308,13 +1336,19 @@
<source>Channel</source>
<translation type="unfinished"></translation>
</message>
<message>
<message numerus="yes">
<source>%1 members</source>
<translation type="unfinished">%1 member</translation>
<translation type="unfinished">
<numerusform>%1 member</numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<message numerus="yes">
<source>%1 subscribers</source>
<translation type="unfinished">%1 subscriber</translation>
<translation type="unfinished">
<numerusform>%1 subscriber</numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Search Chats</source>
@ -1403,6 +1437,22 @@
<source>Enable storage optimizer</source>
<translation type="unfinished"></translation>
</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>
<name>StickerPicker</name>
@ -1430,6 +1480,45 @@
<translation>Download failed.</translation>
</message>
</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>
<name>WebPagePreview</name>
<message>