Merge pull request #330 from jgibbon/feature/messageContent_photo
Feature/message content photo
This commit is contained in:
commit
bd53d1435d
10 changed files with 189 additions and 181 deletions
|
@ -62,6 +62,8 @@ DISTFILES += qml/harbour-fernschreiber.qml \
|
|||
qml/components/StickerPicker.qml \
|
||||
qml/components/PhotoTextsListItem.qml \
|
||||
qml/components/TDLibImage.qml \
|
||||
qml/components/TDLibMinithumbnail.qml \
|
||||
qml/components/TDLibPhoto.qml \
|
||||
qml/components/TDLibThumbnail.qml \
|
||||
qml/components/VoiceNoteOverlay.qml \
|
||||
qml/components/chatInformationPage/ChatInformationEditArea.qml \
|
||||
|
|
56
qml/components/TDLibMinithumbnail.qml
Normal file
56
qml/components/TDLibMinithumbnail.qml
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
property var minithumbnail
|
||||
property bool highlighted
|
||||
anchors.fill: parent
|
||||
active: !!minithumbnail
|
||||
visible: active
|
||||
sourceComponent: Component {
|
||||
Item {
|
||||
Image {
|
||||
id: minithumbnailImage
|
||||
anchors.fill: parent
|
||||
source: "data:image/jpg;base64,"+minithumbnail.data
|
||||
asynchronous: true
|
||||
fillMode: tdLibImage.fillMode
|
||||
opacity: status === Image.Ready ? 1.0 : 0.0
|
||||
cache: false
|
||||
visible: opacity > 0
|
||||
Behavior on opacity { FadeAnimation {} }
|
||||
|
||||
layer {
|
||||
enabled: loader.highlighted
|
||||
effect: PressEffect { source: minithumbnailImage }
|
||||
}
|
||||
}
|
||||
|
||||
FastBlur {
|
||||
anchors.fill: parent
|
||||
source: minithumbnailImage
|
||||
radius: Theme.paddingLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
69
qml/components/TDLibPhoto.qml
Normal file
69
qml/components/TDLibPhoto.qml
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import WerkWolf.Fernschreiber 1.0
|
||||
import Sailfish.Silica 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
id: tdLibPhoto
|
||||
property var photo
|
||||
property bool highlighted
|
||||
readonly property alias fileInformation: tdLibImage.fileInformation
|
||||
readonly property alias image: tdLibImage
|
||||
|
||||
onWidthChanged: setImageFile()
|
||||
onPhotoChanged: setImageFile()
|
||||
|
||||
function setImageFile() {
|
||||
if (photo) {
|
||||
var photoSize;
|
||||
for (var i = 0; i < photo.sizes.length; i++) {
|
||||
photoSize = photo.sizes[i].photo;
|
||||
if (photo.sizes[i].width >= width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (photoSize && photoSize.id !== tdLibImage.fileInformation.id) {
|
||||
tdLibImage.fileInformation = photoSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TDLibMinithumbnail {
|
||||
id: minithumbnailLoader
|
||||
active: !!minithumbnail && tdLibImage.opacity < 1.0
|
||||
minithumbnail: tdLibPhoto.photo.minithumbnail
|
||||
highlighted: parent.highlighted
|
||||
}
|
||||
|
||||
BackgroundImage {
|
||||
visible: tdLibImage.opacity < 1.0
|
||||
}
|
||||
|
||||
TDLibImage {
|
||||
id: tdLibImage
|
||||
width: parent.width //don't use anchors here for easier custom scaling
|
||||
height: parent.height
|
||||
cache: false
|
||||
highlighted: parent.highlighted
|
||||
}
|
||||
|
||||
Component.onCompleted: setImageFile()
|
||||
}
|
|
@ -50,7 +50,7 @@ Item {
|
|||
- video
|
||||
- videoNote
|
||||
*/
|
||||
property var minithumbnail
|
||||
property alias minithumbnail: minithumbnailLoader.minithumbnail
|
||||
property bool useBackgroundImage: true
|
||||
property bool highlighted
|
||||
|
||||
|
@ -65,29 +65,12 @@ Item {
|
|||
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 {} }
|
||||
}
|
||||
TDLibMinithumbnail {
|
||||
id: minithumbnailLoader
|
||||
active: !!minithumbnail && thumbnailImage.opacity < 1.0
|
||||
}
|
||||
BackgroundImage {
|
||||
visible: tdlibThumbnail.useBackgroundImage && thumbnailImage.opacity < 1.0
|
||||
}
|
||||
|
||||
// image thumbnail
|
||||
|
|
|
@ -17,52 +17,13 @@
|
|||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import QtMultimedia 5.6
|
||||
import WerkWolf.Fernschreiber 1.0
|
||||
import "../"
|
||||
|
||||
InlineQueryResult {
|
||||
id: queryResultItem
|
||||
|
||||
|
||||
TDLibFile {
|
||||
id: file
|
||||
tdlib: tdLibWrapper
|
||||
autoLoad: true
|
||||
}
|
||||
|
||||
Loader {
|
||||
asynchronous: true
|
||||
active: file.isDownloadingCompleted
|
||||
TDLibPhoto {
|
||||
anchors.fill: parent
|
||||
opacity: item && item.status === Image.Ready ? 1.0 : 0.0
|
||||
Behavior on opacity { FadeAnimation {} }
|
||||
sourceComponent: Component {
|
||||
Image {
|
||||
id: image
|
||||
source: file.path
|
||||
asynchronous: true
|
||||
clip: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
layer.enabled: queryResultItem.pressed
|
||||
layer.effect: PressEffect { source: image }
|
||||
}
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (model.photo) {
|
||||
// Check first which size fits best...
|
||||
var photo
|
||||
for (var i = 0; i < model.photo.sizes.length; i++) {
|
||||
photo = model.photo.sizes[i].photo
|
||||
if (model.photo.sizes[i].width >= queryResultItem.width) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (photo) {
|
||||
file.fileInformation = photo
|
||||
}
|
||||
}
|
||||
photo: model.photo
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,72 +22,51 @@ import Sailfish.Silica 1.0
|
|||
import "../"
|
||||
|
||||
MessageContentBase {
|
||||
|
||||
id: imagePreviewItem
|
||||
|
||||
property var locationData : ( rawMessage.content['@type'] === "messageLocation" ) ? rawMessage.content.location : ( ( rawMessage.content['@type'] === "messageVenue" ) ? rawMessage.content.venue.location : "" )
|
||||
|
||||
property string chatId: rawMessage.chat_id
|
||||
property var pictureFileInformation;
|
||||
id: contentItem
|
||||
height: width * 0.66666666;
|
||||
property string fileExtra
|
||||
|
||||
Component.onCompleted: {
|
||||
updatePicture();
|
||||
}
|
||||
property var locationData : rawMessage.content.location
|
||||
property string fileExtra;
|
||||
|
||||
onClicked: {
|
||||
if(!processLauncher.launchProgram('harbour-pure-maps', ["geo:"+locationData.latitude+","+locationData.longitude])) {
|
||||
imageNotification.show(qsTr("Install Pure Maps to inspect this location."));
|
||||
}
|
||||
}
|
||||
onLocationDataChanged: updatePicture()
|
||||
onWidthChanged: updatePicture()
|
||||
|
||||
function updatePicture() {
|
||||
imagePreviewItem.pictureFileInformation = null;
|
||||
if (locationData) {
|
||||
fileExtra = "location:" + locationData.latitude + ":" + locationData.longitude + ":" + Math.round(imagePreviewItem.width) + ":" + Math.round(imagePreviewItem.height);
|
||||
tdLibWrapper.getMapThumbnailFile(chatId, locationData.latitude, locationData.longitude, Math.round(imagePreviewItem.width), Math.round(imagePreviewItem.height), fileExtra);
|
||||
fileExtra = "location:" + locationData.latitude + ":" + locationData.longitude + ":" + Math.round(contentItem.width) + ":" + Math.round(contentItem.height);
|
||||
tdLibWrapper.getMapThumbnailFile(rawMessage.chat_id, locationData.latitude, locationData.longitude, Math.round(contentItem.width), Math.round(contentItem.height), fileExtra);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: tdLibWrapper
|
||||
onFileUpdated: {
|
||||
if(fileInformation["@extra"] !== imagePreviewItem.fileExtra && (!imagePreviewItem.pictureFileInformation || imagePreviewItem.pictureFileInformation.id !== fileInformation.id)) {
|
||||
return;
|
||||
if(fileInformation["@extra"] === contentItem.fileExtra) {
|
||||
if(fileInformation.id !== image.file.fileId) {
|
||||
image.fileInformation = fileInformation
|
||||
}
|
||||
if(fileInformation.local.is_downloading_completed) {
|
||||
singleImage.source = fileInformation.local.path;
|
||||
} else if(fileInformation.local.can_be_downloaded && !fileInformation.local.is_downloading_active) {
|
||||
tdLibWrapper.downloadFile(fileInformation.id);
|
||||
}
|
||||
|
||||
imagePreviewItem.pictureFileInformation = fileInformation;
|
||||
}
|
||||
}
|
||||
|
||||
AppNotification {
|
||||
id: imageNotification
|
||||
}
|
||||
|
||||
Image {
|
||||
id: singleImage
|
||||
width: parent.width - Theme.paddingSmall
|
||||
height: parent.height - Theme.paddingSmall
|
||||
anchors.centerIn: parent
|
||||
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
autoTransform: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation {} }
|
||||
|
||||
layer.enabled: imagePreviewItem.highlighted
|
||||
layer.effect: PressEffect { source: singleImage }
|
||||
TDLibImage {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
cache: false
|
||||
highlighted: contentItem.highlighted
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
width: markerImage.width
|
||||
height: markerImage.height * 1.75 // 0.875 (vertical pin point) * 2
|
||||
Image {
|
||||
Icon {
|
||||
id: markerImage
|
||||
source: 'image://theme/icon-m-location'
|
||||
}
|
||||
|
@ -105,7 +84,10 @@ MessageContentBase {
|
|||
}
|
||||
|
||||
BackgroundImage {
|
||||
visible: singleImage.status !== Image.Ready
|
||||
visible: image.status !== Image.Ready
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
updatePicture();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,68 +18,26 @@
|
|||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import WerkWolf.Fernschreiber 1.0
|
||||
import "../"
|
||||
|
||||
MessageContentBase {
|
||||
id: imagePreviewItem
|
||||
|
||||
readonly property int defaultHeight: Math.round(width * 2 / 3)
|
||||
|
||||
height: singleImage.visible ? Math.min(defaultHeight, singleImage.bestHeight + Theme.paddingSmall) : defaultHeight
|
||||
height: Math.max(Theme.itemSizeExtraSmall, Math.min(defaultHeight, width / (biggest.width/biggest.height)))
|
||||
readonly property int defaultHeight: Math.round(width * 0.66666666)
|
||||
readonly property var biggest: rawMessage.content.photo.sizes[rawMessage.content.photo.sizes.length - 1];
|
||||
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("../../pages/ImagePage.qml"), {
|
||||
"photoData" : imagePreviewItem.rawMessage.content.photo
|
||||
"photoData" : photo.photo,
|
||||
// "pictureFileInformation" : photo.fileInformation
|
||||
})
|
||||
}
|
||||
|
||||
Component.onCompleted: updateImage()
|
||||
|
||||
onRawMessageChanged: updateImage()
|
||||
|
||||
function updateImage() {
|
||||
if (rawMessage.content.photo) {
|
||||
// Check first which size fits best...
|
||||
var photo
|
||||
var photoData = rawMessage.content.photo
|
||||
for (var i = 0; i < photoData.sizes.length; i++) {
|
||||
photo = photoData.sizes[i].photo
|
||||
if (photoData.sizes[i].width >= imagePreviewItem.width) {
|
||||
break
|
||||
TDLibPhoto {
|
||||
id: photo
|
||||
anchors.fill: parent
|
||||
photo: rawMessage.content.photo
|
||||
highlighted: parent.highlighted
|
||||
}
|
||||
}
|
||||
if (photo) {
|
||||
imageFile.fileInformation = photo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TDLibFile {
|
||||
id: imageFile
|
||||
tdlib: tdLibWrapper
|
||||
autoLoad: true
|
||||
}
|
||||
|
||||
Image {
|
||||
id: singleImage
|
||||
width: parent.width - Theme.paddingSmall
|
||||
height: parent.height - Theme.paddingSmall
|
||||
readonly property int bestHeight: (status === Image.Ready) ? Math.round(implicitHeight * width / implicitWidth) : 0
|
||||
anchors.centerIn: parent
|
||||
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
autoTransform: true
|
||||
asynchronous: true
|
||||
source: imageFile.isDownloadingCompleted ? imageFile.path : ""
|
||||
visible: status === Image.Ready
|
||||
opacity: visible ? 1 : 0
|
||||
Behavior on opacity { FadeAnimation {} }
|
||||
layer.enabled: imagePreviewItem.highlighted
|
||||
layer.effect: PressEffect { source: singleImage }
|
||||
}
|
||||
|
||||
BackgroundImage {
|
||||
visible: singleImage.status !== Image.Ready
|
||||
visible: !rawMessage.content.photo.minithumbnail && photo.image.status !== Image.Ready
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
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
|
||||
|
||||
MessageLocation {}
|
||||
MessageLocation {
|
||||
locationData: rawMessage.content.venue.location
|
||||
}
|
||||
|
|
|
@ -1059,7 +1059,7 @@ Page {
|
|||
function getContentComponentHeight(contentType, content, parentWidth) {
|
||||
switch(contentType) {
|
||||
case "messageAnimation":
|
||||
return Functions.getVideoHeight(parentWidth, content.video);
|
||||
return Functions.getVideoHeight(parentWidth, content.animation);
|
||||
case "messageAudio":
|
||||
case "messageVoiceNote":
|
||||
case "messageDocument":
|
||||
|
@ -1067,9 +1067,12 @@ Page {
|
|||
case "messageGame":
|
||||
return parentWidth * 0.66666666 + Theme.itemSizeLarge; // 2 / 3;
|
||||
case "messageLocation":
|
||||
case "messagePhoto":
|
||||
case "messageVenue":
|
||||
return parentWidth * 0.66666666; // 2 / 3;
|
||||
case "messagePhoto":
|
||||
var biggest = content.photo.sizes[content.photo.sizes.length - 1];
|
||||
var aspectRatio = biggest.width/biggest.height;
|
||||
return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio));
|
||||
case "messagePoll":
|
||||
return Theme.itemSizeSmall * (4 + content.poll.options);
|
||||
case "messageSticker":
|
||||
|
|
|
@ -47,28 +47,20 @@ Page {
|
|||
|
||||
Component.onCompleted: {
|
||||
if (photoData) {
|
||||
// Check first which size fits best...
|
||||
var photo
|
||||
var biggestIndex = -1
|
||||
for (var i = 0; i < photoData.sizes.length; i++) {
|
||||
imagePage.imageWidth = photoData.sizes[i].width;
|
||||
imagePage.imageHeight = photoData.sizes[i].height;
|
||||
photo = photoData.sizes[i].photo
|
||||
if (photoData.sizes[i].width >= imagePage.width) {
|
||||
break;
|
||||
if (biggestIndex === -1 || photoData.sizes[i].width > photoData.sizes[biggestIndex].width) {
|
||||
biggestIndex = i;
|
||||
}
|
||||
}
|
||||
if (photo) {
|
||||
imageFile.fileInformation = photo
|
||||
if (biggestIndex > -1) {
|
||||
imagePage.imageWidth = photoData.sizes[biggestIndex].width;
|
||||
imagePage.imageHeight = photoData.sizes[biggestIndex].height;
|
||||
singleImage.fileInformation = photoData.sizes[biggestIndex].photo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TDLibFile {
|
||||
id: imageFile
|
||||
tdlib: tdLibWrapper
|
||||
autoLoad: true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: tdLibWrapper
|
||||
onCopyToDownloadsSuccessful: {
|
||||
|
@ -86,11 +78,11 @@ Page {
|
|||
interactive: !imageOnly
|
||||
|
||||
PullDownMenu {
|
||||
visible: !imageOnly && imageFile.isDownloadingCompleted && imageFile.path
|
||||
visible: !imageOnly && singleImage.file.isDownloadingCompleted
|
||||
MenuItem {
|
||||
text: qsTr("Download Picture")
|
||||
onClicked: {
|
||||
tdLibWrapper.copyFileToDownloads(imageFile.path);
|
||||
tdLibWrapper.copyFileToDownloads(singleImage.file.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,9 +114,8 @@ Page {
|
|||
imageFlickable.returnToBounds()
|
||||
}
|
||||
|
||||
Image {
|
||||
TDLibImage {
|
||||
id: singleImage
|
||||
source: imageFile.isDownloadingCompleted ? imageFile.path : ""
|
||||
width: imagePage.imageWidth * imagePage.sizingFactor
|
||||
height: imagePage.imageHeight * imagePage.sizingFactor
|
||||
anchors.centerIn: parent
|
||||
|
|
Loading…
Reference in a new issue