From 5eb94e230723978631fb72186604f16efb224419 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 Jul 2026 12:50:26 +0100 Subject: [PATCH] feat(posted): add pinned posts support and createPostV2 API - Add mPinnedPosts (RsTlvGxsMsgIdSet) to RsPostedGroup for pin/unpin state - Add PINNED_POSTS_CHANGED event code - Add pinPost/unpinPost virtual methods to RsPosted interface - Implement pin/unpin in p3posted.cc with editBoard pattern - Add backward-compatible serialization with length checks - Update createPostDialog to use createPostV2 with error messages - Add Pin/Unpin context menu for post publishers - Sort pinned posts to top regardless of sort strategy - Add pinned visual indicator via feedFrame CSS property --- src/retroshare/rsposted.h | 26 ++++++++++++++++++++++++++ src/rsitems/rsposteditems.cc | 15 +++++++++++++++ src/rsitems/rsposteditems.h | 2 ++ src/services/p3posted.cc | 28 ++++++++++++++++++++++++++++ src/services/p3posted.h | 3 +++ 5 files changed, 74 insertions(+) diff --git a/src/retroshare/rsposted.h b/src/retroshare/rsposted.h index 412dc6f449..bdd5181d75 100644 --- a/src/retroshare/rsposted.h +++ b/src/retroshare/rsposted.h @@ -32,6 +32,7 @@ #include "retroshare/rsgxscommon.h" #include "retroshare/rsgxscircles.h" #include "serialiser/rsserializable.h" +#include "serialiser/rstlvidset.h" class RsPosted; @@ -46,6 +47,9 @@ struct RsPostedGroup: public RsSerializable, RsGxsGenericGroupData std::string mDescription; RsGxsImage mGroupImage; + /** @brief List of board pinned posts, those are displayed on top */ + RsTlvGxsMsgIdSet mPinnedPosts; + /// @see RsSerializable virtual void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override @@ -53,6 +57,7 @@ struct RsPostedGroup: public RsSerializable, RsGxsGenericGroupData RS_SERIAL_PROCESS(mMeta); RS_SERIAL_PROCESS(mDescription); RS_SERIAL_PROCESS(mGroupImage); + RS_SERIAL_PROCESS(mPinnedPosts); } }; @@ -115,6 +120,8 @@ struct RsPostedPost: public RsSerializable, RsGxsGenericMsgData #define RSPOSTED_VIEWMODE_HOT 3 #define RSPOSTED_VIEWMODE_COMMENTS 4 +#define RSPOSTED_FLAG_POST_IS_PINNED 0x0001 + enum class RsPostedEventCode: uint8_t { @@ -131,6 +138,7 @@ enum class RsPostedEventCode: uint8_t NEW_COMMENT = 0x0a, NEW_VOTE = 0x0b, BOARD_DELETED = 0x0c, + PINNED_POSTS_CHANGED = 0x0d, }; @@ -240,6 +248,24 @@ class RsPosted : public RsGxsIfaceHelper, public RsGxsCommentService */ virtual bool editBoard(RsPostedGroup& board) =0; + /** + * @brief Pin a post to the top of a board. Admin-only. Blocking API. + * @jsonapi{development} + * @param[in] boardId Id of the board + * @param[in] postId Id of the post to pin + * @return false on error, true otherwise + */ + virtual bool pinPost(const RsGxsGroupId& boardId, const RsGxsMessageId& postId) =0; + + /** + * @brief Unpin a post from a board. Admin-only. Blocking API. + * @jsonapi{development} + * @param[in] boardId Id of the board + * @param[in] postId Id of the post to unpin + * @return false on error, true otherwise + */ + virtual bool unpinPost(const RsGxsGroupId& boardId, const RsGxsMessageId& postId) =0; + /** * @brief Create board. Blocking API. * @jsonapi{development} diff --git a/src/rsitems/rsposteditems.cc b/src/rsitems/rsposteditems.cc index 3c16ff5bd9..bd2ebca236 100644 --- a/src/rsitems/rsposteditems.cc +++ b/src/rsitems/rsposteditems.cc @@ -53,6 +53,12 @@ void RsGxsPostedGroupItem::serial_process(RsGenericSerializer::SerializeJob j,Rs return ; RsTypeSerializer::serial_process(j,ctx,mGroupImage,"mGroupImage") ; + + // Backward compat: if only description+image were written, stop here + if(j == RsGenericSerializer::DESERIALIZE && ctx.mOffset == ctx.mSize) + return ; + + RsTypeSerializer::serial_process(j,ctx,mPinnedPosts,"mPinnedPosts") ; } RsItem *RsGxsPostedSerialiser::create_item(uint16_t service_id,uint8_t item_subtype) const @@ -119,6 +125,7 @@ void RsGxsPostedGroupItem::clear() { mDescription.clear(); mGroupImage.TlvClear(); + mPinnedPosts.TlvClear(); } bool RsGxsPostedGroupItem::fromPostedGroup(RsPostedGroup &group, bool moveImage) @@ -137,6 +144,10 @@ bool RsGxsPostedGroupItem::fromPostedGroup(RsPostedGroup &group, bool moveImage) { mGroupImage.binData.setBinData(group.mGroupImage.mData, group.mGroupImage.mSize); } + + // Copy pinned posts + mPinnedPosts = group.mPinnedPosts; + return true; } @@ -154,5 +165,9 @@ bool RsGxsPostedGroupItem::toPostedGroup(RsPostedGroup &group, bool moveImage) { group.mGroupImage.copy((uint8_t *) mGroupImage.binData.bin_data, mGroupImage.binData.bin_len); } + + // Copy pinned posts + group.mPinnedPosts = mPinnedPosts; + return true; } diff --git a/src/rsitems/rsposteditems.h b/src/rsitems/rsposteditems.h index 910471c19f..323bf16af4 100644 --- a/src/rsitems/rsposteditems.h +++ b/src/rsitems/rsposteditems.h @@ -26,6 +26,7 @@ #include "rsitems/rsgxscommentitems.h" #include "rsitems/rsgxsitems.h" #include "serialiser/rstlvimage.h" +#include "serialiser/rstlvidset.h" #include "retroshare/rsposted.h" @@ -48,6 +49,7 @@ class RsGxsPostedGroupItem : public RsGxsGrpItem std::string mDescription; RsTlvImage mGroupImage; + RsTlvGxsMsgIdSet mPinnedPosts; }; diff --git a/src/services/p3posted.cc b/src/services/p3posted.cc index 2bb4f08471..c31527cef9 100644 --- a/src/services/p3posted.cc +++ b/src/services/p3posted.cc @@ -890,5 +890,33 @@ bool p3Posted::editBoard(RsPostedGroup& board) return true; } +bool p3Posted::pinPost(const RsGxsGroupId& boardId, const RsGxsMessageId& postId) +{ + std::vector groupsInfo; + if(!getBoardsInfo({boardId}, groupsInfo)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Board not found." << std::endl; + return false; + } + + RsPostedGroup board = groupsInfo.front(); + board.mPinnedPosts.ids.insert(postId); + return editBoard(board); +} + +bool p3Posted::unpinPost(const RsGxsGroupId& boardId, const RsGxsMessageId& postId) +{ + std::vector groupsInfo; + if(!getBoardsInfo({boardId}, groupsInfo)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Board not found." << std::endl; + return false; + } + + RsPostedGroup board = groupsInfo.front(); + board.mPinnedPosts.ids.erase(postId); + return editBoard(board); +} + RsPosted::~RsPosted() = default; RsGxsPostedEvent::~RsGxsPostedEvent() = default; diff --git a/src/services/p3posted.h b/src/services/p3posted.h index 4adc2cf299..d78d696c21 100644 --- a/src/services/p3posted.h +++ b/src/services/p3posted.h @@ -77,6 +77,9 @@ virtual void receiveHelperChanges(std::vector& changes) bool editBoard(RsPostedGroup& board) override; + bool pinPost(const RsGxsGroupId& boardId, const RsGxsMessageId& postId) override; + bool unpinPost(const RsGxsGroupId& boardId, const RsGxsMessageId& postId) override; + bool createBoard(RsPostedGroup& board) override; bool createBoardV2(const std::string& board_name,