mirror of
https://gitlab.com/comunic/comunicmessages
synced 2024-12-04 11:14:09 +00:00
Display the list of conversations.
This commit is contained in:
parent
3d9748ad62
commit
1df359735d
@ -22,7 +22,10 @@ SOURCES += \
|
||||
widgets/conversationitemwidget.cpp \
|
||||
data/user.cpp \
|
||||
helpers/usershelper.cpp \
|
||||
data/conversationslist.cpp
|
||||
data/conversationslist.cpp \
|
||||
utils/uiutils.cpp \
|
||||
data/userslist.cpp \
|
||||
utils/timeutils.cpp
|
||||
|
||||
HEADERS += \
|
||||
helpers/accounthelper.h \
|
||||
@ -46,10 +49,16 @@ HEADERS += \
|
||||
widgets/conversationitemwidget.h \
|
||||
data/user.h \
|
||||
helpers/usershelper.h \
|
||||
data/conversationslist.h
|
||||
data/conversationslist.h \
|
||||
utils/uiutils.h \
|
||||
data/userslist.h \
|
||||
utils/timeutils.h
|
||||
|
||||
FORMS += \
|
||||
widgets/loginwidget.ui \
|
||||
widgets/mainwindow.ui \
|
||||
widgets/aboutthisappdialog.ui \
|
||||
widgets/conversationitemwidget.ui
|
||||
|
||||
RESOURCES += \
|
||||
res/ressources.qrc
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "conversation.h"
|
||||
#include "userslist.h"
|
||||
|
||||
Conversation::Conversation()
|
||||
{
|
||||
|
@ -15,3 +15,13 @@ QList<int> ConversationsList::getAllMembersId() const
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
UsersList ConversationsList::getMembersInformation() const
|
||||
{
|
||||
return mMembersInformation;
|
||||
}
|
||||
|
||||
void ConversationsList::setMembersInformation(const UsersList &membersInformation)
|
||||
{
|
||||
mMembersInformation = membersInformation;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <QList>
|
||||
|
||||
#include "conversation.h"
|
||||
#include "user.h"
|
||||
#include "userslist.h"
|
||||
|
||||
class ConversationsList : public QList<Conversation>
|
||||
{
|
||||
@ -23,6 +25,14 @@ public:
|
||||
* @return The IDs of the conversations
|
||||
*/
|
||||
QList<int> getAllMembersId() const;
|
||||
|
||||
UsersList getMembersInformation() const;
|
||||
void setMembersInformation(const UsersList &membersInformation);
|
||||
|
||||
private:
|
||||
|
||||
//Private fields
|
||||
UsersList mMembersInformation;
|
||||
};
|
||||
|
||||
#endif // CONVERSATIONSLIST_H
|
||||
|
@ -35,6 +35,11 @@ void User::setLastName(const QString &lastName)
|
||||
mLastName = lastName;
|
||||
}
|
||||
|
||||
QString User::displayName() const
|
||||
{
|
||||
return firstName() + " " + lastName();
|
||||
}
|
||||
|
||||
QString User::accountImage() const
|
||||
{
|
||||
return mAccountImage;
|
||||
|
@ -23,6 +23,11 @@ public:
|
||||
QString lastName() const;
|
||||
void setLastName(const QString &lastName);
|
||||
|
||||
/**
|
||||
* Get and return display name of the user
|
||||
*/
|
||||
QString displayName() const;
|
||||
|
||||
QString accountImage() const;
|
||||
void setAccountImage(const QString &accountImage);
|
||||
|
||||
|
16
data/userslist.cpp
Normal file
16
data/userslist.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "userslist.h"
|
||||
|
||||
UsersList::UsersList() : QList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
User UsersList::get(int userID) const
|
||||
{
|
||||
for(User user : *this)
|
||||
if(user.iD() == userID)
|
||||
return user;
|
||||
|
||||
//User not found
|
||||
return User();
|
||||
}
|
31
data/userslist.h
Normal file
31
data/userslist.h
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* A list of users
|
||||
*
|
||||
* Provides methods to fetch users
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
#ifndef USERSLISTS_H
|
||||
#define USERSLISTS_H
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include "user.h"
|
||||
|
||||
class UsersList : public QList<User>
|
||||
{
|
||||
public:
|
||||
UsersList();
|
||||
|
||||
/**
|
||||
* Find and return information about a user specified
|
||||
* by its ID
|
||||
*
|
||||
* @param userID The ID of the target user
|
||||
* @return Information about the user
|
||||
*/
|
||||
User get(int userID) const;
|
||||
};
|
||||
|
||||
#endif // USERSLISTS_H
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "conversationslisthelper.h"
|
||||
#include "apihelper.h"
|
||||
#include "accounthelper.h"
|
||||
#include "../data/apirequest.h"
|
||||
#include "../data/conversationslist.h"
|
||||
|
||||
@ -20,6 +21,31 @@ void ConversationsListHelper::getList()
|
||||
mAPIHelper->execute(request);
|
||||
}
|
||||
|
||||
QString ConversationsListHelper::getConversationDisplayName(const Conversation &conv, const UsersList &usersInfo)
|
||||
{
|
||||
//Check if the conversation has already a name
|
||||
if(conv.name().length() > 0)
|
||||
return conv.name();
|
||||
|
||||
QString name;
|
||||
|
||||
int i = 0;
|
||||
for(int j = 0; j < conv.members().length() && i < 3; j++){
|
||||
|
||||
//We bypass the current user name
|
||||
if(conv.members().at(j) == AccountHelper::getUserID())
|
||||
continue;
|
||||
|
||||
if(name.length() > 0)
|
||||
name += ", ";
|
||||
name += usersInfo.get(conv.members().at(j)).displayName();
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void ConversationsListHelper::getConvListCallback(int code, const QJsonDocument &document)
|
||||
{
|
||||
//Delete the request
|
||||
@ -49,8 +75,8 @@ Conversation ConversationsListHelper::GetConversationFromJson(const QJsonObject
|
||||
conv.setIDowner(obj.value("ID_owner").toInt());
|
||||
conv.setLastActive(obj.value("last_active").toInt());
|
||||
conv.setName(obj.value("name").toString());
|
||||
conv.setFollowing(obj.value("following").toBool());
|
||||
conv.setSawLastMessage(obj.value("saw_last_message").toBool());
|
||||
conv.setFollowing(obj.value("following").toInt() == 1);
|
||||
conv.setSawLastMessage(obj.value("saw_last_message").toInt() == 1);
|
||||
|
||||
//Process the list of members of the conversation
|
||||
QJsonArray members_arr = obj.value("members").toArray();
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "../data/conversation.h"
|
||||
#include "../data/userslist.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
@ -29,6 +30,15 @@ public:
|
||||
*/
|
||||
void getList();
|
||||
|
||||
/**
|
||||
* Get and return the display name of a conversation
|
||||
*
|
||||
* @param conv Information about the target conversation
|
||||
* @param usersInfo Information about related users
|
||||
* @return The name of the conversation
|
||||
*/
|
||||
static QString getConversationDisplayName(const Conversation &conv, const UsersList &usersInfo);
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
|
@ -31,12 +31,12 @@ void UsersHelper::getUsersInformationFinished(int code, const QJsonDocument &doc
|
||||
|
||||
//Check for error
|
||||
if(code != 200){
|
||||
emit onGotUsersInfo(false, QList<User>());
|
||||
emit onGotUsersInfo(false, UsersList());
|
||||
return;
|
||||
}
|
||||
|
||||
//Parse the list of object
|
||||
QList<User> list;
|
||||
UsersList list;
|
||||
QJsonObject obj = document.object();
|
||||
for(QString id : obj.keys())
|
||||
list.append(ParseJSONToUser(obj.value(id).toObject()));
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "../data/user.h"
|
||||
#include "../data/userslist.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
@ -38,7 +39,7 @@ signals:
|
||||
* @param success TRUE for a success / FALSE else
|
||||
* @param list Information about the users
|
||||
*/
|
||||
void onGotUsersInfo(bool success, const QList<User> &list);
|
||||
void onGotUsersInfo(bool success, const UsersList &list);
|
||||
|
||||
public slots:
|
||||
|
||||
|
BIN
res/baseline_access_time_black_48dp.png
Executable file
BIN
res/baseline_access_time_black_48dp.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 655 B |
BIN
res/baseline_people_black_48dp.png
Executable file
BIN
res/baseline_people_black_48dp.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 317 B |
6
res/ressources.qrc
Normal file
6
res/ressources.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>baseline_people_black_48dp.png</file>
|
||||
<file>baseline_access_time_black_48dp.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
46
utils/timeutils.cpp
Normal file
46
utils/timeutils.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <QDateTime>
|
||||
|
||||
#include "timeutils.h"
|
||||
|
||||
TimeUtils::TimeUtils()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString TimeUtils::TimeDiffToString(qint64 time)
|
||||
{
|
||||
qint64 diffTime = QDateTime::currentSecsSinceEpoch() - time;
|
||||
|
||||
if(diffTime < 60)
|
||||
return QObject::tr("%1s ago").arg(diffTime);
|
||||
|
||||
diffTime = diffTime/60;
|
||||
if(diffTime < 60)
|
||||
return QObject::tr("%1m ago").arg(diffTime);
|
||||
|
||||
diffTime = diffTime/60;
|
||||
if(diffTime < 24)
|
||||
return QObject::tr("%1h ago").arg(diffTime);
|
||||
|
||||
diffTime = diffTime/24;
|
||||
if(diffTime < 30){
|
||||
if(diffTime == 1)
|
||||
return QObject::tr("1 day ago");
|
||||
else
|
||||
return QObject::tr("%1 days ago").arg(diffTime);
|
||||
}
|
||||
|
||||
diffTime = diffTime/30;
|
||||
if(diffTime < 12){
|
||||
if(diffTime == 1)
|
||||
return QObject::tr("1 month ago");
|
||||
else
|
||||
return QObject::tr("%1 months ago").arg(diffTime);
|
||||
}
|
||||
|
||||
diffTime = diffTime/12;
|
||||
if(diffTime == 1)
|
||||
return QObject::tr("1 year ago");
|
||||
else
|
||||
return QObject::tr("%1 years ago").arg(diffTime);
|
||||
}
|
27
utils/timeutils.h
Normal file
27
utils/timeutils.h
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Time utilities
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
#ifndef TIMEUTILS_H
|
||||
#define TIMEUTILS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TimeUtils
|
||||
{
|
||||
public:
|
||||
TimeUtils();
|
||||
|
||||
/**
|
||||
* Turn a timestamp into a diff string, in order to
|
||||
* be shown to the user
|
||||
*
|
||||
* @param time The time to convert
|
||||
* @return Generated string
|
||||
*/
|
||||
static QString TimeDiffToString(qint64 time);
|
||||
};
|
||||
|
||||
#endif // TIMEUTILS_H
|
24
utils/uiutils.cpp
Normal file
24
utils/uiutils.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include <QLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "uiutils.h"
|
||||
|
||||
UiUtils::UiUtils()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UiUtils::emptyLayout(QLayout *layout)
|
||||
{
|
||||
while(layout->count() > 0){
|
||||
QLayoutItem *item = layout->itemAt(0);
|
||||
|
||||
if(item->layout() != nullptr)
|
||||
emptyLayout(item->layout());
|
||||
|
||||
if(item->widget() != nullptr)
|
||||
item->widget()->deleteLater();
|
||||
|
||||
layout->removeItem(item);
|
||||
}
|
||||
}
|
25
utils/uiutils.h
Normal file
25
utils/uiutils.h
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* UI utilities
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
#ifndef UIUTILS_H
|
||||
#define UIUTILS_H
|
||||
|
||||
class QLayout;
|
||||
|
||||
class UiUtils
|
||||
{
|
||||
public:
|
||||
UiUtils();
|
||||
|
||||
/**
|
||||
* Remove all the items of a layout
|
||||
*
|
||||
* @param layout The layout to process
|
||||
*/
|
||||
static void emptyLayout(QLayout *layout);
|
||||
};
|
||||
|
||||
#endif // UIUTILS_H
|
31
widgets/conversationitemwidget.cpp
Normal file
31
widgets/conversationitemwidget.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#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)
|
||||
{
|
||||
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()));
|
||||
}
|
41
widgets/conversationitemwidget.h
Normal file
41
widgets/conversationitemwidget.h
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Conversation item widget
|
||||
*
|
||||
* Contains information about a single conversation
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
#ifndef CONVERSATIONITEMWIDGET_H
|
||||
#define CONVERSATIONITEMWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ConversationItemWidget;
|
||||
}
|
||||
|
||||
class Conversation;
|
||||
class User;
|
||||
class UsersList;
|
||||
|
||||
class ConversationItemWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConversationItemWidget(QWidget *parent = nullptr);
|
||||
~ConversationItemWidget();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private:
|
||||
Ui::ConversationItemWidget *ui;
|
||||
};
|
||||
|
||||
#endif // CONVERSATIONITEMWIDGET_H
|
129
widgets/conversationitemwidget.ui
Normal file
129
widgets/conversationitemwidget.ui
Normal file
@ -0,0 +1,129 @@
|
||||
<?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>8</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<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>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<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>
|
@ -1,9 +1,12 @@
|
||||
#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)
|
||||
@ -15,6 +18,9 @@ ConversationsListWidget::ConversationsListWidget(QWidget *parent) :
|
||||
//Create users helper
|
||||
mUsersHelper = new UsersHelper(this);
|
||||
connect(mUsersHelper, &UsersHelper::onGotUsersInfo, this, &ConversationsListWidget::onGotUsersInfo);
|
||||
|
||||
//Set conversations list layout
|
||||
new QVBoxLayout(this);
|
||||
}
|
||||
|
||||
ConversationsListWidget::~ConversationsListWidget()
|
||||
@ -38,15 +44,28 @@ void ConversationsListWidget::onGotConversationsList(bool success, const Convers
|
||||
|
||||
//Get the list of users
|
||||
mUsersHelper->getList(list.getAllMembersId());
|
||||
|
||||
//Save the list of conversations
|
||||
mCurrList = list;
|
||||
}
|
||||
|
||||
void ConversationsListWidget::onGotUsersInfo(bool success, const QList<User> &users)
|
||||
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;
|
||||
}
|
||||
|
||||
qDebug("Got the list of members of the conversations.");
|
||||
//TODO : use ConversationItemWidget
|
||||
//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);
|
||||
layout()->addWidget(item);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "../data/conversationslist.h"
|
||||
#include "../data/userslist.h"
|
||||
#include "../data/user.h"
|
||||
|
||||
class ConversationsListHelper;
|
||||
@ -38,7 +39,7 @@ private slots:
|
||||
* @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 QList<User> &users);
|
||||
void onGotUsersInfo(bool success, const UsersList &users);
|
||||
|
||||
private:
|
||||
ConversationsListHelper *mConversationsList;
|
||||
|
Loading…
Reference in New Issue
Block a user