mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-12 16:09:37 -05:00
Merge pull request #2734 from csoler/v0.6-Channels-002
Fixing Channel posts counting
This commit is contained in:
commit
1d0c05fa93
@ -369,7 +369,25 @@ void GxsChannelDialog::toggleAutoDownload()
|
||||
|
||||
bool GxsChannelDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat)
|
||||
{
|
||||
return rsGxsChannels->getChannelStatistics(groupId,stat);
|
||||
// What follows is a hack to replace the GXS group statistics by the actual count of unread messages in channels,
|
||||
// which should take into account old post versions, discard comments and votes, etc.
|
||||
|
||||
RsGxsChannelStatistics s;
|
||||
bool res = rsGxsChannels->getChannelStatistics(groupId,s);
|
||||
|
||||
if(!res)
|
||||
return false;
|
||||
|
||||
stat.mGrpId = groupId;
|
||||
stat.mNumMsgs = s.mNumberOfPosts;
|
||||
|
||||
stat.mTotalSizeOfMsgs = 0; // hopefuly unused. Required the loading of the full channel data, so not very convenient.
|
||||
stat.mNumThreadMsgsNew = s.mNumberOfNewPosts;
|
||||
stat.mNumThreadMsgsUnread = s.mNumberOfUnreadPosts;
|
||||
stat.mNumChildMsgsNew = 0;
|
||||
stat.mNumChildMsgsUnread = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GxsChannelDialog::getGroupData(std::list<RsGxsGenericGroupData*>& groupInfo)
|
||||
|
@ -66,40 +66,6 @@ void RsGxsChannelPostsModel::setMode(TreeMode mode)
|
||||
triggerViewUpdate(true,true);
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::computeCommentCounts( std::vector<RsGxsChannelPost>& posts, std::vector<RsGxsComment>& comments)
|
||||
{
|
||||
// Store posts IDs in a std::map to avoid a quadratic cost
|
||||
|
||||
std::map<RsGxsMessageId,uint32_t> post_indices;
|
||||
|
||||
for(uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
post_indices[posts[i].mMeta.mMsgId] = i;
|
||||
posts[i].mCommentCount = 0; // should be 0 already, but we secure that value.
|
||||
posts[i].mUnreadCommentCount = 0;
|
||||
}
|
||||
|
||||
// now look into comments and increase the count
|
||||
|
||||
for(uint32_t i=0;i<comments.size();++i)
|
||||
{
|
||||
auto it = post_indices.find(comments[i].mMeta.mThreadId);
|
||||
|
||||
// This happens when because of sync periods, we receive
|
||||
// the comments for a post, but not the post itself.
|
||||
// In this case, the post the comment refers to is just not here.
|
||||
// it->second>=posts.size() is impossible by construction, since post_indices
|
||||
// is previously filled using posts ids.
|
||||
|
||||
if(it == post_indices.end())
|
||||
continue;
|
||||
|
||||
++posts[it->second].mCommentCount;
|
||||
|
||||
if(IS_MSG_NEW(comments[i].mMeta.mMsgStatus))
|
||||
++posts[it->second].mUnreadCommentCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RsGxsChannelPostsModel::initEmptyHierarchy()
|
||||
@ -548,8 +514,9 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
|
||||
initEmptyHierarchy();
|
||||
mChannelGroup = group;
|
||||
|
||||
createPostsArray(posts);
|
||||
// createPostsArray(posts);
|
||||
|
||||
mPosts = posts;
|
||||
std::sort(mPosts.begin(),mPosts.end());
|
||||
|
||||
for(uint32_t i=0;i<mPosts.size();++i)
|
||||
@ -597,30 +564,23 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
||||
|
||||
RsGxsChannelGroup group = groups[0];
|
||||
|
||||
// We use the heap because the arrays need to be stored accross async
|
||||
std::vector<RsGxsChannelPost> *posts = new std::vector<RsGxsChannelPost>(); // We use the heap because the arrays need to be stored accross async
|
||||
std::vector<RsGxsComment> comments ;
|
||||
std::vector<RsGxsVote> votes ;
|
||||
|
||||
std::vector<RsGxsChannelPost> *posts = new std::vector<RsGxsChannelPost>();
|
||||
std::vector<RsGxsComment> *comments = new std::vector<RsGxsComment>();
|
||||
std::vector<RsGxsVote> *votes = new std::vector<RsGxsVote>();
|
||||
|
||||
if(!rsGxsChannels->getChannelAllContent(group_id, *posts,*comments,*votes))
|
||||
if(!rsGxsChannels->getChannelAllContent(group_id, *posts,comments,votes))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel messages for channel " << group_id << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cerr << "Got channel all content for channel " << group_id << std::endl;
|
||||
std::cerr << " posts : " << posts->size() << std::endl;
|
||||
std::cerr << " comments: " << comments->size() << std::endl;
|
||||
std::cerr << " votes : " << votes->size() << std::endl;
|
||||
|
||||
// This shouldn't be needed normally. We need it until a background process computes the number of comments per
|
||||
// post and stores it in the service string. Since we request all data, this process isn't costing much anyway.
|
||||
|
||||
computeCommentCounts(*posts,*comments);
|
||||
std::cerr << " comments: " << comments.size() << std::endl;
|
||||
std::cerr << " votes : " << votes.size() << std::endl;
|
||||
|
||||
// 2 - update the model in the UI thread.
|
||||
|
||||
RsQThreadUtils::postToObject( [group,posts,comments,votes,this]()
|
||||
RsQThreadUtils::postToObject( [group,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
|
||||
@ -631,8 +591,6 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
||||
setPosts(group,*posts) ;
|
||||
|
||||
delete posts;
|
||||
delete comments;
|
||||
delete votes;
|
||||
|
||||
MainWindow::getPage(MainWindow::Channels)->setCursor(Qt::ArrowCursor) ;
|
||||
|
||||
@ -641,268 +599,6 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
||||
});
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::old_createPostsArray(std::vector<RsGxsChannelPost>& posts)
|
||||
{
|
||||
// collect new versions of posts if any
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Inserting channel posts" << std::endl;
|
||||
#endif
|
||||
|
||||
std::vector<uint32_t> new_versions ;
|
||||
for (uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
if(posts[i].mMeta.mOrigMsgId == posts[i].mMeta.mMsgId)
|
||||
posts[i].mMeta.mOrigMsgId.clear();
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " " << i << ": name=\"" << posts[i].mMeta.mMsgName << "\" msg_id=" << posts[i].mMeta.mMsgId << ": orig msg id = " << posts[i].mMeta.mOrigMsgId << std::endl;
|
||||
#endif
|
||||
|
||||
if(!posts[i].mMeta.mOrigMsgId.isNull())
|
||||
new_versions.push_back(i) ;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "New versions: " << new_versions.size() << std::endl;
|
||||
#endif
|
||||
|
||||
if(!new_versions.empty())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " New versions present. Replacing them..." << std::endl;
|
||||
std::cerr << " Creating search map." << std::endl;
|
||||
#endif
|
||||
|
||||
// make a quick search map
|
||||
std::map<RsGxsMessageId,uint32_t> search_map ;
|
||||
for (uint32_t i=0;i<posts.size();++i)
|
||||
search_map[posts[i].mMeta.mMsgId] = i ;
|
||||
|
||||
for(uint32_t i=0;i<new_versions.size();++i)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " Taking care of new version at index " << new_versions[i] << std::endl;
|
||||
#endif
|
||||
|
||||
uint32_t current_index = new_versions[i] ;
|
||||
uint32_t source_index = new_versions[i] ;
|
||||
|
||||
// What we do is everytime we find a replacement post, we climb up the replacement graph until we find the original post
|
||||
// (or the most recent version of it). When we reach this post, we replace it with the data of the source post.
|
||||
// In the mean time, all other posts have their MsgId cleared, so that the posts are removed from the list.
|
||||
|
||||
//std::vector<uint32_t> versions ;
|
||||
std::map<RsGxsMessageId,uint32_t>::const_iterator vit ;
|
||||
|
||||
while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId)))
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ;
|
||||
#endif
|
||||
|
||||
// Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple
|
||||
// times. In this case, we only need to replace the post with the newest version
|
||||
|
||||
//uint32_t prev_index = current_index ;
|
||||
current_index = vit->second ;
|
||||
|
||||
if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further.
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " already erased. Stopping." << std::endl;
|
||||
#endif
|
||||
break ;
|
||||
}
|
||||
|
||||
if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " and is more recent => following" << std::endl;
|
||||
#endif
|
||||
for(std::set<RsGxsMessageId>::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt)
|
||||
posts[source_index].mOlderVersions.insert(*itt);
|
||||
|
||||
posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId);
|
||||
posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored
|
||||
}
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
else
|
||||
std::cerr << " but is older -> Stopping" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Now adding " << posts.size() << " posts into array structure..." << std::endl;
|
||||
#endif
|
||||
|
||||
mPosts.clear();
|
||||
|
||||
for (std::vector<RsGxsChannelPost>::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it)
|
||||
{
|
||||
if(!(*it).mMeta.mMsgId.isNull())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " adding post \"" << (*it).mMeta.mMsgName << "\"" << std::endl;
|
||||
#endif
|
||||
mPosts.push_back(*it);
|
||||
}
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
else
|
||||
std::cerr << " skipped older version post \"" << (*it).mMeta.mMsgName << "\"" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& posts)
|
||||
{
|
||||
// The hierarchy of posts may contain edited posts. In the new model (03/2023), mOrigMsgId points to the original
|
||||
// top-level post in the hierarchy of edited posts. However, in the old model, mOrigMsgId points to the edited post.
|
||||
// Therefore the algorithm below is made to cope with both models at once.
|
||||
//
|
||||
// In the future, using the new model, it will be possible to delete old versions from the db, and detect new versions
|
||||
// because they all share the same mOrigMsgId.
|
||||
//
|
||||
// We proceed as follows:
|
||||
//
|
||||
// 1 - create a search map to convert post IDs into their index in the posts tab
|
||||
// 2 - recursively climb up the post mOrigMsgId until no parent is found. At top level, create the original post, and add all previous elements as newer versions.
|
||||
// 3 - go through the list of original posts, select among them the most recent version, and set all others as older versions.
|
||||
//
|
||||
// The algorithm handles the case where some parent has been deleted.
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Inserting channel posts" << std::endl;
|
||||
#endif
|
||||
|
||||
// 1 - create a search map to convert post IDs into their index in the posts tab
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " Given list: " << std::endl;
|
||||
#endif
|
||||
std::map<RsGxsMessageId,uint32_t> search_map ;
|
||||
|
||||
for (uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " " << i << ": " << posts[i].mMeta.mMsgId << " orig=" << posts[i].mMeta.mOrigMsgId << " publish TS =" << posts[i].mMeta.mPublishTs << std::endl;
|
||||
#endif
|
||||
search_map[posts[i].mMeta.mMsgId] = i ;
|
||||
}
|
||||
|
||||
// 2 - recursively climb up the post mOrigMsgId until no parent is found. At top level, create the original post, and add all previous elements as newer versions.
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " Searching for top-level posts..." << std::endl;
|
||||
#endif
|
||||
std::map<RsGxsMessageId,std::pair<uint32_t,std::set<RsGxsMessageId> > > original_versions ;
|
||||
|
||||
for (uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " Post " << i;
|
||||
#endif
|
||||
|
||||
// We use a recursive function here, so as to collect versions when climbing up to the top level post, and
|
||||
// set the top level as the orig for all visited posts on the way back.
|
||||
|
||||
std::function<RsGxsMessageId (uint32_t,std::set<RsGxsMessageId>& versions,rstime_t newest_time,uint32_t newest_index,int depth)> recurs_find_top_level
|
||||
= [&posts,&search_map,&recurs_find_top_level,&original_versions](uint32_t index,
|
||||
std::set<RsGxsMessageId>& collected_versions,
|
||||
rstime_t newest_time,
|
||||
uint32_t newest_index,
|
||||
int depth)
|
||||
-> RsGxsMessageId
|
||||
{
|
||||
const auto& m(posts[index].mMeta);
|
||||
|
||||
if(m.mPublishTs > newest_time)
|
||||
{
|
||||
newest_index = index;
|
||||
newest_time = m.mPublishTs;
|
||||
}
|
||||
collected_versions.insert(m.mMsgId);
|
||||
|
||||
RsGxsMessageId top_level_id;
|
||||
std::map<RsGxsMessageId,uint32_t>::const_iterator it;
|
||||
|
||||
if(m.mOrigMsgId.isNull() || m.mOrigMsgId==m.mMsgId) // we have a top-level post.
|
||||
top_level_id = m.mMsgId;
|
||||
else if( (it = search_map.find(m.mOrigMsgId)) == search_map.end()) // we don't have the post. Never mind, we store the
|
||||
{
|
||||
top_level_id = m.mOrigMsgId;
|
||||
collected_versions.insert(m.mOrigMsgId); // this one will never be added to the set by the previous call
|
||||
}
|
||||
else
|
||||
{
|
||||
top_level_id = recurs_find_top_level(it->second,collected_versions,newest_time,newest_index,depth+1);
|
||||
posts[index].mMeta.mOrigMsgId = top_level_id; // this fastens calculation because it will skip already seen posts.
|
||||
|
||||
return top_level_id;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << std::string(2*depth,' ') << " top level = " << top_level_id ;
|
||||
#endif
|
||||
auto vit = original_versions.find(top_level_id);
|
||||
|
||||
if(vit != original_versions.end())
|
||||
{
|
||||
if(posts[vit->second.first].mMeta.mPublishTs < newest_time)
|
||||
vit->second.first = newest_index;
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " already existing. " << std::endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
original_versions[top_level_id].first = newest_index;
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " new. " << std::endl;
|
||||
#endif
|
||||
}
|
||||
original_versions[top_level_id].second.insert(collected_versions.begin(),collected_versions.end());
|
||||
|
||||
return top_level_id;
|
||||
};
|
||||
|
||||
auto versions_set = std::set<RsGxsMessageId>();
|
||||
recurs_find_top_level(i,versions_set,posts[i].mMeta.mPublishTs,i,0);
|
||||
}
|
||||
|
||||
mPosts.clear();
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " Total top_level posts: " << original_versions.size() << std::endl;
|
||||
|
||||
for(auto it:original_versions)
|
||||
{
|
||||
std::cerr << " Post " << it.first << ". Total versions = " << it.second.second.size() << " latest: " << posts[it.second.first].mMeta.mMsgId << std::endl;
|
||||
|
||||
for(auto m:it.second.second)
|
||||
if(m != it.first)
|
||||
std::cerr << " other (newer version): " << m << std::endl;
|
||||
}
|
||||
#endif
|
||||
// make sure the posts are delivered in the same order they appears in the posts[] tab.
|
||||
|
||||
std::vector<uint32_t> ids;
|
||||
|
||||
for(auto id:original_versions)
|
||||
ids.push_back(id.second.first);
|
||||
|
||||
std::sort(ids.begin(),ids.end());
|
||||
|
||||
for(uint32_t i=0;i<ids.size();++i)
|
||||
{
|
||||
mPosts.push_back(posts[ids[i]]);
|
||||
mPosts.back().mOlderVersions = original_versions[posts[ids[i]].mMeta.mMsgId].second;
|
||||
}
|
||||
|
||||
}
|
||||
void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status)
|
||||
{
|
||||
// No need to call preMods()/postMods() here because we're not changing the model
|
||||
|
@ -821,19 +821,6 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr<con
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to call this in order to get the actual comment count. The previous call only retrieves the message, since we supplied the message ID.
|
||||
// another way to go would be to save the comment ids of the existing message and re-insert them before calling getChannelContent.
|
||||
|
||||
if(!rsGxsChannels->getChannelComments(grp_id,{ msg_id },comments))
|
||||
{
|
||||
RsErr() << " failed to retrieve message comment data for channel/msg " << grp_id << "/" << msg_id ;
|
||||
return;
|
||||
}
|
||||
|
||||
// Normally, there's a single post in the "post" array. The function below takes a full array of posts however.
|
||||
|
||||
RsGxsChannelPostsModel::computeCommentCounts(posts,comments);
|
||||
|
||||
// 2 - update the model in the UI thread.
|
||||
|
||||
RsQThreadUtils::postToObject( [post=posts[0],this]()
|
||||
|
@ -335,7 +335,7 @@
|
||||
<string><html><head/><body><p>Includes all posts, comments and votes. This number is progressively updated when new friend connect. The local vs. at friends difference may indicate that you would get older posts by increasing the synchronization period.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Posts (locally / at friends):</string>
|
||||
<string>Items (locally / at friends):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -402,7 +402,7 @@
|
||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
@ -657,6 +657,12 @@ p, li { white-space: pre-wrap; }
|
||||
<extends>QLineEdit</extends>
|
||||
<header location="global">gui/common/LineEditClear.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RSTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>gui/common/RSTreeView.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GxsIdLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
@ -673,12 +679,6 @@ p, li { white-space: pre-wrap; }
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/common/SubscribeToolButton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RSTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>gui/common/RSTreeView.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GxsCommentDialog</class>
|
||||
<extends>QWidget</extends>
|
||||
|
@ -800,7 +800,6 @@ void RsGxsForumModel::update_posts(const RsGxsGroupId& group_id)
|
||||
// 1 - get message data from p3GxsForums
|
||||
|
||||
std::list<RsGxsGroupId> forumIds;
|
||||
std::vector<RsMsgMetaData> msg_metas;
|
||||
std::vector<RsGxsForumGroup> groups;
|
||||
|
||||
forumIds.push_back(group_id);
|
||||
@ -811,21 +810,19 @@ void RsGxsForumModel::update_posts(const RsGxsGroupId& group_id)
|
||||
return;
|
||||
}
|
||||
|
||||
if(!rsGxsForums->getForumMsgMetaData(group_id,msg_metas))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve forum message info for forum " << group_id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 - sort the messages into a proper hierarchy
|
||||
// 2 - sort messages into a proper hierarchy
|
||||
|
||||
auto post_versions = new std::map<RsGxsMessageId,std::vector<std::pair<time_t, RsGxsMessageId> > >() ;
|
||||
std::vector<ForumModelPostEntry> *vect = new std::vector<ForumModelPostEntry>();
|
||||
std::vector<ForumPostEntry> *vect = new std::vector<ForumPostEntry>();
|
||||
RsGxsForumGroup group = groups[0];
|
||||
|
||||
computeMessagesHierarchy(group,msg_metas,*vect,*post_versions);
|
||||
if(!rsGxsForums->getForumPostsHierarchy(group,*vect,*post_versions))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve forum hierarchy of message info for forum " << group_id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 3 - update the model in the UI thread.
|
||||
// 3 - update the model in the UI thread.
|
||||
|
||||
RsQThreadUtils::postToObject( [group,vect,post_versions,this]()
|
||||
{
|
||||
@ -835,10 +832,14 @@ void RsGxsForumModel::update_posts(const RsGxsGroupId& group_id)
|
||||
* Qt::QueuedConnection is important!
|
||||
*/
|
||||
|
||||
setPosts(group,*vect,*post_versions) ;
|
||||
std::vector<ForumModelPostEntry> psts;
|
||||
for(const auto& p:*vect)
|
||||
psts.push_back( ForumModelPostEntry(p));
|
||||
|
||||
delete vect;
|
||||
delete post_versions;
|
||||
setPosts(group,psts,*post_versions) ;
|
||||
|
||||
delete vect;
|
||||
delete post_versions;
|
||||
|
||||
|
||||
}, this );
|
||||
@ -846,379 +847,7 @@ void RsGxsForumModel::update_posts(const RsGxsGroupId& group_id)
|
||||
});
|
||||
}
|
||||
|
||||
ForumModelIndex RsGxsForumModel::addEntry(std::vector<ForumModelPostEntry>& posts,const ForumModelPostEntry& entry,ForumModelIndex parent)
|
||||
{
|
||||
uint32_t N = posts.size();
|
||||
posts.push_back(entry);
|
||||
|
||||
posts[N].mParent = parent;
|
||||
posts[parent].mChildren.push_back(N);
|
||||
#ifdef DEBUG_FORUMMODEL
|
||||
std::cerr << "Added new entry " << N << " children of " << parent << std::endl;
|
||||
#endif
|
||||
if(N == parent)
|
||||
std::cerr << "(EE) trying to add a post as its own parent!" << std::endl;
|
||||
return ForumModelIndex(N);
|
||||
}
|
||||
|
||||
void RsGxsForumModel::generateMissingItem(const RsGxsMessageId &msgId,ForumModelPostEntry& entry)
|
||||
{
|
||||
entry.mPostFlags = ForumModelPostEntry::FLAG_POST_IS_MISSING ;
|
||||
entry.mTitle = std::string(tr("[ ... Missing Message ... ]").toUtf8());
|
||||
entry.mMsgId = msgId;
|
||||
entry.mAuthorId.clear();
|
||||
entry.mPublishTs=0;
|
||||
entry.mReputationWarningLevel = 3;
|
||||
}
|
||||
|
||||
void RsGxsForumModel::convertMsgToPostEntry(const RsGxsForumGroup& mForumGroup,const RsMsgMetaData& msg, bool /*useChildTS*/, ForumModelPostEntry& fentry)
|
||||
{
|
||||
fentry.mTitle = msg.mMsgName;
|
||||
fentry.mAuthorId = msg.mAuthorId;
|
||||
fentry.mMsgId = msg.mMsgId;
|
||||
fentry.mPublishTs = msg.mPublishTs;
|
||||
fentry.mPostFlags = 0;
|
||||
fentry.mMsgStatus = msg.mMsgStatus;
|
||||
|
||||
if(mForumGroup.mPinnedPosts.ids.find(msg.mMsgId) != mForumGroup.mPinnedPosts.ids.end())
|
||||
fentry.mPostFlags |= ForumModelPostEntry::FLAG_POST_IS_PINNED;
|
||||
|
||||
// Early check for a message that should be hidden because its author
|
||||
// is flagged with a bad reputation
|
||||
|
||||
computeReputationLevel(mForumGroup.mMeta.mSignFlags,fentry);
|
||||
}
|
||||
|
||||
void RsGxsForumModel::computeReputationLevel(uint32_t forum_sign_flags,ForumModelPostEntry& fentry)
|
||||
{
|
||||
uint32_t idflags =0;
|
||||
RsReputationLevel reputation_level =
|
||||
rsReputations->overallReputationLevel(fentry.mAuthorId, &idflags);
|
||||
|
||||
if(reputation_level == RsReputationLevel::LOCALLY_NEGATIVE)
|
||||
fentry.mPostFlags |= ForumModelPostEntry::FLAG_POST_IS_REDACTED;
|
||||
else
|
||||
fentry.mPostFlags &= ~ForumModelPostEntry::FLAG_POST_IS_REDACTED;
|
||||
|
||||
// We use a specific item model for forums in order to handle the post pinning.
|
||||
|
||||
if(reputation_level == RsReputationLevel::UNKNOWN)
|
||||
fentry.mReputationWarningLevel = 3 ;
|
||||
else if(reputation_level == RsReputationLevel::LOCALLY_NEGATIVE)
|
||||
fentry.mReputationWarningLevel = 2 ;
|
||||
else if(reputation_level < rsGxsForums->minReputationForForwardingMessages(forum_sign_flags,idflags))
|
||||
fentry.mReputationWarningLevel = 1 ;
|
||||
else
|
||||
fentry.mReputationWarningLevel = 0 ;
|
||||
}
|
||||
|
||||
static bool decreasing_time_comp(const std::pair<time_t,RsGxsMessageId>& e1,const std::pair<time_t,RsGxsMessageId>& e2) { return e2.first < e1.first ; }
|
||||
|
||||
void RsGxsForumModel::computeMessagesHierarchy(const RsGxsForumGroup& forum_group,
|
||||
const std::vector<RsMsgMetaData>& msgs_metas_array,
|
||||
std::vector<ForumModelPostEntry>& posts,
|
||||
std::map<RsGxsMessageId,std::vector<std::pair<time_t,RsGxsMessageId> > >& mPostVersions
|
||||
)
|
||||
{
|
||||
std::cerr << "updating messages data with " << msgs_metas_array.size() << " messages" << std::endl;
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Retrieved group data: " << std::endl;
|
||||
std::cerr << " Group ID: " << forum_group.mMeta.mGroupId << std::endl;
|
||||
std::cerr << " Admin lst: " << forum_group.mAdminList.ids.size() << " elements." << std::endl;
|
||||
for(auto it(forum_group.mAdminList.ids.begin());it!=forum_group.mAdminList.ids.end();++it)
|
||||
std::cerr << " " << *it << std::endl;
|
||||
std::cerr << " Pinned Post: " << forum_group.mPinnedPosts.ids.size() << " messages." << std::endl;
|
||||
for(auto it(forum_group.mPinnedPosts.ids.begin());it!=forum_group.mPinnedPosts.ids.end();++it)
|
||||
std::cerr << " " << *it << std::endl;
|
||||
#endif
|
||||
|
||||
/* get messages */
|
||||
std::map<RsGxsMessageId,RsMsgMetaData> msgs;
|
||||
|
||||
for(uint32_t i=0;i<msgs_metas_array.size();++i)
|
||||
{
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Adding message " << msgs_metas_array[i].mMeta.mMsgId << " with parent " << msgs_metas_array[i].mMeta.mParentId << " to message map" << std::endl;
|
||||
#endif
|
||||
msgs[msgs_metas_array[i].mMsgId] = msgs_metas_array[i] ;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
size_t count = msgs.size();
|
||||
#endif
|
||||
// int pos = 0;
|
||||
// int steps = count / PROGRESSBAR_MAX;
|
||||
// int step = 0;
|
||||
|
||||
initEmptyHierarchy(posts);
|
||||
|
||||
// ThreadList contains the list of parent threads. The algorithm below iterates through all messages
|
||||
// and tries to establish parenthood relationships between them, given that we only know the
|
||||
// immediate parent of a message and now its children. Some messages have a missing parent and for them
|
||||
// a fake top level parent is generated.
|
||||
|
||||
// In order to be efficient, we first create a structure that lists the children of every mesage ID in the list.
|
||||
// Then the hierarchy of message is build by attaching the kids to every message until all of them have been processed.
|
||||
// The messages with missing parents will be the last ones remaining in the list.
|
||||
|
||||
std::list<std::pair< RsGxsMessageId, ForumModelIndex > > threadStack;
|
||||
std::map<RsGxsMessageId,std::list<RsGxsMessageId> > kids_array ;
|
||||
std::set<RsGxsMessageId> missing_parents;
|
||||
|
||||
// First of all, remove all older versions of posts. This is done by first adding all posts into a hierarchy structure
|
||||
// and then removing all posts which have a new versions available. The older versions are kept appart.
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() Collecting post versions" << std::endl;
|
||||
#endif
|
||||
mPostVersions.clear();
|
||||
|
||||
for ( auto msgIt = msgs.begin(); msgIt != msgs.end();++msgIt)
|
||||
{
|
||||
if(!msgIt->second.mOrigMsgId.isNull() && msgIt->second.mOrigMsgId != msgIt->second.mMsgId)
|
||||
{
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << " Post " << msgIt->second.mMeta.mMsgId << " is a new version of " << msgIt->second.mMeta.mOrigMsgId << std::endl;
|
||||
#endif
|
||||
auto msgIt2 = msgs.find(msgIt->second.mOrigMsgId);
|
||||
|
||||
// Ensuring that the post exists allows to only collect the existing data.
|
||||
|
||||
if(msgIt2 == msgs.end())
|
||||
continue ;
|
||||
|
||||
// Make sure that the author is the same than the original message, or is a moderator. This should always happen when messages are constructed using
|
||||
// the UI but nothing can prevent a nasty user to craft a new version of a message with his own signature.
|
||||
|
||||
if(msgIt2->second.mAuthorId != msgIt->second.mAuthorId)
|
||||
{
|
||||
if( !IS_FORUM_MSG_MODERATION(msgIt->second.mMsgFlags) ) // if authors are different the moderation flag needs to be set on the editing msg
|
||||
continue ;
|
||||
|
||||
if( !forum_group.canEditPosts(msgIt->second.mAuthorId)) // if author is not a moderator, continue
|
||||
continue ;
|
||||
}
|
||||
|
||||
// always add the post a self version
|
||||
|
||||
if(mPostVersions[msgIt->second.mOrigMsgId].empty())
|
||||
mPostVersions[msgIt->second.mOrigMsgId].push_back(std::make_pair(msgIt2->second.mPublishTs,msgIt2->second.mMsgId)) ;
|
||||
|
||||
mPostVersions[msgIt->second.mOrigMsgId].push_back(std::make_pair(msgIt->second.mPublishTs,msgIt->second.mMsgId)) ;
|
||||
}
|
||||
}
|
||||
|
||||
// The following code assembles all new versions of a given post into the same array, indexed by the oldest version of the post.
|
||||
|
||||
for(auto it(mPostVersions.begin());it!=mPostVersions.end();++it)
|
||||
{
|
||||
auto& v(it->second) ;
|
||||
|
||||
for(size_t i=0;i<v.size();++i)
|
||||
{
|
||||
if(v[i].second != it->first)
|
||||
{
|
||||
RsGxsMessageId sub_msg_id = v[i].second ;
|
||||
|
||||
auto it2 = mPostVersions.find(sub_msg_id);
|
||||
|
||||
if(it2 != mPostVersions.end())
|
||||
{
|
||||
for(size_t j=0;j<it2->second.size();++j)
|
||||
if(it2->second[j].second != sub_msg_id) // dont copy it, since it is already present at slot i
|
||||
v.push_back(it2->second[j]) ;
|
||||
|
||||
mPostVersions.erase(it2) ; // it2 is never equal to it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now remove from msg ids, all posts except the most recent one. And make the mPostVersion be indexed by the most recent version of the post,
|
||||
// which corresponds to the item in the tree widget.
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Final post versions: " << std::endl;
|
||||
#endif
|
||||
std::map<RsGxsMessageId,std::vector<std::pair<time_t,RsGxsMessageId> > > mTmp;
|
||||
std::map<RsGxsMessageId,RsGxsMessageId> most_recent_versions ;
|
||||
|
||||
for(auto it(mPostVersions.begin());it!=mPostVersions.end();++it)
|
||||
{
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Original post: " << it.key() << std::endl;
|
||||
#endif
|
||||
// Finally, sort the posts from newer to older
|
||||
|
||||
std::sort(it->second.begin(),it->second.end(),decreasing_time_comp) ;
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << " most recent version " << (*it)[0].first << " " << (*it)[0].second << std::endl;
|
||||
#endif
|
||||
for(size_t i=1;i<it->second.size();++i)
|
||||
{
|
||||
msgs.erase(it->second[i].second) ;
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << " older version " << (*it)[i].first << " " << (*it)[i].second << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
mTmp[it->second[0].second] = it->second ; // index the versions map by the ID of the most recent post.
|
||||
|
||||
// Now make sure that message parents are consistent. Indeed, an old post may have the old version of a post as parent. So we need to change that parent
|
||||
// to the newest version. So we create a map of which is the most recent version of each message, so that parent messages can be searched in it.
|
||||
|
||||
for(size_t i=1;i<it->second.size();++i)
|
||||
most_recent_versions[it->second[i].second] = it->second[0].second ;
|
||||
}
|
||||
mPostVersions = mTmp ;
|
||||
|
||||
// The next step is to find the top level thread messages. These are defined as the messages without
|
||||
// any parent message ID.
|
||||
|
||||
// this trick is needed because while we remove messages, the parents a given msg may already have been removed
|
||||
// and wrongly understand as a missing parent.
|
||||
|
||||
std::map<RsGxsMessageId,RsMsgMetaData> kept_msgs;
|
||||
|
||||
for ( auto msgIt = msgs.begin(); msgIt != msgs.end();++msgIt)
|
||||
{
|
||||
|
||||
if(msgIt->second.mParentId.isNull())
|
||||
{
|
||||
|
||||
/* add all threads */
|
||||
const RsMsgMetaData& msg = msgIt->second;
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() Adding TopLevel Thread: mId: " << msg.mMsgId << std::endl;
|
||||
#endif
|
||||
|
||||
ForumModelPostEntry entry;
|
||||
convertMsgToPostEntry(forum_group,msg, mUseChildTS, entry);
|
||||
|
||||
ForumModelIndex entry_index = addEntry(posts,entry,0);
|
||||
|
||||
//if (!mFlatView)
|
||||
threadStack.push_back(std::make_pair(msg.mMsgId,entry_index)) ;
|
||||
|
||||
//calculateExpand(msg, item);
|
||||
//mItems.append(entry_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() Storing kid " << msgIt->first << " of message " << msgIt->second.mParentId << std::endl;
|
||||
#endif
|
||||
// The same missing parent may appear multiple times, so we first store them into a unique container.
|
||||
|
||||
RsGxsMessageId parent_msg = msgIt->second.mParentId;
|
||||
|
||||
if(msgs.find(parent_msg) == msgs.end())
|
||||
{
|
||||
// also check that the message is not versionned
|
||||
|
||||
std::map<RsGxsMessageId,RsGxsMessageId>::const_iterator mrit = most_recent_versions.find(parent_msg) ;
|
||||
|
||||
if(mrit != most_recent_versions.end())
|
||||
parent_msg = mrit->second ;
|
||||
else
|
||||
missing_parents.insert(parent_msg);
|
||||
}
|
||||
|
||||
kids_array[parent_msg].push_back(msgIt->first) ;
|
||||
kept_msgs.insert(*msgIt) ;
|
||||
}
|
||||
}
|
||||
|
||||
msgs = kept_msgs;
|
||||
|
||||
// Also create a list of posts by time, when they are new versions of existing posts. Only the last one will have an item created.
|
||||
|
||||
// Add a fake toplevel item for the parent IDs that we dont actually have.
|
||||
|
||||
for(std::set<RsGxsMessageId>::const_iterator it(missing_parents.begin());it!=missing_parents.end();++it)
|
||||
{
|
||||
// add dummy parent item
|
||||
ForumModelPostEntry e ;
|
||||
generateMissingItem(*it,e);
|
||||
|
||||
ForumModelIndex e_index = addEntry(posts,e,0); // no parent -> parent is level 0
|
||||
//mItems.append( e_index );
|
||||
|
||||
threadStack.push_back(std::make_pair(*it,e_index)) ;
|
||||
}
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() Processing stack:" << std::endl;
|
||||
#endif
|
||||
// Now use a stack to go down the hierarchy
|
||||
|
||||
while (!threadStack.empty())
|
||||
{
|
||||
std::pair<RsGxsMessageId, uint32_t> threadPair = threadStack.front();
|
||||
threadStack.pop_front();
|
||||
|
||||
std::map<RsGxsMessageId, std::list<RsGxsMessageId> >::iterator it = kids_array.find(threadPair.first) ;
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() Node: " << threadPair.first << std::endl;
|
||||
#endif
|
||||
if(it == kids_array.end())
|
||||
continue ;
|
||||
|
||||
|
||||
for(std::list<RsGxsMessageId>::const_iterator it2(it->second.begin());it2!=it->second.end();++it2)
|
||||
{
|
||||
// We iterate through the top level thread items, and look for which message has the current item as parent.
|
||||
// When found, the item is put in the thread list itself, as a potential new parent.
|
||||
|
||||
auto mit = msgs.find(*it2) ;
|
||||
|
||||
if(mit == msgs.end())
|
||||
{
|
||||
std::cerr << "GxsForumsFillThread::run() Cannot find submessage " << *it2 << " !!!" << std::endl;
|
||||
continue ;
|
||||
}
|
||||
|
||||
const RsMsgMetaData& msg(mit->second) ;
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() adding sub_item " << msg.mMsgId << std::endl;
|
||||
#endif
|
||||
|
||||
|
||||
ForumModelPostEntry e ;
|
||||
convertMsgToPostEntry(forum_group,msg,mUseChildTS,e) ;
|
||||
ForumModelIndex e_index = addEntry(posts,e, threadPair.second);
|
||||
|
||||
//calculateExpand(msg, item);
|
||||
|
||||
/* add item to process list */
|
||||
threadStack.push_back(std::make_pair(msg.mMsgId, e_index));
|
||||
|
||||
msgs.erase(mit);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "GxsForumsFillThread::run() Erasing entry " << it->first << " from kids tab." << std::endl;
|
||||
#endif
|
||||
kids_array.erase(it) ; // This is not strictly needed, but it improves performance by reducing the search space.
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Kids array now has " << kids_array.size() << " elements" << std::endl;
|
||||
for(std::map<RsGxsMessageId,std::list<RsGxsMessageId> >::const_iterator it(kids_array.begin());it!=kids_array.end();++it)
|
||||
{
|
||||
std::cerr << "Node " << it->first << std::endl;
|
||||
for(std::list<RsGxsMessageId>::const_iterator it2(it->second.begin());it2!=it->second.end();++it2)
|
||||
std::cerr << " " << *it2 << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "GxsForumsFillThread::run() stopped: " << (wasStopped() ? "yes" : "no") << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RsGxsForumModel::setMsgReadStatus(const QModelIndex& i,bool read_status,bool with_children)
|
||||
{
|
||||
@ -1248,8 +877,8 @@ void RsGxsForumModel::setMsgReadStatus(const QModelIndex& i,bool read_status,boo
|
||||
|
||||
void RsGxsForumModel::recursSetMsgReadStatus(ForumModelIndex i,bool read_status,bool with_children)
|
||||
{
|
||||
int newStatus = (read_status ? mPosts[i].mMsgStatus & ~static_cast<int>(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD)
|
||||
: mPosts[i].mMsgStatus | static_cast<int>(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD));
|
||||
uint32_t newStatus = (read_status ? mPosts[i].mMsgStatus & ~static_cast<int>(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD)
|
||||
: mPosts[i].mMsgStatus | static_cast<int>(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD));
|
||||
bool bChanged = (mPosts[i].mMsgStatus != newStatus);
|
||||
mPosts[i].mMsgStatus = newStatus;
|
||||
//Remove Unprocessed and New flags
|
||||
@ -1420,7 +1049,7 @@ void RsGxsForumModel::setAuthorOpinion(const QModelIndex& indx, RsOpinion op)
|
||||
for(uint32_t i=0;i<mPosts.size();++i)
|
||||
if(mPosts[i].mAuthorId == author_id)
|
||||
{
|
||||
computeReputationLevel(mForumGroup.mMeta.mSignFlags,mPosts[i]);
|
||||
rsGxsForums->updateReputationLevel(mForumGroup.mMeta.mSignFlags,mPosts[i]);
|
||||
|
||||
// notify the widgets that the data has changed.
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(0,COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL));
|
||||
|
@ -23,44 +23,19 @@
|
||||
#include <QModelIndex>
|
||||
#include <QColor>
|
||||
|
||||
// This class holds the actual hierarchy of posts, represented by identifiers
|
||||
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
|
||||
// safely access the data.
|
||||
|
||||
// The model contains a post in place 0 that is the parent of all posts.
|
||||
|
||||
typedef uint32_t ForumModelIndex;
|
||||
|
||||
struct ForumModelPostEntry
|
||||
struct ForumModelPostEntry: public ForumPostEntry
|
||||
{
|
||||
ForumModelPostEntry() : mPublishTs(0),mMostRecentTsInThread(0),mPostFlags(0),mReputationWarningLevel(0),mMsgStatus(0),prow(0) {}
|
||||
ForumModelPostEntry() : mMostRecentTsInThread(0),prow(0) {}
|
||||
ForumModelPostEntry(const ForumPostEntry& p) : mMostRecentTsInThread(0),prow(0) { *static_cast<ForumPostEntry*>(this) = p; }
|
||||
|
||||
enum { // flags for display of posts. To be used in mPostFlags
|
||||
FLAG_POST_IS_PINNED = 0x0001,
|
||||
FLAG_POST_IS_MISSING = 0x0002,
|
||||
FLAG_POST_IS_REDACTED = 0x0004,
|
||||
FLAG_POST_HAS_UNREAD_CHILDREN = 0x0008,
|
||||
FLAG_POST_HAS_READ_CHILDREN = 0x0010,
|
||||
FLAG_POST_PASSES_FILTER = 0x0020,
|
||||
FLAG_POST_CHILDREN_PASSES_FILTER = 0x0040,
|
||||
};
|
||||
|
||||
std::string mTitle ;
|
||||
RsGxsId mAuthorId ;
|
||||
RsGxsMessageId mMsgId;
|
||||
uint32_t mPublishTs;
|
||||
uint32_t mMostRecentTsInThread;
|
||||
uint32_t mPostFlags;
|
||||
int mReputationWarningLevel;
|
||||
int mMsgStatus;
|
||||
|
||||
std::vector<ForumModelIndex> mChildren;
|
||||
ForumModelIndex mParent;
|
||||
int prow ; // parent row
|
||||
uint32_t mMostRecentTsInThread;
|
||||
int prow ; // parent row. Used by Qt abstract item models.
|
||||
};
|
||||
|
||||
// This class is the item model used by Qt to display the information
|
||||
|
||||
typedef uint32_t ForumModelIndex;
|
||||
|
||||
class RsGxsForumModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -104,11 +104,23 @@ bool GxsForumsDialog::getGroupData(std::list<RsGxsGenericGroupData*>& groupInfo)
|
||||
|
||||
bool GxsForumsDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat)
|
||||
{
|
||||
return rsGxsForums->getForumStatistics(groupId,stat);
|
||||
RsGxsForumStatistics s;
|
||||
|
||||
if(!rsGxsForums->getForumStatistics(groupId,s))
|
||||
return false;
|
||||
|
||||
stat.mGrpId = groupId;
|
||||
stat.mNumMsgs = s.mNumberOfMessages;
|
||||
|
||||
stat.mTotalSizeOfMsgs = 0; // hopefuly unused. Required the loading of the full channel data, so not very convenient.
|
||||
stat.mNumThreadMsgsNew = s.mNumberOfNewMessages;
|
||||
stat.mNumThreadMsgsUnread = s.mNumberOfUnreadMessages;
|
||||
stat.mNumChildMsgsNew = 0;
|
||||
stat.mNumChildMsgsUnread = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString GxsForumsDialog::getHelpString() const
|
||||
{
|
||||
int H = misc::getFontSizeFactor("HelpButton").height();
|
||||
|
Loading…
Reference in New Issue
Block a user