// Draft recovery — keep what you typed when a write fails.
//
// THE PROBLEM. A post goes through hpp over P2P, and that can fail: the daemon is restarting, the peer
// is unreachable, the record cannot be signed yet. When it did, the browser navigated to a redirect and
// the text was simply GONE. Losing someone's words because the network blinked is the worst small bug a
// client can have, and it is entirely avoidable.
//
// Deliberately dumb: the value is mirrored to localStorage as you type, restored on load, and cleared
// only once a post has actually SUCCEEDED. Nothing is scheduled, nothing is synced, and if scripting is
// off the composer behaves exactly as it did before.
const KEY = 'favela.draft'
function fields () {
// The main composer and the reply box are the same shape; both are worth protecting, keyed apart so a
// half-written reply does not reappear in the timeline composer.
return [...document.querySelectorAll('form.composer textarea[name="text"], form.replyf textarea[name="text"]')]
}
const keyFor = (ta) => KEY + ':' + (ta.closest('form.replyf') ? 'reply:' + location.pathname : 'composer')
for (const ta of fields()) {
const k = keyFor(ta)
// Restore first — but never clobber something the server already put there (a quote preview, an edit).
if (!ta.value) {
let saved = null
try { saved = localStorage.getItem(k) } catch {}
if (saved) {
ta.value = saved
const note = document.createElement('p')
note.className = 'draftnote'
note.textContent = 'restored an unsent draft'
const clear = document.createElement('button')
clear.type = 'button'
clear.className = 'devbtn'
clear.textContent = 'discard'
clear.addEventListener('click', () => {
ta.value = ''
try { localStorage.removeItem(k) } catch {}
note.remove()
})
note.appendChild(document.createTextNode(' '))
note.appendChild(clear)
ta.insertAdjacentElement('beforebegin', note)
}
}
ta.addEventListener('input', () => {
try { if (ta.value) localStorage.setItem(k, ta.value); else localStorage.removeItem(k) } catch {}
})
// Clear ONLY on a real success. A plain form navigates away on submit, so we cannot see the outcome
// here — instead the server sets ?posted=1 on the redirect, and that is the signal. Clearing on submit
// would throw the text away in exactly the case this exists for: the write that failed.
const form = ta.closest('form')
if (form) form.addEventListener('submit', () => { try { sessionStorage.setItem(KEY + '.pending', k) } catch {} })
}
// On load: if we arrived from a successful post, drop the draft it came from.
try {
const params = new URLSearchParams(location.search)
const pending = sessionStorage.getItem(KEY + '.pending')
if (params.get('posted') === '1' && pending) {
localStorage.removeItem(pending)
sessionStorage.removeItem(KEY + '.pending')
}
} catch {}