# Анализ: Загрузка и встраивание опросов (polls) в DOM
## 🔍 Текущая архитектура
### Backend: Сериализация опросов
**Файл**: `backend/communications/consumers.py`
```python
def serialize_message(m: Message) -> dict:
# ...
# Голосование
if hasattr(m, 'poll'):
poll = m.poll
poll_data = {
"id": poll.id,
"question": poll.question,
"is_anonymous": poll.is_anonymous,
"is_multiple_choice": poll.is_multiple_choice,
"is_quiz": poll.is_quiz,
"is_closed": poll.is_closed,
"closes_at": poll.closes_at.isoformat() if poll.closes_at else None,
"total_voters": poll.total_voters,
"options": []
}
for option in poll.options.all():
poll_data["options"].append({
"id": option.id,
"text": option.text,
"position": option.position,
"vote_count": option.vote_count,
"percentage": 0 # Будет пересчитан на клиенте
})
data["poll"] = poll_data
```
✅ **Опросы загружаются** в payload сообщения.
---
### Frontend: Генерация HTML
#### ❌ ПРОБЛЕМА: MessageRenderer НЕ генерировал HTML для опросов
**Файл**: `backend/static/js/components/messageRenderer.js`
**ДО исправления:**
```javascript
buildMessageHtml(msg, isOwn) {
const attachmentsHtml = msg.attachments?.length ?
msg.attachments.map(att => this.buildAttachmentHtml(att)).join('') : '';
// Опросы НЕ рендерились!
return `
${msg.content}
${attachmentsHtml}
`;
}
```
**ПОСЛЕ исправления:**
```javascript
buildMessageHtml(msg, isOwn) {
const attachmentsHtml = msg.attachments?.length ?
msg.attachments.map(att => this.buildAttachmentHtml(att)).join('') : '';
// ✅ Добавлена генерация HTML для опроса
const pollHtml = msg.poll ? this.buildPollHtml(msg.poll) : '';
return `
${msg.content}
${attachmentsHtml}
${pollHtml} ← добавлено!
`;
}
// ✅ Новый метод
buildPollHtml(poll) {
if (!poll || !poll.id) return '';
const totalVotes = poll.total_voters || 0;
const isClosed = poll.is_closed || false;
return `
`;
}
```
---
### Frontend: Инициализация опросов
**Файл**: `backend/static/js/components/chatPoll.js`
```javascript
class ChatPoll {
initializeExistingPolls() {
const pollWidgets = document.querySelectorAll('.poll-widget[data-poll-id]');
pollWidgets.forEach((widget) => {
const pollId = widget.dataset.pollId;
this.refreshPoll(pollId, widget);
});
}
async refreshPoll(pollId, pollWidget = null) {
const widget = pollWidget || document.querySelector(`[data-poll-id="${pollId}"]`);
if (!widget) return;
// Загружает актуальные данные с сервера
const results = await this.fetchPollResults(pollId);
if (!results) return;
// Обновляет HTML опроса (результаты, проценты и т.д.)
this.updatePollWidget(widget, results);
}
}
```
✅ `ChatPoll` обрабатывает существующие опросы через `initializeExistingPolls()`.
---
### Frontend: Реинициализация после редактирования
**Файл**: `backend/static/js/components/messageEditing.js`
**ДО исправления:**
```javascript
function reinitMessageComponents(messageEl, messageId, currentUserId) {
// 4. Голосования (если есть)
const pollEl = messageEl.querySelector('[data-poll-id]');
if (pollEl && window.chatPoll) {
const pollId = pollEl.dataset.pollId;
// ❌ Только логирование, НЕ инициализация!
console.log('[MessageEditing] ✓ Poll detected, id:', pollId);
}
}
```
**ПОСЛЕ исправления:**
```javascript
function reinitMessageComponents(messageEl, messageId, currentUserId) {
// 4. Голосования (если есть)
const pollEl = messageEl.querySelector('[data-poll-id]');
if (pollEl && window.chatPoll) {
const pollId = pollEl.dataset.pollId;
console.log('[MessageEditing] Refreshing poll:', pollId);
// ✅ Явно вызываем refreshPoll для обновления состояния
window.chatPoll.refreshPoll(pollId, pollEl);
console.log('[MessageEditing] ✓ Poll reinitialized, id:', pollId);
}
}
```
---
## 🔄 Процесс загрузки и встраивания
### 1. Первоначальная загрузка сообщений
```
User opens chat
↓
Django renders chat_detail.html
↓
JavaScript загружает начальные сообщения через API
↓
Для каждого сообщения:
├─ MessageRenderer.buildMessageHtml(msg) создаёт HTML
│ ├─ msg.content → текст
│ ├─ msg.attachments → вложения
│ ├─ msg.reply_to → ответ на сообщение
│ └─ msg.poll → ТЕПЕРЬ ГЕНЕРИРУЕТСЯ! ✅
│ └─ buildPollHtml(poll) →