feat: enable progressive mobile capture
CI / lint (pull_request) Successful in 3m36s
CI / build-release (pull_request) Successful in 8s
CI / browser-journey (pull_request) Successful in 6m26s
CI / release-candidate (pull_request) Has been skipped

Closes #1407
This commit is contained in:
timmy committed 2026-08-25 20:11:13 +00:00
1 parent a23276f0b8
commit 8bb8fb0cd9
11 files changed
+344 -3

No files matched your search

+6
View File
@@ -1203,6 +1203,12 @@ textarea { resize: vertical; min-height: 120px; }
.voice-conversation-review textarea { box-sizing:border-box; width:100%; min-height:96px; resize:vertical; }
.create-issue-capture-actions { position:sticky; bottom:0; display:grid; grid-template-columns:1fr 1fr; gap:8px; padding:10px 0; padding-bottom:calc(10px + env(safe-area-inset-bottom)); background:#0b1526; }
.create-issue-capture-actions[hidden] { display:none; }
.create-issue-sheet.progressive-capture .mobile-create-issue-nav,
.create-issue-sheet.progressive-capture .create-issue-attachment,
.create-issue-sheet.progressive-capture .create-issue-filing,
.create-issue-sheet.progressive-capture #switch-to-create-pull,
.create-issue-sheet.progressive-capture .today-capture-interruption,
.create-issue-sheet.progressive-capture .shared-content-conflict { display:none; }
.create-issue-filing { display:grid; gap:12px; min-width:0; }
.create-issue-filing[hidden] { display:none; }
.create-issue-attachment { display:grid; gap:8px; min-width:0; }
+6
View File
@@ -1,6 +1,7 @@
(async function(){
const workspaceLifecycle = await (window.stackchainWorkspaceLifecycle || loadWorkspace({ document, window }));
await workspaceLifecycle.optionalReady;
const progressiveCaptureHandoff = window.stackchainProgressiveCapture?.handoff?.();
const progressiveWorkHandoff = window.stackchainProgressiveMyWork?.handoff?.();
window.stackchainProgressiveMyWork?.stop();
const qs = (s, el=document) => el.querySelector(s);
@@ -6426,6 +6427,11 @@
if (selector === '#create-issue-title') scheduleIssueDuplicateCheck();
})
);
if (progressiveCaptureHandoff?.open && await ensureIssueCapture()) {
issueCapture.saveDraft({repository:'', labelIds:[],
title:progressiveCaptureHandoff.title, body:progressiveCaptureHandoff.body});
await openCreateIssueSheet(false);
}
qs('#create-issue-repository').addEventListener('change', event => {
issueTemplatePicker.changeRepository(currentIssueCaptureDraft());
issueOwnerPicker.reset(event.target.value);
+1
View File
@@ -2285,6 +2285,7 @@
<script src="static/offline-today.js"></script>
<script src="static/my-work.js"></script>
<script src="static/progressive-my-work.js"></script>
<script src="static/progressive-capture.js"></script>
<script src="static/agenda-replan.js"></script>
<script src="static/agenda-calendar.js"></script>
<script src="static/protect-today.js"></script>
+128
View File
@@ -0,0 +1,128 @@
function createProgressiveCapture({
document,
storage,
getLogin = () => '',
createCaptures = globalThis.createUnfiledCaptures,
requestFullWorkspace = async () => {
const lifecycle = await globalThis.stackchainWorkspaceLifecycle;
return lifecycle?.optionalReady;
},
createId,
now,
}) {
const button = document.querySelector('[data-mobile-task="new"]');
const root = document.querySelector('#create-issue-sheet');
const title = document.querySelector('#create-issue-title');
const body = document.querySelector('#create-issue-body');
const heading = document.querySelector('#create-issue-heading');
const save = document.querySelector('#save-unfiled-issue');
const file = document.querySelector('#file-new-issue');
const cancel = document.querySelector('#cancel-new-issue');
const status = document.querySelector('#my-work-action-status');
const captures = createCaptures({
storage,
getCaptureLogin:getLogin,
getCurrentLogin:getLogin,
...(createId ? {createId} : {}),
...(now ? {now} : {}),
});
const listeners = [];
let started = false;
let saving = null;
let saved = false;
let handedOff = false;
function listen(element, name, callback) {
element?.addEventListener?.(name, callback);
listeners.push([element, name, callback]);
}
function close() {
root?.classList.remove('open');
}
function open(event) {
saved = false;
if (heading) heading.textContent = 'Capture work';
root?.classList.add('open');
title?.focus?.();
}
async function saveDraft() {
if (saved || saving) return saving;
save.disabled = true;
saving = Promise.resolve().then(() => captures.save({
title:title?.value || '', body:body?.value || '',
})).then(() => {
saved = true;
if (title) title.value = '';
if (body) body.value = '';
close();
if (status) status.textContent = 'Saved to Drafts. Choose a repository when you’re ready to file it.';
return true;
}).catch(error => {
if (status) status.textContent = error.message;
title?.focus?.();
return false;
}).finally(() => {
save.disabled = false;
saving = null;
});
return saving;
}
async function fileNow() {
if (file.disabled) return false;
file.disabled = true;
if (status) status.textContent = 'Loading filing tools…';
try {
await requestFullWorkspace();
return true;
} catch (_error) {
file.disabled = false;
if (status) status.textContent = 'Filing tools unavailable. Your capture is still editable; retry when connected.';
title?.focus?.();
return false;
}
}
function start() {
if (started) return false;
started = true;
root?.classList.add('progressive-capture');
listen(button, 'click', open);
listen(save, 'click', saveDraft);
listen(file, 'click', fileNow);
listen(cancel, 'click', close);
return true;
}
function handoff() {
if (handedOff) return null;
handedOff = true;
const state = {
open:Boolean(root?.classList.contains('open')),
title:title?.value || '', body:body?.value || '',
};
root?.classList.remove('progressive-capture');
file.disabled = false;
listeners.forEach(([element, name, callback]) => element?.removeEventListener?.(name, callback));
listeners.length = 0;
return state;
}
return {start, handoff};
}
if (typeof window !== 'undefined' && typeof document !== 'undefined' &&
typeof createUnfiledCaptures === 'function' &&
window.matchMedia?.('(max-width: 600px)').matches === true) {
window.stackchainProgressiveCapture = createProgressiveCapture({
document,
storage:window.localStorage,
getLogin:() => window.stackchainProgressiveMyWork?.login?.() || '',
});
window.stackchainProgressiveCapture.start();
}
if (typeof module !== 'undefined' && module.exports) module.exports = createProgressiveCapture;
+3
View File
@@ -9,6 +9,7 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
let selectedByUser = false;
let liveSnapshot = null;
let liveSnapshotPromise = null;
let confirmedLogin = '';
const deferredQueues = {
today:'Today', agenda:'Agenda', later:'Later', draft:'Drafts',
};
@@ -60,6 +61,7 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
});
return {
login() { return confirmedLogin; },
handoff() {
const state = { selectedFilter:selectedByUser ? active : null };
if (liveSnapshot) {
@@ -87,6 +89,7 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
if (transferable) liveSnapshot = snapshot;
else liveSnapshotPromise = null;
const context = snapshot?.context || snapshot || {};
confirmedLogin = String(context.user?.login || '').trim();
items = buildMyWork({ ...context, notifications:snapshot?.notifications || context.notifications || [] });
updateCounts();
render();
+1
View File
@@ -187,6 +187,7 @@ const SHELL = [
BASE + 'static/offline-today.js',
BASE + 'static/my-work.js',
BASE + 'static/progressive-my-work.js',
BASE + 'static/progressive-capture.js',
BASE + 'static/agenda-replan.js',
BASE + 'static/agenda-calendar.js',
BASE + 'static/protect-today.js',