mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat: add core storage and scroll position libraries with initial implementations and configurations
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
} from './guards/activate-process-id.guard';
|
||||
import { MatomoRouteData } from 'ngx-matomo-client';
|
||||
import { processResolverFn } from '@isa/core/process';
|
||||
import { provideScrollPositionRestoration } from '@isa/core/scroll-position';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -176,5 +177,6 @@ if (isDevMode()) {
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes), TokenLoginModule],
|
||||
exports: [RouterModule],
|
||||
providers: [provideScrollPositionRestoration()],
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
||||
7
libs/core/scroll-position/README.md
Normal file
7
libs/core/scroll-position/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# core-scroll-position
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test core-scroll-position` to execute the unit tests.
|
||||
34
libs/core/scroll-position/eslint.config.mjs
Normal file
34
libs/core/scroll-position/eslint.config.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
import nx from '@nx/eslint-plugin';
|
||||
import baseConfig from '../../../eslint.config.mjs';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
21
libs/core/scroll-position/jest.config.ts
Normal file
21
libs/core/scroll-position/jest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
displayName: 'core-scroll-position',
|
||||
preset: '../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../coverage/libs/core/scroll-position',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
20
libs/core/scroll-position/project.json
Normal file
20
libs/core/scroll-position/project.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "core-scroll-position",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/core/scroll-position/src",
|
||||
"prefix": "lib",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/core/scroll-position/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
libs/core/scroll-position/src/index.ts
Normal file
1
libs/core/scroll-position/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './lib/scroll-position-restoration';
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ViewportScroller } from '@angular/common';
|
||||
import {
|
||||
afterNextRender,
|
||||
EnvironmentProviders,
|
||||
inject,
|
||||
Injector,
|
||||
provideEnvironmentInitializer,
|
||||
} from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute, NavigationStart, Router } from '@angular/router';
|
||||
import { SessionStorageProvider } from '@isa/core/storage';
|
||||
|
||||
// const route: Route = {
|
||||
// component: AnyComponent,
|
||||
// data: {
|
||||
// scrollPositionRestoration: true
|
||||
// }
|
||||
// }
|
||||
|
||||
const getDeepestActivatedRoute = (route: ActivatedRoute): ActivatedRoute => {
|
||||
while (route.firstChild) {
|
||||
route = route.firstChild;
|
||||
}
|
||||
return route;
|
||||
};
|
||||
|
||||
export function provideScrollPositionRestoration(): EnvironmentProviders {
|
||||
return provideEnvironmentInitializer(() => {
|
||||
const router = inject(Router);
|
||||
const viewportScroller = inject(ViewportScroller);
|
||||
const sessionStorage = inject(SessionStorageProvider);
|
||||
|
||||
router.events.pipe(takeUntilDestroyed()).subscribe((event) => {
|
||||
if (event instanceof NavigationStart) {
|
||||
const url = router.url;
|
||||
const route = getDeepestActivatedRoute(router.routerState.root);
|
||||
|
||||
if (route.snapshot.data?.['scrollPositionRestoration']) {
|
||||
sessionStorage.set(url, viewportScroller.getScrollPosition());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function restoreScrollPosition() {
|
||||
const injector = inject(Injector);
|
||||
const router = inject(Router);
|
||||
const viewportScroller = inject(ViewportScroller);
|
||||
const sessionStorage = inject(SessionStorageProvider);
|
||||
|
||||
const url = router.url;
|
||||
const position = await sessionStorage.get(url);
|
||||
|
||||
if (position) {
|
||||
afterNextRender(
|
||||
() => {
|
||||
viewportScroller.scrollToPosition(position as [number, number]);
|
||||
},
|
||||
{ injector },
|
||||
);
|
||||
}
|
||||
}
|
||||
6
libs/core/scroll-position/src/test-setup.ts
Normal file
6
libs/core/scroll-position/src/test-setup.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||
|
||||
setupZoneTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
});
|
||||
28
libs/core/scroll-position/tsconfig.json
Normal file
28
libs/core/scroll-position/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
17
libs/core/scroll-position/tsconfig.lib.json
Normal file
17
libs/core/scroll-position/tsconfig.lib.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/test-setup.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
16
libs/core/scroll-position/tsconfig.spec.json
Normal file
16
libs/core/scroll-position/tsconfig.spec.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"target": "es2016",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
7
libs/core/storage/README.md
Normal file
7
libs/core/storage/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# core-storage
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test core-storage` to execute the unit tests.
|
||||
34
libs/core/storage/eslint.config.mjs
Normal file
34
libs/core/storage/eslint.config.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
import nx from '@nx/eslint-plugin';
|
||||
import baseConfig from '../../../eslint.config.mjs';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
21
libs/core/storage/jest.config.ts
Normal file
21
libs/core/storage/jest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
displayName: 'core-storage',
|
||||
preset: '../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../coverage/libs/core/storage',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
20
libs/core/storage/project.json
Normal file
20
libs/core/storage/project.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "core-storage",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/core/storage/src",
|
||||
"prefix": "lib",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/core/storage/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
libs/core/storage/src/index.ts
Normal file
5
libs/core/storage/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './lib/idb.storage-provider';
|
||||
export * from './lib/local.storage-provider';
|
||||
export * from './lib/session.storage-provider';
|
||||
export * from './lib/storage-provider';
|
||||
export * from './lib/storage';
|
||||
4
libs/core/storage/src/lib/hash.utils.ts
Normal file
4
libs/core/storage/src/lib/hash.utils.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export function hash(obj: object): string {
|
||||
// TODO: Implement hash function
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
61
libs/core/storage/src/lib/idb.storage-provider.ts
Normal file
61
libs/core/storage/src/lib/idb.storage-provider.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { StorageProvider } from './storage-provider';
|
||||
|
||||
const DB_NAME = 'storage';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IDBStorageProvider implements StorageProvider {
|
||||
private db!: IDBDatabase;
|
||||
|
||||
private async openDB(): Promise<IDBDatabase> {
|
||||
if (this.db) {
|
||||
return this.db; // Datenbank bereits geöffnet, bestehende Verbindung zurückgeben
|
||||
}
|
||||
|
||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const request = indexedDB.open('isa-cache', 1);
|
||||
|
||||
request.onerror = (event) => {
|
||||
reject(event);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
this.db = (event.target as IDBOpenDBRequest).result;
|
||||
|
||||
if (!this.db.objectStoreNames.contains(DB_NAME)) {
|
||||
this.db.createObjectStore(DB_NAME, { keyPath: 'key' });
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
this.db = (event.target as IDBOpenDBRequest).result;
|
||||
resolve(this.db);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async getObjectStore(mode: IDBTransactionMode = 'readonly'): Promise<IDBObjectStore> {
|
||||
const db = await this.openDB();
|
||||
const transaction = db.transaction(DB_NAME, mode);
|
||||
return transaction.objectStore(DB_NAME);
|
||||
}
|
||||
|
||||
async set(key: string, value: unknown): Promise<void> {
|
||||
const store = await this.getObjectStore('readwrite');
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const request = store.put({ key, value });
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = (event) => reject(event);
|
||||
});
|
||||
}
|
||||
|
||||
async get(key: string): Promise<unknown> {
|
||||
const store = await this.getObjectStore();
|
||||
|
||||
return new Promise<unknown>((resolve, reject) => {
|
||||
const request = store.get(key);
|
||||
request.onsuccess = () => resolve(request.result?.value);
|
||||
request.onerror = (event) => reject(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
17
libs/core/storage/src/lib/local.storage-provider.ts
Normal file
17
libs/core/storage/src/lib/local.storage-provider.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { StorageProvider } from './storage-provider';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class LocalStorageProvider implements StorageProvider {
|
||||
async set(key: string, value: unknown): Promise<void> {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
async get(key: string): Promise<unknown> {
|
||||
const data = localStorage.getItem(key);
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
16
libs/core/storage/src/lib/session.storage-provider.ts
Normal file
16
libs/core/storage/src/lib/session.storage-provider.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { StorageProvider } from './storage-provider';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SessionStorageProvider implements StorageProvider {
|
||||
async set(key: string, value: unknown): Promise<void> {
|
||||
sessionStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
async get(key: string): Promise<unknown> {
|
||||
const data = sessionStorage.getItem(key);
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
5
libs/core/storage/src/lib/storage-provider.ts
Normal file
5
libs/core/storage/src/lib/storage-provider.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface StorageProvider {
|
||||
set(key: string, value: unknown): Promise<void>;
|
||||
|
||||
get(key: string): Promise<unknown>;
|
||||
}
|
||||
49
libs/core/storage/src/lib/storage.ts
Normal file
49
libs/core/storage/src/lib/storage.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { inject, Type } from '@angular/core';
|
||||
import { StorageProvider } from './storage-provider';
|
||||
import { z } from 'zod';
|
||||
import { hash } from './hash.utils';
|
||||
|
||||
export class Storage {
|
||||
constructor(private storageProvider: StorageProvider) {}
|
||||
|
||||
set<T>(token: string | object, value: T): Promise<void> {
|
||||
let key: string;
|
||||
|
||||
if (typeof token === 'string') {
|
||||
key = token;
|
||||
} else {
|
||||
key = hash(token);
|
||||
}
|
||||
|
||||
return this.storageProvider.set(key, value);
|
||||
}
|
||||
|
||||
async get<T>(token: string | object, schema?: z.ZodType<T>): Promise<T | undefined> {
|
||||
let key: string;
|
||||
|
||||
if (typeof token === 'string') {
|
||||
key = token;
|
||||
} else {
|
||||
key = hash(token);
|
||||
}
|
||||
|
||||
const data = await this.storageProvider.get(key);
|
||||
if (schema) {
|
||||
return await schema.parse(data);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const storageMap = new WeakMap<Type<StorageProvider>, Storage>();
|
||||
|
||||
export function storage(storageProvider: Type<StorageProvider>): Storage {
|
||||
if (storageMap.has(storageProvider)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return storageMap.get(storageProvider)!;
|
||||
}
|
||||
|
||||
const storage = new Storage(inject(storageProvider));
|
||||
storageMap.set(storageProvider, storage);
|
||||
return storage;
|
||||
}
|
||||
28
libs/core/storage/src/lib/user.storage-provider.ts
Normal file
28
libs/core/storage/src/lib/user.storage-provider.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { StorageProvider } from './storage-provider';
|
||||
import { UserStateService } from '@generated/swagger/isa-api';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UserStorageProvider implements StorageProvider {
|
||||
#userStateService = inject(UserStateService);
|
||||
|
||||
async set(key: string, value: unknown): Promise<void> {
|
||||
const current = (await this.get(key)) || {};
|
||||
firstValueFrom(
|
||||
this.#userStateService.UserStateSetUserState({
|
||||
content: JSON.stringify({ ...current, [key]: value }),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async get(key: string): Promise<unknown> {
|
||||
const res = await firstValueFrom(this.#userStateService.UserStateGetUserState());
|
||||
|
||||
if (res.result?.content) {
|
||||
return JSON.parse(res.result.content)[key];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
6
libs/core/storage/src/test-setup.ts
Normal file
6
libs/core/storage/src/test-setup.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||
|
||||
setupZoneTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
});
|
||||
28
libs/core/storage/tsconfig.json
Normal file
28
libs/core/storage/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
17
libs/core/storage/tsconfig.lib.json
Normal file
17
libs/core/storage/tsconfig.lib.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/test-setup.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
16
libs/core/storage/tsconfig.spec.json
Normal file
16
libs/core/storage/tsconfig.spec.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"target": "es2016",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
afterNextRender,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
@@ -23,10 +22,8 @@ import {
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { FilterService, SearchBarInputComponent } from '@isa/shared/filter';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { ViewportScroller } from '@angular/common';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { debounceTime, fromEvent, map } from 'rxjs';
|
||||
import { EmptyStateComponent } from '@isa/ui/empty-state';
|
||||
import { restoreScrollPosition } from '@isa/core/scroll-position';
|
||||
|
||||
type EmptyState = {
|
||||
title: string;
|
||||
@@ -50,7 +47,6 @@ type EmptyState = {
|
||||
],
|
||||
})
|
||||
export class ResultsPageComponent {
|
||||
#viewportScroller = inject(ViewportScroller);
|
||||
#route = inject(ActivatedRoute);
|
||||
#router = inject(Router);
|
||||
|
||||
@@ -87,16 +83,7 @@ export class ResultsPageComponent {
|
||||
};
|
||||
});
|
||||
|
||||
listElements =
|
||||
viewChildren<QueryList<ReturnResultsItemListComponent>>('listElement');
|
||||
|
||||
scrollPosY = toSignal(
|
||||
fromEvent(window, 'scroll').pipe(
|
||||
debounceTime(100),
|
||||
map(() => this.#viewportScroller.getScrollPosition()[1]),
|
||||
),
|
||||
{ initialValue: this.#viewportScroller.getScrollPosition()[1] },
|
||||
);
|
||||
listElements = viewChildren<QueryList<ReturnResultsItemListComponent>>('listElement');
|
||||
|
||||
searchEffectFn = () =>
|
||||
effect(() => {
|
||||
@@ -107,8 +94,7 @@ export class ResultsPageComponent {
|
||||
if (processId) {
|
||||
const entity = this._entity();
|
||||
if (entity) {
|
||||
const isPending =
|
||||
this.entityStatus() === ReturnSearchStatus.Pending;
|
||||
const isPending = this.entityStatus() === ReturnSearchStatus.Pending;
|
||||
// Trigger reload search if no search request is already pending and
|
||||
// 1. List scrolled to bottom
|
||||
// 2. After Process change AND no items in entity
|
||||
@@ -123,31 +109,9 @@ export class ResultsPageComponent {
|
||||
});
|
||||
});
|
||||
|
||||
saveScrollPosEffectFn = () =>
|
||||
effect(() => {
|
||||
const scrollPos = this.scrollPosY();
|
||||
untracked(() => {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
console.log('scrollPos', scrollPos);
|
||||
this._returnSearchStore.setScrollPos(processId, scrollPos);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
applyScrollPosFn = () =>
|
||||
afterNextRender(() => {
|
||||
const entity = this._entity();
|
||||
console.log('after next render scroll pos');
|
||||
if (entity && entity.scrollPos) {
|
||||
this.#viewportScroller.scrollToPosition([0, entity.scrollPos]);
|
||||
}
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this.applyScrollPosFn();
|
||||
this.searchEffectFn();
|
||||
this.saveScrollPosEffectFn();
|
||||
restoreScrollPosition();
|
||||
}
|
||||
|
||||
async onSearch() {
|
||||
|
||||
@@ -12,8 +12,15 @@ export const routes: Routes = [
|
||||
resolve: { querySettings: querySettingsResolverFn },
|
||||
children: [
|
||||
{ path: '', component: MainPageComponent },
|
||||
{ path: 'results', component: ResultsPageComponent },
|
||||
{ path: 'receipt/:receiptId', component: DetailsPageComponent },
|
||||
{
|
||||
path: 'results',
|
||||
component: ResultsPageComponent,
|
||||
data: { scrollPositionRestoration: true },
|
||||
},
|
||||
{
|
||||
path: 'receipt/:receiptId',
|
||||
component: DetailsPageComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
"@isa/core/config": ["libs/core/config/src/index.ts"],
|
||||
"@isa/core/process": ["libs/core/process/src/index.ts"],
|
||||
"@isa/core/scanner": ["libs/core/scanner/src/index.ts"],
|
||||
"@isa/core/scroll-position": ["libs/core/scroll-position/src/index.ts"],
|
||||
"@isa/core/storage": ["libs/core/storage/src/index.ts"],
|
||||
"@isa/icons": ["libs/icons/src/index.ts"],
|
||||
"@isa/oms/utils/translation": ["libs/oms/utils/translation/src/index.ts"],
|
||||
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user