diff --git a/contact/ui/control_ui.py b/contact/ui/control_ui.py index f7f42d1..5929c79 100644 --- a/contact/ui/control_ui.py +++ b/contact/ui/control_ui.py @@ -510,6 +510,26 @@ def settings_menu(stdscr: object, interface: object) -> None: menu_state.selected_index = 0 elif key == curses.KEY_LEFT: + + # If we are at the main menu and there are unsaved changes, prompt to save + if len(menu_state.menu_path) == 3 and modified_settings: + + current_section = menu_state.menu_path[-1] + save_prompt = get_list_input( + f"You have unsaved changes in {current_section}. Save before exiting?", + None, + ["Yes", "No", "Cancel"], + ) + if save_prompt == "Cancel": + continue # Stay in the menu without doing anything + elif save_prompt == "Yes": + save_changes(interface, modified_settings, menu_state) + logging.info("Changes Saved") + + modified_settings.clear() + menu = rebuild_menu_at_current_path(interface, menu_state) + pass + menu_state.need_redraw = True menu_win.erase() @@ -521,8 +541,8 @@ def settings_menu(stdscr: object, interface: object) -> None: menu_win.refresh() help_win.refresh() - if len(menu_state.menu_path) < 2: - modified_settings.clear() + # if len(menu_state.menu_path) < 2: + # modified_settings.clear() # Navigate back to the previous menu if len(menu_state.menu_path) > 1: @@ -539,6 +559,16 @@ def settings_menu(stdscr: object, interface: object) -> None: break +def rebuild_menu_at_current_path(interface, menu_state): + """Rebuild menus from the device and re-point current_menu to the same path.""" + new_menu = generate_menu_from_protobuf(interface) + cur = new_menu["Main Menu"] + for step in menu_state.menu_path[1:]: + cur = cur.get(step, {}) + menu_state.current_menu = cur + return new_menu + + def set_region(interface: object) -> None: node = interface.getNode("^local") device_config = node.localConfig diff --git a/contact/utilities/input_handlers.py b/contact/utilities/input_handlers.py index bf6ccbb..833c974 100644 --- a/contact/utilities/input_handlers.py +++ b/contact/utilities/input_handlers.py @@ -109,6 +109,8 @@ def get_text_input(prompt: str, selected_config: str, input_type: str) -> Option return None elif key in (chr(curses.KEY_ENTER), chr(10), chr(13)): + menu_state.need_redraw = True + if not user_input.strip(): invalid_input(input_win, "Value cannot be empty.", redraw_func=redraw_input_win) continue @@ -293,10 +295,10 @@ def get_admin_key_input(current_value: List[bytes]) -> Optional[List[str]]: return None elif key == ord("\n"): # Enter key to save and return + menu_state.need_redraw = True if all(is_valid_base64(val) for val in user_values): # Ensure all values are valid Base64 and 32 bytes curses.noecho() curses.curs_set(0) - menu_state.need_redraw = True return user_values # Return the edited Base64 values else: invalid_input = "Error: Each key must be valid Base64 and 32 bytes long!" @@ -446,16 +448,15 @@ def get_fixed32_input(current_value: int) -> int: elif key in ("\n", curses.KEY_ENTER): octets = user_input.split(".") + menu_state.need_redraw = True if len(octets) == 4 and all(octet.isdigit() and 0 <= int(octet) <= 255 for octet in octets): curses.noecho() curses.curs_set(0) - menu_state.need_redraw = True return int(ipaddress.ip_address(user_input)) else: fixed32_win.addstr(7, 2, "Invalid IP address. Try again.", get_color("settings_default", bold=True)) fixed32_win.refresh() curses.napms(1500) - menu_state.need_redraw = True user_input = "" elif key in (curses.KEY_BACKSPACE, 127):