Упрощение функции handle() в main_input.rs путём извлечения
обработчиков режимов в отдельные функции.
Извлечённые функции:
- handle_profile_mode() - режим профиля с модалкой выхода (~114 строк)
- handle_message_search_mode() - поиск по сообщениям (~73 строки)
- handle_pinned_mode() - закреплённые сообщения (~34 строки)
- handle_reaction_picker_mode() - выбор реакции (~79 строк)
- handle_delete_confirmation() - подтверждение удаления (~53 строки)
- handle_forward_mode() - выбор чата для пересылки (~48 строк)
- handle_chat_search_mode() - поиск по чатам (~32 строки)
- handle_escape_key() - обработка Esc (~25 строк)
- handle_message_selection() - выбор сообщения (~85 строк)
Итого извлечено: ~543 строки из основной функции handle()
Результат:
- handle() сократилась с 891 до ~350 строк (на 61%)
- Каждый режим теперь изолирован и легко тестируется
- Улучшена читаемость и maintainability кода
- Все тесты проходят успешно
Также:
- Обновлён tdlib-rs с 1.1 на 1.2.0
Added 13 integration tests for keyboard navigation:
Arrow Keys Navigation:
- test_arrow_navigation_in_chat_list: Up/Down arrows, circular wrapping
- test_vim_navigation_in_chat_list: j/k vim-style navigation
- test_russian_keyboard_navigation: Russian layout (о/р) support
- test_enter_opens_chat: Enter to open selected chat
- test_esc_closes_chat: Esc to close open chat
Cursor Navigation in Input:
- test_cursor_navigation_in_input: Left/Right arrow keys
- test_home_end_in_input: Home/End keys
- test_backspace_with_cursor: Backspace at different positions
- test_insert_char_at_cursor_position: Insert char in middle
Message Navigation:
- test_up_arrow_selects_last_message_when_input_empty: Up arrow for message selection
Additional:
- test_circular_navigation_optional: Circular list navigation
These tests verify that the navigation functionality works correctly
through the main_input handler, protecting against future refactoring
that might break keyboard input.
All tests compile and verify actual input handling via handle_main_input().
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Verified that P4.14 - Async/await consistency is already implemented.
**Verification Results**:
✅ No blocking calls in async context:
- std::fs used only in Config::load() at startup (before async runtime)
- std::thread::sleep not found anywhere
✅ Async-friendly APIs used correctly:
- tokio::time::sleep in messages.rs for delays
- tokio::time::timeout in auth.rs, main_input.rs, main.rs
- All async operations are non-blocking
✅ Config operations properly organized:
- Config::load() called once in main.rs:36 BEFORE async runtime
- Synchronous initialization, not a problem
**Conclusion**: Code is already async-clean! No blocking operations in async
context. P4.14 was already completed during development.
**Progress**:
- Priority 4: 4/4 tasks COMPLETE! 🎉🎉🎉
- Total: 16/17 tasks complete (94%)
**Remaining**:
- Priority 5: Feature flags, LRU generics, CLI args (optional improvements)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added 9 unit tests for src/utils.rs to cover time/date formatting logic.
**Tests added**:
- ✅ format_timestamp_with_tz with positive offset (+03:00)
- ✅ format_timestamp_with_tz with negative offset (-05:00)
- ✅ format_timestamp_with_tz with zero offset (UTC)
- ✅ format_timestamp_with_tz midnight wrap (23:00 + 2h = 01:00)
- ✅ format_timestamp_with_tz invalid timezone (fallback to +03:00)
- ✅ get_day - extract day from timestamp
- ✅ get_day_grouping - messages on same day
- ✅ format_datetime - full date and time with MSK
- ✅ parse_timezone_offset via public API
**Coverage**:
- format_timestamp_with_tz() - all edge cases
- parse_timezone_offset() - tested indirectly (private fn)
- get_day() - day calculation and grouping
- format_datetime() - partial coverage
**Result**: 54 unit tests pass (was 45, +9 new)
Related: REFACTORING_ROADMAP.md P4.11
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added 15 tests for config validation to ensure invalid values are caught.
**What's tested**:
- ✅ Valid default config
- ✅ Timezone validation (must start with + or -)
- ✅ Valid positive/negative timezones (+09:00, -05:00)
- ✅ Invalid timezone without sign
- ✅ Color validation (all 18 standard ratatui colors)
- ✅ Invalid colors (rainbow, purple, pink)
- ✅ Case-insensitive color parsing (RED, Green, YELLOW)
- ✅ parse_color() for all variants (standard, light, gray/grey)
- ✅ Fallback to White for invalid colors
**Coverage**:
- Config::validate() - timezone and color checks
- Config::parse_color() - all color parsing logic
- All 23 config tests pass (8 existing + 15 new)
**Note**: Validation was already implemented in config.rs:344-389
and called in load():450-456. This PR adds comprehensive test coverage.
Related: REFACTORING_ROADMAP.md P4.13
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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>
When editing a message that has a reply, convert_message() creates
a new ReplyInfo with sender_name="Unknown" and text="...". This was
causing the reply info to be lost after editing.
Solution: Save the old reply_to from the original message before
replacement, and restore it if the new message has "Unknown" reply info.
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.
- Fix key_matches() to use chars().count() instead of len()
- This correctly handles multi-byte UTF-8 characters (russian layout)
- All 9 config tests now pass (was 7/9, now 9/9)
Fixes:
- test_hotkeys_matches_char_keys
- test_hotkeys_matches_russian_vim_keys
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Изменён порядок хранения сообщений (теперь от старых к новым)
- Исправлена логика выбора сообщений для редактирования
- Исправлена отправка 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>