diff --git a/src/app/features/project/files/components/file-metadata/file-metadata.component.scss b/src/app/features/files/components/file-metadata/file-metadata.component.scss
similarity index 100%
rename from src/app/features/project/files/components/file-metadata/file-metadata.component.scss
rename to src/app/features/files/components/file-metadata/file-metadata.component.scss
diff --git a/src/app/features/project/files/components/file-metadata/file-metadata.component.spec.ts b/src/app/features/files/components/file-metadata/file-metadata.component.spec.ts
similarity index 100%
rename from src/app/features/project/files/components/file-metadata/file-metadata.component.spec.ts
rename to src/app/features/files/components/file-metadata/file-metadata.component.spec.ts
diff --git a/src/app/features/project/files/components/file-metadata/file-metadata.component.ts b/src/app/features/files/components/file-metadata/file-metadata.component.ts
similarity index 87%
rename from src/app/features/project/files/components/file-metadata/file-metadata.component.ts
rename to src/app/features/files/components/file-metadata/file-metadata.component.ts
index 10b04affb..0e87413ed 100644
--- a/src/app/features/project/files/components/file-metadata/file-metadata.component.ts
+++ b/src/app/features/files/components/file-metadata/file-metadata.component.ts
@@ -12,10 +12,9 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
-import { ProjectFilesSelectors, SetFileMetadata } from '@osf/features/project/files/store';
-
import { FileMetadataFields } from '../../constants';
import { PatchFileMetadata } from '../../models';
+import { FilesSelectors, SetFileMetadata } from '../../store';
import { EditFileMetadataDialogComponent } from '../edit-file-metadata-dialog/edit-file-metadata-dialog.component';
import { environment } from 'src/environments/environment';
@@ -34,8 +33,8 @@ export class FileMetadataComponent {
private readonly dialogService = inject(DialogService);
private readonly translateService = inject(TranslateService);
- fileMetadata = select(ProjectFilesSelectors.getFileCustomMetadata);
- isLoading = select(ProjectFilesSelectors.isFileMetadataLoading);
+ fileMetadata = select(FilesSelectors.getFileCustomMetadata);
+ isLoading = select(FilesSelectors.isFileMetadataLoading);
readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid'])) ?? of(undefined));
@@ -60,7 +59,7 @@ export class FileMetadataComponent {
.open(EditFileMetadataDialogComponent, {
width: '448px',
focusOnShow: false,
- header: this.translateService.instant('project.files.detail.fileMetadata.edit'),
+ header: this.translateService.instant('files.detail.fileMetadata.edit'),
closeOnEscape: true,
modal: true,
closable: true,
diff --git a/src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.html b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.html
similarity index 51%
rename from src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.html
rename to src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.html
index b71dd21f0..98142cf0f 100644
--- a/src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.html
+++ b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.html
@@ -1,35 +1,37 @@
-
{{ 'project.files.detail.projectMetadata.title' | translate }}
+
+ {{ 'files.detail.resourceMetadata.title.' + resourceType() | translate }}
+
- @if (isProjectMetadataLoading()) {
+ @if (isResourceMetadataLoading()) {
} @else {
- @for (funder of projectMetadata()?.funders; track $index) {
+ @for (funder of resourceMetadata()?.funders; track $index) {
@if (funder.funderName) {
-
{{ 'project.files.detail.projectMetadata.fields.funder' | translate }}
+ {{ 'files.detail.resourceMetadata.fields.funder' | translate }}
{{ funder.funderName }}
}
@if (funder.awardTitle) {
-
{{ 'project.files.detail.projectMetadata.fields.awardTitle' | translate }}
+ {{ 'files.detail.resourceMetadata.fields.awardTitle' | translate }}
{{ funder.awardTitle }}
}
@if (funder.awardTitle) {
-
{{ 'project.files.detail.projectMetadata.fields.awardNumber' | translate }}
+ {{ 'files.detail.resourceMetadata.fields.awardNumber' | translate }}
{{ funder.awardTitle }}
}
@if (funder.awardUri) {
-
{{ 'project.files.detail.projectMetadata.fields.awardUri' | translate }}
+ {{ 'files.detail.resourceMetadata.fields.awardUri' | translate }}
{{ funder.awardUri }}
}
@@ -37,72 +39,72 @@
{{ 'project.files.detail.projectMetadata.fields.awardUri' | translate }}
-
{{ 'project.files.detail.projectMetadata.fields.title' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.title' | translate }}
-
{{ projectMetadata()?.title }}
+
{{ resourceMetadata()?.title }}
- @if (projectMetadata()?.description) {
+ @if (resourceMetadata()?.description) {
-
{{ 'project.files.detail.projectMetadata.fields.description' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.description' | translate }}
- {{ projectMetadata()?.description }}
+ {{ resourceMetadata()?.description }}
}
- @if (projectMetadata()?.resourceTypeGeneral) {
+ @if (resourceMetadata()?.resourceTypeGeneral) {
-
{{ 'project.files.detail.projectMetadata.fields.resourceType' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.resourceType' | translate }}
- {{ projectMetadata()?.resourceTypeGeneral }}
+ {{ resourceMetadata()?.resourceTypeGeneral }}
}
- @if (projectMetadata()?.language) {
+ @if (resourceMetadata()?.language) {
-
{{ 'project.files.detail.projectMetadata.fields.resourceLanguage' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.resourceLanguage' | translate }}
- {{ projectMetadata()?.language }}
+ {{ resourceMetadata()?.language }}
}
- @if (projectMetadata()?.dateCreated) {
+ @if (resourceMetadata()?.dateCreated) {
-
{{ 'project.files.detail.projectMetadata.fields.dateCreated' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.dateCreated' | translate }}
- {{ projectMetadata()?.dateCreated | date: 'MMMM d, y' }}
+ {{ resourceMetadata()?.dateCreated | date: 'MMMM d, y' }}
}
- @if (projectMetadata()?.dateModified) {
+ @if (resourceMetadata()?.dateModified) {
-
{{ 'project.files.detail.projectMetadata.fields.dateModified' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.dateModified' | translate }}
- {{ projectMetadata()?.dateModified | date: 'MMMM d, y' }}
+ {{ resourceMetadata()?.dateModified | date: 'MMMM d, y' }}
}
}
- @if (isProjectContributorsLoading()) {
+ @if (isResourceContributorsLoading()) {
} @else {
@if (contributors()?.length) {
-
{{ 'project.files.detail.projectMetadata.fields.contributors' | translate }}
+
{{ 'files.detail.resourceMetadata.fields.contributors' | translate }}
@for (contributor of contributors(); track $index) {
-
{{ contributor.name }}
+
{{ contributor.fullName }}
@if (!$last) {
,
}
diff --git a/src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.scss b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.scss
similarity index 100%
rename from src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.scss
rename to src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.scss
diff --git a/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.spec.ts b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.spec.ts
new file mode 100644
index 000000000..ca59b6b86
--- /dev/null
+++ b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FileResourceMetadataComponent } from './file-resource-metadata.component';
+
+describe('FileResourceMetadataComponent', () => {
+ let component: FileResourceMetadataComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [FileResourceMetadataComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(FileResourceMetadataComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.ts b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.ts
new file mode 100644
index 000000000..8043b6459
--- /dev/null
+++ b/src/app/features/files/components/file-resource-metadata/file-resource-metadata.component.ts
@@ -0,0 +1,25 @@
+import { select } from '@ngxs/store';
+
+import { TranslatePipe } from '@ngx-translate/core';
+
+import { Skeleton } from 'primeng/skeleton';
+
+import { DatePipe } from '@angular/common';
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+
+import { FilesSelectors } from '../../store';
+
+@Component({
+ selector: 'osf-file-resource-metadata',
+ imports: [DatePipe, TranslatePipe, Skeleton],
+ templateUrl: './file-resource-metadata.component.html',
+ styleUrl: './file-resource-metadata.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FileResourceMetadataComponent {
+ resourceType = input('nodes');
+ resourceMetadata = select(FilesSelectors.getResourceMetadata);
+ contributors = select(FilesSelectors.getContributors);
+ isResourceMetadataLoading = select(FilesSelectors.isResourceMetadataLoading);
+ isResourceContributorsLoading = select(FilesSelectors.isResourceContributorsLoading);
+}
diff --git a/src/app/features/project/files/components/file-revisions/file-revisions.component.html b/src/app/features/files/components/file-revisions/file-revisions.component.html
similarity index 77%
rename from src/app/features/project/files/components/file-revisions/file-revisions.component.html
rename to src/app/features/files/components/file-revisions/file-revisions.component.html
index 7fb8c95a3..2c7e02e54 100644
--- a/src/app/features/project/files/components/file-revisions/file-revisions.component.html
+++ b/src/app/features/files/components/file-revisions/file-revisions.component.html
@@ -1,6 +1,6 @@
-
{{ 'project.files.detail.revisions.title' | translate }}
+ {{ 'files.detail.revisions.title' | translate }}
@if (isLoading()) {
@@ -18,12 +18,12 @@
{{ 'project.files.detail.revisions.title' | translate }}
@@ -31,12 +31,12 @@
{{ 'project.files.detail.revisions.title' | translate }}
@@ -45,7 +45,7 @@
{{ 'project.files.detail.revisions.title' | translate }}
diff --git a/src/app/features/project/files/components/file-revisions/file-revisions.component.scss b/src/app/features/files/components/file-revisions/file-revisions.component.scss
similarity index 100%
rename from src/app/features/project/files/components/file-revisions/file-revisions.component.scss
rename to src/app/features/files/components/file-revisions/file-revisions.component.scss
diff --git a/src/app/features/project/files/components/file-revisions/file-revisions.component.spec.ts b/src/app/features/files/components/file-revisions/file-revisions.component.spec.ts
similarity index 100%
rename from src/app/features/project/files/components/file-revisions/file-revisions.component.spec.ts
rename to src/app/features/files/components/file-revisions/file-revisions.component.spec.ts
diff --git a/src/app/features/project/files/components/file-revisions/file-revisions.component.ts b/src/app/features/files/components/file-revisions/file-revisions.component.ts
similarity index 80%
rename from src/app/features/project/files/components/file-revisions/file-revisions.component.ts
rename to src/app/features/files/components/file-revisions/file-revisions.component.ts
index b0b4ab493..2fc72a763 100644
--- a/src/app/features/project/files/components/file-revisions/file-revisions.component.ts
+++ b/src/app/features/files/components/file-revisions/file-revisions.component.ts
@@ -13,8 +13,10 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
-import { ProjectFilesSelectors } from '@osf/features/project/files/store';
-import { CopyButtonComponent, InfoIconComponent } from '@shared/components';
+import { CopyButtonComponent } from '@osf/shared/components';
+import { InfoIconComponent } from '@osf/shared/components/info-icon/info-icon.component';
+
+import { FilesSelectors } from '../../store';
import { environment } from 'src/environments/environment';
@@ -31,16 +33,16 @@ import { environment } from 'src/environments/environment';
Button,
DatePipe,
TranslatePipe,
- InfoIconComponent,
CopyButtonComponent,
Skeleton,
+ InfoIconComponent,
],
})
export class FileRevisionsComponent {
private readonly route = inject(ActivatedRoute);
- readonly fileRevisions = select(ProjectFilesSelectors.getFileRevisions);
- readonly isLoading = select(ProjectFilesSelectors.isFileRevisionsLoading);
+ readonly fileRevisions = select(FilesSelectors.getFileRevisions);
+ readonly isLoading = select(FilesSelectors.isFileRevisionsLoading);
readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid'])) ?? of(undefined));
downloadRevision(version: string): void {
diff --git a/src/app/features/project/files/components/index.ts b/src/app/features/files/components/index.ts
similarity index 57%
rename from src/app/features/project/files/components/index.ts
rename to src/app/features/files/components/index.ts
index 3325c793f..80c02901d 100644
--- a/src/app/features/project/files/components/index.ts
+++ b/src/app/features/files/components/index.ts
@@ -2,9 +2,7 @@ export { CreateFolderDialogComponent } from './create-folder-dialog/create-folde
export { EditFileMetadataDialogComponent } from './edit-file-metadata-dialog/edit-file-metadata-dialog.component';
export { FileKeywordsComponent } from './file-keywords/file-keywords.component';
export { FileMetadataComponent } from './file-metadata/file-metadata.component';
-export { FileProjectMetadataComponent } from './file-project-metadata/file-project-metadata.component';
+export { FileResourceMetadataComponent } from './file-resource-metadata/file-resource-metadata.component';
+export { FileRevisionsComponent } from './file-revisions/file-revisions.component';
export { MoveFileDialogComponent } from './move-file-dialog/move-file-dialog.component';
export { RenameFileDialogComponent } from './rename-file-dialog/rename-file-dialog.component';
-export { FileRevisionsComponent } from '@osf/features/project/files/components/file-revisions/file-revisions.component';
-export { FileDetailComponent } from '@osf/features/project/files/pages/file-detail/file-detail.component';
-export { FilesTreeComponent } from '@shared/components/files-tree/files-tree.component';
diff --git a/src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.html b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.html
similarity index 87%
rename from src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.html
rename to src/app/features/files/components/move-file-dialog/move-file-dialog.component.html
index 563817c83..fb2589340 100644
--- a/src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.html
+++ b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.html
@@ -6,7 +6,7 @@
-
{{ 'project.files.dialogs.moveFile.storage' | translate }}
+
{{ 'files.dialogs.moveFile.storage' | translate }}
@@ -37,10 +37,7 @@
{{ 'project.files.dialogs.moveFile.storage' | translate
} @else if (config.data.file.id === file.id) {
-
+
{{ file?.name ?? '' }}
} @else {
@@ -60,7 +57,7 @@
{{ 'project.files.dialogs.moveFile.storage' | translate
}
@if (!files().length) {
-
{{ 'project.files.emptyState' | translate }}
+ {{ 'files.emptyState' | translate }}
}
diff --git a/src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.scss b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.scss
similarity index 100%
rename from src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.scss
rename to src/app/features/files/components/move-file-dialog/move-file-dialog.component.scss
diff --git a/src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.spec.ts b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts
similarity index 100%
rename from src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.spec.ts
rename to src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts
diff --git a/src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.ts b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts
similarity index 88%
rename from src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.ts
rename to src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts
index 2cb2615be..0b4ba48e4 100644
--- a/src/app/features/project/files/components/move-file-dialog/move-file-dialog.component.ts
+++ b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts
@@ -14,13 +14,13 @@ import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, signa
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
+ FilesSelectors,
GetFiles,
GetMoveFileFiles,
GetRootFolderFiles,
- ProjectFilesSelectors,
SetCurrentFolder,
SetMoveFileCurrentFolder,
-} from '@osf/features/project/files/store';
+} from '@osf/features/files/store';
import { IconComponent, LoadingSpinnerComponent } from '@shared/components';
import { OsfFile } from '@shared/models';
import { FilesService, ToastService } from '@shared/services';
@@ -41,15 +41,15 @@ export class MoveFileDialogComponent {
private readonly translateService = inject(TranslateService);
private readonly toastService = inject(ToastService);
- protected readonly files = select(ProjectFilesSelectors.getMoveFileFiles);
- protected readonly isLoading = select(ProjectFilesSelectors.isMoveFileFilesLoading);
- protected readonly currentFolder = select(ProjectFilesSelectors.getMoveFileCurrentFolder);
- private readonly rootFolders = select(ProjectFilesSelectors.getRootFolders);
+ protected readonly files = select(FilesSelectors.getMoveFileFiles);
+ protected readonly isLoading = select(FilesSelectors.isMoveFileFilesLoading);
+ protected readonly currentFolder = select(FilesSelectors.getMoveFileCurrentFolder);
+ private readonly rootFolders = select(FilesSelectors.getRootFolders);
protected readonly isFilesUpdating = signal(false);
protected readonly isFolderSame = computed(() => {
return this.currentFolder()?.id === this.config.data.file.relationships.parentFolderId;
});
- protected readonly provider = select(ProjectFilesSelectors.getProvider);
+ protected readonly provider = select(FilesSelectors.getProvider);
protected readonly dispatch = createDispatchMap({
getMoveFileFiles: GetMoveFileFiles,
@@ -105,7 +105,7 @@ export class MoveFileDialogComponent {
let path = this.currentFolder()?.path;
if (!path) {
- throw new Error(this.translateService.instant('project.files.dialogs.moveFile.pathError'));
+ throw new Error(this.translateService.instant('files.dialogs.moveFile.pathError'));
}
if (!this.currentFolder()?.relationships.parentFolderLink) {
diff --git a/src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.html b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html
similarity index 82%
rename from src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.html
rename to src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html
index 03cf87dde..2abb1b505 100644
--- a/src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.html
+++ b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html
@@ -2,8 +2,8 @@
diff --git a/src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.spec.ts b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.spec.ts
similarity index 100%
rename from src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.spec.ts
rename to src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.spec.ts
diff --git a/src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.ts b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts
similarity index 100%
rename from src/app/features/project/files/components/rename-file-dialog/rename-file-dialog.component.ts
rename to src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts
diff --git a/src/app/features/project/files/models/data/embed-content.const.ts b/src/app/features/files/constants/embed-content.constants.ts
similarity index 100%
rename from src/app/features/project/files/models/data/embed-content.const.ts
rename to src/app/features/files/constants/embed-content.constants.ts
diff --git a/src/app/features/files/constants/file-metadata-fields.constants.ts b/src/app/features/files/constants/file-metadata-fields.constants.ts
new file mode 100644
index 000000000..0b9617fa4
--- /dev/null
+++ b/src/app/features/files/constants/file-metadata-fields.constants.ts
@@ -0,0 +1,8 @@
+import { MetadataField } from '../models';
+
+export const FileMetadataFields: MetadataField[] = [
+ { key: 'title', label: 'files.detail.fileMetadata.fields.title' },
+ { key: 'description', label: 'files.detail.fileMetadata.fields.description' },
+ { key: 'resourceTypeGeneral', label: 'files.detail.fileMetadata.fields.resourceType' },
+ { key: 'language', label: 'files.detail.fileMetadata.fields.resourceLanguage' },
+];
diff --git a/src/app/features/project/files/models/data/file-provider.const.ts b/src/app/features/files/constants/file-provider.constants.ts
similarity index 90%
rename from src/app/features/project/files/models/data/file-provider.const.ts
rename to src/app/features/files/constants/file-provider.constants.ts
index 546a640d5..01df352e3 100644
--- a/src/app/features/project/files/models/data/file-provider.const.ts
+++ b/src/app/features/files/constants/file-provider.constants.ts
@@ -6,4 +6,5 @@ export const FileProvider = {
OneDrive: 'onedrive',
WebDav: 'webdav',
S3: 's3',
+ GitHub: 'github',
};
diff --git a/src/app/features/files/constants/index.ts b/src/app/features/files/constants/index.ts
new file mode 100644
index 000000000..72af33152
--- /dev/null
+++ b/src/app/features/files/constants/index.ts
@@ -0,0 +1,3 @@
+export * from './embed-content.constants';
+export * from './file-metadata-fields.constants';
+export * from './file-provider.constants';
diff --git a/src/app/features/project/files/enums/file-detail-tab.enum.ts b/src/app/features/files/enums/file-detail-tab.enum.ts
similarity index 100%
rename from src/app/features/project/files/enums/file-detail-tab.enum.ts
rename to src/app/features/files/enums/file-detail-tab.enum.ts
diff --git a/src/app/features/files/enums/index.ts b/src/app/features/files/enums/index.ts
new file mode 100644
index 000000000..01282d755
--- /dev/null
+++ b/src/app/features/files/enums/index.ts
@@ -0,0 +1 @@
+export * from './file-detail-tab.enum';
diff --git a/src/app/features/files/files.routes.ts b/src/app/features/files/files.routes.ts
new file mode 100644
index 000000000..e9ebf8b53
--- /dev/null
+++ b/src/app/features/files/files.routes.ts
@@ -0,0 +1,30 @@
+import { Routes } from '@angular/router';
+
+import { FilesContainerComponent } from './pages/files-container/files-container.component';
+
+export const filesRoutes: Routes = [
+ {
+ path: '',
+ component: FilesContainerComponent,
+ children: [
+ {
+ path: '',
+ loadComponent: () => import('@osf/features/files/pages/files/files.component').then((c) => c.FilesComponent),
+ },
+ {
+ path: ':fileGuid',
+ loadComponent: () =>
+ import('@osf/features/files/pages/file-detail/file-detail.component').then((c) => c.FileDetailComponent),
+ children: [
+ {
+ path: 'metadata',
+ loadComponent: () =>
+ import('@osf/features/files/pages/community-metadata/community-metadata.component').then(
+ (c) => c.CommunityMetadataComponent
+ ),
+ },
+ ],
+ },
+ ],
+ },
+];
diff --git a/src/app/features/project/files/mappers/file-custom-metadata.mapper.ts b/src/app/features/files/mappers/file-custom-metadata.mapper.ts
similarity index 79%
rename from src/app/features/project/files/mappers/file-custom-metadata.mapper.ts
rename to src/app/features/files/mappers/file-custom-metadata.mapper.ts
index 7b9e66674..dc75ca1e3 100644
--- a/src/app/features/project/files/mappers/file-custom-metadata.mapper.ts
+++ b/src/app/features/files/mappers/file-custom-metadata.mapper.ts
@@ -1,6 +1,7 @@
-import { FileCustomMetadata, OsfFileCustomMetadata } from '@osf/features/project/files/models';
import { ApiData } from '@osf/shared/models';
+import { FileCustomMetadata, OsfFileCustomMetadata } from '../models';
+
export function MapFileCustomMetadata(data: ApiData): OsfFileCustomMetadata {
return {
id: data.id,
diff --git a/src/app/features/files/mappers/file-revision.mapper.ts b/src/app/features/files/mappers/file-revision.mapper.ts
new file mode 100644
index 000000000..764b7718d
--- /dev/null
+++ b/src/app/features/files/mappers/file-revision.mapper.ts
@@ -0,0 +1,13 @@
+import { ApiData } from '@osf/shared/models';
+
+import { FileRevisionJsonApi, OsfFileRevision } from '../models';
+
+export function MapFileRevision(data: ApiData[]): OsfFileRevision[] {
+ const revision = data.map((revision) => ({
+ downloads: revision.attributes.extra.downloads,
+ hashes: { md5: revision.attributes.extra.hashes?.md5, sha256: revision.attributes.extra.hashes?.sha256 },
+ dateTime: new Date(revision.attributes.modified_utc),
+ version: revision.attributes.version,
+ }));
+ return revision;
+}
diff --git a/src/app/features/files/mappers/index.ts b/src/app/features/files/mappers/index.ts
new file mode 100644
index 000000000..96736c05c
--- /dev/null
+++ b/src/app/features/files/mappers/index.ts
@@ -0,0 +1,3 @@
+export * from './file-custom-metadata.mapper';
+export * from './file-revision.mapper';
+export * from './resource-metadata.mapper';
diff --git a/src/app/features/project/files/mappers/project-metadata.mapper.ts b/src/app/features/files/mappers/resource-metadata.mapper.ts
similarity index 66%
rename from src/app/features/project/files/mappers/project-metadata.mapper.ts
rename to src/app/features/files/mappers/resource-metadata.mapper.ts
index caf5a309f..c4f1bf7bf 100644
--- a/src/app/features/project/files/mappers/project-metadata.mapper.ts
+++ b/src/app/features/files/mappers/resource-metadata.mapper.ts
@@ -1,13 +1,12 @@
-import {
- GetProjectCustomMetadataResponse,
- GetProjectShortInfoResponse,
- OsfProjectMetadata,
-} from '@osf/features/project/files/models';
+import { ResourceMetadata } from '@osf/shared/models';
-export function MapProjectMetadata(
- shortInfo: GetProjectShortInfoResponse,
- customMetadata: GetProjectCustomMetadataResponse
-): OsfProjectMetadata {
+import { GetResourceCustomMetadataResponse } from '../models/get-resource-custom-metadata-response.model';
+import { GetResourceShortInfoResponse } from '../models/get-resource-short-info-response.model';
+
+export function MapResourceMetadata(
+ shortInfo: GetResourceShortInfoResponse,
+ customMetadata: GetResourceCustomMetadataResponse
+): ResourceMetadata {
return {
title: shortInfo.data.attributes.title,
description: shortInfo.data.attributes.description,
diff --git a/src/app/features/project/files/models/responses/create-folder-response.model.ts b/src/app/features/files/models/create-folder-response.model.ts
similarity index 100%
rename from src/app/features/project/files/models/responses/create-folder-response.model.ts
rename to src/app/features/files/models/create-folder-response.model.ts
diff --git a/src/app/features/project/files/models/osf-models/file-custom-metadata.model.ts b/src/app/features/files/models/file-custom-metadata.model.ts
similarity index 100%
rename from src/app/features/project/files/models/osf-models/file-custom-metadata.model.ts
rename to src/app/features/files/models/file-custom-metadata.model.ts
diff --git a/src/app/features/project/files/models/osf-models/file-revisions.model.ts b/src/app/features/files/models/file-revisions.model.ts
similarity index 100%
rename from src/app/features/project/files/models/osf-models/file-revisions.model.ts
rename to src/app/features/files/models/file-revisions.model.ts
diff --git a/src/app/features/project/files/models/osf-models/file-target.model.ts b/src/app/features/files/models/file-target.model.ts
similarity index 94%
rename from src/app/features/project/files/models/osf-models/file-target.model.ts
rename to src/app/features/files/models/file-target.model.ts
index 84485a11e..85858e99d 100644
--- a/src/app/features/project/files/models/osf-models/file-target.model.ts
+++ b/src/app/features/files/models/file-target.model.ts
@@ -1,4 +1,5 @@
export interface OsfFileTarget {
+ id: string;
title: string;
description: string;
category: string;
@@ -18,4 +19,5 @@ export interface OsfFileTarget {
currentUserIsContributorOrGroupMember: boolean;
wikiEnabled: boolean;
public: boolean;
+ type: string;
}
diff --git a/src/app/features/project/files/models/files-metadata-fields.ts b/src/app/features/files/models/files-metadata-fields.model.ts
similarity index 52%
rename from src/app/features/project/files/models/files-metadata-fields.ts
rename to src/app/features/files/models/files-metadata-fields.model.ts
index 73534a682..7d06ec9a1 100644
--- a/src/app/features/project/files/models/files-metadata-fields.ts
+++ b/src/app/features/files/models/files-metadata-fields.model.ts
@@ -1,4 +1,4 @@
-import { OsfFileCustomMetadata } from './osf-models/file-custom-metadata.model';
+import { OsfFileCustomMetadata } from './file-custom-metadata.model';
export interface MetadataField {
key: keyof OsfFileCustomMetadata;
diff --git a/src/app/features/project/files/models/responses/get-project-contributors-response.model.ts b/src/app/features/files/models/get-contributors-response.model.ts
similarity index 74%
rename from src/app/features/project/files/models/responses/get-project-contributors-response.model.ts
rename to src/app/features/files/models/get-contributors-response.model.ts
index 66f1475dd..ed19f0e76 100644
--- a/src/app/features/project/files/models/responses/get-project-contributors-response.model.ts
+++ b/src/app/features/files/models/get-contributors-response.model.ts
@@ -1,6 +1,6 @@
import { ApiData, JsonApiResponse } from '@osf/shared/models';
-export type GetProjectContributorsResponse = JsonApiResponse<
+export type GetContributorsResponse = JsonApiResponse<
ApiData<
{
full_name: string;
diff --git a/src/app/features/files/models/get-custom-metadata-response.model.ts b/src/app/features/files/models/get-custom-metadata-response.model.ts
new file mode 100644
index 000000000..947edeff0
--- /dev/null
+++ b/src/app/features/files/models/get-custom-metadata-response.model.ts
@@ -0,0 +1,26 @@
+import { ApiData, JsonApiResponse } from '@osf/shared/models';
+
+export type GetCustomMetadataResponse = JsonApiResponse, null>;
+
+export interface MetadataEmbedResponse {
+ custom_metadata: JsonApiResponse<
+ ApiData<
+ {
+ language: string;
+ resource_type_general: string;
+ funders: {
+ funder_name: string;
+ funder_identifier: string;
+ funder_identifier_type: string;
+ award_number: string;
+ award_uri: string;
+ award_title: string;
+ }[];
+ },
+ null,
+ null,
+ null
+ >,
+ null
+ >;
+}
diff --git a/src/app/features/project/files/models/responses/get-file-metadata-reponse.model.ts b/src/app/features/files/models/get-file-metadata-response.model.ts
similarity index 100%
rename from src/app/features/project/files/models/responses/get-file-metadata-reponse.model.ts
rename to src/app/features/files/models/get-file-metadata-response.model.ts
diff --git a/src/app/features/project/files/models/responses/get-file-revisions-response.model.ts b/src/app/features/files/models/get-file-revisions-response.model.ts
similarity index 100%
rename from src/app/features/project/files/models/responses/get-file-revisions-response.model.ts
rename to src/app/features/files/models/get-file-revisions-response.model.ts
diff --git a/src/app/features/project/files/models/responses/get-file-target-response.model.ts b/src/app/features/files/models/get-file-target-response.model.ts
similarity index 100%
rename from src/app/features/project/files/models/responses/get-file-target-response.model.ts
rename to src/app/features/files/models/get-file-target-response.model.ts
diff --git a/src/app/features/project/files/models/responses/get-project-custom-metadata-response.model.ts b/src/app/features/files/models/get-resource-custom-metadata-response.model.ts
similarity index 74%
rename from src/app/features/project/files/models/responses/get-project-custom-metadata-response.model.ts
rename to src/app/features/files/models/get-resource-custom-metadata-response.model.ts
index b4e3028f1..34b6a7f89 100644
--- a/src/app/features/project/files/models/responses/get-project-custom-metadata-response.model.ts
+++ b/src/app/features/files/models/get-resource-custom-metadata-response.model.ts
@@ -1,11 +1,11 @@
import { ApiData, JsonApiResponse } from '@osf/shared/models';
-export type GetProjectCustomMetadataResponse = JsonApiResponse<
- ApiData,
+export type GetResourceCustomMetadataResponse = JsonApiResponse<
+ ApiData,
null
>;
-export interface ProjectMetadataEmbedResponse {
+export interface ResourceMetadataEmbedResponse {
custom_metadata: JsonApiResponse<
ApiData<
{
diff --git a/src/app/features/files/models/get-resource-short-info-response.model.ts b/src/app/features/files/models/get-resource-short-info-response.model.ts
new file mode 100644
index 000000000..8b50228f3
--- /dev/null
+++ b/src/app/features/files/models/get-resource-short-info-response.model.ts
@@ -0,0 +1,16 @@
+import { ApiData, JsonApiResponse } from '@osf/shared/models';
+
+export type GetResourceShortInfoResponse = JsonApiResponse<
+ ApiData<
+ {
+ title: string;
+ description: string;
+ date_created: string;
+ date_modified: string;
+ },
+ null,
+ null,
+ null
+ >,
+ null
+>;
diff --git a/src/app/features/project/files/models/responses/get-project-short-info-response.model.ts b/src/app/features/files/models/get-short-info-response.model.ts
similarity index 79%
rename from src/app/features/project/files/models/responses/get-project-short-info-response.model.ts
rename to src/app/features/files/models/get-short-info-response.model.ts
index 80a8835a0..da3aed695 100644
--- a/src/app/features/project/files/models/responses/get-project-short-info-response.model.ts
+++ b/src/app/features/files/models/get-short-info-response.model.ts
@@ -1,6 +1,6 @@
import { ApiData, JsonApiResponse } from '@shared/models';
-export type GetProjectShortInfoResponse = JsonApiResponse<
+export type GetShortInfoResponse = JsonApiResponse<
ApiData<
{
title: string;
diff --git a/src/app/features/files/models/index.ts b/src/app/features/files/models/index.ts
new file mode 100644
index 000000000..f3e000f81
--- /dev/null
+++ b/src/app/features/files/models/index.ts
@@ -0,0 +1,12 @@
+export * from './create-folder-response.model';
+export * from './file-custom-metadata.model';
+export * from './file-revisions.model';
+export * from './file-target.model';
+export * from './files-metadata-fields.model';
+export * from './get-contributors-response.model';
+export * from './get-custom-metadata-response.model';
+export * from './get-file-metadata-response.model';
+export * from './get-file-revisions-response.model';
+export * from './get-file-target-response.model';
+export * from './get-short-info-response.model';
+export * from './patch-file-metadata.model';
diff --git a/src/app/features/project/files/models/requests/patch-file-metadata.model.ts b/src/app/features/files/models/patch-file-metadata.model.ts
similarity index 100%
rename from src/app/features/project/files/models/requests/patch-file-metadata.model.ts
rename to src/app/features/files/models/patch-file-metadata.model.ts
diff --git a/src/app/features/project/files/pages/community-metadata/community-metadata.component.html b/src/app/features/files/pages/community-metadata/community-metadata.component.html
similarity index 100%
rename from src/app/features/project/files/pages/community-metadata/community-metadata.component.html
rename to src/app/features/files/pages/community-metadata/community-metadata.component.html
diff --git a/src/app/features/project/files/pages/community-metadata/community-metadata.component.scss b/src/app/features/files/pages/community-metadata/community-metadata.component.scss
similarity index 100%
rename from src/app/features/project/files/pages/community-metadata/community-metadata.component.scss
rename to src/app/features/files/pages/community-metadata/community-metadata.component.scss
diff --git a/src/app/features/project/files/pages/community-metadata/community-metadata.component.spec.ts b/src/app/features/files/pages/community-metadata/community-metadata.component.spec.ts
similarity index 100%
rename from src/app/features/project/files/pages/community-metadata/community-metadata.component.spec.ts
rename to src/app/features/files/pages/community-metadata/community-metadata.component.spec.ts
diff --git a/src/app/features/project/files/pages/community-metadata/community-metadata.component.ts b/src/app/features/files/pages/community-metadata/community-metadata.component.ts
similarity index 100%
rename from src/app/features/project/files/pages/community-metadata/community-metadata.component.ts
rename to src/app/features/files/pages/community-metadata/community-metadata.component.ts
diff --git a/src/app/features/project/files/pages/file-detail/file-detail.component.html b/src/app/features/files/pages/file-detail/file-detail.component.html
similarity index 84%
rename from src/app/features/project/files/pages/file-detail/file-detail.component.html
rename to src/app/features/files/pages/file-detail/file-detail.component.html
index 0ef4b7ea3..9169ca030 100644
--- a/src/app/features/project/files/pages/file-detail/file-detail.component.html
+++ b/src/app/features/files/pages/file-detail/file-detail.component.html
@@ -2,9 +2,9 @@
- {{ 'project.files.detail.tabs.details' | translate }}
- {{ 'project.files.detail.tabs.revisions' | translate }}
- {{ 'project.files.detail.tabs.keywords' | translate }}
+ {{ 'files.detail.tabs.details' | translate }}
+ {{ 'files.detail.tabs.revisions' | translate }}
+ {{ 'files.detail.tabs.keywords' | translate }}
@@ -13,14 +13,14 @@
@@ -91,7 +91,7 @@
} @else {
-
+
}
diff --git a/src/app/features/project/files/pages/file-detail/file-detail.component.scss b/src/app/features/files/pages/file-detail/file-detail.component.scss
similarity index 100%
rename from src/app/features/project/files/pages/file-detail/file-detail.component.scss
rename to src/app/features/files/pages/file-detail/file-detail.component.scss
diff --git a/src/app/features/project/files/pages/file-detail/file-detail.component.spec.ts b/src/app/features/files/pages/file-detail/file-detail.component.spec.ts
similarity index 100%
rename from src/app/features/project/files/pages/file-detail/file-detail.component.spec.ts
rename to src/app/features/files/pages/file-detail/file-detail.component.spec.ts
diff --git a/src/app/features/project/files/pages/file-detail/file-detail.component.ts b/src/app/features/files/pages/file-detail/file-detail.component.ts
similarity index 65%
rename from src/app/features/project/files/pages/file-detail/file-detail.component.ts
rename to src/app/features/files/pages/file-detail/file-detail.component.ts
index bd8cb5be6..8a0c74f1b 100644
--- a/src/app/features/project/files/pages/file-detail/file-detail.component.ts
+++ b/src/app/features/files/pages/file-detail/file-detail.component.ts
@@ -1,4 +1,4 @@
-import { select, Store } from '@ngxs/store';
+import { createDispatchMap, select, Store } from '@ngxs/store';
import { TranslatePipe } from '@ngx-translate/core';
@@ -6,33 +6,34 @@ import { Button } from 'primeng/button';
import { Menu } from 'primeng/menu';
import { Tab, TabList, Tabs } from 'primeng/tabs';
-import { EMPTY, switchMap } from 'rxjs';
+import { switchMap } from 'rxjs';
import { ChangeDetectionStrategy, Component, DestroyRef, HostBinding, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
+import { LoadingSpinnerComponent, SubHeaderComponent } from '@osf/shared/components';
+import { OsfFile } from '@shared/models';
+import { CustomConfirmationService, ToastService } from '@shared/services';
+
import {
FileKeywordsComponent,
FileMetadataComponent,
- FileProjectMetadataComponent,
+ FileResourceMetadataComponent,
FileRevisionsComponent,
-} from '@osf/features/project/files/components';
-import { FileDetailTab } from '@osf/features/project/files/enums/file-detail-tab.enum';
-import { embedDynamicJs, embedStaticHtml } from '@osf/features/project/files/models';
+} from '../../components';
+import { embedDynamicJs, embedStaticHtml } from '../../constants';
+import { FileDetailTab } from '../../enums';
import {
DeleteEntry,
+ FilesSelectors,
GetFile,
GetFileMetadata,
- GetFileProjectContributors,
- GetFileProjectMetadata,
+ GetFileResourceContributors,
+ GetFileResourceMetadata,
GetFileRevisions,
- ProjectFilesSelectors,
-} from '@osf/features/project/files/store';
-import { LoadingSpinnerComponent, SubHeaderComponent } from '@shared/components';
-import { OsfFile } from '@shared/models';
-import { CustomConfirmationService, ToastService } from '@shared/services';
+} from '../../store';
@Component({
selector: 'osf-file-detail',
@@ -49,7 +50,7 @@ import { CustomConfirmationService, ToastService } from '@shared/services';
FileKeywordsComponent,
FileRevisionsComponent,
FileMetadataComponent,
- FileProjectMetadataComponent,
+ FileResourceMetadataComponent,
],
templateUrl: './file-detail.component.html',
styleUrl: './file-detail.component.scss',
@@ -66,10 +67,20 @@ export class FileDetailComponent {
readonly toastService = inject(ToastService);
readonly customConfirmationService = inject(CustomConfirmationService);
- file = select(ProjectFilesSelectors.getOpenedFile);
- isFileLoading = select(ProjectFilesSelectors.isOpenedFileLoading);
+ private readonly actions = createDispatchMap({
+ getFile: GetFile,
+ getFileRevisions: GetFileRevisions,
+ getFileMetadata: GetFileMetadata,
+ getFileResourceMetadata: GetFileResourceMetadata,
+ getFileResourceContributors: GetFileResourceContributors,
+ deleteEntry: DeleteEntry,
+ });
+
+ file = select(FilesSelectors.getOpenedFile);
+ isFileLoading = select(FilesSelectors.isOpenedFileLoading);
safeLink: SafeResourceUrl | null = null;
- projectId: string | null = null;
+ resourceId = '';
+ resourceType = '';
isIframeLoading = true;
@@ -81,66 +92,60 @@ export class FileDetailComponent {
embedItems = [
{
- label: 'project.files.detail.actions.copyDynamicIframe',
+ label: 'files.detail.actions.copyDynamicIframe',
command: () => this.handleCopyDynamicEmbed(),
},
{
- label: 'project.files.detail.actions.copyStaticIframe',
+ label: 'files.detail.actions.copyStaticIframe',
command: () => this.handleCopyStaticEmbed(),
},
];
shareItems = [
{
- label: 'project.files.detail.actions.share.email',
+ label: 'files.detail.actions.share.email',
command: () => this.handleEmailShare(),
},
{
- label: 'project.files.detail.actions.share.x',
+ label: 'files.detail.actions.share.x',
command: () => this.handleXShare(),
},
{
- label: 'project.files.detail.actions.share.facebook',
+ label: 'files.detail.actions.share.facebook',
command: () => this.handleFacebookShare(),
},
];
constructor() {
- this.route.parent?.parent?.parent?.parent?.params.subscribe((params) => {
- if (params['id']) {
- this.projectId = params['id'];
- }
- });
-
this.route.params
.pipe(
takeUntilDestroyed(this.destroyRef),
switchMap((params) => {
this.fileGuid = params['fileGuid'];
- return this.store
- .dispatch(new GetFile(this.fileGuid))
- .pipe(switchMap(() => this.route.parent?.parent?.parent?.params || EMPTY));
+ return this.actions.getFile(this.fileGuid);
})
)
- .subscribe((parentParams) => {
+ .subscribe(() => {
const link = this.file()?.links.render;
if (link) {
this.safeLink = this.sanitizer.bypassSecurityTrustResourceUrl(link);
}
+ this.resourceId = this.file()?.target.id || '';
+ this.resourceType = this.file()?.target.type || '';
+ const fileId = this.file()?.path.replaceAll('/', '');
+ if (this.resourceId && this.resourceType) {
+ this.actions.getFileResourceMetadata(this.resourceId, this.resourceType);
+ this.actions.getFileResourceContributors(this.resourceId, this.resourceType);
- this.projectId = parentParams['id'];
- if (this.projectId) {
- this.store.dispatch(new GetFileProjectMetadata(this.projectId));
- this.store.dispatch(new GetFileProjectContributors(this.projectId));
- const fileId = this.file()?.path.replaceAll('/', '');
if (fileId) {
- this.store.dispatch(new GetFileRevisions(this.projectId, fileId));
+ const fileProvider = this.file()?.provider || '';
+ this.actions.getFileRevisions(this.resourceId, fileProvider, fileId);
}
}
});
this.route.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
- this.store.dispatch(new GetFileMetadata(params['fileGuid']));
+ this.actions.getFileMetadata(params['fileGuid']);
});
}
@@ -152,7 +157,7 @@ export class FileDetailComponent {
navigator.clipboard
.writeText(embedHtml)
.then(() => {
- this.toastService.showSuccess('project.files.detail.toast.copiedToClipboard');
+ this.toastService.showSuccess('files.detail.toast.copiedToClipboard');
})
.catch((err) => {
this.toastService.showError(err.message);
@@ -160,21 +165,23 @@ export class FileDetailComponent {
}
deleteEntry(link: string): void {
- if (this.projectId) {
- this.store
- .dispatch(new DeleteEntry(this.projectId, link))
+ if (this.resourceId) {
+ const redirectUrl =
+ this.resourceType === 'nodes' ? `/project/${this.resourceId}/files` : `/registry/${this.resourceId}/files`;
+ this.actions
+ .deleteEntry(this.resourceId, link)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
- this.router.navigate(['/project', this.projectId, 'files']);
+ this.router.navigate([redirectUrl]);
});
}
}
confirmDelete(file: OsfFile): void {
this.customConfirmationService.confirmDelete({
- headerKey: 'project.files.dialogs.deleteFile.title',
+ headerKey: 'files.dialogs.deleteFile.title',
messageParams: { name: file.name },
- messageKey: 'project.files.dialogs.deleteFile.message',
+ messageKey: 'files.dialogs.deleteFile.message',
onConfirm: () => this.deleteEntry(file.links.delete),
});
}
diff --git a/src/app/features/project/files/pages/project-files-container/project-files-container.component.html b/src/app/features/files/pages/files-container/files-container.component.html
similarity index 100%
rename from src/app/features/project/files/pages/project-files-container/project-files-container.component.html
rename to src/app/features/files/pages/files-container/files-container.component.html
diff --git a/src/app/features/project/files/pages/project-files-container/project-files-container.component.spec.ts b/src/app/features/files/pages/files-container/files-container.component.spec.ts
similarity index 51%
rename from src/app/features/project/files/pages/project-files-container/project-files-container.component.spec.ts
rename to src/app/features/files/pages/files-container/files-container.component.spec.ts
index f2e7467a8..673b9b09c 100644
--- a/src/app/features/project/files/pages/project-files-container/project-files-container.component.spec.ts
+++ b/src/app/features/files/pages/files-container/files-container.component.spec.ts
@@ -4,18 +4,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SubHeaderComponent } from '@osf/shared/components';
-import { ProjectFilesContainerComponent } from './project-files-container.component';
+import { FilesContainerComponent } from './files-container.component';
-describe('ProjectFilesContainerComponent', () => {
- let component: ProjectFilesContainerComponent;
- let fixture: ComponentFixture;
+describe('FilesContainerComponent', () => {
+ let component: FilesContainerComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [ProjectFilesContainerComponent, MockComponent(SubHeaderComponent)],
+ imports: [FilesContainerComponent, MockComponent(SubHeaderComponent)],
}).compileComponents();
- fixture = TestBed.createComponent(ProjectFilesContainerComponent);
+ fixture = TestBed.createComponent(FilesContainerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/features/project/files/pages/project-files-container/project-files-container.component.ts b/src/app/features/files/pages/files-container/files-container.component.ts
similarity index 58%
rename from src/app/features/project/files/pages/project-files-container/project-files-container.component.ts
rename to src/app/features/files/pages/files-container/files-container.component.ts
index 21ae90b0e..dc4406436 100644
--- a/src/app/features/project/files/pages/project-files-container/project-files-container.component.ts
+++ b/src/app/features/files/pages/files-container/files-container.component.ts
@@ -2,9 +2,9 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
- selector: 'osf-project-files-container',
+ selector: 'osf-files-container',
imports: [RouterOutlet],
- templateUrl: './project-files-container.component.html',
+ templateUrl: './files-container.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ProjectFilesContainerComponent {}
+export class FilesContainerComponent {}
diff --git a/src/app/features/project/files/pages/project-files/project-files.component.html b/src/app/features/files/pages/files/files.component.html
similarity index 68%
rename from src/app/features/project/files/pages/project-files/project-files.component.html
rename to src/app/features/files/pages/files/files.component.html
index 00edc7c98..9e25d61ce 100644
--- a/src/app/features/project/files/pages/project-files/project-files.component.html
+++ b/src/app/features/files/pages/files/files.component.html
@@ -1,4 +1,4 @@
-
+
@if (!dataLoaded()) {
@@ -20,7 +20,7 @@
{{ option.label }}
- {{ 'project.files.storageLocation' | translate }}
+ {{ 'files.storageLocation' | translate }}
@@ -29,14 +29,14 @@
@@ -49,39 +49,40 @@
raised
(click)="downloadFolder()"
[icon]="'fas fa-download'"
- [label]="'project.files.actions.downloadAsZip' | translate"
+ [label]="'files.actions.downloadAsZip' | translate"
>
-
-
-
-
-
+ @if (!isViewOnly()) {
+
+
+
+
+ }
{
- let component: ProjectFilesComponent;
- let fixture: ComponentFixture;
+describe('FilesComponent', () => {
+ let component: FilesComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [ProjectFilesComponent, MockComponent(SubHeaderComponent)],
+ imports: [FilesComponent, MockComponent(SubHeaderComponent)],
}).compileComponents();
- fixture = TestBed.createComponent(ProjectFilesComponent);
+ fixture = TestBed.createComponent(FilesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/features/project/files/pages/project-files/project-files.component.ts b/src/app/features/files/pages/files/files.component.ts
similarity index 71%
rename from src/app/features/project/files/pages/project-files/project-files.component.ts
rename to src/app/features/files/pages/files/files.component.ts
index 01463158c..88b5d81bd 100644
--- a/src/app/features/project/files/pages/project-files/project-files.component.ts
+++ b/src/app/features/files/pages/files/files.component.ts
@@ -28,14 +28,12 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
-import { CreateFolderDialogComponent } from '@osf/features/project/files/components';
import {
CreateFolder,
DeleteEntry,
GetConfiguredStorageAddons,
GetFiles,
GetRootFolders,
- ProjectFilesSelectors,
RenameEntry,
ResetState,
SetCurrentFolder,
@@ -43,10 +41,9 @@ import {
SetMoveFileCurrentFolder,
SetSearch,
SetSort,
-} from '@osf/features/project/files/store';
-import { approveFile } from '@osf/features/project/files/utils';
-import { GetProjectById, ProjectOverviewSelectors } from '@osf/features/project/overview/store';
+} from '@osf/features/files/store';
import { ALL_SORT_OPTIONS } from '@osf/shared/constants';
+import { ResourceType } from '@osf/shared/enums';
import {
FilesTreeComponent,
FormSelectComponent,
@@ -57,8 +54,14 @@ import {
import { ConfiguredStorageAddon, FilesTreeActions, OsfFile } from '@shared/models';
import { FilesService } from '@shared/services';
+import { CreateFolderDialogComponent } from '../../components';
+import { FileProvider } from '../../constants';
+import { FilesSelectors } from '../../store';
+
+import { environment } from 'src/environments/environment';
+
@Component({
- selector: 'osf-project-files',
+ selector: 'osf-files',
imports: [
TableModule,
Button,
@@ -74,12 +77,12 @@ import { FilesService } from '@shared/services';
FilesTreeComponent,
FormSelectComponent,
],
- templateUrl: './project-files.component.html',
- styleUrl: './project-files.component.scss',
+ templateUrl: './files.component.html',
+ styleUrl: './files.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [DialogService, TreeDragDropService],
})
-export class ProjectFilesComponent {
+export class FilesComponent {
@HostBinding('class') classes = 'flex flex-column flex-1 w-full h-full';
private readonly filesService = inject(FilesService);
@@ -98,23 +101,21 @@ export class ProjectFilesComponent {
setMoveFileCurrentFolder: SetMoveFileCurrentFolder,
setSearch: SetSearch,
setSort: SetSort,
- getProject: GetProjectById,
getRootFolders: GetRootFolders,
getConfiguredStorageAddons: GetConfiguredStorageAddons,
resetState: ResetState,
});
- protected readonly files = select(ProjectFilesSelectors.getFiles);
- protected readonly isFilesLoading = select(ProjectFilesSelectors.isFilesLoading);
- protected readonly currentFolder = select(ProjectFilesSelectors.getCurrentFolder);
- protected readonly provider = select(ProjectFilesSelectors.getProvider);
-
- protected readonly project = select(ProjectOverviewSelectors.getProject);
- protected readonly projectId = signal('');
- private readonly rootFolders = select(ProjectFilesSelectors.getRootFolders);
- protected isRootFoldersLoading = select(ProjectFilesSelectors.isRootFoldersLoading);
- private readonly configuredStorageAddons = select(ProjectFilesSelectors.getConfiguredStorageAddons);
- protected isConfiguredStorageAddonsLoading = select(ProjectFilesSelectors.isConfiguredStorageAddonsLoading);
+ protected readonly files = select(FilesSelectors.getFiles);
+ protected readonly isFilesLoading = select(FilesSelectors.isFilesLoading);
+ protected readonly currentFolder = select(FilesSelectors.getCurrentFolder);
+ protected readonly provider = select(FilesSelectors.getProvider);
+
+ protected readonly resourceId = signal('');
+ private readonly rootFolders = select(FilesSelectors.getRootFolders);
+ protected isRootFoldersLoading = select(FilesSelectors.isRootFoldersLoading);
+ private readonly configuredStorageAddons = select(FilesSelectors.getConfiguredStorageAddons);
+ protected isConfiguredStorageAddonsLoading = select(FilesSelectors.isConfiguredStorageAddonsLoading);
protected currentRootFolder = model<{ label: string; folder: OsfFile } | null>(null);
protected readonly progress = signal(0);
protected readonly fileName = signal('');
@@ -122,10 +123,14 @@ export class ProjectFilesComponent {
protected readonly searchControl = new FormControl('');
protected readonly sortControl = new FormControl(ALL_SORT_OPTIONS[0].value);
+ private readonly urlMap = new Map([
+ [ResourceType.Project, 'nodes'],
+ [ResourceType.Registration, 'registrations'],
+ ]);
+
protected readonly rootFoldersOptions = computed(() => {
const rootFolders = this.rootFolders();
const addons = this.configuredStorageAddons();
-
if (rootFolders && addons) {
return rootFolders.map((folder) => ({
label: this.getAddonName(addons, folder.provider),
@@ -135,43 +140,54 @@ export class ProjectFilesComponent {
return [];
});
+ resourceType = signal(
+ this.activeRoute.parent?.parent?.snapshot.data['resourceType'] || ResourceType.Project
+ );
+
+ protected readonly isViewOnly = computed(() => {
+ return this.resourceType() === ResourceType.Registration;
+ });
+
+ protected readonly isViewOnlyDownloadable = computed(() => {
+ return this.resourceType() === ResourceType.Registration;
+ });
+
fileIsUploading = signal(false);
isFolderOpening = signal(false);
sortOptions = ALL_SORT_OPTIONS;
+ storageProvider = FileProvider.OsfStorage;
+
protected readonly filesTreeActions: FilesTreeActions = {
setCurrentFolder: (folder) => this.actions.setCurrentFolder(folder),
setFilesIsLoading: (isLoading) => this.actions.setFilesIsLoading(isLoading),
getFiles: (filesLink) => this.actions.getFiles(filesLink),
- deleteEntry: (projectId, link) => this.actions.deleteEntry(projectId, link),
- renameEntry: (projectId, link, newName) => this.actions.renameEntry(projectId, link, newName),
+ deleteEntry: (resourceId, link) => this.actions.deleteEntry(resourceId, link),
+ renameEntry: (resourceId, link, newName) => this.actions.renameEntry(resourceId, link, newName),
setMoveFileCurrentFolder: (folder) => this.actions.setMoveFileCurrentFolder(folder),
};
constructor() {
this.activeRoute.parent?.parent?.parent?.params.subscribe((params) => {
if (params['id']) {
- this.projectId.set(params['id']);
- if (!this.project()) {
- this.filesTreeActions.setFilesIsLoading?.(true);
- this.actions.getProject(params['id']);
- }
+ this.resourceId.set(params['id']);
+ this.filesTreeActions.setFilesIsLoading?.(true);
}
});
effect(() => {
- const project = this.project();
+ const resourceId = this.resourceId();
- if (project) {
- this.actions.getRootFolders(project.links.rootFolder);
- this.actions.getConfiguredStorageAddons(project.links.iri);
- }
+ const resourcePath = this.urlMap.get(this.resourceType()!);
+ const folderLink = `${environment.apiUrl}/${resourcePath}/${resourceId}/files/`;
+ const iriLink = `${environment.webUrl}/${resourceId}`;
+ this.actions.getRootFolders(folderLink);
+ this.actions.getConfiguredStorageAddons(iriLink);
});
effect(() => {
const rootFolders = this.rootFolders();
-
if (rootFolders) {
const osfRootFolder = rootFolders.find((folder) => folder.provider === 'osfstorage');
if (osfRootFolder) {
@@ -185,15 +201,18 @@ export class ProjectFilesComponent {
effect(() => {
const currentRootFolder = this.currentRootFolder();
-
if (currentRootFolder) {
this.actions.setCurrentFolder(currentRootFolder.folder);
}
});
effect(() => {
- if (!this.isConfiguredStorageAddonsLoading() && !this.isRootFoldersLoading()) {
+ if (this.resourceType() === ResourceType.Registration) {
this.dataLoaded.set(true);
+ } else {
+ if (!this.isConfiguredStorageAddonsLoading() && !this.isRootFoldersLoading()) {
+ this.dataLoaded.set(true);
+ }
}
});
@@ -243,13 +262,15 @@ export class ProjectFilesComponent {
if (event.type === HttpEventType.UploadProgress && event.total) {
this.progress.set(Math.round((event.loaded / event.total) * 100));
}
-
- if (event.type === HttpEventType.Response) {
- if (event.body) {
- const fileId = event?.body?.data.id;
- approveFile(fileId, this.projectId());
- }
- }
+ // [NM] Check if need to create guid here
+ // if (event.type === HttpEventType.Response) {
+ // if (event.body) {
+ // const fileId = event?.body?.data?.id?.split('/').pop();
+ // if (fileId) {
+ // this.filesService.getFileGuid(fileId).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
+ // }
+ // }
+ // }
});
}
@@ -271,7 +292,7 @@ export class ProjectFilesComponent {
.open(CreateFolderDialogComponent, {
width: '448px',
focusOnShow: false,
- header: this.translateService.instant('project.files.dialogs.createFolder.title'),
+ header: this.translateService.instant('files.dialogs.createFolder.title'),
closeOnEscape: true,
modal: true,
closable: true,
@@ -291,17 +312,17 @@ export class ProjectFilesComponent {
}
downloadFolder(): void {
- const projectId = this.projectId();
+ const resourceId = this.resourceId();
const folderId = this.currentFolder()?.id ?? '';
const isRootFolder = !this.currentFolder()?.relationships?.parentFolderLink;
const provider = this.currentRootFolder()?.folder?.provider ?? 'osfstorage';
- if (projectId && folderId) {
+ if (resourceId && folderId) {
if (isRootFolder) {
- const link = this.filesService.getFolderDownloadLink(projectId, provider, '', true);
+ const link = this.filesService.getFolderDownloadLink(resourceId, provider, '', true);
window.open(link, '_blank')?.focus();
} else {
- const link = this.filesService.getFolderDownloadLink(projectId, provider, folderId, false);
+ const link = this.filesService.getFolderDownloadLink(resourceId, provider, folderId, false);
window.open(link, '_blank')?.focus();
}
}
diff --git a/src/app/features/files/store/files.actions.ts b/src/app/features/files/store/files.actions.ts
new file mode 100644
index 000000000..a7ac92dc4
--- /dev/null
+++ b/src/app/features/files/store/files.actions.ts
@@ -0,0 +1,153 @@
+import { OsfFile } from '@osf/shared/models';
+
+import { PatchFileMetadata } from '../models';
+
+export class GetRootFolderFiles {
+ static readonly type = '[Files] Get Root Folder Files';
+
+ constructor(public resourceId: string) {}
+}
+
+export class GetFiles {
+ static readonly type = '[Files] Get Files';
+
+ constructor(public filesLink: string) {}
+}
+
+export class SetFilesIsLoading {
+ static readonly type = '[Files] Set Files Loading';
+
+ constructor(public isLoading: boolean) {}
+}
+
+export class RenameEntry {
+ static readonly type = '[Files] Rename entry';
+
+ constructor(
+ public resourceId: string,
+ public link: string,
+ public name: string
+ ) {}
+}
+
+export class SetSearch {
+ static readonly type = '[Files] Set Search';
+
+ constructor(public search: string) {}
+}
+
+export class SetSort {
+ static readonly type = '[Files] Set Sort';
+
+ constructor(public sort: string) {}
+}
+
+export class SetCurrentFolder {
+ static readonly type = '[Files] Set Current Folder';
+
+ constructor(public folder: OsfFile | null) {}
+}
+
+export class SetMoveFileCurrentFolder {
+ static readonly type = '[Files] Set Move File Current Folder';
+
+ constructor(public folder: OsfFile | null) {}
+}
+
+export class GetMoveFileFiles {
+ static readonly type = '[Files] Get Move File Files';
+
+ constructor(public filesLink: string) {}
+}
+
+export class GetFile {
+ static readonly type = '[Files] Get File';
+
+ constructor(public fileGuid: string) {}
+}
+
+export class GetFileMetadata {
+ static readonly type = '[Files] Get File Metadata';
+
+ constructor(public fileGuid: string) {}
+}
+
+export class GetFileResourceMetadata {
+ static readonly type = '[Files] Get File Resource Metadata';
+
+ constructor(
+ public resourceId: string,
+ public resourceType: string
+ ) {}
+}
+
+export class GetFileResourceContributors {
+ static readonly type = '[Files] Get File Resource Contributors';
+
+ constructor(
+ public resourceId: string,
+ public resourceType: string
+ ) {}
+}
+
+export class SetFileMetadata {
+ static readonly type = '[Files] Set File Metadata';
+
+ constructor(
+ public payload: PatchFileMetadata,
+ public fileGuid: string
+ ) {}
+}
+
+export class GetFileRevisions {
+ static readonly type = '[Files] Get Revisions';
+
+ constructor(
+ public resourceId: string,
+ public fileProvider: string,
+ public fileId: string
+ ) {}
+}
+
+export class UpdateTags {
+ static readonly type = '[Files] Update Tags';
+
+ constructor(
+ public tags: string[],
+ public fileGuid: string
+ ) {}
+}
+
+export class CreateFolder {
+ static readonly type = '[Files] Create folder';
+
+ constructor(
+ public newFolderLink: string,
+ public folderName: string
+ ) {}
+}
+
+export class DeleteEntry {
+ static readonly type = '[Files] Delete entry';
+
+ constructor(
+ public resourceId: string,
+ public link: string
+ ) {}
+}
+
+export class GetRootFolders {
+ static readonly type = '[Files] Get Folders';
+
+ constructor(public folderLink: string) {}
+}
+
+export class GetConfiguredStorageAddons {
+ static readonly type = '[Files] Get ConfiguredStorageAddons';
+
+ constructor(public resourceUri: string) {}
+}
+
+export class ResetState {
+ static readonly type = '[Files] Reset State';
+}
diff --git a/src/app/features/files/store/files.model.ts b/src/app/features/files/store/files.model.ts
new file mode 100644
index 000000000..79d621f16
--- /dev/null
+++ b/src/app/features/files/store/files.model.ts
@@ -0,0 +1,82 @@
+import { ContributorModel, OsfFile, ResourceMetadata } from '@shared/models';
+import { ConfiguredStorageAddon } from '@shared/models/addons';
+import { AsyncStateModel } from '@shared/models/store';
+
+import { FileProvider } from '../constants';
+import { OsfFileCustomMetadata, OsfFileRevision } from '../models';
+
+export interface FilesStateModel {
+ files: AsyncStateModel;
+ moveFileFiles: AsyncStateModel;
+ currentFolder: OsfFile | null;
+ moveFileCurrentFolder: OsfFile | null;
+ search: string;
+ sort: string;
+ provider: (typeof FileProvider)[keyof typeof FileProvider];
+ openedFile: AsyncStateModel;
+ fileMetadata: AsyncStateModel;
+ resourceMetadata: AsyncStateModel;
+ contributors: AsyncStateModel[] | null>;
+ fileRevisions: AsyncStateModel;
+ tags: AsyncStateModel;
+ rootFolders: AsyncStateModel;
+ configuredStorageAddons: AsyncStateModel;
+}
+
+export const filesStateDefaults: FilesStateModel = {
+ files: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ moveFileFiles: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ currentFolder: null,
+ moveFileCurrentFolder: null,
+ search: '',
+ sort: 'name',
+ provider: FileProvider.OsfStorage,
+ openedFile: {
+ data: null,
+ isLoading: false,
+ error: null,
+ },
+ fileMetadata: {
+ data: null,
+ isLoading: false,
+ error: null,
+ },
+ resourceMetadata: {
+ data: null,
+ isLoading: false,
+ error: null,
+ },
+ contributors: {
+ data: null,
+ isLoading: false,
+ error: null,
+ },
+ fileRevisions: {
+ data: null,
+ isLoading: false,
+ error: null,
+ },
+ tags: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ rootFolders: {
+ data: [],
+ isLoading: true,
+ error: null,
+ },
+ configuredStorageAddons: {
+ data: [],
+ isLoading: true,
+ error: null,
+ },
+};
diff --git a/src/app/features/files/store/files.selectors.ts b/src/app/features/files/store/files.selectors.ts
new file mode 100644
index 000000000..a7033f803
--- /dev/null
+++ b/src/app/features/files/store/files.selectors.ts
@@ -0,0 +1,125 @@
+import { Selector } from '@ngxs/store';
+
+import { ConfiguredStorageAddon, ContributorModel, OsfFile, ResourceMetadata } from '@shared/models';
+
+import { OsfFileCustomMetadata, OsfFileRevision } from '../models';
+
+import { FilesStateModel } from './files.model';
+import { FilesState } from './files.state';
+
+export class FilesSelectors {
+ @Selector([FilesState])
+ static getFiles(state: FilesStateModel): OsfFile[] {
+ return state.files.data;
+ }
+
+ @Selector([FilesState])
+ static isFilesLoading(state: FilesStateModel): boolean {
+ return state.files.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getMoveFileFiles(state: FilesStateModel): OsfFile[] {
+ return state.moveFileFiles.data;
+ }
+
+ @Selector([FilesState])
+ static isMoveFileFilesLoading(state: FilesStateModel): boolean {
+ return state.moveFileFiles.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getCurrentFolder(state: FilesStateModel): OsfFile | null {
+ return state.currentFolder;
+ }
+
+ @Selector([FilesState])
+ static getMoveFileCurrentFolder(state: FilesStateModel): OsfFile | null {
+ return state.moveFileCurrentFolder;
+ }
+
+ @Selector([FilesState])
+ static getProvider(state: FilesStateModel): string {
+ return state.provider;
+ }
+
+ @Selector([FilesState])
+ static getOpenedFile(state: FilesStateModel): OsfFile | null {
+ return state.openedFile.data;
+ }
+
+ @Selector([FilesState])
+ static isOpenedFileLoading(state: FilesStateModel): boolean {
+ return state.openedFile.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getFileCustomMetadata(state: FilesStateModel): OsfFileCustomMetadata | null {
+ return state.fileMetadata.data;
+ }
+
+ @Selector([FilesState])
+ static isFileMetadataLoading(state: FilesStateModel): boolean {
+ return state.fileMetadata.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getResourceMetadata(state: FilesStateModel): ResourceMetadata | null {
+ return state.resourceMetadata.data;
+ }
+
+ @Selector([FilesState])
+ static isResourceMetadataLoading(state: FilesStateModel): boolean {
+ return state.resourceMetadata.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getContributors(state: FilesStateModel): Partial[] | null {
+ return state.contributors.data;
+ }
+
+ @Selector([FilesState])
+ static isResourceContributorsLoading(state: FilesStateModel): boolean {
+ return state.contributors.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getFileRevisions(state: FilesStateModel): OsfFileRevision[] | null {
+ return state.fileRevisions.data;
+ }
+
+ @Selector([FilesState])
+ static isFileRevisionsLoading(state: FilesStateModel): boolean {
+ return state.fileRevisions.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getFileTags(state: FilesStateModel): string[] {
+ return state.tags.data;
+ }
+
+ @Selector([FilesState])
+ static isFileTagsLoading(state: FilesStateModel): boolean {
+ return state.tags.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getRootFolders(state: FilesStateModel): OsfFile[] | null {
+ return state.rootFolders.data;
+ }
+
+ @Selector([FilesState])
+ static isRootFoldersLoading(state: FilesStateModel): boolean {
+ return state.rootFolders.isLoading;
+ }
+
+ @Selector([FilesState])
+ static getConfiguredStorageAddons(state: FilesStateModel): ConfiguredStorageAddon[] | null {
+ return state.configuredStorageAddons.data;
+ }
+
+ @Selector([FilesState])
+ static isConfiguredStorageAddonsLoading(state: FilesStateModel): boolean {
+ return state.configuredStorageAddons.isLoading;
+ }
+}
diff --git a/src/app/features/project/files/store/project-files.state.ts b/src/app/features/files/store/files.state.ts
similarity index 58%
rename from src/app/features/project/files/store/project-files.state.ts
rename to src/app/features/files/store/files.state.ts
index 413079895..61d6217bb 100644
--- a/src/app/features/project/files/store/project-files.state.ts
+++ b/src/app/features/files/store/files.state.ts
@@ -1,18 +1,22 @@
import { Action, State, StateContext } from '@ngxs/store';
-import { catchError, finalize, forkJoin, tap, throwError } from 'rxjs';
+import { catchError, finalize, forkJoin, tap } from 'rxjs';
import { inject, Injectable } from '@angular/core';
-import { MapProjectMetadata } from '@osf/features/project/files/mappers';
+import { handleSectionError } from '@osf/shared/helpers';
+import { FilesService, ToastService } from '@shared/services';
+
+import { MapResourceMetadata } from '../mappers/resource-metadata.mapper';
+
import {
CreateFolder,
DeleteEntry,
GetConfiguredStorageAddons,
GetFile,
GetFileMetadata,
- GetFileProjectContributors,
- GetFileProjectMetadata,
+ GetFileResourceContributors,
+ GetFileResourceMetadata,
GetFileRevisions,
GetFiles,
GetMoveFileFiles,
@@ -27,24 +31,20 @@ import {
SetSearch,
SetSort,
UpdateTags,
-} from '@osf/features/project/files/store/project-files.actions';
-import { ProjectFilesStateModel } from '@osf/features/project/files/store/project-files.model';
-import { ToastService } from '@shared/services';
-import { FilesService } from '@shared/services/files.service';
-
-import { projectFilesStateDefaults } from '../models';
+} from './files.actions';
+import { filesStateDefaults, FilesStateModel } from './files.model';
@Injectable()
-@State({
- name: 'projectFilesState',
- defaults: projectFilesStateDefaults,
+@State({
+ name: 'filesState',
+ defaults: filesStateDefaults,
})
-export class ProjectFilesState {
+export class FilesState {
filesService = inject(FilesService);
toastService = inject(ToastService);
@Action(GetMoveFileFiles)
- getMoveFileFiles(ctx: StateContext, action: GetMoveFileFiles) {
+ getMoveFileFiles(ctx: StateContext, action: GetMoveFileFiles) {
const state = ctx.getState();
ctx.patchState({
moveFileFiles: { ...state.moveFileFiles, isLoading: true, error: null },
@@ -62,15 +62,14 @@ export class ProjectFilesState {
});
},
}),
- catchError((error) => this.handleError(ctx, 'moveFileFiles', error))
+ catchError((error) => handleSectionError(ctx, 'moveFileFiles', error))
);
}
@Action(GetFiles)
- getFiles(ctx: StateContext, action: GetFiles) {
+ getFiles(ctx: StateContext, action: GetFiles) {
const state = ctx.getState();
ctx.patchState({ files: { ...state.files, isLoading: true, error: null } });
-
return this.filesService.getFiles(action.filesLink, state.search, state.sort).pipe(
tap({
next: (files) => {
@@ -83,28 +82,28 @@ export class ProjectFilesState {
});
},
}),
- catchError((error) => this.handleError(ctx, 'files', error))
+ catchError((error) => handleSectionError(ctx, 'files', error))
);
}
@Action(SetFilesIsLoading)
- setFilesIsLoading(ctx: StateContext, action: SetFilesIsLoading) {
+ setFilesIsLoading(ctx: StateContext, action: SetFilesIsLoading) {
const state = ctx.getState();
ctx.patchState({ files: { ...state.files, isLoading: action.isLoading, error: null } });
}
@Action(SetCurrentFolder)
- setSelectedFolder(ctx: StateContext, action: SetCurrentFolder) {
+ setSelectedFolder(ctx: StateContext, action: SetCurrentFolder) {
ctx.patchState({ currentFolder: action.folder });
}
@Action(SetMoveFileCurrentFolder)
- setMoveFileSelectedFolder(ctx: StateContext, action: SetMoveFileCurrentFolder) {
+ setMoveFileSelectedFolder(ctx: StateContext, action: SetMoveFileCurrentFolder) {
ctx.patchState({ moveFileCurrentFolder: action.folder });
}
@Action(CreateFolder)
- createFolder(ctx: StateContext, action: CreateFolder) {
+ createFolder(ctx: StateContext, action: CreateFolder) {
const state = ctx.getState();
ctx.patchState({ files: { ...state.files, isLoading: true, error: null } });
@@ -114,7 +113,7 @@ export class ProjectFilesState {
}
@Action(DeleteEntry)
- deleteEntry(ctx: StateContext, action: DeleteEntry) {
+ deleteEntry(ctx: StateContext, action: DeleteEntry) {
return this.filesService.deleteEntry(action.link).pipe(
tap({
next: () => {
@@ -122,7 +121,7 @@ export class ProjectFilesState {
if (selectedFolder?.relationships.filesLink) {
ctx.dispatch(new GetFiles(selectedFolder?.relationships.filesLink));
} else {
- ctx.dispatch(new GetRootFolderFiles(action.projectId));
+ ctx.dispatch(new GetRootFolderFiles(action.resourceId));
}
},
})
@@ -130,7 +129,7 @@ export class ProjectFilesState {
}
@Action(RenameEntry)
- renameEntry(ctx: StateContext, action: RenameEntry) {
+ renameEntry(ctx: StateContext, action: RenameEntry) {
const state = ctx.getState();
ctx.patchState({ files: { ...state.files, isLoading: true, error: null } });
@@ -141,7 +140,7 @@ export class ProjectFilesState {
if (selectedFolder?.relationships.filesLink) {
ctx.dispatch(new GetFiles(selectedFolder?.relationships.filesLink));
} else {
- ctx.dispatch(new GetRootFolderFiles(action.projectId));
+ ctx.dispatch(new GetRootFolderFiles(action.resourceId));
}
},
})
@@ -149,17 +148,17 @@ export class ProjectFilesState {
}
@Action(SetSearch)
- setSearch(ctx: StateContext, action: SetSearch) {
+ setSearch(ctx: StateContext, action: SetSearch) {
ctx.patchState({ search: action.search });
}
@Action(SetSort)
- setSort(ctx: StateContext, action: SetSort) {
+ setSort(ctx: StateContext, action: SetSort) {
ctx.patchState({ sort: action.sort });
}
@Action(GetFile)
- getFile(ctx: StateContext, action: GetFile) {
+ getFile(ctx: StateContext, action: GetFile) {
const state = ctx.getState();
ctx.patchState({ openedFile: { ...state.openedFile, isLoading: true, error: null } });
ctx.patchState({ tags: { ...state.tags, isLoading: true, error: null } });
@@ -171,12 +170,12 @@ export class ProjectFilesState {
ctx.patchState({ tags: { data: file.tags, isLoading: false, error: null } });
},
}),
- catchError((error) => this.handleError(ctx, 'openedFile', error))
+ catchError((error) => handleSectionError(ctx, 'openedFile', error))
);
}
@Action(GetFileMetadata)
- getFileMetadata(ctx: StateContext, action: GetFileMetadata) {
+ getFileMetadata(ctx: StateContext, action: GetFileMetadata) {
const state = ctx.getState();
ctx.patchState({ fileMetadata: { ...state.fileMetadata, isLoading: true, error: null } });
@@ -186,12 +185,12 @@ export class ProjectFilesState {
ctx.patchState({ fileMetadata: { data: metadata, isLoading: false, error: null } });
},
}),
- catchError((error) => this.handleError(ctx, 'fileMetadata', error))
+ catchError((error) => handleSectionError(ctx, 'fileMetadata', error))
);
}
@Action(SetFileMetadata)
- setFileMetadata(ctx: StateContext, action: SetFileMetadata) {
+ setFileMetadata(ctx: StateContext, action: SetFileMetadata) {
const state = ctx.getState();
ctx.patchState({ fileMetadata: { ...state.fileMetadata, isLoading: true, error: null } });
@@ -203,25 +202,25 @@ export class ProjectFilesState {
}
},
}),
- catchError((error) => this.handleError(ctx, 'fileMetadata', error))
+ catchError((error) => handleSectionError(ctx, 'fileMetadata', error))
);
}
- @Action(GetFileProjectMetadata)
- getFileProjectMetadata(ctx: StateContext, action: GetFileProjectMetadata) {
+ @Action(GetFileResourceMetadata)
+ getFileResourceMetadata(ctx: StateContext, action: GetFileResourceMetadata) {
const state = ctx.getState();
- ctx.patchState({ projectMetadata: { ...state.projectMetadata, isLoading: true, error: null } });
+ ctx.patchState({ resourceMetadata: { ...state.resourceMetadata, isLoading: true, error: null } });
forkJoin({
- projectShortInfo: this.filesService.getProjectShortInfo(action.projectId),
- projectMetadata: this.filesService.getProjectCustomMetadata(action.projectId),
+ resourceShortInfo: this.filesService.getResourceShortInfo(action.resourceId, action.resourceType),
+ resourceMetadata: this.filesService.getCustomMetadata(action.resourceId),
})
- .pipe(catchError((error) => this.handleError(ctx, 'projectMetadata', error)))
+ .pipe(catchError((error) => handleSectionError(ctx, 'resourceMetadata', error)))
.subscribe((results) => {
- const projectMetadata = MapProjectMetadata(results.projectShortInfo, results.projectMetadata);
+ const resourceMetadata = MapResourceMetadata(results.resourceShortInfo, results.resourceMetadata);
ctx.patchState({
- projectMetadata: {
- data: projectMetadata,
+ resourceMetadata: {
+ data: resourceMetadata,
isLoading: false,
error: null,
},
@@ -229,38 +228,38 @@ export class ProjectFilesState {
});
}
- @Action(GetFileProjectContributors)
- getFileProjectContributors(ctx: StateContext, action: GetFileProjectContributors) {
+ @Action(GetFileResourceContributors)
+ getFileResourceContributors(ctx: StateContext, action: GetFileResourceContributors) {
const state = ctx.getState();
ctx.patchState({ contributors: { ...state.contributors, isLoading: true, error: null } });
- return this.filesService.getProjectContributors(action.projectId).pipe(
+ return this.filesService.getResourceContributors(action.resourceId, action.resourceType).pipe(
tap({
next: (contributors) => {
ctx.patchState({ contributors: { data: contributors, isLoading: false, error: null } });
},
}),
- catchError((error) => this.handleError(ctx, 'contributors', error))
+ catchError((error) => handleSectionError(ctx, 'contributors', error))
);
}
@Action(GetFileRevisions)
- getFileRevisions(ctx: StateContext, action: GetFileRevisions) {
+ getFileRevisions(ctx: StateContext, action: GetFileRevisions) {
const state = ctx.getState();
ctx.patchState({ fileRevisions: { ...state.fileRevisions, isLoading: true, error: null } });
- return this.filesService.getFileRevisions(action.projectId, state.provider, action.fileId).pipe(
+ return this.filesService.getFileRevisions(action.resourceId, action.fileProvider, action.fileId).pipe(
tap({
next: (revisions) => {
ctx.patchState({ fileRevisions: { data: revisions, isLoading: false, error: null } });
},
}),
- catchError((error) => this.handleError(ctx, 'fileRevisions', error))
+ catchError((error) => handleSectionError(ctx, 'fileRevisions', error))
);
}
@Action(UpdateTags)
- updateTags(ctx: StateContext, action: UpdateTags) {
+ updateTags(ctx: StateContext, action: UpdateTags) {
const state = ctx.getState();
ctx.patchState({ tags: { ...state.tags, isLoading: true, error: null } });
@@ -270,12 +269,12 @@ export class ProjectFilesState {
ctx.patchState({ tags: { data: file.tags, isLoading: false, error: null } });
},
}),
- catchError((error) => this.handleError(ctx, 'tags', error))
+ catchError((error) => handleSectionError(ctx, 'tags', error))
);
}
@Action(GetRootFolders)
- getRootFolders(ctx: StateContext, action: GetRootFolders) {
+ getRootFolders(ctx: StateContext, action: GetRootFolders) {
const state = ctx.getState();
ctx.patchState({ rootFolders: { ...state.rootFolders, isLoading: true } });
@@ -290,12 +289,12 @@ export class ProjectFilesState {
},
}),
}),
- catchError((error) => this.handleError(ctx, 'rootFolders', error))
+ catchError((error) => handleSectionError(ctx, 'rootFolders', error))
);
}
@Action(GetConfiguredStorageAddons)
- getConfiguredStorageAddons(ctx: StateContext, action: GetConfiguredStorageAddons) {
+ getConfiguredStorageAddons(ctx: StateContext, action: GetConfiguredStorageAddons) {
const state = ctx.getState();
ctx.patchState({ configuredStorageAddons: { ...state.configuredStorageAddons, isLoading: true } });
@@ -310,41 +309,12 @@ export class ProjectFilesState {
},
}),
}),
- finalize(() => {
- ctx.patchState({ configuredStorageAddons: { ...state.configuredStorageAddons, isLoading: false } });
- }),
- catchError((error) => this.handleError(ctx, 'configuredStorageAddons', error))
+ catchError((error) => handleSectionError(ctx, 'configuredStorageAddons', error))
);
}
@Action(ResetState)
- resetState(ctx: StateContext) {
- ctx.patchState(projectFilesStateDefaults);
- }
-
- private handleError(
- ctx: StateContext,
- section:
- | 'files'
- | 'moveFileFiles'
- | 'openedFile'
- | 'fileMetadata'
- | 'projectMetadata'
- | 'contributors'
- | 'fileRevisions'
- | 'tags'
- | 'rootFolders'
- | 'configuredStorageAddons',
- error: Error
- ) {
- ctx.patchState({
- [section]: {
- ...ctx.getState()[section],
- isLoading: false,
- error: error.message,
- },
- });
- this.toastService.showError(error.message);
- return throwError(() => error);
+ resetState(ctx: StateContext) {
+ ctx.patchState(filesStateDefaults);
}
}
diff --git a/src/app/features/files/store/index.ts b/src/app/features/files/store/index.ts
new file mode 100644
index 000000000..04f101f76
--- /dev/null
+++ b/src/app/features/files/store/index.ts
@@ -0,0 +1,3 @@
+export * from './files.actions';
+export * from './files.selectors';
+export * from './files.state';
diff --git a/src/app/features/moderation/components/bulk-upload/bulk-upload.component.html b/src/app/features/moderation/components/bulk-upload/bulk-upload.component.html
index d9d0c619f..206118b6b 100644
--- a/src/app/features/moderation/components/bulk-upload/bulk-upload.component.html
+++ b/src/app/features/moderation/components/bulk-upload/bulk-upload.component.html
@@ -18,7 +18,7 @@ {{ 'moderation.bulkUpload' | translate }}
raised
variant="text"
severity="success"
- [label]="'project.files.dialogs.uploadFile.title' | translate"
+ [label]="'files.dialogs.uploadFile.title' | translate"
(click)="chooseCallback()"
/>
diff --git a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.html b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.html
index d82ef794d..aa5746e53 100644
--- a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.html
+++ b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.html
@@ -18,7 +18,7 @@
{
- let component: FileProjectMetadataComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [FileProjectMetadataComponent],
- }).compileComponents();
-
- fixture = TestBed.createComponent(FileProjectMetadataComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.ts b/src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.ts
deleted file mode 100644
index 57b5df777..000000000
--- a/src/app/features/project/files/components/file-project-metadata/file-project-metadata.component.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { select } from '@ngxs/store';
-
-import { TranslatePipe } from '@ngx-translate/core';
-
-import { Skeleton } from 'primeng/skeleton';
-
-import { DatePipe } from '@angular/common';
-import { ChangeDetectionStrategy, Component } from '@angular/core';
-
-import { ProjectFilesSelectors } from '@osf/features/project/files/store';
-
-@Component({
- selector: 'osf-file-project-metadata',
- imports: [DatePipe, TranslatePipe, Skeleton],
- templateUrl: './file-project-metadata.component.html',
- styleUrl: './file-project-metadata.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class FileProjectMetadataComponent {
- projectMetadata = select(ProjectFilesSelectors.getProjectMetadata);
- contributors = select(ProjectFilesSelectors.getProjectContributors);
- isProjectMetadataLoading = select(ProjectFilesSelectors.isProjectMetadataLoading);
- isProjectContributorsLoading = select(ProjectFilesSelectors.isProjectContributorsLoading);
-}
diff --git a/src/app/features/project/files/constants/file-metadata-fields.const.ts b/src/app/features/project/files/constants/file-metadata-fields.const.ts
deleted file mode 100644
index 6aadeb040..000000000
--- a/src/app/features/project/files/constants/file-metadata-fields.const.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { MetadataField } from '../models';
-
-export const FileMetadataFields: MetadataField[] = [
- { key: 'title', label: 'project.files.detail.fileMetadata.fields.title' },
- { key: 'description', label: 'project.files.detail.fileMetadata.fields.description' },
- { key: 'resourceTypeGeneral', label: 'project.files.detail.fileMetadata.fields.resourceType' },
- { key: 'language', label: 'project.files.detail.fileMetadata.fields.resourceLanguage' },
-];
diff --git a/src/app/features/project/files/constants/index.ts b/src/app/features/project/files/constants/index.ts
deleted file mode 100644
index 8ccd501a6..000000000
--- a/src/app/features/project/files/constants/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './file-metadata-fields.const';
diff --git a/src/app/features/project/files/mappers/file-revision.mapper.ts b/src/app/features/project/files/mappers/file-revision.mapper.ts
deleted file mode 100644
index 6b3267a73..000000000
--- a/src/app/features/project/files/mappers/file-revision.mapper.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { OsfFileRevision } from '@osf/features/project/files/models/osf-models/file-revisions.model';
-import { FileRevisionJsonApi } from '@osf/features/project/files/models/responses/get-file-revisions-response.model';
-import { ApiData } from '@shared/models';
-
-export function MapFileRevision(data: ApiData[]): OsfFileRevision[] {
- return data.map((revision) => ({
- downloads: revision.attributes.extra.downloads,
- hashes: { md5: revision.attributes.extra.hashes.md5, sha256: revision.attributes.extra.hashes.sha256 },
- dateTime: new Date(revision.attributes.modified_utc),
- version: revision.attributes.version,
- }));
-}
diff --git a/src/app/features/project/files/mappers/index.ts b/src/app/features/project/files/mappers/index.ts
deleted file mode 100644
index bfa419062..000000000
--- a/src/app/features/project/files/mappers/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './file-custom-metadata.mapper';
-export * from './file-revision.mapper';
-export * from './project-metadata.mapper';
-export * from '@shared/mappers/files/files.mapper';
diff --git a/src/app/features/project/files/models/data/project-files-state-defaults.const.ts b/src/app/features/project/files/models/data/project-files-state-defaults.const.ts
deleted file mode 100644
index 0b959d759..000000000
--- a/src/app/features/project/files/models/data/project-files-state-defaults.const.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { FileProvider } from '@osf/features/project/files/models';
-
-export const projectFilesStateDefaults = {
- files: {
- data: [],
- isLoading: false,
- error: null,
- },
- moveFileFiles: {
- data: [],
- isLoading: false,
- error: null,
- },
- currentFolder: null,
- moveFileCurrentFolder: null,
- search: '',
- sort: 'name',
- provider: FileProvider.OsfStorage,
- openedFile: {
- data: null,
- isLoading: false,
- error: null,
- },
- fileMetadata: {
- data: null,
- isLoading: false,
- error: null,
- },
- projectMetadata: {
- data: null,
- isLoading: false,
- error: null,
- },
- contributors: {
- data: null,
- isLoading: false,
- error: null,
- },
- fileRevisions: {
- data: null,
- isLoading: false,
- error: null,
- },
- tags: {
- data: [],
- isLoading: false,
- error: null,
- },
- rootFolders: {
- data: [],
- isLoading: true,
- error: null,
- },
- configuredStorageAddons: {
- data: [],
- isLoading: true,
- error: null,
- },
-};
diff --git a/src/app/features/project/files/models/index.ts b/src/app/features/project/files/models/index.ts
deleted file mode 100644
index 923d1245d..000000000
--- a/src/app/features/project/files/models/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-// OSF Models
-export * from './osf-models/file-custom-metadata.model';
-export * from './osf-models/file-project-contributor.model';
-export * from './osf-models/file-revisions.model';
-export * from './osf-models/file-target.model';
-export * from './osf-models/project-custom-metadata.model';
-export * from './osf-models/project-short-info.model';
-
-// Response Models
-export * from './responses/create-folder-response.model';
-export * from './responses/get-file-metadata-reponse.model';
-export * from './responses/get-file-revisions-response.model';
-export * from './responses/get-file-target-response.model';
-export * from './responses/get-project-contributors-response.model';
-export * from './responses/get-project-custom-metadata-response.model';
-export * from './responses/get-project-short-info-response.model';
-
-// Request Models
-export * from './requests/patch-file-metadata.model';
-
-// Constants
-export * from './data/embed-content.const';
-export * from './data/file-provider.const';
-export * from './data/project-files-state-defaults.const';
-export * from './files-metadata-fields';
diff --git a/src/app/features/project/files/models/osf-models/file-project-contributor.model.ts b/src/app/features/project/files/models/osf-models/file-project-contributor.model.ts
deleted file mode 100644
index b31c96060..000000000
--- a/src/app/features/project/files/models/osf-models/file-project-contributor.model.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface OsfFileProjectContributor {
- id: string;
- name: string;
- active: boolean;
-}
diff --git a/src/app/features/project/files/models/osf-models/project-short-info.model.ts b/src/app/features/project/files/models/osf-models/project-short-info.model.ts
deleted file mode 100644
index 424b9e43e..000000000
--- a/src/app/features/project/files/models/osf-models/project-short-info.model.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export interface OsfProjectShortInfo {
- id: string;
- title: string;
- description: string;
- dateCreated: string;
- dateModified: string;
-}
diff --git a/src/app/features/project/files/pages/index.ts b/src/app/features/project/files/pages/index.ts
deleted file mode 100644
index 17695906d..000000000
--- a/src/app/features/project/files/pages/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
-export { FileDetailComponent } from './file-detail/file-detail.component';
-export { ProjectFilesComponent } from './project-files/project-files.component';
-export { ProjectFilesContainerComponent } from './project-files-container/project-files-container.component';
diff --git a/src/app/features/project/files/project-files.routes.ts b/src/app/features/project/files/project-files.routes.ts
deleted file mode 100644
index 44f13d430..000000000
--- a/src/app/features/project/files/project-files.routes.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Routes } from '@angular/router';
-
-import { ProjectFilesContainerComponent } from './pages/project-files-container/project-files-container.component';
-
-export const projectFilesRoutes: Routes = [
- {
- path: '',
- component: ProjectFilesContainerComponent,
- children: [
- {
- path: '',
- loadComponent: () =>
- import('@osf/features/project/files/pages/project-files/project-files.component').then(
- (c) => c.ProjectFilesComponent
- ),
- },
- {
- path: ':fileGuid',
- loadComponent: () =>
- import('@osf/features/project/files/pages/file-detail/file-detail.component').then(
- (c) => c.FileDetailComponent
- ),
- children: [
- {
- path: 'metadata',
- loadComponent: () =>
- import('@osf/features/project/files/pages/community-metadata/community-metadata.component').then(
- (c) => c.CommunityMetadataComponent
- ),
- },
- ],
- },
- ],
- },
-];
diff --git a/src/app/features/project/files/store/index.ts b/src/app/features/project/files/store/index.ts
deleted file mode 100644
index b4f8c0ff3..000000000
--- a/src/app/features/project/files/store/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './project-files.actions';
-export * from './project-files.selectors';
-export * from './project-files.state';
diff --git a/src/app/features/project/files/store/project-files.actions.ts b/src/app/features/project/files/store/project-files.actions.ts
deleted file mode 100644
index 105f16f7d..000000000
--- a/src/app/features/project/files/store/project-files.actions.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-import { PatchFileMetadata } from '@osf/features/project/files/models';
-import { OsfFile } from '@shared/models';
-
-export class GetRootFolderFiles {
- static readonly type = '[Project Files] Get Root Folder Files';
-
- constructor(public projectId: string) {}
-}
-
-export class GetFiles {
- static readonly type = '[Project Files] Get Files';
-
- constructor(public filesLink: string) {}
-}
-
-export class SetFilesIsLoading {
- static readonly type = '[Project Files] Set Files Loading';
-
- constructor(public isLoading: boolean) {}
-}
-
-export class GetFile {
- static readonly type = '[Project Files] Get File';
-
- constructor(public fileGuid: string) {}
-}
-
-export class GetFileMetadata {
- static readonly type = '[Project Files] Get File Metadata';
-
- constructor(public fileGuid: string) {}
-}
-
-export class GetFileProjectMetadata {
- static readonly type = '[Project Files] Get File Project Metadata';
-
- constructor(public projectId: string) {}
-}
-
-export class GetMoveFileFiles {
- static readonly type = '[Project Files] Get Move File Files';
-
- constructor(public filesLink: string) {}
-}
-
-export class SetCurrentFolder {
- static readonly type = '[Project Files] Set Current Folder';
-
- constructor(public folder: OsfFile | null) {}
-}
-
-export class SetMoveFileCurrentFolder {
- static readonly type = '[Project Files] Set Move File Files';
-
- constructor(public folder: OsfFile | null) {}
-}
-
-export class CreateFolder {
- static readonly type = '[Project Files] Create folder';
-
- constructor(
- public newFolderLink: string,
- public folderName: string
- ) {}
-}
-
-export class DeleteEntry {
- static readonly type = '[Project Files] Delete entry';
-
- constructor(
- public projectId: string,
- public link: string
- ) {}
-}
-export class RenameEntry {
- static readonly type = '[Project Files] Rename entry';
-
- constructor(
- public projectId: string,
- public link: string,
- public name: string
- ) {}
-}
-
-export class SetSearch {
- static readonly type = '[Project Files] Set Search';
-
- constructor(public search: string) {}
-}
-
-export class SetSort {
- static readonly type = '[Project Files] Set Sort';
-
- constructor(public sort: string) {}
-}
-
-export class GetFileProjectContributors {
- static readonly type = '[Project Files] Get Projects Contributors';
-
- constructor(public projectId: string) {}
-}
-
-export class SetFileMetadata {
- static readonly type = '[Project Files] Set File Metadata';
-
- constructor(
- public payload: PatchFileMetadata,
- public fileGuid: string
- ) {}
-}
-
-export class GetFileRevisions {
- static readonly type = '[Project Files] Get Revisions';
-
- constructor(
- public projectId: string,
- public fileId: string
- ) {}
-}
-
-export class UpdateTags {
- static readonly type = '[Project Files] Update Tags';
-
- constructor(
- public tags: string[],
- public fileGuid: string
- ) {}
-}
-
-export class GetRootFolders {
- static readonly type = '[Project Files] Get Folders';
-
- constructor(public folderLink: string) {}
-}
-
-export class GetConfiguredStorageAddons {
- static readonly type = '[Project Files] Get ConfiguredStorageAddons';
-
- constructor(public resourceUri: string) {}
-}
-
-export class ResetState {
- static readonly type = '[Project Files] Reset State';
-}
diff --git a/src/app/features/project/files/store/project-files.model.ts b/src/app/features/project/files/store/project-files.model.ts
deleted file mode 100644
index b97c74559..000000000
--- a/src/app/features/project/files/store/project-files.model.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import {
- FileProvider,
- OsfFileCustomMetadata,
- OsfFileProjectContributor,
- OsfFileRevision,
- OsfProjectMetadata,
-} from '@osf/features/project/files/models';
-import { OsfFile } from '@shared/models';
-import { ConfiguredStorageAddon } from '@shared/models/addons';
-import { AsyncStateModel } from '@shared/models/store';
-
-export interface ProjectFilesStateModel {
- files: AsyncStateModel;
- moveFileFiles: AsyncStateModel;
- currentFolder: OsfFile | null;
- moveFileCurrentFolder: OsfFile | null;
- search: string;
- sort: string;
- provider: (typeof FileProvider)[keyof typeof FileProvider];
- openedFile: AsyncStateModel;
- fileMetadata: AsyncStateModel;
- projectMetadata: AsyncStateModel;
- contributors: AsyncStateModel;
- fileRevisions: AsyncStateModel;
- tags: AsyncStateModel;
- rootFolders: AsyncStateModel;
- configuredStorageAddons: AsyncStateModel;
-}
diff --git a/src/app/features/project/files/store/project-files.selectors.ts b/src/app/features/project/files/store/project-files.selectors.ts
deleted file mode 100644
index 24c6fda03..000000000
--- a/src/app/features/project/files/store/project-files.selectors.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import { Selector } from '@ngxs/store';
-
-import {
- OsfFileCustomMetadata,
- OsfFileProjectContributor,
- OsfFileRevision,
- OsfProjectMetadata,
-} from '@osf/features/project/files/models';
-import { OsfFile } from '@shared/models';
-import { ConfiguredStorageAddon } from '@shared/models/addons';
-
-import { ProjectFilesStateModel } from './project-files.model';
-import { ProjectFilesState } from './project-files.state';
-
-export class ProjectFilesSelectors {
- @Selector([ProjectFilesState])
- static getFiles(state: ProjectFilesStateModel): OsfFile[] {
- return state.files.data;
- }
-
- @Selector([ProjectFilesState])
- static isFilesLoading(state: ProjectFilesStateModel): boolean {
- return state.files.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getMoveFileFiles(state: ProjectFilesStateModel): OsfFile[] {
- return state.moveFileFiles.data;
- }
-
- @Selector([ProjectFilesState])
- static isMoveFileFilesLoading(state: ProjectFilesStateModel): boolean {
- return state.moveFileFiles.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getCurrentFolder(state: ProjectFilesStateModel): OsfFile | null {
- return state.currentFolder;
- }
-
- @Selector([ProjectFilesState])
- static getMoveFileCurrentFolder(state: ProjectFilesStateModel): OsfFile | null {
- return state.moveFileCurrentFolder;
- }
-
- @Selector([ProjectFilesState])
- static getProvider(state: ProjectFilesStateModel): string {
- return state.provider;
- }
-
- @Selector([ProjectFilesState])
- static getOpenedFile(state: ProjectFilesStateModel): OsfFile | null {
- return state.openedFile.data;
- }
-
- @Selector([ProjectFilesState])
- static isOpenedFileLoading(state: ProjectFilesStateModel): boolean {
- return state.openedFile.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getFileCustomMetadata(state: ProjectFilesStateModel): OsfFileCustomMetadata | null {
- return state.fileMetadata.data;
- }
-
- @Selector([ProjectFilesState])
- static isFileMetadataLoading(state: ProjectFilesStateModel): boolean {
- return state.fileMetadata.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getProjectMetadata(state: ProjectFilesStateModel): OsfProjectMetadata | null {
- return state.projectMetadata.data;
- }
-
- @Selector([ProjectFilesState])
- static isProjectMetadataLoading(state: ProjectFilesStateModel): boolean {
- return state.projectMetadata.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getProjectContributors(state: ProjectFilesStateModel): OsfFileProjectContributor[] | null {
- return state.contributors.data;
- }
-
- @Selector([ProjectFilesState])
- static isProjectContributorsLoading(state: ProjectFilesStateModel): boolean {
- return state.contributors.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getFileRevisions(state: ProjectFilesStateModel): OsfFileRevision[] | null {
- return state.fileRevisions.data;
- }
-
- @Selector([ProjectFilesState])
- static isFileRevisionsLoading(state: ProjectFilesStateModel): boolean {
- return state.fileRevisions.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getFileTags(state: ProjectFilesStateModel): string[] {
- return state.tags.data;
- }
-
- @Selector([ProjectFilesState])
- static isFileTagsLoading(state: ProjectFilesStateModel): boolean {
- return state.tags.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getRootFolders(state: ProjectFilesStateModel): OsfFile[] | null {
- return state.rootFolders.data;
- }
-
- @Selector([ProjectFilesState])
- static isRootFoldersLoading(state: ProjectFilesStateModel): boolean {
- return state.rootFolders.isLoading;
- }
-
- @Selector([ProjectFilesState])
- static getConfiguredStorageAddons(state: ProjectFilesStateModel): ConfiguredStorageAddon[] | null {
- return state.configuredStorageAddons.data;
- }
-
- @Selector([ProjectFilesState])
- static isConfiguredStorageAddonsLoading(state: ProjectFilesStateModel): boolean {
- return state.configuredStorageAddons.isLoading;
- }
-}
diff --git a/src/app/features/project/files/utils/approve-file.helper.ts b/src/app/features/project/files/utils/approve-file.helper.ts
deleted file mode 100644
index 87af40df6..000000000
--- a/src/app/features/project/files/utils/approve-file.helper.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { environment } from 'src/environments/environment';
-
-export function approveFile(fileId: string, projectId: string): void {
- const link = `${environment.apiUrlV1}/${projectId}/files/${fileId}/`;
-
- const iframe = document.createElement('iframe');
- iframe.style.display = 'none';
- iframe.src = link;
-
- iframe.onload = () => {
- setTimeout(() => iframe.remove(), 3000);
- };
-
- document.body.appendChild(iframe);
-}
diff --git a/src/app/features/project/files/utils/index.ts b/src/app/features/project/files/utils/index.ts
deleted file mode 100644
index 22377b7a2..000000000
--- a/src/app/features/project/files/utils/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './approve-file.helper';
diff --git a/src/app/features/project/project.routes.ts b/src/app/features/project/project.routes.ts
index 8d9961a60..444d534d7 100644
--- a/src/app/features/project/project.routes.ts
+++ b/src/app/features/project/project.routes.ts
@@ -15,7 +15,6 @@ import {
} from '@osf/shared/stores';
import { AnalyticsState } from './analytics/store';
-import { ProjectFilesState } from './files/store';
import { SettingsState } from './settings/store';
export const projectRoutes: Routes = [
@@ -42,11 +41,8 @@ export const projectRoutes: Routes = [
},
{
path: 'files',
- loadChildren: () => import('../project/files/project-files.routes').then((mod) => mod.projectFilesRoutes),
- providers: [provideStates([ProjectFilesState])],
- data: {
- context: ResourceType.Project,
- },
+ loadChildren: () => import('@osf/features/files/files.routes').then((mod) => mod.filesRoutes),
+ data: { resourceType: ResourceType.Project },
},
{
path: 'registrations',
diff --git a/src/app/features/registries/components/custom-step/custom-step.component.html b/src/app/features/registries/components/custom-step/custom-step.component.html
index a34f93398..f2113e9eb 100644
--- a/src/app/features/registries/components/custom-step/custom-step.component.html
+++ b/src/app/features/registries/components/custom-step/custom-step.component.html
@@ -146,7 +146,7 @@
}
}
@case (FieldType.File) {
- {{ 'project.files.actions.uploadFile' | translate }}
+ {{ 'files.actions.uploadFile' | translate }}
{{ 'shared.files.limitText' | translate }}
diff --git a/src/app/features/registries/components/custom-step/custom-step.component.ts b/src/app/features/registries/components/custom-step/custom-step.component.ts
index 139d456a4..4afacc16e 100644
--- a/src/app/features/registries/components/custom-step/custom-step.component.ts
+++ b/src/app/features/registries/components/custom-step/custom-step.component.ts
@@ -96,7 +96,7 @@ export class CustomStepComponent implements OnDestroy {
stepForm!: FormGroup;
- attachedFiles: Record[]> = {};
+ attachedFiles: Record[]> = {};
constructor() {
this.route.params.pipe(takeUntilDestroyed()).subscribe((params) => {
@@ -181,7 +181,7 @@ export class CustomStepComponent implements OnDestroy {
onAttachFile(file: OsfFile, questionKey: string): void {
this.attachedFiles[questionKey] = this.attachedFiles[questionKey] || [];
- if (!this.attachedFiles[questionKey].some((f) => f.id === file.id)) {
+ if (!this.attachedFiles[questionKey].some((f) => f.file_id === file.id)) {
this.attachedFiles[questionKey].push(file);
this.stepForm.patchValue({
[questionKey]: [...(this.attachedFiles[questionKey] || []), file],
@@ -189,20 +189,36 @@ export class CustomStepComponent implements OnDestroy {
const otherFormValues = { ...this.stepForm.value };
delete otherFormValues[questionKey];
this.updateAction.emit({
- [questionKey]: [...this.attachedFiles[questionKey].map((f) => FilesMapper.toFilePayload(f as OsfFile))],
+ [questionKey]: [
+ ...this.attachedFiles[questionKey].map((f) => {
+ if (f.file_id) {
+ const { name, ...payload } = f;
+ return payload;
+ }
+ return FilesMapper.toFilePayload(f as OsfFile);
+ }),
+ ],
...otherFormValues,
});
}
}
- removeFromAttachedFiles(file: Partial, questionKey: string): void {
+ removeFromAttachedFiles(file: Partial, questionKey: string): void {
if (this.attachedFiles[questionKey]) {
- this.attachedFiles[questionKey] = this.attachedFiles[questionKey].filter((f) => f.id !== file.id);
+ this.attachedFiles[questionKey] = this.attachedFiles[questionKey].filter((f) => f.file_id !== file.file_id);
this.stepForm.patchValue({
[questionKey]: this.attachedFiles[questionKey],
});
this.updateAction.emit({
- [questionKey]: [...this.attachedFiles[questionKey].map((f) => FilesMapper.toFilePayload(f as OsfFile))],
+ [questionKey]: [
+ ...this.attachedFiles[questionKey].map((f) => {
+ if (f.file_id) {
+ const { name, ...payload } = f;
+ return payload;
+ }
+ return FilesMapper.toFilePayload(f as OsfFile);
+ }),
+ ],
});
}
}
diff --git a/src/app/features/registries/components/files-control/files-control.component.html b/src/app/features/registries/components/files-control/files-control.component.html
index f49641a6b..9cbcb880d 100644
--- a/src/app/features/registries/components/files-control/files-control.component.html
+++ b/src/app/features/registries/components/files-control/files-control.component.html
@@ -12,7 +12,7 @@
raised
severity="success"
[icon]="'fas fa-plus'"
- [label]="'project.files.actions.createFolder' | translate"
+ [label]="'files.actions.createFolder' | translate"
(click)="createFolder()"
>
@@ -23,7 +23,7 @@
raised
severity="success"
[icon]="'fas fa-upload'"
- [label]="'project.files.actions.uploadFile' | translate"
+ [label]="'files.actions.uploadFile' | translate"
(click)="fileInput.click()"
>
@@ -35,7 +35,7 @@
{
+ this.selectFile(file);
+ });
}
}
}
diff --git a/src/app/features/registry/pages/registry-files/registry-files.component.html b/src/app/features/registry/pages/registry-files/registry-files.component.html
deleted file mode 100644
index 531f597e8..000000000
--- a/src/app/features/registry/pages/registry-files/registry-files.component.html
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-@if (!dataLoaded()) {
-
-} @else {
-
-}
diff --git a/src/app/features/registry/pages/registry-files/registry-files.component.ts b/src/app/features/registry/pages/registry-files/registry-files.component.ts
deleted file mode 100644
index a4bf463d2..000000000
--- a/src/app/features/registry/pages/registry-files/registry-files.component.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-import { createDispatchMap, select } from '@ngxs/store';
-
-import { TranslatePipe } from '@ngx-translate/core';
-
-import { TreeDragDropService } from 'primeng/api';
-import { Button } from 'primeng/button';
-import { DialogService } from 'primeng/dynamicdialog';
-
-import { debounceTime, EMPTY, Observable, skip } from 'rxjs';
-
-import { ChangeDetectionStrategy, Component, DestroyRef, effect, inject, signal } from '@angular/core';
-import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
-import { FormControl } from '@angular/forms';
-import { ActivatedRoute, Router } from '@angular/router';
-
-import {
- FilesTreeComponent,
- FormSelectComponent,
- LoadingSpinnerComponent,
- SearchInputComponent,
- SubHeaderComponent,
-} from '@shared/components';
-import { ALL_SORT_OPTIONS } from '@shared/constants';
-import { FilesTreeActions, OsfFile } from '@shared/models';
-import { FilesService } from '@shared/services';
-
-import {
- GetRegistryFiles,
- SetCurrentFolder,
- SetSearch,
- SetSort,
-} from '../../store/registry-files/registry-files.actions';
-import { RegistryFilesSelectors } from '../../store/registry-files/registry-files.selectors';
-import { GetRegistryById } from '../../store/registry-overview/registry-overview.actions';
-import { RegistryOverviewSelectors } from '../../store/registry-overview/registry-overview.selectors';
-
-@Component({
- selector: 'osf-registry-files',
- imports: [
- SubHeaderComponent,
- TranslatePipe,
- Button,
- FilesTreeComponent,
- FormSelectComponent,
- SearchInputComponent,
- LoadingSpinnerComponent,
- ],
- templateUrl: './registry-files.component.html',
- styleUrl: './registry-files.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush,
- providers: [DialogService, TreeDragDropService],
-})
-export class RegistryFilesComponent {
- protected readonly registry = select(RegistryOverviewSelectors.getRegistry);
- protected readonly isRegistryLoading = select(RegistryOverviewSelectors.isRegistryLoading);
- protected readonly files = select(RegistryFilesSelectors.getFiles);
- protected readonly isFilesLoading = select(RegistryFilesSelectors.isFilesLoading);
- protected readonly currentFolder = select(RegistryFilesSelectors.getCurrentFolder);
- private readonly router = inject(Router);
- private readonly route = inject(ActivatedRoute);
- private readonly filesService = inject(FilesService);
- private readonly destroyRef = inject(DestroyRef);
-
- private readonly actions = createDispatchMap({
- setCurrentFolder: SetCurrentFolder,
- getFiles: GetRegistryFiles,
- getRootFolderFiles: GetRegistryFiles,
- getRegistryById: GetRegistryById,
- setSearch: SetSearch,
- setSort: SetSort,
- });
-
- protected readonly filesTreeActions: FilesTreeActions = {
- setCurrentFolder: (folder) => this.actions.setCurrentFolder(folder),
- getFiles: (filesLink) => this.actions.getFiles(filesLink),
- };
-
- protected readonly searchControl = new FormControl('');
- protected readonly sortControl = new FormControl(ALL_SORT_OPTIONS[0].value);
-
- protected isFolderOpening = signal(false);
- protected registryId = signal('');
- protected dataLoaded = signal(false);
-
- protected readonly sortOptions = ALL_SORT_OPTIONS;
- protected readonly provider = 'osfstorage';
-
- constructor() {
- this.route.parent?.params.subscribe((params) => {
- const id = params['id'];
- if (id) {
- this.registryId.set(id);
- if (!this.registry()) {
- this.actions.getRegistryById(id);
- }
- }
- });
-
- effect(() => {
- const registry = this.registry();
-
- if (registry) {
- this.actions.getFiles(registry.links.files).subscribe(() => this.dataLoaded.set(true));
- }
- });
-
- this.searchControl.valueChanges
- .pipe(skip(1), takeUntilDestroyed(this.destroyRef), debounceTime(500))
- .subscribe((searchText) => {
- this.actions.setSearch(searchText ?? '');
- if (!this.isFolderOpening()) {
- this.updateFilesList();
- }
- });
-
- this.sortControl.valueChanges.pipe(skip(1), takeUntilDestroyed(this.destroyRef)).subscribe((sort) => {
- this.actions.setSort(sort ?? '');
- if (!this.isFolderOpening()) {
- this.updateFilesList();
- }
- });
- }
-
- folderIsOpening(value: boolean): void {
- this.isFolderOpening.set(value);
- if (value) {
- this.searchControl.setValue('');
- this.sortControl.setValue(ALL_SORT_OPTIONS[0].value);
- }
- }
-
- updateFilesList(): Observable {
- const currentFolder = this.currentFolder();
- if (currentFolder?.relationships.filesLink) {
- return this.actions.getFiles(currentFolder?.relationships.filesLink).pipe(takeUntilDestroyed(this.destroyRef));
- }
-
- const registry = this.registry();
-
- if (registry) {
- return this.actions.getFiles(registry.links.files);
- }
-
- return EMPTY;
- }
-
- navigateToFile(file: OsfFile) {
- this.router.navigate([file.guid], { relativeTo: this.route });
- }
-
- downloadFolder(): void {
- const registryId = this.registry()?.id;
- const folderId = this.currentFolder()?.id ?? '';
- const isRootFolder = !this.currentFolder()?.relationships?.parentFolderLink;
-
- if (registryId) {
- if (isRootFolder || !folderId) {
- const link = this.filesService.getFolderDownloadLink(registryId, this.provider, '', true);
- window.open(link, '_blank')?.focus();
- } else {
- const link = this.filesService.getFolderDownloadLink(registryId, this.provider, folderId, false);
- window.open(link, '_blank')?.focus();
- }
- }
- }
-}
diff --git a/src/app/features/registry/registry.routes.ts b/src/app/features/registry/registry.routes.ts
index 0a0fd0d58..398be1c9e 100644
--- a/src/app/features/registry/registry.routes.ts
+++ b/src/app/features/registry/registry.routes.ts
@@ -3,7 +3,6 @@ import { provideStates } from '@ngxs/store';
import { Routes } from '@angular/router';
import { RegistryComponentsState } from '@osf/features/registry/store/registry-components';
-import { RegistryFilesState } from '@osf/features/registry/store/registry-files';
import { RegistryLinksState } from '@osf/features/registry/store/registry-links';
import { RegistryMetadataState } from '@osf/features/registry/store/registry-metadata';
import { RegistryOverviewState } from '@osf/features/registry/store/registry-overview';
@@ -81,12 +80,8 @@ export const registryRoutes: Routes = [
},
{
path: 'files',
- loadComponent: () =>
- import('./pages/registry-files/registry-files.component').then((c) => c.RegistryFilesComponent),
- providers: [provideStates([RegistryFilesState])],
- data: {
- context: ResourceType.Registration,
- },
+ loadChildren: () => import('@osf/features/files/files.routes').then((mod) => mod.filesRoutes),
+ data: { resourceType: ResourceType.Registration },
},
{
path: 'components',
diff --git a/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.html b/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.html
index 670da525f..dbcb2445c 100644
--- a/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.html
+++ b/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.html
@@ -110,7 +110,7 @@
class="w-10rem btn-full-width"
[label]="'settings.addons.form.buttons.authorize' | translate"
type="submit"
- [disabled]="!isFormValid() || isSubmitting()"
+ [disabled]="!isFormValid || isSubmitting()"
[loading]="isSubmitting()"
>
} @else {
@@ -126,7 +126,7 @@
class="w-10rem btn-full-width"
[label]="'settings.addons.form.buttons.reconnect' | translate"
type="submit"
- [disabled]="!isFormValid() || isSubmitting()"
+ [disabled]="!isFormValid || isSubmitting()"
>
}
diff --git a/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts b/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts
index 980aa140f..8bf5f5e3a 100644
--- a/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts
+++ b/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts
@@ -33,14 +33,14 @@ export class AddonSetupAccountFormComponent {
protected readonly formControls = AddonFormControls;
+ get isFormValid() {
+ return this.addonForm().valid;
+ }
+
protected readonly addonForm = computed>(() => {
return this.addonFormService.initializeForm(this.addon());
});
- protected readonly isFormValid = computed(() => {
- return this.addonForm().valid;
- });
-
protected readonly isAccessSecretKeysFormat = computed(() => {
return this.addon().credentialsFormat === CredentialsFormat.ACCESS_SECRET_KEYS;
});
@@ -63,7 +63,7 @@ export class AddonSetupAccountFormComponent {
});
protected handleSubmit(): void {
- if (!this.isFormValid()) return;
+ if (!this.isFormValid) return;
const formValue = this.addonForm().value;
const payload = this.addonFormService.generateAuthorizedAddonPayload(
diff --git a/src/app/shared/components/copy-button/copy-button.component.html b/src/app/shared/components/copy-button/copy-button.component.html
index c36f64f20..f426260cb 100644
--- a/src/app/shared/components/copy-button/copy-button.component.html
+++ b/src/app/shared/components/copy-button/copy-button.component.html
@@ -4,7 +4,8 @@
icon="fas fa-clone"
[label]="label()"
text
- [pTooltip]="tooltip() | translate"
+ [pTooltip]="(copyItem() ? tooltip() : 'common.labels.noData') | translate"
+ [disabled]="!copyItem()"
(click)="copy()"
>
diff --git a/src/app/shared/components/file-link/file-link.component.html b/src/app/shared/components/file-link/file-link.component.html
new file mode 100644
index 000000000..372199538
--- /dev/null
+++ b/src/app/shared/components/file-link/file-link.component.html
@@ -0,0 +1,14 @@
+
+ @if (hasProjectedContent()) {
+
+ } @else {
+
+ }
+
diff --git a/src/app/features/registry/pages/registry-files/registry-files.component.scss b/src/app/shared/components/file-link/file-link.component.scss
similarity index 100%
rename from src/app/features/registry/pages/registry-files/registry-files.component.scss
rename to src/app/shared/components/file-link/file-link.component.scss
diff --git a/src/app/features/registry/pages/registry-files/registry-files.component.spec.ts b/src/app/shared/components/file-link/file-link.component.spec.ts
similarity index 50%
rename from src/app/features/registry/pages/registry-files/registry-files.component.spec.ts
rename to src/app/shared/components/file-link/file-link.component.spec.ts
index 2dd8158f1..3e88dedfe 100644
--- a/src/app/features/registry/pages/registry-files/registry-files.component.spec.ts
+++ b/src/app/shared/components/file-link/file-link.component.spec.ts
@@ -1,17 +1,17 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { RegistryFilesComponent } from './registry-files.component';
+import { FileLinkComponent } from './file-link.component';
-describe('RegistryFilesComponent', () => {
- let component: RegistryFilesComponent;
- let fixture: ComponentFixture;
+describe('FileLinkComponent', () => {
+ let component: FileLinkComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [RegistryFilesComponent],
+ imports: [FileLinkComponent],
}).compileComponents();
- fixture = TestBed.createComponent(RegistryFilesComponent);
+ fixture = TestBed.createComponent(FileLinkComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/shared/components/file-link/file-link.component.ts b/src/app/shared/components/file-link/file-link.component.ts
new file mode 100644
index 000000000..d494d838a
--- /dev/null
+++ b/src/app/shared/components/file-link/file-link.component.ts
@@ -0,0 +1,46 @@
+import { Tag } from 'primeng/tag';
+
+import {
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ contentChild,
+ DestroyRef,
+ ElementRef,
+ inject,
+ input,
+} from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { Router } from '@angular/router';
+
+import { FilesService } from '@osf/shared/services';
+
+@Component({
+ selector: 'osf-file-link',
+ imports: [Tag],
+ templateUrl: './file-link.component.html',
+ styleUrl: './file-link.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FileLinkComponent {
+ file = input.required<{ file_id: string; file_name: string }>();
+ content = contentChild('content');
+
+ private readonly filesService = inject(FilesService);
+ private readonly router = inject(Router);
+ private readonly destroyRef = inject(DestroyRef);
+
+ hasProjectedContent = computed(() => !!this.content());
+
+ navigateToFile() {
+ const fileId = this.file().file_id;
+ if (fileId) {
+ this.filesService
+ .getFileGuid(fileId)
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe((file) => {
+ this.router.navigate(['/files', file.guid]);
+ });
+ }
+ }
+}
diff --git a/src/app/shared/components/file-menu/file-menu.component.ts b/src/app/shared/components/file-menu/file-menu.component.ts
index 149a8caa0..57cf1da05 100644
--- a/src/app/shared/components/file-menu/file-menu.component.ts
+++ b/src/app/shared/components/file-menu/file-menu.component.ts
@@ -29,17 +29,17 @@ export class FileMenuComponent {
icon: 'fas fa-share',
items: [
{
- label: 'project.files.detail.actions.share.email',
+ label: 'files.detail.actions.share.email',
icon: 'fas fa-envelope',
command: () => this.emitAction(FileMenuType.Share, { type: 'email' }),
},
{
- label: 'project.files.detail.actions.share.x',
+ label: 'files.detail.actions.share.x',
icon: 'fab fa-square-x-twitter',
command: () => this.emitAction(FileMenuType.Share, { type: 'twitter' }),
},
{
- label: 'project.files.detail.actions.share.facebook',
+ label: 'files.detail.actions.share.facebook',
icon: 'fab fa-facebook',
command: () => this.emitAction(FileMenuType.Share, { type: 'facebook' }),
},
@@ -50,12 +50,12 @@ export class FileMenuComponent {
icon: 'fas fa-code',
items: [
{
- label: 'project.files.detail.actions.copyDynamicIframe',
+ label: 'files.detail.actions.copyDynamicIframe',
icon: 'fas fa-file-code',
command: () => this.emitAction(FileMenuType.Embed, { type: 'dynamic' }),
},
{
- label: 'project.files.detail.actions.copyStaticIframe',
+ label: 'files.detail.actions.copyStaticIframe',
icon: 'fas fa-file-code',
command: () => this.emitAction(FileMenuType.Embed, { type: 'static' }),
},
diff --git a/src/app/shared/components/files-tree/files-tree.component.html b/src/app/shared/components/files-tree/files-tree.component.html
index a1d66f1f1..75189dbcd 100644
--- a/src/app/shared/components/files-tree/files-tree.component.html
+++ b/src/app/shared/components/files-tree/files-tree.component.html
@@ -10,7 +10,7 @@
@if (isDragOver()) {
-
{{ 'project.files.dropText' | translate }}
+
{{ 'files.dropText' | translate }}
}
@@ -94,11 +94,11 @@
@if (!files().length) {
@if (viewOnly()) {
-
{{ 'project.files.emptyState' | translate }}
+
{{ 'files.emptyState' | translate }}
} @else {
-
{{ 'project.files.dropText' | translate }}
+
{{ 'files.dropText' | translate }}
}
diff --git a/src/app/shared/components/files-tree/files-tree.component.ts b/src/app/shared/components/files-tree/files-tree.component.ts
index a27ce8fdc..35b28b39e 100644
--- a/src/app/shared/components/files-tree/files-tree.component.ts
+++ b/src/app/shared/components/files-tree/files-tree.component.ts
@@ -25,8 +25,8 @@ import {
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { MoveFileDialogComponent, RenameFileDialogComponent } from '@osf/features/project/files/components';
-import { embedDynamicJs, embedStaticHtml } from '@osf/features/project/files/models';
+import { MoveFileDialogComponent, RenameFileDialogComponent } from '@osf/features/files/components';
+import { embedDynamicJs, embedStaticHtml } from '@osf/features/files/constants';
import { FileMenuType } from '@osf/shared/enums';
import { FileMenuComponent, LoadingSpinnerComponent } from '@shared/components';
import { StopPropagationDirective } from '@shared/directives';
@@ -128,9 +128,9 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
if (files && files.length > 0) {
this.customConfirmationService.confirmAccept({
- headerKey: 'project.files.dialogs.uploadFile.title',
+ headerKey: 'files.dialogs.uploadFile.title',
messageParams: { name: files[0].name },
- messageKey: 'project.files.dialogs.uploadFile.message',
+ messageKey: 'files.dialogs.uploadFile.message',
acceptLabelKey: 'common.buttons.upload',
onConfirm: () => this.uploadFileConfirmed.emit(files[0]),
});
@@ -149,7 +149,13 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
openEntry(file: OsfFile) {
if (file.kind === 'file') {
- this.entryFileClicked.emit(file);
+ if (file.guid) {
+ this.entryFileClicked.emit(file);
+ } else {
+ this.filesService.getFileGuid(file.id).subscribe((file) => {
+ this.entryFileClicked.emit(file);
+ });
+ }
} else {
this.actions().setFilesIsLoading?.(true);
this.folderIsOpening.emit(true);
@@ -247,9 +253,9 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
confirmDelete(file: OsfFile): void {
this.customConfirmationService.confirmDelete({
- headerKey: 'project.files.dialogs.deleteFile.title',
+ headerKey: 'files.dialogs.deleteFile.title',
messageParams: { name: file.name },
- messageKey: 'project.files.dialogs.deleteFile.message',
+ messageKey: 'files.dialogs.deleteFile.message',
acceptLabelKey: 'common.buttons.remove',
onConfirm: () => this.deleteEntry(file.links.delete),
});
@@ -265,7 +271,7 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
.open(RenameFileDialogComponent, {
width: '448px',
focusOnShow: false,
- header: this.translateService.instant('project.files.dialogs.renameFile.title'),
+ header: this.translateService.instant('files.dialogs.renameFile.title'),
closeOnEscape: true,
modal: true,
closable: true,
@@ -319,8 +325,8 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
.subscribe(() => {
const header =
action === 'move'
- ? this.translateService.instant('project.files.dialogs.moveFile.title')
- : this.translateService.instant('project.files.dialogs.copyFile.title');
+ ? this.translateService.instant('files.dialogs.moveFile.title')
+ : this.translateService.instant('files.dialogs.copyFile.title');
this.dialogService.open(MoveFileDialogComponent, {
width: '552px',
@@ -351,7 +357,7 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
navigator.clipboard
.writeText(embedHtml)
.then(() => {
- this.toastService.showSuccess(this.translateService.instant('project.files.toast.copiedToClipboard'));
+ this.toastService.showSuccess('files.toast.copiedToClipboard');
})
.catch((err) => {
this.toastService.showError(err.message);
@@ -363,12 +369,12 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
const dropNode = event.dropNode as OsfFile;
this.customConfirmationService.confirmAccept({
- headerKey: 'project.files.dialogs.moveFile.title',
+ headerKey: 'files.dialogs.moveFile.title',
messageParams: {
dragNodeName: dragNode.name,
dropNodeName: dropNode.previousFolder ? 'parent folder' : dropNode.name,
},
- messageKey: 'project.files.dialogs.moveFile.message',
+ messageKey: 'files.dialogs.moveFile.message',
onConfirm: async () => {
await this.dropFileToFolder(event);
},
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
index a1c1589d2..e5eabe591 100644
--- a/src/app/shared/components/index.ts
+++ b/src/app/shared/components/index.ts
@@ -7,6 +7,7 @@ export { EducationHistoryComponent } from './education-history/education-history
export { EducationHistoryDialogComponent } from './education-history-dialog/education-history-dialog.component';
export { EmploymentHistoryComponent } from './employment-history/employment-history.component';
export { EmploymentHistoryDialogComponent } from './employment-history-dialog/employment-history-dialog.component';
+export { FileLinkComponent } from './file-link/file-link.component';
export { FileMenuComponent } from './file-menu/file-menu.component';
export { FilesTreeComponent } from './files-tree/files-tree.component';
export { FilterChipsComponent } from './filter-chips/filter-chips.component';
diff --git a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html
index d4392c874..bf98e1db7 100644
--- a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html
+++ b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html
@@ -22,7 +22,7 @@
@if (reviewData()[question.responseKey!].length) {
@for (file of reviewData()[question.responseKey!]; track file.id) {
-
+
}
} @else {
diff --git a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts
index 846aa407e..080c02764 100644
--- a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts
+++ b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts
@@ -9,9 +9,11 @@ import { INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants';
import { FieldType } from '@osf/shared/enums';
import { Question } from '@osf/shared/models';
+import { FileLinkComponent } from '../file-link/file-link.component';
+
@Component({
selector: 'osf-registration-blocks-data',
- imports: [Tag, TranslatePipe, Message],
+ imports: [Tag, TranslatePipe, Message, FileLinkComponent],
templateUrl: './registration-blocks-data.component.html',
styleUrl: './registration-blocks-data.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/src/app/shared/components/shared-metadata/components/project-metadata-funding/project-metadata-funding.component.html b/src/app/shared/components/shared-metadata/components/project-metadata-funding/project-metadata-funding.component.html
index b551468ff..6efd901ea 100644
--- a/src/app/shared/components/shared-metadata/components/project-metadata-funding/project-metadata-funding.component.html
+++ b/src/app/shared/components/shared-metadata/components/project-metadata-funding/project-metadata-funding.component.html
@@ -15,21 +15,19 @@ {{ 'project.overview.metadata.fundingSupport' | translate }}
@for (funder of funders(); track funder.funder_identifier) {
-
{{ 'project.files.detail.projectMetadata.fields.funder' | translate }}: {{ funder.funder_name }}
+
{{ 'files.detail.resourceMetadata.fields.funder' | translate }}: {{ funder.funder_name }}
-
{{ 'project.files.detail.projectMetadata.fields.awardTitle' | translate }}: {{ funder.award_title }}
+
{{ 'files.detail.resourceMetadata.fields.awardTitle' | translate }}: {{ funder.award_title }}
@if (funder.award_uri) {
- {{ 'project.files.detail.projectMetadata.fields.awardUri' | translate }}:
+ {{ 'files.detail.resourceMetadata.fields.awardUri' | translate }}:
{{ funder.award_title }}
}
@if (funder.award_number) {
-
- {{ 'project.files.detail.projectMetadata.fields.awardNumber' | translate }} : {{ funder.award_number }}
-
+
{{ 'files.detail.resourceMetadata.fields.awardNumber' | translate }} : {{ funder.award_number }}
}
@if (funder.award_uri) {
diff --git a/src/app/shared/components/shared-metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html b/src/app/shared/components/shared-metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html
index 99d6ed586..45ee9b76f 100644
--- a/src/app/shared/components/shared-metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html
+++ b/src/app/shared/components/shared-metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html
@@ -1,7 +1,7 @@