function createUpdateOwnership({ claim, addToday, available, start, recover, onClaimed, onState, onStartState, }) { let detail = null; let update = null; let claimedItem = null; let request = null; function state(value) { onState?.(value); } function startState(value) { onStartState?.(value); } function claimed(result) { claimedItem = { ...result, repository: detail.repository, kind: 'issue', notification_id: update?.notification_id, updated_at: update?.updated_at || result.updated_at, has_update: true, }; detail.issue.claimable = false; onClaimed?.(claimedItem); startState({ action: 'hidden', busy: false, message: '' }); return claimedItem; } function recoverStartedItem(item) { recover?.(item); startState({ action: 'hidden', busy: false, message: 'Assigned to you, but Today could not start. The update is still unread; the owned issue is open so you can recover.', }); state({ action: 'today', busy: false, message: '' }); return 'recovery'; } function open(nextDetail, nextUpdate) { detail = nextDetail; update = nextUpdate; claimedItem = null; request = null; state({ action: nextDetail?.issue?.claimable ? 'claim' : 'hidden', busy: false, message: '', }); startState({ action: nextDetail?.issue?.claimable && available && start ? 'start' : 'hidden', busy: false, message: '', }); } function act() { if (request) return request; if (claimedItem) { const result = addToday(claimedItem); const messages = { added: 'Added to Today.', exists: 'This issue is already in Today.', full: 'Today is limited to 5 items. Remove one before adding more.', unavailable: 'Could not save Today on this device.', }; state({ action: 'today', busy: false, message: messages[result] || messages.unavailable }); return Promise.resolve(result); } if (!detail?.issue?.claimable) return Promise.resolve('hidden'); state({ action: 'claim', busy: true, message: 'Assigning…' }); request = Promise.resolve(claim({ repository: detail.repository, number: detail.issue.number, })).then(result => { claimed(result); state({ action: 'today', busy: false, message: 'Assigned to you. The update is still unread.' }); return claimedItem; }).catch(error => { if (error?.status === 409) { detail.issue.claimable = false; startState({ action: 'hidden', busy: false, message: '' }); state({ action: 'hidden', busy: false, message: 'Someone else claimed or closed this issue. The update is still unread.', }); return 'conflict'; } state({ action: 'claim', busy: false, message: (error?.message || 'The issue could not be assigned.') + ' The update and your reply draft are safe; retry.', }); return 'retry'; }).finally(() => { request = null; }); return request; } function startNow() { if (request) return request; if (!detail?.issue?.claimable || !available || !start) return Promise.resolve('hidden'); if (!available()) { const message = 'Today is limited to 5 items. Remove one before taking ownership.'; startState({ action: 'start', busy: false, message }); return Promise.resolve('full'); } startState({ action: 'start', busy: true, message: 'Assigning and starting…' }); state({ action: 'claim', busy: true, message: '' }); request = Promise.resolve(claim({ repository: detail.repository, number: detail.issue.number, })).then(result => { const item = claimed(result); const outcome = start(item); if (outcome === 'started') { startState({ action: 'hidden', busy: false, message: 'Assigned, added to Today, and ready to work. The update is still unread.', }); state({ action: 'today', busy: false, message: '' }); return outcome; } return recoverStartedItem(item); }).catch(error => { if (claimedItem) { return recoverStartedItem(claimedItem); } if (error?.status === 409) { detail.issue.claimable = false; state({ action: 'hidden', busy: false, message: '' }); startState({ action: 'hidden', busy: false, message: 'Someone else claimed or closed this issue. The update is still unread.', }); return 'conflict'; } state({ action: 'claim', busy: false, message: '' }); startState({ action: 'start', busy: false, message: (error?.message || 'The issue could not be assigned.') + ' The update and your reply draft are safe; retry.', }); return 'retry'; }).finally(() => { request = null; }); return request; } return { open, act, start:startNow }; } if (typeof module !== 'undefined' && module.exports) module.exports = createUpdateOwnership;