feat: restructure menu and add file import
- Consolidate "Add word" menu with submenu (Manual, Thematic, Import) - Add file import support (.txt, .md) with AI batch translation - Add vocabulary pagination with navigation buttons - Add "Add word" button in tasks for new words mode - Fix undefined variables bug in vocabulary confirm handler - Add localization keys for add_menu in ru/en/ja 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,11 @@ async def confirm_add_word(callback: CallbackQuery, state: FSMContext):
|
||||
user_id = data.get("user_id")
|
||||
|
||||
async with async_session_maker() as session:
|
||||
# Получаем пользователя для языков
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
source_lang = user.learning_language if user else 'en'
|
||||
ui_lang = user.language_interface if user else 'ru'
|
||||
|
||||
# Добавляем слово в базу
|
||||
await VocabularyService.add_word(
|
||||
session,
|
||||
@@ -140,12 +145,9 @@ async def confirm_add_word(callback: CallbackQuery, state: FSMContext):
|
||||
)
|
||||
|
||||
# Получаем общее количество слов
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
words_count = await VocabularyService.get_words_count(session, user_id, learning_lang=user.learning_language)
|
||||
lang = ui_lang or 'ru'
|
||||
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
lang = (user.language_interface if user else 'ru') or 'ru'
|
||||
await callback.message.edit_text(
|
||||
t(lang, 'add.added_success', word=word_data['word'], count=words_count)
|
||||
)
|
||||
@@ -164,29 +166,55 @@ async def cancel_add_word(callback: CallbackQuery, state: FSMContext):
|
||||
await callback.answer()
|
||||
|
||||
|
||||
WORDS_PER_PAGE = 10
|
||||
|
||||
|
||||
@router.message(Command("vocabulary"))
|
||||
async def cmd_vocabulary(message: Message):
|
||||
"""Обработчик команды /vocabulary"""
|
||||
await show_vocabulary_page(message, page=0)
|
||||
|
||||
|
||||
async def show_vocabulary_page(message_or_callback, page: int = 0, edit: bool = False):
|
||||
"""Показать страницу словаря"""
|
||||
# Определяем, это Message или CallbackQuery
|
||||
# В CallbackQuery from_user — это пользователь, а message.from_user — бот
|
||||
user_id = message_or_callback.from_user.id
|
||||
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
||||
user = await UserService.get_user_by_telegram_id(session, user_id)
|
||||
|
||||
if not user:
|
||||
await message.answer(t('ru', 'common.start_first'))
|
||||
if edit:
|
||||
await message_or_callback.message.edit_text(t('ru', 'common.start_first'))
|
||||
else:
|
||||
await message_or_callback.answer(t('ru', 'common.start_first'))
|
||||
return
|
||||
|
||||
# Получаем слова пользователя
|
||||
words = await VocabularyService.get_user_words(session, user.id, limit=10, learning_lang=user.learning_language)
|
||||
# Получаем слова с пагинацией
|
||||
offset = page * WORDS_PER_PAGE
|
||||
words = await VocabularyService.get_user_words(
|
||||
session, user.id,
|
||||
limit=WORDS_PER_PAGE,
|
||||
offset=offset,
|
||||
learning_lang=user.learning_language
|
||||
)
|
||||
total_count = await VocabularyService.get_words_count(session, user.id, learning_lang=user.learning_language)
|
||||
|
||||
if not words:
|
||||
lang = (user.language_interface if user else 'ru') or 'ru'
|
||||
await message.answer(t(lang, 'vocab.empty'))
|
||||
lang = get_user_lang(user)
|
||||
|
||||
if not words and page == 0:
|
||||
if edit:
|
||||
await message_or_callback.message.edit_text(t(lang, 'vocab.empty'))
|
||||
else:
|
||||
await message_or_callback.answer(t(lang, 'vocab.empty'))
|
||||
return
|
||||
|
||||
# Формируем список слов
|
||||
lang = (user.language_interface if user else 'ru') or 'ru'
|
||||
total_pages = (total_count + WORDS_PER_PAGE - 1) // WORDS_PER_PAGE
|
||||
words_list = t(lang, 'vocab.header') + "\n\n"
|
||||
for idx, word in enumerate(words, 1):
|
||||
|
||||
for idx, word in enumerate(words, start=offset + 1):
|
||||
progress = ""
|
||||
if word.times_reviewed > 0:
|
||||
accuracy = int((word.correct_answers / word.times_reviewed) * 100)
|
||||
@@ -197,9 +225,49 @@ async def cmd_vocabulary(message: Message):
|
||||
f" 🔊 [{word.transcription or ''}]{progress}\n\n"
|
||||
)
|
||||
|
||||
if total_count > 10:
|
||||
words_list += "\n" + t(lang, 'vocab.shown_last', n=total_count)
|
||||
else:
|
||||
words_list += "\n" + t(lang, 'vocab.total', n=total_count)
|
||||
words_list += t(lang, 'vocab.page_info', page=page + 1, total=total_pages, count=total_count)
|
||||
|
||||
await message.answer(words_list)
|
||||
# Кнопки пагинации
|
||||
buttons = []
|
||||
nav_row = []
|
||||
|
||||
if page > 0:
|
||||
nav_row.append(InlineKeyboardButton(text="⬅️", callback_data=f"vocab_page_{page - 1}"))
|
||||
|
||||
nav_row.append(InlineKeyboardButton(text=f"{page + 1}/{total_pages}", callback_data="vocab_noop"))
|
||||
|
||||
if page < total_pages - 1:
|
||||
nav_row.append(InlineKeyboardButton(text="➡️", callback_data=f"vocab_page_{page + 1}"))
|
||||
|
||||
if nav_row:
|
||||
buttons.append(nav_row)
|
||||
|
||||
buttons.append([InlineKeyboardButton(text=t(lang, 'vocab.close_btn'), callback_data="vocab_close")])
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||
|
||||
if edit:
|
||||
await message_or_callback.message.edit_text(words_list, reply_markup=keyboard)
|
||||
else:
|
||||
await message_or_callback.answer(words_list, reply_markup=keyboard)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("vocab_page_"))
|
||||
async def vocab_page_callback(callback: CallbackQuery):
|
||||
"""Переключение страницы словаря"""
|
||||
page = int(callback.data.split("_")[-1])
|
||||
await callback.answer()
|
||||
await show_vocabulary_page(callback, page=page, edit=True)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "vocab_noop")
|
||||
async def vocab_noop_callback(callback: CallbackQuery):
|
||||
"""Пустой callback для кнопки с номером страницы"""
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data == "vocab_close")
|
||||
async def vocab_close_callback(callback: CallbackQuery):
|
||||
"""Закрыть словарь"""
|
||||
await callback.message.delete()
|
||||
await callback.answer()
|
||||
|
||||
Reference in New Issue
Block a user