Skip to content

Commit

Permalink
feat: add store to coupling feature
Browse files Browse the repository at this point in the history
  • Loading branch information
manfredsteyer committed Sep 28, 2024
1 parent 76656b3 commit 28b7c31
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .detective/hash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b710a462a265c9bf0de882fc6886c806a46d2279, v1.1.2
37698fb21ae14ef200051ddf81bbee302fd731c1, v1.1.2
6 changes: 6 additions & 0 deletions .detective/log
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
"Manfred Steyer <[email protected]>,Fri Sep 27 22:48:19 2024 +0200 76656b36d327a6c9f45040294c99cc83a30d84b0,refactor(frontend): remove unnecessary rendering effect in hotspot component"
1 1 .detective/hash
9 0 .detective/log
5 1 apps/frontend/src/app/features/hotspot/hotspot.component.html
26 7 apps/frontend/src/app/features/hotspot/hotspot.component.ts

"Manfred Steyer <[email protected]>,Fri Sep 27 22:20:25 2024 +0200 5547a7c4196a2ba80acb704250c0a76d9e1101c1,feat: add limits store to team alignment and hotspots"
1 1 .detective/hash
8 8 .detective/log
Expand Down
13 changes: 11 additions & 2 deletions apps/frontend/src/app/features/coupling/coupling.component.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
<div class="forensic-filter">
<div>
<mat-checkbox class="mr30" [(ngModel)]="groupByFolder"
<mat-checkbox
class="mr30"
[ngModel]="groupByFolder()"
(ngModelChange)="updateFilter({ groupByFolder: $event })"
>Group by folder</mat-checkbox
>

<mat-form-field appearance="outline" class="connections">
<mat-label>Min. Connections</mat-label>
<input type="number" matInput min="1" [(ngModel)]="minConnections" />
<input
type="number"
matInput
min="1"
[ngModel]="minConnections()"
(ngModelChange)="updateFilter({ minConnections: $event })"
/>
</mat-form-field>

<mat-icon
Expand Down
73 changes: 26 additions & 47 deletions apps/frontend/src/app/features/coupling/coupling.component.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,25 @@
import { Component, computed, inject, input, signal } from '@angular/core';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { Component, computed, inject, input } from '@angular/core';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatTooltipModule } from '@angular/material/tooltip';
import {
catchError,
combineLatest,
Observable,
of,
startWith,
switchMap,
} from 'rxjs';

import { CouplingService } from '../../data/coupling.service';
import { combineLatest, startWith } from 'rxjs';

import { LimitsStore } from '../../data/limits.store';
import { StatusStore } from '../../data/status.store';
import {
CouplingResult,
initCouplingResult,
} from '../../model/coupling-result';
import { CouplingResult } from '../../model/coupling-result';
import { GraphType } from '../../model/graph-type';
import { initLimits, Limits, LimitType } from '../../model/limits';
import { Limits } from '../../model/limits';
import { Graph, CouplingNodeDefinition } from '../../ui/graph/graph';
import { GraphComponent } from '../../ui/graph/graph.component';
import { LimitsComponent } from '../../ui/limits/limits.component';
import { debounceTimeSkipFirst } from '../../utils/debounce';
import { injectShowError } from '../../utils/error-handler';
import { EventService } from '../../utils/event.service';

import { CouplingFilter, CouplingStore } from './coupling.store';
import { createGroups, createNodes, createEdges } from './graph.adapter';

const STRUCTURE_TIP =
Expand All @@ -55,47 +44,39 @@ const COUPLING_TIP =
styleUrl: './coupling.component.css',
})
export class CouplingComponent {
private statusStore = inject(StatusStore);
private limitsStore = inject(LimitsStore);
private couplingStore = inject(CouplingStore);

private couplingService = inject(CouplingService);
private eventService = inject(EventService);
private statusStore = inject(StatusStore);
private showError = injectShowError();

type = input<GraphType>('structure');
toolTip = computed(() =>
this.type() === 'structure' ? STRUCTURE_TIP : COUPLING_TIP
);

totalCommits = this.statusStore.commits;
groupByFolder = signal(false);

limits = this.limitsStore.limits;
minConnections = signal(1);

couplingResult$ = combineLatest({
groupByFolder = this.couplingStore.filter.groupByFolder;
minConnections = this.couplingStore.filter.minConnections;

couplingResult = this.couplingStore.couplingResult;
graph = computed(() => this.toGraph(this.couplingResult()));

toolTip = computed(() =>
this.type() === 'structure' ? STRUCTURE_TIP : COUPLING_TIP
);

loadOptions$ = combineLatest({
limits: toObservable(this.limits).pipe(debounceTimeSkipFirst(300)),
filterChanged: this.eventService.filterChanged.pipe(startWith(null)),
type: toObservable(this.type),
}).pipe(switchMap((combi) => this.loadCoupling(combi)));
}).pipe(takeUntilDestroyed());

couplingResult = toSignal(this.couplingResult$, {
initialValue: initCouplingResult,
});

graph = computed(() => this.toGraph(this.couplingResult()));
constructor() {
this.couplingStore.rxLoad(this.loadOptions$);
}

private loadCoupling(combi: {
limits: Limits;
filterChanged: void | null;
type: GraphType;
}): Observable<CouplingResult> {
return this.couplingService.load(combi.type, combi.limits).pipe(
catchError((err) => {
this.showError(err);
return of(initCouplingResult);
})
);
updateFilter(filter: Partial<CouplingFilter>) {
this.couplingStore.updateFilter(filter);
}

updateLimits(limits: Limits): void {
Expand All @@ -105,8 +86,6 @@ export class CouplingComponent {
toGraph(result: CouplingResult): Graph {
result.matrix = clearSelfLinks(result.matrix);

console.log('result', result);

const groupNodes: CouplingNodeDefinition[] = this.groupByFolder()
? createGroups(result.dimensions)
: [];
Expand Down
66 changes: 66 additions & 0 deletions apps/frontend/src/app/features/coupling/coupling.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { inject } from '@angular/core';
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { catchError, Observable, of, pipe, switchMap, tap } from 'rxjs';

import { CouplingService } from '../../data/coupling.service';
import {
CouplingResult,
initCouplingResult,
} from '../../model/coupling-result';
import { GraphType } from '../../model/graph-type';
import { Limits } from '../../model/limits';
import { injectShowError } from '../../utils/error-handler';

export type CouplingFilter = {
groupByFolder: boolean;
minConnections: number;
};

export type LoadOptions = {
type: GraphType;
limits: Limits;
};

export const CouplingStore = signalStore(
{ providedIn: 'root' },
withState({
filter: {
groupByFolder: false,
minConnections: 1,
} as CouplingFilter,
couplingResult: initCouplingResult,
}),
withMethods(
(
_store,
couplingService = inject(CouplingService),
showError = injectShowError()
) => ({
_loadCoupling(options: LoadOptions): Observable<CouplingResult> {
return couplingService.load(options.type, options.limits).pipe(
catchError((err) => {
showError(err);
return of(initCouplingResult);
})
);
},
})
),
withMethods((store) => ({
updateFilter(filter: Partial<CouplingFilter>) {
patchState(store, (state) => ({
filter: {
...state.filter,
...filter,
},
}));
},
rxLoad: rxMethod<LoadOptions>(
pipe(
switchMap((options) => store._loadCoupling(options)),
tap((couplingResult) => patchState(store, { couplingResult }))
)
),
}))
);

0 comments on commit 28b7c31

Please sign in to comment.