summaryrefslogtreecommitdiff
path: root/source/ui/MainMenu.mc
diff options
context:
space:
mode:
Diffstat (limited to 'source/ui/MainMenu.mc')
-rw-r--r--source/ui/MainMenu.mc113
1 files changed, 94 insertions, 19 deletions
diff --git a/source/ui/MainMenu.mc b/source/ui/MainMenu.mc
index 8863c6d..db59f4a 100644
--- a/source/ui/MainMenu.mc
+++ b/source/ui/MainMenu.mc
@@ -18,7 +18,10 @@ module MainMenu {
var menu = new WatchUi.Menu2({:title => "OTP Wallet"});
var waiting = ChangeQueue.count(vault);
if (waiting > 0) {
- menu.addItem(new WatchUi.MenuItem("Phone changes", waiting + " to confirm", :queue, null));
+ // Only "Require PIN" waiting: it applies at the next PIN anyway.
+ var toConfirm = ChangeQueue.countToConfirm(vault);
+ menu.addItem(new WatchUi.MenuItem("Phone changes", toConfirm > 0 ? toConfirm + " to confirm" : "Apply at next PIN",
+ :queue, null));
}
if (vault.session.isUnlocked()) {
menu.addItem(new WatchUi.MenuItem("Lock now", null, :lock, null));
@@ -32,28 +35,29 @@ module MainMenu {
menu.addItem(new WatchUi.MenuItem("Clock status", null, :clock, null));
menu.addItem(new WatchUi.MenuItem("About", null, :about, null));
DebugMenu.addItems(menu);
+ logItems(menu);
return menu;
}
- function entryIdLines() as Array<String> {
- var vault = AppState.theVault();
- var ids = vault.ids();
- var lines = [] as Array<String>;
- for (var i = 0; i < ids.size(); i++) {
- var header = vault.header(ids[i]);
- var name = header == null ? "damaged" : (header.name.length() > 0 ? header.name : "Locked entry");
- lines.add(ids[i] + " " + name);
- }
- if (lines.size() == 0) {
- lines.add("No entries yet.");
- }
- var pending = vault.pendingIds().size();
- if (pending > 0) {
- lines.add(pending + " locked entries wait for your PIN.");
+ // Debug builds: the item labels in order, so UI scripts can find an
+ // item on touch-only watches, whose menus have no focus to move.
+ (:debug)
+ function logItems(menu as WatchUi.Menu2) as Void {
+ var labels = "";
+ for (var i = 0; menu.getItem(i) != null; i++) {
+ labels += (i > 0 ? "|" : "") + (menu.getItem(i) as WatchUi.MenuItem).getLabel();
}
- return lines;
+ Diag.data("menu", "items=" + labels);
+ }
+
+ (:release)
+ function logItems(menu as WatchUi.Menu2) as Void {
}
+ // The Entry IDs screen: "Reading…" while EntryIdsJob reads the
+ // headers on a timer, then the list (kept so the runner isn't collected).
+ var idsScreen as EntryIdsScreen? = null;
+
function clockLines() as Array<String> {
var lines = [] as Array<String>;
var watch = Clock.nowUnix(0);
@@ -118,7 +122,9 @@ class MainMenuDelegate extends WatchUi.Menu2InputDelegate {
WatchUi.switchToView(new PinView(PinView.MODE_UNLOCK, then.method(:run)), new PinDelegate(), WatchUi.SLIDE_LEFT);
}
} else if (id == :reset) {
- WatchUi.pushView(new WatchUi.Confirmation("Erase locked entries and set a new PIN?"), new ResetPinConfirmation(),
+ // Two short lines (the item said "Reset PIN"): the native dialog shows
+ // two lines and cuts longer text mid-word.
+ WatchUi.pushView(new WatchUi.Confirmation("Erase locked\nentries?"), new ResetPinConfirmation(),
WatchUi.SLIDE_LEFT);
} else if (id == :results) {
WatchUi.switchToView(new InfoView("Results", Results.lines(), "results"), new InfoDelegate(),
@@ -127,7 +133,9 @@ class MainMenuDelegate extends WatchUi.Menu2InputDelegate {
} else if (id == :queue) {
WatchUi.switchToView(QueueMenu.build(), new QueueMenuDelegate(), WatchUi.SLIDE_LEFT);
} else if (id == :ids) {
- WatchUi.switchToView(new InfoView("Entry IDs", MainMenu.entryIdLines(), "ids"), new InfoDelegate(), WatchUi.SLIDE_LEFT);
+ var screen = new EntryIdsScreen();
+ MainMenu.idsScreen = screen;
+ screen.start();
} else if (id == :clock) {
WatchUi.switchToView(new InfoView("Clock status", MainMenu.clockLines(), "clock"), new InfoDelegate(), WatchUi.SLIDE_LEFT);
} else if (id == :about) {
@@ -138,6 +146,73 @@ class MainMenuDelegate extends WatchUi.Menu2InputDelegate {
}
}
+// Reads one header per entry, a few per timer step: reading 110 in one
+// event tripped the watchdog. Ends with the lines of the Entry IDs screen.
+class EntryIdsJob extends Jobs.Job {
+
+ static const HEADERS_PER_STEP = 10;
+
+ var lines as Array<String> = [] as Array<String>;
+ private var _ids as Array<String>;
+ private var _at as Number = 0;
+
+ function initialize() {
+ Job.initialize();
+ _ids = AppState.theVault().ids();
+ }
+
+ function step(budget as Number) as Boolean {
+ var vault = AppState.theVault();
+ var stop = _at + HEADERS_PER_STEP * budget;
+ stop = stop < _ids.size() ? stop : _ids.size();
+ for (; _at < stop; _at++) {
+ var id = _ids[_at];
+ var header = vault.header(id);
+ var name = header == null ? "damaged" : (header.name.length() > 0 ? header.name : "Locked entry");
+ lines.add(id + " " + name);
+ }
+ if (_at < _ids.size()) {
+ return false;
+ }
+ if (lines.size() == 0) {
+ lines.add("No entries yet.");
+ }
+ var pending = vault.pendingIds().size();
+ if (pending > 0) {
+ lines.add(pending + " locked entries wait for your PIN.");
+ }
+ return true;
+ }
+}
+
+// Shows "Reading…" (replacing the menu), runs EntryIdsJob, then replaces
+// that screen with the IDs, unless the user left it meanwhile.
+class EntryIdsScreen {
+
+ private var _job as EntryIdsJob = new EntryIdsJob();
+ // The timer driver (kept so it isn't collected).
+ var runner as Jobs.JobRunner? = null;
+ private var _reading as InfoView = new InfoView("Entry IDs", ["Reading…"] as Array<String>, "ids_reading");
+
+ function initialize() {
+ }
+
+ function start() as Void {
+ WatchUi.switchToView(_reading, new InfoDelegate(), WatchUi.SLIDE_LEFT);
+ var r = new Jobs.JobRunner(_job, 1, method(:done));
+ runner = r;
+ r.start();
+ }
+
+ function done() as Void {
+ runner = null;
+ MainMenu.idsScreen = null;
+ if (WatchUi.getCurrentView()[0] == _reading) {
+ WatchUi.switchToView(new InfoView("Entry IDs", _job.lines, "ids"), new InfoDelegate(), WatchUi.SLIDE_IMMEDIATE);
+ }
+ }
+}
+
// After unlocking from the menu's "Change PIN": ask for the new PIN.
class ChangePinAfterUnlock {