mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Merge branch 'dev' into feature-websockets-status
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { cloneFilterRules, FilterRule } from '../data/filter-rule';
|
||||
import { PaperlessDocument } from '../data/paperless-document';
|
||||
import { SavedViewConfig } from '../data/saved-view-config';
|
||||
import { DOCUMENT_LIST_SERVICE, GENERAL_SETTINGS } from '../data/storage-keys';
|
||||
import { PaperlessSavedView } from '../data/paperless-saved-view';
|
||||
import { DOCUMENT_LIST_SERVICE } from '../data/storage-keys';
|
||||
import { DocumentService } from './rest/document.service';
|
||||
import { SettingsService, SETTINGS_KEYS } from './settings.service';
|
||||
|
||||
|
||||
/**
|
||||
* This service manages the document list which is displayed using the document list view.
|
||||
*
|
||||
*
|
||||
* This service also serves saved views by transparently switching between the document list
|
||||
* and saved views on request. See below.
|
||||
*/
|
||||
@@ -23,27 +25,31 @@ export class DocumentListViewService {
|
||||
isReloading: boolean = false
|
||||
documents: PaperlessDocument[] = []
|
||||
currentPage = 1
|
||||
currentPageSize: number = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT
|
||||
currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
|
||||
collectionSize: number
|
||||
|
||||
|
||||
/**
|
||||
* This is the current config for the document list. The service will always remember the last settings used for the document list.
|
||||
*/
|
||||
private _documentListViewConfig: SavedViewConfig
|
||||
private _documentListViewConfig: PaperlessSavedView
|
||||
/**
|
||||
* Optionally, this is the currently selected saved view, which might be null.
|
||||
*/
|
||||
private _savedViewConfig: SavedViewConfig
|
||||
private _savedViewConfig: PaperlessSavedView
|
||||
|
||||
get savedView() {
|
||||
get savedView(): PaperlessSavedView {
|
||||
return this._savedViewConfig
|
||||
}
|
||||
|
||||
set savedView(value) {
|
||||
if (value) {
|
||||
set savedView(value: PaperlessSavedView) {
|
||||
if (value && !this._savedViewConfig || value && value.id != this._savedViewConfig.id) {
|
||||
//saved view inactive and should be active now, or saved view active, but a different view is requested
|
||||
//this is here so that we don't modify value, which might be the actual instance of the saved view.
|
||||
this.selectNone()
|
||||
this._savedViewConfig = Object.assign({}, value)
|
||||
} else {
|
||||
} else if (this._savedViewConfig && !value) {
|
||||
//saved view active, but document list requested
|
||||
this.selectNone()
|
||||
this._savedViewConfig = null
|
||||
}
|
||||
}
|
||||
@@ -53,7 +59,7 @@ export class DocumentListViewService {
|
||||
}
|
||||
|
||||
get savedViewTitle() {
|
||||
return this.savedView?.title
|
||||
return this.savedView?.name
|
||||
}
|
||||
|
||||
get documentListView() {
|
||||
@@ -75,11 +81,11 @@ export class DocumentListViewService {
|
||||
return this.savedView || this.documentListView
|
||||
}
|
||||
|
||||
load(config: SavedViewConfig) {
|
||||
this.view.filterRules = cloneFilterRules(config.filterRules)
|
||||
this.view.sortDirection = config.sortDirection
|
||||
this.view.sortField = config.sortField
|
||||
this.reload()
|
||||
load(view: PaperlessSavedView) {
|
||||
this.documentListView.filter_rules = cloneFilterRules(view.filter_rules)
|
||||
this.documentListView.sort_reverse = view.sort_reverse
|
||||
this.documentListView.sort_field = view.sort_field
|
||||
this.saveDocumentListView()
|
||||
}
|
||||
|
||||
clear() {
|
||||
@@ -90,12 +96,12 @@ export class DocumentListViewService {
|
||||
|
||||
reload(onFinish?) {
|
||||
this.isReloading = true
|
||||
this.documentService.list(
|
||||
this.documentService.listFiltered(
|
||||
this.currentPage,
|
||||
this.currentPageSize,
|
||||
this.view.sortField,
|
||||
this.view.sortDirection,
|
||||
this.view.filterRules).subscribe(
|
||||
this.view.sort_field,
|
||||
this.view.sort_reverse,
|
||||
this.view.filter_rules).subscribe(
|
||||
result => {
|
||||
this.collectionSize = result.count
|
||||
this.documents = result.results
|
||||
@@ -105,7 +111,8 @@ export class DocumentListViewService {
|
||||
this.isReloading = false
|
||||
},
|
||||
error => {
|
||||
if (error.error['detail'] == 'Invalid page.') {
|
||||
if (this.currentPage != 1 && error.status == 404) {
|
||||
// this happens when applying a filter: the current page might not be available anymore due to the reduced result set.
|
||||
this.currentPage = 1
|
||||
this.reload()
|
||||
}
|
||||
@@ -116,39 +123,55 @@ export class DocumentListViewService {
|
||||
set filterRules(filterRules: FilterRule[]) {
|
||||
//we're going to clone the filterRules object, since we don't
|
||||
//want changes in the filter editor to propagate into here right away.
|
||||
this.view.filterRules = cloneFilterRules(filterRules)
|
||||
this.view.filter_rules = filterRules
|
||||
this.reload()
|
||||
this.reduceSelectionToFilter()
|
||||
this.saveDocumentListView()
|
||||
}
|
||||
|
||||
get filterRules(): FilterRule[] {
|
||||
return cloneFilterRules(this.view.filterRules)
|
||||
return this.view.filter_rules
|
||||
}
|
||||
|
||||
set sortField(field: string) {
|
||||
this.view.sortField = field
|
||||
this.view.sort_field = field
|
||||
this.saveDocumentListView()
|
||||
this.reload()
|
||||
}
|
||||
|
||||
get sortField(): string {
|
||||
return this.view.sortField
|
||||
return this.view.sort_field
|
||||
}
|
||||
|
||||
set sortDirection(direction: string) {
|
||||
this.view.sortDirection = direction
|
||||
set sortReverse(reverse: boolean) {
|
||||
this.view.sort_reverse = reverse
|
||||
this.saveDocumentListView()
|
||||
this.reload()
|
||||
}
|
||||
|
||||
get sortDirection(): string {
|
||||
return this.view.sortDirection
|
||||
get sortReverse(): boolean {
|
||||
return this.view.sort_reverse
|
||||
}
|
||||
|
||||
setSort(field: string, reverse: boolean) {
|
||||
this.view.sort_field = field
|
||||
this.view.sort_reverse = reverse
|
||||
this.saveDocumentListView()
|
||||
this.reload()
|
||||
}
|
||||
|
||||
private saveDocumentListView() {
|
||||
sessionStorage.setItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG, JSON.stringify(this.documentListView))
|
||||
}
|
||||
|
||||
quickFilter(filterRules: FilterRule[]) {
|
||||
this.savedView = null
|
||||
this.view.filter_rules = filterRules
|
||||
this.reduceSelectionToFilter()
|
||||
this.saveDocumentListView()
|
||||
this.router.navigate(["documents"])
|
||||
}
|
||||
|
||||
getLastPage(): number {
|
||||
return Math.ceil(this.collectionSize / this.currentPageSize)
|
||||
}
|
||||
@@ -185,14 +208,56 @@ export class DocumentListViewService {
|
||||
}
|
||||
|
||||
updatePageSize() {
|
||||
let newPageSize = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT
|
||||
let newPageSize = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
|
||||
if (newPageSize != this.currentPageSize) {
|
||||
this.currentPageSize = newPageSize
|
||||
//this.reload()
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private documentService: DocumentService) {
|
||||
selected = new Set<number>()
|
||||
|
||||
selectNone() {
|
||||
this.selected.clear()
|
||||
}
|
||||
|
||||
reduceSelectionToFilter() {
|
||||
if (this.selected.size > 0) {
|
||||
this.documentService.listAllFilteredIds(this.filterRules).subscribe(ids => {
|
||||
let subset = new Set<number>()
|
||||
for (let id of ids) {
|
||||
if (this.selected.has(id)) {
|
||||
subset.add(id)
|
||||
}
|
||||
}
|
||||
this.selected = subset
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.documentService.listAllFilteredIds(this.filterRules).subscribe(ids => ids.forEach(id => this.selected.add(id)))
|
||||
}
|
||||
|
||||
selectPage() {
|
||||
this.selected.clear()
|
||||
this.documents.forEach(doc => {
|
||||
this.selected.add(doc.id)
|
||||
})
|
||||
}
|
||||
|
||||
isSelected(d: PaperlessDocument) {
|
||||
return this.selected.has(d.id)
|
||||
}
|
||||
|
||||
setSelected(d: PaperlessDocument, value: boolean) {
|
||||
if (value) {
|
||||
this.selected.add(d.id)
|
||||
} else if (!value) {
|
||||
this.selected.delete(d.id)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private documentService: DocumentService, private settings: SettingsService, private router: Router) {
|
||||
let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
|
||||
if (documentListViewConfigJson) {
|
||||
try {
|
||||
@@ -202,11 +267,11 @@ export class DocumentListViewService {
|
||||
this.documentListView = null
|
||||
}
|
||||
}
|
||||
if (!this.documentListView) {
|
||||
if (!this.documentListView || this.documentListView.filter_rules == null || this.documentListView.sort_reverse == null || this.documentListView.sort_field == null) {
|
||||
this.documentListView = {
|
||||
filterRules: [],
|
||||
sortDirection: 'des',
|
||||
sortField: 'created'
|
||||
filter_rules: [],
|
||||
sort_reverse: true,
|
||||
sort_field: 'created'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PaperlessDocument } from '../data/paperless-document';
|
||||
import { OPEN_DOCUMENT_SERVICE } from '../data/storage-keys';
|
||||
import { DocumentService } from './rest/document.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -9,7 +10,7 @@ export class OpenDocumentsService {
|
||||
|
||||
private MAX_OPEN_DOCUMENTS = 5
|
||||
|
||||
constructor() {
|
||||
constructor(private documentService: DocumentService) {
|
||||
if (sessionStorage.getItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS)) {
|
||||
try {
|
||||
this.openDocuments = JSON.parse(sessionStorage.getItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS))
|
||||
@@ -22,6 +23,18 @@ export class OpenDocumentsService {
|
||||
|
||||
private openDocuments: PaperlessDocument[] = []
|
||||
|
||||
refreshDocument(id: number) {
|
||||
let index = this.openDocuments.findIndex(doc => doc.id == id)
|
||||
if (index > -1) {
|
||||
this.documentService.get(id).subscribe(doc => {
|
||||
this.openDocuments[index] = doc
|
||||
}, error => {
|
||||
this.openDocuments.splice(index, 1)
|
||||
this.save()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getOpenDocuments(): PaperlessDocument[] {
|
||||
return this.openDocuments
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||
import { Observable, of, Subject } from 'rxjs'
|
||||
import { Observable } from 'rxjs'
|
||||
import { map, publishReplay, refCount } from 'rxjs/operators'
|
||||
import { ObjectWithId } from 'src/app/data/object-with-id'
|
||||
import { Results } from 'src/app/data/results'
|
||||
@@ -22,17 +22,15 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
return url
|
||||
}
|
||||
|
||||
private getOrderingQueryParam(sortField: string, sortDirection: string) {
|
||||
if (sortField && sortDirection) {
|
||||
return (sortDirection == 'des' ? '-' : '') + sortField
|
||||
} else if (sortField) {
|
||||
return sortField
|
||||
private getOrderingQueryParam(sortField: string, sortReverse: boolean) {
|
||||
if (sortField) {
|
||||
return (sortReverse ? '-' : '') + sortField
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
list(page?: number, pageSize?: number, sortField?: string, sortDirection?: string, extraParams?): Observable<Results<T>> {
|
||||
list(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, extraParams?): Observable<Results<T>> {
|
||||
let httpParams = new HttpParams()
|
||||
if (page) {
|
||||
httpParams = httpParams.set('page', page.toString())
|
||||
@@ -40,7 +38,7 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
if (pageSize) {
|
||||
httpParams = httpParams.set('page_size', pageSize.toString())
|
||||
}
|
||||
let ordering = this.getOrderingQueryParam(sortField, sortDirection)
|
||||
let ordering = this.getOrderingQueryParam(sortField, sortReverse)
|
||||
if (ordering) {
|
||||
httpParams = httpParams.set('ordering', ordering)
|
||||
}
|
||||
@@ -54,9 +52,9 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
|
||||
private _listAll: Observable<Results<T>>
|
||||
|
||||
listAll(ordering?: string, extraParams?): Observable<Results<T>> {
|
||||
listAll(sortField?: string, sortReverse?: boolean, extraParams?): Observable<Results<T>> {
|
||||
if (!this._listAll) {
|
||||
this._listAll = this.list(1, 100000, ordering, extraParams).pipe(
|
||||
this._listAll = this.list(1, 100000, sortField, sortReverse, extraParams).pipe(
|
||||
publishReplay(1),
|
||||
refCount()
|
||||
)
|
||||
@@ -76,22 +74,32 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
)
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
this._listAll = null
|
||||
}
|
||||
|
||||
get(id: number): Observable<T> {
|
||||
return this.http.get<T>(this.getResourceUrl(id))
|
||||
}
|
||||
|
||||
create(o: T): Observable<T> {
|
||||
this._listAll = null
|
||||
this.clearCache()
|
||||
return this.http.post<T>(this.getResourceUrl(), o)
|
||||
}
|
||||
|
||||
delete(o: T): Observable<any> {
|
||||
this._listAll = null
|
||||
this.clearCache()
|
||||
return this.http.delete(this.getResourceUrl(o.id))
|
||||
}
|
||||
|
||||
update(o: T): Observable<T> {
|
||||
this._listAll = null
|
||||
this.clearCache()
|
||||
return this.http.put<T>(this.getResourceUrl(o.id), o)
|
||||
}
|
||||
}
|
||||
|
||||
patch(o: T): Observable<T> {
|
||||
this.clearCache()
|
||||
return this.http.patch<T>(this.getResourceUrl(o.id), o)
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Results } from 'src/app/data/results';
|
||||
import { FilterRule } from 'src/app/data/filter-rule';
|
||||
@@ -10,21 +10,28 @@ import { map } from 'rxjs/operators';
|
||||
import { CorrespondentService } from './correspondent.service';
|
||||
import { DocumentTypeService } from './document-type.service';
|
||||
import { TagService } from './tag.service';
|
||||
|
||||
import { FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type';
|
||||
|
||||
export const DOCUMENT_SORT_FIELDS = [
|
||||
{ field: "correspondent__name", name: "Correspondent" },
|
||||
{ field: "document_type__name", name: "Document type" },
|
||||
{ field: 'title', name: 'Title' },
|
||||
{ field: 'archive_serial_number', name: 'ASN' },
|
||||
{ field: 'created', name: 'Created' },
|
||||
{ field: 'added', name: 'Added' },
|
||||
{ field: 'modified', name: 'Modified' }
|
||||
{ field: 'archive_serial_number', name: $localize`ASN` },
|
||||
{ field: "correspondent__name", name: $localize`Correspondent` },
|
||||
{ field: 'title', name: $localize`Title` },
|
||||
{ field: "document_type__name", name: $localize`Document type` },
|
||||
{ field: 'created', name: $localize`Created` },
|
||||
{ field: 'added', name: $localize`Added` },
|
||||
{ field: 'modified', name: $localize`Modified` }
|
||||
]
|
||||
|
||||
export const SORT_DIRECTION_ASCENDING = "asc"
|
||||
export const SORT_DIRECTION_DESCENDING = "des"
|
||||
export interface SelectionDataItem {
|
||||
id: number
|
||||
document_count: number
|
||||
}
|
||||
|
||||
export interface SelectionData {
|
||||
selected_correspondents: SelectionDataItem[]
|
||||
selected_tags: SelectionDataItem[]
|
||||
selected_document_types: SelectionDataItem[]
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -39,10 +46,13 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
|
||||
if (filterRules) {
|
||||
let params = {}
|
||||
for (let rule of filterRules) {
|
||||
if (rule.type.multi) {
|
||||
params[rule.type.filtervar] = params[rule.type.filtervar] ? params[rule.type.filtervar] + "," + rule.value : rule.value
|
||||
let ruleType = FILTER_RULE_TYPES.find(t => t.id == rule.rule_type)
|
||||
if (ruleType.multi) {
|
||||
params[ruleType.filtervar] = params[ruleType.filtervar] ? params[ruleType.filtervar] + "," + rule.value : rule.value
|
||||
} else if (ruleType.isnull_filtervar && rule.value == null) {
|
||||
params[ruleType.isnull_filtervar] = true
|
||||
} else {
|
||||
params[rule.type.filtervar] = rule.value
|
||||
params[ruleType.filtervar] = rule.value
|
||||
}
|
||||
}
|
||||
return params
|
||||
@@ -64,8 +74,8 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
|
||||
return doc
|
||||
}
|
||||
|
||||
list(page?: number, pageSize?: number, sortField?: string, sortDirection?: string, filterRules?: FilterRule[]): Observable<Results<PaperlessDocument>> {
|
||||
return super.list(page, pageSize, sortField, sortDirection, this.filterRulesToQueryParams(filterRules)).pipe(
|
||||
listFiltered(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, filterRules?: FilterRule[], extraParams = {}): Observable<Results<PaperlessDocument>> {
|
||||
return this.list(page, pageSize, sortField, sortReverse, Object.assign(extraParams, this.filterRulesToQueryParams(filterRules))).pipe(
|
||||
map(results => {
|
||||
results.results.forEach(doc => this.addObservablesToDocument(doc))
|
||||
return results
|
||||
@@ -73,6 +83,12 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
|
||||
)
|
||||
}
|
||||
|
||||
listAllFilteredIds(filterRules?: FilterRule[]): Observable<number[]> {
|
||||
return this.listFiltered(1, 100000, null, null, filterRules, {"fields": "id"}).pipe(
|
||||
map(response => response.results.map(doc => doc.id))
|
||||
)
|
||||
}
|
||||
|
||||
getPreviewUrl(id: number, original: boolean = false): string {
|
||||
let url = this.getResourceUrl(id, 'preview')
|
||||
if (original) {
|
||||
@@ -94,11 +110,23 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
|
||||
}
|
||||
|
||||
uploadDocument(formData) {
|
||||
return this.http.post(this.getResourceUrl(null, 'post_document'), formData)
|
||||
return this.http.post(this.getResourceUrl(null, 'post_document'), formData, {reportProgress: true, observe: "events"})
|
||||
}
|
||||
|
||||
getMetadata(id: number): Observable<PaperlessDocumentMetadata> {
|
||||
return this.http.get<PaperlessDocumentMetadata>(this.getResourceUrl(id, 'metadata'))
|
||||
}
|
||||
|
||||
bulkEdit(ids: number[], method: string, args: any) {
|
||||
return this.http.post(this.getResourceUrl(null, 'bulk_edit'), {
|
||||
'documents': ids,
|
||||
'method': method,
|
||||
'parameters': args
|
||||
})
|
||||
}
|
||||
|
||||
getSelectionData(ids: number[]): Observable<SelectionData> {
|
||||
return this.http.post<SelectionData>(this.getResourceUrl(null, 'selection_data'), {"documents": ids})
|
||||
}
|
||||
|
||||
}
|
||||
|
16
src-ui/src/app/services/rest/saved-view.service.spec.ts
Normal file
16
src-ui/src/app/services/rest/saved-view.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SavedViewService } from './saved-view.service';
|
||||
|
||||
describe('SavedViewService', () => {
|
||||
let service: SavedViewService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(SavedViewService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
59
src-ui/src/app/services/rest/saved-view.service.ts
Normal file
59
src-ui/src/app/services/rest/saved-view.service.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SavedViewService extends AbstractPaperlessService<PaperlessSavedView> {
|
||||
|
||||
constructor(http: HttpClient) {
|
||||
super(http, 'saved_views')
|
||||
this.reload()
|
||||
}
|
||||
|
||||
private reload() {
|
||||
this.listAll().subscribe(r => this.savedViews = r.results)
|
||||
}
|
||||
|
||||
private savedViews: PaperlessSavedView[] = []
|
||||
|
||||
get allViews() {
|
||||
return this.savedViews
|
||||
}
|
||||
|
||||
get sidebarViews() {
|
||||
return this.savedViews.filter(v => v.show_in_sidebar)
|
||||
}
|
||||
|
||||
get dashboardViews() {
|
||||
return this.savedViews.filter(v => v.show_on_dashboard)
|
||||
}
|
||||
|
||||
create(o: PaperlessSavedView) {
|
||||
return super.create(o).pipe(
|
||||
tap(() => this.reload())
|
||||
)
|
||||
}
|
||||
|
||||
update(o: PaperlessSavedView) {
|
||||
return super.update(o).pipe(
|
||||
tap(() => this.reload())
|
||||
)
|
||||
}
|
||||
|
||||
patchMany(objects: PaperlessSavedView[]): Observable<PaperlessSavedView[]> {
|
||||
return combineLatest(objects.map(o => super.patch(o))).pipe(
|
||||
tap(() => this.reload())
|
||||
)
|
||||
}
|
||||
|
||||
delete(o: PaperlessSavedView) {
|
||||
return super.delete(o).pipe(
|
||||
tap(() => this.reload())
|
||||
)
|
||||
}
|
||||
}
|
@@ -15,11 +15,17 @@ export class SearchService {
|
||||
|
||||
constructor(private http: HttpClient, private documentService: DocumentService) { }
|
||||
|
||||
search(query: string, page?: number): Observable<SearchResult> {
|
||||
let httpParams = new HttpParams().set('query', query)
|
||||
search(query: string, page?: number, more_like?: number): Observable<SearchResult> {
|
||||
let httpParams = new HttpParams()
|
||||
if (query) {
|
||||
httpParams = httpParams.set('query', query)
|
||||
}
|
||||
if (page) {
|
||||
httpParams = httpParams.set('page', page.toString())
|
||||
}
|
||||
if (more_like) {
|
||||
httpParams = httpParams.set('more_like', more_like.toString())
|
||||
}
|
||||
return this.http.get<SearchResult>(`${environment.apiBaseUrl}search/`, {params: httpParams}).pipe(
|
||||
map(result => {
|
||||
result.results.forEach(hit => this.documentService.addObservablesToDocument(hit.document))
|
||||
|
@@ -1,16 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SavedViewConfigService } from './saved-view-config.service';
|
||||
|
||||
describe('SavedViewConfigService', () => {
|
||||
let service: SavedViewConfigService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(SavedViewConfigService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,66 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SavedViewConfig } from '../data/saved-view-config';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SavedViewConfigService {
|
||||
|
||||
constructor() {
|
||||
let savedConfigs = localStorage.getItem('saved-view-config-service:savedConfigs')
|
||||
if (savedConfigs) {
|
||||
try {
|
||||
this.configs = JSON.parse(savedConfigs)
|
||||
} catch (e) {
|
||||
this.configs = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private configs: SavedViewConfig[] = []
|
||||
|
||||
getConfigs(): SavedViewConfig[] {
|
||||
return this.configs
|
||||
}
|
||||
|
||||
getDashboardConfigs(): SavedViewConfig[] {
|
||||
return this.configs.filter(sf => sf.showInDashboard)
|
||||
}
|
||||
|
||||
getSideBarConfigs(): SavedViewConfig[] {
|
||||
return this.configs.filter(sf => sf.showInSideBar)
|
||||
}
|
||||
|
||||
getConfig(id: string): SavedViewConfig {
|
||||
return this.configs.find(sf => sf.id == id)
|
||||
}
|
||||
|
||||
newConfig(config: SavedViewConfig) {
|
||||
config.id = uuidv4()
|
||||
this.configs.push(config)
|
||||
|
||||
this.save()
|
||||
}
|
||||
|
||||
updateConfig(config: SavedViewConfig) {
|
||||
let savedConfig = this.configs.find(c => c.id == config.id)
|
||||
if (savedConfig) {
|
||||
Object.assign(savedConfig, config)
|
||||
this.save()
|
||||
}
|
||||
}
|
||||
|
||||
private save() {
|
||||
localStorage.setItem('saved-view-config-service:savedConfigs', JSON.stringify(this.configs))
|
||||
}
|
||||
|
||||
deleteConfig(config: SavedViewConfig) {
|
||||
let index = this.configs.findIndex(vc => vc.id == config.id)
|
||||
if (index != -1) {
|
||||
this.configs.splice(index, 1)
|
||||
this.save()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
16
src-ui/src/app/services/settings.service.spec.ts
Normal file
16
src-ui/src/app/services/settings.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsService } from './settings.service';
|
||||
|
||||
describe('SettingsService', () => {
|
||||
let service: SettingsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(SettingsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
88
src-ui/src/app/services/settings.service.ts
Normal file
88
src-ui/src/app/services/settings.service.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
|
||||
|
||||
export interface PaperlessSettings {
|
||||
key: string
|
||||
type: string
|
||||
default: any
|
||||
}
|
||||
|
||||
export const SETTINGS_KEYS = {
|
||||
BULK_EDIT_CONFIRMATION_DIALOGS: 'general-settings:bulk-edit:confirmation-dialogs',
|
||||
BULK_EDIT_APPLY_ON_CLOSE: 'general-settings:bulk-edit:apply-on-close',
|
||||
DOCUMENT_LIST_SIZE: 'general-settings:documentListSize',
|
||||
DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system',
|
||||
DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled'
|
||||
}
|
||||
|
||||
const SETTINGS: PaperlessSettings[] = [
|
||||
{key: SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, type: "boolean", default: true},
|
||||
{key: SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, type: "boolean", default: false},
|
||||
{key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50},
|
||||
{key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true},
|
||||
{key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false}
|
||||
]
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SettingsService {
|
||||
|
||||
private renderer: Renderer2;
|
||||
|
||||
constructor(
|
||||
private rendererFactory: RendererFactory2,
|
||||
@Inject(DOCUMENT) private document
|
||||
) {
|
||||
this.renderer = rendererFactory.createRenderer(null, null);
|
||||
|
||||
this.updateDarkModeSettings()
|
||||
}
|
||||
|
||||
updateDarkModeSettings(): void {
|
||||
let darkModeUseSystem = this.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)
|
||||
let darkModeEnabled = this.get(SETTINGS_KEYS.DARK_MODE_ENABLED)
|
||||
|
||||
if (darkModeUseSystem) {
|
||||
this.renderer.addClass(this.document.body, 'color-scheme-system')
|
||||
this.renderer.removeClass(this.document.body, 'color-scheme-dark')
|
||||
} else {
|
||||
this.renderer.removeClass(this.document.body, 'color-scheme-system')
|
||||
darkModeEnabled ? this.renderer.addClass(this.document.body, 'color-scheme-dark') : this.renderer.removeClass(this.document.body, 'color-scheme-dark')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get(key: string): any {
|
||||
let setting = SETTINGS.find(s => s.key == key)
|
||||
|
||||
if (!setting) {
|
||||
return null
|
||||
}
|
||||
|
||||
let value = localStorage.getItem(key)
|
||||
|
||||
if (value != null) {
|
||||
switch (setting.type) {
|
||||
case "boolean":
|
||||
return JSON.parse(value)
|
||||
case "number":
|
||||
return +value
|
||||
case "string":
|
||||
return value
|
||||
default:
|
||||
return value
|
||||
}
|
||||
} else {
|
||||
return setting.default
|
||||
}
|
||||
}
|
||||
|
||||
set(key: string, value: any) {
|
||||
localStorage.setItem(key, value.toString())
|
||||
}
|
||||
|
||||
unset(key: string) {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ export interface Toast {
|
||||
|
||||
content: string
|
||||
|
||||
delay?: number
|
||||
delay: number
|
||||
|
||||
action?: any
|
||||
|
||||
@@ -26,17 +26,17 @@ export class ToastService {
|
||||
|
||||
private toastsSubject: Subject<Toast[]> = new Subject()
|
||||
|
||||
showToast(toast: Toast) {
|
||||
show(toast: Toast) {
|
||||
this.toasts.push(toast)
|
||||
this.toastsSubject.next(this.toasts)
|
||||
}
|
||||
|
||||
showInfo(message: string) {
|
||||
this.showToast({title: "Information", content: message, delay: 5000})
|
||||
showError(content: string, delay: number = 10000) {
|
||||
this.show({title: $localize`Error`, content: content, delay: delay})
|
||||
}
|
||||
|
||||
showError(message: string) {
|
||||
this.showToast({title: "Error", content: message, delay: 10000})
|
||||
showInfo(content: string, delay: number = 5000) {
|
||||
this.show({title: $localize`Information`, content: content, delay: delay})
|
||||
}
|
||||
|
||||
closeToast(toast: Toast) {
|
||||
|
Reference in New Issue
Block a user