diff --git a/frontend/dashboard.js b/frontend/dashboard.js
index bbee033..59012c2 100644
--- a/frontend/dashboard.js
+++ b/frontend/dashboard.js
@@ -143,6 +143,7 @@
const mobileFirstTask = createMobileFirstTask({
getLogin: () => confirmedOwnerLogin,
hasWork: () => todayMyWork.length > 0 || activeMyWork.length > 0,
+ isTodayActive: () => workSession.checkpointed(),
});
mobileFirstTask.start();
const mobileWorkEntry = createMobileWorkEntry({
diff --git a/frontend/index.html b/frontend/index.html
index 6ecd390..ecee3c9 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1838,8 +1838,8 @@
Choose work, start it, then return to Work whenever you want to continue.
-
-
+
+
diff --git a/frontend/mobile-first-task.js b/frontend/mobile-first-task.js
index 1cb108b..3369c59 100644
--- a/frontend/mobile-first-task.js
+++ b/frontend/mobile-first-task.js
@@ -10,11 +10,13 @@
mediaQuery: win?.matchMedia('(max-width: 600px)') || {matches:false},
eventTarget: win,
sheet: doc?.querySelector('#mobile-first-task'),
+ title: doc?.querySelector('#mobile-first-task-title'),
findButton: doc?.querySelector('#mobile-first-task-find'),
createButton: doc?.querySelector('#mobile-first-task-create'),
setupButton: doc?.querySelector('#mobile-first-task-setup'),
closeButton: doc?.querySelector('#close-mobile-first-task'),
status: doc?.querySelector('#mobile-first-task-status'),
+ isTodayActive: () => false,
onFind: () => doc?.querySelector('#find-work')?.click(),
onCreate: () => doc?.querySelector('#new-issue')?.click(),
onSetup: () => doc?.querySelector('#open-device-setup')?.click(),
@@ -38,7 +40,7 @@
}
function required() {
- return Boolean(options.mediaQuery.matches && account() && !options.hasWork() && !completed());
+ return Boolean(options.mediaQuery.matches && account() && !options.isTodayActive() && !completed());
}
function markComplete() {
@@ -50,9 +52,14 @@
function render() {
const online = options.isOnline();
+ const awaitingStart = options.hasWork();
options.findButton.disabled = !online;
- options.status.textContent = online ?
- 'Choose a task to claim or create one of your own.' :
+ if (options.title) options.title.textContent = awaitingStart ? 'Finish starting your first task' : 'Start your first task';
+ options.findButton.textContent = 'Find & start';
+ options.createButton.textContent = 'Create & start';
+ options.status.textContent = awaitingStart ?
+ 'Your task is ready. Start it from Find or create and start another task.' : online ?
+ 'Choose a task to claim and start, or create and start one of your own.' :
'You are offline. Create a task now and it will stay in Drafts until you reconnect.';
}
@@ -65,12 +72,13 @@
}
function refresh() {
- if (account() && options.hasWork() && !completed()) {
+ if (account() && options.isTodayActive() && !completed()) {
markComplete();
if (options.sheet.open) options.sheet.close();
return 'completed';
}
if (options.sheet.open) render();
+ if (required() && options.hasWork()) return 'awaiting-start';
return required() ? 'required' : 'inactive';
}
diff --git a/tests/test_mobile_first_task.py b/tests/test_mobile_first_task.py
index 69c478d..5968b13 100644
--- a/tests/test_mobile_first_task.py
+++ b/tests/test_mobile_first_task.py
@@ -28,7 +28,7 @@ class Element {{
return json.loads(result.stdout)
-def test_first_task_activation_is_account_bound_and_completes_when_work_appears():
+def test_first_task_activation_is_account_bound_and_completes_only_when_today_is_active():
result = run_node(
"""
const values = new Map();
@@ -36,24 +36,30 @@ const storage = {getItem:key => values.get(key) || null, setItem:(key,value) =>
const sheet = new Element();
let login = 'Timmy';
let hasWork = false;
+let todayActive = false;
const controller = createFirstTask({
- storage, getLogin:() => login, hasWork:() => hasWork, isOnline:() => true,
- mediaQuery:{matches:true}, sheet, findButton:new Element(), createButton:new Element(),
- setupButton:new Element(), closeButton:new Element(), status:new Element(),
- onFind() {}, onCreate() {}, onSetup() {},
+ storage, getLogin:() => login, hasWork:() => hasWork, isTodayActive:() => todayActive,
+ isOnline:() => true, mediaQuery:{matches:true}, sheet, title:new Element(),
+ findButton:new Element(), createButton:new Element(), setupButton:new Element(),
+ closeButton:new Element(), status:new Element(), onFind() {}, onCreate() {}, onSetup() {},
});
const before = [controller.required(), controller.open(), sheet.open];
hasWork = true;
+const awaitingStart = controller.refresh();
+const stillRequired = controller.required();
+todayActive = true;
const completed = controller.refresh();
const timmyRequired = controller.required();
-login = 'alexander'; hasWork = false;
+login = 'alexander'; hasWork = false; todayActive = false;
const alexanderRequired = controller.required();
-process.stdout.write(JSON.stringify({before, completed, sheetOpen:sheet.open, timmyRequired, alexanderRequired, values:[...values]}));
+process.stdout.write(JSON.stringify({before, awaitingStart, stillRequired, completed, sheetOpen:sheet.open, timmyRequired, alexanderRequired, values:[...values]}));
"""
)
assert result == {
"before": [True, True, True],
+ "awaitingStart": "awaiting-start",
+ "stillRequired": True,
"completed": "completed",
"sheetOpen": False,
"timmyRequired": False,
@@ -62,6 +68,37 @@ process.stdout.write(JSON.stringify({before, completed, sheetOpen:sheet.open, ti
}
+def test_first_task_activation_resumes_with_finish_starting_guidance():
+ result = run_node(
+ """
+const sheet = new Element();
+const title = new Element();
+const findButton = new Element();
+const createButton = new Element();
+const status = new Element();
+const controller = createFirstTask({
+ storage:{getItem:() => null, setItem() {}}, getLogin:() => 'timmy', hasWork:() => true,
+ isTodayActive:() => false, isOnline:() => true, mediaQuery:{matches:true}, sheet, title,
+ findButton, createButton, setupButton:new Element(), closeButton:new Element(), status,
+ onFind() {}, onCreate() {}, onSetup() {},
+});
+controller.open();
+process.stdout.write(JSON.stringify({
+ required:controller.required(), title:title.textContent, status:status.textContent,
+ find:findButton.textContent, create:createButton.textContent,
+}));
+"""
+ )
+
+ assert result == {
+ "required": True,
+ "title": "Finish starting your first task",
+ "status": "Your task is ready. Start it from Find or create and start another task.",
+ "find": "Find & start",
+ "create": "Create & start",
+ }
+
+
def test_first_task_activation_routes_existing_flows_and_keeps_create_available_offline():
result = run_node(
"""
@@ -112,11 +149,12 @@ async def test_dashboard_renders_and_wires_phone_safe_first_task_activation():
assert '