perf: lazy-load pull request workspace (Closes #541)
This commit is contained in:
parent
3605c42651
commit
7d4ff1f095
|
|
@ -269,11 +269,8 @@
|
||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
const reviewController = createReviewController({ fetchJson: fetchReviewJson, storage: localStorage });
|
let reviewController = null;
|
||||||
const wrapPreference = createReviewController.createWrapPreference({
|
let wrapPreference = null;
|
||||||
storage: localStorage,
|
|
||||||
mobile: window.matchMedia('(max-width: 600px)').matches,
|
|
||||||
});
|
|
||||||
const issueController = createIssueSheet({ fetchJson: fetchReviewJson, storage: localStorage });
|
const issueController = createIssueSheet({ fetchJson: fetchReviewJson, storage: localStorage });
|
||||||
const issueAttachmentController = issueAttachment.mount({
|
const issueAttachmentController = issueAttachment.mount({
|
||||||
input: qs('#issue-attachment'),
|
input: qs('#issue-attachment'),
|
||||||
|
|
@ -438,7 +435,27 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (Object.values(sharedLaunch).some(Boolean)) await ensureIssueCapture();
|
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 });
|
const draftInbox = createDraftInbox({ storage: localStorage, getCurrentLogin: () => activeFlushLogin });
|
||||||
outboxCoordinator.subscribe(() => refreshMyWorkView());
|
outboxCoordinator.subscribe(() => refreshMyWorkView());
|
||||||
const offlineWorkStore = createOfflineWorkStore({ storage: localStorage, indexedDB:window.indexedDB });
|
const offlineWorkStore = createOfflineWorkStore({ storage: localStorage, indexedDB:window.indexedDB });
|
||||||
|
|
@ -456,8 +473,13 @@
|
||||||
retry.hidden = status.failed === 0;
|
retry.hidden = status.failed === 0;
|
||||||
}
|
}
|
||||||
const offlineToday = createOfflineToday({
|
const offlineToday = createOfflineToday({
|
||||||
loadDetail: item => item.is_review ? reviewController.load(item) :
|
loadDetail: async item => {
|
||||||
item.kind === 'pull' ? pullController.load(item) : issueController.load(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),
|
loadSavedDetail: (login, item) => offlineWorkStore.loadDetail(login, item),
|
||||||
saveDetail: (login, item, detail) => offlineWorkStore.saveDetail(login, item, detail),
|
saveDetail: (login, item, detail) => offlineWorkStore.saveDetail(login, item, detail),
|
||||||
onStatus: renderOfflineTodayStatus,
|
onStatus: renderOfflineTodayStatus,
|
||||||
|
|
@ -2588,6 +2610,7 @@
|
||||||
|
|
||||||
async function openPullSheet(item, trigger, offlineDetail = null) {
|
async function openPullSheet(item, trigger, offlineDetail = null) {
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
if (!await ensurePullWorkflow(trigger)) return;
|
||||||
if (!sameWorkTarget(selectedPull, item)) pullAttachmentController.clear();
|
if (!sameWorkTarget(selectedPull, item)) pullAttachmentController.clear();
|
||||||
qs('#pull-review').inert = false;
|
qs('#pull-review').inert = false;
|
||||||
selectedPull = item;
|
selectedPull = item;
|
||||||
|
|
@ -3144,6 +3167,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openReviewSheet(item, trigger, cachedDetail = null) {
|
async function openReviewSheet(item, trigger, cachedDetail = null) {
|
||||||
|
if (!item || !await ensurePullWorkflow(trigger)) return;
|
||||||
selectedReview = item;
|
selectedReview = item;
|
||||||
reviewTrigger = trigger;
|
reviewTrigger = trigger;
|
||||||
offlineReview = Boolean(cachedDetail);
|
offlineReview = Boolean(cachedDetail);
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ function createFeatureLoader({ document, urls, timeoutMs = 10000 }) {
|
||||||
async function run(name, elements, callback) {
|
async function run(name, elements, callback) {
|
||||||
const trigger = elements?.trigger;
|
const trigger = elements?.trigger;
|
||||||
const status = elements?.status;
|
const status = elements?.status;
|
||||||
|
const retryLabel = elements?.retryLabel || 'Tap New issue to retry.';
|
||||||
if (trigger) trigger.disabled = true;
|
if (trigger) trigger.disabled = true;
|
||||||
if (status) status.textContent = 'Loading ' + name.replace(/-/g, ' ') + '…';
|
if (status) status.textContent = 'Loading ' + name.replace(/-/g, ' ') + '…';
|
||||||
try {
|
try {
|
||||||
|
|
@ -45,8 +46,9 @@ function createFeatureLoader({ document, urls, timeoutMs = 10000 }) {
|
||||||
return true;
|
return true;
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
if (status) {
|
if (status) {
|
||||||
const label = name === 'issue-capture' ? 'Issue capture' : name.replace(/-/g, ' ');
|
const label = name === 'issue-capture' ? 'Issue capture' :
|
||||||
status.textContent = label + ' could not load. Tap New issue to retry.';
|
name === 'pull-workflow' ? 'Pull workspace' : name.replace(/-/g, ' ');
|
||||||
|
status.textContent = label + ' could not load. ' + retryLabel;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,10 @@ import rjsmin
|
||||||
|
|
||||||
SCRIPT_TAG = re.compile(r'^<script src="(static/[^"?]+\.js)"></script>$', re.MULTILINE)
|
SCRIPT_TAG = re.compile(r'^<script src="(static/[^"?]+\.js)"></script>$', re.MULTILINE)
|
||||||
WORKER_RUNTIME_SOURCE = "static/background-issue-sync.js"
|
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(
|
CACHE_DECLARATION = re.compile(
|
||||||
r"const CACHE = 'stackchain-dashboard-shell-(?:v\d+|[0-9a-f]{16})';"
|
r"const CACHE = 'stackchain-dashboard-shell-(?:v\d+|[0-9a-f]{16})';"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -68,4 +68,24 @@ console.log(JSON.stringify({loading,failedState,first,second,opened,finalStatus:
|
||||||
"second": True,
|
"second": True,
|
||||||
"opened": 1,
|
"opened": 1,
|
||||||
"finalStatus": "",
|
"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,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,23 @@ def test_page_runtime_is_one_deterministic_content_addressed_bundle(tmp_path):
|
||||||
assert changed.runtime_bytes != first.runtime_bytes
|
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)
|
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"]
|
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" not in first.runtime_bytes
|
||||||
assert b"function createIssueCapture" in capture.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-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 + '{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"
|
changed_frontend = tmp_path / "frontend"
|
||||||
shutil.copytree(FRONTEND, changed_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.runtime_name == first.runtime_name
|
||||||
assert changed.feature_bundles["issue-capture"].runtime_name != capture.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
|
@pytest.mark.anyio
|
||||||
async def test_feature_chunk_is_immutable_and_rejects_unknown_revision():
|
async def test_feature_chunk_is_immutable_and_rejects_unknown_revision():
|
||||||
|
|
|
||||||
|
|
@ -4279,7 +4279,10 @@ async def test_assigned_pulls_open_accessible_mobile_completion_sheet():
|
||||||
assert "pullConversation.append(comment)" in html
|
assert "pullConversation.append(comment)" in html
|
||||||
assert 'id="pull-comment"' in html and 'maxlength="10000"' 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 '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 "pullController.load(item)" in html
|
||||||
assert "createPullSheet.renderFile" in html
|
assert "createPullSheet.renderFile" in html
|
||||||
assert "pullController.toggleReviewed" in html
|
assert "pullController.toggleReviewed" in html
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user