diff --git a/src/app/file-browser/components/file-viewer/file-viewer.component.html b/src/app/file-browser/components/file-viewer/file-viewer.component.html index 632471ff6..7d19ad537 100644 --- a/src/app/file-browser/components/file-viewer/file-viewer.component.html +++ b/src/app/file-browser/components/file-viewer/file-viewer.component.html @@ -125,26 +125,38 @@ > } + @if (showEdtfDatePicker) { +
| Date | -
- |
-
| Date | +
+ |
+
| Uploaded | {{ currentRecord.createdDT | date }} | diff --git a/src/app/file-browser/components/file-viewer/file-viewer.component.spec.ts b/src/app/file-browser/components/file-viewer/file-viewer.component.spec.ts index 7bc80b80e..db8cb326d 100644 --- a/src/app/file-browser/components/file-viewer/file-viewer.component.spec.ts +++ b/src/app/file-browser/components/file-viewer/file-viewer.component.spec.ts @@ -15,7 +15,13 @@ import { ApiService } from '@shared/services/api/api.service'; import { FeatureFlagService } from '@root/app/feature-flag/services/feature-flag.service'; import { MockComponent } from 'ng-mocks'; import { GetThumbnailPipe } from '@shared/pipes/get-thumbnail.pipe'; +import { MessageService } from '@shared/services/message/message.service'; +import { + DateTimeModel, + EdtfService, +} from '@shared/services/edtf-service/edtf.service'; import { TagsComponent } from '../../../shared/components/tags/tags.component'; +import { EditDateTimeModalService } from '../edit-date-time-modal/edit-date-time-modal.service'; import { FileViewerComponent } from './file-viewer.component'; @Pipe({ name: 'dsFileSize', standalone: false }) @@ -229,6 +235,19 @@ describe('FileViewerComponent', () => { isEnabled: (flag: string) => featureFlagsEnabled.get(flag) ?? false, }, }, + { + provide: MessageService, + useValue: { + showError: () => {}, + showMessage: () => {}, + }, + }, + { + provide: EditDateTimeModalService, + useValue: { + open: () => ({ closed: { subscribe: () => {} } }), + }, + }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }).compileComponents(); @@ -244,6 +263,68 @@ describe('FileViewerComponent', () => { expect(component).not.toBeNull(); }); + describe('edtf-date feature flag', () => { + it('should show the EDTF date picker when the edtf-date flag is enabled', async () => { + featureFlagsEnabled.set('edtf-date', true); + await recreateComponent(); + + expect(component.showEdtfDatePicker).toBe(true); + expect( + fixture.nativeElement.querySelector('pr-sidebar-date-picker'), + ).toBeTruthy(); + }); + + it('should show the legacy date field and hide the EDTF picker when the edtf-date flag is disabled', async () => { + featureFlagsEnabled.set('edtf-date', false); + await recreateComponent(); + + expect(component.showEdtfDatePicker).toBe(false); + expect( + fixture.nativeElement.querySelector('pr-sidebar-date-picker'), + ).toBeNull(); + + const dateRowLabel = Array.from( + fixture.nativeElement.querySelectorAll('.metadata-table td'), + ).find((td: HTMLElement) => td.textContent?.trim() === 'Date'); + + expect(dateRowLabel).toBeTruthy(); + }); + }); + + describe('EDTF date handling', () => { + const recordWithDate = () => + new RecordVO({ + type: 'document', + displayName: 'Dated Doc', + TagVOs: [], + displayTime: '1985-05-20', + }); + + it('should compute the cached display time from the record on init', async () => { + activatedRouteData.currentRecord = recordWithDate(); + await recreateComponent(); + + expect(component.displayTimeObject?.date.year).toBe('1985'); + }); + + it('should reset the cached display time and show one error when an invalid date is saved', async () => { + activatedRouteData.currentRecord = recordWithDate(); + await recreateComponent(); + + const edtfService = TestBed.inject(EdtfService); + spyOn(edtfService, 'toEdtfDate').and.throwError('invalid date'); + const showErrorSpy = spyOn(TestBed.inject(MessageService), 'showError'); + + await component.onDateSaved({ + date: { year: 'bad' } as never, + time: { format: 'am' }, + } as DateTimeModel); + + expect(showErrorSpy).toHaveBeenCalledTimes(1); + expect(component.displayTimeObject?.date.year).toBe('1985'); + }); + }); + it('should have two tags components', () => { const tagsComponents = fixture.nativeElement.querySelectorAll('pr-tags'); diff --git a/src/app/file-browser/components/file-viewer/file-viewer.component.ts b/src/app/file-browser/components/file-viewer/file-viewer.component.ts index 9dd8b679a..c918383e8 100644 --- a/src/app/file-browser/components/file-viewer/file-viewer.component.ts +++ b/src/app/file-browser/components/file-viewer/file-viewer.component.ts @@ -30,7 +30,13 @@ import { GetAccessFile } from '@models/get-access-file'; import { ShareLinksService } from '@root/app/share-links/services/share-links.service'; import { ApiService } from '@shared/services/api/api.service'; import { FeatureFlagService } from '@root/app/feature-flag/services/feature-flag.service'; +import { + DateTimeModel, + EdtfService, +} from '@shared/services/edtf-service/edtf.service'; +import { MessageService } from '@shared/services/message/message.service'; import { TagsService } from '../../../core/services/tags/tags.service'; +import { EditDateTimeModalService } from '../edit-date-time-modal/edit-date-time-modal.service'; @Component({ selector: 'pr-file-viewer', @@ -62,6 +68,12 @@ export class FileViewerComponent implements OnInit, OnDestroy { public canEdit: boolean; + public showEdtfDatePicker = false; + + public editingDate: boolean = false; + + public displayTimeObject: DateTimeModel | null = null; + // Swiping private touchElement: HTMLElement; private thumbElement: HTMLElement; @@ -77,10 +89,10 @@ export class FileViewerComponent implements OnInit, OnDestroy { // UI public useMinimalView = false; - public editingDate: boolean = false; private bodyScrollTop: number; private itemTagsSubscription: Subscription; private tagsSubscription: Subscription; + private dateModalSubscription?: Subscription; private isUnlistedShare = true; constructor( @@ -88,6 +100,7 @@ export class FileViewerComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private element: ElementRef, private dataService: DataService, + private message: MessageService, @Inject(DOCUMENT) private document: any, public sanitizer: DomSanitizer, private accountService: AccountService, @@ -97,10 +110,14 @@ export class FileViewerComponent implements OnInit, OnDestroy { private shareLinksService: ShareLinksService, private api: ApiService, private feature: FeatureFlagService, + private edtfService: EdtfService, + private editDateTimeModalService: EditDateTimeModalService, ) { // store current scroll position in file list this.bodyScrollTop = window.scrollY; + this.showEdtfDatePicker = this.feature.isEnabled('edtf-date'); + const resolvedRecord = route.snapshot.data.currentRecord; this.allTags = tagsService.getTags(); @@ -186,6 +203,7 @@ export class FileViewerComponent implements OnInit, OnDestroy { }); this.itemTagsSubscription.unsubscribe(); this.tagsSubscription.unsubscribe(); + this.dateModalSubscription?.unsubscribe(); } private setRecordsToPreview(resolvedRecord: RecordVO) { @@ -244,6 +262,7 @@ export class FileViewerComponent implements OnInit, OnDestroy { this.replayUrl = this.getReplayUrl(); } this.setCurrentTags(); + this.updateDisplayTimeObject(); } toggleSwipe(value: boolean) { @@ -432,6 +451,50 @@ export class FileViewerComponent implements OnInit, OnDestroy { } } + private updateDisplayTimeObject(): void { + const timeSource = + this.currentRecord?.displayTime || this.currentRecord?.displayDT; + try { + this.displayTimeObject = timeSource + ? this.edtfService.toDateTimeModel(timeSource) + : null; + } catch (err) { + this.displayTimeObject = null; + this.message.showError({ message: err?.message }); + } + } + + public async onDateSaved(result: DateTimeModel): Promise