From c06da9975707646836e1387c0b37178ba97fdfb4 Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 9 Feb 2023 23:27:43 +0100 Subject: [PATCH 01/13] attempt at not reloading full channel data at every change --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 180 ++-- .../gui/gxschannels/GxsChannelPostsModel.h | 4 +- .../GxsChannelPostsWidgetWithModel.cpp | 805 +++++++++--------- .../GxsChannelPostsWidgetWithModel.h | 6 +- .../GxsChannelPostsWidgetWithModel.ui | 4 +- 5 files changed, 520 insertions(+), 479 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 06b671532..0ad05b4a1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -118,85 +118,21 @@ void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptrmChannelEventCode) { 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! + case RsChannelEventCode::READ_STATUS_CHANGED: + case RsChannelEventCode::NEW_COMMENT: + { + // 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. + // In particular, mChannelMsgId may refer to a comment, but this will be handled correctly + // by update_single_post which will automatically retrive the correct parent msg. - RsGxsChannelEvent E(*e); + if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId) + update_single_post(e->mChannelGroupId,e->mChannelMsgId,e->mChannelThreadId); + }; + break; - 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 posts; - std::vector comments; - std::vector votes; - std::set 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 p2.mMeta.mPublishTs; } +void RsGxsChannelPostsModel::setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post) +{ + if(mChannelGroup.mMeta.mGroupId != group.mMeta.mGroupId) + return; + + preMods(); + + // This is potentially quadratic, so it should not be called many times! + + bool found=false; + + for(auto& p:mPosts) + if(post.mMeta.mMsgId == p.mMeta.mMsgId || post.mMeta.mOrigMsgId == p.mMeta.mMsgId) + { + p = post; + found = true; + } + if(!found) + mPosts.push_back(post); + + std::sort(mPosts.begin(),mPosts.end()); + + mFilteredPosts.clear(); + + for(uint32_t i=0;i0) + { + beginInsertRows(QModelIndex(),0,rowCount()-1); + endInsertRows(); + } + + postMods(); + + emit channelPostsLoaded(); +} void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector& posts) { preMods(); @@ -593,6 +570,57 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto emit channelPostsLoaded(); } +void RsGxsChannelPostsModel::update_single_post(const RsGxsGroupId& group_id,const RsGxsMessageId& msg_id,const RsGxsMessageId& thread_id) +{ + RsThread::async([this, group_id, msg_id,thread_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 posts; + std::vector comments; + std::vector votes; + std::set msg_ids({ (thread_id.isNull())?msg_id:thread_id } ); // we have a post message + + if(!rsGxsChannels->getChannelContent(group_id,msg_ids, posts,comments,votes)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << group_id << "/" << msg_id << std::endl; + 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(group_id,msg_ids,comments)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message comment data for channel/msg " << group_id << "/" << msg_id << std::endl; + 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. + + RsQThreadUtils::postToObject( [posts,this]() + { + for(uint32_t i=0;i& messages); @@ -231,7 +232,8 @@ private: //void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector &msgs_array, std::vector &posts, std::map > > &mPostVersions); void createPostsArray(std::vector &posts); void setPosts(const RsGxsChannelGroup& group, std::vector &posts); - void initEmptyHierarchy(); + void setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post); + void initEmptyHierarchy(); void handleEvent_main_thread(std::shared_ptr event); std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 1d8de3f89..b90f94d5e 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -99,9 +99,9 @@ int ChannelPostDelegate::cellSize(int col,const QFont& font,uint32_t parent_widt void ChannelPostDelegate::zoom(bool zoom_or_unzoom) { if(zoom_or_unzoom) - mZoom *= 1.02; + mZoom *= 1.02; else - mZoom /= 1.02; + mZoom /= 1.02; if(mZoom < 0.5) mZoom = 0.5; @@ -280,52 +280,52 @@ void ChannelPostDelegate::setWidgetGrid(bool use_grid) QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex& index) const { - ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; - if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE) - { - GxsChannelFilesStatusWidget* w = new GxsChannelFilesStatusWidget(file,parent); - connect(w,SIGNAL(onButtonClick()),this->parent(),SLOT(updateDAll_PB())); - return w; - } - else - return NULL; + if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE) + { + GxsChannelFilesStatusWidget* w = new GxsChannelFilesStatusWidget(file,parent); + connect(w,SIGNAL(onButtonClick()),this->parent(),SLOT(updateDAll_PB())); + return w; + } + else + return NULL; } void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { - editor->setGeometry(option.rect); + editor->setGeometry(option.rect); } void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { - ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; - // prepare - painter->save(); - painter->setClipRect(option.rect); + // prepare + painter->save(); + painter->setClipRect(option.rect); painter->save(); painter->fillRect( option.rect, option.backgroundBrush); - //optionFocusRect.backgroundColor = option.palette.color(colorgroup, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); + //optionFocusRect.backgroundColor = option.palette.color(colorgroup, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); painter->restore(); switch(index.column()) { case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter," " + QString::fromUtf8(file.mName.c_str())); - break; + break; case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: painter->drawText(option.rect,Qt::AlignRight | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); - break; + break; case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QDateTime::fromMSecsSinceEpoch(file.mPublishTime*1000).toString("MM/dd/yyyy, hh:mm")); - break; + break; case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: { - GxsChannelFilesStatusWidget w(file); + GxsChannelFilesStatusWidget w(file); w.setFixedWidth(option.rect.width()); - w.setFixedHeight(option.rect.height()); + w.setFixedHeight(option.rect.height()); - QPixmap pixmap(w.size()); + QPixmap pixmap(w.size()); // apparently we need to do the alternate colors manually //if(index.row() & 0x01) @@ -341,13 +341,13 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI default: painter->drawText(option.rect,Qt::AlignLeft,QString("[No data]")); - break; + break; } } QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; QFontMetricsF fm(option.font); @@ -367,11 +367,11 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : - GxsMessageFrameWidget(rsGxsChannels, parent), - ui(new Ui::GxsChannelPostsWidgetWithModel) + GxsMessageFrameWidget(rsGxsChannels, parent), + ui(new Ui::GxsChannelPostsWidgetWithModel) { - /* Invoke the Qt Designer generated object setup routine */ - ui->setupUi(this); + /* Invoke the Qt Designer generated object setup routine */ + ui->setupUi(this); ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/gridlayout.svg")); ui->viewType_TB->setToolTip(tr("Click to switch to list view")); @@ -427,75 +427,75 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->postDetails_TE->setPlaceholderText(tr("No text to display")); - // Set initial size of the splitter - ui->splitter->setStretchFactor(0, 1); - ui->splitter->setStretchFactor(1, 0); + // Set initial size of the splitter + ui->splitter->setStretchFactor(0, 1); + ui->splitter->setStretchFactor(1, 0); - QFontMetricsF fm(font()); + QFontMetricsF fm(font()); - if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID) - for(int i=0;icolumnCount();++i) - ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width())); + if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID) + for(int i=0;icolumnCount();++i) + ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width())); - /* Setup UI helper */ + /* Setup UI helper */ - /* Connect signals */ - connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); - connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); - connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); + /* Connect signals */ + connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); + connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); + connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); - ui->postButton->setText(tr("Add new post")); - - /* add filter actions */ + ui->postButton->setText(tr("Add new post")); + + /* add filter actions */ ui->filterLineEdit->setPlaceholderText(tr("Search...")); - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); ui->postsTree->setPlaceholderText(tr("No posts available in this channel")); ui->postsTree->setMinimumWidth(COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()+1); connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); - /* Set initial section sizes */ - QHeaderView * channelpostfilesheader = ui->channelPostFiles_TV->header () ; - QHeaderView * channelfilesheader = ui->channelFiles_TV->header () ; + /* Set initial section sizes */ + QHeaderView * channelpostfilesheader = ui->channelPostFiles_TV->header () ; + QHeaderView * channelfilesheader = ui->channelFiles_TV->header () ; - channelpostfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); - channelfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); + channelpostfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); + channelfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); - /* Initialize feed widget */ - //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); - //ui->feedWidget->setFilterCallback(filterItem); + /* Initialize feed widget */ + //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); + //ui->feedWidget->setFilterCallback(filterItem); - /* load settings */ - processSettings(true); + /* load settings */ + processSettings(true); ui->channelPostFiles_TV->setColumnHidden(RsGxsChannelPostFilesModel::COLUMN_FILES_DATE, true); // no need to show this here. - /* Initialize subscribe button */ - QIcon icon; + /* Initialize subscribe button */ + QIcon icon; icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/redled.png"), QIcon::Normal, QIcon::On); icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/start.png"), QIcon::Normal, QIcon::Off); #ifdef TO_REMOVE - mAutoDownloadAction = new QAction(icon, "", this); - mAutoDownloadAction->setCheckable(true); - connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload())); + mAutoDownloadAction = new QAction(icon, "", this); + mAutoDownloadAction->setCheckable(true); + connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload())); - ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction); + ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction); setAutoDownload(false); #endif ui->commentsDialog->setGxsService(rsGxsChannels); - /* Initialize GUI */ - settingsChanged(); + /* Initialize GUI */ + settingsChanged(); setGroupId(channelId); - mChannelPostsModel->updateChannel(channelId); + mChannelPostsModel->updateChannel(channelId); - mEventHandlerId = 0; - // Needs to be asynced because this function is called by another thread! - rsEvents->registerEventsHandler( [this](std::shared_ptr event) + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); }, mEventHandlerId, RsEventType::GXS_CHANNELS ); @@ -512,7 +512,7 @@ void GxsChannelPostsWidgetWithModel::updateZoomFactor(bool zoom_or_unzoom) int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),s.width())))); - mChannelPostsModel->setNumColumns(n_columns); // forces the update + mChannelPostsModel->setNumColumns(n_columns); // forces the update ui->postsTree->dataChanged(QModelIndex(),QModelIndex()); } @@ -555,10 +555,10 @@ void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&) } menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); - if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) - menu.addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/pencil-edit-button.png"), tr("Edit"), this, SLOT(editPost())); + if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) + menu.addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/pencil-edit-button.png"), tr("Edit"), this, SLOT(editPost())); - menu.exec(QCursor::pos()); + menu.exec(QCursor::pos()); } void GxsChannelPostsWidgetWithModel::markMessageUnread() @@ -628,29 +628,29 @@ void GxsChannelPostsWidgetWithModel::switchView() void GxsChannelPostsWidgetWithModel::copyMessageLink() { try - { - if (groupId().isNull()) - throw std::runtime_error("No channel currently selected!"); + { + if (groupId().isNull()) + throw std::runtime_error("No channel currently selected!"); - QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); - if(!index.isValid()) - throw std::runtime_error("No post under mouse!"); + if(!index.isValid()) + throw std::runtime_error("No post under mouse!"); - RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - if(post.mMeta.mMsgId.isNull()) - throw std::runtime_error("Post has empty MsgId!"); + if(post.mMeta.mMsgId.isNull()) + throw std::runtime_error("Post has empty MsgId!"); - RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_CHANNEL, groupId(), post.mMeta.mMsgId, QString::fromUtf8(post.mMeta.mMsgName.c_str())); + RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_CHANNEL, groupId(), post.mMeta.mMsgId, QString::fromUtf8(post.mMeta.mMsgName.c_str())); - if (!link.valid()) - throw std::runtime_error("Link is not valid"); + if (!link.valid()) + throw std::runtime_error("Link is not valid"); - QList urls; - urls.push_back(link); - RSLinkClipboard::copyLinks(urls); - } + QList urls; + urls.push_back(link); + RSLinkClipboard::copyLinks(urls); + } catch(std::exception& e) { QMessageBox::critical(NULL,tr("Link creation error"),tr("Link could not be created: ")+e.what()); @@ -679,41 +679,41 @@ void GxsChannelPostsWidgetWithModel::download() rsFiles->FileRequest(file.mName, file.mHash, file.mSize, destination, RS_FILE_REQ_ANONYMOUS_ROUTING, sources); } - ui->postDAll_PB->hide(); + ui->postDAll_PB->hide(); } void GxsChannelPostsWidgetWithModel::updateDAll_PB() { - QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); - RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - size_t newFileToDl = 0; - uint64_t newFileTotalSize = 0; - QString newFilesDetails; + size_t newFileToDl = 0; + uint64_t newFileTotalSize = 0; + QString newFilesDetails; - for(auto& file:post.mFiles) - { - FileInfo fileInfo; - if (!rsFiles->FileDetails(file.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo)) { - ++newFileToDl; - newFileTotalSize += file.mSize; - newFilesDetails += QString::fromUtf8(file.mName.c_str()) + " " + misc::friendlyUnit(file.mSize) + "\n"; - } - } + for(auto& file:post.mFiles) + { + FileInfo fileInfo; + if (!rsFiles->FileDetails(file.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo)) { + ++newFileToDl; + newFileTotalSize += file.mSize; + newFilesDetails += QString::fromUtf8(file.mName.c_str()) + " " + misc::friendlyUnit(file.mSize) + "\n"; + } + } - ui->postDAll_PB->setHidden(newFileToDl == 0); - ui->postDAll_PB->setToolTip((newFileToDl == 1 ? tr("Download this file:") : tr("Download All these %1 files:").arg(newFileToDl) ) + "\n" - + newFilesDetails - + tr("Totaling: %1").arg(misc::friendlyUnit(newFileTotalSize))); + ui->postDAll_PB->setHidden(newFileToDl == 0); + ui->postDAll_PB->setToolTip((newFileToDl == 1 ? tr("Download this file:") : tr("Download All these %1 files:").arg(newFileToDl) ) + "\n" + + newFilesDetails + + tr("Totaling: %1").arg(misc::friendlyUnit(newFileTotalSize))); } void GxsChannelPostsWidgetWithModel::editPost() { QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); - RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(post.mMeta.mGroupId,post.mMeta.mMsgId); + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(post.mMeta.mGroupId,post.mMeta.mMsgId); msgDialog->show(); } @@ -742,69 +742,83 @@ void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s,bool forc void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr event) { - const RsGxsChannelEvent *e = dynamic_cast(event.get()); + const RsGxsChannelEvent *e = dynamic_cast(event.get()); - if(!e) - return; + if(!e) + return; - switch(e->mChannelEventCode) - { - case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]]; + switch(e->mChannelEventCode) + { + case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]]; case RsChannelEventCode::DELETED_CHANNEL: // [[fallthrough]]; - case RsChannelEventCode::NEW_COMMENT: // [[fallthrough]]; - case RsChannelEventCode::NEW_VOTE: // [[fallthrough]]; case RsChannelEventCode::UPDATED_CHANNEL: // [[fallthrough]]; - case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]]; - case RsChannelEventCode::UPDATED_MESSAGE: case RsChannelEventCode::RECEIVED_PUBLISH_KEY: case RsChannelEventCode::SYNC_PARAMETERS_UPDATED: { - if(e->mChannelGroupId == groupId()) - updateDisplay(true); - } + if(e->mChannelGroupId == groupId()) + updateDisplay(true,false); + } + break; - default: - break; - } + case RsChannelEventCode::NEW_COMMENT: // [[fallthrough]]; + case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]]; + { + if(e->mChannelGroupId == groupId()) + updateDisplay(true,true); + } + break; + + case RsChannelEventCode::NEW_VOTE: // [[fallthrough]]; + + if(e->mChannelGroupId == groupId() && e->mChannelThreadId == ui->commentsDialog->messageId()) + ui->commentsDialog->refresh(); + break; + + default: + break; + + // case RsChannelEventCode::UPDATED_MESSAGE: // handled in GxsChannelPostsModel + // case RsChannelEventCode::READ_STATUS_CHANGED: // handled in GxsChannelPostsModel + } } void GxsChannelPostsWidgetWithModel::showPostDetails() { QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); - RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; #ifdef DEBUG_CHANNEL_POSTS_WIDGET std::cerr << "showPostDetails: current index is " << index.row() << "," << index.column() << std::endl; #endif - //QTextDocument doc; - //doc.setHtml(post.mMsg.c_str()); + //QTextDocument doc; + //doc.setHtml(post.mMsg.c_str()); - if(post.mMeta.mPublishTs == 0) - { - ui->postDetails_TE->clear(); - ui->postLogo_LB->hide(); - ui->postName_LB->hide(); - ui->postTime_LB->hide(); - mChannelPostFilesModel->clear(); - ui->details_TW->setEnabled(false); - //mLastSelectedPosts[groupId()].clear(); + if(post.mMeta.mPublishTs == 0) + { + ui->postDetails_TE->clear(); + ui->postLogo_LB->hide(); + ui->postName_LB->hide(); + ui->postTime_LB->hide(); + mChannelPostFilesModel->clear(); + ui->details_TW->setEnabled(false); + //mLastSelectedPosts[groupId()].clear(); - return; - } - ui->details_TW->setEnabled(true); + return; + } + ui->details_TW->setEnabled(true); - ui->postLogo_LB->show(); - ui->postName_LB->show(); - ui->postTime_LB->show(); + ui->postLogo_LB->show(); + ui->postName_LB->show(); + ui->postTime_LB->show(); #ifdef DEBUG_CHANNEL_POSTS_WIDGET - std::cerr << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] << std::endl; + std::cerr << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] << std::endl; #endif - mLastSelectedPosts[groupId()] = post.mMeta.mMsgId; + mLastSelectedPosts[groupId()] = post.mMeta.mMsgId; - std::list files; - for(auto& file:post.mFiles) - files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs)); + std::list files; + for(auto& file:post.mFiles) + files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs)); mChannelPostFilesModel->setFiles(files); @@ -819,41 +833,41 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), /* RSHTML_FORMATTEXT_EMBED_SMILEYS |*/ RSHTML_FORMATTEXT_EMBED_LINKS)); - QPixmap postImage; + QPixmap postImage; - if (post.mThumbnail.mData != NULL) - GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL); - else - postImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); + if (post.mThumbnail.mData != NULL) + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL); + else + postImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); int W = QFontMetricsF(font()).height() * 8; // Using fixed width so that the post will not displace the text when we browse. - ui->postLogo_LB->setPixmap(postImage); - ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); + ui->postLogo_LB->setPixmap(postImage); + ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); - ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); + ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); - ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm")); - ui->postTime_LB->setFixedWidth(W); + ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm")); + ui->postTime_LB->setFixedWidth(W); //ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); //ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelPostFiles_TV->setAutoSelect(true); - // Now also set the post as read + // Now also set the post as read - if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) - { - RsGxsGrpMsgIdPair postId; - postId.second = post.mMeta.mMsgId; - postId.first = post.mMeta.mGroupId; + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + RsGxsGrpMsgIdPair postId; + postId.second = post.mMeta.mMsgId; + postId.first = post.mMeta.mGroupId; RsThread::async([postId]() { rsGxsChannels->setMessageReadStatus(postId, true) ; } ); - } + } - updateDAll_PB(); + updateDAll_PB(); } void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n) @@ -863,30 +877,30 @@ void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n) else ui->details_TW->setTabText(2,tr("Comments")); } -void GxsChannelPostsWidgetWithModel::updateGroupData() +void GxsChannelPostsWidgetWithModel::updateData(bool update_group_data, bool update_posts) { - if(groupId().isNull()) + if(groupId().isNull()) { // clear post, files and comment widgets showPostDetails(); - return; + return; } - RsThread::async([this]() - { - RsGxsChannelGroup group; - std::vector groups; + RsThread::async([this,update_group_data,update_posts]() + { + std::vector groups; + RsGxsChannelGroup group; - if(rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups) && groups.size()==1) + if(rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups) && groups.size()==1) group = groups[0]; else if(!rsGxsChannels->getDistantSearchResultGroupData(groupId(),group)) - { - std::cerr << __PRETTY_FUNCTION__ << " failed to get group data for channel: " << groupId() << std::endl; - return; - } + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get group data for channel: " << groupId() << std::endl; + return; + } - RsQThreadUtils::postToObject( [this,group]() + RsQThreadUtils::postToObject( [this,update_group_data,update_posts,group]() { if(mGroup.mMeta.mGroupId != group.mMeta.mGroupId) // this prevents any attempt to display the wrong index. Navigate() if needed will use mNavigatePendingMsgId { @@ -898,52 +912,58 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() updateCommentsCount(0); } - mGroup = group; + if(update_group_data) + { + mGroup = group; + insertChannelDetails(mGroup); + } - ui->postsTree->setPlaceholderText(tr("Loading...")); + if(update_posts) + { + ui->postsTree->setPlaceholderText(tr("Loading...")); - mChannelPostsModel->updateChannel(groupId()); - whileBlocking(ui->filterLineEdit)->clear(); - whileBlocking(ui->showUnread_TB)->setChecked(false); + mChannelPostsModel->updateChannel(groupId()); - insertChannelDetails(mGroup); + whileBlocking(ui->filterLineEdit)->clear(); + whileBlocking(ui->showUnread_TB)->setChecked(false); + } emit groupDataLoaded(); emit groupChanged(this); // signals the parent widget to e.g. update the group tab name } ); - }); + }); } void GxsChannelPostsWidgetWithModel::postChannelPostLoad() { - std::cerr << "Post channel load..." << std::endl; + std::cerr << "Post channel load..." << std::endl; - if (!mNavigatePendingMsgId.isNull()) - navigate(mNavigatePendingMsgId); + if (!mNavigatePendingMsgId.isNull()) + navigate(mNavigatePendingMsgId); - else if( (mLastSelectedPosts.count(groupId()) > 0) + else if( (mLastSelectedPosts.count(groupId()) > 0) && !mLastSelectedPosts[groupId()].isNull()) - { - QModelIndex index = mChannelPostsModel->getIndexOfMessage(mLastSelectedPosts[groupId()]); + { + QModelIndex index = mChannelPostsModel->getIndexOfMessage(mLastSelectedPosts[groupId()]); - std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " - << mLastSelectedPosts[groupId()].toStdString() << std::endl; + std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " + << mLastSelectedPosts[groupId()].toStdString() << std::endl; - ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); - ui->postsTree->scrollTo(index);//May change if model reloaded - ui->postsTree->setFocus(); - } - else - std::cerr << "No pre-selected channel post." << std::endl; + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(index);//May change if model reloaded + ui->postsTree->setFocus(); + } + else + std::cerr << "No pre-selected channel post." << std::endl; - std::list files; + std::list files; mChannelPostsModel->getFilesList(files); mChannelFilesModel->setFiles(files); - ui->channelFiles_TV->setAutoSelect(true); - ui->channelFiles_TV->sortByColumn(ui->channelFiles_TV->header()->sortIndicatorSection() - ,ui->channelFiles_TV->header()->sortIndicatorOrder()); + ui->channelFiles_TV->setAutoSelect(true); + ui->channelFiles_TV->sortByColumn(ui->channelFiles_TV->header()->sortIndicatorSection() + ,ui->channelFiles_TV->header()->sortIndicatorOrder()); // if there's no posts, this is what's going to be displayed. ui->postsTree->setPlaceholderText(tr("No posts available in this channel.")); @@ -971,84 +991,75 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update } -void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete) +void GxsChannelPostsWidgetWithModel::updateDisplay(bool update_group_data,bool update_posts) { - // First, clear all widget - blank(); + // First, clear all widget + blank(); #ifdef DEBUG_CHANNEL std::cerr << "udateDisplay: groupId()=" << groupId()<< std::endl; #endif - if(groupId().isNull()) + if(groupId().isNull()) { #ifdef DEBUG_CHANNEL std::cerr << " group_id=0. Return!"<< std::endl; #endif - return; + return; } - if(mGroup.mMeta.mGroupId.isNull() && !groupId().isNull()) + if(mGroup.mMeta.mGroupId != groupId()) { #ifdef DEBUG_FORUMS std::cerr << " inconsistent group data. Reloading!"<< std::endl; #endif - complete = true; + update_group_data = true; + update_posts = true; } - if(complete) // need to update the group data, reload the messages etc. - { -#warning csoler 2020-06-02 : todo - //saveExpandedItems(mSavedExpandedMessages); - //if(mGroupId != mChannelPostsModel->currentGroupId()) - // mThreadId.clear(); - - updateGroupData(); - - return; - } + updateData(update_group_data,update_posts); } GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel() { - rsEvents->unregisterEventsHandler(mEventHandlerId); - // save settings - processSettings(false); + rsEvents->unregisterEventsHandler(mEventHandlerId); + // save settings + processSettings(false); //delete(mAutoDownloadAction); delete mFilesDelegate; - delete ui; + delete ui; } void GxsChannelPostsWidgetWithModel::processSettings(bool load) { - QHeaderView *channelpostfilesheader = ui->channelPostFiles_TV->header () ; - QHeaderView *channelfilesheader = ui->channelFiles_TV->header () ; - - Settings->beginGroup(QString("ChannelPostsWidget")); + QHeaderView *channelpostfilesheader = ui->channelPostFiles_TV->header () ; + QHeaderView *channelfilesheader = ui->channelFiles_TV->header () ; - if (load) { + Settings->beginGroup(QString("ChannelPostsWidget")); - // state of files tree - channelpostfilesheader->restoreState(Settings->value("PostFilesTree").toByteArray()); - channelfilesheader->restoreState(Settings->value("FilesTree").toByteArray()); + if (load) { - // state of splitter - ui->splitter->restoreState(Settings->value("SplitterChannelPosts").toByteArray()); - } else { - // state of files tree - Settings->setValue("PostFilesTree", channelpostfilesheader->saveState()); - Settings->setValue("FilesTree", channelfilesheader->saveState()); + // state of files tree + channelpostfilesheader->restoreState(Settings->value("PostFilesTree").toByteArray()); + channelfilesheader->restoreState(Settings->value("FilesTree").toByteArray()); - // state of splitter - Settings->setValue("SplitterChannelPosts", ui->splitter->saveState()); - } + // state of splitter + ui->splitter->restoreState(Settings->value("SplitterChannelPosts").toByteArray()); + } else { + // state of files tree + Settings->setValue("PostFilesTree", channelpostfilesheader->saveState()); + Settings->setValue("FilesTree", channelfilesheader->saveState()); - Settings->endGroup(); + // state of splitter + Settings->setValue("SplitterChannelPosts", ui->splitter->saveState()); + } + + Settings->endGroup(); } void GxsChannelPostsWidgetWithModel::settingsChanged() { - mUseThread = Settings->getChannelLoadThread(); + mUseThread = Settings->getChannelLoadThread(); - //mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); + //mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); } QString GxsChannelPostsWidgetWithModel::groupName(bool) @@ -1068,15 +1079,15 @@ void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &/*name*/) QIcon GxsChannelPostsWidgetWithModel::groupIcon() { - /* CHANNEL IMAGE */ - QPixmap chanImage; - if (mGroup.mImage.mData != NULL) { - GxsIdDetails::loadPixmapFromData(mGroup.mImage.mData, mGroup.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); - } else { - chanImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); - } + /* CHANNEL IMAGE */ + QPixmap chanImage; + if (mGroup.mImage.mData != NULL) { + GxsIdDetails::loadPixmapFromData(mGroup.mImage.mData, mGroup.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); + } else { + chanImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); + } - return QIcon(chanImage); + return QIcon(chanImage); } /*************************************************************************************/ @@ -1086,50 +1097,50 @@ QIcon GxsChannelPostsWidgetWithModel::groupIcon() // Callback from Widget->FeedHolder->ServiceDialog->CommentContainer->CommentDialog, void GxsChannelPostsWidgetWithModel::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title) { - emit loadComment(groupId, msg_versions,msgId, title); + emit loadComment(groupId, msg_versions,msgId, title); } void GxsChannelPostsWidgetWithModel::createMsg() { - if (groupId().isNull()) { - return; - } + if (groupId().isNull()) { + return; + } - if (!IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { - return; - } + if (!IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { + return; + } - CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(groupId()); - msgDialog->show(); + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(groupId()); + msgDialog->show(); - /* window will destroy itself! */ + /* window will destroy itself! */ } void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGroup &group) { // save selection if needed - /* IMAGE */ - QPixmap chanImage; - if (group.mImage.mData != NULL) { - GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); - } else { + /* IMAGE */ + QPixmap chanImage; + if (group.mImage.mData != NULL) { + GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); + } else { chanImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); - } + } if(group.mMeta.mGroupName.empty()) - ui->channelName_LB->setText(tr("[No name]")); + ui->channelName_LB->setText(tr("[No name]")); else - ui->channelName_LB->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); + ui->channelName_LB->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); - ui->logoLabel->setPixmap(chanImage); + ui->logoLabel->setPixmap(chanImage); ui->logoLabel->setFixedSize(QSize(ui->logoLabel->height()*chanImage.width()/(float)chanImage.height(),ui->logoLabel->height())); // make the logo have the same aspect ratio than the original image - ui->postButton->setEnabled(bool(group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)); + ui->postButton->setEnabled(bool(group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)); #ifdef TO_REMOVE - bool autoDownload ; - rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); - setAutoDownload(autoDownload); + bool autoDownload ; + rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); + setAutoDownload(autoDownload); #endif setSubscribeButtonText(group.mMeta.mGroupId,group.mMeta.mSubscribeFlags, group.mMeta.mPop); @@ -1154,12 +1165,12 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou } - ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); + ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); - if(group.mMeta.mLastPost==0) - ui->infoLastPost->setText(tr("Never")); - else - ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); + if(group.mMeta.mLastPost==0) + ui->infoLastPost->setText(tr("Never")); + else + ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); uint32_t current_sync_time = GxsGroupFrameDialog::checkDelay(rsGxsChannels->getSyncPeriod(group.mMeta.mGroupId))/86400 ; @@ -1185,65 +1196,65 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou ui->infoSyncTimeLabel->setText(sync_string); - QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); + QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); - unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; + unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; - // embed smileys ? - if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { - formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; - } + // embed smileys ? + if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { + formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; + } - formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); + formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); - ui->infoDescription->setText(formatDescription); + ui->infoDescription->setText(formatDescription); - ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; + ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; - if(!group.mMeta.mAuthorId.isNull()) - { - RetroShareLink link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); - ui->infoAdministrator->setText(link.toHtml()); - } - else - ui->infoAdministrator->setText("[No contact author]"); + if(!group.mMeta.mAuthorId.isNull()) + { + RetroShareLink link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); + ui->infoAdministrator->setText(link.toHtml()); + } + else + ui->infoAdministrator->setText("[No contact author]"); - ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); + ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); - QString distrib_string ( "[unknown]" ); + QString distrib_string ( "[unknown]" ); - switch((RsGxsCircleType)group.mMeta.mCircleType) - { - case RsGxsCircleType::PUBLIC: distrib_string = tr("Public") ; - break ; - case RsGxsCircleType::EXTERNAL: - { - RsGxsCircleDetails det ; + switch((RsGxsCircleType)group.mMeta.mCircleType) + { + case RsGxsCircleType::PUBLIC: distrib_string = tr("Public") ; + break ; + case RsGxsCircleType::EXTERNAL: + { + RsGxsCircleDetails det ; - // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. + // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. - if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) - distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; - else - distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; - } - break ; - case RsGxsCircleType::YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); - break ; - case RsGxsCircleType::LOCAL: distrib_string = tr("You and your friend nodes"); - break ; - default: - std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; - } + if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) + distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; + else + distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; + } + break ; + case RsGxsCircleType::YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); + break ; + case RsGxsCircleType::LOCAL: distrib_string = tr("You and your friend nodes"); + break ; + default: + std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; + } - ui->infoDistribution->setText(distrib_string); + ui->infoDistribution->setText(distrib_string); #ifdef TODO - ui->infoWidget->show(); - ui->feedWidget->hide(); - ui->fileWidget->hide(); + ui->infoWidget->show(); + ui->feedWidget->hide(); + ui->fileWidget->hide(); - //ui->feedToolButton->setEnabled(false); - //ui->fileToolButton->setEnabled(false); + //ui->feedToolButton->setEnabled(false); + //ui->fileToolButton->setEnabled(false); #endif setSubscribeButtonText(group.mMeta.mGroupId,group.mMeta.mSubscribeFlags, group.mMeta.mPop); @@ -1322,70 +1333,70 @@ void GxsChannelPostsWidgetWithModel::switchOnlyUnread(bool) } void GxsChannelPostsWidgetWithModel::filterChanged(QString s) { - QStringList ql = s.split(' ',QString::SkipEmptyParts); - uint32_t count; - mChannelPostsModel->setFilter(ql,ui->showUnread_TB->isChecked(),count); - mChannelFilesModel->setFilter(ql,count); + QStringList ql = s.split(' ',QString::SkipEmptyParts); + uint32_t count; + mChannelPostsModel->setFilter(ql,ui->showUnread_TB->isChecked(),count); + mChannelFilesModel->setFilter(ql,count); } void GxsChannelPostsWidgetWithModel::blank() { - ui->postButton->setEnabled(false); - ui->subscribeToolButton->setEnabled(false); + ui->postButton->setEnabled(false); + ui->subscribeToolButton->setEnabled(false); - ui->channelName_LB->setText(tr("No Channel Selected")); - ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); - ui->infoPosts->setText(""); - ui->infoLastPost->setText(""); - ui->infoAdministrator->setText(""); - ui->infoDistribution->setText(""); - ui->infoCreated->setText(""); - ui->infoDescription->setText(""); + ui->channelName_LB->setText(tr("No Channel Selected")); + ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); + ui->infoPosts->setText(""); + ui->infoLastPost->setText(""); + ui->infoAdministrator->setText(""); + ui->infoDistribution->setText(""); + ui->infoCreated->setText(""); + ui->infoDescription->setText(""); - mChannelPostsModel->clear(); - mChannelPostFilesModel->clear(); - ui->postDetails_TE->clear(); - ui->postLogo_LB->hide(); - ui->postName_LB->hide(); - ui->postTime_LB->hide(); - ui->postDAll_PB->hide(); - groupNameChanged(QString()); + mChannelPostsModel->clear(); + mChannelPostFilesModel->clear(); + ui->postDetails_TE->clear(); + ui->postLogo_LB->hide(); + ui->postName_LB->hide(); + ui->postTime_LB->hide(); + ui->postDAll_PB->hide(); + groupNameChanged(QString()); } bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId& msgId) { - QModelIndex index = mChannelPostsModel->getIndexOfMessage(msgId); + QModelIndex index = mChannelPostsModel->getIndexOfMessage(msgId); - if(!index.isValid()) - { - std::cerr << "(EE) Cannot navigate to msg " << msgId << " in channel " << mGroup.mMeta.mGroupId << ": index unknown. Setting mNavigatePendingMsgId." << std::endl; + if(!index.isValid()) + { + std::cerr << "(EE) Cannot navigate to msg " << msgId << " in channel " << mGroup.mMeta.mGroupId << ": index unknown. Setting mNavigatePendingMsgId." << std::endl; - mNavigatePendingMsgId = msgId; // not found. That means the forum may not be loaded yet. So we keep that post in mind, for after loading. - return true; // we have to return true here, otherwise the caller will intepret the async loading as an error. - } + mNavigatePendingMsgId = msgId; // not found. That means the forum may not be loaded yet. So we keep that post in mind, for after loading. + return true; // we have to return true here, otherwise the caller will intepret the async loading as an error. + } - ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); - ui->postsTree->scrollTo(index);//May change if model reloaded - ui->postsTree->setFocus(); + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(index);//May change if model reloaded + ui->postsTree->setFocus(); - ui->channel_TW->setCurrentIndex(CHANNEL_TABS_POSTS); - ui->details_TW->setCurrentIndex(CHANNEL_TABS_DETAILS); + ui->channel_TW->setCurrentIndex(CHANNEL_TABS_POSTS); + ui->details_TW->setCurrentIndex(CHANNEL_TABS_DETAILS); - mNavigatePendingMsgId.clear(); + mNavigatePendingMsgId.clear(); - return true; + return true; } void GxsChannelPostsWidgetWithModel::subscribeGroup(bool subscribe) { - RsGxsGroupId grpId(groupId()); - if (grpId.isNull()) return; + RsGxsGroupId grpId(groupId()); + if (grpId.isNull()) return; - RsThread::async([=]() - { - rsGxsChannels->subscribeToChannel(grpId, subscribe); - } ); + RsThread::async([=]() + { + rsGxsChannels->subscribeToChannel(grpId, subscribe); + } ); } #ifdef TO_REMOVE @@ -1397,50 +1408,50 @@ void GxsChannelPostsWidgetWithModel::setAutoDownload(bool autoDl) void GxsChannelPostsWidgetWithModel::toggleAutoDownload() { - RsGxsGroupId grpId = groupId(); - if (grpId.isNull()) { - return; - } + RsGxsGroupId grpId = groupId(); + if (grpId.isNull()) { + return; + } - bool autoDownload; - if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) - { - std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " - << "for channel: " << grpId.toStdString() << std::endl; - return; - } + bool autoDownload; + if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } - RsThread::async([this, grpId, autoDownload]() - { - if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) - { - std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " - << "for channel: " << grpId.toStdString() << std::endl; - return; - } - }); + RsThread::async([this, grpId, autoDownload]() + { + if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } + }); } #endif class GxsChannelPostsReadData { public: - explicit GxsChannelPostsReadData(bool read) - { - mRead = read; - } + explicit GxsChannelPostsReadData(bool read) + { + mRead = read; + } public: - bool mRead; + bool mRead; }; void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read) { - if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) - return; + if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) + return; - //QModelIndex src_index; + //QModelIndex src_index; - mChannelPostsModel->setAllMsgReadStatus(read); + mChannelPostsModel->setAllMsgReadStatus(read); } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index d37b83f22..56e46f49d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -104,11 +104,11 @@ public: /* GxsMessageFrameWidget */ virtual QIcon groupIcon() override; - virtual void groupIdChanged() override { updateDisplay(true); } + virtual void groupIdChanged() override { updateDisplay(true,true); } virtual QString groupName(bool) override; virtual bool navigate(const RsGxsMessageId&) override; - void updateDisplay(bool complete); + void updateDisplay(bool update_group_data, bool update_posts); #ifdef TODO /* FeedHolder */ @@ -141,7 +141,7 @@ protected: private slots: void showPostDetails(); - void updateGroupData(); + void updateData(bool update_group_data,bool update_posts); void download(); void updateDAll_PB(); void createMsg(); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index cc98c8e56..c4005cfc6 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -194,7 +194,7 @@ - 1 + 0 @@ -402,7 +402,7 @@ <!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:9pt; 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> From 43fd84e777f294b86829ad1fcec925f8bc8966c8 Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 9 Feb 2023 23:32:19 +0100 Subject: [PATCH 02/13] attempt at not reloading full channel data at every change --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index b90f94d5e..1fa83926a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -760,15 +760,15 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) updateDisplay(true,true); } break; - case RsChannelEventCode::NEW_VOTE: // [[fallthrough]]; + case RsChannelEventCode::NEW_COMMENT: // [[fallthrough]]; + case RsChannelEventCode::NEW_VOTE: if(e->mChannelGroupId == groupId() && e->mChannelThreadId == ui->commentsDialog->messageId()) ui->commentsDialog->refresh(); From a37ee4673fc4a124317acd1c265b42021658c094 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 22 Mar 2023 09:07:27 +0100 Subject: [PATCH 03/13] half-implemented the system to perform lighter updates of channels --- .../gxschannels/GxsChannelPostFilesModel.cpp | 6 +- .../gxschannels/GxsChannelPostFilesModel.h | 3 + .../gui/gxschannels/GxsChannelPostsModel.cpp | 105 +++++++++--------- .../gui/gxschannels/GxsChannelPostsModel.h | 6 +- .../GxsChannelPostsWidgetWithModel.cpp | 15 ++- 5 files changed, 78 insertions(+), 57 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index a0db07085..78748bcbe 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -426,7 +426,6 @@ QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,in void RsGxsChannelPostFilesModel::clear() { - initEmptyHierarchy(); emit channelLoaded(); @@ -463,3 +462,8 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list & postMods(); } + +void RsGxsChannelPostFilesModel::update_files(const std::set& added_files,const std::set& removed_files) +{ +#error TODO +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index 1aaa60fcb..8fbd6bbb1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -83,6 +83,9 @@ public: void setFiles(const std::list& files); void setFilter(const QStringList &strings, uint32_t &count) ; + // This method adds/removes the given lists of files. Useful when a single post is updated + void update_files(const std::set& added_files,const std::set& removed_files); + #ifdef TODO QModelIndex getIndexOfFile(const RsFileHash& hash) const; void setSortMode(SortMode mode) ; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 0ad05b4a1..425f39a1b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -35,7 +35,7 @@ #include "GxsChannelPostsModel.h" #include "GxsChannelPostFilesModel.h" -//#define DEBUG_CHANNEL_MODEL +#define DEBUG_CHANNEL_MODEL Q_DECLARE_METATYPE(RsMsgMetaData) @@ -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 event) - { - RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); - }, mEventHandlerId, RsEventType::GXS_CHANNELS ); } RsGxsChannelPostsModel::~RsGxsChannelPostsModel() @@ -108,35 +100,6 @@ void updateCommentCounts( std::vector& posts, std::vector event) -{ - const RsGxsChannelEvent *e = dynamic_cast(event.get()); - - if(!e) - return; - - switch(e->mChannelEventCode) - { - case RsChannelEventCode::UPDATED_MESSAGE: - case RsChannelEventCode::READ_STATUS_CHANGED: - case RsChannelEventCode::NEW_COMMENT: - { - // Normally we should just emit dataChanged() on the index of the data that has changed: - // We need to update the data! - - // In particular, mChannelMsgId may refer to a comment, but this will be handled correctly - // by update_single_post which will automatically retrive the correct parent msg. - - if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId) - update_single_post(e->mChannelGroupId,e->mChannelMsgId,e->mChannelThreadId); - }; - break; - - default: - break; - } -} - void RsGxsChannelPostsModel::initEmptyHierarchy() { beginResetModel(); @@ -570,9 +533,12 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto emit channelPostsLoaded(); } -void RsGxsChannelPostsModel::update_single_post(const RsGxsGroupId& group_id,const RsGxsMessageId& msg_id,const RsGxsMessageId& thread_id) +void RsGxsChannelPostsModel::update_single_post(const RsGxsMessageId& msg_id,std::set& added_files,std::set& removed_files) { - RsThread::async([this, group_id, msg_id,thread_id]() +#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. @@ -580,20 +546,19 @@ void RsGxsChannelPostsModel::update_single_post(const RsGxsGroupId& group_id,con std::vector posts; std::vector comments; std::vector votes; - std::set msg_ids({ (thread_id.isNull())?msg_id:thread_id } ); // we have a post message - if(!rsGxsChannels->getChannelContent(group_id,msg_ids, posts,comments,votes)) + if(!rsGxsChannels->getChannelContent(currentGroupId(), { msg_id }, posts,comments,votes) || posts.size() != 1) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << group_id << "/" << msg_id << std::endl; + 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(group_id,msg_ids,comments)) + if(!rsGxsChannels->getChannelComments(currentGroupId(),{ msg_id },comments)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message comment data for channel/msg " << group_id << "/" << msg_id << std::endl; + RsErr() << " failed to retrieve message comment data for channel/msg " << currentGroupId() << "/" << msg_id ; return; } @@ -603,20 +568,58 @@ void RsGxsChannelPostsModel::update_single_post(const RsGxsGroupId& group_id,con // 2 - update the model in the UI thread. - RsQThreadUtils::postToObject( [posts,this]() + added_files.clear(); + removed_files.clear(); + + RsQThreadUtils::postToObject( [&posts,&added_files,&removed_files,this]() { for(uint32_t i=0;i &added_files, std::set &removed_files); + +private: #ifdef TODO void setForumMessageSummary(const std::vector& messages); @@ -234,7 +237,6 @@ private: void setPosts(const RsGxsChannelGroup& group, std::vector &posts); void setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post); void initEmptyHierarchy(); - void handleEvent_main_thread(std::shared_ptr event); std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. std::vector mPosts ; // store the list of posts updated from rsForums. diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 74bd7361f..e4123dfc5 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -783,7 +783,14 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) - updateDisplay(true,true); + { + std::set added_files,removed_files; + + mChannelPostsModel->update_single_post(e->mChannelMsgId,added_files,removed_files); + mChannelFilesModel->update_files(added_files,removed_files); + + updateDisplay(true,false); + } } break; @@ -794,11 +801,13 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrcommentsDialog->refresh(); break; + case RsChannelEventCode::READ_STATUS_CHANGED: + mChannelPostsModel->triggerViewUpdate(); + break; + default: break; - // case RsChannelEventCode::UPDATED_MESSAGE: // handled in GxsChannelPostsModel - // case RsChannelEventCode::READ_STATUS_CHANGED: // handled in GxsChannelPostsModel } } From aba0ffa581e025a6c2b1e89fd75d91cc2c81078b Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 23 Mar 2023 17:04:22 +0100 Subject: [PATCH 04/13] finished the system to perform lighter updates of channels --- .../gxschannels/GxsChannelPostFilesModel.cpp | 46 ++++- .../gxschannels/GxsChannelPostFilesModel.h | 86 ++++----- .../gui/gxschannels/GxsChannelPostsModel.cpp | 169 ++++++------------ .../gui/gxschannels/GxsChannelPostsModel.h | 7 +- .../GxsChannelPostsWidgetWithModel.cpp | 53 +++++- 5 files changed, 193 insertions(+), 168 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 78748bcbe..2d0b10106 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -463,7 +463,49 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list & postMods(); } -void RsGxsChannelPostFilesModel::update_files(const std::set& added_files,const std::set& removed_files) +void RsGxsChannelPostFilesModel::update_files(std::set& added_files,std::set& 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& files); + void setFiles(const std::list& files); void setFilter(const QStringList &strings, uint32_t &count) ; // This method adds/removes the given lists of files. Useful when a single post is updated - void update_files(const std::set& added_files,const std::set& removed_files); + void update_files(std::set &added_files, std::set &removed_files); #ifdef TODO - QModelIndex getIndexOfFile(const RsFileHash& hash) const; + QModelIndex getIndexOfFile(const RsFileHash& hash) const; void setSortMode(SortMode mode) ; - void setTextColorRead (QColor color) { mTextColorRead = color;} - void setTextColorUnread (QColor color) { mTextColorUnread = color;} - void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} - void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} - void setTextColorMissing (QColor color) { mTextColorMissing = color;} - void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); + void setTextColorRead (QColor color) { mTextColorRead = color;} + void setTextColorUnread (QColor color) { mTextColorUnread = color;} + void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} + void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} + void setTextColorMissing (QColor color) { mTextColorMissing = color;} + void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); #endif // Helper functions @@ -115,25 +115,25 @@ public: Qt::ItemFlags flags(const QModelIndex& index) 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 QVariant sizeHintRole (int col) const; - QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const; - QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const; - QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const; - QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const; - QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const; #ifdef TODO - QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; - QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; + QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; + QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; #endif /*! @@ -154,20 +154,20 @@ private: bool mFilteringEnabled; SortMode mSortMode; #endif - void preMods() ; - void postMods() ; + void preMods() ; + void postMods() ; quintptr getParentRow(quintptr ref,int& row) const; quintptr getChildRef(quintptr ref, int index) const; int getChildrenCount(quintptr ref) const; 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 - static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); + static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); #endif - void initEmptyHierarchy(); + void initEmptyHierarchy(); std::vector mFilteredFiles ; // store the list of files for the post std::vector mFiles ; // store the list of files for the post diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 425f39a1b..4f3223186 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -64,7 +64,7 @@ void RsGxsChannelPostsModel::setMode(TreeMode mode) triggerViewUpdate(); } -void updateCommentCounts( std::vector& posts, std::vector& comments) +void RsGxsChannelPostsModel::computeCommentCounts( std::vector& posts, std::vector& comments) { // 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; } -void RsGxsChannelPostsModel::setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post) +void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std::set& added_files,std::set& removed_files) { - if(mChannelGroup.mMeta.mGroupId != group.mMeta.mGroupId) - return; +#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(); - 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) - if(post.mMeta.mMsgId == p.mMeta.mMsgId || post.mMeta.mOrigMsgId == p.mMeta.mMsgId) + for(uint32_t j=0;j0) - { - beginInsertRows(QModelIndex(),0,rowCount()-1); - endInsertRows(); + // We don't do that, because otherwise the filtered posts will be changed dynamically when browsing, which is bad. + // + // mFilteredPosts.clear(); + // for(uint32_t i=0;i& posts) { preMods(); @@ -533,96 +559,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto emit channelPostsLoaded(); } -void RsGxsChannelPostsModel::update_single_post(const RsGxsMessageId& msg_id,std::set& added_files,std::set& 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 posts; - std::vector comments; - std::vector 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, std::vector& comments); QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const; @@ -214,8 +215,6 @@ private: static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry); void update_posts(const RsGxsGroupId& group_id); -public: - void update_single_post(const RsGxsMessageId& msgId, std::set &added_files, std::set &removed_files); private: @@ -235,7 +234,9 @@ private: //void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector &msgs_array, std::vector &posts, std::map > > &mPostVersions); void createPostsArray(std::vector &posts); void setPosts(const RsGxsChannelGroup& group, std::vector &posts); - void setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post); +public: + void updateSinglePost(const RsGxsChannelPost& post, std::set& added_files, std::set& removed_files); +private: void initEmptyHierarchy(); std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index e4123dfc5..023732ed7 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -784,12 +784,57 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) { - std::set added_files,removed_files; - mChannelPostsModel->update_single_post(e->mChannelMsgId,added_files,removed_files); - mChannelFilesModel->update_files(added_files,removed_files); + 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. - updateDisplay(true,false); + std::vector posts; + std::vector comments; + std::vector 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 added_files,removed_files; + + mChannelPostsModel->updateSinglePost(post,added_files,removed_files); + + std::set 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; From 141ee0b7e04826f5b07f1dc9c8d8699782b22582 Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 23 Mar 2023 21:13:38 +0100 Subject: [PATCH 05/13] fixed bug in updating filtered posts --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 53 +++++++++++-------- .../gui/gxschannels/GxsChannelPostsModel.h | 5 ++ .../GxsChannelPostsWidgetWithModel.cpp | 9 ++-- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 4f3223186..508db7046 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -140,7 +140,31 @@ void RsGxsChannelPostsModel::getFilesList(std::list& 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(); @@ -151,18 +175,8 @@ void RsGxsChannelPostsModel::setFilter(const QStringList& strings,bool only_unre endResetModel(); for(size_t i=0;i& posts) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h index a84a4df60..4fe35e589 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h @@ -141,6 +141,8 @@ public: void setAllMsgReadStatus(bool read_status); void setFilter(const QStringList &strings, bool only_unread,uint32_t &count) ; + bool postPassesFilter(const RsGxsChannelPost &post, const QStringList &strings, bool only_unread) const; + void updateFilter(uint32_t& count); #ifdef TODO void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); @@ -239,6 +241,9 @@ public: private: void initEmptyHierarchy(); + QStringList mFilteredStrings; + bool mFilterUnread; + std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. std::vector mPosts ; // store the list of posts updated from rsForums. diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 023732ed7..b1715d9d8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -784,6 +784,7 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) { + RsDbg() << "Received new message in current channel, msgId=" << e->mChannelMsgId ; RsThread::async([this,e]() { @@ -803,6 +804,7 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrupdate_files(added_filesi,removed_filesi); - updateDisplay(true,false); - },this); }); } @@ -994,6 +995,8 @@ void GxsChannelPostsWidgetWithModel::updateData(bool update_group_data, bool upd if(update_posts) { + blank(); + ui->postsTree->setPlaceholderText(tr("Loading...")); mChannelPostsModel->updateChannel(groupId()); @@ -1068,7 +1071,7 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() void GxsChannelPostsWidgetWithModel::updateDisplay(bool update_group_data,bool update_posts) { // First, clear all widget - blank(); + #ifdef DEBUG_CHANNEL std::cerr << "udateDisplay: groupId()=" << groupId()<< std::endl; #endif From dec1d3068b75b376d9c520aa58eb3b2b3fd4db2b Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 23 Mar 2023 22:07:28 +0100 Subject: [PATCH 06/13] fixed 2 bugs in post display --- libbitdht | 2 +- libretroshare | 2 +- .../gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 9 ++------- retroshare-webui | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/libbitdht b/libbitdht index 2ddc86fb5..659423769 160000 --- a/libbitdht +++ b/libbitdht @@ -1 +1 @@ -Subproject commit 2ddc86fb575a61170f4c06a00152e3e7dc74c8f4 +Subproject commit 659423769541169457c41f71c8a038e2d64ba079 diff --git a/libretroshare b/libretroshare index a9d4447c5..74cd958cf 160000 --- a/libretroshare +++ b/libretroshare @@ -1 +1 @@ -Subproject commit a9d4447c5660337b4f7945d20e0f4ffd68d918e9 +Subproject commit 74cd958cf8a3c8b3e2d3f8a22657b5e16bdad476 diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index b1715d9d8..ed721a66a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -780,6 +780,7 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) @@ -821,7 +822,7 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr added_files,removed_files; @@ -847,10 +848,6 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrcommentsDialog->refresh(); break; - case RsChannelEventCode::READ_STATUS_CHANGED: - mChannelPostsModel->triggerViewUpdate(); - break; - default: break; @@ -995,8 +992,6 @@ void GxsChannelPostsWidgetWithModel::updateData(bool update_group_data, bool upd if(update_posts) { - blank(); - ui->postsTree->setPlaceholderText(tr("Loading...")); mChannelPostsModel->updateChannel(groupId()); diff --git a/retroshare-webui b/retroshare-webui index a593b9c80..5cc37b3e5 160000 --- a/retroshare-webui +++ b/retroshare-webui @@ -1 +1 @@ -Subproject commit a593b9c808f90cce7d47a5de86c207ef8675945f +Subproject commit 5cc37b3e5ad6b64b247e9d2066a9b6c8acae67ce From e4910cf8fdd9ee43f78a1cb97b91bbf6fea83cea Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 24 Mar 2023 13:48:48 +0100 Subject: [PATCH 07/13] fixed update of comment/unread comment count when a new version of a msg arrives --- .../src/gui/gxschannels/GxsChannelPostsModel.cpp | 13 +++++++++++++ .../gxschannels/GxsChannelPostsWidgetWithModel.cpp | 2 -- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 508db7046..865cd776b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -498,7 +498,14 @@ void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std:: 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 @@ -512,7 +519,13 @@ void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std:: 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." ; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index ed721a66a..e63b96633 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -805,7 +805,6 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr Date: Sun, 26 Mar 2023 16:29:00 +0200 Subject: [PATCH 08/13] fixed bug accessing a potentially deleted object of the parent thread --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index e63b96633..c8a0fd117 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -787,7 +787,7 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr= mFilteredFiles.size()) { -#ifdef DEBUG_CHANNEL_MODEL +#ifdef DEBUG_CHANNEL_FILES_MODEL std::cerr << "Bad pointer: " << (void*)ref << std::endl; #endif return QVariant() ; @@ -443,7 +443,7 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list & for(uint32_t i=0;i& add { // 1 - remove common files from both lists - std::cerr << "RsGxsChannelPostsFilesModel:: updating files." << std::endl; +#ifdef DEBUG_CHANNEL_FILES_MODEL + RsDbg() << "RsGxsChannelPostsFilesModel:: updating files." ; +#endif for(auto afit=added_files.begin();afit!=added_files.end();) { @@ -475,7 +477,9 @@ void RsGxsChannelPostFilesModel::update_files(std::set& add if(rfit != removed_files.end()) { - std::cerr << " Eliminating common file " << rfit->mName << std::endl; +#ifdef DEBUG_CHANNEL_FILES_MODEL + RsDbg() << " Eliminating common file " << rfit->mName ; +#endif removed_files.erase(rfit); auto tmp = afit; ++tmp; @@ -486,11 +490,15 @@ void RsGxsChannelPostFilesModel::update_files(std::set& add ++afit; } + RsDbg() << " Remains: " << added_files.size() << " added files and " << removed_files.size() << " removed files" ; + // 2 - add whatever file remains, for(const auto& f:removed_files) { - std::cerr << " Removing deleted file " << f.mName << std::endl; +#ifdef DEBUG_CHANNEL_FILES_MODEL + RsDbg() << " Removing deleted file " << f.mName ; +#endif for(uint32_t i=0;i& add for(const auto& f:added_files ) { +#ifdef DEBUG_CHANNEL_FILES_MODEL + RsDbg() << " Adding new file " << f.mName ; +#endif mFilteredFiles.push_back(mFiles.size()); mFiles.push_back(f); } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 865cd776b..0fe8265f6 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -35,6 +35,7 @@ #include "GxsChannelPostsModel.h" #include "GxsChannelPostFilesModel.h" +//#define DEBUG_CHANNEL_MODEL_DATA #define DEBUG_CHANNEL_MODEL Q_DECLARE_METATYPE(RsMsgMetaData) @@ -283,7 +284,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) ; @@ -373,7 +374,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 @@ -390,13 +391,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() ; @@ -404,7 +405,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() ; From e63ff4d7db27b9d6fa125c18f3eac9e82ab45a3b Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 26 Mar 2023 22:49:08 +0200 Subject: [PATCH 10/13] made setAllMsgAsRead faster in channels, avoiding to reload all posts --- .../gxschannels/GxsChannelPostFilesModel.cpp | 2 +- .../gui/gxschannels/GxsChannelPostsModel.cpp | 23 +++++++++++++-- .../GxsChannelPostsWidgetWithModel.cpp | 29 +++++++++++++------ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index ee1bc5fe2..856ef32e1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -33,7 +33,7 @@ #include "GxsChannelPostFilesModel.h" -#define DEBUG_CHANNEL_FILES_MODEL +//#define DEBUG_CHANNEL_FILES_MODEL Q_DECLARE_METATYPE(ChannelPostFileInfo) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 0fe8265f6..b7f9f53d1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -39,7 +39,6 @@ #define DEBUG_CHANNEL_MODEL Q_DECLARE_METATYPE(RsMsgMetaData) - Q_DECLARE_METATYPE(RsGxsChannelPost) std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere @@ -206,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; } @@ -790,6 +789,16 @@ 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;isetMessageReadStatus(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; + else + mPosts[mFilteredPosts[entry]].mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD; + + emit dataChanged(i,i); } QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) const diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index c8a0fd117..9198ed73a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -119,6 +119,7 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & painter->save(); painter->setClipRect(option.rect); + std::cerr << "option.rect=" << option.rect.width() << " x " << option.rect.height() << std::endl; RsGxsChannelPost post = index.data(Qt::UserRole).value() ; // if(index.row() & 0x01) @@ -262,6 +263,9 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM float cell_width = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height(); float cell_height = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height()*aspect_ratio; +#ifdef DEBUG_CHANNEL_POSTS_WIDGET + RsDbg() << "SizeHint: mUseGrid=" << mUseGrid << " cell_width=" << cell_width << " cell_height=" << cell_height << " mZoom=" << mZoom ; +#endif if(mUseGrid || index.column()==0) return QSize(cell_width,cell_height); @@ -743,7 +747,7 @@ void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s,bool forc return; int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),ui->postsTree->width())))); - std::cerr << "nb columns: " << n_columns << " current count=" << mChannelPostsModel->columnCount() << std::endl; + RsDbg() << "nb columns: " << n_columns << " current count=" << mChannelPostsModel->columnCount() ; // save current post. The setNumColumns() indeed loses selection @@ -780,7 +784,9 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) @@ -857,7 +863,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); RsGxsChannelPost post = index.data(Qt::UserRole).value() ; #ifdef DEBUG_CHANNEL_POSTS_WIDGET - std::cerr << "showPostDetails: current index is " << index.row() << "," << index.column() << std::endl; + RsDbg() << "showPostDetails: current index is " << index.row() << "," << index.column() ; #endif //QTextDocument doc; @@ -882,7 +888,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postTime_LB->show(); #ifdef DEBUG_CHANNEL_POSTS_WIDGET - std::cerr << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] << std::endl; + RsDbg() << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] ; #endif mLastSelectedPosts[groupId()] = post.mMeta.mMsgId; @@ -898,7 +904,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->commentsDialog->commentLoad(post.mMeta.mGroupId, all_msgs_versions, post.mMeta.mMsgId,true); #ifdef DEBUG_CHANNEL_POSTS_WIDGET - std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl; + RsDbg() << "Showing details about selected index : "<< index.row() << "," << index.column() ; #endif ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), /* RSHTML_FORMATTEXT_EMBED_SMILEYS |*/ RSHTML_FORMATTEXT_EMBED_LINKS)); @@ -930,11 +936,16 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) { - RsGxsGrpMsgIdPair postId; - postId.second = post.mMeta.mMsgId; - postId.first = post.mMeta.mGroupId; + mChannelPostsModel->setMsgReadStatus(index,true); - RsThread::async([postId]() { rsGxsChannels->setMessageReadStatus(postId, true) ; } ); + //RsGxsGrpMsgIdPair postId; + //postId.second = post.mMeta.mMsgId; + //postId.first = post.mMeta.mGroupId; + + //RsThread::async([postId]() + //{ + //rsGxsChannels->setMessageReadStatus(postId, true) ; + //} ); } updateDAll_PB(); From 09e66cbde4bb864ce4a6fcc31e914a963db9b1d4 Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 26 Mar 2023 23:03:09 +0200 Subject: [PATCH 11/13] code cleaning/disabled debug output --- .../src/gui/gxschannels/GxsChannelPostsModel.cpp | 2 +- .../GxsChannelPostsWidgetWithModel.cpp | 16 ++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index b7f9f53d1..7d34bb007 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -36,7 +36,7 @@ #include "GxsChannelPostFilesModel.h" //#define DEBUG_CHANNEL_MODEL_DATA -#define DEBUG_CHANNEL_MODEL +//#define DEBUG_CHANNEL_MODEL Q_DECLARE_METATYPE(RsMsgMetaData) Q_DECLARE_METATYPE(RsGxsChannelPost) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 9198ed73a..268296220 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -119,14 +119,9 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & painter->save(); painter->setClipRect(option.rect); - std::cerr << "option.rect=" << option.rect.width() << " x " << option.rect.height() << std::endl; RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - // if(index.row() & 0x01) - // painter->fillRect( option.rect, option.palette.alternateBase().color()); - // else - painter->fillRect( option.rect, option.palette.base().color()); - + painter->fillRect( option.rect, option.palette.base().color()); painter->restore(); if(mUseGrid || index.column()==0) @@ -145,14 +140,7 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row) pixmap.fill(SelectedColor); // I dont know how to grab the backgroud color for selected objects automatically. else - { - // we need to do the alternate color manually - - //if(index.row() & 0x01) - // pixmap.fill(option.palette.alternateBase().color()); - //else - pixmap.fill(option.palette.base().color()); - } + pixmap.fill(option.palette.base().color()); w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background From 48c959c8583155ca941f01376559f3f56c0dc56f Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 27 Mar 2023 21:27:07 +0200 Subject: [PATCH 12/13] fixed bug in setting msg as read --- retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 7d34bb007..6063d4d64 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -794,9 +794,9 @@ void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status) for(uint32_t i=0;i Date: Wed, 29 Mar 2023 20:46:41 +0200 Subject: [PATCH 13/13] using orange color for unread comment bubble in channel post widget, and fixed the color update when new comment arrives --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 12 ++++ .../gui/gxschannels/GxsChannelPostsModel.h | 1 + .../GxsChannelPostsWidgetWithModel.cpp | 65 +++++++++++-------- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 6063d4d64..7d22208d5 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -801,6 +801,16 @@ void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status) 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 0) + { + QPainter p(&pixmap); + QFontMetricsF fm(option.font); - if(post.mUnreadCommentCount) - { - QPainter p(&pixmap); - QFontMetricsF fm(option.font); - - p.drawPixmap(QPoint(pixmap.width(),0.0)+mZoom*QPoint(-2.9*fm.height(),0.4*fm.height()), - FilesDefs::getPixmapFromQtResourcePath(COMMENT_OVERLAY_IMAGE).scaled(mZoom*3*fm.height(),mZoom*3*fm.height(), - Qt::KeepAspectRatio,Qt::SmoothTransformation)); - } + p.drawPixmap(QPoint(pixmap.width(),0.0)+mZoom*QPoint(-2.9*fm.height(),0.4*fm.height()), + FilesDefs::getPixmapFromQtResourcePath(UNREAD_COMMENT_OVERLAY_IMAGE).scaled(mZoom*3*fm.height(),mZoom*3*fm.height(), + Qt::KeepAspectRatio,Qt::SmoothTransformation)); + } + else if(post.mCommentCount > 0) + { + QPainter p(&pixmap); + QFontMetricsF fm(option.font); + p.drawPixmap(QPoint(pixmap.width(),0.0)+mZoom*QPoint(-2.9*fm.height(),0.4*fm.height()), + FilesDefs::getPixmapFromQtResourcePath(COMMENT_OVERLAY_IMAGE).scaled(mZoom*3*fm.height(),mZoom*3*fm.height(), + Qt::KeepAspectRatio,Qt::SmoothTransformation)); } painter->drawPixmap(option.rect.topLeft(), - pixmap.scaled(option.rect.width(),option.rect.width()*pixmap.height()/(float)pixmap.width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); + pixmap.scaled(option.rect.width(),option.rect.width()*pixmap.height()/(float)pixmap.width(), + Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); } else { @@ -833,7 +840,11 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId() && e->mChannelThreadId != ui->commentsDialog->messageId()) + mChannelPostsModel->updatePostWithNewComment(e->mChannelThreadId); [[fallthrough]]; + case RsChannelEventCode::NEW_VOTE: if(e->mChannelGroupId == groupId() && e->mChannelThreadId == ui->commentsDialog->messageId()) @@ -922,7 +933,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() // Now also set the post as read - if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) || post.mUnreadCommentCount > 0) { mChannelPostsModel->setMsgReadStatus(index,true);