Compare commits

..

3 Commits

Author SHA1 Message Date
dependabot[bot]
95736eebc4 docker(deps): Bump astral-sh/uv (#11283)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.9.4-python3.12-bookworm-slim to 0.9.7-python3.12-bookworm-slim.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.9.4...0.9.7)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.9.7-python3.12-bookworm-slim
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 20:12:56 -08:00
shamoon
85027dbffd Fix: ensure custom field query propagation, change detection (#11291) 2025-11-04 12:40:05 -08:00
github-actions[bot]
74f72e417d Documentation: Add v2.19.4 changelog (#11285)
* Changelog v2.19.4 - GHA

* Update changelog for paperless-ngx 2.19.4

Reorganize changelog to include performance enhancements.

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-11-03 17:39:28 -08:00
6 changed files with 123 additions and 16 deletions

View File

@@ -32,7 +32,7 @@ RUN set -eux \
# Purpose: Installs s6-overlay and rootfs
# Comments:
# - Don't leave anything extra in here either
FROM ghcr.io/astral-sh/uv:0.9.4-python3.12-bookworm-slim AS s6-overlay-base
FROM ghcr.io/astral-sh/uv:0.9.7-python3.12-bookworm-slim AS s6-overlay-base
WORKDIR /usr/src/s6

View File

@@ -1,5 +1,41 @@
# Changelog
## paperless-ngx 2.19.4
### Bug Fixes
- Fix: use original_file when attaching docs to workflow emails with added trigger [@shamoon](https://github.com/shamoon) ([#11266](https://github.com/paperless-ngx/paperless-ngx/pull/11266))
- Fix: mark 'Select' button in doc list for translation [@shamoon](https://github.com/shamoon) ([#11278](https://github.com/paperless-ngx/paperless-ngx/pull/11278))
- Fix: respect fields parameter for created field [@shamoon](https://github.com/shamoon) ([#11251](https://github.com/paperless-ngx/paperless-ngx/pull/11251))
- Fix: improve legibility of processed mail error popover in light mode [@shamoon](https://github.com/shamoon) ([#11258](https://github.com/paperless-ngx/paperless-ngx/pull/11258))
- Fixhancement: truncate large logs, improve auto-scroll [@shamoon](https://github.com/shamoon) ([#11239](https://github.com/paperless-ngx/paperless-ngx/pull/11239))
- Chore: add max-height and overflow to processedmail error popover [@shamoon](https://github.com/shamoon) ([#11252](https://github.com/paperless-ngx/paperless-ngx/pull/11252))
- Fix: delay iframe DOM removal, handle onafterprint error for print in FF [@shamoon](https://github.com/shamoon) ([#11237](https://github.com/paperless-ngx/paperless-ngx/pull/11237))
- Fix: de-deduplicate children in tag list when filtering [@shamoon](https://github.com/shamoon) ([#11229](https://github.com/paperless-ngx/paperless-ngx/pull/11229))
### Performance
- Performance: re-enable virtual scroll, bump ng-select [@shamoon](https://github.com/shamoon) ([#11279](https://github.com/paperless-ngx/paperless-ngx/pull/11279))
- Performance: use virtual scroll container and log level parsing for logs view [@MickLesk](https://github.com/MickLesk) ([#11233](https://github.com/paperless-ngx/paperless-ngx/pull/11233))
### All App Changes
<details>
<summary>11 changes</summary>
- Performance: re-enable virtual scroll, bump ng-select [@shamoon](https://github.com/shamoon) ([#11279](https://github.com/paperless-ngx/paperless-ngx/pull/11279))
- Fix: use original_file when attaching docs to workflow emails with added trigger [@shamoon](https://github.com/shamoon) ([#11266](https://github.com/paperless-ngx/paperless-ngx/pull/11266))
- Fix: mark 'Select' button in doc list for translation [@shamoon](https://github.com/shamoon) ([#11278](https://github.com/paperless-ngx/paperless-ngx/pull/11278))
- Fix: respect fields parameter for created field [@shamoon](https://github.com/shamoon) ([#11251](https://github.com/paperless-ngx/paperless-ngx/pull/11251))
- Fix: improve legibility of processed mail error popover in light mode [@shamoon](https://github.com/shamoon) ([#11258](https://github.com/paperless-ngx/paperless-ngx/pull/11258))
- Fixhancement: truncate large logs, improve auto-scroll [@shamoon](https://github.com/shamoon) ([#11239](https://github.com/paperless-ngx/paperless-ngx/pull/11239))
- Chore: add max-height and overflow to processedmail error popover [@shamoon](https://github.com/shamoon) ([#11252](https://github.com/paperless-ngx/paperless-ngx/pull/11252))
- Fix: delay iframe DOM removal, handle onafterprint error for print in FF [@shamoon](https://github.com/shamoon) ([#11237](https://github.com/paperless-ngx/paperless-ngx/pull/11237))
- Performance: use virtual scroll container and log level parsing for logs view [@MickLesk](https://github.com/MickLesk) ([#11233](https://github.com/paperless-ngx/paperless-ngx/pull/11233))
- Chore: cache Github version check for 15 minutes [@shamoon](https://github.com/shamoon) ([#11235](https://github.com/paperless-ngx/paperless-ngx/pull/11235))
- Fix: de-deduplicate children in tag list when filtering [@shamoon](https://github.com/shamoon) ([#11229](https://github.com/paperless-ngx/paperless-ngx/pull/11229))
</details>
## paperless-ngx 2.19.3
### Bug Fixes

View File

@@ -354,5 +354,13 @@ describe('CustomFieldsQueryDropdownComponent', () => {
model.removeElement(atom)
expect(completeSpy).toHaveBeenCalled()
})
it('should subscribe to existing elements when queries are assigned', () => {
const expression = new CustomFieldQueryExpression()
const nextSpy = jest.spyOn(model.changed, 'next')
model.queries = [expression]
expression.changed.next(expression)
expect(nextSpy).toHaveBeenCalledWith(model)
})
})
})

View File

@@ -17,7 +17,7 @@ import {
} from '@ng-bootstrap/ng-bootstrap'
import { NgSelectComponent, NgSelectModule } from '@ng-select/ng-select'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { first, Subject, takeUntil } from 'rxjs'
import { first, Subject, Subscription, takeUntil } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import {
CUSTOM_FIELD_QUERY_MAX_ATOMS,
@@ -41,10 +41,27 @@ import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.comp
import { DocumentLinkComponent } from '../input/document-link/document-link.component'
export class CustomFieldQueriesModel {
public queries: CustomFieldQueryElement[] = []
private _queries: CustomFieldQueryElement[] = []
private rootSubscriptions: Subscription[] = []
public readonly changed = new Subject<CustomFieldQueriesModel>()
public get queries(): CustomFieldQueryElement[] {
return this._queries
}
public set queries(value: CustomFieldQueryElement[]) {
this.teardownRootSubscriptions()
this._queries = value ?? []
for (const element of this._queries) {
this.rootSubscriptions.push(
element.changed.subscribe(() => {
this.changed.next(this)
})
)
}
}
public clear(fireEvent = true) {
this.queries = []
if (fireEvent) {
@@ -107,14 +124,14 @@ export class CustomFieldQueriesModel {
public addExpression(
expression: CustomFieldQueryExpression = new CustomFieldQueryExpression()
) {
if (this.queries.length > 0) {
;(
(this.queries[0] as CustomFieldQueryExpression)
.value as CustomFieldQueryElement[]
).push(expression)
} else {
this.queries.push(expression)
if (this.queries.length === 0) {
this.queries = [expression]
return
}
;(
(this.queries[0] as CustomFieldQueryExpression)
.value as CustomFieldQueryElement[]
).push(expression)
expression.changed.subscribe(() => {
this.changed.next(this)
})
@@ -166,6 +183,13 @@ export class CustomFieldQueriesModel {
this.changed.next(this)
}
}
private teardownRootSubscriptions() {
for (const subscription of this.rootSubscriptions) {
subscription.unsubscribe()
}
this.rootSubscriptions = []
}
}
@Component({

View File

@@ -1,4 +1,3 @@
import { fakeAsync, tick } from '@angular/core/testing'
import {
CustomFieldQueryElementType,
CustomFieldQueryLogicalOperator,
@@ -111,13 +110,38 @@ describe('CustomFieldQueryAtom', () => {
expect(atom.serialize()).toEqual([1, 'operator', 'value'])
})
it('should emit changed on value change after debounce', fakeAsync(() => {
it('should emit changed on value change immediately', () => {
const atom = new CustomFieldQueryAtom()
const changeSpy = jest.spyOn(atom.changed, 'next')
atom.value = 'new value'
tick(1000)
expect(changeSpy).toHaveBeenCalled()
}))
})
it('should ignore duplicate array emissions', () => {
const atom = new CustomFieldQueryAtom()
atom.operator = CustomFieldQueryOperator.In
const changeSpy = jest.fn()
atom.changed.subscribe(changeSpy)
atom.value = [1, 2]
expect(changeSpy).toHaveBeenCalledTimes(1)
changeSpy.mockClear()
atom.value = [1, 2]
expect(changeSpy).not.toHaveBeenCalled()
})
it('should emit when array values differ while length matches', () => {
const atom = new CustomFieldQueryAtom()
atom.operator = CustomFieldQueryOperator.In
const changeSpy = jest.fn()
atom.changed.subscribe(changeSpy)
atom.value = [1, 2]
changeSpy.mockClear()
atom.value = [1, 3]
expect(changeSpy).toHaveBeenCalledTimes(1)
})
})
describe('CustomFieldQueryExpression', () => {

View File

@@ -1,4 +1,4 @@
import { Subject, debounceTime, distinctUntilChanged } from 'rxjs'
import { Subject, distinctUntilChanged } from 'rxjs'
import { v4 as uuidv4 } from 'uuid'
import {
CUSTOM_FIELD_QUERY_VALUE_TYPES_BY_OPERATOR,
@@ -110,7 +110,22 @@ export class CustomFieldQueryAtom extends CustomFieldQueryElement {
protected override connectValueModelChanged(): void {
this.valueModelChanged
.pipe(debounceTime(1000), distinctUntilChanged())
.pipe(
distinctUntilChanged((previous, current) => {
if (Array.isArray(previous) && Array.isArray(current)) {
if (previous.length !== current.length) {
return false
}
for (let i = 0; i < previous.length; i++) {
if (previous[i] !== current[i]) {
return false
}
}
return true
}
return previous === current
})
)
.subscribe(() => {
this.changed.next(this)
})