Lazy-load the complete pull request workspace #542

Merged
timmy merged 1 commits from timmy/541-lazy-pull-workflow into main 2026-08-11 04:55:20 +00:00
6 changed files with 81 additions and 15 deletions

View File

@ -269,11 +269,8 @@
}
return payload;
}
const reviewController = createReviewController({ fetchJson: fetchReviewJson, storage: localStorage });
const wrapPreference = createReviewController.createWrapPreference({
storage: localStorage,
mobile: window.matchMedia('(max-width: 600px)').matches,
});
let reviewController = null;
let wrapPreference = null;
const issueController = createIssueSheet({ fetchJson: fetchReviewJson, storage: localStorage });
const issueAttachmentController = issueAttachment.mount({
input: qs('#issue-attachment'),
@ -438,7 +435,27 @@
});
}
if (Object.values(sharedLaunch).some(Boolean)) await ensureIssueCapture();
const pullController = createPullSheet({ fetchJson: fetchReviewJson, storage: localStorage });
const pullWorkflowFeatures = createFeatureLoader({
document,
urls: {
'pull-workflow': document.querySelector('meta[name="stackchain-feature-pull-workflow"]')?.content || '',
},
});
let pullController = null;
async function ensurePullWorkflow(trigger = null) {
return await pullWorkflowFeatures.run('pull-workflow', {
trigger, status: qs('#my-work-action-status'), retryLabel:'Tap the work card to retry.',
}, () => {
if (!pullController) pullController = createPullSheet({ fetchJson: fetchReviewJson, storage: localStorage });
if (!reviewController) reviewController = createReviewController({ fetchJson: fetchReviewJson, storage: localStorage });
if (!wrapPreference) {
wrapPreference = createReviewController.createWrapPreference({
storage: localStorage,
mobile: window.matchMedia('(max-width: 600px)').matches,
});
}
});
}
const draftInbox = createDraftInbox({ storage: localStorage, getCurrentLogin: () => activeFlushLogin });
outboxCoordinator.subscribe(() => refreshMyWorkView());
const offlineWorkStore = createOfflineWorkStore({ storage: localStorage, indexedDB:window.indexedDB });
@ -456,8 +473,13 @@
retry.hidden = status.failed === 0;
}
const offlineToday = createOfflineToday({
loadDetail: item => item.is_review ? reviewController.load(item) :
item.kind === 'pull' ? pullController.load(item) : issueController.load(item),
loadDetail: async item => {
if (item.is_review || item.kind === 'pull') {
if (!await ensurePullWorkflow()) throw new Error('Pull workspace is unavailable.');
return item.is_review ? reviewController.load(item) : pullController.load(item);
}
return issueController.load(item);
},
loadSavedDetail: (login, item) => offlineWorkStore.loadDetail(login, item),
saveDetail: (login, item, detail) => offlineWorkStore.saveDetail(login, item, detail),
onStatus: renderOfflineTodayStatus,
@ -2588,6 +2610,7 @@
async function openPullSheet(item, trigger, offlineDetail = null) {
if (!item) return;
if (!await ensurePullWorkflow(trigger)) return;
if (!sameWorkTarget(selectedPull, item)) pullAttachmentController.clear();
qs('#pull-review').inert = false;
selectedPull = item;
@ -3144,6 +3167,7 @@
}
async function openReviewSheet(item, trigger, cachedDetail = null) {
if (!item || !await ensurePullWorkflow(trigger)) return;
selectedReview = item;
reviewTrigger = trigger;
offlineReview = Boolean(cachedDetail);

View File

@ -36,6 +36,7 @@ function createFeatureLoader({ document, urls, timeoutMs = 10000 }) {
async function run(name, elements, callback) {
const trigger = elements?.trigger;
const status = elements?.status;
const retryLabel = elements?.retryLabel || 'Tap New issue to retry.';
if (trigger) trigger.disabled = true;
if (status) status.textContent = 'Loading ' + name.replace(/-/g, ' ') + '…';
try {
@ -45,8 +46,9 @@ function createFeatureLoader({ document, urls, timeoutMs = 10000 }) {
return true;
} catch (_error) {
if (status) {
const label = name === 'issue-capture' ? 'Issue capture' : name.replace(/-/g, ' ');
status.textContent = label + ' could not load. Tap New issue to retry.';
const label = name === 'issue-capture' ? 'Issue capture' :
name === 'pull-workflow' ? 'Pull workspace' : name.replace(/-/g, ' ');
status.textContent = label + ' could not load. ' + retryLabel;
}
return false;
} finally {

View File

@ -13,7 +13,10 @@ import rjsmin
SCRIPT_TAG = re.compile(r'^<script src="(static/[^"?]+\.js)"></script>$', re.MULTILINE)
WORKER_RUNTIME_SOURCE = "static/background-issue-sync.js"
FEATURE_SOURCES = {"issue-capture": ("static/create-issue-sheet.js",)}
FEATURE_SOURCES = {
"issue-capture": ("static/create-issue-sheet.js",),
"pull-workflow": ("static/pull-sheet.js", "static/review-sheet.js"),
}
CACHE_DECLARATION = re.compile(
r"const CACHE = 'stackchain-dashboard-shell-(?:v\d+|[0-9a-f]{16})';"
)

View File

@ -68,4 +68,24 @@ console.log(JSON.stringify({loading,failedState,first,second,opened,finalStatus:
"second": True,
"opened": 1,
"finalStatus": "",
}
}
def test_pull_workspace_failure_is_accessible_and_retryable_from_same_card():
result = run_loader("""
const trigger={disabled:false}; const status={textContent:''}; let opened=0;
const loader=createFeatureLoader({document, urls:{'pull-workflow':'feature-pull.js'}, timeoutMs:100});
const failed=loader.run('pull-workflow',{trigger,status,retryLabel:'Tap the work card to retry.'},()=>{opened++;});
state.node.onerror(); const first=await failed;
const failedStatus=status.textContent;
const retry=loader.run('pull-workflow',{trigger,status,retryLabel:'Tap the work card to retry.'},()=>{opened++;});
state.node.onload(); const second=await retry;
console.log(JSON.stringify({first,second,failedStatus,opened,appends:state.appends}));
""")
assert result == {
"first": False,
"second": True,
"failedStatus": "Pull workspace could not load. Tap the work card to retry.",
"opened": 1,
"appends": 2,
}

View File

@ -42,15 +42,23 @@ def test_page_runtime_is_one_deterministic_content_addressed_bundle(tmp_path):
assert changed.runtime_bytes != first.runtime_bytes
def test_issue_capture_is_a_stable_lazy_feature_chunk(tmp_path):
def test_product_workflows_are_stable_lazy_feature_chunks(tmp_path):
first = build_frontend(FRONTEND)
assert set(first.feature_bundles) == {"issue-capture"}
assert set(first.feature_bundles) == {"issue-capture", "pull-workflow"}
capture = first.feature_bundles["issue-capture"]
pull_workflow = first.feature_bundles["pull-workflow"]
assert b"function createIssueCapture" not in first.runtime_bytes
assert b"function createIssueCapture" in capture.runtime_bytes
assert b"function createPullSheet" not in first.runtime_bytes
assert b"function createReviewController" not in first.runtime_bytes
assert b"function createPullSheet" in pull_workflow.runtime_bytes
assert b"function createReviewController" in pull_workflow.runtime_bytes
assert len(first.runtime_gzip_bytes) <= 95 * 1024
assert f'name="stackchain-feature-issue-capture" content="{capture.runtime_name}"' in first.dashboard_html
assert f'name="stackchain-feature-pull-workflow" content="{pull_workflow.runtime_name}"' in first.dashboard_html
assert f"BASE + '{capture.runtime_name}'" in first.service_worker_source
assert f"BASE + '{pull_workflow.runtime_name}'" in first.service_worker_source
changed_frontend = tmp_path / "frontend"
shutil.copytree(FRONTEND, changed_frontend)
@ -61,6 +69,12 @@ def test_issue_capture_is_a_stable_lazy_feature_chunk(tmp_path):
assert changed.runtime_name == first.runtime_name
assert changed.feature_bundles["issue-capture"].runtime_name != capture.runtime_name
pull_source = changed_frontend / "pull-sheet.js"
pull_source.write_text(pull_source.read_text() + "\n// pull-workflow-only change\n")
pull_changed = build_frontend(changed_frontend)
assert pull_changed.runtime_name == first.runtime_name
assert pull_changed.feature_bundles["pull-workflow"].runtime_name != pull_workflow.runtime_name
@pytest.mark.anyio
async def test_feature_chunk_is_immutable_and_rejects_unknown_revision():

View File

@ -4279,7 +4279,10 @@ async def test_assigned_pulls_open_accessible_mobile_completion_sheet():
assert "pullConversation.append(comment)" in html
assert 'id="pull-comment"' in html and 'maxlength="10000"' in html
assert 'id="merge-pull"' in html and 'id="open-pull-gitea"' in html
assert '<script src="static/pull-sheet.js"></script>' in html
assert 'stackchain-feature-pull-workflow' in html
assert "pullWorkflowFeatures.run('pull-workflow'" in html
assert "let pullController = null" in html
assert "let reviewController = null" in html
assert "pullController.load(item)" in html
assert "createPullSheet.renderFile" in html
assert "pullController.toggleReviewed" in html