(function () {
'use strict';
// Server-side hint: did the user have paid privileges when the page
// rendered? Used to decide whether to offer the 'Import & sync' button.
// We still call CassandraSync.getStatus() at click time as the source
// of truth, but this lets us skip rendering a button we know is dead.
// Value is passed via data-paid attribute on #drop-zone.
function ready(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn);
} else { fn(); }
}
ready(function () {
var P = window.CassandraPortfolio;
if (!P) return;
var esc = P.esc, fmt = P.fmt, signed = P.signed, cls = P.cls;
var dropZone = document.getElementById('drop-zone');
var fileInput = document.getElementById('file-input');
var browseLink = document.getElementById('browse-link');
var filenameEl = document.getElementById('dz-filename');
var previewEl = document.getElementById('import-preview');
var resultEl = document.getElementById('import-result');
if (!dropZone) return;
var IS_PAID = dropZone.dataset.paid === 'true';
var currentPie = null; // most recently parsed pie, awaiting commit
function showError(msg) {
previewEl.hidden = true;
resultEl.className = 'result result--err';
resultEl.innerHTML =
'
' +
'Also stores an encrypted copy on the server, ' +
'restorable on any device with your PIN. Only you can decrypt ' +
'it — losing the PIN means losing the backup.' +
'
' +
'
')
: ('
' +
'' +
'
' +
'Encrypted cloud backup is available on the paid tier.' +
'
' +
'Saved to this browser only. No server-side copy of your holdings.' +
'
' +
'
' +
syncBtn +
'
' +
'' +
'
' +
'
' +
'
';
previewEl.hidden = false;
document.getElementById('commit-local').addEventListener('click', commitLocal);
document.getElementById('commit-cancel').addEventListener('click', resetUploader);
var syncEl = document.getElementById('commit-sync');
if (syncEl) syncEl.addEventListener('click', commitSync);
}
function commitLocal() {
if (!currentPie) return;
P.savePie(currentPie);
showSuccess('▸ Imported to this browser.',
'Pie kept locally; no server-side copy.');
currentPie = null;
}
async function commitSync() {
if (!currentPie) return;
// Save locally first so the cloud-sync flow uses the freshly-imported
// pie (the enable-PIN modal in this same page reads from localStorage).
P.savePie(currentPie);
var S = window.CassandraSync;
if (!S) { showError('Cloud sync module not loaded.'); return; }
var status;
try { status = await S.getStatus(); }
catch (e) { showError('Could not check sync status: ' + (e.message || e)); return; }
if (!status.paid) {
showError('Cloud sync requires the paid tier.');
return;
}
if (status.exists) {
// Already enabled — try a direct push using the cached session
// key. If no key is cached (fresh browser session), this throws,
// and we fall back to the enable-PIN modal so the user can
// re-enter their PIN.
try {
await S.pushSync(currentPie, null);
showSuccess('▸ Imported and synced.',
'Encrypted copy updated on the server.');
currentPie = null;
if (window.cassandraRefreshSyncStatus) window.cassandraRefreshSyncStatus();
return;
} catch (e) {
// Fall through to modal so the user can re-auth with their PIN.
console.warn('direct push failed, falling back to PIN modal', e);
}
}
// !status.exists OR cached-key push failed → use the modal.
if (window.cassandraOpenSyncModal) {
window.cassandraOpenSyncModal({
onSuccess: function () {
showSuccess('▸ Imported and synced.',
'Cloud sync is now enabled and the pie is stored encrypted.');
currentPie = null;
},
});
} else {
showError('Cloud sync UI unavailable on this page. ' +
'Use the Cloud sync section below to enable.');
}
}
function resetUploader() {
currentPie = null;
previewEl.hidden = true;
previewEl.innerHTML = '';
resultEl.hidden = true;
filenameEl.textContent = '';
fileInput.value = '';
}
async function parseFile(file) {
filenameEl.textContent = file.name + ' (' + Math.round(file.size / 1024) + ' KB) — parsing…';
previewEl.hidden = true;
resultEl.hidden = true;
try {
var pie = await P.parseCsv(file);
renderPreview(pie);
filenameEl.textContent = file.name + ' (' + Math.round(file.size / 1024) + ' KB)';
} catch (e) {
filenameEl.textContent = file.name + ' (failed)';
showError(e.message || 'Unknown error');
}
}
browseLink.addEventListener('click', function (e) { e.preventDefault(); fileInput.click(); });
fileInput.addEventListener('change', function () {
if (fileInput.files[0]) parseFile(fileInput.files[0]);
});
['dragenter', 'dragover'].forEach(function (ev) {
dropZone.addEventListener(ev, function (e) {
e.preventDefault(); e.stopPropagation();
dropZone.classList.add('dz--over');
});
});
['dragleave', 'drop'].forEach(function (ev) {
dropZone.addEventListener(ev, function (e) {
e.preventDefault(); e.stopPropagation();
dropZone.classList.remove('dz--over');
});
});
dropZone.addEventListener('drop', function (e) {
var f = e.dataTransfer.files && e.dataTransfer.files[0];
if (f) parseFile(f);
});
dropZone.addEventListener('click', function (e) {
if (e.target.tagName !== 'A') fileInput.click();
});
});
})();