feat: schedule search results into Later (Closes #799)
CI / lint (pull_request) Successful in 1m42s
CI / build-release (pull_request) Successful in 6s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-14 04:30:02 +00:00
1 parent 0a8f33dc83
commit 71b38687c4
8 files changed
+167 -1

No files matched your search

+33
View File
@@ -2231,6 +2231,7 @@
});
const laterPickerElement = qs('#later-picker');
const laterPickerInput = qs('#later-picker-time');
let searchDefer = null;
const laterPicker = createLaterPicker({
history: window.history,
eventTarget: window,
@@ -2249,6 +2250,13 @@
}
},
onConfirm: (item, until, context) => {
if (context === 'search') {
return () => searchDefer.run(item, until).then(outcome => {
if (outcome === 'deferred') closeSearchPreview();
}).catch(error => {
qs('#search-preview-status').textContent = error.message + ' Retry.';
});
}
if (context === 'detail') {
const inSession = workSession.active();
const deferred = detailDefer.deferUntil(item, until, {
@@ -4330,6 +4338,7 @@
const sheet = qs('#search-preview');
const status = qs('#search-preview-status');
const claimButton = qs('#claim-search-result');
const deferButton = qs('#defer-search-result');
const queueButton = qs('#queue-search-result');
const startButton = qs('#start-search-result');
const shareButton = qs('#share-search-result');
@@ -4343,6 +4352,8 @@
sheet.classList.add('open');
claimButton.hidden = true;
claimButton.disabled = false;
deferButton.hidden = true;
deferButton.disabled = false;
queueButton.hidden = true;
startButton.hidden = true;
startButton.disabled = false;
@@ -4378,6 +4389,10 @@
claimButton.hidden = !(detail.claimable || detail.assigned_to_me);
claimButton.textContent = detail.assigned_to_me ? 'Open in My Work' : 'Assign to me';
claimButton.disabled = state.status === 'claiming';
deferButton.hidden = !(detail.kind === 'issue' && detail.state === 'open' &&
(detail.claimable || detail.assigned_to_me));
deferButton.textContent = detail.assigned_to_me ? 'Defer' : 'Assign & defer';
deferButton.disabled = state.status === 'claiming' || searchDefer?.pending();
queueButton.hidden = !(detail.kind === 'issue' && detail.state === 'open' &&
(detail.claimable || detail.assigned_to_me));
queueButton.textContent = detail.assigned_to_me ? 'Add to Today' : 'Assign & add to Today';
@@ -4409,6 +4424,18 @@
share: url => createWorkRoute.share(url, navigator, navigator.clipboard),
onState: renderSearchPreview,
});
searchDefer = createSearchDefer({
claim: detail => searchPreview.claim(detail),
accept: confirmed => acceptClaimedIssue(confirmed),
defer: (item, until) => laterWork.defer(item, until),
refresh: refreshMyWorkView,
announce: message => {
qs('#search-preview-status').textContent = message;
qs('#cmd-search-action-status').textContent = message;
qs('#my-work-action-status').textContent = message;
},
formatTime: fmt,
});
function canonicalSearchPreviewUrl() {
const { query, preview, scope = currentSearchScope() } = taskOverlayHistory.currentState();
const params = new URLSearchParams({
@@ -4736,6 +4763,12 @@
qs('#search-preview-status').textContent = error.message + ' Retry assignment.';
}
});
qs('#defer-search-result').addEventListener('click', event => {
const detail = searchPreviewDetail;
if (!detail || detail.kind !== 'issue' || detail.state !== 'open' ||
(!detail.claimable && !detail.assigned_to_me) || searchDefer.pending()) return;
laterPicker.open(detail, event.currentTarget, 'search');
});
qs('#queue-search-result').addEventListener('click', async () => {
const detail = searchPreviewDetail;
if (!detail || detail.kind !== 'issue' || detail.state !== 'open' ||
+2
View File
@@ -468,6 +468,7 @@
<div class="search-preview-actions">
<div class="search-preview-primary-actions">
<button id="claim-search-result" type="button" hidden>Assign to me</button>
<button id="defer-search-result" type="button" hidden>Assign &amp; defer</button>
<button id="queue-search-result" type="button" hidden>Assign &amp; add to Today</button>
<button id="start-search-result" type="button" hidden>Assign &amp; start</button>
@@ -1052,6 +1053,7 @@
<script src="static/markdown.js"></script>
<script src="static/commands.js"></script>
<script src="static/search-preview.js"></script>
<script src="static/search-defer.js"></script>
<script src="static/widgets.js"></script>
<script src="static/drafts.js"></script>
<script src="static/unfiled-captures.js"></script>
+28
View File
@@ -0,0 +1,28 @@
function createSearchDefer({ claim, accept, defer, refresh, announce, formatTime }) {
let request = null;
function run(detail, until) {
if (request) return request;
const wasClaimable = Boolean(detail?.claimable);
request = Promise.resolve(wasClaimable ? claim(detail) : detail)
.then(confirmed => {
const item = accept(confirmed);
const outcome = defer(item, until);
refresh();
if (outcome === 'deferred') {
announce((wasClaimable ? 'Assigned and deferred until ' : 'Deferred until ') + formatTime(until) + '.');
} else {
announce(wasClaimable
? 'Assignment succeeded, but Later could not be saved. Open this issue in My Work to retry.'
: 'Could not save Later on this device. Retry scheduling.');
}
return outcome;
})
.finally(() => { request = null; });
return request;
}
return { run, pending:() => Boolean(request) };
}
if (typeof module !== 'undefined' && module.exports) module.exports = createSearchDefer;
+1
View File
@@ -18,6 +18,7 @@ const SHELL = [
BASE + 'static/markdown.js',
BASE + 'static/commands.js',
BASE + 'static/search-preview.js',
BASE + 'static/search-defer.js',
BASE + 'static/widgets.js',
BASE + 'static/drafts.js',
BASE + 'static/unfiled-captures.js',