Merge pull request #846 from csoler/v0.6-GxsTransport

minor code cleaning in p3GxsTrans (struct->class for consistency, sen…
This commit is contained in:
csoler 2017-05-27 22:37:14 +02:00 committed by GitHub
commit e0498ba715
8 changed files with 103 additions and 111 deletions

View File

@ -349,7 +349,7 @@ bool p3ChatService::sendChat(ChatId destination, std::string msg)
uint32_t sz = _serializer->size(ci); uint32_t sz = _serializer->size(ci);
std::vector<uint8_t> data; data.resize(sz); std::vector<uint8_t> data; data.resize(sz);
_serializer->serialise(ci, &data[0], &sz); _serializer->serialise(ci, &data[0], &sz);
mGxsTransport.sendMail(tId, GxsTransSubServices::P3_CHAT_SERVICE, mGxsTransport.sendData(tId, GxsTransSubServices::P3_CHAT_SERVICE,
de.from, de.to, &data[0], sz); de.from, de.to, &data[0], sz);
} }
else else

View File

@ -63,7 +63,7 @@ bool p3GxsTrans::getStatistics(GxsTransStatistics& stats)
return true; return true;
} }
bool p3GxsTrans::sendMail( RsGxsTransId& mailId, bool p3GxsTrans::sendData( RsGxsTransId& mailId,
GxsTransSubServices service, GxsTransSubServices service,
const RsGxsId& own_gxsid, const RsGxsId& recipient, const RsGxsId& own_gxsid, const RsGxsId& recipient,
const uint8_t* data, uint32_t size, const uint8_t* data, uint32_t size,
@ -558,6 +558,7 @@ void p3GxsTrans::processOutgoingRecord(OutgoingRecord& pr)
{ {
pr.mailItem.saltRecipientHint(pr.recipient); pr.mailItem.saltRecipientHint(pr.recipient);
pr.mailItem.saltRecipientHint(RsGxsId::random()); pr.mailItem.saltRecipientHint(RsGxsId::random());
pr.mailItem.meta.mPublishTs = time(NULL);
} }
case GxsTransSendStatus::PENDING_PREFERRED_GROUP: case GxsTransSendStatus::PENDING_PREFERRED_GROUP:
{ {
@ -640,8 +641,7 @@ void p3GxsTrans::processOutgoingRecord(OutgoingRecord& pr)
pr.recipient, encryptError, true ) ) pr.recipient, encryptError, true ) )
{ {
pr.mailItem.payload.resize(encryptedSize); pr.mailItem.payload.resize(encryptedSize);
memcpy( &pr.mailItem.payload[0], encryptedData, memcpy( &pr.mailItem.payload[0], encryptedData, encryptedSize );
encryptedSize );
free(encryptedData); free(encryptedData);
break; break;
} }

View File

@ -112,7 +112,7 @@ public:
* This method is part of the public interface of this service. * This method is part of the public interface of this service.
* @return true if the mail will be sent, false if not * @return true if the mail will be sent, false if not
*/ */
bool sendMail( RsGxsTransId& mailId, bool sendData( RsGxsTransId& mailId,
GxsTransSubServices service, GxsTransSubServices service,
const RsGxsId& own_gxsid, const RsGxsId& recipient, const RsGxsId& own_gxsid, const RsGxsId& recipient,
const uint8_t* data, uint32_t size, const uint8_t* data, uint32_t size,

View File

@ -28,17 +28,23 @@
#include "services/p3idservice.h" #include "services/p3idservice.h"
#include "serialiser/rstypeserializer.h" #include "serialiser/rstypeserializer.h"
struct RsNxsTransPresignedReceipt : RsNxsMsg class RsNxsTransPresignedReceipt : public RsNxsMsg
{ {
public:
RsNxsTransPresignedReceipt() : RsNxsMsg(RS_SERVICE_TYPE_GXS_TRANS) {} RsNxsTransPresignedReceipt() : RsNxsMsg(RS_SERVICE_TYPE_GXS_TRANS) {}
virtual ~RsNxsTransPresignedReceipt() {}
}; };
struct RsGxsTransBaseItem : RsGxsMsgItem class RsGxsTransBaseItem : public RsGxsMsgItem
{ {
public:
RsGxsTransBaseItem(GxsTransItemsSubtypes subtype) : RsGxsTransBaseItem(GxsTransItemsSubtypes subtype) :
RsGxsMsgItem( RS_SERVICE_TYPE_GXS_TRANS, RsGxsMsgItem( RS_SERVICE_TYPE_GXS_TRANS,
static_cast<uint8_t>(subtype) ), mailId(0) {} static_cast<uint8_t>(subtype) ), mailId(0) {}
virtual ~RsGxsTransBaseItem() {}
RsGxsTransId mailId; RsGxsTransId mailId;
void inline clear() void inline clear()
@ -52,10 +58,11 @@ struct RsGxsTransBaseItem : RsGxsMsgItem
{ RS_REGISTER_SERIAL_MEMBER_TYPED(mailId, uint64_t); } { RS_REGISTER_SERIAL_MEMBER_TYPED(mailId, uint64_t); }
}; };
struct RsGxsTransPresignedReceipt : RsGxsTransBaseItem class RsGxsTransPresignedReceipt : public RsGxsTransBaseItem
{ {
RsGxsTransPresignedReceipt() : public:
RsGxsTransBaseItem(GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT) {} RsGxsTransPresignedReceipt() : RsGxsTransBaseItem(GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT) {}
virtual ~RsGxsTransPresignedReceipt() {}
}; };
enum class RsGxsTransEncryptionMode : uint8_t enum class RsGxsTransEncryptionMode : uint8_t
@ -65,12 +72,15 @@ enum class RsGxsTransEncryptionMode : uint8_t
UNDEFINED_ENCRYPTION = 250 UNDEFINED_ENCRYPTION = 250
}; };
struct RsGxsTransMailItem : RsGxsTransBaseItem class RsGxsTransMailItem : public RsGxsTransBaseItem
{ {
public:
RsGxsTransMailItem() : RsGxsTransMailItem() :
RsGxsTransBaseItem(GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL), RsGxsTransBaseItem(GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL),
cryptoType(RsGxsTransEncryptionMode::UNDEFINED_ENCRYPTION) {} cryptoType(RsGxsTransEncryptionMode::UNDEFINED_ENCRYPTION) {}
virtual ~RsGxsTransMailItem() {}
RsGxsTransEncryptionMode cryptoType; RsGxsTransEncryptionMode cryptoType;
/** /**
@ -147,8 +157,9 @@ struct RsGxsTransMailItem : RsGxsTransBaseItem
const static uint32_t MAX_SIZE = 10*8*1024*1024; const static uint32_t MAX_SIZE = 10*8*1024*1024;
}; };
struct RsGxsTransGroupItem : RsGxsGrpItem class RsGxsTransGroupItem : public RsGxsGrpItem
{ {
public:
RsGxsTransGroupItem() : RsGxsTransGroupItem() :
RsGxsGrpItem( RS_SERVICE_TYPE_GXS_TRANS, RsGxsGrpItem( RS_SERVICE_TYPE_GXS_TRANS,
static_cast<uint8_t>( static_cast<uint8_t>(
@ -158,6 +169,7 @@ struct RsGxsTransGroupItem : RsGxsGrpItem
meta.mGroupName = "Mail"; meta.mGroupName = "Mail";
meta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC; meta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC;
} }
virtual ~RsGxsTransGroupItem() {}
// TODO: Talk with Cyril why there is no RsGxsGrpItem::serial_process // TODO: Talk with Cyril why there is no RsGxsGrpItem::serial_process
virtual void serial_process(RsGenericSerializer::SerializeJob /*j*/, virtual void serial_process(RsGenericSerializer::SerializeJob /*j*/,
@ -170,11 +182,15 @@ struct RsGxsTransGroupItem : RsGxsGrpItem
}; };
class RsGxsTransSerializer; class RsGxsTransSerializer;
struct OutgoingRecord : RsItem
class OutgoingRecord : public RsItem
{ {
public:
OutgoingRecord( RsGxsId rec, GxsTransSubServices cs, OutgoingRecord( RsGxsId rec, GxsTransSubServices cs,
const uint8_t* data, uint32_t size ); const uint8_t* data, uint32_t size );
virtual ~OutgoingRecord() {}
GxsTransSendStatus status; GxsTransSendStatus status;
RsGxsId recipient; RsGxsId recipient;
/// Don't use a pointer would be invalid after publish /// Don't use a pointer would be invalid after publish
@ -194,10 +210,11 @@ private:
}; };
struct RsGxsTransSerializer : public RsServiceSerializer class RsGxsTransSerializer : public RsServiceSerializer
{ {
public:
RsGxsTransSerializer() : RsServiceSerializer(RS_SERVICE_TYPE_GXS_TRANS) {} RsGxsTransSerializer() : RsServiceSerializer(RS_SERVICE_TYPE_GXS_TRANS) {}
~RsGxsTransSerializer() {} virtual ~RsGxsTransSerializer() {}
RsItem* create_item(uint16_t service_id, uint8_t item_sub_id) const RsItem* create_item(uint16_t service_id, uint8_t item_sub_id) const
{ {

View File

@ -2259,7 +2259,7 @@ void p3MsgService::sendDistantMsgItem(RsMsgItem *msgitem)
msg_serialized_data, msg_serialized_rssize, msg_serialized_data, msg_serialized_rssize,
signing_key_id, grouter_message_id ); signing_key_id, grouter_message_id );
RsGxsTransId gxsMailId; RsGxsTransId gxsMailId;
mGxsTransServ.sendMail( gxsMailId, GxsTransSubServices::P3_MSG_SERVICE, mGxsTransServ.sendData( gxsMailId, GxsTransSubServices::P3_MSG_SERVICE,
signing_key_id, destination_key_id, signing_key_id, destination_key_id,
msg_serialized_data, msg_serialized_rssize ); msg_serialized_data, msg_serialized_rssize );

View File

@ -57,6 +57,8 @@
#define COL_GROUP_GRP_ID 0 #define COL_GROUP_GRP_ID 0
#define COL_GROUP_NUM_MSGS 1 #define COL_GROUP_NUM_MSGS 1
#define COL_GROUP_SIZE_MSGS 2 #define COL_GROUP_SIZE_MSGS 2
#define COL_GROUP_SUBSCRIBED 3
#define COL_GROUP_POPULARITY 4
static const int PARTIAL_VIEW_SIZE = 9 ; static const int PARTIAL_VIEW_SIZE = 9 ;
static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ; static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ;
@ -66,14 +68,6 @@ static const int GXSTRANS_STATISTICS_DELAY_BETWEEN_GROUP_REQ = 30 ; // never req
#define GXSTRANS_GROUP_DATA 0x02 #define GXSTRANS_GROUP_DATA 0x02
#define GXSTRANS_GROUP_STAT 0x03 #define GXSTRANS_GROUP_STAT 0x03
// static QColor colorScale(float f)
// {
// if(f == 0)
// return QColor::fromHsv(0,0,192) ;
// else
// return QColor::fromHsv((int)((1.0-f)*280),200,255) ;
// }
GxsTransportStatistics::GxsTransportStatistics(QWidget *parent) GxsTransportStatistics::GxsTransportStatistics(QWidget *parent)
: RsAutoUpdatePage(2000,parent) : RsAutoUpdatePage(2000,parent)
{ {
@ -87,8 +81,6 @@ GxsTransportStatistics::GxsTransportStatistics(QWidget *parent)
m_bProcessSettings = false; m_bProcessSettings = false;
mLastGroupReqTS = 0 ; mLastGroupReqTS = 0 ;
//_router_F->setWidget( _tst_CW = new GxsTransportStatisticsWidget() ) ;
/* Set header resize modes and initial section sizes Uploads TreeView*/ /* Set header resize modes and initial section sizes Uploads TreeView*/
QHeaderView_setSectionResizeMode(treeWidget->header(), QHeaderView::ResizeToContents); QHeaderView_setSectionResizeMode(treeWidget->header(), QHeaderView::ResizeToContents);
@ -238,9 +230,9 @@ void GxsTransportStatistics::updateContent()
groupTreeWidget->clear(); groupTreeWidget->clear();
for(std::map<RsGxsGroupId,GxsGroupStatistic>::const_iterator it(mGroupStats.begin());it!=mGroupStats.end();++it) for(std::map<RsGxsGroupId,RsGxsTransGroupStatistics>::const_iterator it(mGroupStats.begin());it!=mGroupStats.end();++it)
{ {
const GxsGroupStatistic& stat(it->second) ; const RsGxsTransGroupStatistics& stat(it->second) ;
QTreeWidgetItem *item = new QTreeWidgetItem(); QTreeWidgetItem *item = new QTreeWidgetItem();
groupTreeWidget->addTopLevelItem(item); groupTreeWidget->addTopLevelItem(item);
@ -248,6 +240,8 @@ void GxsTransportStatistics::updateContent()
item->setData(COL_GROUP_GRP_ID, Qt::DisplayRole, QString::fromStdString(stat.mGrpId.toStdString())) ; item->setData(COL_GROUP_GRP_ID, Qt::DisplayRole, QString::fromStdString(stat.mGrpId.toStdString())) ;
item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs)) ; item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs)) ;
item->setData(COL_GROUP_SIZE_MSGS,Qt::DisplayRole, QString::number(stat.mTotalSizeOfMsgs)) ; item->setData(COL_GROUP_SIZE_MSGS,Qt::DisplayRole, QString::number(stat.mTotalSizeOfMsgs)) ;
item->setData(COL_GROUP_SUBSCRIBED,Qt::DisplayRole, stat.subscribed?tr("Yes"):tr("No")) ;
item->setData(COL_GROUP_POPULARITY,Qt::DisplayRole, QString::number(stat.popularity)) ;
} }
} }
@ -266,7 +260,9 @@ void GxsTransportStatistics::personDetails()
void GxsTransportStatistics::loadRequest(const TokenQueue *queue, const TokenRequest &req) void GxsTransportStatistics::loadRequest(const TokenQueue *queue, const TokenRequest &req)
{ {
#ifdef DEBUG_GXSTRANS_STATS
std::cerr << "GxsTransportStatistics::loadRequest() UserType: " << req.mUserType << std::endl; std::cerr << "GxsTransportStatistics::loadRequest() UserType: " << req.mUserType << std::endl;
#endif
if (queue != mTransQueue) if (queue != mTransQueue)
{ {
@ -280,9 +276,6 @@ void GxsTransportStatistics::loadRequest(const TokenQueue *queue, const TokenReq
case GXSTRANS_GROUP_META: loadGroupMeta(req.mToken); case GXSTRANS_GROUP_META: loadGroupMeta(req.mToken);
break; break;
case GXSTRANS_GROUP_DATA: loadGroupData(req.mToken);
break;
case GXSTRANS_GROUP_STAT: loadGroupStat(req.mToken); case GXSTRANS_GROUP_STAT: loadGroupStat(req.mToken);
break; break;
@ -297,8 +290,10 @@ void GxsTransportStatistics::requestGroupMeta()
{ {
mStateHelper->setLoading(GXSTRANS_GROUP_META, true); mStateHelper->setLoading(GXSTRANS_GROUP_META, true);
#ifdef DEBUG_GXSTRANS_STATS
std::cerr << "GxsTransportStatisticsWidget::requestGroupMeta()"; std::cerr << "GxsTransportStatisticsWidget::requestGroupMeta()";
std::cerr << std::endl; std::cerr << std::endl;
#endif
mTransQueue->cancelActiveRequestTokens(GXSTRANS_GROUP_META); mTransQueue->cancelActiveRequestTokens(GXSTRANS_GROUP_META);
@ -318,21 +313,23 @@ void GxsTransportStatistics::requestGroupStat(const RsGxsGroupId &groupId)
void GxsTransportStatistics::loadGroupStat(const uint32_t &token) void GxsTransportStatistics::loadGroupStat(const uint32_t &token)
{ {
#ifdef DEBUG_GXSTRANS_STATS
std::cerr << "GxsTransportStatistics::loadGroupStat." << std::endl; std::cerr << "GxsTransportStatistics::loadGroupStat." << std::endl;
#endif
GxsGroupStatistic stats; GxsGroupStatistic stats;
rsGxsTrans->getGroupStatistic(token, stats); rsGxsTrans->getGroupStatistic(token, stats);
mGroupStats[stats.mGrpId] = stats ; dynamic_cast<GxsGroupStatistic&>(mGroupStats[stats.mGrpId]) = stats ;
} }
void GxsTransportStatistics::loadGroupMeta(const uint32_t& token) void GxsTransportStatistics::loadGroupMeta(const uint32_t& token)
{ {
mStateHelper->setLoading(GXSTRANS_GROUP_META, false); mStateHelper->setLoading(GXSTRANS_GROUP_META, false);
#ifdef DEBUG_GXSTRANS_STATS
std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta()"; std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta()";
std::cerr << std::endl; std::cerr << std::endl;
#endif
// ui.treeWidget_membership->clear();
std::list<RsGroupMetaData> groupInfo; std::list<RsGroupMetaData> groupInfo;
std::list<RsGroupMetaData>::iterator vit; std::list<RsGroupMetaData>::iterator vit;
@ -347,45 +344,29 @@ void GxsTransportStatistics::loadGroupMeta(const uint32_t& token)
mStateHelper->setActive(GXSTRANS_GROUP_META, true); mStateHelper->setActive(GXSTRANS_GROUP_META, true);
// /* add the top level item */ std::set<RsGxsGroupId> existing_groups ;
// QTreeWidgetItem *personalCirclesItem = new QTreeWidgetItem();
// personalCirclesItem->setText(0, tr("Personal Circles"));
// ui.treeWidget_membership->addTopLevelItem(personalCirclesItem);
//
// QTreeWidgetItem *externalAdminCirclesItem = new QTreeWidgetItem();
// externalAdminCirclesItem->setText(0, tr("External Circles (Admin)"));
// ui.treeWidget_membership->addTopLevelItem(externalAdminCirclesItem);
//
// QTreeWidgetItem *externalSubCirclesItem = new QTreeWidgetItem();
// externalSubCirclesItem->setText(0, tr("External Circles (Subscribed)"));
// ui.treeWidget_membership->addTopLevelItem(externalSubCirclesItem);
//
// QTreeWidgetItem *externalOtherCirclesItem = new QTreeWidgetItem();
// externalOtherCirclesItem->setText(0, tr("External Circles (Other)"));
// ui.treeWidget_membership->addTopLevelItem(externalOtherCirclesItem);
std::set<RsGxsGroupId> existing_groups ;
for(vit = groupInfo.begin(); vit != groupInfo.end(); ++vit) for(vit = groupInfo.begin(); vit != groupInfo.end(); ++vit)
{ {
existing_groups.insert(vit->mGroupId) ; existing_groups.insert(vit->mGroupId) ;
/* Add Widget, and request Pages */ /* Add Widget, and request Pages */
#ifdef DEBUG_GXSTRANS_STATS
std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta() GroupId: " << vit->mGroupId << " Group: " << vit->mGroupName << std::endl; std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta() GroupId: " << vit->mGroupId << " Group: " << vit->mGroupName << std::endl;
#endif
requestGroupStat(vit->mGroupId) ; requestGroupStat(vit->mGroupId) ;
RsGxsTransGroupStatistics& s(mGroupStats[vit->mGroupId]);
s.popularity = vit->mPop ;
s.subscribed = IS_GROUP_SUBSCRIBED(vit->mSubscribeFlags) ;
} }
// remove group stats for group that do not exist anymore // remove group stats for group that do not exist anymore
for(std::map<RsGxsGroupId,GxsGroupStatistic>::iterator it(mGroupStats.begin());it!=mGroupStats.end();) for(std::map<RsGxsGroupId,RsGxsTransGroupStatistics>::iterator it(mGroupStats.begin());it!=mGroupStats.end();)
if(existing_groups.find(it->first) == existing_groups.end()) if(existing_groups.find(it->first) == existing_groups.end())
it = mGroupStats.erase(it); it = mGroupStats.erase(it);
else else
++it; ++it;
} }
void GxsTransportStatistics::loadGroupData(const uint32_t& token)
{
std::cerr << __PRETTY_FUNCTION__ << ": not implemented." << std::endl;
}

View File

@ -34,72 +34,53 @@
class GxsTransportStatisticsWidget ; class GxsTransportStatisticsWidget ;
class UIStateHelper; class UIStateHelper;
class RsGxsTransGroupStatistics: public GxsGroupStatistic
{
public:
RsGxsTransGroupStatistics() {}
bool subscribed ;
int popularity ;
};
class GxsTransportStatistics: public RsAutoUpdatePage, public TokenResponse, public Ui::GxsTransportStatistics class GxsTransportStatistics: public RsAutoUpdatePage, public TokenResponse, public Ui::GxsTransportStatistics
{ {
Q_OBJECT Q_OBJECT
public: public:
GxsTransportStatistics(QWidget *parent = NULL) ; GxsTransportStatistics(QWidget *parent = NULL) ;
~GxsTransportStatistics(); ~GxsTransportStatistics();
// Cache for peer names. // Cache for peer names.
static QString getPeerName(const RsPeerId& peer_id) ; static QString getPeerName(const RsPeerId& peer_id) ;
virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req) ; virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req) ;
void updateContent() ; void updateContent() ;
private slots: private slots:
/** Create the context popup menu and it's submenus */ /** Create the context popup menu and it's submenus */
void CustomPopupMenu( QPoint point ); void CustomPopupMenu( QPoint point );
void personDetails(); void personDetails();
private: private:
void loadGroupData(const uint32_t& token); void loadGroupMeta(const uint32_t& token);
void loadGroupMeta(const uint32_t& token); void loadGroupStat(const uint32_t& token);
void loadGroupStat(const uint32_t& token);
void requestGroupData(); void requestGroupMeta();
void requestGroupMeta(); void requestGroupStat(const RsGxsGroupId &groupId);
void requestGroupStat(const RsGxsGroupId &groupId);
void processSettings(bool bLoad); void processSettings(bool bLoad);
bool m_bProcessSettings; bool m_bProcessSettings;
virtual void updateDisplay() ; virtual void updateDisplay() ;
GxsTransportStatisticsWidget *_tst_CW ; GxsTransportStatisticsWidget *_tst_CW ;
TokenQueue *mTransQueue ; TokenQueue *mTransQueue ;
UIStateHelper *mStateHelper; UIStateHelper *mStateHelper;
uint32_t mLastGroupReqTS ; uint32_t mLastGroupReqTS ;
// temporary storage of retrieved data, for display (useful because this is obtained from the async token system) // temporary storage of retrieved data, for display (useful because this is obtained from the async token system)
std::map<RsGxsGroupId,GxsGroupStatistic> mGroupStats ; // stores the list of active groups and statistics about each of them. std::map<RsGxsGroupId,RsGxsTransGroupStatistics> mGroupStats ; // stores the list of active groups and statistics about each of them.
} ; } ;
// class GxsTransportStatisticsWidget: public QWidget
// {
// Q_OBJECT
//
// public:
// GxsTransportStatisticsWidget(QWidget *parent = NULL) ;
//
// virtual void paintEvent(QPaintEvent *event) ;
// virtual void resizeEvent(QResizeEvent *event);
// virtual void wheelEvent(QWheelEvent *event);
//
// void updateContent() ;
// private:
// static QString speedString(float f) ;
//
// QPixmap pixmap ;
// int maxWidth,maxHeight ;
// int mCurrentN ;
// int mNumberOfKnownKeys ;
// int mMinWheelZoneX ;
// int mMinWheelZoneY ;
// int mMaxWheelZoneX ;
// int mMaxWheelZoneY ;
// };

View File

@ -41,6 +41,16 @@
<string>Total size of messages</string> <string>Total size of messages</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>Subscribed</string>
</property>
</column>
<column>
<property name="text">
<string>Popularity</string>
</property>
</column>
</widget> </widget>
</widget> </widget>
</item> </item>
@ -61,6 +71,9 @@
<property name="sortingEnabled"> <property name="sortingEnabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<column> <column>
<property name="text"> <property name="text">
<string>ID</string> <string>ID</string>