Updated project structure

This commit is contained in:
2019-01-10 04:29:58 +01:00
parent bdfbe8ba48
commit 4da286c23e
87 changed files with 96 additions and 73 deletions

View File

@ -0,0 +1,14 @@
#include "aboutthisappdialog.h"
#include "ui_aboutthisappdialog.h"
AboutThisAppDialog::AboutThisAppDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutThisAppDialog)
{
ui->setupUi(this);
}
AboutThisAppDialog::~AboutThisAppDialog()
{
delete ui;
}

View File

@ -0,0 +1,28 @@
/**
* About this app dialog
*
* @author Pierre HUBERT
*/
#ifndef ABOUTTHISAPPDIALOG_H
#define ABOUTTHISAPPDIALOG_H
#include <QDialog>
namespace Ui {
class AboutThisAppDialog;
}
class AboutThisAppDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutThisAppDialog(QWidget *parent = nullptr);
~AboutThisAppDialog();
private:
Ui::AboutThisAppDialog *ui;
};
#endif // ABOUTTHISAPPDIALOG_H

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutThisAppDialog</class>
<widget class="QDialog" name="AboutThisAppDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>About ComunicMessages</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextBrowser" name="textBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This application is a client of the free and OpenSource social network Comunic.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The philosophy of Comunic is to respect its users privacy by all means.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Comunic is available everywhere :&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;* In your browser : https://comunic.io/&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;* In your smartphone : Search &amp;quot;Comunic&amp;quot; in the Play Store.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; text-decoration: underline; color:#007af4;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This application has been built by Pierre HUBERT.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AboutThisAppDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutThisAppDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,11 @@
#include "clickablelabel.h"
ClickableLabel::ClickableLabel(QWidget *parent) : QLabel (parent)
{
}
void ClickableLabel::mousePressEvent(QMouseEvent *)
{
emit clicked();
}

View File

@ -0,0 +1,26 @@
#ifndef CLICKABLELABEL_H
#define CLICKABLELABEL_H
#include <QLabel>
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
ClickableLabel(QWidget *parent=nullptr);
signals:
/**
* This signal is emitted when the a click is made o the view
*/
void clicked();
protected:
void mousePressEvent(QMouseEvent*) override;
};
#endif // CLICKABLELABEL_H

View File

@ -0,0 +1,57 @@
#include "conversationitemwidget.h"
#include "ui_conversationitemwidget.h"
#include "../helpers/conversationslisthelper.h"
#include "../utils/timeutils.h"
ConversationItemWidget::ConversationItemWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConversationItemWidget)
{
ui->setupUi(this);
}
ConversationItemWidget::~ConversationItemWidget()
{
delete ui;
}
void ConversationItemWidget::setConversation(const Conversation &conv, const UsersList &list)
{
mCurrentConversation = conv;
ui->nameLabel->setText(ConversationsListHelper::getConversationDisplayName(conv, list));
QFont font = ui->nameLabel->font();
font.setBold(!conv.sawLastMessage());
ui->nameLabel->setFont(font);
if(conv.members().size() == 1)
ui->numberMembersLabel->setText(tr("1 member"));
else
ui->numberMembersLabel->setText(tr("%1 members").arg(conv.members().size()));
ui->lastActivityLabel->setText(TimeUtils::TimeDiffToString(conv.lastActive()));
//Conversation not active by default
setActive(false);
}
void ConversationItemWidget::mousePressEvent(QMouseEvent *)
{
emit openConversation();
}
Conversation ConversationItemWidget::currentConversation() const
{
return mCurrentConversation;
}
void ConversationItemWidget::setActive(bool active)
{
QString styleContainer = active ? "background: black; color: white" : "";
setStyleSheet(styleContainer);
QString styleLabels = "padding-left: 5px;";
ui->numberMembersLabel->setStyleSheet(styleLabels);
ui->lastActivityLabel->setStyleSheet(styleLabels);
}

View File

@ -0,0 +1,69 @@
/**
* Conversation item widget
*
* Contains information about a single conversation
*
* @author Pierre HUBERT
*/
#ifndef CONVERSATIONITEMWIDGET_H
#define CONVERSATIONITEMWIDGET_H
#include <QWidget>
#include "../data/conversation.h"
namespace Ui {
class ConversationItemWidget;
}
class User;
class UsersList;
class ConversationItemWidget : public QWidget
{
Q_OBJECT
public:
explicit ConversationItemWidget(QWidget *parent = nullptr);
~ConversationItemWidget() override;
/**
* Apply a conversation to the widget
*
* @param conv Information about the conversation to apply
* @param list Information about potential users of the conversation
*/
void setConversation(const Conversation &conv, const UsersList &list);
/**
* Get the current conversation represented by this widget
*
* @return The current conversation of this widget
*/
Conversation currentConversation() const;
/**
* Mark the current conversation as active or not
*
* @param active TRUE to mark the conversation as active /
* FALSE else
*/
void setActive(bool active);
protected:
void mousePressEvent(QMouseEvent *) override;
signals:
/**
* Request the conversation represented by this widget to be opened
*/
void openConversation();
private:
Ui::ConversationItemWidget *ui;
Conversation mCurrentConversation;
};
#endif // CONVERSATIONITEMWIDGET_H

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConversationItemWidget</class>
<widget class="QWidget" name="ConversationItemWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>138</width>
<height>91</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="nameLabel">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QLabel" name="membersIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>15</width>
<height>15</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../res/ressources.qrc">:/baseline_people_black_48dp.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="numberMembersLabel">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lastUpdateLabel">
<property name="maximumSize">
<size>
<width>15</width>
<height>15</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../res/ressources.qrc">:/baseline_access_time_black_48dp.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lastActivityLabel">
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../res/ressources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,35 @@
#include "conversationmessagewidget.h"
#include "ui_conversationmessagewidget.h"
#include "../data/user.h"
#include "../data/conversationmessage.h"
#include "remoteimagemanager.h"
#include "clickablelabel.h"
ConversationMessageWidget::ConversationMessageWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ConversationMessageWidget)
{
ui->setupUi(this);
}
ConversationMessageWidget::~ConversationMessageWidget()
{
delete ui;
}
void ConversationMessageWidget::setMessage(const ConversationMessage &message, const User &user)
{
ui->nameLabel->setText(user.displayName());
ui->messageLabel->setText(message.message());
new RemoteImageManager(ui->accountImageLabel, user.accountImage(), this);
//Add message image (if any)
if(message.hasImage()){
ClickableLabel *label = new ClickableLabel;
label->setParent(ui->messageContentContainer);
label->setScaledContents(true);
label->setMaximumSize(200, 200);
ui->messageLayout->addWidget(label);
(new RemoteImageManager(label, message.imagePath(), this))->setEnlargeable(true);
}
}

View File

@ -0,0 +1,32 @@
#ifndef CONVERSATIONMESSAGEWIDGET_H
#define CONVERSATIONMESSAGEWIDGET_H
#include <QWidget>
namespace Ui {
class ConversationMessageWidget;
}
class ConversationMessage;
class User;
class ConversationMessageWidget : public QWidget
{
Q_OBJECT
public:
explicit ConversationMessageWidget(QWidget *parent = nullptr);
~ConversationMessageWidget();
/**
* Apply a new conversation message to the widget
*
* @param message The message to apply to the widget
*/
void setMessage(const ConversationMessage &message, const User &user);
private:
Ui::ConversationMessageWidget *ui;
};
#endif // CONVERSATIONMESSAGEWIDGET_H

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConversationMessageWidget</class>
<widget class="QWidget" name="ConversationMessageWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>227</width>
<height>53</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="accountImageLabel">
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../res/ressources.qrc">:/baseline_person_black_48dp.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="nameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="messageContentContainer" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="messageLayout">
<item>
<widget class="QLabel" name="messageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Message content</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../res/ressources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,94 @@
#include <QMessageBox>
#include <QVBoxLayout>
#include "conversationslistwidget.h"
#include "conversationitemwidget.h"
#include "../helpers/conversationslisthelper.h"
#include "../helpers/usershelper.h"
#include "../data/conversationslist.h"
#include "../utils/uiutils.h"
ConversationsListWidget::ConversationsListWidget(QWidget *parent) :
QWidget(parent)
{
//Create conversations helper
mConversationsList = new ConversationsListHelper(this);
connect(mConversationsList, &ConversationsListHelper::onGotList, this, &ConversationsListWidget::onGotConversationsList);
//Create users helper
mUsersHelper = new UsersHelper(this);
connect(mUsersHelper, &UsersHelper::onGotUsersInfo, this, &ConversationsListWidget::onGotUsersInfo);
//Set conversations list layout
new QVBoxLayout(this);
}
ConversationsListWidget::~ConversationsListWidget()
{
}
void ConversationsListWidget::refresh()
{
mConversationsList->getList();
}
void ConversationsListWidget::onGotConversationsList(bool success, const ConversationsList &list)
{
qWarning("Got conversations list callback.");
if(!success){
QMessageBox::warning(this, tr("Error"), tr("Could not get the list of conversations!"));
return;
}
//Get the list of users
mUsersHelper->getList(list.getAllMembersId());
//Save the list of conversations
mCurrList = list;
}
void ConversationsListWidget::onGotUsersInfo(bool success, const UsersList &users)
{
if(!success){
QMessageBox::warning(this, tr("Error"), tr("Could not get information about the members of the conversations!"));
return;
}
//Save members information
mCurrList.setMembersInformation(users);
//First, remove any present convversation
UiUtils::emptyLayout(layout());
//Append the list of conversations
for(Conversation conv : mCurrList){
ConversationItemWidget *item = new ConversationItemWidget;
item->setConversation(conv, users);
connect(item, &ConversationItemWidget::openConversation, this, &ConversationsListWidget::onRequestOpenConversation);
layout()->addWidget(item);
}
}
void ConversationsListWidget::onRequestOpenConversation()
{
Conversation conversation = qobject_cast<ConversationItemWidget *>(sender())->currentConversation();
//Notify ourselves
setCurrentConversation(conversation);
//Notify everybody
emit openConversation(conversation, mCurrList.getMembersInformation());
}
void ConversationsListWidget::setCurrentConversation(const Conversation &currentConversation)
{
mCurrentConversation = currentConversation;
//Update UI
for(int i = 0; i < layout()->count(); i++){
ConversationItemWidget *widget = qobject_cast<ConversationItemWidget *>(layout()->itemAt(i)->widget());
widget->setActive(widget->currentConversation().iD() == currentConversation.iD());
}
}

View File

@ -0,0 +1,77 @@
#ifndef CONVERSATIONSLISTWIDGET_H
#define CONVERSATIONSLISTWIDGET_H
#include <QWidget>
#include "../data/conversationslist.h"
#include "../data/userslist.h"
#include "../data/user.h"
class ConversationsListHelper;
class UsersHelper;
class ConversationsListWidget : public QWidget
{
Q_OBJECT
public:
explicit ConversationsListWidget(QWidget *parent = nullptr);
~ConversationsListWidget();
/**
* Refresh the list of conversations of the user
*/
void refresh();
/**
* This method is used to update the currently active conversation
*
* @param currentConversation The current conversation
*/
void setCurrentConversation(const Conversation &currentConversation);
signals:
/**
* Signal emitted when a conversation is requested to be opened
*
* @param conversation The conversation to open
* @param list Information about potential required users
*/
void openConversation(Conversation conversation, UsersList list);
private slots:
/**
* This slot is triggered once we have got a new list of conversations
*
* @param success TRUE for a success / FALSE else
* @param list The list of conversation (empty list in case of failure)
*/
void onGotConversationsList(bool success, const ConversationsList &list);
/**
* This slot is triggered once we have information about the users
*
* @param success TRUE in case of success / FALSE else
* @param users The list of suers (empty list in case of failure)
*/
void onGotUsersInfo(bool success, const UsersList &users);
/**
* Triggered when a conversation item request a conversation to opened
*/
void onRequestOpenConversation();
private:
ConversationsListHelper *mConversationsList;
UsersHelper *mUsersHelper;
//Current conversations list in cache
ConversationsList mCurrList;
//Current opened conversation
Conversation mCurrentConversation;
};
#endif // CONVERSATIONSLISTWIDGET_H

View File

@ -0,0 +1,247 @@
#include <QMessageBox>
#include <algorithm>
#include <QTimer>
#include <QFileDialog>
#include <QScrollBar>
#include "conversationwidget.h"
#include "ui_conversationwidget.h"
#include "conversationmessagewidget.h"
#include "../helpers/conversationslisthelper.h"
#include "../helpers/conversationhelper.h"
#include "../config.h"
#include "../data/conversationmessage.h"
#include "../utils/uiutils.h"
ConversationWidget::ConversationWidget(const Conversation &conversation, const UsersList &list, QWidget *parent) :
QWidget(parent),
ui(new Ui::ConversationWidget),
mConversation(conversation),
mUsersList(list)
{
ui->setupUi(this);
//Initialize UI
QString convTitle = ConversationsListHelper::getConversationDisplayName(conversation, list);
ui->convName->setText(convTitle);
connect(ui->scrollArea->verticalScrollBar(), &QScrollBar::valueChanged, this, &ConversationWidget::messagesListScrolled);
//Initalize helpers
mConversationHelper = new ConversationHelper(this);
connect(mConversationHelper, &ConversationHelper::sendMessageCallback, this, &ConversationWidget::sendMessageCallback);
connect(mConversationHelper, &ConversationHelper::getMessagesCallback, this, &ConversationWidget::getMessagesCallback);
//Initialize refresh timeout
mTimer = new QTimer(this);
connect(mTimer, &QTimer::timeout, this, &ConversationWidget::refreshTimeout);
emit refreshTimeout();
}
ConversationWidget::~ConversationWidget()
{
delete ui;
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()
{
if(isSendMessageFormLocked()){
qInfo("sendMessage cancelled because another send message request is running...");
return;
}
QString content = ui->messageContentInput->text();
//Check message length
if(content.length() < CONVERSATION_MESSAGE_MIN_LENGTH && !hasUserSelectedImageToSend()){
QMessageBox::warning(this, tr("Invalid message!"), tr("Specified message is too short!"));
return;
}
//Lock send form
setSendMessageFormLocked(true);
//Send a request to send message
NewConversationMessage newMessage;
newMessage.setIDConversation(mConversation.iD());
newMessage.setMessage(content);
//Include user image (if any)
if(hasUserSelectedImageToSend())
newMessage.setImagePath(mPathToCurrentImageInForm);
//Request the message to be sent
mConversationHelper->sendMessage(newMessage);
}
void ConversationWidget::refreshTimeout()
{
//Check if messages are already being loaded
if(mIsLoadingMessages)
return;
mIsLoadingMessages = true;
mTimer->stop();
//Get the latest message of the conversation
mConversationHelper->getMessages(mConversation.iD(), mMessages.getLastMessageID());
}
void ConversationWidget::getMessagesCallback(bool success, QList<ConversationMessage> list)
{
//Restart counter
mTimer->start(CONVERSATION_MESSAGES_REFRESH_INTERVAL);
mIsLoadingMessages = false;
if(!success){
QMessageBox::warning(this, tr("Error while getting messages list"), tr("Could not refresh messages list!"));
return;
}
//Stop now if the list of messages is empty
if(list.empty()){
//Check if we were loading older messages
if(mIsLoadingOlderMessages){
mGotOldestConversationMessage = true;
mIsLoadingOlderMessages = false;
}
return;
}
mMessages.append(list);
std::sort(mMessages.begin(), mMessages.end());
//Remove previous list of messages
UiUtils::emptyLayout(ui->messagesLayout);
//Apply the list of messages
QList<ConversationMessage>::iterator it = mMessages.begin();
while(it != mMessages.end()){
ConversationMessageWidget *convMessageWidget = new ConversationMessageWidget();
convMessageWidget->setMessage(*it, mUsersList.get(it->userID()));
ui->messagesLayout->addWidget(convMessageWidget);
it++;
}
//Scroll to the end of the widget if required
if(list.at(list.count()-1).iD() == mMessages.getLastMessageID())
QTimer::singleShot(1000, this, &ConversationWidget::scrollToBottom);
//Else we can check if we reached the top of the conversation
else if(list.count() < NUMBER_OF_OLDER_MESSAGES_TO_GET)
mGotOldestConversationMessage = true;
}
void ConversationWidget::scrollToBottom()
{
ui->scrollArea->verticalScrollBar()->setValue(
ui->scrollArea->verticalScrollBar()->maximum());
}
void ConversationWidget::sendMessageCallback(bool success)
{
setSendMessageFormLocked(false);
if(!success){
QMessageBox::warning(this, tr("Error"), tr("Could not send your message! Please check it and your Internet connection..."));
return;
}
//Reset message form
resetSendMessageForm();
}
void ConversationWidget::messagesListScrolled(int value)
{
//Check if the user reached the top of the conversation
if(value > 0)
return; //Nothing to be done
//Check if the conversation does not contains any message yet
// or if we already retrieved the oldest message of the conversation
if(mMessages.count() == 0 || mGotOldestConversationMessage)
return;
//Check if messsages are already being loaded
if(mIsLoadingMessages)
return;
mIsLoadingMessages = true;
mIsLoadingOlderMessages = true;
//Get older messages
mConversationHelper->getOlderMessages(mConversation.iD(), mMessages.getOldestMessageID());
}
void ConversationWidget::on_sendMessageButton_clicked()
{
sendMessage();
}
void ConversationWidget::on_messageContentInput_returnPressed()
{
sendMessage();
}
void ConversationWidget::setSendMessageFormLocked(bool lock)
{
ui->sendMessageButton->setEnabled(!lock);
ui->messageContentInput->setEnabled(!lock);
ui->addImageButton->setEnabled(!lock);
}
bool ConversationWidget::isSendMessageFormLocked()
{
return !ui->sendMessageButton->isEnabled();
}
void ConversationWidget::resetSendMessageForm()
{
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

@ -0,0 +1,120 @@
/**
* Conversation widget
*
* Allows to display all the components
* of a single conversation
*
* @author Pierre HUBERT
*/
#ifndef CONVERSATIONWIDGET_H
#define CONVERSATIONWIDGET_H
#include <QWidget>
#include "../data/conversation.h"
#include "../data/userslist.h"
#include "../data/conversationmessage.h"
#include "../data/conversationmessageslist.h"
namespace Ui {
class ConversationWidget;
}
class ConversationHelper;
class ConversationWidget : public QWidget
{
Q_OBJECT
public:
explicit ConversationWidget(const Conversation &conversation, const UsersList &list, QWidget *parent = nullptr);
~ConversationWidget();
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
*/
void sendMessage();
private slots:
/**
* This slot is called at regular interval in order to
* update regulary the list of messages
*/
void refreshTimeout();
/**
* Method called once we have got the list of messages
*
* @param success TRUE in case of success of the operation / FALSE else
* @param list The list of downloaded messages
*/
void getMessagesCallback(bool success, QList<ConversationMessage> list);
/**
* Scroll to the bottom of the conversation
*/
void scrollToBottom();
/**
* Method called once the server replied to a send conversation
* message request
*
* @param success TRUE in case of success / FALSE else
*/
void sendMessageCallback(bool success);
/**
* Slot called when the user scroll the conversation
*
* @param value The new scroll value
*/
void messagesListScrolled(int value);
void on_sendMessageButton_clicked();
void on_messageContentInput_returnPressed();
void on_addImageButton_clicked();
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
* lock state
*/
void setSendMessageFormLocked(bool lock);
bool isSendMessageFormLocked();
void resetSendMessageForm();
void refreshPickImageButton();
//Private fields
Ui::ConversationWidget *ui;
QString mPathToCurrentImageInForm;
QTimer *mTimer;
ConversationHelper *mConversationHelper;
Conversation mConversation;
UsersList mUsersList;
ConversationMessagesList mMessages;
bool mIsLoadingMessages = false;
bool mGotOldestConversationMessage = false;
bool mIsLoadingOlderMessages = false;
};
#endif // CONVERSATIONWIDGET_H

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConversationWidget</class>
<widget class="QWidget" name="ConversationWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="convName">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Conversation name</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="conversationMessages">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>224</height>
</rect>
</property>
<layout class="QVBoxLayout" name="messagesLayout"/>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="messageContentInput">
<property name="maxLength">
<number>200</number>
</property>
</widget>
</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>
<widget class="QPushButton" name="sendMessageButton">
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../res/ressources.qrc"/>
</resources>
<connections/>
</ui>

118
src/widgets/loginwidget.cpp Normal file
View File

@ -0,0 +1,118 @@
#include <QMessageBox>
#include "loginwidget.h"
#include "ui_loginwidget.h"
#include "../controllers/initcontroller.h"
#include "../utils/accountutils.h"
#include "../helpers/accounthelper.h"
LoginWidget::LoginWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::LoginWidget)
{
ui->setupUi(this);
mAccountHelper = new AccountHelper(this);
connect(mAccountHelper, &AccountHelper::loginResult, this, &LoginWidget::loginResult);
}
LoginWidget::~LoginWidget()
{
delete ui;
}
void LoginWidget::loginResult(LoginResult result)
{
//Check if login is successfull
if(result == LoginResult::LOGIN_SUCCESS){
qDebug("User successfully signed in.");
//Restart application
(new InitController())->init();
close();
return;
}
//Release login button
ui->loginButton->setEnabled(true);
//Display appropriate error
switch (result) {
case LoginResult::INVALID_CREDENTIALS:
showError(tr("The email and / or the password was rejected by the server!"));
break;
case LoginResult::NETWORK_ERROR:
showError(tr("A network occurred. Please check your Internet connection..."));
break;
case LoginResult::TOO_MANY_REQUEST:
showError(tr("Too many login attempts from your computer. Please try again later..."));
break;
case LoginResult::INVALID_SERVER_RESPONSE:
showError(tr("Could not understand server response!"));
break;
case LoginResult::OTHER_ERROR:
default:
showError(tr("Login attempt did not succeed."));
break;
}
}
void LoginWidget::on_loginButton_clicked()
{
submitForm();
}
void LoginWidget::on_emailEdit_returnPressed()
{
submitForm();
}
void LoginWidget::on_passwordEdit_returnPressed()
{
submitForm();
}
void LoginWidget::submitForm()
{
showError("");
QString emailAddress = ui->emailEdit->text();
QString password = ui->passwordEdit->text();
//Check input
if(emailAddress.length() < 5){
showError(tr("Please specify an email address!"));
return;
}
if(password.length() < 3){
showError(tr("Please specify a valid password!"));
return;
}
if(!AccountUtils::CheckEmailAddress(emailAddress)){
showError(("Please specify a valid email address!"));
return;
}
//Block login button
ui->loginButton->setEnabled(false);
//Try to sign in user
AccountLoginRequest request;
request.setEmailAddress(emailAddress);
request.setPassword(password);
mAccountHelper->login(request);
}
void LoginWidget::showError(const QString &msg)
{
ui->errorLabel->setText(msg);
}

50
src/widgets/loginwidget.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef LOGINWIDGET_H
#define LOGINWIDGET_H
#include <QWidget>
#include "../data/accountloginrequest.h"
namespace Ui {
class LoginWidget;
}
class AccountHelper;
class LoginWidget : public QWidget
{
Q_OBJECT
public:
explicit LoginWidget(QWidget *parent = nullptr);
~LoginWidget();
private slots:
void loginResult(LoginResult result);
//UI Slots
void on_loginButton_clicked();
void on_emailEdit_returnPressed();
void on_passwordEdit_returnPressed();
private:
/**
* Submit login form
*/
void submitForm();
/**
* Display an error message on the widget
*
* @param msg The message to show
*/
void showError(const QString &msg);
Ui::LoginWidget *ui;
AccountHelper *mAccountHelper;
};
#endif // LOGINWIDGET_H

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoginWidget</class>
<widget class="QWidget" name="LoginWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>274</width>
<height>397</height>
</rect>
</property>
<property name="windowTitle">
<string>Login to Comunic</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="errorLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="emailEdit">
<property name="inputMask">
<string/>
</property>
<property name="placeholderText">
<string>Your email address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passwordEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>Your password</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loginButton">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,62 @@
#include <QMessageBox>
#include <QPushButton>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "aboutthisappdialog.h"
#include "loginwidget.h"
#include "conversationslistwidget.h"
#include "conversationwidget.h"
#include "../utils/uiutils.h"
#include "../helpers/accounthelper.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//Display the list of conversations
mConversationsListWidget = new ConversationsListWidget;
mConversationsListWidget->refresh();
ui->conversationsListArea->setWidget(mConversationsListWidget);
connect(mConversationsListWidget, &ConversationsListWidget::openConversation, this, &MainWindow::openConversation);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::openConversation(Conversation conversation, UsersList list)
{
//Remove any previous conversation
UiUtils::emptyLayout(ui->conversationsContainerLayout);
qWarning("Open conversation");
ConversationWidget *widget = new ConversationWidget(conversation, list);
ui->conversationsContainerLayout->addWidget(widget);
}
void MainWindow::on_actionAbout_Qt_triggered()
{
QApplication::aboutQt();
}
void MainWindow::on_actionAbout_this_App_triggered()
{
AboutThisAppDialog(this).exec();
}
void MainWindow::on_actionLogout_triggered()
{
if(QMessageBox::question(this, tr("Sign out"), tr("Do you really want to sign out from your account?")) != QMessageBox::Yes)
return;
AccountHelper().logout();
//Display login widget
(new LoginWidget())->show();
//Close this widget
close();
}

52
src/widgets/mainwindow.h Normal file
View File

@ -0,0 +1,52 @@
/**
* Main window of the project
*
* @author Pierre HUBERT
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "../data/conversation.h"
#include "../data/userslist.h"
namespace Ui {
class MainWindow;
}
class ConversationsListWidget;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
/**
* Request a conversation to be opened
*
* @param conversation Information about the conversation
* to open
* @param list Information about the users
*/
void openConversation(Conversation conversation, UsersList list);
void on_actionAbout_Qt_triggered();
void on_actionAbout_this_App_triggered();
void on_actionLogout_triggered();
private:
Ui::MainWindow *ui;
ConversationsListWidget *mConversationsListWidget;
};
#endif // MAINWINDOW_H

84
src/widgets/mainwindow.ui Normal file
View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>ComunicMessages</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QScrollArea" name="conversationsListArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>772</width>
<height>536</height>
</rect>
</property>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="conversationsContainerLayout"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuAccount">
<property name="title">
<string>Account</string>
</property>
<addaction name="actionLogout"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout_this_App"/>
<addaction name="actionAbout_Qt"/>
</widget>
<addaction name="menuAccount"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionAbout_Qt">
<property name="text">
<string>About Qt</string>
</property>
</action>
<action name="actionAbout_this_App">
<property name="text">
<string>About this App</string>
</property>
</action>
<action name="actionLogout">
<property name="text">
<string>Logout</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,42 @@
#include <QLabel>
#include "remoteimagemanager.h"
#include "clickablelabel.h"
#include "../helpers/imageloadhelper.h"
RemoteImageManager::RemoteImageManager(QLabel *label, const QString &url, QObject *parent) : QObject(parent)
{
//Load remote image
ImageLoadHelper::Load(label, url);
mUrl = url;
}
RemoteImageManager::RemoteImageManager(ClickableLabel *label, const QString &url, QObject *parent) : QObject (parent)
{
//Load remote image
ImageLoadHelper::Load(label, url);
mUrl = url;
connect(label, &ClickableLabel::clicked, this, &RemoteImageManager::enlarge);
}
bool RemoteImageManager::enlargeable() const
{
return mEnlargeable;
}
void RemoteImageManager::setEnlargeable(bool enlargeable)
{
mEnlargeable = enlargeable;
}
void RemoteImageManager::enlarge()
{
if(!enlargeable())
return;
//Open the image in a bigger label (to improve)
QLabel *label = new QLabel;
ImageLoadHelper::Load(label, mUrl);
label->show();
}

View File

@ -0,0 +1,45 @@
/**
* This object has to be used in all classes
* in order to manage remote image management
*
* @author Pierre HUBERT
*/
#ifndef REMOTEIMAGEMANAGER_H
#define REMOTEIMAGEMANAGER_H
#include <QObject>
class QLabel;
class ClickableLabel;
class RemoteImageManager : public QObject
{
Q_OBJECT
public:
explicit RemoteImageManager(QLabel *label, const QString &url, QObject *parent); //Avoid loss of memory : force parent = nullptr);
explicit RemoteImageManager(ClickableLabel *label, const QString &url, QObject *parent);
/**
* Note that the enlargable feature is currently available only with
* the ClickableLabel widgets
*/
bool enlargeable() const;
void setEnlargeable(bool enlargeable);
signals:
public slots:
/**
* This slot is triggered when we want to enlarge the image
*/
void enlarge();
private:
//Private URL
QString mUrl;
bool mEnlargeable = false;
};
#endif // REMOTEIMAGEMANAGER_H