Извлечены функции из render() (Phase 5 начало):
- render_chat_header() - заголовок чата с typing status (~50 строк)
- render_pinned_bar() - панель закреплённого сообщения (~30 строк)
Результат:
- Главная функция render() сокращена на ~65 строк
- Применены let-else guards для упрощения
- Каждая функция имеет чёткую ответственность
Phase 5: Рефакторинг ui/messages.rs (879 строк)
Цель: разделить монолитную функцию render() на модули
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Применены дополнительные упрощения:
- handle_escape_key: преобразован в early returns
- handle_message_selection: применены let-else guards для всех веток
- Блоки 'd', 'y', 'e' теперь с явными guards
Результат Phase 4:
- Уменьшена вложенность во всех извлечённых функциях
- Применены паттерны: early returns, let-else guards, вспомогательные функции
- Код стал максимально линейным и читаемым
- Глубина вложенности: 6+ → 2-3 уровня
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Применены паттерны упрощения вложенности:
- handle_profile_mode: упрощён блок Enter с let-else
- handle_profile_open: применён early return guard
- handle_enter_key: разделена на 3 функции + early returns
- edit_message() - редактирование сообщения
- send_new_message() - отправка нового сообщения
- Сокращено с ~130 до ~40 строк
- handle_message_search_mode: извлечена функция perform_message_search()
- Упрощены блоки Backspace и Char с let-else
Результат: код стал более линейным, уменьшена глубина вложенности с 6+ до 2-3 уровней
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Обновлена документация с результатами Phase 3:
- Функция handle() сократилась с 891 до 82 строк (91% сокращение!)
- Всего извлечено 13 специализированных функций
- Phase 2: 2 функции (~163 строки)
- Phase 3: 11 функций (~783 строки)
Код стал линейным и простым для понимания.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Извлечены все оставшиеся блоки из функции handle():
- handle_profile_mode() - режим профиля с модалкой (~120 строк)
- handle_message_search_mode() - поиск по сообщениям (~73 строки)
- handle_pinned_mode() - закреплённые сообщения (~42 строки)
- handle_reaction_picker_mode() - emoji picker (~90 строк)
- handle_delete_confirmation() - подтверждение удаления (~60 строк)
- handle_forward_mode() - пересылка сообщений (~52 строки)
- handle_chat_search_mode() - поиск по чатам (~43 строки)
- handle_enter_key() - обработка Enter (~145 строк)
- handle_escape_key() - обработка Esc (~35 строк)
- handle_message_selection() - режим выбора сообщения (~95 строк)
- handle_profile_open() - Ctrl+U для профиля (~28 строк)
Результат:
- Функция handle() сокращена с 734 до 82 строк (89% сокращение!)
- Всего извлечено 13 специализированных функций
- Каждая функция имеет чёткую ответственность
- Код стал линейным и легко читаемым
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Извлечены оставшиеся обработчики из функции handle():
- handle_open_chat_keyboard_input() - ввод текста, навигация курсора, скролл (~129 строк)
- handle_chat_list_navigation() - навигация по чатам и папкам (~34 строки)
Результат:
- Функция handle() сокращена с 891 до 734 строк
- Всего извлечено 12 специализированных функций
- Каждая функция имеет чёткую ответственность и документацию
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Упрощение функции 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.