Pithos: files waiting to upload are shown, and go up by themselves

s3d keeps small uploads in a local buffer until about 40 MB has built
up, so a handful of files could sit there for months, only on this
computer, while the Overview said 0 B on Sia. Overview and Drives now
say how many files are waiting, that they are only on this computer
until then, and offer Upload now; automatic upload of leftovers (after
10 minutes with no new uploads) is on unless turned off.
This commit is contained in:
Local Dev 2026-10-04 14:39:34 +02:00
parent 9024a2a217
commit a1c8ed34d5
2 changed files with 32 additions and 2 deletions

View file

@ -340,7 +340,9 @@ export async function createPithos(opts = {}) {
const autoFlush = createAutoFlush({
settings: () => {
const p = readPrefs(configFile);
return { enabled: p.autoFlush === true, minutes: p.autoFlushMinutes ?? DEFAULT_MINUTES };
// On unless the user turned it off: files left in the buffer otherwise
// never reach Sia, and nobody expects to have to push them.
return { enabled: p.autoFlush !== false, minutes: p.autoFlushMinutes ?? DEFAULT_MINUTES };
},
// Only an s3d that is up can be asked; a stopped one just means no flush.
stats: () => (daemon.state === 'starting' || daemon.state === 'stopping'

View file

@ -255,6 +255,7 @@ function overview(main) {
main.append(
h('div', { class: 'page-head' }, h('div', {}, h('h1', {}, 'Overview'), h('p', { class: 'muted' }, 'Your s3d gateway and its upload pipeline to Sia.'))),
banners,
pendingBanner(),
h('div', { class: 'grid two' }, daemonCard, statsCard),
);
@ -1285,6 +1286,33 @@ function siaAccountBlock(a, onRefresh) {
'The same phrase approved from two different logins makes two different accounts; the same phrase from the same login is the same account, even on another computer.'));
}
// "N files are waiting to upload to Sia" with Upload now. s3d holds small
// uploads until about 40 MB has built up; until then they exist only on this
// computer, which is easy to miss.
function pendingBanner() {
const el = h('div', { hidden: true });
const st = daemonState();
if (st !== 'running' && st !== 'external') return el;
Promise.all([api('GET', '/api/stats'), api('GET', '/api/autoflush').catch(() => null)]).then(([x, af]) => {
if (!x?.pendingObjects) return;
const n = x.pendingObjects;
const when = af?.enabled
? (af.dueAt ? `They go up by themselves in about ${Math.max(1, Math.ceil((af.dueAt - Date.now()) / 60_000))} min if nothing new arrives.` : `They go up by themselves after ${af.minutes} min with no new uploads.`)
: 'They go up only when about 40 MB has built up, or when you upload them now.';
const btn = h('button', { class: 'btn primary small', onclick: async () => {
btn.disabled = true; put(btn, h('span', { class: 'spinner' }), ' Uploading…');
try { await api('POST', '/api/flush'); toast('Your files are on Sia'); el.hidden = true; }
catch (e) { fail(e); btn.disabled = false; put(btn, 'Upload now'); }
} }, 'Upload now');
put(el, h('div', { class: 'banner info' },
h('span', {}, h('strong', {}, `${n.toLocaleString()} file${n === 1 ? '' : 's'} (${fmtBytes(x.pendingSize)}) waiting to upload to Sia. `),
h('span', { class: 'small' }, `Until then they are only on this computer. ${when}`)),
btn));
el.hidden = false;
}).catch(() => {});
return el;
}
// The Accounts card at the top of the navigation: which Sia account this is
// and how much room it has left. Refreshed at most every 30 s as pages change.
let navAccountAt = 0;
@ -1331,7 +1359,7 @@ async function bucketList(head, body, user, userSel) {
h('div', { class: 'row' }, userPicker(userSel), h('button', { class: 'btn primary', onclick: create }, 'New drive')));
const card = h('div', { class: 'card' }, h('p', { class: 'muted' }, 'Loading…'));
const bar = h('div', { class: 'view-bar' });
put(body, bar, card);
put(body, pendingBanner(), bar, card);
const base = `/api/s3/${encodeURIComponent(user)}/buckets`;
let mode = viewMode('drives', 'icons');
let list = [];