Can send image to conversations

This commit is contained in:
Pierre HUBERT 2018-12-19 08:26:11 +01:00
parent 0148f7aaa5
commit 9100c14dfd
13 changed files with 224 additions and 12 deletions

View File

@ -1,4 +1,5 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QFile>
#include "apirequest.h" #include "apirequest.h"
@ -38,6 +39,31 @@ void APIRequest::addBool(QString name, bool value)
mArguments.append(APIRequestParameter(name, value ? "true" : "false")); mArguments.append(APIRequestParameter(name, value ? "true" : "false"));
} }
void APIRequest::addFileFromPath(const QString &name, const QString &path, const QString &fileType)
{
//Determine files name for the request
QString partName = name;
partName.replace("\"", "\\\"");
QString fileName = ("/"+path).split("/").last();
fileName.replace("\"", "\\\"");
QHttpPart part;
part.setHeader(QNetworkRequest::ContentTypeHeader, fileType);
part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\""+partName+"\"; filename=\""+fileName+"\""));
QFile *file = new QFile(path);
if(!file->open(QIODevice::ReadOnly)){
qWarning("Could not open file to send: %s !", path.toStdString().c_str());
return;
}
part.setBodyDevice(file);
mParts.append(part);
//Automatically delete file object once request is completed
file->setParent(this);
}
QList<APIRequestParameter> APIRequest::arguments() const QList<APIRequestParameter> APIRequest::arguments() const
{ {
return mArguments; return mArguments;
@ -52,3 +78,13 @@ void APIRequest::setNetworkReply(QNetworkReply *networkReply)
{ {
mNetworkReply = networkReply; mNetworkReply = networkReply;
} }
QList<QHttpPart> *APIRequest::parts()
{
return &mParts;
}
bool APIRequest::hasParts() const
{
return mParts.size() > 0;
}

View File

@ -10,6 +10,7 @@
#include <QObject> #include <QObject>
#include <QJsonDocument> #include <QJsonDocument>
#include <QHttpPart>
#include "apirequestparameter.h" #include "apirequestparameter.h"
@ -50,6 +51,15 @@ public:
*/ */
void addBool(QString name, bool value); void addBool(QString name, bool value);
/**
* Add a file from filesystem path
*
* @param name The name of the file to add
* @param path The path to the file
* @param fileType The type of the file to add
*/
void addFileFromPath(const QString &name, const QString &path, const QString &fileType);
/** /**
* Get the entire list of arguments of the request * Get the entire list of arguments of the request
* *
@ -57,6 +67,14 @@ public:
*/ */
QList<APIRequestParameter> arguments() const; QList<APIRequestParameter> arguments() const;
/**
* Get the list of HTTP parts included with this request
*
* @return Pointer on the list containing the list of parts
*/
QList<QHttpPart> *parts();
bool hasParts() const;
//Get and set network reply associated with the request //Get and set network reply associated with the request
QNetworkReply *networkReply() const; QNetworkReply *networkReply() const;
void setNetworkReply(QNetworkReply *networkReply); void setNetworkReply(QNetworkReply *networkReply);
@ -91,6 +109,7 @@ public slots:
private: private:
QString mURI; QString mURI;
QList<APIRequestParameter> mArguments; QList<APIRequestParameter> mArguments;
QList<QHttpPart> mParts;
QNetworkReply *mNetworkReply = nullptr; QNetworkReply *mNetworkReply = nullptr;
}; };

View File

@ -24,3 +24,18 @@ void NewConversationMessage::setMessage(const QString &message)
{ {
mMessage = message; mMessage = message;
} }
QString NewConversationMessage::imagePath() const
{
return mImagePath;
}
bool NewConversationMessage::hasImage() const
{
return !mImagePath.isEmpty();
}
void NewConversationMessage::setImagePath(const QString &imagePath)
{
mImagePath = imagePath;
}

View File

@ -22,9 +22,14 @@ public:
QString message() const; QString message() const;
void setMessage(const QString &message); void setMessage(const QString &message);
QString imagePath() const;
bool hasImage() const;
void setImagePath(const QString &imagePath);
private: private:
int mIDConversation; int mIDConversation;
QString mMessage; QString mMessage;
QString mImagePath;
}; };
#endif // NEWCONVERSATIONMESSAGE_H #endif // NEWCONVERSATIONMESSAGE_H

View File

@ -30,16 +30,49 @@ void APIHelper::execute(APIRequest *request)
request->addString("userToken2", tokens.token2()); request->addString("userToken2", tokens.token2());
} }
//Prepare request QNetworkReply *reply = nullptr;
//See this SO question to learn more : https://stackoverflow.com/questions/2599423
QUrlQuery queryData;
for(APIRequestParameter param : request->arguments())
queryData.addQueryItem(param.name(), param.value());
//Send request //Prepare request
QNetworkRequest networkRequest((QUrl(requestURL))); //Check if the request contains files or not
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); if(!request->hasParts()){
QNetworkReply *reply = mNetworkManager.post(networkRequest, queryData.toString(QUrl::FullyEncoded).toUtf8());
//See this SO question to learn more : https://stackoverflow.com/questions/2599423
QUrlQuery queryData;
for(APIRequestParameter param : request->arguments())
queryData.addQueryItem(param.name(), param.value());
//Send request
QNetworkRequest networkRequest((QUrl(requestURL)));
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
reply = mNetworkManager.post(networkRequest, queryData.toString(QUrl::FullyEncoded).toUtf8());
}
//Multiple entries request
else {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
//Process the list of "normal" arguments
for(APIRequestParameter param : request->arguments()){
QHttpPart part;
part.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\""+param.name()+"\"");
part.setBody(param.value().toStdString().c_str());
multiPart->append(part);
}
//Append all the other parts
for(int i = 0; i < request->parts()->size(); i++)
multiPart->append(request->parts()->at(i));
//Send request
QNetworkRequest networkRequest((QUrl(requestURL)));
reply = mNetworkManager.post(networkRequest, multiPart);
//Delete multipart as soon as the request ends
multiPart->setParent(reply);
}
//Make connections //Make connections
connect(reply, &QNetworkReply::finished, this, &APIHelper::finished); connect(reply, &QNetworkReply::finished, this, &APIHelper::finished);

View File

@ -4,6 +4,7 @@
#include "apihelper.h" #include "apihelper.h"
#include "conversationhelper.h" #include "conversationhelper.h"
#include "../data/apirequest.h" #include "../data/apirequest.h"
#include "../utils/filesutils.h"
ConversationHelper::ConversationHelper(QObject *parent) : QObject(parent) ConversationHelper::ConversationHelper(QObject *parent) : QObject(parent)
{ {
@ -17,6 +18,14 @@ void ConversationHelper::sendMessage(const NewConversationMessage &message)
request->addInt("conversationID", message.iDConversation()); request->addInt("conversationID", message.iDConversation());
request->addString("message", message.message()); request->addString("message", message.message());
//Add image (if any)
if(message.hasImage()){
//Add image to request
request->addFileFromPath("image", message.imagePath(), FilesUtils::GetFileMimeType(message.imagePath()));
}
connect(request, &APIRequest::finished, this, &ConversationHelper::sendMessageFinished); connect(request, &APIRequest::finished, this, &ConversationHelper::sendMessageFinished);
mAPIHelper->execute(request); mAPIHelper->execute(request);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

View File

@ -3,5 +3,6 @@
<file>baseline_people_black_48dp.png</file> <file>baseline_people_black_48dp.png</file>
<file>baseline_access_time_black_48dp.png</file> <file>baseline_access_time_black_48dp.png</file>
<file>baseline_person_black_48dp.png</file> <file>baseline_person_black_48dp.png</file>
<file>baseline_insert_photo_black_48dp.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -1,4 +1,5 @@
#include <QDir> #include <QDir>
#include <QMimeDatabase>
#include "filesutils.h" #include "filesutils.h"
@ -17,3 +18,8 @@ bool FilesUtils::CreateDirectoryIfNotExists(const QString &path)
return dir.mkpath("."); return dir.mkpath(".");
} }
QString FilesUtils::GetFileMimeType(const QString &filePath)
{
return (QMimeDatabase()).mimeTypeForFile(filePath).name();
}

View File

@ -23,6 +23,14 @@ public:
* FALSE else * FALSE else
*/ */
static bool CreateDirectoryIfNotExists(const QString &path); static bool CreateDirectoryIfNotExists(const QString &path);
/**
* Get the mime type of a file
*
* @param filePath The path of the file to determine
* @return File type
*/
static QString GetFileMimeType(const QString &filePath);
}; };
#endif // FILESUTILS_H #endif // FILESUTILS_H

View File

@ -1,6 +1,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <algorithm> #include <algorithm>
#include <QTimer> #include <QTimer>
#include <QFileDialog>
#include "conversationwidget.h" #include "conversationwidget.h"
#include "ui_conversationwidget.h" #include "ui_conversationwidget.h"
@ -40,6 +41,31 @@ ConversationWidget::~ConversationWidget()
delete mTimer; delete mTimer;
} }
void ConversationWidget::setMessageFormImage()
{
//Check if an image has already been selected by the user
if(hasUserSelectedImageToSend()){
//Ask user confirmation
if(QMessageBox::question(
this,
tr("Unselect image"),
tr("Are you sure to remove currently selected image from message ?")
) != QMessageBox::Yes)
return;
mPathToCurrentImageInForm = "";
}
//Pick an image
else
mPathToCurrentImageInForm = QFileDialog::getOpenFileName(this,
tr("Choose image to include in the message"), "", tr("Image Files (*.png *.jpg *.jpeg, *.gif)"));
//Check if we have an image selected
refreshPickImageButton();
}
void ConversationWidget::sendMessage() void ConversationWidget::sendMessage()
{ {
if(isSendMessageFormLocked()){ if(isSendMessageFormLocked()){
@ -50,7 +76,7 @@ void ConversationWidget::sendMessage()
QString content = ui->messageContentInput->text(); QString content = ui->messageContentInput->text();
//Check message length //Check message length
if(content.length() < CONVERSATION_MESSAGE_MIN_LENGTH){ if(content.length() < CONVERSATION_MESSAGE_MIN_LENGTH && !hasUserSelectedImageToSend()){
QMessageBox::warning(this, tr("Invalid message!"), tr("Specified message is too short!")); QMessageBox::warning(this, tr("Invalid message!"), tr("Specified message is too short!"));
return; return;
} }
@ -63,6 +89,10 @@ void ConversationWidget::sendMessage()
newMessage.setIDConversation(mConversation.iD()); newMessage.setIDConversation(mConversation.iD());
newMessage.setMessage(content); newMessage.setMessage(content);
//Include user image (if any)
if(hasUserSelectedImageToSend())
newMessage.setImagePath(mPathToCurrentImageInForm);
//Request the message to be sent //Request the message to be sent
mConversationHelper->sendMessage(newMessage); mConversationHelper->sendMessage(newMessage);
} }
@ -135,6 +165,7 @@ void ConversationWidget::setSendMessageFormLocked(bool lock)
{ {
ui->sendMessageButton->setEnabled(!lock); ui->sendMessageButton->setEnabled(!lock);
ui->messageContentInput->setEnabled(!lock); ui->messageContentInput->setEnabled(!lock);
ui->addImageButton->setEnabled(!lock);
} }
bool ConversationWidget::isSendMessageFormLocked() bool ConversationWidget::isSendMessageFormLocked()
@ -145,4 +176,21 @@ bool ConversationWidget::isSendMessageFormLocked()
void ConversationWidget::resetSendMessageForm() void ConversationWidget::resetSendMessageForm()
{ {
ui->messageContentInput->setText(""); ui->messageContentInput->setText("");
mPathToCurrentImageInForm = "";
refreshPickImageButton();
}
void ConversationWidget::refreshPickImageButton()
{
ui->addImageButton->setFlat(hasUserSelectedImageToSend());
}
void ConversationWidget::on_addImageButton_clicked()
{
setMessageFormImage();
}
bool ConversationWidget::hasUserSelectedImageToSend()
{
return !mPathToCurrentImageInForm.isEmpty();
} }

View File

@ -32,13 +32,17 @@ public:
public slots: public slots:
/**
* Ask the user to choose an image to send with the form
*/
void setMessageFormImage();
/** /**
* Send the message entered by the user in the form * Send the message entered by the user in the form
*/ */
void sendMessage(); void sendMessage();
private slots: private slots:
/** /**
@ -67,8 +71,18 @@ private slots:
void on_messageContentInput_returnPressed(); void on_messageContentInput_returnPressed();
void on_addImageButton_clicked();
private: private:
/**
* Check out whether the user has selected an image to include
* to the next message he will send through the form
*
* @return TRUE if the user has selected an image / FALSE else
*/
bool hasUserSelectedImageToSend();
/** /**
* Methods to get and set send message form * Methods to get and set send message form
* lock state * lock state
@ -76,9 +90,11 @@ private:
void setSendMessageFormLocked(bool lock); void setSendMessageFormLocked(bool lock);
bool isSendMessageFormLocked(); bool isSendMessageFormLocked();
void resetSendMessageForm(); void resetSendMessageForm();
void refreshPickImageButton();
//Private fields //Private fields
Ui::ConversationWidget *ui; Ui::ConversationWidget *ui;
QString mPathToCurrentImageInForm;
QTimer *mTimer; QTimer *mTimer;
ConversationHelper *mConversationHelper; ConversationHelper *mConversationHelper;
Conversation mConversation; Conversation mConversation;

View File

@ -54,6 +54,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="addImageButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../res/ressources.qrc">
<normaloff>:/baseline_insert_photo_black_48dp.png</normaloff>:/baseline_insert_photo_black_48dp.png</iconset>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="sendMessageButton"> <widget class="QPushButton" name="sendMessageButton">
<property name="text"> <property name="text">
@ -65,6 +79,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<resources/> <resources>
<include location="../res/ressources.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>