const EstAttachments = (() => {
function render() {
const { state, esc, number, byId } = EstApp;
const rows = state.project.attachments || [];
byId("work-content").innerHTML =
`
첨부파일${rows.length}개
첨부파일은 이 작업의 JSON 저장에 포함됩니다.
| 파일명 | 크기 (바이트) | 작업 |
${rows.map((row) => `| ${esc(row.name)} | ${number(row.size)} | |
`).join("")}
`;
}
function read(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = () => reject(Error(`파일 읽기 실패: ${file.name}`));
reader.onload = () =>
resolve({
id: EstApp.uid(),
name: file.name,
type: file.type || "",
size: file.size,
data: String(reader.result).split(",", 2)[1],
});
reader.readAsDataURL(file);
});
}
function requiredBytes(project, files) {
const metadata = files.map((file) => ({
id: "0".repeat(36),
name: file.name,
type: file.type || "",
size: file.size,
data: "",
}));
const candidate = {
...project,
id: project.id || "0".repeat(24),
attachments: [...(project.attachments || []), ...metadata],
};
return (
new Blob([JSON.stringify(candidate)]).size +
files.reduce((sum, file) => sum + 4 * Math.ceil(file.size / 3), 0)
);
}
function install() {
document.addEventListener("change", async (event) => {
const input = event.target;
if (input.dataset.attachmentAdd === undefined || !input.files.length) return;
const { state } = EstApp,
key = state.documentKey,
revision = state.revision;
try {
const selected = [...input.files];
if (requiredBytes(state.project, selected) > 256 * 1024 * 1024)
throw Error("첨부파일을 포함한 작업이 저장 한도 256MB를 초과합니다.");
const files = await Promise.all(selected.map(read));
if (key !== state.documentKey || revision !== state.revision)
throw Error("작업이 변경되었습니다. 첨부파일을 다시 추가하세요.");
EstApp.change(() => (state.project.attachments ||= []).push(...files), false);
} catch (error) {
state.notice = error.message;
EstApp.render();
} finally {
input.value = "";
}
});
document.addEventListener("click", (event) => {
const node = event.target.closest("[data-attachment-download],[data-attachment-delete]");
if (!node) return;
const { state } = EstApp,
id = node.dataset.attachmentDownload ?? node.dataset.attachmentDelete;
const index = (state.project.attachments || []).findIndex((item) => item.id === id);
if (index < 0) return;
if (node.dataset.attachmentDelete !== undefined)
EstApp.change(() => state.project.attachments.splice(index, 1), false);
else {
const item = state.project.attachments[index],
text = atob(item.data),
bytes = new Uint8Array(text.length);
for (let i = 0; i < text.length; i++) bytes[i] = text.charCodeAt(i);
EstApp.download(item.name, bytes, "application/octet-stream");
}
});
}
return { render, install, requiredBytes };
})();