Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
<p-step [value]="targetStepValue()" [disabled]="isDisabled()">
<ng-template #content>
<div class="flex flex-column gap-4 w-full">
<h3>{{ 'collections.addToCollection.projectContributors' | translate }}</h3>
<div class="flex align-items-baseline gap-1">
<h3>{{ 'collections.addToCollection.projectContributors' | translate }}</h3>
<osf-info-icon
[tooltipText]="'collections.addToCollection.contributorsTooltip' | translate"
tooltipPosition="bottom"
></osf-info-icon>
</div>
@if (!isDisabled() && stepperActiveValue() !== targetStepValue()) {
@if (projectContributors().length) {
<div class="flex flex-column gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,27 @@ import { filter } from 'rxjs/operators';
import { ChangeDetectionStrategy, Component, DestroyRef, effect, inject, input, output, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

import { findChangedItems } from '@osf/shared/helpers';
import { InfoIconComponent } from '@osf/shared/components';
import {
AddContributorDialogComponent,
AddUnregisteredContributorDialogComponent,
ContributorsListComponent,
} from '@shared/components/contributors';
import { AddContributorType, ResourceType } from '@shared/enums';
import { ContributorDialogAddModel, ContributorModel } from '@shared/models';
import { CustomConfirmationService, ToastService } from '@shared/services';
import { AddContributor, ContributorsSelectors, DeleteContributor, UpdateContributor } from '@shared/stores';
import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors';
} from '@osf/shared/components/contributors';
import { AddContributorType, ResourceType } from '@osf/shared/enums';
import { findChangedItems } from '@osf/shared/helpers';
import { ContributorDialogAddModel, ContributorModel } from '@osf/shared/models';
import { CustomConfirmationService, ToastService } from '@osf/shared/services';
import {
AddContributor,
ContributorsSelectors,
DeleteContributor,
ProjectsSelectors,
UpdateContributor,
} from '@osf/shared/stores';

@Component({
selector: 'osf-project-contributors-step',
imports: [Button, TranslatePipe, ContributorsListComponent, Step, StepItem, StepPanel, Tooltip],
imports: [Button, Step, StepItem, StepPanel, Tooltip, TranslatePipe, ContributorsListComponent, InfoIconComponent],
templateUrl: './project-contributors-step.component.html',
styleUrl: './project-contributors-step.component.scss',
providers: [DialogService],
Expand All @@ -40,9 +46,9 @@ export class ProjectContributorsStepComponent {
private readonly toastService = inject(ToastService);
private readonly customConfirmationService = inject(CustomConfirmationService);

protected readonly projectContributors = select(ContributorsSelectors.getContributors);
protected readonly isContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
protected readonly selectedProject = select(ProjectsSelectors.getSelectedProject);
readonly projectContributors = select(ContributorsSelectors.getContributors);
readonly isContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
readonly selectedProject = select(ProjectsSelectors.getSelectedProject);

private initialContributors = signal<ContributorModel[]>([]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<main class="flex flex-column gap-3">
@for (item of filteredInfoItems(); track item.titleKey) {
<div>
<h3>{{ item.titleKey | translate }}</h3>
<p>{{ item.descriptionKey | translate }}</p>
</div>
}

<p>
{{ 'files.filesBrowserDialog.moreInfo' | translate }}
<a class="font-bold" href="https://help.osf.io/article/387-project-files">
{{ 'files.filesBrowserDialog.helpGuides' | translate }}
</a>
</p>
</main>

<div class="flex justify-content-end">
<p-button [label]="'common.buttons.close' | translate" (click)="dialogRef.close()" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { FileBrowserInfoComponent } from './file-browser-info.component';

describe('FileBrowserInfoComponent', () => {
let component: FileBrowserInfoComponent;
let fixture: ComponentFixture<FileBrowserInfoComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FileBrowserInfoComponent],
}).compileComponents();

fixture = TestBed.createComponent(FileBrowserInfoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { TranslatePipe } from '@ngx-translate/core';

import { Button } from 'primeng/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';

import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';

import { ResourceType } from '@osf/shared/enums';

import { FILE_BROWSER_INFO_ITEMS } from '../../constants';

@Component({
selector: 'osf-file-browser-info',
imports: [Button, TranslatePipe],
templateUrl: './file-browser-info.component.html',
styleUrl: './file-browser-info.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FileBrowserInfoComponent {
readonly dialogRef = inject(DynamicDialogRef);
readonly config = inject(DynamicDialogConfig);

readonly resourceType = computed(() => (this.config.data as ResourceType) || ResourceType.Project);

readonly infoItems = FILE_BROWSER_INFO_ITEMS;

readonly filteredInfoItems = computed(() => {
return this.infoItems.filter((item) => item.showForResourceTypes.includes(this.resourceType()));
});
}
1 change: 1 addition & 0 deletions src/app/features/files/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { CreateFolderDialogComponent } from './create-folder-dialog/create-folder-dialog.component';
export { EditFileMetadataDialogComponent } from './edit-file-metadata-dialog/edit-file-metadata-dialog.component';
export { FileBrowserInfoComponent } from './file-browser-info/file-browser-info.component';
export { FileKeywordsComponent } from './file-keywords/file-keywords.component';
export { FileMetadataComponent } from './file-metadata/file-metadata.component';
export { FileResourceMetadataComponent } from './file-resource-metadata/file-resource-metadata.component';
Expand Down
61 changes: 61 additions & 0 deletions src/app/features/files/constants/file-browser-info.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ResourceType } from '@osf/shared/enums';

import { FileInfoItem } from '../models';

export const FILE_BROWSER_INFO_ITEMS: FileInfoItem[] = [
{
titleKey: 'files.filesBrowserDialog.seeAllFiles',
descriptionKey: 'files.filesBrowserDialog.seeAllFilesDescription',
showForResourceTypes: [ResourceType.Project, ResourceType.Registration],
},
{
titleKey: 'files.filesBrowserDialog.selectFilesFolders',
descriptionKey: 'files.filesBrowserDialog.selectFilesFoldersDescription',
showForResourceTypes: [ResourceType.Project],
},
{
titleKey: 'files.filesBrowserDialog.openViewFiles',
descriptionKey: 'files.filesBrowserDialog.openViewFilesDescription',
showForResourceTypes: [ResourceType.Project, ResourceType.Registration],
},
{
titleKey: 'files.filesBrowserDialog.upload',
descriptionKey: 'files.filesBrowserDialog.uploadDescription',
showForResourceTypes: [ResourceType.Project],
},
{
titleKey: 'files.filesBrowserDialog.createFolder',
descriptionKey: 'files.filesBrowserDialog.createFolderDescription',
showForResourceTypes: [ResourceType.Project],
},
{
titleKey: 'files.filesBrowserDialog.renameFolderFile',
descriptionKey: 'files.filesBrowserDialog.renameFolderFileDescription',
showForResourceTypes: [ResourceType.Project],
},
{
titleKey: 'files.filesBrowserDialog.move',
descriptionKey: 'files.filesBrowserDialog.moveDescription',
showForResourceTypes: [ResourceType.Project],
},
{
titleKey: 'files.filesBrowserDialog.copy',
descriptionKey: 'files.filesBrowserDialog.copyDescription',
showForResourceTypes: [ResourceType.Project],
},
{
titleKey: 'files.filesBrowserDialog.downloadAllFilesZip',
descriptionKey: 'files.filesBrowserDialog.downloadAllFilesZipDescription',
showForResourceTypes: [ResourceType.Project, ResourceType.Registration],
},
{
titleKey: 'files.filesBrowserDialog.downloadFolderZip',
descriptionKey: 'files.filesBrowserDialog.downloadFolderZipDescription',
showForResourceTypes: [ResourceType.Project, ResourceType.Registration],
},
{
titleKey: 'files.filesBrowserDialog.downloadFile',
descriptionKey: 'files.filesBrowserDialog.downloadFileDescription',
showForResourceTypes: [ResourceType.Project, ResourceType.Registration],
},
];
1 change: 1 addition & 0 deletions src/app/features/files/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './embed-content.constants';
export * from './file-browser-info.constants';
export * from './file-metadata-fields.constants';
export * from './file-provider.constants';
1 change: 1 addition & 0 deletions src/app/features/files/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ 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 './info-item.model';
export * from './patch-file-metadata.model';
7 changes: 7 additions & 0 deletions src/app/features/files/models/info-item.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ResourceType } from '@osf/shared/enums';

export interface FileInfoItem {
titleKey: string;
descriptionKey: string;
showForResourceTypes: ResourceType[];
}
10 changes: 6 additions & 4 deletions src/app/features/files/pages/files/files.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,19 @@
</div>
</div>

<div class="flex flex-column w-full gap-2 sm:flex-row md:w-max">
<div class="flex flex-column align-items-center w-full gap-2 sm:flex-row md:w-max">
<p-button
[disabled]="fileIsUploading() || isFilesLoading()"
outlined
raised
(click)="downloadFolder()"
(onClick)="downloadFolder()"
[icon]="'fas fa-download'"
[label]="'files.actions.downloadAsZip' | translate"
>
</p-button>

<p-button icon="fas fa-info-circle blue-icon" severity="secondary" text (onClick)="showInfoDialog()"></p-button>

@if (!isViewOnly()) {
<p-button
[disabled]="fileIsUploading() || isFilesLoading()"
Expand All @@ -61,7 +63,7 @@
severity="success"
[icon]="'fas fa-plus'"
[label]="'files.actions.createFolder' | translate"
(click)="createFolder()"
(onClick)="createFolder()"
>
</p-button>

Expand All @@ -72,7 +74,7 @@
severity="success"
[icon]="'fas fa-upload'"
[label]="'files.actions.uploadFile' | translate"
(click)="fileInput.click()"
(onClick)="fileInput.click()"
>
</p-button>
}
Expand Down
77 changes: 48 additions & 29 deletions src/app/features/files/pages/files/files.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
model,
signal,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

Expand All @@ -44,6 +44,7 @@ import {
} from '@osf/features/files/store';
import { ALL_SORT_OPTIONS } from '@osf/shared/constants';
import { ResourceType } from '@osf/shared/enums';
import { IS_MEDIUM } from '@osf/shared/helpers';
import {
FilesTreeComponent,
FormSelectComponent,
Expand All @@ -54,7 +55,7 @@ import {
import { ConfiguredStorageAddonModel, FilesTreeActions, OsfFile } from '@shared/models';
import { FilesService } from '@shared/services';

import { CreateFolderDialogComponent } from '../../components';
import { CreateFolderDialogComponent, FileBrowserInfoComponent } from '../../components';
import { FileProvider } from '../../constants';
import { FilesSelectors } from '../../store';

Expand Down Expand Up @@ -106,29 +107,40 @@ export class FilesComponent {
resetState: ResetState,
});

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<string>('');
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('');
protected readonly dataLoaded = signal(false);
protected readonly searchControl = new FormControl<string>('');
protected readonly sortControl = new FormControl(ALL_SORT_OPTIONS[0].value);
isMedium = toSignal(inject(IS_MEDIUM));

readonly files = select(FilesSelectors.getFiles);
readonly isFilesLoading = select(FilesSelectors.isFilesLoading);
readonly currentFolder = select(FilesSelectors.getCurrentFolder);
readonly provider = select(FilesSelectors.getProvider);

readonly resourceId = signal<string>('');
readonly rootFolders = select(FilesSelectors.getRootFolders);
readonly isRootFoldersLoading = select(FilesSelectors.isRootFoldersLoading);
readonly configuredStorageAddons = select(FilesSelectors.getConfiguredStorageAddons);
readonly isConfiguredStorageAddonsLoading = select(FilesSelectors.isConfiguredStorageAddonsLoading);

readonly progress = signal(0);
readonly fileName = signal('');
readonly dataLoaded = signal(false);
readonly searchControl = new FormControl<string>('');
readonly sortControl = new FormControl(ALL_SORT_OPTIONS[0].value);

currentRootFolder = model<{ label: string; folder: OsfFile } | null>(null);

fileIsUploading = signal(false);
isFolderOpening = signal(false);

sortOptions = ALL_SORT_OPTIONS;

storageProvider = FileProvider.OsfStorage;

private readonly urlMap = new Map<ResourceType, string>([
[ResourceType.Project, 'nodes'],
[ResourceType.Registration, 'registrations'],
]);

protected readonly rootFoldersOptions = computed(() => {
readonly rootFoldersOptions = computed(() => {
const rootFolders = this.rootFolders();
const addons = this.configuredStorageAddons();
if (rootFolders && addons) {
Expand All @@ -144,22 +156,15 @@ export class FilesComponent {
this.activeRoute.parent?.parent?.snapshot.data['resourceType'] || ResourceType.Project
);

protected readonly isViewOnly = computed(() => {
readonly isViewOnly = computed(() => {
return this.resourceType() === ResourceType.Registration;
});

protected readonly isViewOnlyDownloadable = computed(() => {
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 = {
readonly filesTreeActions: FilesTreeActions = {
setCurrentFolder: (folder) => this.actions.setCurrentFolder(folder),
setFilesIsLoading: (isLoading) => this.actions.setFilesIsLoading(isLoading),
getFiles: (filesLink) => this.actions.getFiles(filesLink),
Expand Down Expand Up @@ -324,6 +329,20 @@ export class FilesComponent {
}
}

showInfoDialog() {
const dialogWidth = this.isMedium() ? '850px' : '95vw';

this.dialogService.open(FileBrowserInfoComponent, {
width: dialogWidth,
focusOnShow: false,
header: this.translateService.instant('files.filesBrowserDialog.title'),
closeOnEscape: true,
modal: true,
closable: true,
data: this.resourceType(),
});
}

updateFilesList(): Observable<void> {
const currentFolder = this.currentFolder();
if (currentFolder?.relationships.filesLink) {
Expand Down
Loading