fixed ChannelsCommentsItem

This commit is contained in:
csoler 2025-10-31 19:49:39 +01:00
parent c8a93d75f2
commit c8dbf34a14
4 changed files with 180 additions and 265 deletions

View file

@ -49,39 +49,14 @@
* #define DEBUG_ITEM 1
****/
// ChannelsCommentsItem::ChannelsCommentsItem(FeedHolder *feedHolder, uint32_t feedId, const RsGroupMetaData& group_meta, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate,const std::set<RsGxsMessageId>& older_versions) :
// GxsFeedItem(feedHolder, feedId, group_meta.mGroupId, messageId, isHome, rsGxsChannels, autoUpdate),
// mGroupMeta(group_meta)
// {
// mLoadingGroup = false;
// mLoadingMessage = false;
// mLoadingComment = false;
//
// mPost.mMeta.mMsgId = messageId; // useful for uniqueIdentifer() before the post is loaded
// mPost.mMeta.mGroupId = mGroupMeta.mGroupId;
//
// QVector<RsGxsMessageId> v;
// //bool self = false;
//
// for(std::set<RsGxsMessageId>::const_iterator it(older_versions.begin());it!=older_versions.end();++it)
// v.push_back(*it) ;
//
// if(older_versions.find(messageId) == older_versions.end())
// v.push_back(messageId);
//
// setMessageVersions(v) ;
// setup();
//
// // no call to loadGroup() here because we have it already.
// }
ChannelsCommentsItem::ChannelsCommentsItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId &commentId, const RsGxsMessageId &threadId, bool isHome, bool autoUpdate) :
GxsFeedItem(feedHolder, feedId, groupId, commentId, isHome, rsGxsChannels, autoUpdate), // this one should be in GxsFeedItem
mThreadId(threadId)
{
mLoading= false;
QVector<RsGxsMessageId> v;
mLoadingStatus = NO_DATA;
mLoadingComment = false;
mLoadingGroup = false;
mLoadingMessage = false;
setup();
}
@ -91,10 +66,28 @@ void ChannelsCommentsItem::paintEvent(QPaintEvent *e)
/* This method employs a trick to trigger a deferred loading. The post and group is requested only
* when actually displayed on the screen. */
if(!mLoaded)
{
mLoaded = true ;
load();
if(mLoadingStatus != FILLED && !mGroupMeta.mGroupId.isNull() && !mComment.mMeta.mMsgId.isNull())
mLoadingStatus = HAS_DATA;
if(mGroupMeta.mGroupId.isNull() && !mLoadingGroup)
loadGroupData();
if(mComment.mMeta.mMsgId.isNull() && !mLoadingComment)
loadCommentData();
if(mPost.mMeta.mMsgId.isNull() && !mLoadingMessage)
loadMessageData();
switch(mLoadingStatus)
{
case FILLED:
case NO_DATA:
default:
break;
case HAS_DATA:
fill();
mLoadingStatus = FILLED;
break;
}
GxsFeedItem::paintEvent(e) ;
@ -104,9 +97,14 @@ ChannelsCommentsItem::~ChannelsCommentsItem()
{
auto timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(GROUP_ITEM_LOADING_TIMEOUT_ms);
while( mLoading && std::chrono::steady_clock::now() < timeout )
while( (mLoadingGroup || mLoadingComment)
&& std::chrono::steady_clock::now() < timeout )
{
RsDbg() << __PRETTY_FUNCTION__ << " is Waiting for data to load " << std::endl;
RsDbg() << __PRETTY_FUNCTION__ << " is Waiting for data to load "
<< (mLoadingGroup ? "Group " : "")
<< (mLoadingMessage ? "Message " : "")
<< (mLoadingComment ? "Comment " : "")
<< std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
@ -115,7 +113,7 @@ ChannelsCommentsItem::~ChannelsCommentsItem()
bool ChannelsCommentsItem::isUnread() const
{
return IS_MSG_UNREAD(mPost.mMeta.mMsgStatus) ;
return IS_MSG_UNREAD(mComment.mMeta.mMsgStatus) ;
}
void ChannelsCommentsItem::setup()
@ -141,9 +139,7 @@ void ChannelsCommentsItem::setup()
setAttribute(Qt::WA_DeleteOnClose, true);
mInFill = false;
mCloseOnRead = false;
mLoaded = false;
/* clear ui */
ui->datetimeLabel->clear();
@ -178,33 +174,6 @@ void ChannelsCommentsItem::setup()
ui->expandFrame->hide();
}
bool ChannelsCommentsItem::setPost(const RsGxsChannelPost& post, bool doFill)
{
mPost = post;
if (doFill)
fill();
return true;
}
bool ChannelsCommentsItem::setMissingPost()
{
fill(true);
return true;
}
QString ChannelsCommentsItem::getTitleLabel()
{
return QString::fromUtf8(mPost.mMeta.mMsgName.c_str());
}
QString ChannelsCommentsItem::getMsgLabel()
{
//return RsHtml().formatText(NULL, QString::fromUtf8(mPost.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS);
// Disabled, because emoticon replacement kills performance.
return QString::fromUtf8(mPost.mMsg.c_str());
}
QString ChannelsCommentsItem::groupName()
{
return QString::fromUtf8(mGroupMeta.mGroupName.c_str());
@ -219,93 +188,109 @@ void ChannelsCommentsItem::loadComments()
return ;
MainWindow::showWindow(MainWindow::Channels);
channelDialog->navigate(mPost.mMeta.mGroupId, mPost.mMeta.mMsgId);
channelDialog->navigate(mComment.mMeta.mGroupId, mComment.mMeta.mMsgId);
}
void ChannelsCommentsItem::loadGroup()
void ChannelsCommentsItem::loadGroupData()
{
//#ifdef DEBUG_ITEM
// std::cerr << "GxsChannelGroupItem::loadGroup()";
// std::cerr << std::endl;
//#endif
// if(mLoading)
// return;
//
// mLoading= true;
//
// std::cerr << "Loading group" << std::endl;
// RsThread::async([this]()
// {
// // 1 - get group data
//
// std::vector<RsGxsChannelGroup> groups;
// const std::list<RsGxsGroupId> groupIds = { groupId() };
//
// if(!rsGxsChannels->getChannelsInfo(groupIds,groups)) // would be better to call channel Summaries for a single group
// {
// RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data" << std::endl;
// return;
// }
//
// if (groups.size() != 1)
// {
// std::cerr << "GxsGxsChannelGroupItem::loadGroup() Wrong number of Items";
// std::cerr << std::endl;
// return;
// }
// RsGxsChannelGroup group(groups[0]);
//
// RsQThreadUtils::postToObject( [group,this]()
// {
// /* Here it goes any code you want to be executed on the Qt Gui
// * thread, for example to update the data model with new information
// * after a blocking call to RetroShare API complete */
//
// mGroupMeta = group.mMeta;
// mLoading= false;
//
// std::cerr << "End loading group" << std::endl;
// }, this );
// });
}
void ChannelsCommentsItem::load()
{
// This function loads everything that's needed:
// - the comment text
// - the comment parent message
std::cerr << "GxsChannelGroupItem::loadGroup()" << std::endl;
#ifdef DEBUG_ITEM
std::cerr << "ChannelsCommentsItem::loadMessage()";
std::cerr << std::endl;
#endif
if(mLoading)
return;
mLoading= true;
mLoadingGroup = true;
RsThread::async([this]()
{
// 1 - get group meta data
{
// 1 - get group data
std::vector<RsGxsChannelGroup> groups;
const std::list<RsGxsGroupId> groupIds = { groupId() };
if(!rsGxsChannels->getChannelsInfo(groupIds,groups)) // would be better to call channel Summaries for a single group
{
RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data" << std::endl;
mLoading= false;
RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data for group " << groupId() << std::endl;
mLoadingGroup = false;
return;
}
if (groups.size() != 1)
{
std::cerr << "GxsGxsChannelGroupItem::loadGroup() Wrong number of Items" << std::endl;
mLoading= false;
std::cerr << "GxsGxsChannelGroupItem::loadGroup() Wrong number of Items for group " << groupId() ;
std::cerr << std::endl;
mLoadingGroup = false;
return;
}
RsGxsChannelGroup group(groups[0]);
// 2 - get message and comment data
RsQThreadUtils::postToObject( [group,this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
mGroupMeta = group.mMeta;
mLoadingGroup = false;
update(); // this triggers a paintEvent if needed.
}, this );
});
}
void ChannelsCommentsItem::loadMessageData()
{
#ifdef DEBUG_ITEM
std::cerr << "ChannelsCommentsItem::loadCommentData()";
std::cerr << std::endl;
#endif
mLoadingMessage = true;
RsThread::async([this]()
{
// 1 - get message and comment data
std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> comments;
std::vector<RsGxsVote> votes;
if(! rsGxsChannels->getChannelContent( groupId(), std::set<RsGxsMessageId>( { mThreadId } ),posts,comments,votes))
{
RsErr() << "GxsGxsChannelGroupItem::loadMessage() ERROR getting data" << std::endl;
mLoadingMessage = false;
return;
}
// now that everything is in place, update the UI
RsQThreadUtils::postToObject( [posts,this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
if(posts.size()!=1) // the original post cannot be found. Removing the comment item.
{
mLoadingMessage = false;
removeItem();
return;
}
mPost = posts[0];
mLoadingMessage = false;
update();
}, this );
});
}
void ChannelsCommentsItem::loadCommentData()
{
#ifdef DEBUG_ITEM
std::cerr << "ChannelsCommentsItem::loadCommentData()";
std::cerr << std::endl;
#endif
mLoadingComment = true;
RsThread::async([this]()
{
// 1 - get message and comment data
std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> comments;
@ -313,53 +298,28 @@ void ChannelsCommentsItem::load()
if(! rsGxsChannels->getChannelContent( groupId(), std::set<RsGxsMessageId>( { messageId(),mThreadId } ),posts,comments,votes))
{
RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data" << std::endl;
mLoading= false;
RsErr() << "GxsGxsChannelGroupItem::loadComment() ERROR getting data" << std::endl;
mLoadingComment = false;
return;
}
if(comments.size()!=1)
{
mLoadingComment = false;
return;
}
// now that everything is in place, update the UI
// now that everything is in place, update the UI
RsQThreadUtils::postToObject( [group,posts,comments,this]()
RsQThreadUtils::postToObject( [comments,this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
mGroupMeta = group.mMeta;
mComment = comments[0];
mLoadingComment = false;
if(comments.size()!=1)
{
mLoading=false;
removeItem();
}
RsGxsComment cmt(comments[0]);
uint32_t autorized_lines = (int)floor( (ui->avatarLabel->height() - ui->button_HL->sizeHint().height())
/ QFontMetricsF(ui->subjectLabel->font()).height());
ui->commLabel->setText(RsHtml().formatText(NULL, RsStringUtil::CopyLines(QString::fromUtf8(cmt.mComment.c_str()), autorized_lines), RSHTML_FORMATTEXT_EMBED_LINKS));
ui->nameLabel->setId(cmt.mMeta.mAuthorId);
ui->datetimeLabel->setText(DateTime::formatLongDateTime(cmt.mMeta.mPublishTs));
RsIdentityDetails idDetails ;
rsIdentity->getIdDetails(cmt.mMeta.mAuthorId,idDetails);
QPixmap pixmap ;
if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL))
pixmap = GxsIdDetails::makeDefaultIcon(cmt.mMeta.mAuthorId,GxsIdDetails::LARGE);
ui->avatarLabel->setPixmap(pixmap);
//Change this item to be uploaded with thread element. This is really bad practice.
if (posts.size() == 1)
setPost(posts[0]);
else
setMissingPost();
emit sizeChanged(this);
mLoading=false;
update();
}, this );
});
@ -372,11 +332,9 @@ void ChannelsCommentsItem::fill(bool missing_post)
std::cerr << std::endl;
#endif
mInFill = true;
if (!mIsHome)
{
if (mCloseOnRead && !IS_MSG_NEW(mPost.mMeta.mMsgStatus)) {
if (mCloseOnRead && !IS_MSG_NEW(mComment.mMeta.mMsgStatus)) {
removeItem();
}
@ -393,13 +351,13 @@ void ChannelsCommentsItem::fill(bool missing_post)
ui->readButton->hide();
if (IS_MSG_NEW(mPost.mMeta.mMsgStatus)) {
if (IS_MSG_NEW(mComment.mMeta.mMsgStatus)) {
mCloseOnRead = true;
}
}
else
{
if(missing_post)
if(mPost.mMeta.mMsgId.isNull())
ui->subjectLabel->setText("[" + QObject::tr("Missing channel post")+"]");
else
ui->subjectLabel->setText(RsStringUtil::CopyLines(QString::fromUtf8(mPost.mMsg.c_str()), 2)) ;
@ -415,7 +373,7 @@ void ChannelsCommentsItem::fill(bool missing_post)
{
ui->readButton->setVisible(true);
setReadStatus(IS_MSG_NEW(mPost.mMeta.mMsgStatus), IS_MSG_UNREAD(mPost.mMeta.mMsgStatus) || IS_MSG_NEW(mPost.mMeta.mMsgStatus));
setReadStatus(IS_MSG_NEW(mComment.mMeta.mMsgStatus), IS_MSG_UNREAD(mComment.mMeta.mMsgStatus) || IS_MSG_NEW(mComment.mMeta.mMsgStatus));
}
else
{
@ -424,31 +382,44 @@ void ChannelsCommentsItem::fill(bool missing_post)
mCloseOnRead = false;
}
mInFill = false;
uint32_t autorized_lines = (int)floor( (ui->avatarLabel->height() - ui->button_HL->sizeHint().height())
/ QFontMetricsF(ui->subjectLabel->font()).height());
ui->commLabel->setText(RsHtml().formatText(NULL, RsStringUtil::CopyLines(QString::fromUtf8(mComment.mComment.c_str()), autorized_lines), RSHTML_FORMATTEXT_EMBED_LINKS));
ui->nameLabel->setId(mComment.mMeta.mAuthorId);
ui->datetimeLabel->setText(DateTime::formatLongDateTime(mComment.mMeta.mPublishTs));
RsIdentityDetails idDetails ;
rsIdentity->getIdDetails(mComment.mMeta.mAuthorId,idDetails);
QPixmap pixmap ;
if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL))
pixmap = GxsIdDetails::makeDefaultIcon(mComment.mMeta.mAuthorId,GxsIdDetails::LARGE);
ui->avatarLabel->setPixmap(pixmap);
}
QString ChannelsCommentsItem::messageName()
{
return QString::fromUtf8(mPost.mMeta.mMsgName.c_str());
return QString::fromUtf8(mPost.mMeta.mMsgName.c_str());
}
void ChannelsCommentsItem::setReadStatus(bool isNew, bool isUnread)
{
if (isNew)
mPost.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_NEW;
mComment.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_NEW;
else
mPost.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_NEW;
mComment.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_NEW;
if (isUnread)
{
mPost.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD;
mComment.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD;
whileBlocking(ui->readButton)->setChecked(true);
ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png"));
}
else
{
mPost.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD;
mComment.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD;
whileBlocking(ui->readButton)->setChecked(false);
ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png"));
}
@ -522,24 +493,18 @@ void ChannelsCommentsItem::unsubscribeChannel()
void ChannelsCommentsItem::readToggled(bool /*checked*/)
{
if (mInFill) {
return;
}
mCloseOnRead = false;
RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId());
rsGxsChannels->setCommentReadStatus(msgPair, isUnread());
//setReadStatus(false, checked); // Updated by events
}
void ChannelsCommentsItem::makeDownVote()
{
RsGxsGrpMsgIdPair msgId;
msgId.first = mPost.mMeta.mGroupId;
msgId.second = mPost.mMeta.mMsgId;
msgId.first = mComment.mMeta.mGroupId;
msgId.second = mComment.mMeta.mMsgId;
ui->voteUpButton->setEnabled(false);
ui->voteDownButton->setEnabled(false);
@ -550,8 +515,8 @@ void ChannelsCommentsItem::makeDownVote()
void ChannelsCommentsItem::makeUpVote()
{
RsGxsGrpMsgIdPair msgId;
msgId.first = mPost.mMeta.mGroupId;
msgId.second = mPost.mMeta.mMsgId;
msgId.first = mComment.mMeta.mGroupId;
msgId.second = mComment.mMeta.mMsgId;
ui->voteUpButton->setEnabled(false);
ui->voteDownButton->setEnabled(false);

View file

@ -50,34 +50,25 @@ public:
bool isHome,
bool autoUpdate);
// This one is used in channel thread widget. We don't want the group data to reload at every post, so we load it in the hosting
// GxsChannelsPostsWidget and pass it to created items.
// ChannelsCommentsItem(FeedHolder *feedHolder, uint32_t feedId, const RsGroupMetaData& group, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate, const std::set<RsGxsMessageId>& older_versions = std::set<RsGxsMessageId>());
virtual ~ChannelsCommentsItem();
uint64_t uniqueIdentifier() const override { return hash_64bits("ChannelsCommentsItem " + messageId().toStdString()) ; }
bool setGroup(const RsGxsChannelGroup& group, bool doFill = true);
bool setPost(const RsGxsChannelPost& post, bool doFill = true);
bool setMissingPost();
protected:
enum LoadingStatus {
NO_DATA = 0x00,
HAS_DATA = 0x01,
FILLED = 0x02
};
QString getTitleLabel();
QString getMsgLabel();
bool isLoaded() const {return mLoaded;};
bool isUnread() const ;
void setReadStatus(bool isNew, bool isUnread);
const std::set<RsGxsMessageId>& olderVersions() const { return mPost.mOlderVersions; }
static uint64_t computeIdentifier(const RsGxsMessageId& msgid) { return hash64("ChannelsCommentsItem " + msgid.toStdString()) ; }
protected:
//void init(const RsGxsMessageId& messageId,const std::set<RsGxsMessageId>& older_versions);
/* FeedItem */
virtual void doExpand(bool open);
virtual void expandFill(bool first);
virtual void doExpand(bool open) override;
virtual void expandFill(bool first) override;
// This does nothing except triggering the loading of the post data and comments. This function is mainly used to detect
// when the post is actually made visible.
@ -85,12 +76,12 @@ protected:
virtual void paintEvent(QPaintEvent *) override;
/* GxsGroupFeedItem */
virtual QString groupName();
virtual void loadGroup() override;
virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_CHANNEL; }
virtual QString groupName() override;
virtual void loadGroup() override {}
virtual RetroShareLink::enumType getLinkType() override { return RetroShareLink::TYPE_CHANNEL; }
/* GxsFeedItem */
virtual QString messageName();
virtual QString messageName() override;
virtual void loadMessage() override {}
virtual void loadComment() override {}
@ -110,19 +101,23 @@ signals:
void vote(const RsGxsGrpMsgIdPair& msgId, bool up);
private:
void load();
void setup();
void fill(bool missing_post=false);
void loadGroupData();
void loadMessageData();
void loadCommentData();
private:
bool mInFill;
bool mCloseOnRead;
bool mLoaded;
bool mLoading;
LoadingStatus mLoadingStatus;
bool mLoadingComment;
bool mLoadingGroup;
bool mLoadingMessage;
RsGroupMetaData mGroupMeta;
RsGxsChannelPost mPost;
RsGxsComment mComment;
RsGxsChannelPost mPost;
RsGxsMessageId mThreadId;
/** Qt Designer generated object */

View file

@ -58,6 +58,8 @@ GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId,
v.push_back(messageId);
mLoadingStatus = NO_DATA;
mLoadingMessage = false;
mLoadingGroup = false;
setMessageVersions(v) ;
setup();
@ -173,8 +175,6 @@ void GxsChannelPostItem::setup()
// hide voting buttons, backend is not implemented yet
ui->voteUpButton->hide();
ui->voteDownButton->hide();
//connect(ui-> voteUpButton, SIGNAL(clicked()), this, SLOT(makeUpVote()));
//connect(ui->voteDownButton, SIGNAL(clicked()), this, SLOT(makeDownVote()));
ui->scoreLabel->hide();
@ -533,22 +533,6 @@ void GxsChannelPostItem::setReadStatus(bool isNew, bool isUnread)
ui->feedFrame->style()->polish( ui->feedFrame);
}
// void GxsChannelPostItem::setFileCleanUpWarning(uint32_t time_left)
// {
// int hours = (int)time_left/3600;
// int minutes = (time_left - hours*3600)%60;
//
// ui->warning_label->setText(tr("Warning! You have less than %1 hours and %2 minute before this file is deleted Consider saving it.").arg(
// QString::number(hours)).arg(QString::number(minutes)));
//
// QFont warnFont = ui->warning_label->font();
// warnFont.setBold(true);
// ui->warning_label->setFont(warnFont);
//
// ui->warn_image_label->setVisible(true);
// ui->warning_label->setVisible(true);
// }
void GxsChannelPostItem::updateItem()
{
/* fill in */
@ -732,28 +716,3 @@ void GxsChannelPostItem::readToggled(bool /*checked*/)
//setReadStatus(false, checked); // Updated by events
}
void GxsChannelPostItem::makeDownVote()
{
RsGxsGrpMsgIdPair msgId;
msgId.first = mPost.mMeta.mGroupId;
msgId.second = mPost.mMeta.mMsgId;
ui->voteUpButton->setEnabled(false);
ui->voteDownButton->setEnabled(false);
emit vote(msgId, false);
}
void GxsChannelPostItem::makeUpVote()
{
RsGxsGrpMsgIdPair msgId;
msgId.first = mPost.mMeta.mGroupId;
msgId.second = mPost.mMeta.mMsgId;
ui->voteUpButton->setEnabled(false);
ui->voteDownButton->setEnabled(false);
emit vote(msgId, true);
}

View file

@ -76,7 +76,6 @@ protected:
const std::set<RsGxsMessageId>& olderVersions() const { return mPost.mOlderVersions; }
static uint64_t computeIdentifier(const RsGxsMessageId& msgid) { return hash64("GxsChannelPostItem " + msgid.toStdString()) ; }
//void init(const RsGxsMessageId& messageId,const std::set<RsGxsMessageId>& older_versions);
/* FeedItem */
virtual void doExpand(bool open) override;
@ -110,9 +109,6 @@ private slots:
void unsubscribeChannel();
void updateItem();
void makeUpVote();
void makeDownVote();
signals:
void vote(const RsGxsGrpMsgIdPair& msgId, bool up);