Compare commits
No commits in common. "e7d51969876f6586b96a5bc9cbaf42b493a86a61" and "4ac5a0f49935b24ba204ef35af81f9e699266946" have entirely different histories.
e7d5196987
...
4ac5a0f499
|
|
@ -1,33 +0,0 @@
|
||||||
(function (root, factory) {
|
|
||||||
if (typeof module === 'object' && module.exports) module.exports = factory;
|
|
||||||
else root.createAgendaSessionLauncher = factory;
|
|
||||||
})(typeof self !== 'undefined' ? self : this, function createAgendaSessionLauncher(options) {
|
|
||||||
let pending = null;
|
|
||||||
|
|
||||||
function open() {
|
|
||||||
if (pending) return pending;
|
|
||||||
options.selectAgenda();
|
|
||||||
pending = Promise.resolve()
|
|
||||||
.then(() => options.discover())
|
|
||||||
.then(complete => {
|
|
||||||
if (!options.isAgendaSelected()) return 'cancelled';
|
|
||||||
if (complete === false || options.hasMore()) {
|
|
||||||
options.announce('Agenda check paused. Retry to check older assigned deadlines.');
|
|
||||||
return 'incomplete';
|
|
||||||
}
|
|
||||||
const opened = options.hasCheckpoint() ? options.resume() : options.start();
|
|
||||||
if (!opened) options.announce('No deadlines are ready in Agenda.');
|
|
||||||
return opened ? 'opened' : 'empty';
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
if (options.isAgendaSelected()) {
|
|
||||||
options.announce('Agenda check paused. Retry to check older assigned deadlines.');
|
|
||||||
}
|
|
||||||
return 'incomplete';
|
|
||||||
})
|
|
||||||
.finally(() => { pending = null; });
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {open};
|
|
||||||
});
|
|
||||||
|
|
@ -1210,18 +1210,11 @@
|
||||||
qs('[data-work-filter="today"]').click();
|
qs('[data-work-filter="today"]').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
const agendaSessionLauncher = createAgendaSessionLauncher({
|
|
||||||
selectAgenda: () => selectMobileQueue('agenda'),
|
|
||||||
discover: completeAgendaIssues,
|
|
||||||
isAgendaSelected: () => selectedWorkFilter === 'agenda',
|
|
||||||
hasMore: () => Boolean(workPagination.issue?.has_more),
|
|
||||||
hasCheckpoint: () => Boolean(agendaSessionCheckpoint.read()),
|
|
||||||
resume: () => workSession.resume(),
|
|
||||||
start: () => workSession.start(),
|
|
||||||
announce: message => { qs('#my-work-action-status').textContent = message; },
|
|
||||||
});
|
|
||||||
function openAgendaSession() {
|
function openAgendaSession() {
|
||||||
return agendaSessionLauncher.open();
|
selectMobileQueue('agenda');
|
||||||
|
const opened = agendaSessionCheckpoint.read() ? workSession.resume() : workSession.start();
|
||||||
|
if (!opened) qs('#my-work-action-status').textContent = 'No deadlines are ready in Agenda.';
|
||||||
|
return opened ? 'opened' : 'empty';
|
||||||
}
|
}
|
||||||
|
|
||||||
let todayReadinessTrigger = null;
|
let todayReadinessTrigger = null;
|
||||||
|
|
@ -2347,20 +2340,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function completeAgendaIssues() {
|
async function completeAgendaIssues() {
|
||||||
if (selectedWorkFilter !== 'agenda') return false;
|
if (selectedWorkFilter !== 'agenda' || !workPagination.issue?.has_more || !lastContextSnapshot) return;
|
||||||
if (!workPagination.issue?.has_more) return true;
|
|
||||||
if (!lastContextSnapshot) return false;
|
|
||||||
agendaChecking = true;
|
agendaChecking = true;
|
||||||
qs('#my-work-action-status').textContent = 'Checking all assigned deadlines…';
|
qs('#my-work-action-status').textContent = 'Checking all assigned deadlines…';
|
||||||
renderMyWork();
|
renderMyWork();
|
||||||
const complete = await workPager.loadAll('issue', () => lastContextSnapshot?.issues || []);
|
const complete = await workPager.loadAll('issue', () => lastContextSnapshot?.issues || []);
|
||||||
agendaChecking = false;
|
agendaChecking = false;
|
||||||
if (selectedWorkFilter !== 'agenda') return false;
|
if (selectedWorkFilter !== 'agenda') return;
|
||||||
qs('#my-work-action-status').textContent = complete ?
|
qs('#my-work-action-status').textContent = complete ?
|
||||||
'All assigned deadlines checked.' :
|
'All assigned deadlines checked.' :
|
||||||
'Agenda check paused. Retry to check older assigned deadlines.';
|
'Agenda check paused. Retry to check older assigned deadlines.';
|
||||||
renderMyWork();
|
renderMyWork();
|
||||||
return complete && !workPagination.issue?.has_more;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDrafts() {
|
function renderDrafts() {
|
||||||
|
|
|
||||||
|
|
@ -1055,7 +1055,6 @@
|
||||||
<script src="static/mobile-task-dock.js"></script>
|
<script src="static/mobile-task-dock.js"></script>
|
||||||
<script src="static/mobile-work-entry.js"></script>
|
<script src="static/mobile-work-entry.js"></script>
|
||||||
<script src="static/mobile-queue-launcher.js"></script>
|
<script src="static/mobile-queue-launcher.js"></script>
|
||||||
<script src="static/agenda-session-launcher.js"></script>
|
|
||||||
<script src="static/mobile-launch.js"></script>
|
<script src="static/mobile-launch.js"></script>
|
||||||
<script src="static/install-app.js"></script>
|
<script src="static/install-app.js"></script>
|
||||||
<script src="static/mobile-device-setup.js"></script>
|
<script src="static/mobile-device-setup.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ const SHELL = [
|
||||||
BASE + 'static/mobile-task-dock.js',
|
BASE + 'static/mobile-task-dock.js',
|
||||||
BASE + 'static/mobile-work-entry.js',
|
BASE + 'static/mobile-work-entry.js',
|
||||||
BASE + 'static/mobile-queue-launcher.js',
|
BASE + 'static/mobile-queue-launcher.js',
|
||||||
BASE + 'static/agenda-session-launcher.js',
|
|
||||||
BASE + 'static/mobile-launch.js',
|
BASE + 'static/mobile-launch.js',
|
||||||
BASE + 'static/install-app.js',
|
BASE + 'static/install-app.js',
|
||||||
BASE + 'static/mobile-device-setup.js',
|
BASE + 'static/mobile-device-setup.js',
|
||||||
|
|
|
||||||
|
|
@ -40,89 +40,6 @@ COMMENT_ACTIONS = Path(__file__).parents[1] / "frontend" / "comment-actions.js"
|
||||||
PICK_WORK = Path(__file__).parents[1] / "frontend" / "pick-work.js"
|
PICK_WORK = Path(__file__).parents[1] / "frontend" / "pick-work.js"
|
||||||
WORK_ROUTE = Path(__file__).parents[1] / "frontend" / "work-route.js"
|
WORK_ROUTE = Path(__file__).parents[1] / "frontend" / "work-route.js"
|
||||||
UPDATE_OWNERSHIP = Path(__file__).parents[1] / "frontend" / "update-ownership.js"
|
UPDATE_OWNERSHIP = Path(__file__).parents[1] / "frontend" / "update-ownership.js"
|
||||||
AGENDA_SESSION_LAUNCHER = Path(__file__).parents[1] / "frontend" / "agenda-session-launcher.js"
|
|
||||||
|
|
||||||
|
|
||||||
def test_agenda_session_launch_waits_for_complete_single_flight_discovery():
|
|
||||||
script = f"""
|
|
||||||
const createLauncher = require({json.dumps(str(AGENDA_SESSION_LAUNCHER))});
|
|
||||||
let finishDiscovery;
|
|
||||||
let discoveries = 0;
|
|
||||||
let opens = 0;
|
|
||||||
let queue = 'agenda';
|
|
||||||
const launcher = createLauncher({{
|
|
||||||
selectAgenda: () => {{ queue = 'agenda'; }},
|
|
||||||
discover: () => {{
|
|
||||||
discoveries += 1;
|
|
||||||
return new Promise(resolve => {{ finishDiscovery = resolve; }});
|
|
||||||
}},
|
|
||||||
isAgendaSelected: () => queue === 'agenda',
|
|
||||||
hasMore: () => false,
|
|
||||||
hasCheckpoint: () => true,
|
|
||||||
resume: () => {{ opens += 1; return true; }},
|
|
||||||
start: () => {{ throw new Error('must resume'); }},
|
|
||||||
announce: () => {{}},
|
|
||||||
}});
|
|
||||||
const first = launcher.open();
|
|
||||||
const second = launcher.open();
|
|
||||||
Promise.resolve().then(() => {{
|
|
||||||
const before = {{discoveries, opens, same:first === second}};
|
|
||||||
finishDiscovery(true);
|
|
||||||
return Promise.all([first, second]).then(results => {{
|
|
||||||
process.stdout.write(JSON.stringify({{before, results, discoveries, opens}}));
|
|
||||||
}});
|
|
||||||
}});
|
|
||||||
"""
|
|
||||||
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
|
|
||||||
|
|
||||||
assert result.returncode == 0, result.stderr
|
|
||||||
assert json.loads(result.stdout) == {
|
|
||||||
"before": {"discoveries": 1, "opens": 0, "same": True},
|
|
||||||
"results": ["opened", "opened"],
|
|
||||||
"discoveries": 1,
|
|
||||||
"opens": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_agenda_session_launch_preserves_progress_when_discovery_fails_or_queue_changes():
|
|
||||||
script = f"""
|
|
||||||
const createLauncher = require({json.dumps(str(AGENDA_SESSION_LAUNCHER))});
|
|
||||||
async function scenario(mode) {{
|
|
||||||
let queue = 'agenda';
|
|
||||||
let opens = 0;
|
|
||||||
const messages = [];
|
|
||||||
const launcher = createLauncher({{
|
|
||||||
selectAgenda: () => {{ queue = 'agenda'; }},
|
|
||||||
discover: async () => {{
|
|
||||||
if (mode === 'leave') queue = 'today';
|
|
||||||
if (mode === 'throw') throw new Error('offline');
|
|
||||||
return mode !== 'incomplete';
|
|
||||||
}},
|
|
||||||
isAgendaSelected: () => queue === 'agenda',
|
|
||||||
hasMore: () => mode === 'incomplete',
|
|
||||||
hasCheckpoint: () => true,
|
|
||||||
resume: () => {{ opens += 1; return true; }},
|
|
||||||
start: () => {{ opens += 1; return true; }},
|
|
||||||
announce: message => messages.push(message),
|
|
||||||
}});
|
|
||||||
return {{result:await launcher.open(), opens, messages}};
|
|
||||||
}}
|
|
||||||
Promise.all(['incomplete', 'throw', 'leave'].map(scenario)).then(results =>
|
|
||||||
process.stdout.write(JSON.stringify(results))
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
|
|
||||||
|
|
||||||
assert result.returncode == 0, result.stderr
|
|
||||||
assert json.loads(result.stdout) == [
|
|
||||||
{"result": "incomplete", "opens": 0, "messages": [
|
|
||||||
"Agenda check paused. Retry to check older assigned deadlines."
|
|
||||||
]},
|
|
||||||
{"result": "incomplete", "opens": 0, "messages": [
|
|
||||||
"Agenda check paused. Retry to check older assigned deadlines."
|
|
||||||
]},
|
|
||||||
{"result": "cancelled", "opens": 0, "messages": []},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_protect_today_route_is_an_explicit_agenda_action():
|
def test_protect_today_route_is_an_explicit_agenda_action():
|
||||||
|
|
@ -388,11 +305,7 @@ process.stdout.write(JSON.stringify({{
|
||||||
async def test_mobile_agenda_launcher_starts_or_resumes_the_durable_agenda_session():
|
async def test_mobile_agenda_launcher_starts_or_resumes_the_durable_agenda_session():
|
||||||
html = await dashboard()
|
html = await dashboard()
|
||||||
|
|
||||||
assert '<script src="static/agenda-session-launcher.js"></script>' in html
|
|
||||||
assert "openAgenda: openAgendaSession" in html
|
assert "openAgenda: openAgendaSession" in html
|
||||||
assert "const agendaSessionLauncher = createAgendaSessionLauncher({" in html
|
|
||||||
assert "discover: completeAgendaIssues" in html
|
|
||||||
assert "hasMore: () => Boolean(workPagination.issue?.has_more)" in html
|
|
||||||
assert "key: 'stackchain.agenda-session.v1'" in html
|
assert "key: 'stackchain.agenda-session.v1'" in html
|
||||||
assert "checkpoint: () => selectedWorkFilter === 'agenda' ? agendaSessionCheckpoint : sessionCheckpoint" in html
|
assert "checkpoint: () => selectedWorkFilter === 'agenda' ? agendaSessionCheckpoint : sessionCheckpoint" in html
|
||||||
assert "checkpointEnabled: () => ['today', 'agenda'].includes(selectedWorkFilter)" in html
|
assert "checkpointEnabled: () => ['today', 'agenda'].includes(selectedWorkFilter)" in html
|
||||||
|
|
|
||||||
|
|
@ -786,7 +786,6 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
|
||||||
"/dashboard/static/mobile-task-dock.js",
|
"/dashboard/static/mobile-task-dock.js",
|
||||||
"/dashboard/static/mobile-work-entry.js",
|
"/dashboard/static/mobile-work-entry.js",
|
||||||
"/dashboard/static/mobile-queue-launcher.js",
|
"/dashboard/static/mobile-queue-launcher.js",
|
||||||
"/dashboard/static/agenda-session-launcher.js",
|
|
||||||
"/dashboard/static/mobile-launch.js",
|
"/dashboard/static/mobile-launch.js",
|
||||||
"/dashboard/static/install-app.js",
|
"/dashboard/static/install-app.js",
|
||||||
"/dashboard/static/mobile-device-setup.js",
|
"/dashboard/static/mobile-device-setup.js",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user