diff --git a/lib/microreader/screens/MainMenu.cpp b/lib/microreader/screens/MainMenu.cpp index 1acdcc5..f0df17d 100644 --- a/lib/microreader/screens/MainMenu.cpp +++ b/lib/microreader/screens/MainMenu.cpp @@ -46,19 +46,141 @@ void MainMenu::update(const ButtonState& buttons, DrawBuffer& buf, IRuntime& run buf.full_refresh(); } + // === DELETE CONFIRMATION MODE === + if (delete_pending_) { + Button btn; + while (buttons.next_press(btn)) { + if (btn == Button::Button1) { + do_delete_(buf, runtime); + return; + } + if (btn == Button::Button0) { + cancel_delete_(buf, runtime); + return; + } + } + draw_all_(buf, runtime.battery_percentage()); + buf.refresh(); + return; + } + + // === NORMAL MODE === + + // Track Button1 hold duration (time-based for frame-rate independence) + if (buttons.is_down(Button::Button1)) + button1_hold_ms_ += runtime.frame_time_ms(); + else + button1_hold_ms_ = 0; + + // Let parent handle up/down/back navigation. + // on_select() stores the index but no longer opens the book immediately. ListMenuScreen::update(buttons, buf, runtime); + + // Handle pending selection (set by on_select above) + if (pending_select_idx_ >= 0) { + // Button released → short press → open the book + if (!buttons.is_down(Button::Button1)) { + pending_select_idx_ = -1; + app_->push_screen(ScreenId::Reader); + return; + } + // Long press threshold reached → enter delete confirmation + if (button1_hold_ms_ >= kLongPressMs) { + const int idx = pending_select_idx_; + pending_select_idx_ = -1; + button1_hold_ms_ = 0; + delete_pending_ = true; + delete_index_ = idx; + delete_book_label_ = entries_[idx].label; + title_ = "Delete?"; + set_item_label(idx, "Back=No Sel=Yes"); + draw_all_(buf, runtime.battery_percentage()); + buf.refresh(); + return; + } + // Button still held but not yet long enough — wait another frame + } } void MainMenu::on_select(int index) { + // Don't open the book immediately — update() handles short-press vs long-press last_selected_path_ = entries_[index].path; app_->reader()->set_path(entries_[index].path.c_str()); - app_->push_screen(ScreenId::Reader); + pending_select_idx_ = index; } void MainMenu::on_back() { app_->push_screen(ScreenId::Settings); } +void MainMenu::do_delete_(DrawBuffer& buf, IRuntime& runtime) { + const std::string& epub_path = entries_[delete_index_].path; + const char* path_cstr = epub_path.c_str(); + + MR_LOGI("menu", "deleting %s", path_cstr); + + // 1. Delete the EPUB file + (void)std::remove(path_cstr); + + // 2. Delete the MRB cache directory (/cache//) + if (app_ && app_->data_dir_) { + std::string cache_dir = std::string(app_->data_dir_) + "/cache/"; + const char* name = path_cstr; + const char* sep = std::strrchr(name, '/'); +#ifdef _WIN32 + const char* bsep = std::strrchr(name, '\\'); + if (bsep && (!sep || bsep > sep)) + sep = bsep; +#endif + if (sep) + name = sep + 1; + const char* dot = std::strrchr(name, '.'); + if (dot) + cache_dir.append(name, static_cast(dot - name)); + else + cache_dir.append(name); + +#ifdef ESP_PLATFORM + DIR* d = opendir(cache_dir.c_str()); + if (d) { + struct dirent* ent; + char fpath[300]; + while ((ent = readdir(d)) != nullptr) { + if (ent->d_name[0] == '.') + continue; + std::snprintf(fpath, sizeof(fpath), "%s/%s", cache_dir.c_str(), ent->d_name); + std::remove(fpath); + } + closedir(d); + rmdir(cache_dir.c_str()); + } +#else + if (fs::exists(cache_dir)) + fs::remove_all(cache_dir); +#endif + } + + // Reset state and rescan + button1_hold_ms_ = 0; + delete_pending_ = false; + delete_index_ = -1; + title_ = "Microreader"; + scan_directory_(buf); + populate_list_(); + draw_all_(buf, runtime.battery_percentage()); + buf.full_refresh(); +} + +void MainMenu::cancel_delete_(DrawBuffer& buf, IRuntime& runtime) { + set_item_label(delete_index_, delete_book_label_); + button1_hold_ms_ = 0; + delete_pending_ = false; + delete_index_ = -1; + title_ = "Microreader"; + draw_all_(buf, runtime.battery_percentage()); + buf.refresh(); +} + void MainMenu::scan_directory_(DrawBuffer& buf) { if (!books_dir_ || !app_->data_dir_) return; diff --git a/lib/microreader/screens/MainMenu.h b/lib/microreader/screens/MainMenu.h index d243417..aef8dac 100644 --- a/lib/microreader/screens/MainMenu.h +++ b/lib/microreader/screens/MainMenu.h @@ -20,6 +20,11 @@ class MainMenu final : public ListMenuScreen { books_dir_ = dir; } + // Force the book list to rescan on the next update. + void request_rescan() { + needs_scan_ = true; + } + // Restore the book list selection to the entry matching this path. // Call before start(); applied after directory scan. void set_initial_selection(const char* path) { @@ -83,6 +88,17 @@ class MainMenu final : public ListMenuScreen { BookListFormat list_format_ = BookListFormat::TitleOnly; bool needs_scan_ = false; + // Long-press delete state + int pending_select_idx_ = -1; // index selected via on_select, pending resolution + int delete_index_ = -1; // book to delete when confirmed + bool delete_pending_ = false; // waiting for confirm/cancel on delete prompt + uint32_t button1_hold_ms_ = 0; // accumulated hold time for Button1 + std::string delete_book_label_; // original label of book pending deletion + static constexpr uint32_t kLongPressMs = 800; + + void do_delete_(DrawBuffer& buf, IRuntime& runtime); + void cancel_delete_(DrawBuffer& buf, IRuntime& runtime); + struct BookEntry { std::string path; std::string label;