Nice, UX for doc in trash

This commit is contained in:
shamoon
2026-01-18 14:14:50 -08:00
parent cd1070bd3f
commit aa4b685a07
4 changed files with 41 additions and 22 deletions

View File

@@ -385,9 +385,13 @@
<a <a
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"
[routerLink]="['/documents', duplicate.id, 'details']" [routerLink]="['/documents', duplicate.id, 'details']"
[class.disabled]="duplicate.deleted_at"
> >
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
<span>{{ duplicate.title || ('#' + duplicate.id) }}</span> <span>{{ duplicate.title || ('#' + duplicate.id) }}</span>
@if (duplicate.deleted_at) {
<span class="badge text-bg-secondary" i18n>In trash</span>
}
</span> </span>
<span class="text-secondary">#{{ duplicate.id }}</span> <span class="text-secondary">#{{ duplicate.id }}</span>
</a> </a>

View File

@@ -456,6 +456,11 @@ export class DocumentDetailComponent
const openDocument = this.openDocumentService.getOpenDocument( const openDocument = this.openDocumentService.getOpenDocument(
this.documentId this.documentId
) )
// update duplicate documents if present
if (openDocument && doc?.duplicate_documents) {
openDocument.duplicate_documents = doc.duplicate_documents
this.openDocumentService.save()
}
const useDoc = openDocument || doc const useDoc = openDocument || doc
if (openDocument) { if (openDocument) {
if ( if (

View File

@@ -148,13 +148,29 @@ def get_document_count_filter_for_user(user):
) )
def get_objects_for_user_owner_aware(user, perms, Model) -> QuerySet: def get_objects_for_user_owner_aware(
objects_owned = Model.objects.filter(owner=user) user,
objects_unowned = Model.objects.filter(owner__isnull=True) perms,
Model,
*,
include_deleted=False,
) -> QuerySet:
"""
Returns objects the user owns, are unowned, or has explicit perms.
When include_deleted is True, soft-deleted items are also included.
"""
manager = (
Model.global_objects
if include_deleted and hasattr(Model, "global_objects")
else Model.objects
)
objects_owned = manager.filter(owner=user)
objects_unowned = manager.filter(owner__isnull=True)
objects_with_perms = get_objects_for_user( objects_with_perms = get_objects_for_user(
user=user, user=user,
perms=perms, perms=perms,
klass=Model, klass=manager.all(),
accept_global_perms=False, accept_global_perms=False,
) )
return objects_owned | objects_unowned | objects_with_perms return objects_owned | objects_unowned | objects_with_perms

View File

@@ -1020,23 +1020,17 @@ def _get_viewable_duplicates(document: Document, user: User | None):
checksums = {document.checksum} checksums = {document.checksum}
if document.archive_checksum: if document.archive_checksum:
checksums.add(document.archive_checksum) checksums.add(document.archive_checksum)
duplicates = ( duplicates = Document.global_objects.filter(
Document.global_objects.filter(
Q(checksum__in=checksums) | Q(archive_checksum__in=checksums), Q(checksum__in=checksums) | Q(archive_checksum__in=checksums),
deleted_at__isnull=True, ).exclude(pk=document.pk)
) duplicates = duplicates.order_by("-created")
.exclude(pk=document.pk) allowed = get_objects_for_user_owner_aware(
.order_by("-created")
)
if user.is_superuser:
return duplicates
return duplicates.filter(
id__in=get_objects_for_user_owner_aware(
user, user,
"documents.view_document", "documents.view_document",
Document, Document,
).values_list("id", flat=True), include_deleted=True,
) )
return duplicates.filter(id__in=allowed.values_list("id", flat=True))
@extend_schema_serializer( @extend_schema_serializer(
@@ -1089,7 +1083,7 @@ class DocumentSerializer(
request = self.context.get("request") request = self.context.get("request")
user = request.user if request else None user = request.user if request else None
duplicates = _get_viewable_duplicates(obj, user) duplicates = _get_viewable_duplicates(obj, user)
return list(duplicates.values("id", "title")) return list(duplicates.values("id", "title", "deleted_at"))
def get_original_file_name(self, obj) -> str | None: def get_original_file_name(self, obj) -> str | None:
return obj.original_filename return obj.original_filename
@@ -2178,7 +2172,7 @@ class TasksViewSerializer(OwnedObjectSerializer):
request = self.context.get("request") request = self.context.get("request")
user = request.user if request else None user = request.user if request else None
duplicates = _get_viewable_duplicates(document, user) duplicates = _get_viewable_duplicates(document, user)
cache[obj.pk] = list(duplicates.values("id", "title")) cache[obj.pk] = list(duplicates.values("id", "title", "deleted_at"))
return cache[obj.pk] return cache[obj.pk]
def get_duplicate_documents(self, obj): def get_duplicate_documents(self, obj):