mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Indexeddb Implementaion
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './application.module';
|
||||
export * from './application.service';
|
||||
export * from './defs';
|
||||
export * from './store';
|
||||
// end:ng42.barrel
|
||||
|
||||
1
apps/core/cache/src/lib/cache-options.ts
vendored
1
apps/core/cache/src/lib/cache-options.ts
vendored
@@ -1,4 +1,3 @@
|
||||
export interface CacheOptions {
|
||||
ttl?: number;
|
||||
persist?: boolean;
|
||||
}
|
||||
|
||||
245
apps/core/cache/src/lib/cache.service.ts
vendored
245
apps/core/cache/src/lib/cache.service.ts
vendored
@@ -1,80 +1,45 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CacheOptions } from './cache-options';
|
||||
import { Cached } from './cached';
|
||||
import { effect, inject, Injectable } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { memorize } from '@utils/common';
|
||||
import { interval } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
interface DbEntry<T> {
|
||||
key: string;
|
||||
token: object | string;
|
||||
data: T;
|
||||
ttl: number;
|
||||
}
|
||||
|
||||
const H12INMS = 1000 * 60 * 60 * 12;
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CacheService {
|
||||
constructor() {
|
||||
this._registerCleanupTask();
|
||||
private auth = inject(AuthService);
|
||||
|
||||
private db!: IDBDatabase;
|
||||
|
||||
get sub() {
|
||||
return this.auth.getClaimByKey('sub');
|
||||
}
|
||||
|
||||
_registerCleanupTask() {
|
||||
_cleanupInterval = toSignal(interval(1000 * 60));
|
||||
|
||||
_cleanupIntervalEffect = effect(() => {
|
||||
this._cleanupInterval();
|
||||
this.cleanup();
|
||||
interval(1000 * 60).subscribe(() => {
|
||||
this.cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
get storeName() {
|
||||
return 'cache';
|
||||
}
|
||||
|
||||
set<T>(token: Object, data: T, options?: CacheOptions) {
|
||||
const persist = options?.persist;
|
||||
const ttl = options?.ttl;
|
||||
const cached: Cached = {
|
||||
data,
|
||||
};
|
||||
|
||||
if (ttl) {
|
||||
cached.until = Date.now() + ttl;
|
||||
} else {
|
||||
cached.until = Date.now() + 1000 * 60 * 60 * 12;
|
||||
private getKey(token: Object | string): string {
|
||||
if (typeof token === 'string') {
|
||||
return this.hash(token);
|
||||
}
|
||||
|
||||
if (persist) {
|
||||
localStorage.setItem(this.getKey(token), this.serialize(cached));
|
||||
} else {
|
||||
sessionStorage.setItem(this.getKey(token), this.serialize(cached));
|
||||
}
|
||||
|
||||
Object.freeze(cached);
|
||||
return cached;
|
||||
}
|
||||
|
||||
get<T = any>(token: Object, from?: 'session' | 'persist'): T {
|
||||
let cached: Cached;
|
||||
|
||||
if (from === 'session') {
|
||||
cached = this.deserialize(sessionStorage.getItem(this.getKey(token)));
|
||||
} else if (from === 'persist') {
|
||||
cached = this.deserialize(localStorage.getItem(this.getKey(token)));
|
||||
} else {
|
||||
cached = this.deserialize(sessionStorage.getItem(this.getKey(token))) || this.deserialize(localStorage.getItem(this.getKey(token)));
|
||||
}
|
||||
|
||||
if (!cached) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (cached.until < Date.now()) {
|
||||
this.delete(token, from);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
delete(token: Object, from: 'session' | 'persist' = 'session') {
|
||||
if (from === 'session') {
|
||||
sessionStorage.removeItem(this.getKey(token));
|
||||
} else if (from === 'persist') {
|
||||
localStorage.removeItem(this.getKey(token));
|
||||
}
|
||||
}
|
||||
|
||||
private getKey(token: Object) {
|
||||
const key = `CacheService_` + this.hash(JSON.stringify(token));
|
||||
return key;
|
||||
return this.hash(JSON.stringify(token));
|
||||
}
|
||||
|
||||
private hash(data: string): string {
|
||||
@@ -82,35 +47,139 @@ export class CacheService {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
hash = data.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
return hash.toString(16);
|
||||
return (this.sub + hash).toString(16);
|
||||
}
|
||||
|
||||
private serialize(data: Cached): string {
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
@memorize()
|
||||
private async openDB(): Promise<IDBDatabase> {
|
||||
if (this.db) {
|
||||
return this.db; // Datenbank bereits geöffnet, bestehende Verbindung zurückgeben
|
||||
}
|
||||
|
||||
private deserialize(data: string): Cached {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const request = indexedDB.open('isa-cache', 1);
|
||||
|
||||
cleanup() {
|
||||
// get all keys created by this service by looking for the service name and remove the entries
|
||||
// that ttl is expired
|
||||
let localStorageKeys = Object.keys(localStorage).filter((key) => key.startsWith('CacheService_'));
|
||||
let seesionStorageKeys = Object.keys(sessionStorage).filter((key) => key.startsWith('CacheService_'));
|
||||
request.onerror = (event) => {
|
||||
console.error('IndexedDB error:', event);
|
||||
reject(event);
|
||||
};
|
||||
|
||||
localStorageKeys.forEach((key) => {
|
||||
const cached = this.deserialize(localStorage.getItem(key));
|
||||
if (cached.until < Date.now()) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
request.onupgradeneeded = (event) => {
|
||||
this.db = (event.target as IDBOpenDBRequest).result;
|
||||
|
||||
if (!this.db.objectStoreNames.contains(this.storeName)) {
|
||||
this.db.createObjectStore(this.storeName, { keyPath: 'key' });
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
this.db = (event.target as IDBOpenDBRequest).result;
|
||||
resolve(this.db);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
seesionStorageKeys.forEach((key) => {
|
||||
const cached = this.deserialize(sessionStorage.getItem(key));
|
||||
if (cached.until < Date.now()) {
|
||||
sessionStorage.removeItem(key);
|
||||
private async getObjectStore(mode: IDBTransactionMode = 'readonly'): Promise<IDBObjectStore> {
|
||||
const db = await this.openDB(); // Datenbankverbindung öffnen oder wiederverwenden
|
||||
const transaction = db.transaction(this.storeName, mode);
|
||||
return transaction.objectStore(this.storeName);
|
||||
}
|
||||
|
||||
async set<T>(token: object | string, data: T, options: { ttl?: number } = {}): Promise<string> {
|
||||
const store = await this.getObjectStore('readwrite');
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const key = this.getKey(token);
|
||||
const entry: DbEntry<T> = {
|
||||
key,
|
||||
data,
|
||||
token,
|
||||
ttl: Date.now() + (options.ttl || H12INMS),
|
||||
};
|
||||
|
||||
const request = store.add(entry);
|
||||
request.onsuccess = (event) => {
|
||||
resolve(key);
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
reject(event);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async cached(token: Object | string): Promise<DbEntry<any> | undefined> {
|
||||
const store = await this.getObjectStore();
|
||||
return new Promise<DbEntry<any> | undefined>((resolve, reject) => {
|
||||
const request = store.get(this.getKey(token));
|
||||
request.onsuccess = (event) => {
|
||||
resolve((event.target as IDBRequest).result);
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
reject(event);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async get<T = any>(token: Object | string): Promise<T | undefined> {
|
||||
const cached = await this.cached(token);
|
||||
|
||||
if (!cached) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (cached.ttl < Date.now()) {
|
||||
this.delete(token);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
async delete(token: Object | string): Promise<void> {
|
||||
const store = await this.getObjectStore('readwrite');
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const request = store.delete(this.getKey(token));
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
reject(event);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
const store = await this.getObjectStore('readwrite');
|
||||
|
||||
store.openCursor().onsuccess = (event) => {
|
||||
const cursor = (event.target as IDBRequest).result;
|
||||
if (cursor) {
|
||||
if (cursor.value.ttl < Date.now()) {
|
||||
store.delete(cursor.key);
|
||||
}
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
store.transaction.oncomplete = () => {
|
||||
resolve();
|
||||
};
|
||||
store.transaction.onerror = (event) => {
|
||||
reject(event);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async clear() {
|
||||
const store = await this.getObjectStore('readwrite');
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const request = store.clear();
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
reject(event);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
4
apps/core/cache/src/lib/cached.ts
vendored
4
apps/core/cache/src/lib/cached.ts
vendored
@@ -1,4 +0,0 @@
|
||||
export interface Cached {
|
||||
until?: number;
|
||||
data?: any;
|
||||
}
|
||||
@@ -160,12 +160,12 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
const cleanQueryParams = this.cleanupQueryParams(queryParams);
|
||||
|
||||
if (processChanged) {
|
||||
this.scrollToItem(this._getScrollIndexFromCache());
|
||||
this.scrollToItem(await this._getScrollIndexFromCache());
|
||||
}
|
||||
|
||||
if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this.searchService.filter.getQueryParams()))) {
|
||||
await this.searchService.setDefaultFilter(queryParams);
|
||||
const data = this.getCachedData(processId, queryParams, selectedBranch?.id);
|
||||
const data = await this.getCachedData(processId, queryParams, selectedBranch?.id);
|
||||
if (data.items?.length > 0) {
|
||||
this.searchService.setItems(data.items);
|
||||
this.searchService.setHits(data.hits);
|
||||
@@ -267,22 +267,22 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.scrollToItem(this._getScrollIndexFromCache());
|
||||
this._getScrollIndexFromCache().then((index) => this.scrollToItem(index));
|
||||
}
|
||||
|
||||
private _addScrollIndexToCache(index: number): void {
|
||||
this.cache.set<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN }, index);
|
||||
}
|
||||
|
||||
private _getScrollIndexFromCache(): number {
|
||||
return this.cache.get<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN }) ?? 0;
|
||||
private async _getScrollIndexFromCache(): Promise<number> {
|
||||
return (await this.cache.get<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN })) ?? 0;
|
||||
}
|
||||
|
||||
scrollToItem(i?: number) {
|
||||
async scrollToItem(i?: number) {
|
||||
let index = i;
|
||||
|
||||
if (!index) {
|
||||
index = this._getScrollIndexFromCache();
|
||||
index = await this._getScrollIndexFromCache();
|
||||
} else {
|
||||
this._addScrollIndexToCache(index);
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
this.patchState({ queryParams });
|
||||
}
|
||||
|
||||
loadSearchHistoryFromSessionStorage() {
|
||||
const searchHistory = this._cache.get<string[]>({ token: 'search_history_customer_orders' });
|
||||
async loadSearchHistoryFromSessionStorage() {
|
||||
const searchHistory = await this._cache.get<string[]>({ token: 'search_history_customer_orders' });
|
||||
this.setSearchHistory(searchHistory?.slice(0, 7) ?? []);
|
||||
}
|
||||
|
||||
@@ -220,12 +220,13 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
this.searchResultClearedSubject.next();
|
||||
}
|
||||
}),
|
||||
switchMap(([options, results, filter, branch]) => {
|
||||
switchMap(async ([options, results, filter, branch]) => {
|
||||
const queryToken = filter?.getQueryToken() ?? {};
|
||||
const cached =
|
||||
options?.siletReload && this._cache.get({ ...filter?.getQueryToken(), processId: this.processId, branchId: String(branch?.id) });
|
||||
options?.siletReload &&
|
||||
(await this._cache.get({ ...filter?.getQueryToken(), processId: this.processId, branchId: String(branch?.id) }));
|
||||
if (cached) {
|
||||
const cachedResults = this._cache.get({ ...queryToken, processId: this.processId, branchId: String(branch?.id) });
|
||||
const cachedResults = await this._cache.get({ ...queryToken, processId: this.processId, branchId: String(branch?.id) });
|
||||
if (cachedResults?.results?.length > 0) {
|
||||
this.patchState(cachedResults);
|
||||
this.searchResultFromCacheSubject.next({ hits: cachedResults.hits, results: cachedResults.results });
|
||||
|
||||
@@ -209,7 +209,7 @@ export class CustomerOrderSearchResultsComponent
|
||||
processId,
|
||||
branchId: String(selectedBranch?.id),
|
||||
};
|
||||
const data = this._customerOrderSearchStore.getCachedData({ queryToken });
|
||||
const data = await this._customerOrderSearchStore.getCachedData({ queryToken });
|
||||
|
||||
if (data?.results?.length > 0) {
|
||||
this._customerOrderSearchStore.patchState({
|
||||
@@ -229,7 +229,7 @@ export class CustomerOrderSearchResultsComponent
|
||||
this._customerOrderSearchStore.search({ siletReload: true });
|
||||
}
|
||||
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
const scrollPos = await this._getScrollPositionFromCache();
|
||||
if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
@@ -309,8 +309,8 @@ export class CustomerOrderSearchResultsComponent
|
||||
}
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this._customerOrderSearchStore.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
private async _getScrollPositionFromCache(): Promise<number> {
|
||||
return await this._cache.get<number>({ processId: this._customerOrderSearchStore.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
// After Navigating to Result Side Outlet
|
||||
|
||||
@@ -155,8 +155,16 @@ export class CustomerTypeSelectorComponent
|
||||
processId: undefined,
|
||||
customerType: undefined,
|
||||
p4mUser: false,
|
||||
options: _cache.get('customerTypeOptions') ?? [],
|
||||
options: [],
|
||||
});
|
||||
this.initOptions();
|
||||
}
|
||||
|
||||
async initOptions() {
|
||||
const options = await this._cache.get('customerTypeOptions');
|
||||
if (options) {
|
||||
this.patchState({ options });
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
@@ -83,8 +83,8 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
) {
|
||||
this.store.search({
|
||||
cb: () => {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache());
|
||||
setTimeout(async () => {
|
||||
this.scrollContainer?.scrollTo(await this._getScrollPositionFromCache());
|
||||
this._removeScrollPositionFromCache();
|
||||
}, 0);
|
||||
},
|
||||
@@ -103,8 +103,8 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.store?.filter?.fromQueryParams(params);
|
||||
this.store.search({
|
||||
cb: () => {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache());
|
||||
setTimeout(async () => {
|
||||
this.scrollContainer?.scrollTo(await this._getScrollPositionFromCache());
|
||||
this._removeScrollPositionFromCache();
|
||||
}, 0);
|
||||
},
|
||||
@@ -159,8 +159,8 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
private async _getScrollPositionFromCache(): Promise<number> {
|
||||
return await this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
actions$ = this.items$.pipe(map((items) => items[0]?.actions));
|
||||
@@ -60,7 +60,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
private _modal: UiModalService,
|
||||
private _config: Config,
|
||||
private _toast: ToasterService,
|
||||
private _cache: CacheService
|
||||
private _cache: CacheService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -83,12 +83,12 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
private _addScrollPositionToCache(): void {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
|
||||
this.scrollContainer?.scrollPos
|
||||
this.scrollContainer?.scrollPos,
|
||||
);
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
private async _getScrollPositionFromCache(): Promise<number> {
|
||||
return await this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
async createBreadcrumb() {
|
||||
@@ -149,7 +149,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
this._store.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
await this.createBreadcrumb();
|
||||
|
||||
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache() ?? 0);
|
||||
this.scrollContainer?.scrollTo((await this._getScrollPositionFromCache()) ?? 0);
|
||||
this._removeScrollPositionFromCache();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
* und mindestens ein package vorhanden ist.
|
||||
*/
|
||||
showList$ = combineLatest([this.store.fetching$, this.store.packages$]).pipe(
|
||||
map(([fetching, packages]) => fetching || packages.length > 0)
|
||||
map(([fetching, packages]) => fetching || packages.length > 0),
|
||||
);
|
||||
|
||||
@ViewChild(PackageListComponent, { static: true, read: PackageListComponent })
|
||||
@@ -54,7 +54,7 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router,
|
||||
private _cache: CacheService,
|
||||
private _navigation: PackageInspectionNavigationService
|
||||
private _navigation: PackageInspectionNavigationService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -78,13 +78,13 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
initFilterSubscription() {
|
||||
const initialFilter$ = this.store.filter$.pipe(
|
||||
filter((f) => f instanceof UiFilter),
|
||||
first()
|
||||
first(),
|
||||
);
|
||||
const queryParams$ = this._activatedRoute.queryParams;
|
||||
|
||||
const filterSub = combineLatest([initialFilter$, queryParams$]).subscribe(([filter, queryParams]) => {
|
||||
const filterSub = combineLatest([initialFilter$, queryParams$]).subscribe(async ([filter, queryParams]) => {
|
||||
const restoredFilter = this.restoreFilterFromQueryParams(filter, queryParams);
|
||||
const restoredData = this.restoreResultsFromCache(restoredFilter);
|
||||
const restoredData = await this.restoreResultsFromCache(restoredFilter);
|
||||
this.createBreadcrumbIfNotExists(restoredFilter || this.store.filter);
|
||||
this.fetchPackages(restoredFilter, { keep: true, take: restoredData?.packages?.length });
|
||||
});
|
||||
@@ -110,8 +110,8 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
return nextFilter;
|
||||
}
|
||||
|
||||
restoreResultsFromCache(filter: UiFilter): PackageResultCacheData | undefined {
|
||||
const data = this._cache.get<PackageResultCacheData>(filter.getQueryParams());
|
||||
async restoreResultsFromCache(filter: UiFilter): Promise<PackageResultCacheData | undefined> {
|
||||
const data = await this._cache.get<PackageResultCacheData>(filter.getQueryParams());
|
||||
if (data) {
|
||||
const update = this.hasUpdate();
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
map(([filter, defaultFilter]) => {
|
||||
const filterQueryParams = filter?.getQueryParams();
|
||||
return !isEqual(filterQueryParams, Filter.create(defaultFilter).getQueryParams());
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
fetching$: Observable<boolean> = this.store.fetchingList$;
|
||||
@@ -117,19 +117,19 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _pickUpShelfInNavigation: PickupShelfInNavigationService,
|
||||
private _cache: CacheService,
|
||||
private _router: Router
|
||||
private _router: Router,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
combineLatest([this.store.processId$, this._activatedRoute.queryParams])
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(([_, queryParams]) => {
|
||||
.subscribe(async ([_, queryParams]) => {
|
||||
if (!this.store.list.length || !isEqual(queryParams, this.cleanupQueryParams(this.store.filter.getQueryParams()))) {
|
||||
this.store.setQueryParams(queryParams);
|
||||
this.store.fetchList({ emitFetchListResponse: false });
|
||||
}
|
||||
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
const scrollPos = await this._getScrollPositionFromCache();
|
||||
if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
@@ -173,8 +173,8 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
private async _getScrollPositionFromCache(): Promise<number> {
|
||||
return await this._cache.get<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
// After Navigating to Result Side Outlet
|
||||
|
||||
@@ -80,7 +80,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
map(([filter, defaultFilter]) => {
|
||||
const filterQueryParams = filter?.getQueryParams();
|
||||
return !isEqual(filterQueryParams, Filter.create(defaultFilter).getQueryParams());
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
fetching$: Observable<boolean> = this.store.fetchingList$;
|
||||
@@ -120,9 +120,9 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
|
||||
actions$ = combineLatest([this.list$, this.selectedIds$]).pipe(
|
||||
map(([items, selectedIds]) =>
|
||||
items?.find((item) => selectedIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId))
|
||||
items?.find((item) => selectedIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId)),
|
||||
),
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action)))
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action))),
|
||||
);
|
||||
|
||||
loadingFetchedActionButton$ = new BehaviorSubject<boolean>(false);
|
||||
@@ -135,16 +135,16 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
private _uiModal: UiModalService,
|
||||
private _pickUpShelfOutNavigation: PickUpShelfOutNavigationService,
|
||||
private _cache: CacheService,
|
||||
private _router: Router
|
||||
private _router: Router,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.processId$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(150)).subscribe((_) => {
|
||||
this.processId$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(150)).subscribe(async (_) => {
|
||||
if (!this.store.list.length) {
|
||||
this.store.fetchList();
|
||||
}
|
||||
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
const scrollPos = await this._getScrollPositionFromCache();
|
||||
if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
@@ -175,8 +175,8 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
private async _getScrollPositionFromCache(): Promise<number> {
|
||||
return await this._cache.get<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
// After Navigating to Result Side Outlet
|
||||
@@ -190,7 +190,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
|
||||
getSelectedItem$(item: DBHOrderItemListItemDTO) {
|
||||
return this.store.selectedListItems$.pipe(
|
||||
map((selectedListItems) => selectedListItems?.find((i) => i?.orderItemSubsetId === item?.orderItemSubsetId))
|
||||
map((selectedListItems) => selectedListItems?.find((i) => i?.orderItemSubsetId === item?.orderItemSubsetId)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
} else {
|
||||
return item?.actions?.some((action) => this.selectionRules(action));
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,26 +89,26 @@ export class PickUpShelfListItemComponent {
|
||||
private _processingStatusPipe: PickupShelfProcessingStatusPipe,
|
||||
) {}
|
||||
|
||||
onDetailsClick() {
|
||||
async onDetailsClick() {
|
||||
if (this.isDesktopLarge) {
|
||||
this.scrollIntoView();
|
||||
}
|
||||
|
||||
if (!this.hasOrderItemInCache()) {
|
||||
if (!(await this.hasOrderItemInCache())) {
|
||||
this.addOrderItemIntoCache();
|
||||
}
|
||||
}
|
||||
|
||||
hasOrderItemInCache() {
|
||||
async hasOrderItemInCache() {
|
||||
const items =
|
||||
this.cache.get<DBHOrderItemListItemDTO[]>({
|
||||
(await this.cache.get<DBHOrderItemListItemDTO[]>({
|
||||
name: 'orderItems',
|
||||
orderId: this.item.orderId,
|
||||
compartmentCode: this.item.compartmentCode,
|
||||
compartmentInfo: this.item.compartmentInfo,
|
||||
orderItemProcessingStatus: this.item.processingStatus,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
}) ?? [];
|
||||
})) ?? [];
|
||||
|
||||
return items.some((i) => i.orderItemSubsetId === this.item.orderItemSubsetId);
|
||||
}
|
||||
@@ -124,7 +124,7 @@ export class PickUpShelfListItemComponent {
|
||||
store: 'PickupShelfDetailsStore',
|
||||
},
|
||||
[this.item],
|
||||
{ persist: false, ttl: 1000 },
|
||||
{ ttl: 1000 },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -362,9 +362,9 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchOrder = (orderId) => {
|
||||
const order = this._cacheService.get<OrderDTO>({ name: 'order', orderId, store: 'PickupShelfDetailsStore' }) ?? { id: orderId };
|
||||
const customer = this._cacheService.get<CustomerInfoDTO>({
|
||||
private beforeFetchOrder = async (orderId) => {
|
||||
const order = (await this._cacheService.get<OrderDTO>({ name: 'order', orderId, store: 'PickupShelfDetailsStore' })) ?? { id: orderId };
|
||||
const customer = await this._cacheService.get<CustomerInfoDTO>({
|
||||
name: 'customer',
|
||||
orderId,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
@@ -377,7 +377,6 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
this.patchState({ fetchingOrder: false, order: res.result });
|
||||
|
||||
this._cacheService.set<OrderDTO>({ name: 'order', orderId: res.result.id, store: 'PickupShelfDetailsStore' }, res.result, {
|
||||
persist: true,
|
||||
ttl: 3600000,
|
||||
});
|
||||
|
||||
@@ -416,7 +415,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchOrderItems = ({
|
||||
private beforeFetchOrderItems = async ({
|
||||
compartmentCode,
|
||||
compartmentInfo,
|
||||
orderItemProcessingStatus,
|
||||
@@ -429,14 +428,14 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
orderItemSubsetId?: number;
|
||||
}) => {
|
||||
const orderItems =
|
||||
this._cacheService.get<DBHOrderItemListItemDTO[]>({
|
||||
(await this._cacheService.get<DBHOrderItemListItemDTO[]>({
|
||||
name: 'orderItems',
|
||||
orderId: this.order?.id,
|
||||
compartmentCode,
|
||||
compartmentInfo,
|
||||
orderItemProcessingStatus,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
}) ?? [];
|
||||
})) ?? [];
|
||||
|
||||
this.patchState({
|
||||
fetchingOrderItems: true,
|
||||
@@ -505,8 +504,8 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
),
|
||||
);
|
||||
|
||||
beforeFetchCustomer = () => {
|
||||
const customer = this._cacheService.get<CustomerInfoDTO>({
|
||||
beforeFetchCustomer = async () => {
|
||||
const customer = await this._cacheService.get<CustomerInfoDTO>({
|
||||
name: 'customer',
|
||||
orderId: this.order.id,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
@@ -548,7 +547,6 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
store: 'PickupShelfDetailsStore',
|
||||
},
|
||||
customer,
|
||||
{ persist: true, ttl: 3600000 },
|
||||
);
|
||||
|
||||
this.patchState({ fetchingCustomer: false, customer });
|
||||
|
||||
@@ -171,7 +171,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
|
||||
fetchQuerySettings = this.effect((trigger$) =>
|
||||
trigger$.pipe(
|
||||
map(() => this.beforeFetchQuerySettings()),
|
||||
switchMap(() => this.beforeFetchQuerySettings()),
|
||||
filter((shouldFetch) => shouldFetch),
|
||||
switchMap(() =>
|
||||
this._pickupShelfIOService.getQuerySettings().pipe(tapResponse(this.fetchQuerySettingsDone, this.fetchQuerySettingsError)),
|
||||
@@ -183,8 +183,8 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
* Before fetching query settings
|
||||
* @returns true if query settings should be fetched
|
||||
*/
|
||||
private beforeFetchQuerySettings = () => {
|
||||
const cachedQuerySettings = this._cacheService.get<QuerySettingsDTO>({
|
||||
private beforeFetchQuerySettings = async () => {
|
||||
const cachedQuerySettings = await this._cacheService.get<QuerySettingsDTO>({
|
||||
name: 'pickup-shelf',
|
||||
providerName: this._pickupShelfIOService.name(),
|
||||
});
|
||||
@@ -220,7 +220,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
trigger$.pipe(
|
||||
this.delayWhenFilterIsNotReady,
|
||||
withLatestFrom(this.filter$, this.processId$),
|
||||
map(([{ emitFetchListResponse } = { emitFetchListResponse: true }, filter, processId]) =>
|
||||
switchMap(([{ emitFetchListResponse } = { emitFetchListResponse: true }, filter, processId]) =>
|
||||
this.beforeFetchList(emitFetchListResponse, filter, processId),
|
||||
),
|
||||
switchMap(({ emitFetchListResponse, filter, processId, list }) =>
|
||||
@@ -240,11 +240,11 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchList = (emitFetchListResponse: boolean, filter: Filter, processId: number) => {
|
||||
private beforeFetchList = async (emitFetchListResponse: boolean, filter: Filter, processId: number) => {
|
||||
this.cancelListRequests();
|
||||
this.patchState({ fetchingList: true });
|
||||
const queryToken = filter.getQueryParams();
|
||||
const cachedListResponse = this._cacheService.get<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken });
|
||||
const cachedListResponse = await this._cacheService.get<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken });
|
||||
|
||||
let list: DBHOrderItemListItemDTO[] = [];
|
||||
|
||||
@@ -274,9 +274,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
this._fetchListResponse.next({ processId, response, queryParams });
|
||||
}
|
||||
|
||||
this._cacheService.set<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken: queryParams }, response, {
|
||||
persist: true,
|
||||
});
|
||||
this._cacheService.set<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken: queryParams }, response);
|
||||
};
|
||||
|
||||
private fetchListError = (err: any) => {
|
||||
|
||||
@@ -170,8 +170,8 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
initSearch() {
|
||||
combineLatest([this._triggerReload$, this._filterChange$])
|
||||
.pipe(debounceTime(500), takeUntil(this._onDestroy$))
|
||||
.subscribe(([reload, filter]) => {
|
||||
const data = this.getCachedData();
|
||||
.subscribe(async ([reload, filter]) => {
|
||||
const data = await this.getCachedData();
|
||||
if (reload || data.items?.length === 0 || filter) {
|
||||
this.search({ newSearch: true });
|
||||
}
|
||||
@@ -254,10 +254,12 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
|
||||
// Only trigger search with _filterChange$ if no cached data available
|
||||
// If cached data is available, there is no need to trigger the search
|
||||
const data = this.getCachedData();
|
||||
if (data.items?.length === 0) {
|
||||
this._filterChange$.next(true);
|
||||
}
|
||||
|
||||
this.getCachedData().then((data) => {
|
||||
if (data.items?.length === 0) {
|
||||
this._filterChange$.next(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
(err) => {},
|
||||
),
|
||||
@@ -439,12 +441,12 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
);
|
||||
}
|
||||
|
||||
getCachedData() {
|
||||
async getCachedData() {
|
||||
return (
|
||||
this._cache.get<{
|
||||
(await this._cache.get<{
|
||||
items: RemissionListItem[];
|
||||
hits: number;
|
||||
}>({ processId: String(this.processId) }) || { items: [], hits: 0 }
|
||||
}>({ processId: String(this.processId) })) || { items: [], hits: 0 }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
this._remissionListStore.setSelectedSource(source);
|
||||
}
|
||||
|
||||
const data = this._remissionListStore.getCachedData();
|
||||
const data = await this._remissionListStore.getCachedData();
|
||||
if (data.items?.length !== 0) {
|
||||
this._remissionListStore.setItems(data.items);
|
||||
this._remissionListStore.setHits(data.hits);
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -27,8 +27,10 @@
|
||||
"@ngrx/entity": "^17.2.0",
|
||||
"@ngrx/store": "^17.2.0",
|
||||
"@ngrx/store-devtools": "^17.2.0",
|
||||
"@tempfix/idb": "^8.0.3",
|
||||
"angular-oauth2-oidc": "^17.0.2",
|
||||
"angular-oauth2-oidc-jwks": "^17.0.2",
|
||||
"idb": "^8.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"ng2-pdf-viewer": "^10.2.2",
|
||||
@@ -4267,6 +4269,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"node_modules/@tempfix/idb": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tempfix/idb/-/idb-8.0.3.tgz",
|
||||
"integrity": "sha512-hPJQKO7+oAIY+pDNImrZ9QAINbz9KmwT+yO4iRVwdPanok2YKpaUxdJzIvCUwY0YgAawlvYdffbLvRLV5hbs2g=="
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "8.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
|
||||
@@ -8482,6 +8489,11 @@
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/idb": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz",
|
||||
"integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw=="
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
@@ -18932,6 +18944,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"@tempfix/idb": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tempfix/idb/-/idb-8.0.3.tgz",
|
||||
"integrity": "sha512-hPJQKO7+oAIY+pDNImrZ9QAINbz9KmwT+yO4iRVwdPanok2YKpaUxdJzIvCUwY0YgAawlvYdffbLvRLV5hbs2g=="
|
||||
},
|
||||
"@testing-library/dom": {
|
||||
"version": "8.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
|
||||
@@ -22198,6 +22215,11 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"idb": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz",
|
||||
"integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw=="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
|
||||
@@ -78,8 +78,10 @@
|
||||
"@ngrx/entity": "^17.2.0",
|
||||
"@ngrx/store": "^17.2.0",
|
||||
"@ngrx/store-devtools": "^17.2.0",
|
||||
"@tempfix/idb": "^8.0.3",
|
||||
"angular-oauth2-oidc": "^17.0.2",
|
||||
"angular-oauth2-oidc-jwks": "^17.0.2",
|
||||
"idb": "^8.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"ng2-pdf-viewer": "^10.2.2",
|
||||
|
||||
Reference in New Issue
Block a user