From c6a16f2286040f58cee1ced2509baa1e0b404035 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Fri, 26 Sep 2025 18:50:37 +0300 Subject: [PATCH 1/7] fix(ang-403): added wiki permissions --- src/app/core/constants/nav-items.constant.ts | 1 + .../linked-resources.component.html | 25 ++++---- .../overview-toolbar.component.html | 2 +- .../overview/project-overview.component.html | 14 ++-- .../overview/project-overview.component.ts | 12 +--- .../store/project-overview.selectors.ts | 17 +++++ .../registrations.component.html | 2 +- .../registrations/registrations.component.ts | 7 +- .../features/project/wiki/wiki.component.html | 6 +- .../features/project/wiki/wiki.component.ts | 9 ++- .../registry-overview.component.html | 1 + .../registry-wiki.component.html | 1 - .../resource-citations.component.html | 64 +++++++++---------- .../resource-metadata.component.html | 2 +- .../resource-metadata.component.ts | 1 + .../wiki/wiki-list/wiki-list.component.html | 7 +- .../wiki/wiki-list/wiki-list.component.ts | 2 +- .../mappers/components/components.mapper.ts | 14 ++-- .../registration/registration.mapper.ts | 4 +- .../current-resource.selectors.ts | 16 +++++ 20 files changed, 124 insertions(+), 83 deletions(-) diff --git a/src/app/core/constants/nav-items.constant.ts b/src/app/core/constants/nav-items.constant.ts index b5b1d2fc4..cfdc5b390 100644 --- a/src/app/core/constants/nav-items.constant.ts +++ b/src/app/core/constants/nav-items.constant.ts @@ -22,6 +22,7 @@ export const VIEW_ONLY_REGISTRY_MENU_ITEMS: string[] = [ 'registration-wiki', 'registration-analytics', 'registration-components', + 'registration-recent-activity', ]; export const PROJECT_MENU_ITEMS: MenuItem[] = [ diff --git a/src/app/features/project/overview/components/linked-resources/linked-resources.component.html b/src/app/features/project/overview/components/linked-resources/linked-resources.component.html index 676352d56..55736a974 100644 --- a/src/app/features/project/overview/components/linked-resources/linked-resources.component.html +++ b/src/app/features/project/overview/components/linked-resources/linked-resources.component.html @@ -21,18 +21,19 @@

{{ linkedResource.title }}

- -
- - -
+ @if (canEdit()) { +
+ + +
+ }
diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html index f67364529..5c702e0fe 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html @@ -23,7 +23,7 @@
} - @if (isCollectionsRoute() || hasViewOnly()) { + @if (isCollectionsRoute() || hasViewOnly() || !canEdit()) { @if (isPublic()) {
diff --git a/src/app/features/project/overview/project-overview.component.html b/src/app/features/project/overview/project-overview.component.html index 238841f18..0800aad51 100644 --- a/src/app/features/project/overview/project-overview.component.html +++ b/src/app/features/project/overview/project-overview.component.html @@ -17,7 +17,7 @@ [isCollectionsRoute]="isCollectionsRoute()" [currentResource]="currentResource()" [projectDescription]="project.description" - [canEdit]="isAdmin()" + [canEdit]="hasAdminAccess()" />
@@ -47,7 +47,7 @@ }
- @if (canWrite()) { + @if (hasWriteAccess()) { } @@ -57,8 +57,11 @@ [areComponentsLoading]="areComponentsLoading()" /> - - + @if (!hasViewOnly()) { + + + } +
@@ -67,7 +70,8 @@ [currentResource]="resourceOverview()" (customCitationUpdated)="onCustomCitationUpdated($event)" [isCollectionsRoute]="isCollectionsRoute()" - [canEdit]="canWrite()" + [canEdit]="hasAdminAccess()" + [showEditButton]="hasWriteAccess()" /> diff --git a/src/app/features/project/overview/project-overview.component.ts b/src/app/features/project/overview/project-overview.component.ts index 368c9a877..53d447925 100644 --- a/src/app/features/project/overview/project-overview.component.ts +++ b/src/app/features/project/overview/project-overview.component.ts @@ -31,7 +31,7 @@ import { CollectionsModerationSelectors, GetSubmissionsReviewActions, } from '@osf/features/moderation/store/collections-moderation'; -import { Mode, ResourceType, UserPermissions } from '@osf/shared/enums'; +import { Mode, ResourceType } from '@osf/shared/enums'; import { hasViewOnlyParam, IS_XSMALL } from '@osf/shared/helpers'; import { MapProjectOverview } from '@osf/shared/mappers'; import { MetaTagsService, ToastService } from '@osf/shared/services'; @@ -129,6 +129,8 @@ export class ProjectOverviewComponent implements OnInit { areSubjectsLoading = select(SubjectsSelectors.areSelectedSubjectsLoading); currentProject = select(ProjectOverviewSelectors.getProject); isAnonymous = select(ProjectOverviewSelectors.isProjectAnonymous); + hasWriteAccess = select(ProjectOverviewSelectors.hasWriteAccess); + hasAdminAccess = select(ProjectOverviewSelectors.hasAdminAccess); private readonly actions = createDispatchMap({ getProject: GetProjectById, @@ -175,14 +177,6 @@ export class ProjectOverviewComponent implements OnInit { userPermissions = computed(() => this.currentProject()?.currentUserPermissions || []); hasViewOnly = computed(() => hasViewOnlyParam(this.router)); - isAdmin = computed(() => { - return this.userPermissions().includes(UserPermissions.Admin); - }); - - canWrite = computed(() => { - return this.userPermissions().includes(UserPermissions.Write); - }); - resourceOverview = computed(() => { const project = this.currentProject(); const subjects = this.subjects(); diff --git a/src/app/features/project/overview/store/project-overview.selectors.ts b/src/app/features/project/overview/store/project-overview.selectors.ts index f8e8e52e6..7b85f5c94 100644 --- a/src/app/features/project/overview/store/project-overview.selectors.ts +++ b/src/app/features/project/overview/store/project-overview.selectors.ts @@ -1,5 +1,7 @@ import { Selector } from '@ngxs/store'; +import { UserPermissions } from '@osf/shared/enums'; + import { ProjectOverviewStateModel } from './project-overview.model'; import { ProjectOverviewState } from './project-overview.state'; @@ -53,4 +55,19 @@ export class ProjectOverviewSelectors { static getDuplicatedProject(state: ProjectOverviewStateModel) { return state.duplicatedProject; } + + @Selector([ProjectOverviewState]) + static hasWriteAccess(state: ProjectOverviewStateModel): boolean { + return state.project.data?.currentUserPermissions.includes(UserPermissions.Write) || false; + } + + @Selector([ProjectOverviewState]) + static hasAdminAccess(state: ProjectOverviewStateModel): boolean { + return state.project.data?.currentUserPermissions.includes(UserPermissions.Admin) || false; + } + + @Selector([ProjectOverviewState]) + static hasNoPermissions(state: ProjectOverviewStateModel): boolean { + return !state.project.data?.currentUserPermissions.length; + } } diff --git a/src/app/features/project/registrations/registrations.component.html b/src/app/features/project/registrations/registrations.component.html index a9bd9b0ad..34300b4eb 100644 --- a/src/app/features/project/registrations/registrations.component.html +++ b/src/app/features/project/registrations/registrations.component.html @@ -1,5 +1,5 @@ params['id'])) ?? of(undefined)); registrations = select(RegistrationsSelectors.getRegistrations); registrationsTotalCount = select(RegistrationsSelectors.getRegistrationsTotalCount); isRegistrationsLoading = select(RegistrationsSelectors.isRegistrationsLoading); - actions = createDispatchMap({ getRegistrations: GetRegistrations }); + actions = createDispatchMap({ getRegistrations: GetRegistrations, getResourceDetails: GetResourceDetails }); itemsPerPage = 10; first = 0; ngOnInit(): void { + this.actions.getResourceDetails(this.projectId(), ResourceType.Project); this.actions.getRegistrations(this.projectId(), 1, this.itemsPerPage); } diff --git a/src/app/features/project/wiki/wiki.component.html b/src/app/features/project/wiki/wiki.component.html index 99de7c58a..4fe159c43 100644 --- a/src/app/features/project/wiki/wiki.component.html +++ b/src/app/features/project/wiki/wiki.component.html @@ -5,7 +5,7 @@ [variant]="wikiModes().view ? undefined : 'outlined'" (onClick)="toggleMode(WikiModes.View)" /> - @if (!hasViewOnly()) { + @if (hasWriteAccess()) { @@ -46,7 +46,7 @@ (selectVersion)="onSelectVersion($event)" > } - @if (!hasViewOnly() && wikiModes().edit) { + @if (hasWriteAccess() && wikiModes().edit) { hasViewOnlyParam(this.router)); + hasWriteAccess = select(CurrentResourceSelectors.hasWriteAccess); + hasAdminAccess = select(CurrentResourceSelectors.hasAdminAccess); + actions = createDispatchMap({ getWikiModes: GetWikiModes, toggleMode: ToggleMode, @@ -95,11 +100,13 @@ export class WikiComponent { createWikiVersion: CreateWikiVersion, getWikiVersionContent: GetWikiVersionContent, getCompareVersionContent: GetCompareVersionContent, + getResourceDetails: GetResourceDetails, }); wikiIdFromQueryParams = this.route.snapshot.queryParams['wiki']; constructor() { + this.actions.getResourceDetails(this.projectId(), ResourceType.Project); this.actions .getWikiList(ResourceType.Project, this.projectId()) .pipe( @@ -108,7 +115,7 @@ export class WikiComponent { if (!this.wikiIdFromQueryParams) { this.navigateToWiki(this.wikiList()?.[0]?.id || ''); } - if (!this.wikiList()?.length) { + if (!this.wikiList()?.length && this.hasWriteAccess()) { this.actions.createWiki(ResourceType.Project, this.projectId(), this.homeWikiName); } }) diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.html b/src/app/features/registry/pages/registry-overview/registry-overview.component.html index d0ec326a6..12cf3c214 100644 --- a/src/app/features/registry/pages/registry-overview/registry-overview.component.html +++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.html @@ -135,6 +135,7 @@

{{ section.title }}

[currentResource]="resourceOverview()" (customCitationUpdated)="onCustomCitationUpdated($event)" [canEdit]="hasWriteAccess()" + [showEditButton]="hasWriteAccess()" /> diff --git a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.html b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.html index 0c8dfc404..799fd09be 100644 --- a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.html +++ b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.html @@ -21,7 +21,6 @@
{{ citation.title }} } } - @if (canEdit()) { - -

{{ 'project.overview.metadata.getMoreCitations' | translate }}

- - - {{ selectedOption.label }} - - - @if (styledCitation()) { -

{{ styledCitation()?.citation }}

- } - - @if (!hasViewOnly) { - - } + +

{{ 'project.overview.metadata.getMoreCitations' | translate }}

+ + + {{ selectedOption.label }} + + + @if (styledCitation()) { +

{{ styledCitation()?.citation }}

+ } + + @if (!hasViewOnly || canEdit()) { + } } } diff --git a/src/app/shared/components/resource-metadata/resource-metadata.component.html b/src/app/shared/components/resource-metadata/resource-metadata.component.html index e8a4488d6..c9f8f8b7c 100644 --- a/src/app/shared/components/resource-metadata/resource-metadata.component.html +++ b/src/app/shared/components/resource-metadata/resource-metadata.component.html @@ -5,7 +5,7 @@ - @if (!viewOnly() && list().length) { + @if (canEdit() && list().length) { @if (!isHomeWikiSelected()) { {{ item.label | translate }} - - @if (!viewOnly()) { + @if (canEdit()) { (); readonly isLoading = input(false); - readonly viewOnly = input(false); + readonly canEdit = input(false); readonly deleteWiki = output(); readonly createWiki = output(); diff --git a/src/app/shared/mappers/components/components.mapper.ts b/src/app/shared/mappers/components/components.mapper.ts index 0cd83baf1..762e19d95 100644 --- a/src/app/shared/mappers/components/components.mapper.ts +++ b/src/app/shared/mappers/components/components.mapper.ts @@ -9,14 +9,14 @@ export class ComponentsMapper { description: response.attributes.description, public: response.attributes.public, contributors: response.embeds.bibliographic_contributors.data.map((contributor) => ({ - id: contributor.embeds.users.data.id, - familyName: contributor.embeds.users.data.attributes.family_name, - fullName: contributor.embeds.users.data.attributes.full_name, - givenName: contributor.embeds.users.data.attributes.given_name, - middleName: contributor.embeds.users.data.attributes.middle_name, - type: contributor.embeds.users.data.type, + id: contributor.embeds.users?.data?.id, + familyName: contributor.embeds.users?.data?.attributes?.family_name, + fullName: contributor.embeds.users?.data?.attributes?.full_name, + givenName: contributor.embeds.users?.data?.attributes?.given_name, + middleName: contributor.embeds.users?.data?.attributes?.middle_name, + type: contributor.embeds.users?.data?.type, })), - currentUserPermissions: response.attributes.current_user_permissions || [], + currentUserPermissions: response.attributes?.current_user_permissions || [], }; } } diff --git a/src/app/shared/mappers/registration/registration.mapper.ts b/src/app/shared/mappers/registration/registration.mapper.ts index 916022d8c..0cf9aa652 100644 --- a/src/app/shared/mappers/registration/registration.mapper.ts +++ b/src/app/shared/mappers/registration/registration.mapper.ts @@ -95,8 +95,8 @@ export class RegistrationMapper { hasSupplements: registration.attributes.has_supplements, contributors: registration.embeds?.bibliographic_contributors?.data.map((contributor) => ({ - id: contributor.embeds.users.data.id, - fullName: contributor.embeds.users.data.attributes.full_name, + id: contributor.embeds.users?.data?.id, + fullName: contributor.embeds.users?.data?.attributes.full_name, })) || [], rootParentId: registration.relationships.root?.data?.id, currentUserPermissions: registration.attributes.current_user_permissions, diff --git a/src/app/shared/stores/current-resource/current-resource.selectors.ts b/src/app/shared/stores/current-resource/current-resource.selectors.ts index bb6ccae03..40534e119 100644 --- a/src/app/shared/stores/current-resource/current-resource.selectors.ts +++ b/src/app/shared/stores/current-resource/current-resource.selectors.ts @@ -1,6 +1,7 @@ import { Selector } from '@ngxs/store'; import { BaseNodeModel, CurrentResource, NodeShortInfoModel } from '@osf/shared/models'; +import { UserPermissions } from '@shared/enums'; import { CurrentResourceStateModel } from './current-resource.model'; import { CurrentResourceState } from './current-resource.state'; @@ -21,6 +22,21 @@ export class CurrentResourceSelectors { return state.resourceChildren.data; } + @Selector([CurrentResourceState]) + static hasWriteAccess(state: CurrentResourceStateModel): boolean { + return state.resourceDetails.data?.currentUserPermissions.includes(UserPermissions.Write) || false; + } + + @Selector([CurrentResourceState]) + static hasAdminAccess(state: CurrentResourceStateModel): boolean { + return state.resourceDetails.data?.currentUserPermissions.includes(UserPermissions.Admin) || false; + } + + @Selector([CurrentResourceState]) + static hasNoPermissions(state: CurrentResourceStateModel): boolean { + return !state.resourceDetails.data?.currentUserPermissions.length; + } + @Selector([CurrentResourceState]) static isResourceDetailsLoading(state: CurrentResourceStateModel): boolean { return state.resourceDetails.isLoading; From 36b2c5d6c8131f91548ae6f8c078488c3eea42ab Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Fri, 26 Sep 2025 19:19:38 +0300 Subject: [PATCH 2/7] fix(ang-403): fixed minor bug --- .../stores/current-resource/current-resource.selectors.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/shared/stores/current-resource/current-resource.selectors.ts b/src/app/shared/stores/current-resource/current-resource.selectors.ts index 40534e119..c63aa0cbd 100644 --- a/src/app/shared/stores/current-resource/current-resource.selectors.ts +++ b/src/app/shared/stores/current-resource/current-resource.selectors.ts @@ -24,17 +24,17 @@ export class CurrentResourceSelectors { @Selector([CurrentResourceState]) static hasWriteAccess(state: CurrentResourceStateModel): boolean { - return state.resourceDetails.data?.currentUserPermissions.includes(UserPermissions.Write) || false; + return state.resourceDetails.data?.currentUserPermissions?.includes(UserPermissions.Write) || false; } @Selector([CurrentResourceState]) static hasAdminAccess(state: CurrentResourceStateModel): boolean { - return state.resourceDetails.data?.currentUserPermissions.includes(UserPermissions.Admin) || false; + return state.resourceDetails.data?.currentUserPermissions?.includes(UserPermissions.Admin) || false; } @Selector([CurrentResourceState]) static hasNoPermissions(state: CurrentResourceStateModel): boolean { - return !state.resourceDetails.data?.currentUserPermissions.length; + return !state.resourceDetails.data?.currentUserPermissions?.length; } @Selector([CurrentResourceState]) From be66c485649c7a09a7e02b50f277a0704f665445 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Fri, 26 Sep 2025 21:30:24 +0300 Subject: [PATCH 3/7] fix(ang-403): added permissions for the nav menu --- .../components/nav-menu/nav-menu.component.ts | 41 ++++++++++++++++--- src/app/core/constants/nav-items.constant.ts | 13 ++++++ src/app/core/helpers/nav-menu.helper.ts | 31 +++++++++++++- src/app/core/models/route-context.model.ts | 5 +-- .../linked-services.component.html | 16 ++++---- .../linked-services.component.ts | 7 ++++ .../registrations/registrations.component.ts | 6 +-- .../features/project/wiki/wiki.component.ts | 3 -- .../current-resource.selectors.ts | 5 +++ 9 files changed, 103 insertions(+), 24 deletions(-) diff --git a/src/app/core/components/nav-menu/nav-menu.component.ts b/src/app/core/components/nav-menu/nav-menu.component.ts index e167f5f43..efbb43336 100644 --- a/src/app/core/components/nav-menu/nav-menu.component.ts +++ b/src/app/core/components/nav-menu/nav-menu.component.ts @@ -1,4 +1,4 @@ -import { select } from '@ngxs/store'; +import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; @@ -7,7 +7,7 @@ import { PanelMenuModule } from 'primeng/panelmenu'; import { filter, map } from 'rxjs'; -import { Component, computed, inject, output } from '@angular/core'; +import { Component, computed, effect, inject, output } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/router'; @@ -18,10 +18,10 @@ import { RouteContext } from '@osf/core/models'; import { AuthService } from '@osf/core/services'; import { UserSelectors } from '@osf/core/store/user'; import { IconComponent } from '@osf/shared/components'; -import { CurrentResourceType, ReviewPermissions } from '@osf/shared/enums'; +import { CurrentResourceType, ResourceType, ReviewPermissions } from '@osf/shared/enums'; import { getViewOnlyParam } from '@osf/shared/helpers'; import { WrapFnPipe } from '@osf/shared/pipes'; -import { CurrentResourceSelectors } from '@osf/shared/stores'; +import { CurrentResourceSelectors, GetResourceDetails } from '@osf/shared/stores'; @Component({ selector: 'osf-nav-menu', @@ -38,8 +38,38 @@ export class NavMenuComponent { private readonly isAuthenticated = select(UserSelectors.isAuthenticated); private readonly currentResource = select(CurrentResourceSelectors.getCurrentResource); + private readonly currentUserPermissions = select(CurrentResourceSelectors.getCurrentUserPermissions); + private readonly isResourceDetailsLoading = select(CurrentResourceSelectors.isResourceDetailsLoading); private readonly provider = select(ProviderSelectors.getCurrentProvider); + readonly actions = createDispatchMap({ getResourceDetails: GetResourceDetails }); + + readonly resourceType = computed(() => { + const type = this.currentResource()?.type; + + switch (type) { + case CurrentResourceType.Projects: + return ResourceType.Project; + case CurrentResourceType.Registrations: + return ResourceType.Registration; + case CurrentResourceType.Preprints: + return ResourceType.Preprint; + default: + return ResourceType.Project; + } + }); + + constructor() { + effect(() => { + const resourceId = this.currentResourceId(); + const resourceType = this.resourceType(); + + if (resourceId && resourceType) { + this.actions.getResourceDetails(resourceId, resourceType); + } + }); + } + readonly mainMenuItems = computed(() => { const isAuthenticated = this.isAuthenticated(); const filtered = filterMenuItems(MENU_ITEMS, isAuthenticated); @@ -65,7 +95,8 @@ export class NavMenuComponent { isCollections: this.isCollectionsRoute() || false, currentUrl: this.router.url, isViewOnly: !!getViewOnlyParam(this.router), - permissions: this.currentResource()?.permissions, + permissions: this.currentUserPermissions(), + isResourceDetailsLoading: this.isResourceDetailsLoading(), }; const items = updateMenuItems(filtered, routeContext); diff --git a/src/app/core/constants/nav-items.constant.ts b/src/app/core/constants/nav-items.constant.ts index cfdc5b390..586341e3a 100644 --- a/src/app/core/constants/nav-items.constant.ts +++ b/src/app/core/constants/nav-items.constant.ts @@ -14,6 +14,7 @@ export const VIEW_ONLY_PROJECT_MENU_ITEMS: string[] = [ 'project-files', 'project-wiki', 'project-analytics', + 'project-links', ]; export const VIEW_ONLY_REGISTRY_MENU_ITEMS: string[] = [ @@ -438,3 +439,15 @@ export const MENU_ITEMS: MenuItem[] = [ styleClass: 'my-5', }, ]; + +export const PROJECT_MENU_PERMISSIONS: Record< + string, + { + requiresWrite?: boolean; + requiresPermissions?: boolean; + } +> = { + 'project-addons': { requiresWrite: true }, + 'project-contributors': { requiresPermissions: true }, + 'project-settings': { requiresPermissions: true }, +}; diff --git a/src/app/core/helpers/nav-menu.helper.ts b/src/app/core/helpers/nav-menu.helper.ts index 034c32c4a..49607ae4a 100644 --- a/src/app/core/helpers/nav-menu.helper.ts +++ b/src/app/core/helpers/nav-menu.helper.ts @@ -1,17 +1,39 @@ import { MenuItem } from 'primeng/api'; +import { UserPermissions } from '@osf/shared/enums'; import { getViewOnlyParamFromUrl } from '@osf/shared/helpers'; import { AUTHENTICATED_MENU_ITEMS, PREPRINT_MENU_ITEMS, PROJECT_MENU_ITEMS, + PROJECT_MENU_PERMISSIONS, REGISTRATION_MENU_ITEMS, VIEW_ONLY_PROJECT_MENU_ITEMS, VIEW_ONLY_REGISTRY_MENU_ITEMS, } from '../constants'; import { RouteContext } from '../models'; +function shouldShowMenuItem(menuItemId: string, permissions: string[] | undefined): boolean { + const permissionConfig = PROJECT_MENU_PERMISSIONS[menuItemId]; + + if (!permissionConfig) { + return true; + } + + if (permissionConfig.requiresPermissions && (!permissions || !permissions.length)) { + return false; + } + + if (permissionConfig.requiresWrite) { + const hasWritePermission = + permissions?.includes(UserPermissions.Write) || permissions?.includes(UserPermissions.Admin); + return hasWritePermission || false; + } + + return true; +} + export function filterMenuItems(items: MenuItem[], isAuthenticated: boolean): MenuItem[] { return items.map((item) => { const isAuthenticatedItem = AUTHENTICATED_MENU_ITEMS.includes(item.id || ''); @@ -101,13 +123,18 @@ function updateProjectMenuItem(item: MenuItem, ctx: RouteContext): MenuItem { }; } - return menuItem; + const isVisible = shouldShowMenuItem(menuItem.id || '', ctx.permissions); + + return { + ...menuItem, + visible: isVisible, + }; }); return { ...subItem, visible: true, - expanded: true, + expanded: !ctx.isResourceDetailsLoading, items: menuItems.map((menuItem) => ({ ...menuItem, routerLink: [ctx.resourceId as string, menuItem.routerLink], diff --git a/src/app/core/models/route-context.model.ts b/src/app/core/models/route-context.model.ts index 456173ee2..395a5ef37 100644 --- a/src/app/core/models/route-context.model.ts +++ b/src/app/core/models/route-context.model.ts @@ -1,5 +1,3 @@ -import { UserPermissions } from '@osf/shared/enums'; - export interface RouteContext { resourceId: string | undefined; providerId?: string; @@ -13,5 +11,6 @@ export interface RouteContext { isCollections: boolean; currentUrl?: string; isViewOnly?: boolean; - permissions?: UserPermissions[]; + permissions?: string[]; + isResourceDetailsLoading?: boolean; } diff --git a/src/app/features/project/linked-services/linked-services.component.html b/src/app/features/project/linked-services/linked-services.component.html index a36358f21..833c79190 100644 --- a/src/app/features/project/linked-services/linked-services.component.html +++ b/src/app/features/project/linked-services/linked-services.component.html @@ -41,13 +41,15 @@

{{ 'project.linkedServices.noLinkedServices' | translate }}

-

- {{ 'project.linkedServices.redirectMessage' | translate }} - - {{ 'project.linkedServices.addonsLink' | translate }} - - {{ 'project.linkedServices.redirectMessageSuffix' | translate }} -

+ @if (canManageAddons()) { +

+ {{ 'project.linkedServices.redirectMessage' | translate }} + + {{ 'project.linkedServices.addonsLink' | translate }} + + {{ 'project.linkedServices.redirectMessageSuffix' | translate }} +

+ } }
diff --git a/src/app/features/project/linked-services/linked-services.component.ts b/src/app/features/project/linked-services/linked-services.component.ts index ea280a770..ff1e5834f 100644 --- a/src/app/features/project/linked-services/linked-services.component.ts +++ b/src/app/features/project/linked-services/linked-services.component.ts @@ -12,6 +12,7 @@ import { LoadingSpinnerComponent, SubHeaderComponent } from '@shared/components' import { AddonServiceNames } from '@shared/enums'; import { convertCamelCaseToNormal } from '@shared/helpers'; import { AddonsSelectors, GetAddonsResourceReference, GetConfiguredLinkAddons } from '@shared/stores'; +import { CurrentResourceSelectors } from '@shared/stores/current-resource'; @Component({ selector: 'osf-linked-services', @@ -29,6 +30,8 @@ export class LinkedServicesComponent implements OnInit { isResourceReferenceLoading = select(AddonsSelectors.getAddonsResourceReferenceLoading); isConfiguredLinkAddonsLoading = select(AddonsSelectors.getConfiguredLinkAddonsLoading); isCurrentUserLoading = select(UserSelectors.getCurrentUserLoading); + hasWriteAccess = select(CurrentResourceSelectors.hasWriteAccess); + hasAdminAccess = select(CurrentResourceSelectors.hasAdminAccess); isLoading = computed(() => { return this.isConfiguredLinkAddonsLoading() || this.isResourceReferenceLoading() || this.isCurrentUserLoading(); @@ -45,6 +48,10 @@ export class LinkedServicesComponent implements OnInit { })); }); + canManageAddons = computed(() => { + return this.hasWriteAccess() || this.hasAdminAccess(); + }); + actions = createDispatchMap({ getConfiguredLinkAddons: GetConfiguredLinkAddons, getAddonsResourceReference: GetAddonsResourceReference, diff --git a/src/app/features/project/registrations/registrations.component.ts b/src/app/features/project/registrations/registrations.component.ts index fcc3a2056..22bb24739 100644 --- a/src/app/features/project/registrations/registrations.component.ts +++ b/src/app/features/project/registrations/registrations.component.ts @@ -19,8 +19,7 @@ import { RegistrationCardComponent, SubHeaderComponent, } from '@osf/shared/components'; -import { ResourceType } from '@shared/enums'; -import { CurrentResourceSelectors, GetResourceDetails } from '@shared/stores'; +import { CurrentResourceSelectors } from '@shared/stores'; import { GetRegistrations, RegistrationsSelectors } from './store'; @@ -49,13 +48,12 @@ export class RegistrationsComponent implements OnInit { registrations = select(RegistrationsSelectors.getRegistrations); registrationsTotalCount = select(RegistrationsSelectors.getRegistrationsTotalCount); isRegistrationsLoading = select(RegistrationsSelectors.isRegistrationsLoading); - actions = createDispatchMap({ getRegistrations: GetRegistrations, getResourceDetails: GetResourceDetails }); + actions = createDispatchMap({ getRegistrations: GetRegistrations }); itemsPerPage = 10; first = 0; ngOnInit(): void { - this.actions.getResourceDetails(this.projectId(), ResourceType.Project); this.actions.getRegistrations(this.projectId(), 1, this.itemsPerPage); } diff --git a/src/app/features/project/wiki/wiki.component.ts b/src/app/features/project/wiki/wiki.component.ts index bb9eaf8f2..ba003aaf7 100644 --- a/src/app/features/project/wiki/wiki.component.ts +++ b/src/app/features/project/wiki/wiki.component.ts @@ -29,7 +29,6 @@ import { DeleteWiki, GetCompareVersionContent, GetComponentsWikiList, - GetResourceDetails, GetWikiContent, GetWikiList, GetWikiModes, @@ -100,13 +99,11 @@ export class WikiComponent { createWikiVersion: CreateWikiVersion, getWikiVersionContent: GetWikiVersionContent, getCompareVersionContent: GetCompareVersionContent, - getResourceDetails: GetResourceDetails, }); wikiIdFromQueryParams = this.route.snapshot.queryParams['wiki']; constructor() { - this.actions.getResourceDetails(this.projectId(), ResourceType.Project); this.actions .getWikiList(ResourceType.Project, this.projectId()) .pipe( diff --git a/src/app/shared/stores/current-resource/current-resource.selectors.ts b/src/app/shared/stores/current-resource/current-resource.selectors.ts index c63aa0cbd..1bf13ad6f 100644 --- a/src/app/shared/stores/current-resource/current-resource.selectors.ts +++ b/src/app/shared/stores/current-resource/current-resource.selectors.ts @@ -46,4 +46,9 @@ export class CurrentResourceSelectors { static isResourceWithChildrenLoading(state: CurrentResourceStateModel): boolean { return state.resourceChildren.isLoading; } + + @Selector([CurrentResourceState]) + static getCurrentUserPermissions(currentResourceState: CurrentResourceStateModel): string[] { + return currentResourceState.resourceDetails.data.currentUserPermissions || []; + } } From 89593d14abcf5c2e7a336961d117aeb3b7ee4e7b Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Mon, 29 Sep 2025 14:11:01 +0300 Subject: [PATCH 4/7] fix(ang-812): fixed view only links registry components page --- .../view-duplicates.component.html | 2 +- .../view-duplicates.component.ts | 3 ++- ...settings-project-affiliation.component.html | 18 ++++++++++-------- .../settings-project-affiliation.component.ts | 1 + .../project/settings/settings.component.html | 5 +++-- .../project/settings/settings.component.ts | 6 +++--- .../registration-links-card.component.html | 6 +++++- .../mappers/registry-components.mapper.ts | 6 +----- .../current-resource.selectors.ts | 6 +++--- 9 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html index 42599a247..4f3926234 100644 --- a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html +++ b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html @@ -1,6 +1,6 @@ diff --git a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts index 54c04300c..f6f789ba1 100644 --- a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts +++ b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts @@ -42,7 +42,7 @@ import { IS_SMALL } from '@osf/shared/helpers'; import { ToolbarResource } from '@osf/shared/models'; import { Duplicate } from '@osf/shared/models/duplicates'; import { CustomDialogService } from '@osf/shared/services'; -import { ClearDuplicates, DuplicatesSelectors, GetAllDuplicates } from '@osf/shared/stores'; +import { ClearDuplicates, CurrentResourceSelectors, DuplicatesSelectors, GetAllDuplicates } from '@osf/shared/stores'; @Component({ selector: 'osf-view-duplicates', @@ -76,6 +76,7 @@ export class ViewDuplicatesComponent { duplicates = select(DuplicatesSelectors.getDuplicates); isDuplicatesLoading = select(DuplicatesSelectors.getDuplicatesLoading); totalDuplicates = select(DuplicatesSelectors.getDuplicatesTotalCount); + hasNoPermissions = select(CurrentResourceSelectors.hasNoPermissions); readonly pageSize = 10; readonly UserPermissions = UserPermissions; diff --git a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.html b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.html index f4107a9c1..086caf3e4 100644 --- a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.html +++ b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.html @@ -26,14 +26,16 @@

{{ 'myProjects.settings.projectAffiliation' | translate

{{ affiliation.name }}

- + @if (canEdit()) { + + } } diff --git a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts index 693cadb10..ae02b0440 100644 --- a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts +++ b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts @@ -18,6 +18,7 @@ import { Institution } from '@osf/shared/models'; export class SettingsProjectAffiliationComponent { affiliations = input([]); removed = output(); + canEdit = input(false); removeAffiliation(affiliation: Institution) { this.removed.emit(affiliation); diff --git a/src/app/features/project/settings/settings.component.html b/src/app/features/project/settings/settings.component.html index 35665d69d..c460d6d31 100644 --- a/src/app/features/project/settings/settings.component.html +++ b/src/app/features/project/settings/settings.component.html @@ -5,7 +5,7 @@ } @else {
- @if (canWrite()) { + @if (hasWriteAccess()) { - @if (isAdmin()) { + @if (hasAdminAccess()) { diff --git a/src/app/features/project/settings/settings.component.ts b/src/app/features/project/settings/settings.component.ts index 66a8effd1..288f6f049 100644 --- a/src/app/features/project/settings/settings.component.ts +++ b/src/app/features/project/settings/settings.component.ts @@ -97,8 +97,8 @@ export class SettingsComponent implements OnInit { title = signal(''); userPermissions = computed(() => this.projectDetails()?.currentUserPermissions || []); - isAdmin = computed(() => this.userPermissions().includes(UserPermissions.Admin)); - canWrite = computed(() => this.userPermissions().includes(UserPermissions.Write)); + hasAdminAccess = computed(() => this.userPermissions().includes(UserPermissions.Admin)); + hasWriteAccess = computed(() => this.userPermissions().includes(UserPermissions.Write)); constructor() { this.setupEffects(); @@ -255,7 +255,7 @@ export class SettingsComponent implements OnInit { effect(() => { const id = this.projectId(); - if (id && this.isAdmin()) { + if (id && this.hasAdminAccess()) { this.actions.getViewOnlyLinks(id, ResourceType.Project); } }); diff --git a/src/app/features/registry/components/registration-links-card/registration-links-card.component.html b/src/app/features/registry/components/registration-links-card/registration-links-card.component.html index 6c0d081f4..d96b84799 100644 --- a/src/app/features/registry/components/registration-links-card/registration-links-card.component.html +++ b/src/app/features/registry/components/registration-links-card/registration-links-card.component.html @@ -40,7 +40,11 @@
{{ 'common.labels.contributors' | translate }}: - + @if (registrationData().contributors.length) { + + } @else { + {{ 'project.overview.metadata.anonymousContributors' | translate }} + }
diff --git a/src/app/features/registry/mappers/registry-components.mapper.ts b/src/app/features/registry/mappers/registry-components.mapper.ts index af272a2fc..6d61f983d 100644 --- a/src/app/features/registry/mappers/registry-components.mapper.ts +++ b/src/app/features/registry/mappers/registry-components.mapper.ts @@ -15,11 +15,7 @@ export class RegistryComponentsMapper { registrationSupplement: apiComponent.attributes.registration_supplement, tags: apiComponent.attributes.tags, isPublic: apiComponent.attributes.public, - contributors: ContributorsMapper.getContributors(apiComponent.embeds.bibliographic_contributors.data) || [], + contributors: ContributorsMapper.getContributors(apiComponent.embeds?.bibliographic_contributors?.data || []), }; } - - static fromApiResponseArray(apiComponents: RegistryComponentJsonApi[]): RegistryComponentModel[] { - return apiComponents.map(this.fromApiResponse); - } } diff --git a/src/app/shared/stores/current-resource/current-resource.selectors.ts b/src/app/shared/stores/current-resource/current-resource.selectors.ts index 1bf13ad6f..ac8497c2b 100644 --- a/src/app/shared/stores/current-resource/current-resource.selectors.ts +++ b/src/app/shared/stores/current-resource/current-resource.selectors.ts @@ -24,17 +24,17 @@ export class CurrentResourceSelectors { @Selector([CurrentResourceState]) static hasWriteAccess(state: CurrentResourceStateModel): boolean { - return state.resourceDetails.data?.currentUserPermissions?.includes(UserPermissions.Write) || false; + return state.currentResource.data?.permissions?.includes(UserPermissions.Write) || false; } @Selector([CurrentResourceState]) static hasAdminAccess(state: CurrentResourceStateModel): boolean { - return state.resourceDetails.data?.currentUserPermissions?.includes(UserPermissions.Admin) || false; + return state.currentResource.data?.permissions?.includes(UserPermissions.Admin) || false; } @Selector([CurrentResourceState]) static hasNoPermissions(state: CurrentResourceStateModel): boolean { - return !state.resourceDetails.data?.currentUserPermissions?.length; + return !state.currentResource.data?.permissions?.length; } @Selector([CurrentResourceState]) From f4061f684df025dd1ca6ff9c209bbd88cee8d7f9 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Mon, 29 Sep 2025 15:58:30 +0300 Subject: [PATCH 5/7] fix(ang-812): fixed overview toolbar permissions --- .../features/metadata/metadata.component.html | 2 +- .../overview-toolbar.component.html | 154 +++++++++--------- .../overview-toolbar.component.ts | 1 + .../registration-links-card.component.html | 9 +- 4 files changed, 84 insertions(+), 82 deletions(-) diff --git a/src/app/features/metadata/metadata.component.html b/src/app/features/metadata/metadata.component.html index 55e98a3ee..4450bb816 100644 --- a/src/app/features/metadata/metadata.component.html +++ b/src/app/features/metadata/metadata.component.html @@ -83,7 +83,7 @@ -
- @if (resource.storage && !isCollectionsRoute()) { -

- {{ +resource.storage.storageUsage | fileSize }} -

- } - -
- @if (showViewOnlyLinks() && canEdit()) { - - {{ resource.viewOnlyLinksCount }} - - + @if (!hasNoPermissions()) { +
+
+ @if (resource.storage && !isCollectionsRoute()) { +

+ {{ +resource.storage.storageUsage | fileSize }} +

} - @if (resource.resourceType === ResourceType.Project && !hasViewOnly()) { - - {{ resource.forksCount }} - - - - - {{ item.label | translate }} - - - - - } +
+ @if (showViewOnlyLinks() && canEdit()) { + + {{ resource.viewOnlyLinksCount }} + + + } - @if (!hasViewOnly()) { - - @if (!isBookmarksLoading() && !isBookmarksSubmitting()) { - - } - - } + @if (resource.resourceType === ResourceType.Project && !hasViewOnly()) { + + {{ resource.forksCount }} + + + + + {{ item.label | translate }} + + + + + } - @if (resource.isPublic && !hasViewOnly()) { - - {{ socialsActionItems().length }} - - - - - - {{ item.label | translate }} - - - - - } + @if (!hasViewOnly()) { + + @if (!isBookmarksLoading() && !isBookmarksSubmitting()) { + + } + + } + + @if (resource.isPublic && !hasViewOnly()) { + + {{ socialsActionItems().length }} + + + + + + {{ item.label | translate }} + + + + + } +
-
+ }
} diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts index f41b112c7..f39edf8e9 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts @@ -78,6 +78,7 @@ export class OverviewToolbarComponent { bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId); bookmarkedProjects = select(MyResourcesSelectors.getBookmarks); duplicatedProject = select(ProjectOverviewSelectors.getDuplicatedProject); + hasNoPermissions = select(ProjectOverviewSelectors.hasNoPermissions); socialsActionItems = computed(() => { const shareableContent = this.createShareableContent(); return shareableContent ? this.buildSocialActionItems(shareableContent) : []; diff --git a/src/app/features/registry/components/registration-links-card/registration-links-card.component.html b/src/app/features/registry/components/registration-links-card/registration-links-card.component.html index d96b84799..926313ed1 100644 --- a/src/app/features/registry/components/registration-links-card/registration-links-card.component.html +++ b/src/app/features/registry/components/registration-links-card/registration-links-card.component.html @@ -40,11 +40,10 @@
{{ 'common.labels.contributors' | translate }}: - @if (registrationData().contributors.length) { - - } @else { - {{ 'project.overview.metadata.anonymousContributors' | translate }} - } +
From 2041c36375e2733f6aecebced3a7395f1cefa497 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Mon, 29 Sep 2025 16:06:52 +0300 Subject: [PATCH 6/7] fix(ang-812): fixed metadata tags permissions --- src/app/features/metadata/metadata.component.html | 4 ++-- src/app/features/metadata/metadata.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/features/metadata/metadata.component.html b/src/app/features/metadata/metadata.component.html index 4450bb816..9eba5487f 100644 --- a/src/app/features/metadata/metadata.component.html +++ b/src/app/features/metadata/metadata.component.html @@ -76,14 +76,14 @@ [resourceType]="resourceType()" /> - @if (showRegistrationDoi()) { + @if (isRegistrationType()) { } this.resourceType() === ResourceType.Registration); + isRegistrationType = computed(() => this.resourceType() === ResourceType.Registration); bibliographicContributors = computed(() => this.contributors().filter((contributor) => contributor.isBibliographic)); From 01ebf538b2042f8f7bd01f6328ddb8a508e281d4 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Mon, 29 Sep 2025 19:15:58 +0300 Subject: [PATCH 7/7] fix(ang-820): fixed duplicate project permissions bug --- .../view-duplicates/view-duplicates.component.html | 2 +- .../view-duplicates/view-duplicates.component.ts | 5 +++-- .../overview-toolbar.component.html | 2 +- .../overview-toolbar/overview-toolbar.component.ts | 3 ++- src/app/features/project/wiki/wiki.component.ts | 9 ++++++++- .../pages/registry-wiki/registry-wiki.component.ts | 13 +++++++++++-- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html index 4f3926234..a959cdfbd 100644 --- a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html +++ b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.html @@ -1,6 +1,6 @@ diff --git a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts index f6f789ba1..e9479339e 100644 --- a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts +++ b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts @@ -22,6 +22,7 @@ import { import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { UserSelectors } from '@core/store/user'; import { DeleteComponentDialogComponent, ForkDialogComponent } from '@osf/features/project/overview/components'; import { ClearProjectOverview, GetProjectById, ProjectOverviewSelectors } from '@osf/features/project/overview/store'; import { @@ -42,7 +43,7 @@ import { IS_SMALL } from '@osf/shared/helpers'; import { ToolbarResource } from '@osf/shared/models'; import { Duplicate } from '@osf/shared/models/duplicates'; import { CustomDialogService } from '@osf/shared/services'; -import { ClearDuplicates, CurrentResourceSelectors, DuplicatesSelectors, GetAllDuplicates } from '@osf/shared/stores'; +import { ClearDuplicates, DuplicatesSelectors, GetAllDuplicates } from '@osf/shared/stores'; @Component({ selector: 'osf-view-duplicates', @@ -76,7 +77,7 @@ export class ViewDuplicatesComponent { duplicates = select(DuplicatesSelectors.getDuplicates); isDuplicatesLoading = select(DuplicatesSelectors.getDuplicatesLoading); totalDuplicates = select(DuplicatesSelectors.getDuplicatesTotalCount); - hasNoPermissions = select(CurrentResourceSelectors.hasNoPermissions); + isAuthenticated = select(UserSelectors.isAuthenticated); readonly pageSize = 10; readonly UserPermissions = UserPermissions; diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html index 7c79576ec..f6e8dd13e 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html @@ -37,7 +37,7 @@ } } - @if (!hasNoPermissions()) { + @if (isAuthenticated()) {
@if (resource.storage && !isCollectionsRoute()) { diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts index f39edf8e9..f99f6d220 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts @@ -15,6 +15,7 @@ import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { UserSelectors } from '@core/store/user'; import { ClearDuplicatedProject, ProjectOverviewSelectors } from '@osf/features/project/overview/store'; import { IconComponent } from '@osf/shared/components'; import { ResourceType } from '@osf/shared/enums'; @@ -78,7 +79,7 @@ export class OverviewToolbarComponent { bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId); bookmarkedProjects = select(MyResourcesSelectors.getBookmarks); duplicatedProject = select(ProjectOverviewSelectors.getDuplicatedProject); - hasNoPermissions = select(ProjectOverviewSelectors.hasNoPermissions); + isAuthenticated = select(UserSelectors.isAuthenticated); socialsActionItems = computed(() => { const shareableContent = this.createShareableContent(); return shareableContent ? this.buildSocialActionItems(shareableContent) : []; diff --git a/src/app/features/project/wiki/wiki.component.ts b/src/app/features/project/wiki/wiki.component.ts index ba003aaf7..7a324ed22 100644 --- a/src/app/features/project/wiki/wiki.component.ts +++ b/src/app/features/project/wiki/wiki.component.ts @@ -7,7 +7,7 @@ import { ButtonGroupModule } from 'primeng/buttongroup'; import { filter, map, mergeMap, of, tap } from 'rxjs'; -import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; @@ -23,6 +23,7 @@ import { hasViewOnlyParam } from '@osf/shared/helpers'; import { WikiModes } from '@osf/shared/models'; import { ToastService } from '@osf/shared/services'; import { + ClearWiki, CreateWiki, CreateWikiVersion, CurrentResourceSelectors, @@ -61,6 +62,7 @@ import { ViewOnlyLinkMessageComponent } from '@shared/components/view-only-link- export class WikiComponent { private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); + private readonly destroyRef = inject(DestroyRef); private toastService = inject(ToastService); WikiModes = WikiModes; @@ -99,6 +101,7 @@ export class WikiComponent { createWikiVersion: CreateWikiVersion, getWikiVersionContent: GetWikiVersionContent, getCompareVersionContent: GetCompareVersionContent, + clearWiki: ClearWiki, }); wikiIdFromQueryParams = this.route.snapshot.queryParams['wiki']; @@ -132,6 +135,10 @@ export class WikiComponent { mergeMap((wikiId) => this.actions.getWikiVersions(wikiId)) ) .subscribe(); + + this.destroyRef.onDestroy(() => { + this.actions.clearWiki(); + }); } toggleMode(mode: WikiModes) { diff --git a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts index f7acf3f79..f9c35ff7f 100644 --- a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts +++ b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts @@ -7,7 +7,7 @@ import { ButtonGroup } from 'primeng/buttongroup'; import { filter, map, mergeMap, tap } from 'rxjs'; -import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; @@ -17,6 +17,7 @@ import { ResourceType } from '@osf/shared/enums'; import { hasViewOnlyParam } from '@osf/shared/helpers'; import { WikiModes } from '@osf/shared/models'; import { + ClearWiki, GetCompareVersionContent, GetComponentsWikiList, GetWikiContent, @@ -47,6 +48,7 @@ import { export class RegistryWikiComponent { private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); + private readonly destroyRef = inject(DestroyRef); WikiModes = WikiModes; wikiModes = select(WikiSelectors.getWikiModes); @@ -73,6 +75,7 @@ export class RegistryWikiComponent { getWikiVersionContent: GetWikiVersionContent, getCompareVersionContent: GetCompareVersionContent, getComponentsWikiList: GetComponentsWikiList, + clearWiki: ClearWiki, }); wikiIdFromQueryParams = this.route.snapshot.queryParams['wiki']; @@ -103,6 +106,10 @@ export class RegistryWikiComponent { mergeMap((wikiId) => this.actions.getWikiVersions(wikiId)) ) .subscribe(); + + this.destroyRef.onDestroy(() => { + this.actions.clearWiki(); + }); } toggleMode(mode: WikiModes) { @@ -110,7 +117,9 @@ export class RegistryWikiComponent { } onSelectVersion(versionId: string) { - this.actions.getWikiVersionContent(this.currentWikiId(), versionId); + if (versionId) { + this.actions.getWikiVersionContent(this.currentWikiId(), versionId); + } } onSelectCompareVersion(versionId: string) {