Implement undo popup for book format deletion

This commit is contained in:
Kovid Goyal 2023-04-13 13:31:38 +05:30
parent 78904f6b20
commit 5470d311a3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 35 additions and 10 deletions

View File

@ -1545,14 +1545,18 @@ class DB:
def remove_formats(self, remove_map, metadata_map):
self.ensure_trash_dir()
removed_map = {}
for book_id, removals in iteritems(remove_map):
paths = set()
removed_map[book_id] = set()
for fmt, fname, path in removals:
path = self.format_abspath(book_id, fmt, fname, path)
if path:
paths.add(path)
removed_map[book_id].add(fmt.upper())
if paths:
self.move_book_files_to_trash(book_id, paths, metadata_map[book_id])
return removed_map
def cover_last_modified(self, path):
path = os.path.abspath(os.path.join(self.library_path, path, COVER_FILE_NAME))

View File

@ -1848,9 +1848,11 @@ class Cache:
:param formats_map: A mapping of book_id to a list of formats to be removed from the book.
:param db_only: If True, only remove the record for the format from the db, do not delete the actual format file from the filesystem.
:return: A map of book id to set of formats actually deleted from the filesystem for that book
'''
table = self.fields['formats'].table
formats_map = {book_id:frozenset((f or '').upper() for f in fmts) for book_id, fmts in iteritems(formats_map)}
removed_map = {}
for book_id, fmts in iteritems(formats_map):
for fmt in fmts:
@ -1874,7 +1876,7 @@ class Cache:
if removes[book_id]:
metadata_map[book_id] = {'title': self._field_for('title', book_id), 'authors': self._field_for('authors', book_id)}
if removes:
self.backend.remove_formats(removes, metadata_map)
removed_map = self.backend.remove_formats(removes, metadata_map)
size_map = table.remove_formats(formats_map, self.backend)
self.fields['size'].table.update_sizes(size_map)
@ -1884,6 +1886,7 @@ class Cache:
self._update_last_modified(tuple(formats_map))
self.event_dispatcher(EventType.formats_removed, formats_map)
return removed_map
@read_api
def get_next_series_num_for(self, series, field='series', current_indices=False):

View File

@ -166,7 +166,7 @@ class DeleteAction(InterfaceAction):
return set(map(self.gui.library_view.model().id, rows))
def _remove_formats_from_ids(self, fmts, ids):
self.gui.library_view.model().db.new_api.remove_formats({bid: fmts for bid in ids})
self.show_undo_for_deleted_formats(self.gui.library_view.model().db.new_api.remove_formats({bid: fmts for bid in ids}))
self.gui.library_view.model().refresh_ids(ids)
self.gui.library_view.model().current_changed(self.gui.library_view.currentIndex(),
self.gui.library_view.currentIndex())
@ -174,7 +174,7 @@ class DeleteAction(InterfaceAction):
def remove_format_by_id(self, book_id, fmt):
title = self.gui.current_db.title(book_id, index_is_id=True)
if not confirm('<p>'+(_(
'The %(fmt)s format will be <b>permanently deleted</b> from '
'The %(fmt)s format will be <b>deleted</b> from '
'%(title)s. Are you sure?')%dict(fmt=fmt, title=title)) +
'</p>', 'library_delete_specific_format', self.gui):
return
@ -193,8 +193,8 @@ class DeleteAction(InterfaceAction):
return error_dialog(self.gui, _('Format not found'), _('The {} format is not present in the selected books.').format(fmt), show=True)
if not confirm(
'<p>'+ ngettext(
_('The {fmt} format will be <b>permanently deleted</b> from {title}.'),
_('The {fmt} format will be <b>permanently deleted</b> from all {num} selected books.'),
_('The {fmt} format will be <b>deleted</b> from {title}.'),
_('The {fmt} format will be <b>deleted</b> from all {num} selected books.'),
len(ids)).format(fmt=fmt.upper(), num=len(ids), title=self.gui.current_db.title(next(iter(ids)), index_is_id=True)
) + ' ' + _('Are you sure?'), 'library_delete_specific_format_from_selected', self.gui
):
@ -217,7 +217,7 @@ class DeleteAction(InterfaceAction):
if not fmts:
return
m = self.gui.library_view.model()
m.db.new_api.remove_formats({book_id:fmts for book_id in ids})
self.show_undo_for_deleted_formats(m.db.new_api.remove_formats({book_id:fmts for book_id in ids}))
m.refresh_ids(ids)
m.current_changed(self.gui.library_view.currentIndex(),
self.gui.library_view.currentIndex())
@ -247,7 +247,7 @@ class DeleteAction(InterfaceAction):
# formats
removals[id] = rfmts
if removals:
m.db.new_api.remove_formats(removals)
self.show_undo_for_deleted_formats(m.db.new_api.remove_formats(removals))
m.refresh_ids(ids)
m.current_changed(self.gui.library_view.currentIndex(),
self.gui.library_view.currentIndex())
@ -270,7 +270,7 @@ class DeleteAction(InterfaceAction):
if fmts:
removals[id] = fmts.split(',')
if removals:
db.new_api.remove_formats(removals)
self.show_undo_for_deleted_formats(db.new_api.remove_formats(removals))
self.gui.library_view.model().refresh_ids(ids)
self.gui.library_view.model().current_changed(self.gui.library_view.currentIndex(),
self.gui.library_view.currentIndex())
@ -367,9 +367,17 @@ class DeleteAction(InterfaceAction):
if not hasattr(self, 'message_popup'):
self.message_popup = MessagePopup(self.gui)
self.message_popup.undo_requested.connect(self.undelete)
self.message_popup(ngettext('One book deleted.', '{} books deleted.', len(ids_deleted)).format(len(ids_deleted)),
self.message_popup(ngettext('One book deleted from library.', '{} books deleted from library.', len(ids_deleted)).format(len(ids_deleted)),
show_undo=(self.gui.current_db.new_api.library_id, ids_deleted))
def show_undo_for_deleted_formats(self, removed_map):
if not hasattr(self, 'message_popup'):
self.message_popup = MessagePopup(self.gui)
self.message_popup.undo_requested.connect(self.undelete)
num = sum(map(len, removed_map.values()))
self.message_popup(ngettext('One book format deleted.', '{} book formats deleted.', num).format(num),
show_undo=(self.gui.current_db.new_api.library_id, removed_map))
def library_changed(self, db):
if hasattr(self, 'message_popup'):
self.message_popup.hide()
@ -377,7 +385,17 @@ class DeleteAction(InterfaceAction):
def undelete(self, what):
library_id, book_ids = what
db = self.gui.current_db.new_api
if library_id == db.library_id:
if library_id != db.library_id:
return
current_idx = self.gui.library_view.currentIndex()
if isinstance(book_ids, dict):
with BusyCursor():
for book_id, fmts in book_ids.items():
for fmt in fmts:
db.move_format_from_trash(book_id, fmt)
if current_idx.isValid():
self.gui.library_view.model().current_changed(current_idx, current_idx)
else:
with BusyCursor():
for book_id in book_ids:
db.move_book_from_trash(book_id)