Get storage path explorer somewhat working

This commit is contained in:
Martin Tan 2023-05-29 03:10:37 +08:00
parent 5ae5777467
commit 10ab272df7
5 changed files with 229 additions and 360 deletions

View File

@ -370,7 +370,7 @@
</td> </td>
<td> <td>
<a <a
routerLink="/explorer/{{ d.id }}" routerLink="/explorer?spid={{ d.id }}"
title="Edit document" title="Edit document"
i18n-title i18n-title
style="overflow-wrap: anywhere" style="overflow-wrap: anywhere"
@ -386,20 +386,6 @@
(click)="clickTag(t.id); $event.stopPropagation()" (click)="clickTag(t.id); $event.stopPropagation()"
></app-tag> ></app-tag>
</td> </td>
<td *ngIf="notesEnabled" class="d-none d-xl-table-cell">
<a
*ngIf="d.notes.length"
routerLink="/documents/{{ d.id }}/notes"
class="btn btn-sm p-0"
>
<span class="badge rounded-pill bg-light border text-primary">
<svg class="metadata-icon ms-1 me-1" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#chat-left-text" />
</svg>
{{ d.notes.length }}</span
>
</a>
</td>
<td class="d-none d-xl-table-cell"> <td class="d-none d-xl-table-cell">
<ng-container *ngIf="d.document_type"> <ng-container *ngIf="d.document_type">
<a <a

View File

@ -8,7 +8,16 @@ import {
} from '@angular/core' } from '@angular/core'
import { ActivatedRoute, Router, convertToParamMap } from '@angular/router' import { ActivatedRoute, Router, convertToParamMap } from '@angular/router'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject, filter, first, map, switchMap, takeUntil } from 'rxjs' import {
Subject,
filter,
first,
map,
switchMap,
take,
takeUntil,
tap,
} from 'rxjs'
import { import {
FilterRule, FilterRule,
filterRulesDiffer, filterRulesDiffer,
@ -34,6 +43,7 @@ import { StoragePathListViewService } from 'src/app/services/storage-path-list-v
import { ToastService } from 'src/app/services/toast.service' import { ToastService } from 'src/app/services/toast.service'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component' import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import { FilterEditorComponent } from './filter-editor/filter-editor.component' import { FilterEditorComponent } from './filter-editor/filter-editor.component'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
@Component({ @Component({
selector: 'app-explorer', selector: 'app-explorer',
@ -140,44 +150,11 @@ export class ExplorerComponent
this.list.reload() this.list.reload()
}) })
this.route.paramMap
.pipe(
filter((params) => params.has('id')), // only on saved view e.g. /view/id
switchMap((params) => {
return this.savedViewService
.getCached(+params.get('id'))
.pipe(map((view) => ({ view })))
})
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({ view }) => {
if (!view) {
this.router.navigate(['404'])
return
}
this.unmodifiedSavedView = view
this.list.activateSavedViewWithQueryParams(
view,
convertToParamMap(this.route.snapshot.queryParams)
)
// this.list.reload()
this.unmodifiedFilterRules = view.filter_rules
})
this.route.queryParamMap this.route.queryParamMap
.pipe( .pipe(takeUntil(this.unsubscribeNotifier))
filter(() => !this.route.snapshot.paramMap.has('id')), // only when not on /view/id
takeUntil(this.unsubscribeNotifier)
)
.subscribe((queryParams) => { .subscribe((queryParams) => {
if (queryParams.has('view')) {
// loading a saved view on /documents
this.loadViewConfig(parseInt(queryParams.get('view')))
} else {
this.list.activateSavedView(null)
this.list.loadFromQueryParams(queryParams) this.list.loadFromQueryParams(queryParams)
this.unmodifiedFilterRules = [] this.unmodifiedFilterRules = []
}
}) })
} }
@ -187,21 +164,12 @@ export class ExplorerComponent
this.unsubscribeNotifier.complete() this.unsubscribeNotifier.complete()
} }
loadViewConfig(viewID: number) { openDocumentDetail(storagePath: PaperlessStoragePath) {
this.savedViewService this.router.navigate(['explorer'], {
.getCached(viewID) queryParams: { spid: storagePath.id },
.pipe(first())
.subscribe((view) => {
this.unmodifiedSavedView = view
this.list.activateSavedView(view)
this.list.reload()
}) })
} }
openDocumentDetail(document: PaperlessDocument) {
this.router.navigate(['documents', document.id])
}
toggleSelected(document: PaperlessDocument, event: MouseEvent): void { toggleSelected(document: PaperlessDocument, event: MouseEvent): void {
if (!event.shiftKey) this.list.toggleSelected(document) if (!event.shiftKey) this.list.toggleSelected(document)
else this.list.selectRangeTo(document) else this.list.selectRangeTo(document)

View File

@ -1,10 +1,7 @@
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Observable, map } from 'rxjs' import { Observable, filter, map, switchMap, tap } from 'rxjs'
import { FilterRule } from 'src/app/data/filter-rule' import { FilterRule } from 'src/app/data/filter-rule'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata'
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path' import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { Results } from 'src/app/data/results' import { Results } from 'src/app/data/results'
import { queryParamsFromFilterRules } from 'src/app/utils/query-params' import { queryParamsFromFilterRules } from 'src/app/utils/query-params'
@ -26,8 +23,6 @@ interface SelectionData {
providedIn: 'root', providedIn: 'root',
}) })
export class CustomStoragePathService extends AbstractPaperlessService<PaperlessStoragePath> { export class CustomStoragePathService extends AbstractPaperlessService<PaperlessStoragePath> {
private _searchQuery: string
constructor(http: HttpClient) { constructor(http: HttpClient) {
super(http, 'storage_paths') super(http, 'storage_paths')
} }
@ -38,16 +33,40 @@ export class CustomStoragePathService extends AbstractPaperlessService<Paperless
sortField?: string, sortField?: string,
sortReverse?: boolean, sortReverse?: boolean,
filterRules?: FilterRule[], filterRules?: FilterRule[],
extraParams = {} extraParams = {},
parentStoragePathId?: number
): Observable<Results<PaperlessStoragePath>> { ): Observable<Results<PaperlessStoragePath>> {
return this.list( const params = Object.assign(
page, extraParams,
pageSize, queryParamsFromFilterRules(filterRules)
sortField, )
sortReverse, if (parentStoragePathId !== null && parentStoragePathId !== undefined) {
Object.assign(extraParams, queryParamsFromFilterRules(filterRules)) return this.get(parentStoragePathId).pipe(
).pipe( switchMap((storagePath) => {
params.path__istartswith = storagePath.path
return this.list(page, pageSize, sortField, sortReverse, params).pipe(
map((results) => { map((results) => {
results.results = results.results.filter((s) => {
const isNotParent = s.id !== parentStoragePathId
const isDirectChild =
s.path
.replace(storagePath.path, '')
.split('/')
.filter((s) => !!s).length === 1
return isNotParent && isDirectChild
})
return results
})
)
})
)
}
return this.list(page, pageSize, sortField, sortReverse, params).pipe(
map((results) => {
results.results = results.results.filter(
(s) => s.path.split('/').length === 1
)
return results return results
}) })
) )
@ -58,79 +77,4 @@ export class CustomStoragePathService extends AbstractPaperlessService<Paperless
fields: 'id', fields: 'id',
}).pipe(map((response) => response.results.map((doc) => doc.id))) }).pipe(map((response) => response.results.map((doc) => doc.id)))
} }
getPreviewUrl(id: number, original: boolean = false): string {
let url = this.getResourceUrl(id, 'preview')
if (this._searchQuery) url += `#search="${this._searchQuery}"`
if (original) {
url += '?original=true'
}
return url
}
getThumbUrl(id: number): string {
return this.getResourceUrl(id, 'thumb')
}
getDownloadUrl(id: number, original: boolean = false): string {
let url = this.getResourceUrl(id, 'download')
if (original) {
url += '?original=true'
}
return url
}
update(o: PaperlessDocument): Observable<PaperlessDocument> {
// we want to only set created_date
o.created = undefined
return super.update(o)
}
uploadDocument(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,
})
}
getSuggestions(id: number): Observable<PaperlessDocumentSuggestions> {
return this.http.get<PaperlessDocumentSuggestions>(
this.getResourceUrl(id, 'suggestions')
)
}
bulkDownload(
ids: number[],
content = 'both',
useFilenameFormatting: boolean = false
) {
return this.http.post(
this.getResourceUrl(null, 'bulk_download'),
{
documents: ids,
content: content,
follow_formatting: useFilenameFormatting,
},
{ responseType: 'blob' }
)
}
public set searchQuery(query: string) {
this._searchQuery = query
}
} }

View File

@ -57,6 +57,8 @@ export interface ListViewState {
* Contains the IDs of all selected documents. * Contains the IDs of all selected documents.
*/ */
selected?: Set<number> selected?: Set<number>
storagePathId?: number | null
} }
/** /**
@ -108,6 +110,7 @@ export class StoragePathListViewService {
sortReverse: true, sortReverse: true,
filterRules: [], filterRules: [],
selected: new Set<number>(), selected: new Set<number>(),
storagePathId: null,
} }
} }
@ -121,56 +124,22 @@ export class StoragePathListViewService {
return this.listViewStates.get(this._activeSavedViewId) return this.listViewStates.get(this._activeSavedViewId)
} }
activateSavedView(view: PaperlessSavedView) {
this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
if (view) {
this._activeSavedViewId = view.id
this.loadSavedView(view)
} else {
this._activeSavedViewId = null
}
}
activateSavedViewWithQueryParams(
view: PaperlessSavedView,
queryParams: ParamMap
) {
const viewState = paramsToViewState(queryParams)
this.activateSavedView(view)
this.currentPage = viewState.currentPage
}
loadSavedView(view: PaperlessSavedView, closeCurrentView: boolean = false) {
if (closeCurrentView) {
this._activeSavedViewId = null
}
this.activeListViewState.filterRules = cloneFilterRules(view.filter_rules)
this.activeListViewState.sortField = view.sort_field
this.activeListViewState.sortReverse = view.sort_reverse
if (this._activeSavedViewId) {
this.activeListViewState.title = view.name
}
this.reduceSelectionToFilter()
if (!this.router.routerState.snapshot.url.includes('/view/')) {
this.router.navigate(['view', view.id])
}
}
loadFromQueryParams(queryParams: ParamMap) { loadFromQueryParams(queryParams: ParamMap) {
const paramsEmpty: boolean = queryParams.keys.length == 0 const isParamsEmpty: boolean = queryParams.keys.length == 0
let newState: ListViewState = this.listViewStates.get( let newState: ListViewState & { storagePathId?: number } =
this._activeSavedViewId this.listViewStates.get(this._activeSavedViewId)
) if (!isParamsEmpty) {
if (!paramsEmpty) newState = paramsToViewState(queryParams) newState = paramsToViewState(queryParams)
if (queryParams.has('spid')) {
newState.storagePathId = parseInt(queryParams.get('spid'))
}
}
if (newState == undefined) newState = this.defaultListViewState() // if nothing in local storage if (newState == undefined) newState = this.defaultListViewState() // if nothing in local storage
// only reload if things have changed // only reload if things have changed
if ( if (
!this.initialized || !this.initialized ||
paramsEmpty || isParamsEmpty ||
this.activeListViewState.sortField !== newState.sortField || this.activeListViewState.sortField !== newState.sortField ||
this.activeListViewState.sortReverse !== newState.sortReverse || this.activeListViewState.sortReverse !== newState.sortReverse ||
this.activeListViewState.currentPage !== newState.currentPage || this.activeListViewState.currentPage !== newState.currentPage ||
@ -183,7 +152,8 @@ export class StoragePathListViewService {
this.activeListViewState.sortField = newState.sortField this.activeListViewState.sortField = newState.sortField
this.activeListViewState.sortReverse = newState.sortReverse this.activeListViewState.sortReverse = newState.sortReverse
this.activeListViewState.currentPage = newState.currentPage this.activeListViewState.currentPage = newState.currentPage
this.reload(null, paramsEmpty) // update the params if there arent any this.activeListViewState.storagePathId = newState.storagePathId
this.reload(null, isParamsEmpty) // update the params if there arent any
} }
} }
@ -198,11 +168,12 @@ export class StoragePathListViewService {
activeListViewState.sortField, activeListViewState.sortField,
activeListViewState.sortReverse, activeListViewState.sortReverse,
activeListViewState.filterRules, activeListViewState.filterRules,
{ truncate_content: true } { truncate_content: true },
activeListViewState.storagePathId
) )
.subscribe({ .subscribe({
next: (result) => { next: (result) => {
console.log('list filtered result:', result) console.log('result:', result)
this.initialized = true this.initialized = true
this.isReloading = false this.isReloading = false
activeListViewState.collectionSize = result.count activeListViewState.collectionSize = result.count