From 427338911ae597f2f79701b079d89594b755cc16 Mon Sep 17 00:00:00 2001
From: mister-ideas <36502095+mister-ideas@users.noreply.github.com>
Date: Mon, 19 Oct 2026 03:34:06 +0200
Subject: [PATCH 1/3] Feature/add sorting to holdings table of home overview
---
CHANGELOG.md | 1 +
.../ui/src/lib/holdings-table/holdings-table.component.html | 6 +-----
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e35621f0b..a4989f0cd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
+- Improved the holdings table of the home overview by adding the sorting by name and performance
- Improved the usability of the activities page by eliminating the reload on canceling the import activities dialog
- Improved the accessibility of the _Buy me a coffee_ button on the about page
- Improved the accessibility of the entity logo component
diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.html b/libs/ui/src/lib/holdings-table/holdings-table.component.html
index 025be8a723..5d586d8911 100644
--- a/libs/ui/src/lib/holdings-table/holdings-table.component.html
+++ b/libs/ui/src/lib/holdings-table/holdings-table.component.html
@@ -210,11 +210,7 @@
-
+
Date: Mon, 19 Oct 2026 13:27:35 +0200
Subject: [PATCH 2/3] Feature/sort the holdings table by performance in
descending order by default
---
CHANGELOG.md | 1 +
.../lib/holdings-table/holdings-table.component.html | 4 ++--
.../lib/holdings-table/holdings-table.component.ts | 11 ++---------
3 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4989f0cd1..01202969ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Improved the holdings table of the home overview by adding the sorting by name and performance
+- Changed the default sorting of the holdings table to the performance in descending order
- Improved the usability of the activities page by eliminating the reload on canceling the import activities dialog
- Improved the accessibility of the _Buy me a coffee_ button on the about page
- Improved the accessibility of the entity logo component
diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.html b/libs/ui/src/lib/holdings-table/holdings-table.component.html
index 5d586d8911..efd6f38763 100644
--- a/libs/ui/src/lib/holdings-table/holdings-table.component.html
+++ b/libs/ui/src/lib/holdings-table/holdings-table.component.html
@@ -5,8 +5,8 @@
matSort
[class.simple]="mode() === 'simple'"
[dataSource]="dataSource"
- [matSortActive]="sortActive()"
- [matSortDirection]="sortDirection()"
+ [matSortActive]="sortActive"
+ [matSortDirection]="sortDirection"
>
|
diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts
index 3181cc1347..70ce2cb1e9 100644
--- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts
+++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts
@@ -101,15 +101,8 @@ export class GfHoldingsTableComponent {
return !this.holdings();
});
- protected readonly sortActive = computed(() => {
- return this.mode() === 'default'
- ? 'allocationInPercentage'
- : 'assetProfile.name';
- });
-
- protected readonly sortDirection = computed(() => {
- return this.mode() === 'default' ? 'desc' : 'asc';
- });
+ protected readonly sortActive = 'netPerformancePercentWithCurrencyEffect';
+ protected readonly sortDirection: SortDirection = 'desc';
public constructor() {
this.dataSource.sortingDataAccessor = (holding, path) => {
From 223b65e0eb60d4c6f8d37684e1602c5a62721994 Mon Sep 17 00:00:00 2001
From: mister-ideas <36502095+mister-ideas@users.noreply.github.com>
Date: Mon, 19 Oct 2026 13:48:12 +0200
Subject: [PATCH 3/3] Feature/persist the sorting of the holdings and accounts
tables on the device
---
CHANGELOG.md | 1 +
.../accounts-table.component.html | 1 +
.../accounts-table.component.ts | 2 +
.../holdings-table.component.html | 1 +
.../holdings-table.component.ts | 2 +
libs/ui/src/lib/persist-sort/index.ts | 1 +
.../persist-sort.directive.spec.ts | 70 +++++++++++++++++++
.../persist-sort/persist-sort.directive.ts | 57 +++++++++++++++
8 files changed, 135 insertions(+)
create mode 100644 libs/ui/src/lib/persist-sort/index.ts
create mode 100644 libs/ui/src/lib/persist-sort/persist-sort.directive.spec.ts
create mode 100644 libs/ui/src/lib/persist-sort/persist-sort.directive.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01202969ae..c4e75fe19e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the holdings table of the home overview by adding the sorting by name and performance
- Changed the default sorting of the holdings table to the performance in descending order
+- Persisted the sorting of the holdings and accounts tables on the device
- Improved the usability of the activities page by eliminating the reload on canceling the import activities dialog
- Improved the accessibility of the _Buy me a coffee_ button on the about page
- Improved the accessibility of the entity logo component
diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.html b/libs/ui/src/lib/accounts-table/accounts-table.component.html
index 43a6a429b4..00b544bf4e 100644
--- a/libs/ui/src/lib/accounts-table/accounts-table.component.html
+++ b/libs/ui/src/lib/accounts-table/accounts-table.component.html
@@ -20,6 +20,7 @@
matSortActive="name"
matSortDirection="asc"
[dataSource]="dataSource"
+ [gfPersistSort]="'accounts'"
>
diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts
index 70ce2cb1e9..d6131c1f07 100644
--- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts
+++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts
@@ -27,6 +27,7 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component';
+import { GfPersistSortDirective } from '../persist-sort/persist-sort.directive';
import { GfValueComponent } from '../value/value.component';
import { HoldingsTableItem } from './interfaces/interfaces';
@@ -34,6 +35,7 @@ import { HoldingsTableItem } from './interfaces/interfaces';
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
GfEntityLogoComponent,
+ GfPersistSortDirective,
GfValueComponent,
MatButtonModule,
MatDialogModule,
diff --git a/libs/ui/src/lib/persist-sort/index.ts b/libs/ui/src/lib/persist-sort/index.ts
new file mode 100644
index 0000000000..22d5db3391
--- /dev/null
+++ b/libs/ui/src/lib/persist-sort/index.ts
@@ -0,0 +1 @@
+export * from './persist-sort.directive';
diff --git a/libs/ui/src/lib/persist-sort/persist-sort.directive.spec.ts b/libs/ui/src/lib/persist-sort/persist-sort.directive.spec.ts
new file mode 100644
index 0000000000..7fa7a7f811
--- /dev/null
+++ b/libs/ui/src/lib/persist-sort/persist-sort.directive.spec.ts
@@ -0,0 +1,70 @@
+import { Component } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MatSort, MatSortModule } from '@angular/material/sort';
+
+import { GfPersistSortDirective } from './persist-sort.directive';
+
+@Component({
+ imports: [GfPersistSortDirective, MatSortModule],
+ template: ``
+})
+class TestHostComponent {}
+
+describe('GfPersistSortDirective', () => {
+ let fixture: ComponentFixture;
+
+ const create = () => {
+ fixture = TestBed.createComponent(TestHostComponent);
+ fixture.detectChanges();
+
+ return fixture.debugElement.children[0].injector.get(MatSort);
+ };
+
+ beforeEach(() => {
+ localStorage.clear();
+ TestBed.configureTestingModule({ imports: [TestHostComponent] });
+ });
+
+ it('keeps the default sorting without a stored value', () => {
+ const sort = create();
+
+ expect(sort.active).toBe('name');
+ expect(sort.direction).toBe('asc');
+ });
+
+ it('stores the sorting on change', () => {
+ const sort = create();
+
+ sort.sort({ id: 'value', start: 'desc', disableClear: false });
+
+ expect(JSON.parse(localStorage.getItem('gf-sort-test') ?? '{}')).toEqual({
+ active: 'value',
+ direction: 'desc'
+ });
+ });
+
+ it('restores a stored sorting', () => {
+ localStorage.setItem(
+ 'gf-sort-test',
+ JSON.stringify({ active: 'value', direction: 'desc' })
+ );
+
+ const sort = create();
+
+ expect(sort.active).toBe('value');
+ expect(sort.direction).toBe('desc');
+ });
+
+ it('ignores an invalid stored sorting', () => {
+ localStorage.setItem('gf-sort-test', '{"active":"value","direction":"x"}');
+
+ const sort = create();
+
+ expect(sort.active).toBe('name');
+ });
+});
diff --git a/libs/ui/src/lib/persist-sort/persist-sort.directive.ts b/libs/ui/src/lib/persist-sort/persist-sort.directive.ts
new file mode 100644
index 0000000000..d7e70f81ae
--- /dev/null
+++ b/libs/ui/src/lib/persist-sort/persist-sort.directive.ts
@@ -0,0 +1,57 @@
+import { DestroyRef, Directive, inject, input, OnInit } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { MatSort, SortDirection } from '@angular/material/sort';
+
+const STORAGE_KEY_PREFIX = 'gf-sort-';
+
+/**
+ * Remembers the sorting of a `matSort` table on the device and restores it
+ * on the next visit.
+ */
+@Directive({
+ selector: '[gfPersistSort]'
+})
+export class GfPersistSortDirective implements OnInit {
+ public readonly gfPersistSort = input.required();
+
+ private readonly destroyRef = inject(DestroyRef);
+ private readonly matSort = inject(MatSort, { self: true });
+
+ public ngOnInit() {
+ const storageKey = `${STORAGE_KEY_PREFIX}${this.gfPersistSort()}`;
+ const storedSort = this.readStoredSort(storageKey);
+
+ if (storedSort) {
+ this.matSort.active = storedSort.active;
+ this.matSort.direction = storedSort.direction;
+ }
+
+ this.matSort.sortChange
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe(({ active, direction }) => {
+ try {
+ globalThis.localStorage?.setItem(
+ storageKey,
+ JSON.stringify({ active, direction })
+ );
+ } catch {}
+ });
+ }
+
+ private readStoredSort(aStorageKey: string) {
+ try {
+ const item = globalThis.localStorage?.getItem(aStorageKey);
+ const { active, direction } = JSON.parse(item ?? 'null') ?? {};
+
+ if (
+ typeof active === 'string' &&
+ active.length > 0 &&
+ (direction === 'asc' || direction === 'desc')
+ ) {
+ return { active, direction: direction as SortDirection };
+ }
+ } catch {}
+
+ return undefined;
+ }
+}
|