diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts index 0b3e928f9..c269b82e1 100644 --- a/src/app/app.routes.server.ts +++ b/src/app/app.routes.server.ts @@ -137,6 +137,10 @@ export const serverRoutes: ServerRoute[] = [ path: ':id/overview', renderMode: RenderMode.Server, }, + { + path: ':id/metadata/:recordId', + renderMode: RenderMode.Server, + }, { path: ':id/files/**', renderMode: RenderMode.Server, diff --git a/src/app/features/files/pages/file-detail/file-detail.component.ts b/src/app/features/files/pages/file-detail/file-detail.component.ts index 80afa822d..1ec9d858f 100644 --- a/src/app/features/files/pages/file-detail/file-detail.component.ts +++ b/src/app/features/files/pages/file-detail/file-detail.component.ts @@ -19,6 +19,7 @@ import { effect, HostBinding, inject, + OnDestroy, OnInit, signal, } from '@angular/core'; @@ -96,7 +97,7 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, providers: [DatePipe], }) -export class FileDetailComponent implements OnInit { +export class FileDetailComponent implements OnInit, OnDestroy { @HostBinding('class') classes = 'flex flex-column flex-1 w-full h-full'; readonly store = inject(Store); @@ -291,6 +292,12 @@ export class FileDetailComponent implements OnInit { this.signpostingService.addSignposting(this.fileGuid); } + ngOnDestroy(): void { + if (this.fileGuid) { + this.signpostingService.removeSignpostingLinkTags(); + } + } + getIframeLink(version: string) { const url = this.getMfrUrlWithVersion(version); if (url) { diff --git a/src/app/features/metadata/metadata.component.ts b/src/app/features/metadata/metadata.component.ts index e20e097f7..4914f6991 100644 --- a/src/app/features/metadata/metadata.component.ts +++ b/src/app/features/metadata/metadata.component.ts @@ -11,6 +11,7 @@ import { DestroyRef, effect, inject, + OnDestroy, OnInit, signal, } from '@angular/core'; @@ -24,6 +25,7 @@ import { MetadataResourceEnum } from '@osf/shared/enums/metadata-resource.enum'; import { ResourceType } from '@osf/shared/enums/resource-type.enum'; import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; +import { SignpostingService } from '@osf/shared/services/signposting.service'; import { ToastService } from '@osf/shared/services/toast.service'; import { CollectionsSelectors, GetProjectSubmissions } from '@osf/shared/stores/collections'; import { @@ -117,7 +119,7 @@ import { styleUrl: './metadata.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MetadataComponent implements OnInit { +export class MetadataComponent implements OnInit, OnDestroy { private readonly activeRoute = inject(ActivatedRoute); private readonly router = inject(Router); private readonly destroyRef = inject(DestroyRef); @@ -125,6 +127,7 @@ export class MetadataComponent implements OnInit { private readonly toastService = inject(ToastService); private readonly customConfirmationService = inject(CustomConfirmationService); private readonly environment = inject(ENVIRONMENT); + private readonly signpostingService = inject(SignpostingService); private resourceId = ''; @@ -264,12 +267,18 @@ export class MetadataComponent implements OnInit { this.actions.getCedarTemplates(); this.actions.fetchSelectedSubjects(this.resourceId, this.resourceType()); + this.signpostingService.addMetadataSignposting(this.resourceId); + if (this.isProjectType()) { this.actions.getProjectSubmissions(this.resourceId); } } } + ngOnDestroy(): void { + this.signpostingService.removeSignpostingLinkTags(); + } + onTabChange(tabId: string | number): void { const tab = this.tabs().find((x) => x.id === tabId.toString()); diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts index abb998716..63cc1772f 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts @@ -317,6 +317,8 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { this.actions.clearCurrentProvider(); } + this.signpostingService.removeSignpostingLinkTags(); + this.helpScoutService.unsetResourceType(); } diff --git a/src/app/features/project/overview/project-overview.component.ts b/src/app/features/project/overview/project-overview.component.ts index 7b9324e14..cc22bd778 100644 --- a/src/app/features/project/overview/project-overview.component.ts +++ b/src/app/features/project/overview/project-overview.component.ts @@ -16,6 +16,7 @@ import { effect, HostBinding, inject, + OnDestroy, OnInit, PLATFORM_ID, } from '@angular/core'; @@ -94,7 +95,7 @@ import { ], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ProjectOverviewComponent implements OnInit { +export class ProjectOverviewComponent implements OnInit, OnDestroy { @HostBinding('class') classes = 'flex flex-1 flex-column w-full h-full'; private readonly route = inject(ActivatedRoute); @@ -199,6 +200,10 @@ export class ProjectOverviewComponent implements OnInit { } } + ngOnDestroy(): void { + this.signpostingService.removeSignpostingLinkTags(); + } + handleOpenMakeDecisionDialog() { this.customDialogService .open(MakeDecisionDialogComponent, { diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.ts b/src/app/features/registry/pages/registry-overview/registry-overview.component.ts index d6199402c..d8331afbf 100644 --- a/src/app/features/registry/pages/registry-overview/registry-overview.component.ts +++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.ts @@ -15,6 +15,7 @@ import { effect, HostBinding, inject, + OnDestroy, OnInit, signal, } from '@angular/core'; @@ -77,7 +78,7 @@ import { styleUrl: './registry-overview.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RegistryOverviewComponent implements OnInit { +export class RegistryOverviewComponent implements OnInit, OnDestroy { @HostBinding('class') classes = 'flex-1 flex flex-column w-full h-full'; private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); @@ -176,6 +177,10 @@ export class RegistryOverviewComponent implements OnInit { this.signpostingService.addSignposting(this.registryId()); } + ngOnDestroy(): void { + this.signpostingService.removeSignpostingLinkTags(); + } + openRevision(revisionIndex: number): void { this.selectedRevisionIndex.set(revisionIndex); } diff --git a/src/app/shared/services/signposting.service.ts b/src/app/shared/services/signposting.service.ts index 9731cc9f4..14ddd2170 100644 --- a/src/app/shared/services/signposting.service.ts +++ b/src/app/shared/services/signposting.service.ts @@ -21,7 +21,30 @@ export class SignpostingService { this.addSignpostingLinkTags(links); } - private generateSignpostingLinks(guid: string): SignpostingLink[] { + addMetadataSignposting(guid: string): void { + const links = this.generateSignpostingLinks(guid, true); + + this.addSignpostingLinkHeaders(links); + this.addSignpostingLinkTags(links); + } + + removeSignpostingLinkTags(): void { + const linkElements = this.document.head.querySelectorAll('link[rel="linkset"], link[rel="describes"]'); + linkElements.forEach((linkElement) => { + this.renderer.removeChild(this.document.head, linkElement); + }); + } + + private generateSignpostingLinks(guid: string, isMetadata?: boolean): SignpostingLink[] { + if (isMetadata) { + return [ + { + rel: 'describes', + href: `${this.environment.webUrl}/${guid}/`, + type: 'text/html', + }, + ]; + } const baseUrl = `${this.environment.webUrl}/metadata/${guid}/`; return [