mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-21 15:30:31 -04:00
fixed merge
This commit is contained in:
commit
c81bdd9932
27 changed files with 1027 additions and 641 deletions
|
@ -35,10 +35,10 @@
|
|||
#include "GxsChannelPostsModel.h"
|
||||
#include "GxsChannelPostFilesModel.h"
|
||||
|
||||
#define DEBUG_CHANNEL_MODEL
|
||||
//#define DEBUG_CHANNEL_MODEL_DATA
|
||||
//#define DEBUG_CHANNEL_MODEL
|
||||
|
||||
Q_DECLARE_METATYPE(RsMsgMetaData)
|
||||
|
||||
Q_DECLARE_METATYPE(RsGxsChannelPost)
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
|
||||
|
@ -47,14 +47,6 @@ RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent)
|
|||
: QAbstractItemModel(parent), mTreeMode(RsGxsChannelPostsModel::TREE_MODE_GRID), mColumns(6)
|
||||
{
|
||||
initEmptyHierarchy();
|
||||
|
||||
mEventHandlerId = 0;
|
||||
// Needs to be asynced because this function is called by another thread!
|
||||
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
|
||||
}, mEventHandlerId, RsEventType::GXS_CHANNELS );
|
||||
}
|
||||
|
||||
RsGxsChannelPostsModel::~RsGxsChannelPostsModel()
|
||||
|
@ -72,7 +64,7 @@ void RsGxsChannelPostsModel::setMode(TreeMode mode)
|
|||
triggerViewUpdate();
|
||||
}
|
||||
|
||||
void updateCommentCounts( std::vector<RsGxsChannelPost>& posts, std::vector<RsGxsComment>& comments)
|
||||
void RsGxsChannelPostsModel::computeCommentCounts( std::vector<RsGxsChannelPost>& posts, std::vector<RsGxsComment>& comments)
|
||||
{
|
||||
// Store posts IDs in a std::map to avoid a quadratic cost
|
||||
|
||||
|
@ -108,99 +100,6 @@ void updateCommentCounts( std::vector<RsGxsChannelPost>& posts, std::vector<RsGx
|
|||
}
|
||||
|
||||
|
||||
void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get());
|
||||
|
||||
if(!e)
|
||||
return;
|
||||
|
||||
switch(e->mChannelEventCode)
|
||||
{
|
||||
case RsChannelEventCode::UPDATED_MESSAGE:
|
||||
case RsChannelEventCode::READ_STATUS_CHANGED:
|
||||
{
|
||||
// Normally we should just emit dataChanged() on the index of the data that has changed:
|
||||
// We need to update the data!
|
||||
|
||||
// make a copy of e, so as to avoid destruction of the shared pointer during async thread execution, since [e] doesn't actually tell
|
||||
// the original shared_ptr that it is copied! So no counter is updated in event, which will be destroyed (as e will be) during or even before
|
||||
// the execution of the lambda.
|
||||
|
||||
RsGxsChannelEvent E(*e);
|
||||
|
||||
if(E.mChannelGroupId == mChannelGroup.mMeta.mGroupId)
|
||||
RsThread::async([this, E]()
|
||||
{
|
||||
// 1 - get message data from p3GxsChannels. No need for pointers here, because we send only a single post to postToObject()
|
||||
// At this point we dont know what kind of msg id we have. It can be a vote, a comment or an actual message.
|
||||
|
||||
std::vector<RsGxsChannelPost> posts;
|
||||
std::vector<RsGxsComment> comments;
|
||||
std::vector<RsGxsVote> votes;
|
||||
std::set<RsGxsMessageId> msg_ids{ E.mChannelMsgId };
|
||||
|
||||
if(!rsGxsChannels->getChannelContent(E.mChannelGroupId,msg_ids, posts,comments,votes))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if what we have actually is a comment or a vote. If so we need to update the actual message they refer to
|
||||
|
||||
if(posts.empty()) // means we have a comment or a vote
|
||||
{
|
||||
msg_ids.clear();
|
||||
|
||||
for(auto c:comments) msg_ids.insert(c.mMeta.mThreadId);
|
||||
for(auto v:votes ) msg_ids.insert(v.mMeta.mThreadId);
|
||||
|
||||
comments.clear();
|
||||
votes.clear();
|
||||
|
||||
if(!rsGxsChannels->getChannelContent(E.mChannelGroupId,msg_ids,posts,comments,votes))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to call this in order to get the actuall 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(E.mChannelGroupId,msg_ids,comments))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message comment data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
updateCommentCounts(posts,comments);
|
||||
|
||||
// 2 - update the model in the UI thread.
|
||||
|
||||
RsQThreadUtils::postToObject( [posts,this]()
|
||||
{
|
||||
for(uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
// linear search. Not good at all, but normally this is for a single post.
|
||||
|
||||
for(uint32_t j=0;j<mPosts.size();++j)
|
||||
if(mPosts[j].mMeta.mMsgId == posts[i].mMeta.mMsgId)
|
||||
{
|
||||
mPosts[j] = posts[i];
|
||||
|
||||
triggerViewUpdate();
|
||||
}
|
||||
}
|
||||
},this);
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::initEmptyHierarchy()
|
||||
{
|
||||
beginResetModel();
|
||||
|
@ -241,7 +140,31 @@ void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files)
|
|||
files.push_back(it.second);
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostsModel::postPassesFilter(const RsGxsChannelPost& post,const QStringList& strings,bool only_unread) const
|
||||
{
|
||||
bool passes_strings = true;
|
||||
|
||||
for(auto& s:strings)
|
||||
passes_strings = passes_strings && QString::fromStdString(post.mMeta.mMsgName).contains(s,Qt::CaseInsensitive);
|
||||
|
||||
if(strings.empty())
|
||||
passes_strings = true;
|
||||
|
||||
if(passes_strings && (!only_unread || (IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus))))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::setFilter(const QStringList& strings,bool only_unread, uint32_t& count)
|
||||
{
|
||||
mFilteredStrings = strings;
|
||||
mFilterUnread = only_unread;
|
||||
|
||||
updateFilter(count);
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::updateFilter(uint32_t& count)
|
||||
{
|
||||
preMods();
|
||||
|
||||
|
@ -252,18 +175,8 @@ void RsGxsChannelPostsModel::setFilter(const QStringList& strings,bool only_unre
|
|||
endResetModel();
|
||||
|
||||
for(size_t i=0;i<mPosts.size();++i)
|
||||
{
|
||||
bool passes_strings = true;
|
||||
|
||||
for(auto& s:strings)
|
||||
passes_strings = passes_strings && QString::fromStdString(mPosts[i].mMeta.mMsgName).contains(s,Qt::CaseInsensitive);
|
||||
|
||||
if(strings.empty())
|
||||
passes_strings = true;
|
||||
|
||||
if(passes_strings && (!only_unread || (IS_MSG_UNREAD(mPosts[i].mMeta.mMsgStatus) || IS_MSG_NEW(mPosts[i].mMeta.mMsgStatus))))
|
||||
mFilteredPosts.push_back(i);
|
||||
}
|
||||
if(postPassesFilter(mPosts[i],mFilteredStrings,mFilterUnread))
|
||||
mFilteredPosts.push_back(i);
|
||||
|
||||
count = mFilteredPosts.size();
|
||||
|
||||
|
@ -292,7 +205,7 @@ int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const
|
|||
return mFilteredPosts.size();
|
||||
}
|
||||
|
||||
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
|
||||
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the proper number of rows." ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -370,7 +283,7 @@ QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex
|
|||
|
||||
quintptr ref = getChildRef(parent.internalId(),(mTreeMode == TREE_MODE_GRID)?(column + row*mColumns):row);
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
#ifdef DEBUG_CHANNEL_MODEL_DATA
|
||||
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
|
||||
#endif
|
||||
return createIndex(row,column,ref) ;
|
||||
|
@ -460,7 +373,7 @@ int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const
|
|||
|
||||
QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
#ifdef DEBUG_CHANNEL_MODEL_DATA
|
||||
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
|
||||
#endif
|
||||
|
||||
|
@ -477,13 +390,13 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
|
|||
quintptr ref = (index.isValid())?index.internalId():0 ;
|
||||
uint32_t entry = 0;
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
#ifdef DEBUG_CHANNEL_MODEL_DATA
|
||||
std::cerr << "data(" << index << ")" ;
|
||||
#endif
|
||||
|
||||
if(!ref)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
#ifdef DEBUG_CHANNEL_MODEL_DATA
|
||||
std::cerr << " [empty]" << std::endl;
|
||||
#endif
|
||||
return QVariant() ;
|
||||
|
@ -491,7 +404,7 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
|
|||
|
||||
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
#ifdef DEBUG_CHANNEL_MODEL_DATA
|
||||
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
|
||||
#endif
|
||||
return QVariant() ;
|
||||
|
@ -564,6 +477,79 @@ bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2)
|
|||
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std::set<RsGxsFile>& added_files,std::set<RsGxsFile>& removed_files)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
RsDbg() << "updating single post for group id=" << currentGroupId() << " and msg id=" << post.mMeta.mMsgId ;
|
||||
#endif
|
||||
added_files.clear();
|
||||
removed_files.clear();
|
||||
|
||||
emit layoutAboutToBeChanged();
|
||||
|
||||
// linear search. Not good at all, but normally this is just for a single post.
|
||||
|
||||
bool found = false;
|
||||
const auto& new_post_meta(post.mMeta);
|
||||
|
||||
for(uint32_t j=0;j<mPosts.size();++j)
|
||||
if(new_post_meta.mMsgId == mPosts[j].mMeta.mMsgId) // same post updated
|
||||
{
|
||||
added_files.insert(post.mFiles.begin(),post.mFiles.end());
|
||||
removed_files.insert(mPosts[j].mFiles.begin(),mPosts[j].mFiles.end());
|
||||
|
||||
auto save_ucc = mPosts[j].mUnreadCommentCount;
|
||||
auto save_cc = mPosts[j].mCommentCount;
|
||||
|
||||
mPosts[j] = post;
|
||||
|
||||
mPosts[j].mUnreadCommentCount = save_ucc;
|
||||
mPosts[j].mCommentCount = save_cc;
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
RsDbg() << " post is an updated existing post." ;
|
||||
#endif
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
else if( (new_post_meta.mOrigMsgId == mPosts[j].mMeta.mOrigMsgId || new_post_meta.mOrigMsgId == mPosts[j].mMeta.mMsgId)
|
||||
&& mPosts[j].mMeta.mPublishTs < new_post_meta.mPublishTs) // new post version
|
||||
{
|
||||
added_files.insert(post.mFiles.begin(),post.mFiles.end());
|
||||
removed_files.insert(mPosts[j].mFiles.begin(),mPosts[j].mFiles.end());
|
||||
|
||||
auto old_post_id = mPosts[j].mMeta.mMsgId;
|
||||
auto save_ucc = mPosts[j].mUnreadCommentCount;
|
||||
auto save_cc = mPosts[j].mCommentCount;
|
||||
|
||||
mPosts[j] = post;
|
||||
|
||||
mPosts[j].mCommentCount += save_cc;
|
||||
mPosts[j].mUnreadCommentCount += save_ucc;
|
||||
mPosts[j].mOlderVersions.insert(old_post_id);
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
RsDbg() << " post is an new version of an existing post." ;
|
||||
#endif
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
RsDbg() << " post is an new post.";
|
||||
#endif
|
||||
added_files.insert(post.mFiles.begin(),post.mFiles.end());
|
||||
mPosts.push_back(post);
|
||||
}
|
||||
std::sort(mPosts.begin(),mPosts.end());
|
||||
|
||||
uint32_t count;
|
||||
updateFilter(count);
|
||||
|
||||
triggerViewUpdate();
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
|
||||
{
|
||||
preMods();
|
||||
|
@ -593,6 +579,8 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
|
|||
emit channelPostsLoaded();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
||||
{
|
||||
if(group_id.isNull())
|
||||
|
@ -635,7 +623,7 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
|||
// 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.
|
||||
|
||||
updateCommentCounts(*posts,*comments);
|
||||
computeCommentCounts(*posts,*comments);
|
||||
|
||||
// 2 - update the model in the UI thread.
|
||||
|
||||
|
@ -945,8 +933,28 @@ void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status)
|
|||
if(!rsGxsChannels->setMessageReadStatus(p,read_status))
|
||||
RsErr() << "setAllMsgReadStatus: failed to change status of msg " << p.first << " in group " << p.second << " to status " << read_status << std::endl;
|
||||
});
|
||||
|
||||
// 3 - update the local model data, since we don't catch the READ_STATUS_CHANGED event later, to avoid re-loading the msg.
|
||||
|
||||
for(uint32_t i=0;i<mPosts.size();++i)
|
||||
if(read_status)
|
||||
mPosts[i].mMeta.mMsgStatus &= ~(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD | GXS_SERV::GXS_MSG_STATUS_GUI_NEW);
|
||||
else
|
||||
mPosts[i].mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD ;
|
||||
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,mColumns-1,(void*)NULL));
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::updatePostWithNewComment(const RsGxsMessageId& msg_id)
|
||||
{
|
||||
for(uint32_t i=0;i<mPosts.size();++i)
|
||||
if(mPosts[i].mMeta.mMsgId == msg_id)
|
||||
{
|
||||
++mPosts[i].mUnreadCommentCount;
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,mColumns-1,(void*)NULL)); // update everything because we don't know the index.
|
||||
break;
|
||||
}
|
||||
}
|
||||
void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_status)
|
||||
{
|
||||
if(!i.isValid())
|
||||
|
@ -961,6 +969,18 @@ void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_sta
|
|||
return ;
|
||||
|
||||
rsGxsChannels->setMessageReadStatus(RsGxsGrpMsgIdPair(mPosts[mFilteredPosts[entry]].mMeta.mGroupId,mPosts[mFilteredPosts[entry]].mMeta.mMsgId),read_status);
|
||||
|
||||
// Quick update to the msg itself. Normally setMsgReadStatus will launch an event,
|
||||
// that we can catch to update the msg, but all the information is already here.
|
||||
|
||||
if(read_status)
|
||||
mPosts[mFilteredPosts[entry]].mMeta.mMsgStatus &= ~(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD | GXS_SERV::GXS_MSG_STATUS_GUI_NEW);
|
||||
else
|
||||
mPosts[mFilteredPosts[entry]].mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD;
|
||||
|
||||
mPosts[mFilteredPosts[entry]].mUnreadCommentCount = 0;
|
||||
|
||||
emit dataChanged(i,i);
|
||||
}
|
||||
|
||||
QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) const
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue