mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Fix Artikelsuche Prozesswechsel und Breadcrumb
Co-authored-by: schickinger <schickinger@users.noreply.github.com>
This commit is contained in:
@@ -59,13 +59,20 @@ export class BreadcrumbService {
|
||||
return this.store.select(selectors.selectBreadcrumbsByKeyAndTags, { key, tags });
|
||||
}
|
||||
|
||||
async removeBreadcrumbsAfter(breadcrumbId: number) {
|
||||
async removeBreadcrumbsAfter(breadcrumbId: number, withTags: string[] = []) {
|
||||
const breadcrumb = await this.getBreadcrumbById$(breadcrumbId).pipe(take(1)).toPromise();
|
||||
|
||||
if (!breadcrumb) {
|
||||
return;
|
||||
}
|
||||
const breadcrumbs = await this.getBreadcrumbByKey$(breadcrumb.key).pipe(take(1)).toPromise();
|
||||
|
||||
let breadcrumbs: Breadcrumb[];
|
||||
|
||||
if (withTags?.length > 0) {
|
||||
breadcrumbs = await this.getBreadcrumbsByKeyAndTags$(breadcrumb.key, withTags).pipe(take(1)).toPromise();
|
||||
} else {
|
||||
breadcrumbs = await this.getBreadcrumbByKey$(breadcrumb.key).pipe(take(1)).toPromise();
|
||||
}
|
||||
|
||||
if (!breadcrumbs?.length) {
|
||||
return;
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Filter } from '@ui/filter';
|
||||
import { ItemDTO, QueryTokenDTO, OrderByDTO } from '@swagger/cat';
|
||||
|
||||
export interface ArticleSearchState {
|
||||
processId?: number;
|
||||
searchState: 'empty' | 'fetching' | '';
|
||||
params: {
|
||||
query?: string;
|
||||
@@ -38,6 +39,11 @@ export interface ArticleSearchState {
|
||||
export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
readonly onSearch = new Subject<{ clear?: boolean; reload?: boolean }>();
|
||||
|
||||
private processIdSelector = (s: ArticleSearchState) => s.processId;
|
||||
get processId() {
|
||||
return this.get(this.processIdSelector);
|
||||
}
|
||||
|
||||
private queryParamsQuerySelector = (s: ArticleSearchState) => (s.params?.query?.length ? decodeURI(s.params.query) : '');
|
||||
readonly queryParamsQuery$ = this.select(this.queryParamsQuerySelector);
|
||||
get query() {
|
||||
@@ -154,14 +160,7 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
)
|
||||
);
|
||||
|
||||
private connectedRouteSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private catalog: DomainCatalogService,
|
||||
private breadcrumb: BreadcrumbService,
|
||||
private application: ApplicationService
|
||||
) {
|
||||
constructor(private router: Router, private catalog: DomainCatalogService, private breadcrumb: BreadcrumbService) {
|
||||
super({
|
||||
searchState: '',
|
||||
params: {},
|
||||
@@ -211,58 +210,9 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
)
|
||||
);
|
||||
|
||||
connect(
|
||||
route: ActivatedRoute,
|
||||
options?: {
|
||||
beforeParamsChange?: (
|
||||
current: StringDictionary<string>,
|
||||
previous: StringDictionary<string>
|
||||
) => StringDictionary<string> | Promise<StringDictionary<string>>;
|
||||
afterParamsChange?: (params: StringDictionary<string>, original?: StringDictionary<string>) => void | Promise<void>;
|
||||
connected?: (params: StringDictionary<string>) => void | Promise<void>;
|
||||
disconnected?: () => void;
|
||||
}
|
||||
) {
|
||||
this.disconnect();
|
||||
let connected = false;
|
||||
|
||||
this.connectedRouteSubscription = route.queryParams
|
||||
.pipe(finalize(() => options?.disconnected?.call(undefined)))
|
||||
.subscribe(async (params) => {
|
||||
let next = { ...params };
|
||||
let previous = this.get(this.queryParamsSelector);
|
||||
|
||||
if (typeof options?.beforeParamsChange === 'function') {
|
||||
next = await options.beforeParamsChange(params, previous);
|
||||
}
|
||||
|
||||
if (!isEqual(previous, next)) {
|
||||
this.setQueryParams({ params: next });
|
||||
}
|
||||
|
||||
if (typeof options?.afterParamsChange === 'function') {
|
||||
await options.afterParamsChange(next, params);
|
||||
}
|
||||
|
||||
if (!connected) {
|
||||
connected = true;
|
||||
setTimeout(() => options?.connected?.call(undefined, next), 0);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
disconnect: () => {
|
||||
this.disconnect();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async updateBreadcrumbs() {
|
||||
const params = this.get(this.queryParamsSelector);
|
||||
const crumbs = await this.breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.application.activatedProcessId, ['catalog', 'filter'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const crumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(this.processId, ['catalog', 'filter']).pipe(first()).toPromise();
|
||||
|
||||
const { hits, query } = this;
|
||||
|
||||
@@ -283,23 +233,28 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
});
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.connectedRouteSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
navigateToResults() {
|
||||
this.router.navigate(['/product/search/results'], { queryParams: this.queryParams });
|
||||
const path = '/product/search/results';
|
||||
if (!this.router.isActive(path, false)) {
|
||||
this.router.navigate([path], { queryParams: this.queryParams });
|
||||
}
|
||||
}
|
||||
|
||||
navigateToDetails(item: ItemDTO) {
|
||||
this.router.navigate(['/product/details', item.id]);
|
||||
const path = '/product/details';
|
||||
if (!this.router.isActive(path, false)) {
|
||||
this.router.navigate([path, item.id]);
|
||||
}
|
||||
}
|
||||
|
||||
navigateToMain() {
|
||||
this.router.navigate(['/product/search'], { queryParams: this.queryParams });
|
||||
const path = '/product/search';
|
||||
if (!this.router.isActive(path, false)) {
|
||||
this.router.navigate([path], { queryParams: this.queryParams });
|
||||
}
|
||||
}
|
||||
|
||||
private setQueryParams({ params }: { params: StringDictionary<string> }) {
|
||||
setQueryParams({ params }: { params: StringDictionary<string> }) {
|
||||
this.patchState({ params });
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { Filter } from '@ui/filter';
|
||||
import { NEVER, Subscription } from 'rxjs';
|
||||
import { catchError, first, switchMap } from 'rxjs/operators';
|
||||
import { combineLatest, NEVER, Subscription } from 'rxjs';
|
||||
import { catchError, debounceTime, first, switchMap } from 'rxjs/operators';
|
||||
import { ArticleSearchStore } from '../article-search-new.store';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
@Component({
|
||||
selector: 'page-article-search-main',
|
||||
@@ -16,13 +17,13 @@ import { ArticleSearchStore } from '../article-search-new.store';
|
||||
})
|
||||
export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
readonly history$ = this.catalog.getSearchHistory({ take: 5 }).pipe(catchError(() => NEVER));
|
||||
readonly inputFilter$ = this.articleSearchStore.inputSelectorFilter$;
|
||||
readonly mainFilter$ = this.articleSearchStore.mainFilter$;
|
||||
readonly inputFilter$ = this.store.inputSelectorFilter$;
|
||||
readonly mainFilter$ = this.store.mainFilter$;
|
||||
|
||||
subscriptions = new Subscription();
|
||||
|
||||
constructor(
|
||||
private articleSearchStore: ArticleSearchStore,
|
||||
private store: ArticleSearchStore,
|
||||
private catalog: DomainCatalogService,
|
||||
private route: ActivatedRoute,
|
||||
private breadcrumb: BreadcrumbService,
|
||||
@@ -30,30 +31,44 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.articleSearchStore.connect(this.route);
|
||||
this.subscriptions.add(
|
||||
this.application.activatedProcessId$
|
||||
.pipe(switchMap((processId) => this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'main']).pipe(first())))
|
||||
.subscribe((crumbs) => {
|
||||
crumbs.forEach((crumb) => this.breadcrumb.removeBreadcrumbsAfter(crumb.id));
|
||||
combineLatest([this.application.activatedProcessId$, this.route.queryParams])
|
||||
.pipe(debounceTime(0))
|
||||
.subscribe(async ([processId, queryParams]) => {
|
||||
// Setzen des aktuellen Prozesses
|
||||
if (this.store?.processId !== processId) {
|
||||
this.store.patchState({ processId });
|
||||
}
|
||||
|
||||
// Updaten der QueryParams wenn diese sich ändern
|
||||
if (!isEqual(this.store.queryParams, queryParams)) {
|
||||
const params = { ...queryParams };
|
||||
delete params.scrollPos;
|
||||
this.store.setQueryParams({ params });
|
||||
}
|
||||
|
||||
const crumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'main']).pipe(first()).toPromise();
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this.breadcrumb.removeBreadcrumbsAfter(crumb.id, ['catalog']);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.articleSearchStore.disconnect();
|
||||
this.subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
checkInputFilter(filter: Filter[]) {
|
||||
this.articleSearchStore.setInputSelectorFilter({ filter });
|
||||
this.store.setInputSelectorFilter({ filter });
|
||||
}
|
||||
|
||||
checkMainFilter(filter: Filter[]) {
|
||||
this.articleSearchStore.setMainFilter({ filter });
|
||||
this.store.setMainFilter({ filter });
|
||||
}
|
||||
|
||||
setQueryHistory(recentQuery: string) {
|
||||
this.articleSearchStore.setQuery({ query: recentQuery });
|
||||
this.store.setQuery({ query: recentQuery });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { CacheService } from 'apps/core/cache/src/public-api';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { isEqual } from 'lodash';
|
||||
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, finalize, first, map, withLatestFrom } from 'rxjs/operators';
|
||||
import { ArticleSearchStore } from '../article-search-new.store';
|
||||
|
||||
@Component({
|
||||
@@ -27,6 +28,8 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy {
|
||||
|
||||
trackByItemId = (item: ItemDTO) => item.id;
|
||||
|
||||
private subscriptions = new Subscription();
|
||||
|
||||
constructor(
|
||||
private store: ArticleSearchStore,
|
||||
private route: ActivatedRoute,
|
||||
@@ -36,58 +39,53 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
let previousProcessId: number;
|
||||
this.store.connect(this.route, {
|
||||
beforeParamsChange: async (current, previous) => {
|
||||
if (previousProcessId) {
|
||||
this.cache.set(previous, this.store.items);
|
||||
await this.updateBreadcrumbs(previousProcessId, {
|
||||
...previous,
|
||||
scrollPos: String(this.scrollContainer.measureScrollOffset('top')),
|
||||
this.subscriptions.add(
|
||||
combineLatest([this.application.activatedProcessId$, this.route.queryParams])
|
||||
.pipe(debounceTime(0))
|
||||
.subscribe(async ([processId, queryParams]) => {
|
||||
// Wenn ein Prozess bereits zugewiesen ist und der Prozess sich ändert
|
||||
// Speicher Ergebnisse in den Cache und Update Breadcrumb Params
|
||||
if (this.store?.processId !== processId) {
|
||||
this.cacheCurrentItems();
|
||||
await this.updateBreadcrumbs();
|
||||
}
|
||||
|
||||
// Setzen des aktuellen Prozesses
|
||||
this.store.patchState({ processId });
|
||||
|
||||
// Updaten der QueryParams wenn diese sich ändern
|
||||
// scrollPos muss entfernt werden um die items anhand der QueryParams zu cachen
|
||||
if (!isEqual(this.store.queryParams, queryParams)) {
|
||||
const params = { ...queryParams };
|
||||
delete params.scrollPos;
|
||||
const items = this.cache.get<ItemDTO[]>(params);
|
||||
this.store.setQueryParams({ params });
|
||||
this.store.setItems({ items });
|
||||
|
||||
this.store.search({ reload: true });
|
||||
}
|
||||
|
||||
// Nach dem setzen der Items im store an die letzte Position scrollen
|
||||
this.scrollTop(Number(queryParams.scrollPos ?? 0));
|
||||
|
||||
// Fügt Breadcrumb hinzu falls dieser noch nicht vorhanden ist
|
||||
this.breadcrumb.addBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: `${this.store.query} (Lade Ergebnisse)`,
|
||||
path: '/product/search/results',
|
||||
params: queryParams,
|
||||
tags: ['catalog', 'filter', 'results'],
|
||||
});
|
||||
}
|
||||
previousProcessId = this.application.activatedProcessId;
|
||||
|
||||
const params = { ...current };
|
||||
delete params['scrollPos'];
|
||||
return params;
|
||||
},
|
||||
afterParamsChange: async (params, original) => {
|
||||
const items = this.cache.get<ItemDTO[]>(params);
|
||||
if (items) {
|
||||
this.store.setItems({ items });
|
||||
}
|
||||
this.store.search({ reload: true });
|
||||
|
||||
const scrollPos = Number(original?.scrollPos);
|
||||
if (scrollPos !== null) {
|
||||
this.scrollTop(scrollPos);
|
||||
}
|
||||
},
|
||||
connected: () => {
|
||||
const { query, queryParams, hits } = this.store;
|
||||
|
||||
this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this.application.activatedProcessId,
|
||||
name: `${query} (${hits ? hits : 'Lade'} Ergebnisse)`,
|
||||
path: '/product/search/results',
|
||||
params: queryParams,
|
||||
tags: ['catalog', 'filter', 'results'],
|
||||
});
|
||||
},
|
||||
disconnected: () => {
|
||||
this.cache.set(this.store.queryParams, this.store.items);
|
||||
this.updateBreadcrumbs(previousProcessId, {
|
||||
...this.store.queryParams,
|
||||
scrollPos: String(this.scrollContainer.measureScrollOffset('top')),
|
||||
});
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.store.disconnect();
|
||||
this.loading$.complete();
|
||||
this.subscriptions?.unsubscribe();
|
||||
|
||||
this.cacheCurrentItems();
|
||||
this.updateBreadcrumbs();
|
||||
}
|
||||
|
||||
scrollTop(scrollPos: number) {
|
||||
@@ -101,11 +99,23 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async updateBreadcrumbs(processId: number, params: StringDictionary<string>) {
|
||||
async updateBreadcrumbs() {
|
||||
const scrollPos = this.scrollContainer.measureScrollOffset('top');
|
||||
const processId = this.store.processId;
|
||||
const queryParams = { ...this.store.queryParams };
|
||||
|
||||
console.log('updateBreadcrumbs', { scrollPos, processId });
|
||||
const crumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'filter', 'results']).pipe(first()).toPromise();
|
||||
|
||||
const params = { ...queryParams, scrollPos };
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this.breadcrumb.patchBreadcrumb(crumb.id, { params: { ...params } });
|
||||
this.breadcrumb.patchBreadcrumb(crumb.id, { params });
|
||||
}
|
||||
}
|
||||
|
||||
cacheCurrentItems() {
|
||||
console.log('cacheCurrentItems');
|
||||
this.cache.set(this.store.queryParams, this.store.items);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user