Единое окно: полное пользовательское удаление загрузки (delete)
Вторая половина «единого окна»: команда «Удалить» снимает наши библиотечные хардлинки (гард последней копии осознанно выключен, в отличие от Undo) и сносит раздачу с файлами из qBittorrent (deleteFiles=true) → терминальный deleted. Доступна из done/orphaned/target_missing, идемпотентна к отсутствующей стороне; инициатор различается через error_code=user_delete. Подтверждение обязательно: веб — danger-секция внизу страницы (hx-confirm + details), Telegram — двухшаговый inline-confirm. qbt.Delete + layout.Remove (unlink без ErrLastCopy, только свои ссылки под movies/series). Граф переходов не менялся — рёбра уже были. OpenSpec: state-reconciliation +1 требование; синк workflow.md; беклог закрыт. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,7 @@ type Reviewer interface {
|
||||
Defer(ctx context.Context, id string) error
|
||||
Cancel(ctx context.Context, id string) error
|
||||
Retry(ctx context.Context, id string) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
// Config — параметры бота.
|
||||
@@ -305,6 +306,20 @@ func (b *Bot) handleCallback(ctx context.Context, cq *tgbotapi.CallbackQuery) {
|
||||
case "retry":
|
||||
err = b.reviewer.Retry(ctx, id)
|
||||
note = "Повторяю…"
|
||||
case "delete":
|
||||
// Первый шаг: показываем подтверждение (необратимое действие — снос
|
||||
// раздачи+файлов и хардлинков). Домен не трогаем, меняем клавиатуру.
|
||||
b.answer(cq.ID, "")
|
||||
b.editMarkup(chatID, msgID, b.deleteConfirmKeyboard(id))
|
||||
return
|
||||
case "delete_cancel":
|
||||
// Отмена подтверждения — возвращаем обычную карточку.
|
||||
b.answer(cq.ID, "Отменено")
|
||||
b.refreshCard(ctx, chatID, msgID, id)
|
||||
return
|
||||
case "delete_confirm":
|
||||
err = b.reviewer.Delete(ctx, id)
|
||||
note = "Удаляю…"
|
||||
case "type":
|
||||
err = b.reviewer.SetType(ctx, id, val)
|
||||
note = "Меняю тип…"
|
||||
@@ -397,6 +412,17 @@ func (b *Bot) answer(callbackID, text string) {
|
||||
}
|
||||
}
|
||||
|
||||
// editMarkup подменяет только клавиатуру сообщения на месте (текст не трогаем).
|
||||
// Используется для шага подтверждения удаления.
|
||||
func (b *Bot) editMarkup(chatID int64, msgID int, kb *tgbotapi.InlineKeyboardMarkup) {
|
||||
if kb == nil {
|
||||
return
|
||||
}
|
||||
if _, err := b.api.Send(tgbotapi.NewEditMessageReplyMarkup(chatID, msgID, *kb)); err != nil {
|
||||
b.log.Warn("telegram edit markup failed", "chat_id", chatID, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bot) setPending(chatID int64, id string) {
|
||||
b.mu.Lock()
|
||||
b.pending[chatID] = id
|
||||
|
||||
Reference in New Issue
Block a user