Add support for pinning topics within a stream
See original GitHub issueA popular feature of alternative chat products is being able to pin messages within a stream. We closed the original issue for this feature when we started displaying stream descriptions at the top of stream narrows. However, that implementation doesn’t cover all use cases for pinning messages, and in any case doesn’t presently let you display a large amount of content (though we do plan to change that). We should consider adding this a high priority given how many people were excited about #5200; we get additional requests about this from time to time.
Our current thinking is that the right model for this in Zulip is to allow pinning one or more topics within a stream, and then users can move messages into the pinned topic as needed. Here’s an implementation/design plan, in a reasonable commit merging order:
- We create a new
StreamTopicdatabase table with columns (realm: Realm,stream_id: ForeignKey,topic_name: str, pinned: boolean=False). We will for now only be creating rows in this table when a topic is pinned, sopinnedwill always beTruein real rows at first, but that will change as we add additional features associated with topics. - We add API code for sending StreamTopic data to clients in
/registerfor the streams the user is subscribed to. We will want an “api design” thread for how this should work. It is probably a good idea to also implementstream_topicevents to send to clients when a topic is pinned/unpinned for data synchronization; this will also allow the mobile and terminal apps to start an implementation. - We tweak
populate_dbto include pinned topics in what it generates. (Possibly just a random topic should be pinned in each of the 3 alphabetically first streams). This can happen at any time; it’s main purpose is to support testing the feature before we add API code (which we generally don’t want to do until we are happy with the UX if one were to create one). - We add UI code to sort pinned topics to the top. I expect this to involve modifying
topic_list_data.jsand in particularget_list_infoto to show pinned topics at the top of the left sidebar. I believe the following is likely to be the correct implementation:
@@ -62,6 +62,10 @@ export function get_list_info(stream_id, zoomed) {
return false;
}
+ if (is_topic_pinned) {
+ return true;
+ }
+
// We include the most recent max_topics topics,
// even if there are no unread messages.
if (idx < max_topics) {
- Add display code to render the topic in the left sidebar with an
fa-pinicon in the gutter where “resolved topic” checkboxes appear. A topic that is both resolved and pinned should show the pin in the gutter and the checkmark in the topic name. - We add a new organization permission
pinned_topics_policythat controls who can pin topics within the organization by default. (Later we’ll do something per-stream, but that’s out of scope for this issue). This setting should support all the normal policy types (including nobody/everyone) and have “Moderators” as the default value. We can merge this commit early by having the settings UI element hidden behind apage_params.development_environmentguard; so it can really happen at any time before the next bullet. - We add a new
PATCH streams/<int:stream_id>/topics/{topic_name}API endpoint for changing the settings for a topic. At present, it should just let you passpinned=true|false. - We add a new menu item to the left sidebar
\vdotsmenu for a topic with “Pin topic”, in the administrative actions section that sends requests to that endpoint. - We add Help Center documentation for the feature.
- We add the feature to marketing pages where we might want to advertise its existence.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:23
- Comments:21 (9 by maintainers)

Top Related StackOverflow Question
Our team is still working on it. We’re currently testing the changes and fixing some bugs in the backend.
Looking forward to it.