feat(bot): кнопка «🚀 Закоммитить» прямо в папке #40
@@ -59,6 +59,7 @@ class Act(StrEnum):
|
||||
PUSH = "ps"
|
||||
PUSH_GO = "pg"
|
||||
PUSH_FORCE = "pf"
|
||||
COMMIT = "cm"
|
||||
|
||||
|
||||
class CommitCb(CallbackData, prefix="c"):
|
||||
@@ -179,8 +180,10 @@ def render_dir(
|
||||
kb.button(text=t.new_dir, callback_data=CommitCb(a=Act.NEW_DIR))
|
||||
sizes.append(2)
|
||||
if draft_count:
|
||||
# the main action once something is staged — right in the folder, not two screens deep
|
||||
kb.button(text=t.commit_button.format(count=draft_count), callback_data=CommitCb(a=Act.COMMIT))
|
||||
kb.button(text=t.draft_button.format(count=draft_count), callback_data=CommitCb(a=Act.DRAFT))
|
||||
sizes.append(1)
|
||||
sizes += [1, 1]
|
||||
if path:
|
||||
kb.button(text=t.up, callback_data=CommitCb(a=Act.UP))
|
||||
sizes.append(1)
|
||||
@@ -260,6 +263,14 @@ def build_router(
|
||||
kb.adjust(1)
|
||||
return text, kb.as_markup()
|
||||
|
||||
def confirm_screen(draft: DraftView) -> tuple[str, InlineKeyboardMarkup]:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=t.push_go, callback_data=CommitCb(a=Act.PUSH_GO))
|
||||
builder.button(text=t.edit_message, callback_data=CommitCb(a=Act.MESSAGE))
|
||||
builder.button(text=texts.menu.back, callback_data=CommitCb(a=Act.TO_DIR))
|
||||
builder.adjust(1)
|
||||
return t.push_confirm.format(summary=summary(draft), message=draft.message), builder.as_markup()
|
||||
|
||||
def summary(draft: DraftView) -> str:
|
||||
ops = {ChangeOp.ADD: t.op_add, ChangeOp.MODIFY: t.op_modify, ChangeOp.DELETE: t.op_delete}
|
||||
return "\n".join(f"{ops[c.op]} /{c.path}" for c in draft.changes)
|
||||
@@ -466,17 +477,28 @@ def build_router(
|
||||
return
|
||||
case Act.MESSAGE:
|
||||
await state.set_state(Flow.message)
|
||||
await state.update_data(commit_then_push=False)
|
||||
await msg.edit_text(t.ask_message, reply_markup=to_dir_keyboard(texts))
|
||||
case Act.PUSH:
|
||||
draft = await drafts.get(uid)
|
||||
if draft is None or not draft.changes or not draft.message:
|
||||
await query.answer()
|
||||
return
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=t.push_go, callback_data=CommitCb(a=Act.PUSH_GO))
|
||||
builder.button(text=texts.menu.back, callback_data=CommitCb(a=Act.DRAFT))
|
||||
confirm = t.push_confirm.format(summary=summary(draft), message=draft.message)
|
||||
await msg.edit_text(confirm, reply_markup=builder.as_markup())
|
||||
confirm, markup = confirm_screen(draft)
|
||||
await msg.edit_text(confirm, reply_markup=markup)
|
||||
case Act.COMMIT:
|
||||
draft = await drafts.get(uid)
|
||||
if draft is None or not draft.changes:
|
||||
await query.answer()
|
||||
return
|
||||
if draft.message:
|
||||
confirm, markup = confirm_screen(draft)
|
||||
await msg.edit_text(confirm, reply_markup=markup)
|
||||
else:
|
||||
# ask for the message, then go straight to the push confirmation
|
||||
await state.set_state(Flow.message)
|
||||
await state.update_data(commit_then_push=True)
|
||||
await msg.edit_text(t.ask_message, reply_markup=to_dir_keyboard(texts))
|
||||
case Act.PUSH_GO | Act.PUSH_FORCE:
|
||||
await query.answer()
|
||||
await do_push(msg, state, member, overwrite=callback_data.a is Act.PUSH_FORCE)
|
||||
@@ -549,6 +571,11 @@ def build_router(
|
||||
return
|
||||
await drafts.set_message(member.tg_id, text)
|
||||
await state.set_state(None)
|
||||
draft = await drafts.get(member.tg_id)
|
||||
if (await state.get_data()).get("commit_then_push") and draft is not None and draft.changes:
|
||||
await state.update_data(commit_then_push=False)
|
||||
screen, kb = confirm_screen(draft)
|
||||
else:
|
||||
screen, kb = await draft_screen(state, member.tg_id)
|
||||
await message.answer(screen, reply_markup=kb)
|
||||
|
||||
|
||||
@@ -110,6 +110,8 @@ class CommitTexts(_Strict):
|
||||
ask_message: str
|
||||
bad_message: str
|
||||
push_button: str
|
||||
commit_button: str
|
||||
edit_message: str
|
||||
push_confirm: str
|
||||
push_go: str
|
||||
pushing: str
|
||||
|
||||
@@ -113,6 +113,8 @@ commit:
|
||||
ask_message: "Сообщение коммита: до 200 символов, первая строка — до 72."
|
||||
bad_message: "Сообщение: 1–200 символов, первая строка — до 72. Пришлите другое."
|
||||
push_button: "🚀 Push"
|
||||
commit_button: "🚀 Закоммитить · {count}"
|
||||
edit_message: "✏️ Изменить сообщение"
|
||||
push_confirm: |-
|
||||
Запушить в main?
|
||||
|
||||
|
||||
@@ -63,3 +63,30 @@ async def test_message_then_push_lands_in_remote(tg: TgClient, texts: Texts, rem
|
||||
tg.session.requests.clear()
|
||||
await tg.send(ADMIN, texts.menu.commit)
|
||||
assert not any(label.startswith("🧾") for label in labels(tg.session.sent(SendMessage)[-1].reply_markup))
|
||||
|
||||
|
||||
async def test_commit_button_in_folder_asks_message_then_confirms(
|
||||
tg: TgClient, texts: Texts, remote: Path, tmp_path: Path
|
||||
) -> None:
|
||||
t = texts.commit
|
||||
await tg.send(ADMIN, texts.menu.commit)
|
||||
await tg.press(ADMIN, cb(Act.NEW_FILE))
|
||||
await tg.send(ADMIN, "quick.txt")
|
||||
await tg.send(ADMIN, "быстро")
|
||||
|
||||
folder = tg.session.sent(SendMessage)[-1]
|
||||
assert t.commit_button.format(count=1) in labels(folder.reply_markup)
|
||||
|
||||
await tg.press(ADMIN, cb(Act.COMMIT))
|
||||
assert tg.session.sent(EditMessageText)[-1].text == t.ask_message
|
||||
await tg.send(ADMIN, "feat: быстрый коммит")
|
||||
|
||||
confirm = tg.session.sent(SendMessage)[-1]
|
||||
assert confirm.text.startswith("Запушить в main?") and "feat: быстрый коммит" in confirm.text
|
||||
assert labels(confirm.reply_markup) == [t.push_go, t.edit_message, texts.menu.back]
|
||||
|
||||
await tg.press(ADMIN, cb(Act.PUSH_GO))
|
||||
assert tg.session.sent(EditMessageText)[-1].text.startswith("✅ Запушено: ")
|
||||
clone = tmp_path / "verify2"
|
||||
git(tmp_path, "clone", "-q", str(remote), str(clone))
|
||||
assert (clone / "quick.txt").read_text(encoding="utf-8") == "быстро"
|
||||
|
||||
Reference in New Issue
Block a user