/** * File import via drag-and-drop and file picker on the landing page. * Handles reading files, creating encrypted documents, and navigating * to the editor with pending import data. * * Extracted from landing.ts for decomposition. */ import type { FolderAssignments } from './landing-types.js'; import { generateKey, exportKey } from './lib/crypto.js'; import { storeKey } from './lib/key-sync.js'; import { createDocument as createLocalDoc } from './lib/local-store.js'; import { moveToFolder } from './landing-utils.js'; import { getFileType, getImportType, pendingImportKey, buildEditorUrl, } from './landing-dragdrop.js'; import { showToast } from './landing-toast.js'; // ── Deps interface ─────────────────────────────────────────── export interface ImportDeps { getCurrentFolderId: () => string | null; getFolderAssignments: () => FolderAssignments; setFolderAssignments: (a: FolderAssignments) => void; } // ── Import a single file ──────────────────────────────────── export async function importFile(deps: ImportDeps, file: File): Promise { const docType = getFileType(file.name); const importType = getImportType(file.name); if (!docType || !importType) { showToast(`Unsupported file type: .${file.name.split('.').pop()}`, 4000, true); return; } try { // Generate encryption key const key = await generateKey(); const keyStr = await exportKey(key); // Use filename (without extension) as the document name const fileBaseName = file.name.replace(/\.[^.]+$/, ''); const defaultName = fileBaseName || (docType === 'doc' ? 'Untitled Document' : 'Untitled Spreadsheet'); // Create document in IndexedDB const doc = await createLocalDoc( docType as 'doc' | 'sheet' | 'form' | 'slide' | 'diagram' | 'calendar', defaultName, new ArrayBuffer(0), key, ); const id = doc.id; await storeKey(id, keyStr); // If inside a folder, assign to it const currentFolderId = deps.getCurrentFolderId(); if (currentFolderId) { const updated = moveToFolder(deps.getFolderAssignments(), id, currentFolderId); deps.setFolderAssignments(updated); localStorage.setItem('atmos-folder-assignments', JSON.stringify(updated)); } // Read file and store in sessionStorage for the editor to pick up const reader = new FileReader(); reader.onload = () => { const payload = JSON.stringify({ name: file.name, type: importType, data: reader.result, // data URL (base64-encoded) }); sessionStorage.setItem(pendingImportKey(id), payload); // Navigate to the editor window.location.href = buildEditorUrl(docType, id, keyStr); }; reader.onerror = () => { showToast('Failed to read file', 4000, true); }; reader.readAsDataURL(file); } catch (err) { showToast('Failed to create document for import', 4000, true); } } // ── Drag-and-drop setup ───────────────────────────────────── export function setupDragAndDrop(deps: ImportDeps): void { const dropOverlay = document.getElementById('drop-overlay'); let dragCounter = 0; function showDropOverlay() { if (dropOverlay) dropOverlay.style.display = ''; } function hideDropOverlay() { if (dropOverlay) dropOverlay.style.display = 'none'; } document.addEventListener('dragenter', (e) => { e.preventDefault(); dragCounter++; if (dragCounter === 1) showDropOverlay(); }); document.addEventListener('dragover', (e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'; }); document.addEventListener('dragleave', (e) => { e.preventDefault(); dragCounter--; if (dragCounter <= 0) { dragCounter = 0; hideDropOverlay(); } }); document.addEventListener('drop', async (e) => { e.preventDefault(); dragCounter = 0; hideDropOverlay(); const file = e.dataTransfer?.files[0]; if (!file) return; importFile(deps, file); }); // File import button (mobile-friendly alternative to drag-drop) const fileImportBtn = document.getElementById('file-import-btn'); const fileImportInput = document.getElementById('file-import-input') as HTMLInputElement | null; if (fileImportBtn && fileImportInput) { fileImportBtn.addEventListener('click', () => fileImportInput.click()); fileImportInput.addEventListener('change', () => { const file = fileImportInput.files?.[0]; if (file) importFile(deps, file); fileImportInput.value = ''; }); } }