mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge pull request #2028 from paperless-ngx/feature-truncate-content
Feature: speed up frontend by truncating content
This commit is contained in:
		| @@ -11,9 +11,9 @@ | |||||||
|       </tr> |       </tr> | ||||||
|     </thead> |     </thead> | ||||||
|     <tbody> |     <tbody> | ||||||
|       <tr *ngFor="let doc of documents" (click)="openDocumentsService.openDocument(doc)"> |       <tr *ngFor="let doc of documents"> | ||||||
|         <td>{{doc.created_date | customDate}}</td> |         <td><a routerLink="/documents/{{doc.id}}" class="d-block text-dark text-decoration-none">{{doc.created_date | customDate}}</a></td> | ||||||
|         <td>{{doc.title | documentTitle}}<app-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ms-1" (click)="clickTag(t); $event.stopPropagation();"></app-tag></td> |         <td><a routerLink="/documents/{{doc.id}}" class="d-block text-dark text-decoration-none">{{doc.title | documentTitle}}<app-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ms-1" (click)="clickTag(t, $event)"></app-tag></a></td> | ||||||
|       </tr> |       </tr> | ||||||
|     </tbody> |     </tbody> | ||||||
|   </table> |   </table> | ||||||
|   | |||||||
| @@ -7,6 +7,6 @@ th:first-child { | |||||||
|   width: 25%; |   width: 25%; | ||||||
| } | } | ||||||
|  |  | ||||||
| tbody tr { | tbody app-tag { | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -72,7 +72,9 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   clickTag(tag: PaperlessTag) { |   clickTag(tag: PaperlessTag, event: MouseEvent) { | ||||||
|  |     event.preventDefault() | ||||||
|  |  | ||||||
|     this.list.quickFilter([ |     this.list.quickFilter([ | ||||||
|       { rule_type: FILTER_HAS_TAGS_ALL, value: tag.id.toString() }, |       { rule_type: FILTER_HAS_TAGS_ALL, value: tag.id.toString() }, | ||||||
|     ]) |     ]) | ||||||
|   | |||||||
| @@ -184,7 +184,7 @@ export class DocumentDetailComponent | |||||||
|               this.openDocumentService.getOpenDocument(this.documentId) |               this.openDocumentService.getOpenDocument(this.documentId) | ||||||
|             ) |             ) | ||||||
|           } else { |           } else { | ||||||
|             this.openDocumentService.openDocument(doc, false) |             this.openDocumentService.openDocument(doc) | ||||||
|             this.updateComponent(doc) |             this.updateComponent(doc) | ||||||
|           } |           } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ | |||||||
|                 <use xlink:href="assets/bootstrap-icons.svg#diagram-3"/> |                 <use xlink:href="assets/bootstrap-icons.svg#diagram-3"/> | ||||||
|               </svg> <span class="d-none d-md-inline" i18n>More like this</span> |               </svg> <span class="d-none d-md-inline" i18n>More like this</span> | ||||||
|             </a> |             </a> | ||||||
|             <a (click)="openDocumentsService.openDocument(document)" class="btn btn-sm btn-outline-secondary"> |             <a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary"> | ||||||
|               <svg class="sidebaricon" fill="currentColor" class="sidebaricon"> |               <svg class="sidebaricon" fill="currentColor" class="sidebaricon"> | ||||||
|                 <use xlink:href="assets/bootstrap-icons.svg#pencil"/> |                 <use xlink:href="assets/bootstrap-icons.svg#pencil"/> | ||||||
|               </svg> <span class="d-none d-md-inline" i18n>Edit</span> |               </svg> <span class="d-none d-md-inline" i18n>Edit</span> | ||||||
|   | |||||||
| @@ -10,9 +10,6 @@ import { PaperlessDocument } from 'src/app/data/paperless-document' | |||||||
| import { DocumentService } from 'src/app/services/rest/document.service' | import { DocumentService } from 'src/app/services/rest/document.service' | ||||||
| import { SettingsService } from 'src/app/services/settings.service' | import { SettingsService } from 'src/app/services/settings.service' | ||||||
| import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | ||||||
| import { OpenDocumentsService } from 'src/app/services/open-documents.service' |  | ||||||
| import { DocumentListViewService } from 'src/app/services/document-list-view.service' |  | ||||||
| import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type' |  | ||||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
| @@ -26,8 +23,7 @@ import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | |||||||
| export class DocumentCardLargeComponent implements OnInit { | export class DocumentCardLargeComponent implements OnInit { | ||||||
|   constructor( |   constructor( | ||||||
|     private documentService: DocumentService, |     private documentService: DocumentService, | ||||||
|     private settingsService: SettingsService, |     private settingsService: SettingsService | ||||||
|     public openDocumentsService: OpenDocumentsService |  | ||||||
|   ) {} |   ) {} | ||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
| @@ -119,6 +115,9 @@ export class DocumentCardLargeComponent implements OnInit { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   get contentTrimmed() { |   get contentTrimmed() { | ||||||
|     return this.document.content.substr(0, 500) |     return ( | ||||||
|  |       this.document.content.substr(0, 500) + | ||||||
|  |       (this.document.content.length > 500 ? '...' : '') | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="d-flex justify-content-between align-items-center"> |       <div class="d-flex justify-content-between align-items-center"> | ||||||
|         <div class="btn-group w-100"> |         <div class="btn-group w-100"> | ||||||
|           <a (click)="openDocumentsService.openDocument(document)" class="btn btn-sm btn-outline-secondary" title="Edit" i18n-title> |           <a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Edit" i18n-title> | ||||||
|             <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> |             <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||||
|               <path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/> |               <path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/> | ||||||
|             </svg> |             </svg> | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ import { PaperlessDocument } from 'src/app/data/paperless-document' | |||||||
| import { DocumentService } from 'src/app/services/rest/document.service' | import { DocumentService } from 'src/app/services/rest/document.service' | ||||||
| import { SettingsService } from 'src/app/services/settings.service' | import { SettingsService } from 'src/app/services/settings.service' | ||||||
| import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | ||||||
| import { OpenDocumentsService } from 'src/app/services/open-documents.service' |  | ||||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
| @@ -25,8 +24,7 @@ import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | |||||||
| export class DocumentCardSmallComponent implements OnInit { | export class DocumentCardSmallComponent implements OnInit { | ||||||
|   constructor( |   constructor( | ||||||
|     private documentService: DocumentService, |     private documentService: DocumentService, | ||||||
|     private settingsService: SettingsService, |     private settingsService: SettingsService | ||||||
|     public openDocumentsService: OpenDocumentsService |  | ||||||
|   ) {} |   ) {} | ||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
|   | |||||||
| @@ -179,7 +179,7 @@ | |||||||
|           </ng-container> |           </ng-container> | ||||||
|         </td> |         </td> | ||||||
|         <td> |         <td> | ||||||
|           <a (click)="openDocumentsService.openDocument(d)" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a> |           <a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a> | ||||||
|           <app-tag [tag]="t" *ngFor="let t of d.tags$ | async" class="ms-1" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(t.id);$event.stopPropagation()"></app-tag> |           <app-tag [tag]="t" *ngFor="let t of d.tags$ | async" class="ms-1" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(t.id);$event.stopPropagation()"></app-tag> | ||||||
|         </td> |         </td> | ||||||
|         <td class="d-none d-xl-table-cell"> |         <td class="d-none d-xl-table-cell"> | ||||||
|   | |||||||
| @@ -213,7 +213,8 @@ export class DocumentListViewService { | |||||||
|         this.currentPageSize, |         this.currentPageSize, | ||||||
|         activeListViewState.sortField, |         activeListViewState.sortField, | ||||||
|         activeListViewState.sortReverse, |         activeListViewState.sortReverse, | ||||||
|         activeListViewState.filterRules |         activeListViewState.filterRules, | ||||||
|  |         { truncate_content: true } | ||||||
|       ) |       ) | ||||||
|       .subscribe({ |       .subscribe({ | ||||||
|         next: (result) => { |         next: (result) => { | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | |||||||
| import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component' | import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component' | ||||||
| import { Observable, Subject, of } from 'rxjs' | import { Observable, Subject, of } from 'rxjs' | ||||||
| import { first } from 'rxjs/operators' | import { first } from 'rxjs/operators' | ||||||
| import { Router } from '@angular/router' |  | ||||||
|  |  | ||||||
| @Injectable({ | @Injectable({ | ||||||
|   providedIn: 'root', |   providedIn: 'root', | ||||||
| @@ -16,8 +15,7 @@ export class OpenDocumentsService { | |||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
|     private documentService: DocumentService, |     private documentService: DocumentService, | ||||||
|     private modalService: NgbModal, |     private modalService: NgbModal | ||||||
|     private router: Router |  | ||||||
|   ) { |   ) { | ||||||
|     if (sessionStorage.getItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS)) { |     if (sessionStorage.getItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS)) { | ||||||
|       try { |       try { | ||||||
| @@ -57,39 +55,28 @@ export class OpenDocumentsService { | |||||||
|     return this.openDocuments.find((d) => d.id == id) |     return this.openDocuments.find((d) => d.id == id) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   openDocument( |   openDocument(doc: PaperlessDocument): Observable<boolean> { | ||||||
|     doc: PaperlessDocument, |  | ||||||
|     navigate: boolean = true |  | ||||||
|   ): Observable<boolean> { |  | ||||||
|     if (this.openDocuments.find((d) => d.id == doc.id) == null) { |     if (this.openDocuments.find((d) => d.id == doc.id) == null) { | ||||||
|       if (this.openDocuments.length == this.MAX_OPEN_DOCUMENTS) { |       if (this.openDocuments.length == this.MAX_OPEN_DOCUMENTS) { | ||||||
|         // at max, ensure changes arent lost |         // at max, ensure changes arent lost | ||||||
|         const docToRemove = this.openDocuments[this.MAX_OPEN_DOCUMENTS - 1] |         const docToRemove = this.openDocuments[this.MAX_OPEN_DOCUMENTS - 1] | ||||||
|         const closeObservable = this.closeDocument(docToRemove) |         const closeObservable = this.closeDocument(docToRemove) | ||||||
|         closeObservable.pipe(first()).subscribe((closed) => { |         closeObservable.pipe(first()).subscribe((closed) => { | ||||||
|           if (closed) this.finishOpenDocument(doc, navigate) |           if (closed) this.finishOpenDocument(doc) | ||||||
|         }) |         }) | ||||||
|         return closeObservable |         return closeObservable | ||||||
|       } else { |       } else { | ||||||
|         // not at max |         // not at max | ||||||
|         this.finishOpenDocument(doc, navigate) |         this.finishOpenDocument(doc) | ||||||
|       } |  | ||||||
|     } else { |  | ||||||
|       // doc is open, just maybe navigate |  | ||||||
|       if (navigate) { |  | ||||||
|         this.router.navigate(['documents', doc.id]) |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return of(true) |     return of(true) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private finishOpenDocument(doc: PaperlessDocument, navigate: boolean) { |   private finishOpenDocument(doc: PaperlessDocument) { | ||||||
|     this.openDocuments.unshift(doc) |     this.openDocuments.unshift(doc) | ||||||
|     this.dirtyDocuments.delete(doc.id) |     this.dirtyDocuments.delete(doc.id) | ||||||
|     this.save() |     this.save() | ||||||
|     if (navigate) { |  | ||||||
|       this.router.navigate(['documents', doc.id]) |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   setDirty(doc: PaperlessDocument, dirty: boolean) { |   setDirty(doc: PaperlessDocument, dirty: boolean) { | ||||||
|   | |||||||
| @@ -234,6 +234,12 @@ class DocumentSerializer(DynamicFieldsModelSerializer): | |||||||
|         else: |         else: | ||||||
|             return None |             return None | ||||||
|  |  | ||||||
|  |     def to_representation(self, instance): | ||||||
|  |         doc = super().to_representation(instance) | ||||||
|  |         if self.truncate_content: | ||||||
|  |             doc["content"] = doc.get("content")[0:550] | ||||||
|  |         return doc | ||||||
|  |  | ||||||
|     def update(self, instance, validated_data): |     def update(self, instance, validated_data): | ||||||
|         if "created_date" in validated_data and "created" not in validated_data: |         if "created_date" in validated_data and "created" not in validated_data: | ||||||
|             new_datetime = datetime.datetime.combine( |             new_datetime = datetime.datetime.combine( | ||||||
| @@ -247,6 +253,11 @@ class DocumentSerializer(DynamicFieldsModelSerializer): | |||||||
|         super().update(instance, validated_data) |         super().update(instance, validated_data) | ||||||
|         return instance |         return instance | ||||||
|  |  | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         self.truncate_content = kwargs.pop("truncate_content", False) | ||||||
|  |  | ||||||
|  |         super().__init__(*args, **kwargs) | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = Document |         model = Document | ||||||
|         depth = 1 |         depth = 1 | ||||||
|   | |||||||
| @@ -226,9 +226,11 @@ class DocumentViewSet( | |||||||
|             fields = fields_param.split(",") |             fields = fields_param.split(",") | ||||||
|         else: |         else: | ||||||
|             fields = None |             fields = None | ||||||
|  |         truncate_content = self.request.query_params.get("truncate_content", "False") | ||||||
|         serializer_class = self.get_serializer_class() |         serializer_class = self.get_serializer_class() | ||||||
|         kwargs.setdefault("context", self.get_serializer_context()) |         kwargs.setdefault("context", self.get_serializer_context()) | ||||||
|         kwargs.setdefault("fields", fields) |         kwargs.setdefault("fields", fields) | ||||||
|  |         kwargs.setdefault("truncate_content", truncate_content.lower() in ["true", "1"]) | ||||||
|         return serializer_class(*args, **kwargs) |         return serializer_class(*args, **kwargs) | ||||||
|  |  | ||||||
|     def update(self, request, *args, **kwargs): |     def update(self, request, *args, **kwargs): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon