// Author: cirakg & claude ai // Summary: Navigation between screens (README 13.2): opening entries, asking for the PIN first when needed, the menu. import Toybox.Lang; import Toybox.System; import Toybox.WatchUi; module Nav { // A second open request within this time is the same tap delivered // twice (as START in onKey and as onTap) and is ignored. const OPEN_DEBOUNCE_MS = 500; var _lastOpenMs as Number? = null; // Open an entry from the list: its detail screen, or the PIN screen first // when it is locked and the session is locked. Ignored while a PIN or // detail screen is already on top, or right after another open, so one // tap can't push two screens. function openEntry(id as String) as Void { var top = WatchUi.getCurrentView()[0]; if (top instanceof PinView || top instanceof OtpView || top instanceof CodeView) { return; } var now = System.getTimer(); var last = _lastOpenMs; if (last != null && now - last >= 0 && now - last < OPEN_DEBOUNCE_MS) { return; } _lastOpenMs = now; var vault = AppState.theVault(); var header = vault.header(id); if (header == null) { showInfo("Damaged entry", ["Damaged entry " + id + ". Delete it and add it again."]); return; } if (header.locked && !vault.session.isUnlocked()) { var action = new OpenAfterPin(id); WatchUi.pushView(new PinView(PinView.MODE_UNLOCK, action.method(:run)), new PinDelegate(), WatchUi.SLIDE_LEFT); return; } WatchUi.pushView(detailView(header), detailDelegate(header), WatchUi.SLIDE_LEFT); } function detailView(header as Entry) as WatchUi.View { if (header.kind == Entry.KIND_OTP) { return new OtpView(header); } return new CodeView(header); } function detailDelegate(header as Entry) as WatchUi.InputDelegate { if (header.kind == Entry.KIND_OTP) { return new OtpDelegate(); } return new CodeDelegate(); } // A simple text screen. function showInfo(title as String, lines as Array) as Void { WatchUi.pushView(new InfoView(title, lines, "info"), new InfoDelegate(), WatchUi.SLIDE_LEFT); } function openMenu() as Void { WatchUi.pushView(MainMenu.build(), new MainMenuDelegate(), WatchUi.SLIDE_UP); } // Opens an entry once the PIN screen succeeded (replacing the PIN screen). class OpenAfterPin { private var _id as String; function initialize(id as String) { _id = id; } function run() as Void { var header = AppState.theVault().header(_id); if (header == null) { WatchUi.popView(WatchUi.SLIDE_RIGHT); return; } WatchUi.switchToView(detailView(header), detailDelegate(header), WatchUi.SLIDE_LEFT); } } }