diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000..b280270ff --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,11 @@ +# Changelog + +We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO. + +26.1.0 (2026-01-01) +=================== + +* Angular FE support for Notification Refactor Project + +# vi: ft=markdown + diff --git a/angular.json b/angular.json index a00a79554..a2e8775f0 100644 --- a/angular.json +++ b/angular.json @@ -53,6 +53,7 @@ "styles": [ "src/styles/styles.scss", "node_modules/primeflex/primeflex.css", + "node_modules/@fortawesome/fontawesome-free/css/all.min.css", "node_modules/ngx-markdown-editor/assets/highlight.js/agate.min.css" ], "stylePreprocessorOptions": { diff --git a/package.json b/package.json index 1c9a244b6..64c19a4eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "osf", - "version": "25.4.0", + "version": "26.1.0", "scripts": { "ng": "ng", "analyze-bundle": "ng build --configuration=analyze-bundle && source-map-explorer dist/**/*.js --no-border-checks", diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html index 3e6baa3d8..396b9a96e 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html @@ -54,27 +54,23 @@

{{ 'project.metadata.addMetadata.notPublishedTe }
- @if (!cedarLoaded()) { - + @if (readonly()) { + } @else { - @if (readonly()) { - - } @else { - - } + }
diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts index e6fb6f0ed..15b023b33 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts @@ -1,11 +1,7 @@ -import { MockComponent } from 'ng-mocks'; - -import { PLATFORM_ID } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CedarMetadataHelper } from '@osf/features/metadata/helpers'; import { CedarMetadataDataTemplateJsonApi } from '@osf/features/metadata/models'; -import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; import { CedarTemplateFormComponent } from './cedar-template-form.component'; @@ -20,8 +16,7 @@ describe('CedarTemplateFormComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [CedarTemplateFormComponent, OSFTestingModule, MockComponent(LoadingSpinnerComponent)], - providers: [{ provide: PLATFORM_ID, useValue: 'browser' }], + imports: [CedarTemplateFormComponent, OSFTestingModule], }).compileComponents(); fixture = TestBed.createComponent(CedarTemplateFormComponent); @@ -80,14 +75,10 @@ describe('CedarTemplateFormComponent', () => { expect(emitSpy).toHaveBeenCalled(); }); - it('should initialize form data with empty metadata when no existing record', async () => { + it('should initialize form data with empty metadata when no existing record', () => { fixture.componentRef.setInput('existingRecord', null); fixture.detectChanges(); - await (component as any).loadCedarLibraries(); - (component as any).initializeCedar(); - fixture.detectChanges(); - const expectedEmptyMetadata = CedarMetadataHelper.buildEmptyMetadata(); expect(component.formData()).toEqual(expectedEmptyMetadata); }); diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts index 29dbccf91..96891e1e6 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts @@ -6,7 +6,7 @@ import { Tooltip } from 'primeng/tooltip'; import { map, of } from 'rxjs'; -import { CommonModule, isPlatformBrowser } from '@angular/common'; +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -16,7 +16,6 @@ import { inject, input, output, - PLATFORM_ID, signal, viewChild, ViewEncapsulation, @@ -25,7 +24,6 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; -import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '../../constants'; import { CedarMetadataHelper } from '../../helpers'; @@ -38,7 +36,7 @@ import { @Component({ selector: 'osf-cedar-template-form', - imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu, LoadingSpinnerComponent], + imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu], templateUrl: './cedar-template-form.component.html', styleUrl: './cedar-template-form.component.scss', schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -65,8 +63,6 @@ export class CedarTemplateFormComponent { private route = inject(ActivatedRoute); readonly environment = inject(ENVIRONMENT); - private platformId = inject(PLATFORM_ID); - readonly cedarLoaded = signal(false); readonly recordId = signal(''); readonly downloadUrl = signal(''); @@ -93,7 +89,7 @@ export class CedarTemplateFormComponent { effect(() => { const tpl = this.template(); if (tpl?.attributes?.template) { - this.loadCedarLibraries().then(() => this.initializeCedar()); + this.initializeCedar(); } }); @@ -101,7 +97,7 @@ export class CedarTemplateFormComponent { const record = this.existingRecord(); this.schemaName.set(record?.embeds?.template.data.attributes.schema_name || ''); if (record) { - this.loadCedarLibraries().then(() => this.initializeCedar()); + this.initializeCedar(); } }); } @@ -138,19 +134,6 @@ export class CedarTemplateFormComponent { } } - private async loadCedarLibraries(): Promise { - if (!isPlatformBrowser(this.platformId) || this.cedarLoaded()) { - return; - } - - try { - await Promise.all([import('cedar-artifact-viewer'), import('cedar-embeddable-editor')]); - this.cedarLoaded.set(true); - } catch { - this.cedarLoaded.set(false); - } - } - downloadMetadadaRecord() { if (this.fileGuid()) { window.open(`${this.environment.webUrl}/metadata/${this.fileGuid()}`)?.focus(); diff --git a/src/app/features/profile/components/profile-information/profile-information.component.html b/src/app/features/profile/components/profile-information/profile-information.component.html index fb23fca21..b4636126f 100644 --- a/src/app/features/profile/components/profile-information/profile-information.component.html +++ b/src/app/features/profile/components/profile-information/profile-information.component.html @@ -14,12 +14,10 @@

{{ currentUser()?.fullName }}

- @if (currentUser()?.social?.orcid) { + @if (orcidId()) { } diff --git a/src/app/features/profile/components/profile-information/profile-information.component.ts b/src/app/features/profile/components/profile-information/profile-information.component.ts index 25d7f1a4d..3304a8ce2 100644 --- a/src/app/features/profile/components/profile-information/profile-information.component.ts +++ b/src/app/features/profile/components/profile-information/profile-information.component.ts @@ -43,6 +43,11 @@ export class ProfileInformationComponent { userSocials = computed(() => mapUserSocials(this.currentUser()?.social, SOCIAL_LINKS)); + orcidId = computed(() => { + const orcid = this.currentUser()?.external_identity?.ORCID; + return orcid?.status?.toUpperCase() === 'VERIFIED' ? orcid.id : undefined; + }); + toProfileSettings() { this.editProfile.emit(); } diff --git a/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts b/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts index 2c9f96421..660924152 100644 --- a/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts +++ b/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts @@ -1,5 +1,5 @@ export enum SubscriptionFrequency { Never = 'none', Daily = 'daily', - Instant = 'instant', + Instant = 'instantly', } diff --git a/src/app/shared/mappers/user/user.mapper.ts b/src/app/shared/mappers/user/user.mapper.ts index e55a1faac..2a735dd38 100644 --- a/src/app/shared/mappers/user/user.mapper.ts +++ b/src/app/shared/mappers/user/user.mapper.ts @@ -32,6 +32,7 @@ export class UserMapper { employment: user.attributes.employment, iri: user.links.iri, social: user.attributes.social, + external_identity: user.attributes.external_identity, defaultRegionId: user.relationships?.default_region?.data?.id, canViewReviews: user.attributes.can_view_reviews === true, // [NS] Do not simplify it timezone: user.attributes.timezone, diff --git a/src/app/shared/models/user/external-identity.model.ts b/src/app/shared/models/user/external-identity.model.ts new file mode 100644 index 000000000..9703400e0 --- /dev/null +++ b/src/app/shared/models/user/external-identity.model.ts @@ -0,0 +1,8 @@ +export interface OrcidInfo { + id: string; + status: string; +} + +export interface ExternalIdentityModel { + ORCID?: OrcidInfo | null; +} diff --git a/src/app/shared/models/user/user-json-api.model.ts b/src/app/shared/models/user/user-json-api.model.ts index 07ce651e0..0d3848e77 100644 --- a/src/app/shared/models/user/user-json-api.model.ts +++ b/src/app/shared/models/user/user-json-api.model.ts @@ -2,6 +2,7 @@ import { ResponseDataJsonApi } from '../common/json-api.model'; import { Education } from './education.model'; import { Employment } from './employment.model'; +import { ExternalIdentityModel } from './external-identity.model'; import { SocialModel } from './social.model'; export type UserResponseJsonApi = ResponseDataJsonApi; @@ -47,6 +48,7 @@ export interface UserAttributesJsonApi { suffix: string; locale: string; social: SocialModel; + external_identity: ExternalIdentityModel; timezone: string; } diff --git a/src/app/shared/models/user/user.models.ts b/src/app/shared/models/user/user.models.ts index 22ee27f4e..8371e46e7 100644 --- a/src/app/shared/models/user/user.models.ts +++ b/src/app/shared/models/user/user.models.ts @@ -1,5 +1,6 @@ import { Education } from './education.model'; import { Employment } from './employment.model'; +import { ExternalIdentityModel } from './external-identity.model'; import { SocialModel } from './social.model'; export interface UserData { @@ -24,6 +25,7 @@ export interface UserModel { timezone: string; locale: string; social: SocialModel; + external_identity: ExternalIdentityModel; defaultRegionId: string; link?: string; iri?: string; diff --git a/src/main.ts b/src/main.ts index 67d73f19f..f2cc9c774 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,12 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from '@osf/app.component'; import { appConfig } from '@osf/app.config'; -bootstrapApplication(AppComponent, appConfig).catch((err) => +import 'cedar-embeddable-editor'; +import 'cedar-artifact-viewer'; + +bootstrapApplication(AppComponent, { + providers: [...appConfig.providers], +}).catch((err) => // eslint-disable-next-line no-console console.error(err) ); diff --git a/src/styles/styles.scss b/src/styles/styles.scss index a51b536b8..f192f1d5d 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -7,11 +7,6 @@ @use "base"; @use "icons"; -@use "@fortawesome/fontawesome-free/scss/fontawesome.scss"; -@use "@fortawesome/fontawesome-free/scss/solid.scss"; -@use "@fortawesome/fontawesome-free/scss/brands.scss"; -@use "@fortawesome/fontawesome-free/scss/regular.scss"; - @use "./components/md-editor"; @use "./components/preprints"; @use "./components/collections";