# How Forum Topics Work in Telegram Desktop Channels: Data::Forum and Data::ForumTopic Architecture

> Explore how Telegram Desktop channels use Data::Forum and Data::ForumTopic to manage forum topics. Learn about their architecture for loading, caching, and UI integration.

- Repository: [Telegram Desktop/tdesktop](https://github.com/telegramdesktop/tdesktop)
- Tags: internals
- Published: 2026-04-05

---

**Telegram Desktop implements channel forum topics through the `Data::Forum` container class that manages topic loading and caching, and the `Data::ForumTopic` thread-derived class that handles individual topic state, unread counters, and UI integration.**

Telegram Desktop organizes channel forum topics using a specialized data layer centered around two core classes in the `Data` namespace. Understanding how forum topics work in channels reveals the complete lifecycle of topic management—from server synchronization to chat list rendering.

## Core Data Architecture

The implementation relies on two primary classes defined in [`Telegram/SourceFiles/data/data_forum.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_forum.h) and [`Telegram/SourceFiles/data/data_forum_topic.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_forum_topic.h):

- **`Data::Forum`**: Acts as the container and manager for all topics within a channel. It handles network requests, maintains the topic cache, and coordinates lifecycle events.
- **`Data::ForumTopic`**: Represents a single topic thread, storing metadata such as title, icon, color, creator information, and unread state.

Both classes inherit from the generic `Thread` hierarchy (see [`data_thread.h`](https://github.com/telegramdesktop/tdesktop/blob/main/data_thread.h)), which allows `ForumTopic` to utilize the same interface as regular chats for features like `chatListMessage()` and `unreadStateFor()` while adding forum-specific functionality.

## Loading and Caching Forum Topics

When a user opens a channel with topics enabled, the system initiates a structured loading sequence in `Forum::preloadTopics()` defined in [`Telegram/SourceFiles/data/data_forum.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_forum.cpp).

The process follows three steps:

1. **API Request**: The client sends `MTPmessages_GetForumTopics` to the Telegram servers.
2. **Response Processing**: `Forum::applyReceivedTopics()` iterates through the `MTPVector<MTPForumTopic>` payload, calling `applyTopicAdded()` for each entry.
3. **Object Creation**: New topics are instantiated and stored in `_topics`, a `base::flat_map<MsgId, std::unique_ptr<ForumTopic>>`. Deleted topics are tracked separately in `_topicsDeleted`.

The topic list UI updates automatically because each `ForumTopic` registers with `Dialogs::MainList _topicsList` during construction via `_list(_forum->topicsList())`.

## Topic Lifecycle Operations

The `Data::Forum` and `Data::ForumTopic` classes expose specific methods for managing topic state transitions:

**Creating Topics**

Client-side topic creation begins with `Forum::reserveCreatingId()`, which allocates a temporary root ID. The `ForumTopic::creating()` method initializes the temporary object, and `ForumTopic::setRealRootId()` updates it once the server confirms creation. If cancelled, `ForumTopic::discard()` removes the temporary entry.

**Editing Metadata**

Title changes invoke `ForumTopic::applyTitle()`, while icon and color updates use `applyIconId()` and `applyColorId()`. These methods trigger UI refreshes via `invalidateTitleWithIcon()` and `session().changes().topicUpdated(this, UpdateFlag::…)`. The editing UI is implemented in [`Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h).

**Closing and Opening**

The `ForumTopic::setClosedAndSave(bool)` method sends `messages.EditForumTopic` to the server. The closed state is stored in `Flag::Closed` and exposed through `canToggleClosed()` for UI validation.

**Deleting Topics**

When deletion occurs, `Forum::applyTopicDeleted()` removes the entry from `_topics` and fires the `_topicDestroyed` event to notify subscribers.

**Marking as Read**

Unread state management delegates to `RepliesList::readTill()` through `ForumTopic::readTillEnd()`, with counters propagated to the chat list via `chatListBadgesState()`.

## Chat List Integration and Unread Counters

`Data::ForumTopic` implements virtual methods from the `Thread` base class to integrate with Telegram Desktop's chat list system:

- `chatListMessage()` and `chatListMessageKnown()` return the last visible message for preview.
- `chatListUnreadState()` constructs a `Dialogs::UnreadState` using `_replies->displayedUnreadCount()`.
- `chatListBadgesState()` handles special cases like "empty inbox" topics (lines 215-225 of [`data_forum_topic.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/data_forum_topic.cpp)).

The `ForumTopic::subscribeToUnreadChanges()` method connects `RepliesList` updates to the chat list entry and invalidates the recent topics cache via `_forum->recentTopicsInvalidate(this)`.

## UI Rendering and Icon Handling

**Userpic Rendering**

The `ForumTopic::paintUserpic()` method (lines 381-425 of [`data_forum_topic.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/data_forum_topic.cpp)) handles visual representation:

- Custom emoji icons (`_icon`) render using `Ui::Text::LimitedLoopsEmoji`.
- Standard topics display a colored square with the first non-emoji letter via `ForumTopicIconFrame`.
- General topics use `ForumTopicGeneralIconFrame`.

**Title and Icon Composition**

The `ForumTopic::titleWithIcon()` method returns a `TextWithEntities` object constructed by `ForumTopicIconWithTitle()` (lines 665-672), combining the topic title with its icon representation.

**Emoji Encoding**

Topics store icons as custom emoji entities (`topic_icon:` or `topic_general:`). The `TopicIconEmojiEntity()` function builds these strings (lines 201-207), while `ParseTopicIconEmojiEntity()` parses them back into `TopicIconDescriptor` structures (lines 210-229). Color mappings are handled in [`Telegram/SourceFiles/data/data_forum_icons.h`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_forum_icons.h).

## Practical Implementation Examples

### Enumerating All Topics in a Channel

To iterate through existing topics in a forum channel:

```cpp
// Assume we have a not_null<ChannelData*> channel
auto *forum = channel->forum();          // Data::Forum*
forum->enumerateTopics([](not_null<Data::ForumTopic*> topic) {
    qDebug() << "Topic:" << topic->title()
             << "ID:" << topic->rootId()
             << "Closed:" << topic->closed();
});

```

This utilizes `ChannelData::forum()` and `Forum::enumerateTopics()` defined in [`data_forum.h`](https://github.com/telegramdesktop/tdesktop/blob/main/data_forum.h).

### Creating a New Topic

Client-side topic creation requires reserving a temporary ID before server confirmation:

```cpp
auto *forum = channel->forum();
MsgId tempId = forum->reserveCreatingId(u"New Topic"_q, /*colorId=*/0x6FB9F0,
                                         /*iconId=*/0);
auto *topic = forum->enforceTopicFor(tempId);   // Returns the freshly created ForumTopic
topic->applyTitle(u"New Topic"_q);
topic->applyColorId(0x6FB9F0);
topic->setClosedAndSave(false);                // Sends EditForumTopic to the server

```

Key methods include `Forum::reserveCreatingId()`, `Forum::enforceTopicFor()`, and `ForumTopic::setClosedAndSave()`.

### Closing an Existing Topic

To programmatically close a topic after verifying permissions:

```cpp
auto *topic = forum->topicFor(existingRootId);
if (topic && topic->canToggleClosed()) {
    topic->setClosedAndSave(true);   // Close and sync with the server
}

```

The `canToggleClosed()` method validates the action before `setClosedAndSave()` transmits the update.

### Reacting to Unread Count Changes

Subscribe to unread changes for custom UI badges:

```cpp
auto *topic = forum->topicFor(rootId);
topic->replies()->unreadCountValue() |
    rpl::filter([](std::optional<int> v) { return v && *v > 0; }) |
    rpl::start_with_next([=](std::optional<int>) {
        // Show a custom badge in the UI
        showBadgeForTopic(topic->rootId());
    }, topic->lifetime());

```

While `ForumTopic::subscribeToUnreadChanges()` automatically updates the chat list, this demonstrates manual subscription to `RepliesList::unreadCountValue()`.

## Summary

- **Data::Forum** manages the collection of topics in a channel, handling loading via `MTPmessages_GetForumTopics` and caching in `_topics`.
- **Data::ForumTopic** inherits from `Thread`, enabling seamless chat list integration while adding forum-specific properties like icons and closure states.
- Topic creation uses temporary IDs via `reserveCreatingId()` until server confirmation updates the real root ID.
- Unread counters flow from `RepliesList` through `chatListBadgesState()` to update the UI reactively.
- Custom icons are encoded as emoji entities and rendered through `paintUserpic()` using specialized frames or limited-loop emoji rendering.

## Frequently Asked Questions

### How does Telegram Desktop load forum topics when opening a channel?

When a channel opens, `Forum::preloadTopics()` sends `MTPmessages_GetForumTopics` to the server. The response handler `Forum::applyReceivedTopics()` iterates through the returned vector, calling `applyTopicAdded()` for each topic to create or update `ForumTopic` objects stored in the `_topics` map.

### What is the relationship between Data::ForumTopic and the Thread class?

`Data::ForumTopic` inherits from the `Thread` base class (defined in [`data_thread.h`](https://github.com/telegramdesktop/tdesktop/blob/main/data_thread.h)), which allows it to implement standard chat interfaces like `chatListMessage()` and `unreadStateFor()`. This inheritance enables topics to appear in the chat list alongside regular conversations while maintaining specialized forum behavior.

### How are custom emoji icons stored and rendered for forum topics?

Topic icons are stored as custom emoji entities using `TopicIconEmojiEntity()` which generates strings like `topic_icon:`. The `ForumTopic::paintUserpic()` method renders these using `Ui::Text::LimitedLoopsEmoji` for custom emojis, or falls back to colored letter frames for standard topics.

### What happens when a user deletes a forum topic?

Deletion triggers `Forum::applyTopicDeleted()`, which removes the topic from the `_topics` map and fires the `_topicDestroyed` event. This signals all UI components to remove the topic from the chat list and clean up associated resources, ensuring reactive updates across the application.