diff --git a/harbour-fernschreiber.pro b/harbour-fernschreiber.pro
index 13a2268..82b4a20 100644
--- a/harbour-fernschreiber.pro
+++ b/harbour-fernschreiber.pro
@@ -61,6 +61,8 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/ReplyMarkupButtons.qml \
qml/components/StickerPicker.qml \
qml/components/PhotoTextsListItem.qml \
+ qml/components/TDLibImage.qml \
+ qml/components/TDLibThumbnail.qml \
qml/components/VoiceNoteOverlay.qml \
qml/components/chatInformationPage/ChatInformationEditArea.qml \
qml/components/chatInformationPage/ChatInformationPageContent.qml \
@@ -91,6 +93,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/messageContent/MessageAnimation.qml \
qml/components/messageContent/MessageAudio.qml \
qml/components/messageContent/MessageContentBase.qml \
+ qml/components/messageContent/MessageContentFileInfoBase.qml \
qml/components/messageContent/MessageDocument.qml \
qml/components/messageContent/MessageGame.qml \
qml/components/messageContent/MessageLocation.qml \
diff --git a/images/icon-m-copy-to-folder.svg b/images/icon-m-copy-to-folder.svg
new file mode 100644
index 0000000..71f0b9c
--- /dev/null
+++ b/images/icon-m-copy-to-folder.svg
@@ -0,0 +1,39 @@
+
+
\ No newline at end of file
diff --git a/qml/components/TDLibImage.qml b/qml/components/TDLibImage.qml
new file mode 100644
index 0000000..5128143
--- /dev/null
+++ b/qml/components/TDLibImage.qml
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2020 Sebastian J. Wolf and other contributors
+
+ This file is part of Fernschreiber.
+
+ Fernschreiber is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fernschreiber is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fernschreiber. If not, see .
+*/
+import QtQuick 2.6
+import WerkWolf.Fernschreiber 1.0
+import Sailfish.Silica 1.0
+
+import "../js/debug.js" as Debug
+
+Image {
+ id: tdLibImage
+ property alias fileInformation: file.fileInformation
+ readonly property alias file: file
+ property bool highlighted
+
+ asynchronous: true
+ enabled: !!file.fileId
+ fillMode: Image.PreserveAspectCrop
+ clip: true
+ opacity: status === Image.Ready ? 1.0 : 0.0
+ source: enabled && file.isDownloadingCompleted ? file.path : ""
+ visible: opacity > 0
+ sourceSize {
+ width: width
+ height: height
+ }
+
+ Behavior on opacity { FadeAnimation {} }
+
+ layer {
+ enabled: tdLibImage.enabled && tdLibImage.highlighted
+ effect: PressEffect { source: tdLibImage }
+ }
+
+ TDLibFile {
+ id: file
+ autoLoad: true
+ tdlib: tdLibWrapper
+ }
+}
diff --git a/qml/components/TDLibThumbnail.qml b/qml/components/TDLibThumbnail.qml
new file mode 100644
index 0000000..8454dd7
--- /dev/null
+++ b/qml/components/TDLibThumbnail.qml
@@ -0,0 +1,129 @@
+/*
+ Copyright (C) 2020 Sebastian J. Wolf and other contributors
+
+ This file is part of Fernschreiber.
+
+ Fernschreiber is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fernschreiber is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fernschreiber. If not, see .
+*/
+import QtQuick 2.6
+import Sailfish.Silica 1.0
+import Nemo.Thumbnailer 1.0
+
+Item {
+ id: tdlibThumbnail
+ /*
+ Optional thumbnail, usually as property "thumbnail".
+ The following TDLib objects can have it:
+ - animation
+ - audio (as "album_cover_thumbnail")
+ - document
+ - sticker (no minithumbnail)
+ - video
+ - videoNote
+ - stickerSet (no minithumbnail)
+ - stickerSetInfo (no minithumbnail)
+ - inlineQueryResultArticle (no minithumbnail)
+ - inlineQueryResultContact (no minithumbnail)
+ - inlineQueryResultLocation (no minithumbnail)
+ - inlineQueryResultVenue (no minithumbnail)
+ */
+ property var thumbnail
+ /*
+ Optional minithumbnail, usually as property "minithumbnail".
+ Has data inline: If present, it doesn't need another request.
+ The following TDLib objects can have it:
+ - animation
+ - audio (as "album_cover_minithumbnail")
+ - document
+ - photo / chatPhoto (Note: No thumbnail, so not applicable here)
+ - video
+ - videoNote
+ */
+ property var minithumbnail
+ property bool useBackgroundImage: true
+ property bool highlighted
+
+ property bool isVideo: !!thumbnail && thumbnail.format["@type"] === "thumbnailFormatMpeg4"
+ property string videoMimeType: "video/mp4"
+
+ readonly property bool hasVisibleThumbnail: thumbnailImage.opacity !== 1.0
+ && !(videoThumbnailLoader.item && videoThumbnailLoader.item.opacity === 1.0)
+
+ layer {
+ enabled: highlighted
+ effect: PressEffect { source: tdlibThumbnail }
+ }
+
+ Loader {
+ id: backgroundLoader
+ anchors.fill: parent
+ active: !parent.hasVisibleThumbnail
+ asynchronous: true
+ sourceComponent: !!parent.minithumbnail ? miniThumbnailComponent : parent.useBackgroundImage ? backgroundImageComponent : ""
+ Component {
+ id: backgroundImageComponent
+ BackgroundImage {}
+ }
+ Component {
+ id: miniThumbnailComponent
+ Image {
+ clip: true
+ asynchronous: true
+ fillMode: Image.PreserveAspectCrop
+ opacity: status === Image.Ready ? 1.0 : 0.0
+ smooth: false
+ source: "data:image/jpg;base64," + tdlibThumbnail.miniThumbnail.data
+ visible: opacity > 0
+ Behavior on opacity { FadeAnimation {} }
+ }
+ }
+ }
+
+ // image thumbnail
+ TDLibImage {
+ id: thumbnailImage
+ anchors.fill: parent
+ enabled: !parent.isVideo
+ fileInformation: tdlibThumbnail.thumbnail ? tdlibThumbnail.thumbnail.file : {}
+ onStatusChanged: { //TODO check if this is really how it is ;)
+ if(status === Image.Error) {
+ // in some cases, webp is used (without correct mime type).
+ // we just try it blindly and cross our fingers:
+ tdlibThumbnail.videoMimeType = "image/webp";
+ tdlibThumbnail.isVideo = true;
+ }
+ }
+ }
+
+ // Fallback for video thumbnail format: try to use Nemo.Thumbnailer
+ Loader {
+ id: videoThumbnailLoader
+ active: parent.isVideo
+ asynchronous: true
+ anchors.fill: parent
+ sourceComponent: Component {
+ id: videoThumbnail
+ Thumbnail {
+ id: thumbnail
+ source: thumbnailImage.file.path
+ sourceSize.width: width
+ sourceSize.height: height
+ mimeType: tdlibThumbnail.videoMimeType
+ visible: opacity > 0
+ opacity: status === Thumbnail.Ready ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ }
+ }
+ }
+}
diff --git a/qml/components/messageContent/MessageAudio.qml b/qml/components/messageContent/MessageAudio.qml
index 38d6de6..7f4c55a 100644
--- a/qml/components/messageContent/MessageAudio.qml
+++ b/qml/components/messageContent/MessageAudio.qml
@@ -20,455 +20,77 @@ import QtQuick 2.6
import Sailfish.Silica 1.0
import QtMultimedia 5.6
import "../"
+import "../../js/twemoji.js" as Emoji
import "../../js/functions.js" as Functions
import "../../js/debug.js" as Debug
-MessageContentBase {
- id: audioMessageComponent
+MessageContentFileInfoBase {
+ id: contentItem
- property var audioData: ( rawMessage.content['@type'] === "messageVoiceNote" ) ? rawMessage.content.voice_note : ( ( rawMessage.content['@type'] === "messageAudio" ) ? rawMessage.content.audio : "");
- property string audioUrl;
- property int previewFileId;
- property int audioFileId;
- property bool onScreen: messageListItem ? messageListItem.page.status === PageStatus.Active : true
- property string audioType : "voiceNote";
+ fileInformation: rawMessage.content.audio.audio
+ thumbnail: rawMessage.content.audio.album_cover_thumbnail
+ minithumbnail: rawMessage.content.audio.album_cover_minithumbnail
- height: width / 2
+ primaryText: Emoji.emojify(rawMessage.content.audio.performer, primaryLabel.font.pixelSize)
+ secondaryText: Emoji.emojify(rawMessage.content.audio.title, secondaryLabel.font.pixelSize)
+ tertiaryLabel.visible: (duration || (audioPlayer.duration/1000)) > 0
+ tertiaryText: (audioPlayer.position > 0 || audioPlayer.playbackState === Audio.PlayingState ? (Format.formatDuration(audioPlayer.position/1000, Formatter.DurationShort)+" / ") : "") + Format.formatDuration(contentItem.duration > 0 ? contentItem.duration : (audioPlayer.duration/1000), Formatter.DurationShort)
- function getTimeString(rawSeconds) {
- var minutes = Math.floor( rawSeconds / 60 );
- var seconds = rawSeconds - ( minutes * 60 );
-
- if ( minutes < 10 ) {
- minutes = "0" + minutes;
- }
- if ( seconds < 10 ) {
- seconds = "0" + seconds;
- }
- return minutes + ":" + seconds;
- }
-
- Component.onCompleted: {
- updateAudioThumbnail();
- }
-
- function updateAudioThumbnail() {
- if (audioData) {
- audioType = ( audioData['@type'] === "voiceNote" ) ? "voice" : "audio";
- audioFileId = audioData[audioType].id;
- if (typeof audioData.album_cover_thumbnail !== "undefined") {
- previewFileId = audioData.album_cover_thumbnail.file.id;
- if (audioData.album_cover_thumbnail.file.local.is_downloading_completed) {
- placeholderImage.source = audioData.album_cover_thumbnail.file.local.path;
+ leftButton {
+ icon.source: audioPlayer.playbackState === Audio.PlayingState || (file.isDownloadingActive && audioPlayer.autoPlay) ? "image://theme/icon-m-pause": "image://theme/icon-m-play"
+ onClicked: {
+ if(!file.isDownloadingCompleted && !file.isDownloadingActive) {
+ file.load();
+ audioPlayer.autoPlay = true;
+ } else if(file.isDownloadingActive) {
+ audioPlayer.autoPlay = false;
+ file.cancel();
+ } else if(file.isDownloadingCompleted) {
+ //playPause
+ if(audioPlayer.playbackState === Audio.PlayingState) {
+ audioPlayer.pause();
} else {
- tdLibWrapper.downloadFile(previewFileId);
- }
- } else {
- placeholderImage.source = "image://theme/icon-l-music?white";
- placeholderImage.width = Theme.itemSizeLarge
- placeholderImage.height = Theme.itemSizeLarge
- }
- }
- }
-
- function handlePlay() {
- if (audioData[audioType].local.is_downloading_completed) {
- audioUrl = audioData[audioType].local.path;
- audioComponentLoader.active = true;
- } else {
- audioDownloadBusyIndicator.running = true;
- tdLibWrapper.downloadFile(audioFileId);
- }
- }
-
- Connections {
- target: tdLibWrapper
- onFileUpdated: {
- if (typeof audioData === "object") {
- if (fileInformation.local.is_downloading_completed) {
- if (fileId === previewFileId) {
- audioData.album_cover_thumbnail.file = fileInformation;
- placeholderImage.source = fileInformation.local.path;
- }
- if (fileId === audioFileId) {
- audioDownloadBusyIndicator.running = false;
- audioData[audioType] = fileInformation;
- audioUrl = fileInformation.local.path;
- if (onScreen) {
- audioComponentLoader.active = true;
- }
- }
- }
- if (fileId === audioFileId) {
- downloadingProgressBar.maximumValue = fileInformation.size;
- downloadingProgressBar.value = fileInformation.local.downloaded_size;
+ audioPlayer.play();
}
}
}
+
}
- Image {
- id: placeholderImage
+ property int duration: rawMessage.content.audio.duration
+
+ Audio {
+ id: audioPlayer
+ source: file.isDownloadingCompleted ? file.path : ""
+ autoPlay: false
+ }
+
+ Slider {
width: parent.width
- height: parent.height
- anchors.centerIn: parent
- asynchronous: true
- fillMode: Image.PreserveAspectCrop
- visible: status === Image.Ready ? true : false
- layer.enabled: audioMessageComponent.highlighted
- layer.effect: PressEffect { source: placeholderImage }
- }
-
- BackgroundImage {
- id: backgroundImage
- visible: placeholderImage.status !== Image.Ready
- layer.enabled: audioMessageComponent.highlighted
- layer.effect: PressEffect { source: backgroundImage }
- }
-
- Rectangle {
- id: placeholderBackground
- color: "black"
- opacity: 0.3
- height: parent.height
- width: parent.width
- visible: playButton.visible
- }
- Label {
- visible: !!(audioData.performer || audioData.title)
- color: placeholderBackground.visible ? "white" : Theme.secondaryHighlightColor
- wrapMode: Text.Wrap
anchors {
- fill: placeholderBackground
- margins: Theme.paddingSmall
+ left: parent.left
+ leftMargin: -Screen.width/16
+ right: parent.right
+ rightMargin: -Screen.width/16
+ top: primaryItem.bottom
+ topMargin: -height/3
}
- text: audioData.performer + (audioData.performer && audioData.title ? " - " : "") + audioData.title
- font.pixelSize: Theme.fontSizeTiny
- }
+ minimumValue: 0
+ maximumValue: audioPlayer.duration ? audioPlayer.duration : 0.1
+ stepSize: 1
+ value: audioPlayer.position
+ enabled: audioPlayer.seekable
+ visible: file.isDownloadingCompleted && audioPlayer.playbackState === Audio.PlayingState || audioPlayer.playbackState === Audio.PausedState
+ opacity: visible ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ height: visible ? implicitHeight : 0
+ Behavior on height { NumberAnimation { duration: 200 } }
- Column {
- width: parent.width
- height: downloadingProgressBar.height + audioControlRow.height
- anchors.centerIn: parent
- Row {
- id: audioControlRow
- width: parent.width
- height: Theme.iconSizeLarge
- Item {
- height: Theme.iconSizeLarge
- width: downloadItem.visible ? parent.width / 2 : parent.width
- IconButton {
- id: playButton
- anchors.centerIn: parent
- width: Theme.iconSizeLarge
- height: Theme.iconSizeLarge
- icon {
- source: "image://theme/icon-l-play?white"
- asynchronous: true
- }
- highlighted: audioMessageComponent.highlighted || down
- visible: placeholderImage.status === Image.Ready ? true : false
- onClicked: {
- handlePlay();
- }
- }
- BusyIndicator {
- id: audioDownloadBusyIndicator
- running: false
- visible: running
- anchors.centerIn: parent
- size: BusyIndicatorSize.Large
- }
- }
- Item {
- id: downloadItem
- width: parent.width / 2
- height: Theme.iconSizeLarge
- visible: audioData[audioType].local.is_downloading_completed
- Rectangle {
- color: Theme.primaryColor
- opacity: Theme.opacityFaint
- width: Theme.iconSizeLarge * 0.9
- height: Theme.iconSizeLarge * 0.9
- anchors.centerIn: parent
- radius: width / 2
- }
-
- IconButton {
- id: downloadButton
- anchors.centerIn: parent
- width: Theme.iconSizeLarge
- height: Theme.iconSizeLarge
- icon {
- source: "image://theme/icon-m-cloud-download?white"
- asynchronous: true
- }
- highlighted: audioMessageComponent.highlighted || down
- onClicked: {
- tdLibWrapper.copyFileToDownloads(audioData[audioType].local.path);
- }
- }
- }
- }
- ProgressBar {
- id: downloadingProgressBar
- minimumValue: 0
- maximumValue: 100
- value: 0
- visible: audioDownloadBusyIndicator.visible
- width: parent.width
+ highlighted: contentItem.highlighted || down
+ onReleased: {
+ audioPlayer.seek(Math.floor(value));
+ audioPlayer.play();
}
}
-
-
- Rectangle {
- id: audioErrorShade
- width: parent.width
- height: parent.height
- color: "lightgrey"
- visible: placeholderImage.status === Image.Error ? true : false
- opacity: 0.3
- }
-
- Rectangle {
- id: errorTextOverlay
- color: "black"
- opacity: 0.8
- width: parent.width
- height: parent.height
- visible: false
- }
-
- Text {
- id: errorText
- visible: false
- width: parent.width
- color: Theme.primaryColor
- font.pixelSize: Theme.fontSizeExtraSmall
- horizontalAlignment: Text.AlignHCenter
- anchors {
- verticalCenter: parent.verticalCenter
- }
- wrapMode: Text.Wrap
- text: ""
- }
-
- Loader {
- id: audioComponentLoader
- active: false
- width: parent.width
- height: parent.height
- sourceComponent: audioComponent
- }
-
- Component {
- id: audioComponent
-
- Item {
- width: parent ? parent.width : 0
- height: parent ? parent.height : 0
-
- Connections {
- target: messageAudio
- onPlaying: {
- playButton.visible = false;
- downloadItem.visible = false;
- }
- }
-
- Connections {
- target: audioMessageComponent
- onClicked: {
- if (messageAudio.playbackState === MediaPlayer.PlayingState) {
- messageAudio.pause();
- timeLeftItem.visible = true;
- } else {
- messageAudio.play();
- }
- }
- }
-
- Audio {
- id: messageAudio
-
- Component.onCompleted: {
- if (messageAudio.error === MediaPlayer.NoError) {
- messageAudio.play();
- } else {
- errorText.text = qsTr("Error loading audio! " + messageAudio.errorString)
- errorTextOverlay.visible = true;
- errorText.visible = true;
- }
- }
-
- onStatusChanged: {
- if (status == MediaPlayer.NoMedia) {
- Debug.log("No Media");
- audioBusyIndicator.visible = false;
- }
- if (status == MediaPlayer.Loading) {
- Debug.log("Loading");
- audioBusyIndicator.visible = true;
- }
- if (status == MediaPlayer.Loaded) {
- Debug.log("Loaded");
- audioBusyIndicator.visible = false;
- }
- if (status == MediaPlayer.Buffering) {
- Debug.log("Buffering");
- audioBusyIndicator.visible = true;
- }
- if (status == MediaPlayer.Stalled) {
- Debug.log("Stalled");
- audioBusyIndicator.visible = true;
- }
- if (status == MediaPlayer.Buffered) {
- Debug.log("Buffered");
- audioBusyIndicator.visible = false;
- }
- if (status == MediaPlayer.EndOfMedia) {
- Debug.log("End of Media");
- audioBusyIndicator.visible = false;
- }
- if (status == MediaPlayer.InvalidMedia) {
- Debug.log("Invalid Media");
- audioBusyIndicator.visible = false;
- }
- if (status == MediaPlayer.UnknownStatus) {
- Debug.log("Unknown Status");
- audioBusyIndicator.visible = false;
- }
- }
-
- source: audioUrl
-
- onStopped: {
- playButton.visible = true;
- downloadItem.visible = true;
- audioComponentLoader.active = false;
- }
- }
-
- BusyIndicator {
- id: audioBusyIndicator
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- visible: false
- running: visible
- size: BusyIndicatorSize.Medium
- }
-
- Item {
- id: timeLeftItem
- width: parent.width
- height: parent.height
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- opacity: visible ? 1 : 0
- Behavior on opacity { NumberAnimation {} }
-
- Rectangle {
- id: positionTextOverlay
- color: "black"
- opacity: 0.3
- width: parent.width
- height: parent.height
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- visible: pausedRow.visible
- }
-
- Row {
- id: pausedRow
- width: parent.width
- height: parent.height - ( messageAudioSlider.visible ? messageAudioSlider.height : 0 ) - ( positionText.visible ? positionText.height : 0 )
- visible: audioComponentLoader.active && messageAudio.playbackState === MediaPlayer.PausedState
- Item {
- height: parent.height
- width: parent.width / 2
- IconButton {
- id: pausedPlayButton
- anchors.centerIn: parent
- width: Theme.iconSizeLarge
- height: Theme.iconSizeLarge
- highlighted: audioMessageComponent.highlighted || down
- icon {
- asynchronous: true
- source: "image://theme/icon-l-play?white"
- }
- onClicked: {
- messageAudio.play();
- }
- }
- }
- Item {
- id: pausedDownloadItem
- width: parent.width / 2
- height: parent.height
- Rectangle {
- color: Theme.primaryColor
- opacity: Theme.opacityFaint
- width: Theme.iconSizeLarge * 0.9
- height: Theme.iconSizeLarge * 0.9
- anchors.centerIn: parent
- radius: width / 2
- }
-
- IconButton {
- id: pausedDownloadButton
- anchors.centerIn: parent
- width: Theme.iconSizeLarge
- height: Theme.iconSizeLarge
- icon {
- source: "image://theme/icon-m-cloud-download?white"
- asynchronous: true
- }
- highlighted: audioMessageComponent.highlighted || down
- onClicked: {
- tdLibWrapper.copyFileToDownloads(audioData[audioType].local.path);
- }
- }
- }
- }
-
- Slider {
- id: messageAudioSlider
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: positionText.top
- minimumValue: 0
- maximumValue: messageAudio.duration ? messageAudio.duration : 0.1
- stepSize: 1
- value: messageAudio.position
- enabled: messageAudio.seekable
- visible: (messageAudio.duration > 0)
- highlighted: audioMessageComponent.highlighted || down
- onReleased: {
- messageAudio.seek(Math.floor(value));
- messageAudio.play();
- }
- valueText: getTimeString(Math.round((messageAudio.duration - messageAudioSlider.value) / 1000))
- }
-
- Text {
- id: positionText
- visible: messageAudio.duration === 0
- color: Theme.primaryColor
- font.pixelSize: Theme.fontSizeTiny
- anchors {
- bottom: parent.bottom
- bottomMargin: Theme.paddingSmall
- horizontalCenter: positionTextOverlay.horizontalCenter
- }
- wrapMode: Text.Wrap
- text: ( messageAudio.duration - messageAudio.position ) > 0 ? getTimeString(Math.round((messageAudio.duration - messageAudio.position) / 1000)) : "-:-"
- }
- }
-
- }
-
-
- }
-
}
diff --git a/qml/components/messageContent/MessageContentFileInfoBase.qml b/qml/components/messageContent/MessageContentFileInfoBase.qml
new file mode 100644
index 0000000..11ed18a
--- /dev/null
+++ b/qml/components/messageContent/MessageContentFileInfoBase.qml
@@ -0,0 +1,201 @@
+/*
+ Copyright (C) 2020 Sebastian J. Wolf and other contributors
+
+ This file is part of Fernschreiber.
+
+ Fernschreiber is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fernschreiber is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fernschreiber. If not, see .
+*/
+import QtQuick 2.6
+import Sailfish.Silica 1.0
+import QtMultimedia 5.6
+import WerkWolf.Fernschreiber 1.0
+import QtGraphicalEffects 1.0
+import "../"
+import "../../js/functions.js" as Functions
+import "../../js/twemoji.js" as Emoji
+import "../../js/debug.js" as Debug
+
+MessageContentBase {
+ id: contentItem
+ height: childrenRect.height
+ property alias fileInformation: file.fileInformation
+ property alias primaryLabel: primaryLabel
+ property alias primaryText: primaryLabel.text
+ property alias secondaryLabel: secondaryLabel
+ property alias secondaryText: secondaryLabel.text
+ property alias tertiaryLabel: tertiaryLabel
+ property alias tertiaryText: tertiaryLabel.text
+ property var thumbnail
+ property var minithumbnail
+
+ readonly property alias file: file
+ readonly property alias primaryItem: primaryItem
+ readonly property alias leftButton: leftButton
+ readonly property alias labelsColumn: labelsColumn
+ readonly property alias copyButton: copyButton
+// readonly property alias downloadNeededIndicatorIcon: downloadNeededIndicatorIcon
+
+ TDLibFile {
+ id: file
+ tdlib: tdLibWrapper
+ autoLoad: false
+ }
+
+ Item {
+ id: primaryItem
+ width: parent.width
+ height: Theme.itemSizeLarge
+ Loader {
+ active: contentItem.thumbnail || contentItem.minithumbnail
+ visible: active
+ anchors.fill: leftButton
+ sourceComponent: Component {
+ TDLibThumbnail {
+ opacity: 0.3
+ thumbnail: contentItem.thumbnail
+ minithumbnail: contentItem.minithumbnail
+ }
+ }
+ }
+
+ IconButton {
+ id: leftButton
+ highlighted: down || contentItem.highlighted
+ anchors.verticalCenter: parent.verticalCenter
+ icon {
+ asynchronous: true
+ }
+
+ ProgressCircle {
+ value: file.downloadedSize / file.expectedSize
+ progressColor: Theme.highlightColor
+ backgroundColor: Theme.highlightDimmerColor
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ visible: opacity > 0
+ opacity: file.isDownloadingActive ? 1.0 : 0.0
+ anchors.centerIn: parent
+ Behavior on opacity { FadeAnimation {} }
+ }
+ Rectangle {
+ anchors.centerIn: downloadNeededIndicatorIcon
+ width: downloadNeededIndicatorIcon.width + Theme.paddingMedium
+ height: width
+
+ color: Theme.rgba(Theme.overlayBackgroundColor, 0.2)
+ opacity: file.isDownloadingActive ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ visible: opacity > 0
+ radius: width/2
+ }
+
+ Icon {
+ id: downloadNeededIndicatorIcon
+ source: file.isDownloadingActive || file.isDownloadingCompleted ? "image://theme/icon-s-clear-opaque-cross" : "image://theme/icon-s-cloud-download"
+ asynchronous: true
+ width: Theme.iconSizeExtraSmall
+ height: width
+ visible: opacity > 0
+ sourceSize.width: width
+ sourceSize.height: height
+ opacity: file.isDownloadingCompleted ? 0.0 : 1.0
+ Behavior on opacity { FadeAnimation {} }
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ margins: Theme.paddingSmall
+ }
+ }
+ }
+
+ Column {
+ id: labelsColumn
+ anchors {
+ left: leftButton.right
+ leftMargin: Theme.paddingSmall
+ right: copyButton.left
+ verticalCenter: leftButton.verticalCenter
+ }
+
+ Label {
+ id: primaryLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeSmall
+ fontSizeMode: Text.HorizontalFit
+ minimumPixelSize: Theme.fontSizeTiny
+ color: Theme.highlightColor
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+
+ Label {
+ id: secondaryLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeExtraSmall
+ fontSizeMode: Text.HorizontalFit
+ minimumPixelSize: Theme.fontSizeTiny
+ color: Theme.secondaryHighlightColor
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+ Item {
+ height: sizeLabel.height
+ width: parent.width
+ Label {
+ id: tertiaryLabel
+ font.pixelSize: Theme.fontSizeTiny
+ color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+ Label {
+ id: sizeLabel
+ anchors.right: parent.right
+ font.pixelSize: Theme.fontSizeTiny
+ color: tertiaryLabel.color
+ text: Format.formatFileSize(file.size || file.expectedSize)
+ visible: (file.size || file.expectedSize) > 0
+ truncationMode: TruncationMode.Fade
+ }
+ }
+ }
+ IconButton {
+ id: copyButton
+ anchors {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
+ opacity: file.isDownloadingCompleted ? 1.0 : 0.0
+ width: file.isDownloadingCompleted ? Theme.itemSizeMedium : 0
+ visible: opacity > 0
+
+ Behavior on opacity { FadeAnimation {} }
+ Behavior on width { NumberAnimation { duration: 200 } }
+ icon {
+ asynchronous: true
+ source: "../../../images/icon-m-copy-to-folder.svg"
+ sourceSize {
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ }
+ }
+ onClicked: {
+ tdLibWrapper.copyFileToDownloads(file.path);
+ // not persistent:
+ opacity = 0;
+ width = 0;
+ }
+ }
+ }
+}
diff --git a/qml/components/messageContent/MessageDocument.qml b/qml/components/messageContent/MessageDocument.qml
index 12b6944..df16feb 100644
--- a/qml/components/messageContent/MessageDocument.qml
+++ b/qml/components/messageContent/MessageDocument.qml
@@ -18,108 +18,65 @@
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
+import "../../js/twemoji.js" as Emoji
-MessageContentBase {
+MessageContentFileInfoBase {
+ id: contentItem
+ fileInformation: rawMessage.content.document.document
- id: documentPreviewItem
- height: Theme.itemSizeLarge
+ primaryText: Emoji.emojify(rawMessage.content.document.file_name || "", primaryLabel.font.pixelSize)
+ secondaryText: Emoji.emojify(Functions.enhanceMessageText(rawMessage.content.caption) || "", secondaryLabel.font.pixelSize)
- property var documentData: rawMessage.content.document
- property bool openRequested: false;
+ minithumbnail: rawMessage.content.document.minithumbnail
+ thumbnail: rawMessage.content.document.thumbnail
- Component.onCompleted: {
- updateDocument();
- }
-
- function updateDocument() {
- if (documentData) {
- if (documentData.document.local.is_downloading_completed) {
- downloadDocumentButton.visible = false;
- openDocumentArea.visible = true;
- } else {
- openDocumentArea.visible = false;
- downloadDocumentButton.visible = true;
- }
- }
- }
-
- Connections {
- target: tdLibWrapper
- onFileUpdated: {
- if (documentData) {
- if (!fileInformation.remote.is_uploading_active && fileId === documentData.document.id && fileInformation.local.is_downloading_completed) {
- downloadingProgressBar.visible = false;
- documentData.document = fileInformation;
- downloadDocumentButton.visible = false;
- openDocumentArea.visible = true;
- if (documentPreviewItem.openRequested) {
- documentPreviewItem.openRequested = false;
- tdLibWrapper.openFileOnDevice(documentData.document.local.path);
- }
- }
- if (fileId === documentData.document.id) {
- downloadingProgressBar.maximumValue = fileInformation.size;
- downloadingProgressBar.value = fileInformation.local.downloaded_size;
- }
- }
- }
- }
-
- Button {
- id: downloadDocumentButton
- preferredWidth: Theme.buttonWidthMedium
- anchors.centerIn: parent
- text: qsTr("Download Document")
- visible: false
- highlighted: documentPreviewItem.highlighted || down
+ leftButton {
+ icon.source: Theme.iconForMimeType(rawMessage.content.document.mime_type)
onClicked: {
- downloadDocumentButton.visible = false;
- downloadingProgressBar.visible = true;
- tdLibWrapper.downloadFile(documentData.document.id);
+ if(file.isDownloadingCompleted) {
+ // in this case, the MouseArea should take over
+ tdLibWrapper.openFileOnDevice(file.path);
+ } else if(!file.isDownloadingActive) {
+ file.load();
+ } else {
+ file.cancel()
+ }
}
}
- ProgressBar {
- id: downloadingProgressBar
- minimumValue: 0
- maximumValue: 100
- value: 0
- visible: false
- width: parent.width
- 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.horizontalCenter: parent.horizontalCenter
- text: qsTr("Open Document")
- highlighted: documentPreviewItem.highlighted || down
- onClicked: {
- documentPreviewItem.openRequested = true;
- tdLibWrapper.openFileOnDevice(documentData.document.local.path);
+ states: [
+ State {
+ when: file.isDownloadingCompleted
+ PropertyChanges { target: openMouseArea; enabled: true }
+ PropertyChanges {
+ target: primaryLabel
+ color: (contentItem.highlighted || openMouseArea.pressed) ? Theme.highlightColor : Theme.primaryColor
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ color: (contentItem.highlighted || openMouseArea.pressed) ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ }
+ PropertyChanges {
+ target: tertiaryLabel
+ color: (contentItem.highlighted || openMouseArea.pressed) ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ }
+ PropertyChanges {
+ target: leftButton
+ highlighted: contentItem.highlighted || openMouseArea.pressed
}
}
- 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);
- }
+ ]
+ MouseArea {
+ id: openMouseArea
+ enabled: file.isDownloadingCompleted
+ visible: enabled
+ anchors {
+ fill: primaryItem
+ rightMargin: copyButton.width
+ }
+ onClicked: {
+ tdLibWrapper.openFileOnDevice(file.path);
}
}
}
diff --git a/qml/components/messageContent/MessageLocation.qml b/qml/components/messageContent/MessageLocation.qml
index c6c46d7..1b26e18 100644
--- a/qml/components/messageContent/MessageLocation.qml
+++ b/qml/components/messageContent/MessageLocation.qml
@@ -29,7 +29,7 @@ MessageContentBase {
property string chatId: rawMessage.chat_id
property var pictureFileInformation;
- height: width / 2
+ height: width * 0.66666666;
property string fileExtra
Component.onCompleted: {
diff --git a/qml/components/messageContent/MessageVoiceNote.qml b/qml/components/messageContent/MessageVoiceNote.qml
index 91b403e..5a387e0 100644
--- a/qml/components/messageContent/MessageVoiceNote.qml
+++ b/qml/components/messageContent/MessageVoiceNote.qml
@@ -18,4 +18,11 @@
*/
import QtQuick 2.6
-MessageAudio {}
+MessageAudio {
+ fileInformation: rawMessage.content.voice_note.voice
+ primaryText: qsTr("Voice Note")
+ secondaryText: ""
+ duration: rawMessage.content.voice_note.duration
+ thumbnail: null
+ minithumbnail: null
+}
diff --git a/qml/js/functions.js b/qml/js/functions.js
index db2705f..b740273 100644
--- a/qml/js/functions.js
+++ b/qml/js/functions.js
@@ -79,7 +79,7 @@ function getMessageText(message, simple, currentUserId, ignoreEntities) {
}
case 'messageDocument':
if (message.content.document.file_name !== "") {
- return simple ? qsTr("Document: %1").arg(message.content.document.file_name) : (message.content.document.file_name + ( message.content.caption.text !== "" ? ("
" + enhanceMessageText(message.content.caption, ignoreEntities) ) : "")).trim();
+ return simple ? qsTr("Document: %1").arg(message.content.document.file_name) : (message.content.caption.text !== "" ? enhanceMessageText(message.content.caption, ignoreEntities) : "").trim();
} else {
return simple ? (myself ? qsTr("sent a document", "myself") : qsTr("sent a document")) : "";
}
diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml
index 30ec601..0e59b1b 100644
--- a/qml/pages/ChatPage.qml
+++ b/qml/pages/ChatPage.qml
@@ -1062,9 +1062,8 @@ Page {
return Functions.getVideoHeight(parentWidth, content.video);
case "messageAudio":
case "messageVoiceNote":
- return Theme.itemSizeLarge;
case "messageDocument":
- return Theme.itemSizeSmall;
+ return Theme.itemSizeLarge;
case "messageGame":
return parentWidth * 0.66666666 + Theme.itemSizeLarge; // 2 / 3;
case "messageLocation":
diff --git a/src/tdlibfile.cpp b/src/tdlibfile.cpp
index dece939..2326435 100644
--- a/src/tdlibfile.cpp
+++ b/src/tdlibfile.cpp
@@ -194,6 +194,16 @@ void TDLibFile::setAutoLoad(bool enableAutoLoad)
}
}
+bool TDLibFile::cancel()
+{
+ if (id && tdLibWrapper && is_downloading_active) {
+ tdLibWrapper->cancelDownloadFile(id);
+ tdLibWrapper->deleteFile(id);
+ return true;
+ }
+ return false;
+}
+
bool TDLibFile::load()
{
// Manual load ignores hold-off timer
diff --git a/src/tdlibfile.h b/src/tdlibfile.h
index 0616271..41d17a1 100644
--- a/src/tdlibfile.h
+++ b/src/tdlibfile.h
@@ -78,6 +78,7 @@ public:
bool isUploadingActive() const;
bool isUploadingCompleted() const;
+ Q_INVOKABLE bool cancel();
Q_INVOKABLE bool load();
signals:
diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp
index f8cdcb8..5446f7e 100644
--- a/src/tdlibwrapper.cpp
+++ b/src/tdlibwrapper.cpp
@@ -1146,6 +1146,37 @@ void TDLibWrapper::sendBotStartMessage(qlonglong botUserId, qlonglong chatId, co
this->sendRequest(requestObject);
}
+void TDLibWrapper::cancelDownloadFile(int fileId)
+{
+ LOG("Cancel Download File" << fileId);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "cancelDownloadFile");
+ requestObject.insert("file_id", fileId);
+ requestObject.insert("only_if_pending", false);
+
+ this->sendRequest(requestObject);
+}
+
+void TDLibWrapper::cancelUploadFile(int fileId)
+{
+ LOG("Cancel Upload File" << fileId);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "cancelUploadFile");
+ requestObject.insert("file_id", fileId);
+
+ this->sendRequest(requestObject);
+}
+
+void TDLibWrapper::deleteFile(int fileId)
+{
+ LOG("Delete cached File" << fileId);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "deleteFile");
+ requestObject.insert("file_id", fileId);
+
+ this->sendRequest(requestObject);
+}
+
void TDLibWrapper::searchEmoji(const QString &queryString)
{
LOG("Searching emoji" << queryString);
diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h
index 914ccf6..8b1ce90 100644
--- a/src/tdlibwrapper.h
+++ b/src/tdlibwrapper.h
@@ -193,6 +193,9 @@ public:
Q_INVOKABLE void getInlineQueryResults(qlonglong botUserId, qlonglong chatId, const QVariantMap &userLocation, const QString &query, const QString &offset, const QString &extra);
Q_INVOKABLE void sendInlineQueryResultMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &queryId, const QString &resultId);
Q_INVOKABLE void sendBotStartMessage(qlonglong botUserId, qlonglong chatId, const QString ¶meter, const QString &extra);
+ Q_INVOKABLE void cancelDownloadFile(int fileId);
+ Q_INVOKABLE void cancelUploadFile(int fileId);
+ Q_INVOKABLE void deleteFile(int fileId);
// Others (candidates for extraction ;))
Q_INVOKABLE void searchEmoji(const QString &queryString);
diff --git a/translations/harbour-fernschreiber-de.ts b/translations/harbour-fernschreiber-de.ts
index e3e2b09..28add57 100644
--- a/translations/harbour-fernschreiber-de.ts
+++ b/translations/harbour-fernschreiber-de.ts
@@ -930,21 +930,6 @@
Über Fernschreiber
-
- MessageDocument
-
-
- Dokument herunterladen
-
-
-
- Dokument öffnen
-
-
-
- Dokument zu Downloads kopieren
-
-
MessageListViewItem
@@ -1075,6 +1060,13 @@
via %1
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-en.ts b/translations/harbour-fernschreiber-en.ts
index 5d697bd..3d0fcce 100644
--- a/translations/harbour-fernschreiber-en.ts
+++ b/translations/harbour-fernschreiber-en.ts
@@ -930,21 +930,6 @@
About Fernschreiber
-
- MessageDocument
-
-
- Download Document
-
-
-
- Open Document
-
-
-
- Copy Document to Downloads
-
-
MessageListViewItem
@@ -1075,6 +1060,13 @@
via %1
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-es.ts b/translations/harbour-fernschreiber-es.ts
index 7cb5598..6bd0ef7 100644
--- a/translations/harbour-fernschreiber-es.ts
+++ b/translations/harbour-fernschreiber-es.ts
@@ -930,21 +930,6 @@
Acerca de
-
- MessageDocument
-
-
- Bajar Documento
-
-
-
- Abrir Documento
-
-
-
- Copiar documento a Downloads
-
-
MessageListViewItem
@@ -1062,7 +1047,7 @@
number of total votes
-
+ %Ln total de votos
@@ -1075,6 +1060,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-fi.ts b/translations/harbour-fernschreiber-fi.ts
index 89dd26a..f1595d0 100644
--- a/translations/harbour-fernschreiber-fi.ts
+++ b/translations/harbour-fernschreiber-fi.ts
@@ -931,21 +931,6 @@
Tietoa Fernschreiberista
-
- MessageDocument
-
-
- Lataa dokumentti
-
-
-
- Avaa dokumentti
-
-
-
-
-
-
MessageListViewItem
@@ -1063,8 +1048,8 @@
number of total votes
-
-
+ yhteensä %Ln ääni
+ yhteensä %Ln ääntä
@@ -1076,6 +1061,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-hu.ts b/translations/harbour-fernschreiber-hu.ts
index 9e765a5..e289d2a 100644
--- a/translations/harbour-fernschreiber-hu.ts
+++ b/translations/harbour-fernschreiber-hu.ts
@@ -918,21 +918,6 @@
A Fernschreiber névjegye
-
- MessageDocument
-
-
- Dokumentum letöltése
-
-
-
- Dokumentum megyitása
-
-
-
-
-
-
MessageListViewItem
@@ -1059,6 +1044,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-it.ts b/translations/harbour-fernschreiber-it.ts
index 7fbe72d..3feba91 100644
--- a/translations/harbour-fernschreiber-it.ts
+++ b/translations/harbour-fernschreiber-it.ts
@@ -930,21 +930,6 @@
Informazioni su Fernschreiber
-
- MessageDocument
-
-
- Scarica documento
-
-
-
- Apri documento
-
-
-
-
-
-
MessageListViewItem
@@ -1075,6 +1060,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-pl.ts b/translations/harbour-fernschreiber-pl.ts
index 2164cd2..23e0666 100644
--- a/translations/harbour-fernschreiber-pl.ts
+++ b/translations/harbour-fernschreiber-pl.ts
@@ -942,21 +942,6 @@
O Fernschreiber
-
- MessageDocument
-
-
- Pobierz dokument
-
-
-
- Otwórz dokument
-
-
-
-
-
-
MessageListViewItem
@@ -1091,6 +1076,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-ru.ts b/translations/harbour-fernschreiber-ru.ts
index 2ec6396..23bfce7 100644
--- a/translations/harbour-fernschreiber-ru.ts
+++ b/translations/harbour-fernschreiber-ru.ts
@@ -942,21 +942,6 @@
О программе
-
- MessageDocument
-
-
- Скачать документ
-
-
-
- Открыть документ
-
-
-
- Сохранить в Загрузках
-
-
MessageListViewItem
@@ -1091,6 +1076,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-sv.ts b/translations/harbour-fernschreiber-sv.ts
index 3947051..48a27cd 100644
--- a/translations/harbour-fernschreiber-sv.ts
+++ b/translations/harbour-fernschreiber-sv.ts
@@ -930,21 +930,6 @@
Om Fernschreiber
-
- MessageDocument
-
-
- Ladda ner dokument
-
-
-
- Öppna dokument
-
-
-
-
-
-
MessageListViewItem
@@ -1075,6 +1060,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber-zh_CN.ts b/translations/harbour-fernschreiber-zh_CN.ts
index 780e8cf..56823f6 100644
--- a/translations/harbour-fernschreiber-zh_CN.ts
+++ b/translations/harbour-fernschreiber-zh_CN.ts
@@ -918,21 +918,6 @@
关于 Fernschreiber
-
- MessageDocument
-
-
- 下载文档
-
-
-
- 打开文档
-
-
-
- 复制文档到下载
-
-
MessageListViewItem
@@ -1047,7 +1032,7 @@
number of total votes
-
+ 总计 %Ln 次投票
@@ -1059,6 +1044,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage
diff --git a/translations/harbour-fernschreiber.ts b/translations/harbour-fernschreiber.ts
index 2d67e32..bfdd7b4 100644
--- a/translations/harbour-fernschreiber.ts
+++ b/translations/harbour-fernschreiber.ts
@@ -930,21 +930,6 @@
About Fernschreiber
-
- MessageDocument
-
-
- Download Document
-
-
-
- Open Document
-
-
-
-
-
-
MessageListViewItem
@@ -1075,6 +1060,13 @@
+
+ MessageVoiceNote
+
+
+
+
+
NewChatPage