Open the epub import card and load a dropped file

This commit is contained in:
tim
2026-09-27 23:38:28 +02:00
parent 03ed1a69cb
commit 1246816ab8
3 changed files with 30 additions and 6 deletions
+19 -5
View File
@@ -106,7 +106,7 @@ function libraryImport(config) {
// Upload a FanFicFare ePub. Bindery stores its chapters as if it had downloaded
// them, so chapters that the site stubbed or removed stay in the book.
export function epubImport(storyId) {
export function epubImport(storyId, title = 'Import an ePub file') {
let check = true;
const file = h('input', { type: 'file', accept: '.epub,application/epub+zip', 'aria-label': 'Epub file' });
const upload = button(
@@ -132,9 +132,22 @@ export function epubImport(storyId) {
'Chapters that Bindery has already keep their stored text.'
: 'Starts tracking the story of an ePub made by FanFicFare, with all of its chapters. ' +
'If Bindery tracks the story already, it adds the chapters that it does not have.';
return h(
'div',
{},
const card = h(
'details',
{
class: 'card',
// A file dropped on the closed card opens it and puts the file in the picker, ready to import.
ondragover: (e) => e.preventDefault(),
ondrop: (e) => {
e.preventDefault();
const dropped = e.dataTransfer?.files;
if (dropped && dropped.length) {
file.files = dropped;
card.open = true;
}
},
},
h('summary', {}, h('h2', {}, title)),
h('p', { class: 'hint' }, hint, ' The next update adds the chapters that the site published after the ePub.'),
h(
'div',
@@ -144,6 +157,7 @@ export function epubImport(storyId) {
h('label', {}, checkbox(true, (on) => (check = on)), ' Check the site for new chapters after import'),
),
);
return card;
}
function storyRow(s) {
@@ -268,7 +282,7 @@ export function storiesPage(main, _params, config) {
table,
),
h('details', { class: 'card', open: true }, h('summary', {}, h('h2', {}, 'Jobs')), jobs.el),
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Import an epub file')), epubImport()),
epubImport(),
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Import from the library')), libraryImport(config)),
);
+1 -1
View File
@@ -613,7 +613,7 @@ export function storyPage(main, { storyId }, config) {
),
chapters,
),
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Import chapters from an epub')), epubImport(id)),
epubImport(id, 'Import chapters from an ePub'),
h('details', { class: 'card', open: true }, h('summary', {}, h('h2', {}, 'Recent jobs')), jobs.el),
h(
'details',
+10
View File
@@ -144,6 +144,16 @@ for (const storyId of [undefined, 1]) {
});
}
test('dropping a file on a closed epub import card opens it and fills the file field', () => {
const root = epubImport(1);
assert.equal(root.open, undefined);
const file = field(root, 'Epub file');
const book = new File(['book'], 'story.epub', { type: 'application/epub+zip' });
root.listeners.drop({ preventDefault() {}, dataTransfer: { files: [book] } });
assert.equal(root.open, true);
assert.equal(file.files[0], book);
});
test('library import sends only selected books and handles an empty candidate list', async () => {
await mountStories();
api.on('GET', 'library/candidates', []);