finished the system to perform lighter updates of channels

This commit is contained in:
csoler 2023-03-23 17:04:22 +01:00
parent a37ee4673f
commit aba0ffa581
5 changed files with 193 additions and 168 deletions

View File

@ -463,7 +463,49 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list<ChannelPostFileInfo> &
postMods(); postMods();
} }
void RsGxsChannelPostFilesModel::update_files(const std::set<RsGxsFile>& added_files,const std::set<RsGxsFile>& removed_files) void RsGxsChannelPostFilesModel::update_files(std::set<ChannelPostFileInfo>& added_files,std::set<ChannelPostFileInfo>& removed_files)
{ {
#error TODO // 1 - remove common files from both lists
std::cerr << "RsGxsChannelPostsFilesModel:: updating files." << std::endl;
for(auto afit=added_files.begin();afit!=added_files.end();)
{
auto rfit = removed_files.find(*afit);
if(rfit != removed_files.end())
{
std::cerr << " Eliminating common file " << rfit->mName << std::endl;
removed_files.erase(rfit);
auto tmp = afit;
++tmp;
added_files.erase(afit);
afit = tmp;
}
else
++afit;
}
// 2 - add whatever file remains,
for(const auto& f:removed_files)
{
std::cerr << " Removing deleted file " << f.mName << std::endl;
for(uint32_t i=0;i<mFiles.size();++i)
if(mFiles[i].mHash == f.mHash)
{
mFiles[i] = mFiles.back();
mFiles.pop_back();
break;
}
}
// 3 - add other files. We do not check that they are duplicates, because the list of files includes duplicates.
for(const auto& f:added_files )
{
mFilteredFiles.push_back(mFiles.size());
mFiles.push_back(f);
}
} }

View File

@ -41,34 +41,34 @@ class QTimer;
struct ChannelPostFileInfo: public RsGxsFile struct ChannelPostFileInfo: public RsGxsFile
{ {
ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t) ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t)
: RsGxsFile(gxs_file),mPublishTime(t) : RsGxsFile(gxs_file),mPublishTime(t)
{} {}
ChannelPostFileInfo() : mPublishTime(0) {} ChannelPostFileInfo() : mPublishTime(0) {}
rstime_t mPublishTime; rstime_t mPublishTime; // related post publish time
}; };
// This class is the item model used by Qt to display the information // This class is the item model used by Qt to display the information
class RsGxsChannelPostFilesModel : public QAbstractItemModel class RsGxsChannelPostFilesModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit RsGxsChannelPostFilesModel(QObject *parent = NULL); explicit RsGxsChannelPostFilesModel(QObject *parent = NULL);
~RsGxsChannelPostFilesModel(){} ~RsGxsChannelPostFilesModel(){}
enum Columns { enum Columns {
COLUMN_FILES_NAME = 0x00, COLUMN_FILES_NAME = 0x00,
COLUMN_FILES_SIZE = 0x01, COLUMN_FILES_SIZE = 0x01,
COLUMN_FILES_FILE = 0x02, COLUMN_FILES_FILE = 0x02,
COLUMN_FILES_DATE = 0x03, COLUMN_FILES_DATE = 0x03,
COLUMN_FILES_NB_COLUMNS = 0x04 COLUMN_FILES_NB_COLUMNS = 0x04
}; };
enum Roles{ SortRole = Qt::UserRole+1, enum Roles{ SortRole = Qt::UserRole+1,
FilterRole = Qt::UserRole+2, FilterRole = Qt::UserRole+2,
}; };
#ifdef TODO #ifdef TODO
@ -77,25 +77,25 @@ public:
}; };
#endif #endif
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
// This method will asynchroneously update the data // This method will asynchroneously update the data
void setFiles(const std::list<ChannelPostFileInfo>& files); void setFiles(const std::list<ChannelPostFileInfo>& files);
void setFilter(const QStringList &strings, uint32_t &count) ; void setFilter(const QStringList &strings, uint32_t &count) ;
// This method adds/removes the given lists of files. Useful when a single post is updated // This method adds/removes the given lists of files. Useful when a single post is updated
void update_files(const std::set<RsGxsFile>& added_files,const std::set<RsGxsFile>& removed_files); void update_files(std::set<ChannelPostFileInfo> &added_files, std::set<ChannelPostFileInfo> &removed_files);
#ifdef TODO #ifdef TODO
QModelIndex getIndexOfFile(const RsFileHash& hash) const; QModelIndex getIndexOfFile(const RsFileHash& hash) const;
void setSortMode(SortMode mode) ; void setSortMode(SortMode mode) ;
void setTextColorRead (QColor color) { mTextColorRead = color;} void setTextColorRead (QColor color) { mTextColorRead = color;}
void setTextColorUnread (QColor color) { mTextColorUnread = color;} void setTextColorUnread (QColor color) { mTextColorUnread = color;}
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
void setTextColorMissing (QColor color) { mTextColorMissing = color;} void setTextColorMissing (QColor color) { mTextColorMissing = color;}
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
#endif #endif
// Helper functions // Helper functions
@ -115,25 +115,25 @@ public:
Qt::ItemFlags flags(const QModelIndex& index) const override; Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
// Custom item roles // Custom item roles
QVariant sizeHintRole (int col) const; QVariant sizeHintRole (int col) const;
QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const; QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const; QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const; QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const; QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const; QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const;
#ifdef TODO #ifdef TODO
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
#endif #endif
/*! /*!
@ -154,20 +154,20 @@ private:
bool mFilteringEnabled; bool mFilteringEnabled;
SortMode mSortMode; SortMode mSortMode;
#endif #endif
void preMods() ; void preMods() ;
void postMods() ; void postMods() ;
quintptr getParentRow(quintptr ref,int& row) const; quintptr getParentRow(quintptr ref,int& row) const;
quintptr getChildRef(quintptr ref, int index) const; quintptr getChildRef(quintptr ref, int index) const;
int getChildrenCount(quintptr ref) const; int getChildrenCount(quintptr ref) const;
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref); static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref);
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry); static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
#ifdef TODO #ifdef TODO
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
#endif #endif
void initEmptyHierarchy(); void initEmptyHierarchy();
std::vector<int> mFilteredFiles ; // store the list of files for the post std::vector<int> mFilteredFiles ; // store the list of files for the post
std::vector<ChannelPostFileInfo> mFiles ; // store the list of files for the post std::vector<ChannelPostFileInfo> mFiles ; // store the list of files for the post

View File

@ -64,7 +64,7 @@ void RsGxsChannelPostsModel::setMode(TreeMode mode)
triggerViewUpdate(); 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 // Store posts IDs in a std::map to avoid a quadratic cost
@ -463,47 +463,73 @@ bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2)
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs; return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
} }
void RsGxsChannelPostsModel::setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post) void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std::set<RsGxsFile>& added_files,std::set<RsGxsFile>& removed_files)
{ {
if(mChannelGroup.mMeta.mGroupId != group.mMeta.mGroupId) #ifdef DEBUG_CHANNEL_MODEL
return; RsDbg() << "updating single post for group id=" << currentGroupId() << " and msg id=" << post.mMeta.mMsgId ;
#endif
added_files.clear();
removed_files.clear();
preMods(); emit layoutAboutToBeChanged();
// This is potentially quadratic, so it should not be called many times! // linear search. Not good at all, but normally this is just for a single post.
bool found=false; bool found = false;
const auto& new_post_meta(post.mMeta);
for(auto& p:mPosts) for(uint32_t j=0;j<mPosts.size();++j)
if(post.mMeta.mMsgId == p.mMeta.mMsgId || post.mMeta.mOrigMsgId == p.mMeta.mMsgId) if(new_post_meta.mMsgId == mPosts[j].mMeta.mMsgId) // same post updated
{ {
p = post; added_files.insert(post.mFiles.begin(),post.mFiles.end());
found = true; removed_files.insert(mPosts[j].mFiles.begin(),mPosts[j].mFiles.end());
mPosts[j] = post;
#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;
mPosts[j] = post;
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) 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); mPosts.push_back(post);
std::sort(mPosts.begin(),mPosts.end()); std::sort(mPosts.begin(),mPosts.end());
mFilteredPosts.clear(); // We don't do that, because otherwise the filtered posts will be changed dynamically when browsing, which is bad.
//
for(uint32_t i=0;i<mPosts.size();++i) // mFilteredPosts.clear();
mFilteredPosts.push_back(i); // for(uint32_t i=0;i<mPosts.size();++i)
// mFilteredPosts.push_back(i);
#ifdef DEBUG_CHANNEL_MODEL
// debug_dump();
#endif
if (rowCount()>0)
{
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
} }
postMods(); triggerViewUpdate();
emit layoutChanged();
emit channelPostsLoaded(); emit channelPostsLoaded();
} }
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts) void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
{ {
preMods(); preMods();
@ -533,96 +559,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
emit channelPostsLoaded(); emit channelPostsLoaded();
} }
void RsGxsChannelPostsModel::update_single_post(const RsGxsMessageId& msg_id,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=" << msg_id ;
#endif
RsThread::async([this,&added_files,&removed_files, msg_id]()
{
// 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;
if(!rsGxsChannels->getChannelContent(currentGroupId(), { msg_id }, posts,comments,votes) || posts.size() != 1)
{
RsErr() << " failed to retrieve channel message data for channel/msg " << currentGroupId() << "/" << msg_id ;
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(currentGroupId(),{ msg_id },comments))
{
RsErr() << " failed to retrieve message comment data for channel/msg " << currentGroupId() << "/" << msg_id ;
return;
}
// Normally, there's a single post in the "post" array. The function below takes a full array of posts however.
updateCommentCounts(posts,comments);
// 2 - update the model in the UI thread.
added_files.clear();
removed_files.clear();
RsQThreadUtils::postToObject( [&posts,&added_files,&removed_files,this]()
{
for(uint32_t i=0;i<posts.size();++i)
{
bool found = false;
// linear search. Not good at all, but normally this is for a single post.
const auto& new_post_meta(posts[i].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(posts[i].mFiles.begin(),posts[i].mFiles.end());
removed_files.insert(mPosts[j].mFiles.begin(),mPosts[j].mFiles.end());
mPosts[j] = posts[i];
#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(posts[i].mFiles.begin(),posts[i].mFiles.end());
removed_files.insert(mPosts[j].mFiles.begin(),mPosts[j].mFiles.end());
posts[i].mOlderVersions.insert(new_post_meta.mMsgId);
mPosts[j] = posts[i];
#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(posts[i].mFiles.begin(),posts[i].mFiles.end());
mPosts.push_back(posts[i]);
}
}
triggerViewUpdate();
},this);
});
}
void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id) void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
{ {
@ -666,7 +603,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 // 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. // 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. // 2 - update the model in the UI thread.

View File

@ -101,6 +101,7 @@ public:
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01, SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
}; };
#endif #endif
static void computeCommentCounts( std::vector<RsGxsChannelPost>& posts, std::vector<RsGxsComment>& comments);
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const; QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const;
@ -214,8 +215,6 @@ private:
static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry); static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry);
void update_posts(const RsGxsGroupId& group_id); void update_posts(const RsGxsGroupId& group_id);
public:
void update_single_post(const RsGxsMessageId& msgId, std::set<RsGxsFile> &added_files, std::set<RsGxsFile> &removed_files);
private: private:
@ -235,7 +234,9 @@ private:
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions); //void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
void createPostsArray(std::vector<RsGxsChannelPost> &posts); void createPostsArray(std::vector<RsGxsChannelPost> &posts);
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts); void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
void setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post); public:
void updateSinglePost(const RsGxsChannelPost& post, std::set<RsGxsFile>& added_files, std::set<RsGxsFile>& removed_files);
private:
void initEmptyHierarchy(); void initEmptyHierarchy();
std::vector<int> mFilteredPosts; // stores the list of displayes indices due to filtering. std::vector<int> mFilteredPosts; // stores the list of displayes indices due to filtering.

View File

@ -784,12 +784,57 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr<con
{ {
if(e->mChannelGroupId == groupId()) if(e->mChannelGroupId == groupId())
{ {
std::set<RsGxsFile> added_files,removed_files;
mChannelPostsModel->update_single_post(e->mChannelMsgId,added_files,removed_files); RsThread::async([this,e]()
mChannelFilesModel->update_files(added_files,removed_files); {
// 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.
updateDisplay(true,false); std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> comments;
std::vector<RsGxsVote> votes;
const auto& msg_id(e->mChannelMsgId);
const auto& grp_id(e->mChannelGroupId);
if(!rsGxsChannels->getChannelContent(grp_id, { msg_id }, posts,comments,votes) || posts.size() != 1)
{
RsErr() << " failed to retrieve channel message data for channel/msg " << grp_id << "/" << msg_id;
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]()
{
std::set<RsGxsFile> added_files,removed_files;
mChannelPostsModel->updateSinglePost(post,added_files,removed_files);
std::set<ChannelPostFileInfo> added_filesi,removed_filesi;
for(auto f:added_files) added_filesi.insert(ChannelPostFileInfo(f,post.mMeta.mPublishTs));
for(auto f:removed_files) removed_filesi.insert(ChannelPostFileInfo(f,post.mMeta.mPublishTs));
mChannelFilesModel->update_files(added_filesi,removed_filesi);
updateDisplay(true,false);
},this);
});
} }
} }
break; break;