Group photos with shared media_album_id into single album bubbles with
grid layout (up to 3x cols). Album navigation treats grouped photos as
one unit (j/k skip entire album). Persist selected account to
accounts.toml so it survives app restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BUG FIX: When opening chat, only the last message was visible instead of
full history (100 messages).
Root cause:
In get_chat_history(), when TDLib returns fewer messages than requested on
first attempt (e.g., 1 message instead of 50), the retry logic would
`continue` without updating from_message_id. This caused the SAME request
to be repeated 20 times, loading the same single message repeatedly.
Code flow (BEFORE fix):
1. Request: get_chat_history(from_message_id=0, limit=50)
2. TDLib returns: 1 message (still syncing with server)
3. Check: received_count < chunk_size? YES → continue
4. Request: get_chat_history(from_message_id=0, limit=50) // SAME request!
5. Repeat 20 times...
6. Result: Only 1 message loaded
Fix:
Added `sleep(Duration::from_millis(100))` before retry to give TDLib time
to sync with server between attempts. This prevents infinite retry loop and
allows TDLib to actually load more messages.
Code flow (AFTER fix):
1. Request: get_chat_history(from_message_id=0, limit=50)
2. TDLib returns: 1 message
3. Check: received_count < chunk_size? YES → sleep 100ms + continue
4. Request: get_chat_history(from_message_id=0, limit=50)
5. TDLib returns: 50 messages (had time to sync)
6. Result: Full history loaded
Also added missing imports:
- use tokio::time::{sleep, Duration};
Impact: Critical - users couldn't see message history when opening chats.
Related commit: 72c4a88 (which removed the sleep but didn't account for retry)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
CRITICAL BUG FIX: Three methods in TdClientTrait impl were calling themselves recursively instead of delegating to actual implementations, causing stack overflow and application panic on startup.
Fixed methods:
1. user_cache_mut() - now returns &mut self.user_cache directly
2. sync_notification_muted_chats() - now delegates to notification_manager.sync_muted_chats()
3. handle_update() - now properly delegates to TdClient::handle_update() using qualified path
This bug caused the app to hang on "Инициализация TDLib..." screen and exit raw mode, displaying escape sequences ("CB52") on key presses when user tried to interact.
Root cause: Introduced in refactoring commit bd5e5be where trait implementations were created but incorrectly delegated to self.method() instead of accessing struct fields directly or using qualified path syntax.
Also added panic hook in main.rs to ensure terminal restoration on panic for better debugging experience.
Impact: Application completely broken - couldn't start. Stack overflow on first update.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Проблема:
- При открытии чата видно только последнее сообщение
- TDLib возвращал 1 сообщение при первом запросе
- Не было retry логики для ожидания синхронизации с сервером
Решение:
1. Динамическая загрузка с retry (до 20 попыток на чанк)
2. Загрузка всей доступной истории (без лимита)
3. Retry при получении малого количества сообщений
4. Корректная чанковая загрузка по 50 сообщений
Алгоритм:
- При открытии чата: get_chat_history(i32::MAX) - загружает всё
- Чанками по 50: TDLIB_MESSAGE_LIMIT
- Retry если получено < 50 при первой загрузке
- Остановка если 3 раза подряд пусто
- Порядок: старые чанки вставляются в начало (splice)
- При скролле: load_older_messages_if_needed() подгружает автоматически
Изменения:
src/tdlib/messages.rs:
- Убрана фиксированная задержка 100ms после open_chat
- Добавлен счетчик consecutive_empty_results
- Retry логика без искусственных sleep()
- Проверка: если получено мало - продолжить попытки
src/input/main_input.rs:
- limit: 100 → i32::MAX (без ограничений)
- timeout: 10s → 30s
tests/chat_list.rs:
- test_chat_history_chunked_loading: проверка 100, 120, 200 сообщений
- test_chat_history_loads_all_without_limit: загрузка 200 без лимита
- test_load_older_messages_pagination: подгрузка при скролле
Все тесты: 104/104 ✅
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Проблема:
- get_chat_history() загружала только один чанк (50 сообщений max)
- При запросе 100 сообщений возвращалось только 50
- Экран не заполнялся полностью при открытии чата
Решение:
- Добавлена чанковая загрузка по TDLIB_MESSAGE_LIMIT (50) сообщений
- Автоматическая подгрузка пока не достигнут запрошенный limit
- Правильная сборка сообщений (старые чанки вставляются в начало)
- Retry логика для каждого чанка (до 3 попыток)
Изменения в src/tdlib/messages.rs:
- get_chat_history(): цикл загрузки чанков вместо одного запроса
- Вставка более старых чанков в начало списка (splice)
- Обработка edge cases (пустые результаты, ошибки, конец истории)
Тесты:
- test_chat_history_chunked_loading: проверка загрузки 100, 120, 200 сообщений
- Проверка правильного порядка сообщений (от старых к новым)
- Проверка границы между чанками (messages 50/51)
Все тесты пройдены: 343/343 ✅
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes two critical bugs:
1. Unread badge not clearing when opening chat - incoming messages weren't marked as viewed
2. Only last 2-3 messages loaded instead of full history due to incorrect break condition
Changes:
- Add incoming message IDs to pending_view_messages queue on chat open
- Remove premature break in get_chat_history() that stopped after 2 messages
- Add FakeTdClient.pending_view_messages field for testing
- Implement process_pending_view_messages() in FakeTdClient
Tests added:
- test_incoming_message_shows_unread_badge: verify "(1)" appears for unread
- test_opening_chat_clears_unread_badge: verify badge clears after opening
- test_opening_chat_loads_many_messages: verify all 50 messages load, not just last few
All 28 chat_list tests pass.
Fixes "Message not found" error when editing immediately after sending.
**Problem**:
When sending a message, TDLib may return a temporary ID, then send
UpdateMessageSendSucceeded with the real server ID. We weren't handling
this update, so the cache kept the old ID while the server had a different
one, causing "Message not found" errors during edits.
**Solution**:
1. Added UpdateMessageSendSucceeded handler (client.rs:801-830)
- Finds message with temporary ID
- Replaces it with new message containing real server ID
- Preserves reply_info if present
2. Added validation before editing (main_input.rs:574-589)
- Checks message exists in cache
- Better error messages with chat_id and message_id
3. Added positive ID check in start_editing_selected (mod.rs:240)
- Blocks editing messages with temporary IDs (negative)
**Test**:
- Added test_edit_immediately_after_send (edit_message.rs:156-181)
- Verifies editing works right after send_message
- All 22 edit_message tests pass
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Issue: Message edits worked on server (other users saw changes),
but local UI didn't update - edited text wasn't visible.
Root cause: Code was updating individual fields instead of replacing
the whole message, and wasn't triggering UI redraw.
Solution:
- Replace entire message with edited_msg (not individual fields)
- Set needs_redraw = true to trigger UI update
- Remove debug logging
Now edited messages immediately appear in local UI.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Log chat_id, message_id, text length before calling edit_message_text
- Log success or exact TDLib error
- This will help identify the root cause
Temporary debug commit to investigate issue.
- Изменён порядок хранения сообщений (теперь от старых к новым)
- Исправлена логика выбора сообщений для редактирования
- Исправлена отправка reply (структура условий)
- Добавлено сохранение reply_info при отправке
- Удалены отладочные логи
Fixes: сообщения теперь отображаются корректно в UI
Fixes: редактирование работает без ошибки 'Message not found'
Fixes: reply показывает превью исходного сообщения
Добавлен MessageBuilder для удобного создания MessageInfo с помощью
fluent API вместо вызова конструктора с 14 параметрами.
Изменения:
- Создан MessageBuilder в tdlib/types.rs с fluent API
- Реализованы методы:
* new(id) - создание builder с обязательным ID
* sender_name(), text(), entities(), date(), edit_date()
* outgoing(), incoming(), read(), unread(), edited()
* editable(), deletable_for_self(), deletable_for_all()
* reply_to(), forward_from(), reactions(), add_reaction()
* build() - финальное создание MessageInfo
- Обновлён convert_message() для использования builder
- Добавлен экспорт MessageBuilder в tdlib/mod.rs
- Добавлены 6 unit тестов демонстрирующих fluent API
Преимущества:
- Более читабельный код создания сообщений
- Самодокументирующийся API
- Гибкость в установке опциональных полей
- Легче добавлять новые поля в будущем
Пример использования:
```rust
let message = MessageBuilder::new(MessageId::new(123))
.sender_name("Alice")
.text("Hello, world!")
.outgoing()
.read()
.build();
```
Статус: Priority 2 ЗАВЕРШЁН 100% (5/5 задач)! 🎉
- ✅ Error enum
- ✅ Config validation
- ✅ Newtype для ID
- ✅ MessageInfo реструктуризация
- ✅ MessageBuilder pattern
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Сгруппированы 16 плоских полей MessageInfo в 4 логические структуры
для улучшения организации кода и maintainability.
Новые структуры:
- MessageMetadata: id, sender_name, date, edit_date
- MessageContent: text, entities
- MessageState: is_outgoing, is_read, can_be_edited, can_be_deleted_*
- MessageInteractions: reply_to, forward_from, reactions
Изменения:
- Добавлены 4 новые структуры в tdlib/types.rs
- Обновлена MessageInfo для использования новых структур
- Добавлен конструктор MessageInfo::new() для удобного создания
- Добавлены getter методы (id(), text(), sender_name() и др.) для удобного доступа
- Обновлены все места создания MessageInfo (convert_message)
- Обновлены все места использования (~200+ обращений):
* ui/messages.rs: рендеринг сообщений
* app/mod.rs: логика приложения
* input/main_input.rs: обработка ввода и копирование
* tdlib/client.rs: обработка updates
* Все тестовые файлы (14 файлов)
Преимущества:
- Логическая группировка данных
- Проще понимать структуру сообщения
- Легче добавлять новые поля в будущем
- Улучшенная читаемость кода
Статус: Priority 2 теперь 80% (4/5 задач)
- ✅ Error enum
- ✅ Config validation
- ✅ Newtype для ID
- ✅ MessageInfo реструктуризация
- ⏳ MessageBuilder pattern
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>