diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts index b845a524a..138ec5ab1 100644 --- a/src-ui/src/app/components/document-list/document-list.component.ts +++ b/src-ui/src/app/components/document-list/document-list.component.ts @@ -241,6 +241,10 @@ export class DocumentListComponent this.list.reload() }) + this.consumerStatusService.onDocumentDeleted().subscribe(() => { + this.list.reload() + }) + this.route.paramMap .pipe( filter((params) => params.has('id')), // only on saved view e.g. /view/id diff --git a/src-ui/src/app/services/consumer-status.service.spec.ts b/src-ui/src/app/services/consumer-status.service.spec.ts index b699f8772..9cdb622bc 100644 --- a/src-ui/src/app/services/consumer-status.service.spec.ts +++ b/src-ui/src/app/services/consumer-status.service.spec.ts @@ -323,4 +323,21 @@ describe('ConsumerStatusService', () => { 1 ) }) + + it('should trigger deleted subject on document deleted', () => { + let deleted = false + consumerStatusService.onDocumentDeleted().subscribe(() => { + deleted = true + }) + + consumerStatusService.connect() + server.send({ + current_progress: 1, + max_progress: 1, + status: 'DELETED', + }) + + consumerStatusService.disconnect() + expect(deleted).toBeTruthy() + }) }) diff --git a/src-ui/src/app/services/consumer-status.service.ts b/src-ui/src/app/services/consumer-status.service.ts index 40641ff81..819179841 100644 --- a/src-ui/src/app/services/consumer-status.service.ts +++ b/src-ui/src/app/services/consumer-status.service.ts @@ -95,6 +95,7 @@ export class ConsumerStatusService { private documentDetectedSubject = new Subject() private documentConsumptionFinishedSubject = new Subject() private documentConsumptionFailedSubject = new Subject() + private documentDeletedSubject = new Subject() private get(taskId: string, filename?: string) { let status = @@ -147,6 +148,11 @@ export class ConsumerStatusService { ) this.statusWebSocket.onmessage = (ev) => { let statusMessage: WebsocketConsumerStatusMessage = JSON.parse(ev['data']) + let isDelete = statusMessage.status === 'DELETED' + if (isDelete) { + this.documentDeletedSubject.next(true) + return + } // fallback if backend didn't restrict message if ( @@ -250,4 +256,8 @@ export class ConsumerStatusService { onDocumentDetected() { return this.documentDetectedSubject } + + onDocumentDeleted() { + return this.documentDeletedSubject + } } diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index f0522eddc..13a430a9f 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -24,6 +24,8 @@ from documents.models import Document from documents.models import DocumentType from documents.models import StoragePath from documents.permissions import set_permissions_for_object +from documents.plugins.helpers import ProgressManager +from documents.plugins.helpers import ProgressStatusOptions from documents.tasks import bulk_update_documents from documents.tasks import consume_file from documents.tasks import update_document_content_maybe_archive_file @@ -219,6 +221,14 @@ def delete(doc_ids: list[int]) -> Literal["OK"]: with index.open_index_writer() as writer: for id in doc_ids: index.remove_document_by_id(writer, id) + + status_mgr = ProgressManager() + status_mgr.send_progress( + status=ProgressStatusOptions.DELETED, + message="Documents deleted", + current_progress=1, + max_progress=1, + ) except Exception as e: if "Data too long for column" in str(e): logger.warning( diff --git a/src/documents/plugins/helpers.py b/src/documents/plugins/helpers.py index 20380b852..6a04fcad8 100644 --- a/src/documents/plugins/helpers.py +++ b/src/documents/plugins/helpers.py @@ -13,6 +13,7 @@ class ProgressStatusOptions(str, enum.Enum): WORKING = "WORKING" SUCCESS = "SUCCESS" FAILED = "FAILED" + DELETED = "DELETED" class ProgressManager: @@ -21,7 +22,7 @@ class ProgressManager: of the open/close of the layer to ensure messages go out and everything is cleaned up """ - def __init__(self, filename: str, task_id: str | None = None) -> None: + def __init__(self, filename: str | None = None, task_id: str | None = None) -> None: self.filename = filename self._channel: RedisPubSubChannelLayer | None = None self.task_id = task_id