diff --git a/src/app/features/collections/collections.routes.ts b/src/app/features/collections/collections.routes.ts index e263b82c6..dbb9388eb 100644 --- a/src/app/features/collections/collections.routes.ts +++ b/src/app/features/collections/collections.routes.ts @@ -4,6 +4,7 @@ import { Routes } from '@angular/router'; import { AddToCollectionState } from '@osf/features/collections/store/add-to-collection'; import { CollectionsState } from '@osf/features/collections/store/collections'; +import { ConfirmLeavingGuard } from '@shared/guards'; import { ContributorsState, ProjectsState } from '@shared/stores'; import { ModeratorsState } from '../moderation/store/moderation'; @@ -40,6 +41,7 @@ export const collectionsRoutes: Routes = [ (mod) => mod.AddToCollectionComponent ), providers: [provideStates([ProjectsState, CollectionsState, AddToCollectionState, ContributorsState])], + canDeactivate: [ConfirmLeavingGuard], }, { path: ':id/moderation', diff --git a/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts b/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts index a00b6cffa..d771da099 100644 --- a/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts +++ b/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts @@ -6,6 +6,8 @@ import { Button } from 'primeng/button'; import { DialogService } from 'primeng/dynamicdialog'; import { Stepper } from 'primeng/stepper'; +import { Observable } from 'rxjs'; + import { ChangeDetectionStrategy, Component, @@ -27,6 +29,7 @@ import { } from '@osf/features/collections/store/add-to-collection/add-to-collection.actions'; import { CollectionsSelectors, GetCollectionProvider } from '@osf/features/collections/store/collections'; import { LoadingSpinnerComponent } from '@shared/components'; +import { CanDeactivateComponent } from '@shared/models'; import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors'; import { @@ -55,7 +58,7 @@ import { providers: [DialogService], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AddToCollectionComponent { +export class AddToCollectionComponent implements CanDeactivateComponent { private readonly router = inject(Router); private readonly route = inject(ActivatedRoute); private readonly destroyRef = inject(DestroyRef); @@ -70,6 +73,7 @@ export class AddToCollectionComponent { protected selectedProject = select(ProjectsSelectors.getSelectedProject); protected currentUser = select(UserSelectors.getCurrentUser); protected providerId = signal(''); + protected isSubmitted = signal(false); protected projectMetadataSaved = signal(false); protected projectContributorsSaved = signal(false); protected collectionMetadataSaved = signal(false); @@ -123,6 +127,7 @@ export class AddToCollectionComponent { collectionMetadata: this.collectionMetadataForm.value || {}, userId: this.currentUser()?.id || '', }; + this.isSubmitted.set(true); const dialogRef = this.dialogService.open(AddToCollectionConfirmationDialogComponent, { width: '500px', @@ -136,6 +141,7 @@ export class AddToCollectionComponent { dialogRef.onClose.subscribe((result) => { if (result) { + this.isSubmitted.set(false); this.router.navigate(['/my-projects', this.selectedProject()?.id, 'overview']); } }); @@ -165,4 +171,8 @@ export class AddToCollectionComponent { $event.preventDefault(); return false; } + + canDeactivate(): Observable | boolean { + return this.isSubmitted(); + } } diff --git a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts index cc859693d..ed402f964 100644 --- a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts +++ b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts @@ -40,7 +40,7 @@ import { License } from '@shared/models'; import { Project } from '@shared/models/projects'; import { InterpolatePipe } from '@shared/pipes'; import { ToastService } from '@shared/services'; -import { GetAllContributors, UpdateProjectMetadata } from '@shared/stores'; +import { ClearProjects, GetAllContributors, UpdateProjectMetadata } from '@shared/stores'; import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors'; @Component({ @@ -95,6 +95,7 @@ export class ProjectMetadataStepComponent { updateCollectionSubmissionMetadata: UpdateProjectMetadata, getAllContributors: GetAllContributors, getCollectionLicenses: GetCollectionLicenses, + clearProjects: ClearProjects, }); protected readonly projectMetadataForm: FormGroup = this.formService.createForm(); @@ -231,5 +232,11 @@ export class ProjectMetadataStepComponent { this.initialProjectMetadataFormValues.set(JSON.stringify(formValue)); } }); + + effect(() => { + this.destroyRef.onDestroy(() => { + this.actions.clearProjects(); + }); + }); } } diff --git a/src/app/features/collections/components/index.ts b/src/app/features/collections/components/index.ts index 61ceb51e6..4afafff2e 100644 --- a/src/app/features/collections/components/index.ts +++ b/src/app/features/collections/components/index.ts @@ -1,3 +1,4 @@ +export { AddToCollectionComponent } from './add-to-collection/add-to-collection.component'; export { CollectionsFilterChipsComponent } from './collections-filter-chips/collections-filter-chips.component'; export { CollectionsFiltersComponent } from './collections-filters/collections-filters.component'; export { CollectionsHelpDialogComponent } from './collections-help-dialog/collections-help-dialog.component'; diff --git a/src/app/features/preprints/guards/index.ts b/src/app/features/preprints/guards/index.ts deleted file mode 100644 index 05326ccfb..000000000 --- a/src/app/features/preprints/guards/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './confirm-leaving.guard'; diff --git a/src/app/features/preprints/models/index.ts b/src/app/features/preprints/models/index.ts index 5889a3cda..8df6fdc96 100644 --- a/src/app/features/preprints/models/index.ts +++ b/src/app/features/preprints/models/index.ts @@ -1,4 +1,3 @@ -export * from './can-deactivate.interface'; export * from './preprint.models'; export * from './preprint-json-api.models'; export * from './preprint-licenses-json-api.models'; diff --git a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts index d100941c5..d160c27d1 100644 --- a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts +++ b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts @@ -31,7 +31,6 @@ import { } from '@osf/features/preprints/components'; import { submitPreprintSteps } from '@osf/features/preprints/constants'; import { PreprintSteps } from '@osf/features/preprints/enums'; -import { CanDeactivateComponent } from '@osf/features/preprints/models'; import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; import { DeletePreprint, @@ -39,7 +38,7 @@ import { ResetState, SetSelectedPreprintProviderId, } from '@osf/features/preprints/store/preprint-stepper'; -import { StepOption } from '@osf/shared/models'; +import { CanDeactivateComponent, StepOption } from '@osf/shared/models'; import { StepperComponent } from '@shared/components'; import { BrandService } from '@shared/services'; import { BrowserTabHelper, HeaderStyleHelper, IS_WEB } from '@shared/utils'; diff --git a/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.ts b/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.ts index 377d4e285..16a6dbd9a 100644 --- a/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.ts +++ b/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.ts @@ -30,7 +30,6 @@ import { } from '@osf/features/preprints/components'; import { updatePreprintSteps } from '@osf/features/preprints/constants'; import { PreprintSteps } from '@osf/features/preprints/enums'; -import { CanDeactivateComponent } from '@osf/features/preprints/models'; import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; import { FetchPreprintById, @@ -39,7 +38,7 @@ import { SetSelectedPreprintProviderId, } from '@osf/features/preprints/store/preprint-stepper'; import { StepperComponent } from '@shared/components'; -import { StepOption } from '@shared/models'; +import { CanDeactivateComponent, StepOption } from '@shared/models'; import { BrandService } from '@shared/services'; import { BrowserTabHelper, HeaderStyleHelper, IS_WEB } from '@shared/utils'; diff --git a/src/app/features/preprints/preprints.routes.ts b/src/app/features/preprints/preprints.routes.ts index 5e68a0c62..0e8295325 100644 --- a/src/app/features/preprints/preprints.routes.ts +++ b/src/app/features/preprints/preprints.routes.ts @@ -2,13 +2,13 @@ import { provideStates } from '@ngxs/store'; import { Routes } from '@angular/router'; -import { ConfirmLeavingGuard } from '@osf/features/preprints/guards'; import { PreprintsComponent } from '@osf/features/preprints/preprints.component'; import { PreprintProvidersState } from '@osf/features/preprints/store/preprint-providers'; import { PreprintStepperState } from '@osf/features/preprints/store/preprint-stepper'; import { PreprintsDiscoverState } from '@osf/features/preprints/store/preprints-discover'; import { PreprintsResourcesFiltersState } from '@osf/features/preprints/store/preprints-resources-filters'; import { PreprintsResourcesFiltersOptionsState } from '@osf/features/preprints/store/preprints-resources-filters-options'; +import { ConfirmLeavingGuard } from '@shared/guards'; import { ResourceType } from '@shared/enums'; import { ContributorsState, SubjectsState } from '@shared/stores'; diff --git a/src/app/features/preprints/guards/confirm-leaving.guard.ts b/src/app/shared/guards/confirm-leaving.guard.ts similarity index 76% rename from src/app/features/preprints/guards/confirm-leaving.guard.ts rename to src/app/shared/guards/confirm-leaving.guard.ts index a2111e594..1bdf3dab9 100644 --- a/src/app/features/preprints/guards/confirm-leaving.guard.ts +++ b/src/app/shared/guards/confirm-leaving.guard.ts @@ -3,10 +3,10 @@ import { Subject } from 'rxjs'; import { inject } from '@angular/core'; import { CanDeactivateFn } from '@angular/router'; -import { SubmitPreprintStepperComponent } from '@osf/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component'; +import { CanDeactivateComponent } from '@shared/models'; import { CustomConfirmationService } from '@shared/services'; -export const ConfirmLeavingGuard: CanDeactivateFn = (component) => { +export const ConfirmLeavingGuard: CanDeactivateFn = (component) => { const confirmationService = inject(CustomConfirmationService); const confirmationResultSubject = new Subject(); const confirmationResultObservable = confirmationResultSubject.asObservable(); diff --git a/src/app/shared/guards/index.ts b/src/app/shared/guards/index.ts new file mode 100644 index 000000000..c0ba5d4a2 --- /dev/null +++ b/src/app/shared/guards/index.ts @@ -0,0 +1 @@ +export { ConfirmLeavingGuard } from './confirm-leaving.guard'; diff --git a/src/app/features/preprints/models/can-deactivate.interface.ts b/src/app/shared/models/can-deactivate.interface.ts similarity index 100% rename from src/app/features/preprints/models/can-deactivate.interface.ts rename to src/app/shared/models/can-deactivate.interface.ts diff --git a/src/app/shared/models/index.ts b/src/app/shared/models/index.ts index b106c200f..9e24c8269 100644 --- a/src/app/shared/models/index.ts +++ b/src/app/shared/models/index.ts @@ -1,6 +1,7 @@ export * from './addons'; export * from './brand.json-api.model'; export * from './brand.model'; +export * from './can-deactivate.interface'; export * from './charts'; export * from './confirmation-options.model'; export * from './contributors'; diff --git a/src/app/shared/stores/projects/projects.actions.ts b/src/app/shared/stores/projects/projects.actions.ts index 0abac435e..e5fa9b65a 100644 --- a/src/app/shared/stores/projects/projects.actions.ts +++ b/src/app/shared/stores/projects/projects.actions.ts @@ -21,3 +21,7 @@ export class UpdateProjectMetadata { constructor(public metadata: ProjectMetadataUpdatePayload) {} } + +export class ClearProjects { + static readonly type = '[Projects] Clear Selected Project'; +} diff --git a/src/app/shared/stores/projects/projects.state.ts b/src/app/shared/stores/projects/projects.state.ts index 3b26b683d..304d5793e 100644 --- a/src/app/shared/stores/projects/projects.state.ts +++ b/src/app/shared/stores/projects/projects.state.ts @@ -6,23 +6,31 @@ import { inject, Injectable } from '@angular/core'; import { handleSectionError } from '@core/handlers'; import { ProjectsService } from '@shared/services/projects.service'; -import { GetProjects, ProjectsStateModel, SetSelectedProject, UpdateProjectMetadata } from '@shared/stores'; +import { + ClearProjects, + GetProjects, + ProjectsStateModel, + SetSelectedProject, + UpdateProjectMetadata, +} from '@shared/stores'; + +const PROJECTS_DEFAULTS: ProjectsStateModel = { + projects: { + data: [], + isLoading: false, + error: null, + }, + selectedProject: { + data: null, + isLoading: false, + isSubmitting: false, + error: null, + }, +}; @State({ name: 'projects', - defaults: { - projects: { - data: [], - isLoading: false, - error: null, - }, - selectedProject: { - data: null, - isLoading: false, - isSubmitting: false, - error: null, - }, - }, + defaults: PROJECTS_DEFAULTS, }) @Injectable() export class ProjectsState { @@ -89,4 +97,9 @@ export class ProjectsState { catchError((error) => handleSectionError(ctx, 'selectedProject', error)) ); } + + @Action(ClearProjects) + clearProjects(ctx: StateContext) { + ctx.patchState(PROJECTS_DEFAULTS); + } }