Merge pull request 'Start an empty Today plan from the mobile Work dock' (#612) from timmy/611-mobile-work-plan-today into main
All checks were successful
CI / lint (push) Successful in 1m27s
CI / build-release (push) Successful in 7s
CI / release-candidate (push) Successful in 7s

This commit is contained in:
rockachopa 2026-08-12 02:28:30 +00:00
commit 8c32a9dd81
4 changed files with 18 additions and 5 deletions

View File

@ -67,9 +67,11 @@
isTodayActive: () => workSession.checkpointed(), isTodayActive: () => workSession.checkpointed(),
isTodayResumable: () => workSession.resumable(), isTodayResumable: () => workSession.resumable(),
getTodayCount: () => todayMyWork.length, getTodayCount: () => todayMyWork.length,
getEligibleCount: () => activeMyWork.length,
continueToday: continueTodaySession, continueToday: continueTodaySession,
resumeToday: resumeTodaySession, resumeToday: resumeTodaySession,
startToday: startTodaySession, startToday: startTodaySession,
planToday: () => openPlanToday(mobileTaskButtons.work),
openFallback: openMobileWorkFallback, openFallback: openMobileWorkFallback,
}); });
const mobileTaskDock = createMobileTaskDock({ const mobileTaskDock = createMobileTaskDock({

View File

@ -43,6 +43,7 @@
continue: ['Continue', 'Continue Today'], continue: ['Continue', 'Continue Today'],
resume: ['Resume', 'Resume Today'], resume: ['Resume', 'Resume Today'],
start: ['Start', 'Start Today'], start: ['Start', 'Start Today'],
plan: ['Plan', 'Plan Today'],
work: ['Work', 'Work'], work: ['Work', 'Work'],
}; };
const workMode = labels[mode] ? mode : 'work'; const workMode = labels[mode] ? mode : 'work';

View File

@ -6,6 +6,7 @@
if (options.isTodayActive()) return 'continue'; if (options.isTodayActive()) return 'continue';
if (options.getTodayCount() > 0 && options.isTodayResumable()) return 'resume'; if (options.getTodayCount() > 0 && options.isTodayResumable()) return 'resume';
if (options.getTodayCount() > 0) return 'start'; if (options.getTodayCount() > 0) return 'start';
if (options.getEligibleCount() > 0) return 'plan';
return 'work'; return 'work';
} }
@ -14,6 +15,7 @@
if (current === 'continue') options.continueToday(); if (current === 'continue') options.continueToday();
else if (current === 'resume') options.resumeToday(); else if (current === 'resume') options.resumeToday();
else if (current === 'start') options.startToday(); else if (current === 'start') options.startToday();
else if (current === 'plan') options.planToday();
else options.openFallback(); else options.openFallback();
return current; return current;
} }

View File

@ -12,18 +12,20 @@ ENTRY = Path(__file__).resolve().parents[1] / "frontend" / "mobile-work-entry.js
TIMER = Path(__file__).resolve().parents[1] / "frontend" / "today-timer.js" TIMER = Path(__file__).resolve().parents[1] / "frontend" / "today-timer.js"
def test_mobile_work_entry_prioritizes_continue_resume_start_then_fallback(): def test_mobile_work_entry_prioritizes_continue_resume_start_plan_then_fallback():
script = f""" script = f"""
const createEntry = require({json.dumps(str(ENTRY))}); const createEntry = require({json.dumps(str(ENTRY))});
const state = {{active:true, resumable:true, today:2}}; const state = {{active:true, resumable:true, today:2, eligible:3}};
const calls = []; const calls = [];
const entry = createEntry({{ const entry = createEntry({{
isTodayActive: () => state.active, isTodayActive: () => state.active,
isTodayResumable: () => state.resumable, isTodayResumable: () => state.resumable,
getTodayCount: () => state.today, getTodayCount: () => state.today,
getEligibleCount: () => state.eligible,
continueToday: () => calls.push('continue'), continueToday: () => calls.push('continue'),
resumeToday: () => calls.push('resume'), resumeToday: () => calls.push('resume'),
startToday: () => calls.push('start'), startToday: () => calls.push('start'),
planToday: () => calls.push('plan'),
openFallback: () => calls.push('fallback'), openFallback: () => calls.push('fallback'),
}}); }});
const modes = []; const modes = [];
@ -31,6 +33,7 @@ modes.push(entry.open());
state.active = false; modes.push(entry.open()); state.active = false; modes.push(entry.open());
state.resumable = false; modes.push(entry.open()); state.resumable = false; modes.push(entry.open());
state.today = 0; modes.push(entry.open()); state.today = 0; modes.push(entry.open());
state.eligible = 0; modes.push(entry.open());
process.stdout.write(JSON.stringify({{modes, calls}})); process.stdout.write(JSON.stringify({{modes, calls}}));
""" """
result = subprocess.run( result = subprocess.run(
@ -39,8 +42,8 @@ process.stdout.write(JSON.stringify({{modes, calls}}));
assert result.returncode == 0, result.stderr assert result.returncode == 0, result.stderr
assert json.loads(result.stdout) == { assert json.loads(result.stdout) == {
"modes": ["continue", "resume", "start", "work"], "modes": ["continue", "resume", "start", "plan", "work"],
"calls": ["continue", "resume", "start", "fallback"], "calls": ["continue", "resume", "start", "plan", "fallback"],
} }
@ -125,9 +128,11 @@ dock.updateWork('resume', 0);
const resuming = {{ text:workLabel.textContent, label:work.attributes['aria-label'] }}; const resuming = {{ text:workLabel.textContent, label:work.attributes['aria-label'] }};
dock.updateWork('start', 0); dock.updateWork('start', 0);
const starting = {{ text:workLabel.textContent, label:work.attributes['aria-label'] }}; const starting = {{ text:workLabel.textContent, label:work.attributes['aria-label'] }};
dock.updateWork('plan', 0);
const planning = {{ text:workLabel.textContent, label:work.attributes['aria-label'] }};
dock.updateWork('work', 0); dock.updateWork('work', 0);
process.stdout.write(JSON.stringify({{ process.stdout.write(JSON.stringify({{
continuing, resuming, starting, continuing, resuming, starting, planning,
fallback:{{ text:workLabel.textContent, label:work.attributes['aria-label'] }}, fallback:{{ text:workLabel.textContent, label:work.attributes['aria-label'] }},
}})); }}));
""" """
@ -143,6 +148,7 @@ process.stdout.write(JSON.stringify({{
}, },
"resuming": {"text": "Resume", "label": "Resume Today"}, "resuming": {"text": "Resume", "label": "Resume Today"},
"starting": {"text": "Start", "label": "Start Today"}, "starting": {"text": "Start", "label": "Start Today"},
"planning": {"text": "Plan", "label": "Plan Today"},
"fallback": {"text": "Work", "label": "Work"}, "fallback": {"text": "Work", "label": "Work"},
} }
@ -244,9 +250,11 @@ async def test_dashboard_wires_mobile_work_dock_into_today_session_lifecycle():
assert "isTodayActive: () => workSession.checkpointed()" in html assert "isTodayActive: () => workSession.checkpointed()" in html
assert "isTodayResumable: () => workSession.resumable()" in html assert "isTodayResumable: () => workSession.resumable()" in html
assert "getTodayCount: () => todayMyWork.length" in html assert "getTodayCount: () => todayMyWork.length" in html
assert "getEligibleCount: () => activeMyWork.length" in html
assert "continueToday: continueTodaySession" in html assert "continueToday: continueTodaySession" in html
assert "resumeToday: resumeTodaySession" in html assert "resumeToday: resumeTodaySession" in html
assert "startToday: startTodaySession" in html assert "startToday: startTodaySession" in html
assert "planToday: () => openPlanToday(mobileTaskButtons.work)" in html
assert "work: () => mobileWorkEntry.open()" in html assert "work: () => mobileWorkEntry.open()" in html
assert "mobileTaskDock.updateWork(mobileWorkEntry.mode())" in html assert "mobileTaskDock.updateWork(mobileWorkEntry.mode())" in html
assert "mobileTaskDock.updateAttention(countMyWork(activeMyWork).attention)" in html assert "mobileTaskDock.updateAttention(countMyWork(activeMyWork).attention)" in html