This commit is contained in:
Bankole Amupitan 2026-09-07 16:00:39 +02:00 committed by GitHub
commit e9afdf5c29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 165 additions and 31 deletions

View File

@ -41,6 +41,7 @@
#include <QBuffer>
#include <iostream>
#include <retroshare/rsidentity.h>
#include <gui/RetroShareLink.h>
#include <util/imageutil.h>
#include "gui/common/FilesDefs.h"
@ -55,9 +56,9 @@
const int MAXMESSAGESIZE = 199000;
PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGroupId& grpId, const RsGxsId& default_author, QWidget *parent):
PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGroupId& grpId, const RsGxsId& default_author, const RsGxsMessageId& existingPostId, QWidget *parent):
QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
mPosted(posted), mGrpId(grpId),
mPosted(posted), mGrpId(grpId), mOrigPostId(existingPostId),
ui(new Ui::PostedCreatePostDialog)
{
ui->setupUi(this);
@ -69,7 +70,14 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou
connect(ui->RichTextEditWidget, SIGNAL(textSizeOk(bool)),ui->postButton, SLOT(setEnabled(bool)));
ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/postedlinks.png"));
ui->headerFrame->setHeaderText(tr("Create a new Post"));
if(!mOrigPostId.isNull())
{
ui->headerFrame->setHeaderText(tr("Edit Post"));
ui->postButton->setText(tr("Update"));
}
else
ui->headerFrame->setHeaderText(tr("Create a new Post"));
setAttribute ( Qt::WA_DeleteOnClose, true );
@ -107,6 +115,61 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou
// should we save the ID in the settings here? I'm not sure we want this.
}
if(!mOrigPostId.isNull())
loadExistingPost();
}
void PostedCreatePostDialog::loadExistingPost()
{
RsGxsGroupId grpId = mGrpId;
RsGxsMessageId postId = mOrigPostId;
RsThread::async([this,grpId,postId]()
{
std::vector<RsPostedPost> posts;
std::vector<RsGxsComment> comments;
std::vector<RsGxsVote> votes;
std::set<RsGxsMessageId> s({ postId });
bool ok = mPosted->getBoardContent(grpId,s,posts,comments,votes) && posts.size()==1;
RsQThreadUtils::postToObject( [ok,posts,this]()
{
if(!ok)
{
QMessageBox::warning(this, tr("RetroShare"), tr("This post could not be loaded, it may not be available locally."), QMessageBox::Ok, QMessageBox::Ok);
return;
}
const RsPostedPost& post(posts[0]);
setTitle(QString::fromUtf8(post.mMeta.mMsgName.c_str()));
setLink(QString::fromUtf8(post.mLink.c_str()));
ui->RichTextEditWidget->setText(QString::fromUtf8(post.mNotes.c_str()));
if(post.mImage.mSize > 0 && post.mImage.mData != NULL)
{
imagebytes = QByteArray((const char*)post.mImage.mData, post.mImage.mSize);
QPixmap pix;
if(pix.loadFromData(imagebytes))
{
ui->imageLabel->setPixmap(pix);
ui->removeButton->show();
ui->stackedWidgetPicture->setCurrentIndex(IMG_PICTURE);
}
}
// Default to editing with the original author's identity when we
// own it (typical case: the author edits their own post). When
// editing someone else's post as board admin, the edit will be
// signed with whichever own identity is currently selected.
if(rsIdentity->isOwnId(post.mMeta.mAuthorId))
ui->idChooser->setChosenId(post.mMeta.mAuthorId);
}, this );
});
}
PostedCreatePostDialog::~PostedCreatePostDialog()
@ -160,42 +223,46 @@ void PostedCreatePostDialog::createPost()
return;
}//switch (ui->idChooser->getChosenId(authorId))
RsPostedPost post;
post.mMeta.mGroupId = mGrpId;
post.mLink = std::string(ui->linkEdit->text().toUtf8());
std::string link = std::string(ui->linkEdit->text().toUtf8());
std::string notes;
if(!ui->RichTextEditWidget->toPlainText().trimmed().isEmpty()) {
QString text;
text = ui->RichTextEditWidget->toHtml();
post.mNotes = std::string(text.toUtf8());
notes = std::string(text.toUtf8());
}
post.mMeta.mAuthorId = authorId;
post.mMeta.mMsgName = std::string(ui->titleEdit->text().toUtf8());
std::string title = std::string(ui->titleEdit->text().toUtf8());
RsGxsImage image;
if(imagebytes.size() > 0)
{
// send posted image
post.mImage.copy((uint8_t *) imagebytes.data(), imagebytes.size());
}
image.copy((uint8_t *) imagebytes.data(), imagebytes.size());
}
int msgsize = post.mLink.length() + post.mMeta.mMsgName.length() + post.mNotes.length() + imagebytes.size();
int msgsize = link.length() + title.length() + notes.length() + imagebytes.size();
if(msgsize > MAXMESSAGESIZE) {
QString errormessage = QString(tr("Message is too large.<br />actual size: %1 bytes, maximum size: %2 bytes.")).arg(msgsize).arg(MAXMESSAGESIZE);
QMessageBox::warning(this, "RetroShare", errormessage, QMessageBox::Ok, QMessageBox::Ok);
return;
}
RsThread::async([this,post]()
RsGxsGroupId grpId = mGrpId;
RsGxsMessageId origPostId = mOrigPostId;
RsThread::async([this,grpId,title,link,notes,authorId,image,origPostId]()
{
RsGxsMessageId post_id;
std::string error_message;
bool res = rsPosted->createPost(post,post_id);
bool res = rsPosted->createPostV2(grpId,title,RsUrl(link),notes,authorId,image,post_id,error_message,origPostId);
RsQThreadUtils::postToObject( [res,this]()
RsQThreadUtils::postToObject( [res,error_message,this]()
{
if(!res)
QMessageBox::information(nullptr,tr("Error while creating post"),tr("An error occurred while creating the post."));
QMessageBox::information(nullptr,tr("Error while creating post"),
tr("An error occurred while creating the post: ") + QString::fromUtf8(error_message.c_str()));
accept();
}, this );

View File

@ -37,8 +37,12 @@ public:
/*!
* @param tokenQ parent callee token
* @param posted
* @param existingPostId When non null, the dialog edits the given
* already existing post instead of creating a new one. Editing
* a post requires either being its original author, or being
* the board admin.
*/
explicit PostedCreatePostDialog(RsPosted* posted, const RsGxsGroupId& grpId, const RsGxsId& default_author=RsGxsId(),QWidget *parent = 0);
explicit PostedCreatePostDialog(RsPosted* posted, const RsGxsGroupId& grpId, const RsGxsId& default_author=RsGxsId(), const RsGxsMessageId& existingPostId=RsGxsMessageId(), QWidget *parent = 0);
~PostedCreatePostDialog();
void setTitle(const QString& title);
@ -62,12 +66,14 @@ private slots:
void setPage(int viewMode);
private:
void processSettings(bool load);
void loadExistingPost();
int viewMode();
QString mLink;
QString mNotes;
RsPosted* mPosted;
RsGxsGroupId mGrpId;
RsGxsMessageId mOrigPostId;
Ui::PostedCreatePostDialog *ui;
};

View File

@ -366,12 +366,26 @@ void PostedListWidgetWithModel::postContextMenu(const QPoint& point)
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_AUTHOR), tr("Show author in People tab"), this, SLOT(showAuthorInPeople()))->setData(index);
#ifdef TODO
// This feature is not implemented yet in libretroshare.
// Own posts can always be edited by their author. Board admins can also
// edit any post, similarly to what is already done for forums.
if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags))
menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost()));
#endif
bool can_edit = rsIdentity->isOwnId(post.mMeta.mAuthorId)
|| IS_GROUP_ADMIN(mGroup.mMeta.mSubscribeFlags);
if(can_edit)
menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost()))->setData(index);
// Sticking ("pinning") a post to the top of the feed is an admin-only
// moderation action, similarly to what is already done for forums.
if(IS_GROUP_ADMIN(mGroup.mMeta.mSubscribeFlags))
{
bool is_pinned = mGroup.mPinnedPosts.ids.find(post.mMeta.mMsgId) != mGroup.mPinnedPosts.ids.end();
menu.addAction( FilesDefs::getIconFromQtResourcePath(":/images/pin32.png"),
is_pinned ? tr("Un-pin this post") : tr("Pin this post up"),
this, SLOT(togglePinPost()))->setData(index);
}
menu.exec(QCursor::pos());
}
@ -537,16 +551,48 @@ void PostedListWidgetWithModel::copyMessageLink()
}
}
#ifdef TODO
void PostedListWidgetWithModel::editPost()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
RsPostedPost post = index.data(Qt::UserRole).value<RsPostedPost>() ;
CreatePostedMsg *msgDialog = new CreatePostedMsg(post.mMeta.mGroupId,post.mMeta.mMsgId);
if(!index.isValid())
return;
RsPostedPost post = index.data(Qt::UserRole).value<RsPostedPost>() ;
if(post.mMeta.mMsgId.isNull())
return;
PostedCreatePostDialog *msgDialog = new PostedCreatePostDialog(rsPosted,post.mMeta.mGroupId,post.mMeta.mAuthorId,post.mMeta.mMsgId);
msgDialog->show();
/* window will destroy itself! */
}
void PostedListWidgetWithModel::togglePinPost()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
if(!index.isValid())
return;
RsPostedPost post = index.data(Qt::UserRole).value<RsPostedPost>() ;
if(post.mMeta.mMsgId.isNull())
return;
if(mGroup.mPinnedPosts.ids.find(post.mMeta.mMsgId) == mGroup.mPinnedPosts.ids.end())
mGroup.mPinnedPosts.ids.insert(post.mMeta.mMsgId);
else
mGroup.mPinnedPosts.ids.erase(post.mMeta.mMsgId);
uint32_t token;
rsPosted->updateGroup(token,mGroup);
// The actual model/view update will be triggered by libretroshare using
// the rsEvent system once the board data is updated (RsPostedEventCode
// ::PINNED_POSTS_CHANGED / UPDATED_POSTED_GROUP).
}
#endif
void PostedListWidgetWithModel::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{
@ -576,6 +622,7 @@ void PostedListWidgetWithModel::handleEvent_main_thread(std::shared_ptr<const Rs
case RsPostedEventCode::UPDATED_POSTED_GROUP: [[fallthrough]];
case RsPostedEventCode::UPDATED_MESSAGE: [[fallthrough]];
case RsPostedEventCode::BOARD_DELETED: [[fallthrough]];
case RsPostedEventCode::PINNED_POSTS_CHANGED: [[fallthrough]];
case RsPostedEventCode::SYNC_PARAMETERS_UPDATED:
{
if(e->mPostedGroupId == groupId())

View File

@ -137,6 +137,8 @@ private slots:
void showPostDetails();
#endif
void postContextMenu(const QPoint&);
void editPost();
void togglePinPost();
void showAuthorInPeople();
void tabCloseRequested(int index);
void updateSorting(int);

View File

@ -480,10 +480,21 @@ class PostSorter
{
public:
PostSorter(RsPostedPostsModel::SortingStrategy s) : mSortingStrategy(s) {}
PostSorter(RsPostedPostsModel::SortingStrategy s, const RsTlvGxsMsgIdSet& pinned_posts)
: mSortingStrategy(s), mPinnedPosts(pinned_posts) {}
bool operator()(const RsPostedPost& p1,const RsPostedPost& p2) const
{
// Pinned ("stuck") posts are always shown on top of the feed,
// regardless of the currently selected sorting strategy. This
// mirrors the behaviour already implemented for forums.
bool p1_pinned = mPinnedPosts.ids.find(p1.mMeta.mMsgId) != mPinnedPosts.ids.end();
bool p2_pinned = mPinnedPosts.ids.find(p2.mMeta.mMsgId) != mPinnedPosts.ids.end();
if(p1_pinned != p2_pinned)
return p1_pinned;
switch(mSortingStrategy)
{
default:
@ -495,6 +506,7 @@ public:
private:
RsPostedPostsModel::SortingStrategy mSortingStrategy;
const RsTlvGxsMsgIdSet& mPinnedPosts;
};
Qt::ItemFlags RsPostedPostsModel::flags(const QModelIndex& index) const
@ -510,7 +522,7 @@ void RsPostedPostsModel::setSortingStrategy(RsPostedPostsModel::SortingStrategy
preMods();
mSortingStrategy = s;
std::sort(mPosts.begin(),mPosts.end(), PostSorter(s));
std::sort(mPosts.begin(),mPosts.end(), PostSorter(s,mPostedGroup.mPinnedPosts));
postMods();
}
@ -564,7 +576,7 @@ void RsPostedPostsModel::setPosts(const RsPostedGroup& group, std::vector<RsPost
createPostsArray(posts);
std::sort(mPosts.begin(),mPosts.end(), PostSorter(mSortingStrategy));
std::sort(mPosts.begin(),mPosts.end(), PostSorter(mSortingStrategy,mPostedGroup.mPinnedPosts));
uint32_t tmpval;
setFilter(QStringList(),tmpval);