From 1f5db8849d2e0c78b2a37f9cc4a9202cec6e7383 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Thu, 19 Oct 2023 12:50:28 +0200 Subject: [PATCH] fix: t#2860 workflow redirections --- .../apps/taiga/src/app/app-routing.module.ts | 3 +- .../+state/effects/project.effects.spec.ts | 31 +++++++++- .../+state/effects/project.effects.ts | 30 +++++++++- .../+state/effects/kanban.effects.spec.ts | 2 +- .../+state/effects/kanban.effects.ts | 2 +- .../project-feature-kanban-routing.module.ts | 19 ------ .../project-feature-kanban.component.ts | 45 +++++--------- .../project-navigation-menu.component.html | 2 +- ...-view-setter.component-kanban.component.ts | 11 +++- .../project-feature-view-setter.component.ts | 58 +++++++++++++++---- 10 files changed, 130 insertions(+), 73 deletions(-) delete mode 100644 javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban-routing.module.ts diff --git a/javascript/apps/taiga/src/app/app-routing.module.ts b/javascript/apps/taiga/src/app/app-routing.module.ts index 566e36b68..2ce370227 100644 --- a/javascript/apps/taiga/src/app/app-routing.module.ts +++ b/javascript/apps/taiga/src/app/app-routing.module.ts @@ -27,8 +27,9 @@ function isViewSetterKanbaStory( ) { const story = ':slug/stories/:storyRef'; const kanban = ':slug/kanban'; + const workflow = ':slug/kanban/:workflow'; - const urls = [story, kanban]; + const urls = [story, kanban, workflow]; const findUrl = (it: ActivatedRouteSnapshot): boolean => { const finded = !!urls.find((url) => it.routeConfig?.path === url); diff --git a/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.spec.ts b/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.spec.ts index b1d6abb70..ea9e41db7 100644 --- a/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.spec.ts +++ b/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.spec.ts @@ -6,7 +6,7 @@ * Copyright (c) 2023-present Kaleidos INC */ -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { randUuid, randCompanyName } from '@ngneat/falso'; import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; import { provideMockActions } from '@ngrx/effects/testing'; @@ -40,6 +40,8 @@ import { import { selectCurrentProject } from '../selectors/project.selectors'; import { ProjectEffects } from './project.effects'; import { HttpErrorResponse } from '@angular/common/http'; +import { selectRouteParams } from '~/app/router-selectors'; +import { Location } from '@angular/common'; describe('ProjectEffects', () => { let actions$: Observable; @@ -53,8 +55,12 @@ describe('ProjectEffects', () => { provideMockActions(() => actions$), { provide: WsService, useValue: WsServiceMock }, provideMockStore({ initialState: {} }), + { + provide: Router, + useValue: { navigate: jest.fn(), url: '/some-url/kanban/old-slug' }, + }, ], - mocks: [ProjectApiService, AppService, Router], + mocks: [ProjectApiService, AppService, ActivatedRoute, Location], }); beforeEach(() => { @@ -330,5 +336,26 @@ describe('ProjectEffects', () => { expect(effects.updateWorkflow$).toBeObservable(expected); }); + + it('should update workflow slug in the URL', () => { + const workflow = WorkflowMockFactory(); + const location = spectator.inject(Location); + const effects = spectator.inject(ProjectEffects); + + const action = projectEventActions.updateWorkflow({ + workflow: workflow, + }); + + actions$ = hot('-a', { a: action }); + + const params = { workflow: 'old-slug' }; + store.overrideSelector(selectRouteParams, params); + + expect(effects.updateWorkflowSlug$).toSatisfyOnFlush(() => { + expect(location.go).toHaveBeenCalledWith( + `/some-url/kanban/${workflow.slug}` + ); + }); + }); }); }); diff --git a/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.ts b/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.ts index 58fc97ab9..427cf5034 100644 --- a/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.ts +++ b/javascript/apps/taiga/src/app/modules/project/data-access/+state/effects/project.effects.ts @@ -9,7 +9,7 @@ import { Location } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import { fetch, pessimisticUpdate } from '@ngrx/router-store/data-persistence'; import { Store } from '@ngrx/store'; @@ -26,7 +26,7 @@ import { } from 'rxjs/operators'; import { selectUser } from '~/app/modules/auth/data-access/+state/selectors/auth.selectors'; import * as ProjectOverviewActions from '~/app/modules/project/feature-overview/data-access/+state/actions/project-overview.actions'; -import { selectUrl } from '~/app/router-selectors'; +import { selectRouteParams, selectUrl } from '~/app/router-selectors'; import { AppService } from '~/app/services/app.service'; import { RevokeInvitationService } from '~/app/services/revoke-invitation.service'; import { invitationProjectActions } from '~/app/shared/invite-user-modal/data-access/+state/actions/invitation.action'; @@ -240,6 +240,29 @@ export class ProjectEffects { ); }); + public updateWorkflowSlug$ = createEffect( + () => { + return this.actions$.pipe( + ofType(projectEventActions.updateWorkflow), + concatLatestFrom(() => [ + this.store.select(selectRouteParams).pipe(filterNil()), + ]), + filter(([action, params]) => params.workflow !== action.workflow.slug), + tap(([action, params]) => { + const paramWorkflow = params.workflow as string; + + void this.location.go( + this.router.url.replace( + `kanban/${paramWorkflow}`, + `kanban/${action.workflow.slug}` + ) + ); + }) + ); + }, + { dispatch: false } + ); + public initAssignUser$ = createEffect(() => { return this.actions$.pipe( ofType(ProjectActions.initAssignUser), @@ -493,6 +516,7 @@ export class ProjectEffects { private router: Router, private revokeInvitationService: RevokeInvitationService, private store: Store, - private location: Location + private location: Location, + private route: ActivatedRoute ) {} } diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.spec.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.spec.ts index 07b8a5ca0..b113d9a43 100644 --- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.spec.ts +++ b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.spec.ts @@ -71,7 +71,7 @@ describe('ProjectEffects', () => { projectApiService.getWorkflow.mockReturnValue(cold('-b|', { b: workflow })); actions$ = hot('-a', { - a: KanbanActions.initKanban({ workflow: workflow.slug }), + a: KanbanActions.loadWorkflowKanban({ workflow: workflow.slug }), }); const expected = cold('--a', { diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts index 2bfc41aa4..c26c2124c 100644 --- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts +++ b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts @@ -45,7 +45,7 @@ import { export class KanbanEffects { public loadKanbanWorkflows$ = createEffect(() => { return this.actions$.pipe( - ofType(KanbanActions.initKanban, KanbanActions.loadWorkflowKanban), + ofType(KanbanActions.loadWorkflowKanban), concatLatestFrom(() => [ this.store.select(selectCurrentProject).pipe(filterNil()), ]), diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban-routing.module.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban-routing.module.ts deleted file mode 100644 index f7bfe170a..000000000 --- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban-routing.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Copyright (c) 2023-present Kaleidos INC - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { ProjectFeatureKanbanComponent } from './project-feature-kanban.component'; - -const routes: Routes = [{ path: '', component: ProjectFeatureKanbanComponent }]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class ProjectFeatureKanbanRoutingModule {} diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts index bf89725ac..06359d2a0 100644 --- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts +++ b/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts @@ -7,7 +7,12 @@ */ import { CommonModule, Location } from '@angular/common'; -import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + ViewChild, +} from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslocoDirective } from '@ngneat/transloco'; @@ -30,16 +35,7 @@ import { WorkflowStatus, } from '@taiga/data'; import { ModalComponent } from '@taiga/ui/modal/components'; -import { - combineLatest, - distinctUntilChanged, - filter, - map, - merge, - pairwise, - skip, - take, -} from 'rxjs'; +import { combineLatest, filter, map, merge, pairwise, take } from 'rxjs'; import * as ProjectActions from '~/app/modules/project/data-access/+state/actions/project.actions'; import { selectCurrentProject, @@ -119,6 +115,13 @@ export class ProjectFeatureKanbanComponent { @ViewChild(ProjectFeatureStoryWrapperSideViewComponent) public projectFeatureStoryWrapperSideViewComponent?: ProjectFeatureStoryWrapperSideViewComponent; + @Input() + public set workflowSlug(workflow: Workflow['slug']) { + if (workflow !== this.state.get('workflow')?.slug) { + this.store.dispatch(KanbanActions.loadWorkflowKanban({ workflow })); + } + } + public invitePeopleModal = false; public kanbanWidth = 0; public model$ = this.state.select(); @@ -146,26 +149,6 @@ export class ProjectFeatureKanbanComponent { return; } - // Load on init kanban page. Not on every reload - const workflow: Workflow['slug'] = this.route.snapshot.params[ - 'workflow' - ] as Workflow['slug']; - if (workflow) { - this.store.dispatch(KanbanActions.initKanban({ workflow })); - } - - this.route.paramMap - .pipe( - map((params) => { - return params.get('workflow') ?? 'main'; - }), - distinctUntilChanged(), - skip(1) - ) - .subscribe((workflow: Workflow['slug']) => { - this.store.dispatch(KanbanActions.loadWorkflowKanban({ workflow })); - }); - this.checkInviteModalStatus(); this.state.connect( 'loadingWorkflow', diff --git a/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html b/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html index 5fec153db..6c17491cd 100644 --- a/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html +++ b/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html @@ -163,7 +163,7 @@