summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-13 13:52:27 +0300
committerPaul Buetow <paul@buetow.org>2026-04-13 13:52:27 +0300
commitd236e785c73d713fba688f5dad2624a313a194dd (patch)
tree0004b72ebf768f00d0b9f0949663e735c4ee3a5e
parentb1af8353ee25d53fc97bf1e3c8252c4511cfd13c (diff)
u2: add cancel fast option that discards the fast without saving to history
Adds fast_cancel() in fastforge_core.c which clears the running fast and saves state without appending to history. Wires a new "Cancel Current Fast" main menu item (index 3) that calls it, showing a confirmation placeholder. Updates sync_main_menu_state() to reflect cancel availability dynamically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--PLAN.md13
-rw-r--r--src/c/fastforge.c34
-rw-r--r--src/c/fastforge.h1
-rw-r--r--src/c/fastforge_core.c20
4 files changed, 56 insertions, 12 deletions
diff --git a/PLAN.md b/PLAN.md
index e0efab3..545f1d5 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -209,12 +209,13 @@ On phone side (minimal companion app or Rebble tools) receive the data as CSV.
1. Start New Fast → Presets submenu
2. Current Timer
-3. Stop Current Fast (if running)
-4. History
-5. Statistics
-6. Settings
-7. Backup to Phone
-8. About
+3. Stop Current Fast (if running) — saves to history
+4. Cancel Current Fast (if running) — discards, not saved to history
+5. History
+6. Statistics
+7. Settings
+8. Backup to Phone
+9. About
---
diff --git a/src/c/fastforge.c b/src/c/fastforge.c
index 623765e..2dc7cf0 100644
--- a/src/c/fastforge.c
+++ b/src/c/fastforge.c
@@ -9,12 +9,13 @@ enum {
MAIN_MENU_INDEX_START_NEW = 0,
MAIN_MENU_INDEX_CURRENT_TIMER = 1,
MAIN_MENU_INDEX_STOP_CURRENT = 2,
- MAIN_MENU_INDEX_HISTORY = 3,
- MAIN_MENU_INDEX_STATS = 4,
- MAIN_MENU_INDEX_SETTINGS = 5,
- MAIN_MENU_INDEX_BACKUP = 6,
- MAIN_MENU_INDEX_ABOUT = 7,
- MAIN_MENU_ITEM_COUNT = 8
+ MAIN_MENU_INDEX_CANCEL_CURRENT = 3,
+ MAIN_MENU_INDEX_HISTORY = 4,
+ MAIN_MENU_INDEX_STATS = 5,
+ MAIN_MENU_INDEX_SETTINGS = 6,
+ MAIN_MENU_INDEX_BACKUP = 7,
+ MAIN_MENU_INDEX_ABOUT = 8,
+ MAIN_MENU_ITEM_COUNT = 9
};
enum {
@@ -96,6 +97,7 @@ static char s_settings_target_text[32];
static char s_settings_dev_text[32];
#endif
static char s_menu_stop_subtitle[32];
+static char s_menu_cancel_subtitle[32];
static char s_placeholder_title_text[24];
static char s_placeholder_body_text[160];
static char s_placeholder_hint_text[24];
@@ -655,10 +657,13 @@ static void refresh_goal_window_content(void) {
static void sync_main_menu_state(void) {
if (fast_is_running()) {
snprintf(s_menu_stop_subtitle, sizeof(s_menu_stop_subtitle), "End now and save");
+ snprintf(s_menu_cancel_subtitle, sizeof(s_menu_cancel_subtitle), "Discard, no history");
} else {
snprintf(s_menu_stop_subtitle, sizeof(s_menu_stop_subtitle), "No fast running");
+ snprintf(s_menu_cancel_subtitle, sizeof(s_menu_cancel_subtitle), "No fast running");
}
s_main_menu_items[MAIN_MENU_INDEX_STOP_CURRENT].subtitle = s_menu_stop_subtitle;
+ s_main_menu_items[MAIN_MENU_INDEX_CANCEL_CURRENT].subtitle = s_menu_cancel_subtitle;
if (s_main_menu_layer) {
menu_layer_reload_data(simple_menu_layer_get_menu_layer(s_main_menu_layer));
@@ -1087,6 +1092,18 @@ static void menu_stop_current_callback(int index, void *context) {
refresh_all_ui_state();
}
+/* Cancel discards the running fast without saving it to history. */
+static void menu_cancel_current_callback(int index, void *context) {
+ (void)index;
+ (void)context;
+ if (!fast_cancel()) {
+ show_placeholder_window("NOT RUNNING", "There is no active fast to cancel.", "BACK Menu");
+ return;
+ }
+ show_placeholder_window("FAST CANCELLED", "Fast discarded. Not in history.", "BACK Menu");
+ refresh_all_ui_state();
+}
+
static void menu_history_callback(int index, void *context) {
(void)index;
(void)context;
@@ -1740,6 +1757,11 @@ static void configure_main_menu_items(void) {
.subtitle = "",
.callback = menu_stop_current_callback
};
+ s_main_menu_items[MAIN_MENU_INDEX_CANCEL_CURRENT] = (SimpleMenuItem) {
+ .title = "Cancel Current Fast",
+ .subtitle = "",
+ .callback = menu_cancel_current_callback
+ };
s_main_menu_items[MAIN_MENU_INDEX_HISTORY] = (SimpleMenuItem) {
.title = "History",
.subtitle = "Completed fasts",
diff --git a/src/c/fastforge.h b/src/c/fastforge.h
index 6c24e75..263a75b 100644
--- a/src/c/fastforge.h
+++ b/src/c/fastforge.h
@@ -28,5 +28,6 @@ void load_all_data(void);
bool fast_is_running(void);
bool fast_start(uint16_t preset_target_minutes);
bool fast_stop(void);
+bool fast_cancel(void);
#endif
diff --git a/src/c/fastforge_core.c b/src/c/fastforge_core.c
index a9bec03..cde224f 100644
--- a/src/c/fastforge_core.c
+++ b/src/c/fastforge_core.c
@@ -297,6 +297,26 @@ bool fast_stop(void) {
return true;
}
+/* Cancel discards the running fast without recording it in history. */
+bool fast_cancel(void) {
+ if (!fast_is_running()) {
+ return false;
+ }
+
+ memset(&current_fast, 0, sizeof(current_fast));
+#ifdef DEBUG
+ s_current_fast_origin_offset_seconds = 0;
+#endif
+ if (alarm_timer) {
+ app_timer_cancel(alarm_timer);
+ alarm_timer = NULL;
+ }
+ target_time = 0;
+ save_all_data();
+ APP_LOG(APP_LOG_LEVEL_INFO, "Cancelled fast (not saved to history)");
+ return true;
+}
+
static void alarm_callback(void *data) {
(void)data;
alarm_timer = NULL;