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
7 changes: 7 additions & 0 deletions src/app/core/constants/nav-items.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ export const PROJECT_MENU_ITEMS: MenuItem[] = [
visible: true,
routerLinkActiveOptions: { exact: true },
},
{
id: 'project-linked-services',
label: 'navigation.linkedServices',
routerLink: 'links',
visible: true,
routerLinkActiveOptions: { exact: true },
},
{
id: 'project-settings',
label: 'navigation.settings',
Expand Down
2 changes: 1 addition & 1 deletion src/app/features/files/pages/files/files.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
SubHeaderComponent,
ViewOnlyLinkMessageComponent,
} from '@osf/shared/components';
import { GoogleFilePickerComponent } from '@osf/shared/components/addons/folder-selector/google-file-picker/google-file-picker.component';
import { GoogleFilePickerComponent } from '@osf/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component';
import { OsfFile } from '@osf/shared/models';
import { CustomConfirmationService, FilesService } from '@osf/shared/services';

Expand Down
14 changes: 4 additions & 10 deletions src/app/features/files/pages/files/files.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import {
SetSearch,
SetSort,
} from '@osf/features/files/store';
import { GoogleFilePickerComponent } from '@osf/shared/components/addons/folder-selector/google-file-picker/google-file-picker.component';
import { ALL_SORT_OPTIONS } from '@osf/shared/constants';
import { ResourceType } from '@osf/shared/enums';
import { hasViewOnlyParam, IS_MEDIUM } from '@osf/shared/helpers';
Expand All @@ -56,13 +55,8 @@ import {
SubHeaderComponent,
ViewOnlyLinkMessageComponent,
} from '@shared/components';
import {
ConfiguredStorageAddonModel,
FileLabelModel,
FilesTreeActions,
OsfFile,
StorageItemModel,
} from '@shared/models';
import { GoogleFilePickerComponent } from '@shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component';
import { ConfiguredAddonModel, FileLabelModel, FilesTreeActions, OsfFile, StorageItemModel } from '@shared/models';
import { FilesService } from '@shared/services';

import { CreateFolderDialogComponent, FileBrowserInfoComponent } from '../../components';
Expand Down Expand Up @@ -389,7 +383,7 @@ export class FilesComponent {
this.router.navigate([file.guid], { relativeTo: this.activeRoute });
}

getAddonName(addons: ConfiguredStorageAddonModel[], provider: string): string {
getAddonName(addons: ConfiguredAddonModel[], provider: string): string {
if (provider === 'osfstorage') {
return this.translateService.instant('files.storageLocation');
} else {
Expand All @@ -407,7 +401,7 @@ export class FilesComponent {
if (googleDrive) {
this.accountId.set(googleDrive.baseAccountId);
this.selectedRootFolder.set({
itemId: googleDrive.selectedFolderId,
itemId: googleDrive.selectedStorageItemId,
});
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/features/files/store/files.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContributorModel, OsfFile, ResourceMetadata } from '@shared/models';
import { ConfiguredStorageAddonModel } from '@shared/models/addons';
import { ConfiguredAddonModel } from '@shared/models/addons';
import { AsyncStateModel, AsyncStateWithTotalCount } from '@shared/models/store';

import { FileProvider } from '../constants';
Expand All @@ -20,7 +20,7 @@ export interface FilesStateModel {
fileRevisions: AsyncStateModel<OsfFileRevision[] | null>;
tags: AsyncStateModel<string[]>;
rootFolders: AsyncStateModel<OsfFile[] | null>;
configuredStorageAddons: AsyncStateModel<ConfiguredStorageAddonModel[] | null>;
configuredStorageAddons: AsyncStateModel<ConfiguredAddonModel[] | null>;
isAnonymous: boolean;
}

Expand Down
4 changes: 2 additions & 2 deletions src/app/features/files/store/files.selectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Selector } from '@ngxs/store';

import { ConfiguredStorageAddonModel, ContributorModel, OsfFile, ResourceMetadata } from '@shared/models';
import { ConfiguredAddonModel, ContributorModel, OsfFile, ResourceMetadata } from '@shared/models';

import { OsfFileCustomMetadata, OsfFileRevision } from '../models';

Expand Down Expand Up @@ -129,7 +129,7 @@ export class FilesSelectors {
}

@Selector([FilesState])
static getConfiguredStorageAddons(state: FilesStateModel): ConfiguredStorageAddonModel[] | null {
static getConfiguredStorageAddons(state: FilesStateModel): ConfiguredAddonModel[] | null {
return state.configuredStorageAddons.data;
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/features/project/addons/addons.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<osf-sub-header [title]="'settings.addons.header.title' | translate" [icon]="'fas fa-gear'" />
<osf-sub-header [title]="'settings.addons.header.title' | translate" />
<section class="flex-column flex flex-1">
<p-tabs [value]="selectedTab()" class="flex flex-1">
<p-tablist class="pr-5 pl-5 hidden md:flex md:flex-column">
Expand Down
130 changes: 88 additions & 42 deletions src/app/features/project/addons/addons.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import {
GetAddonsUserReference,
GetCitationAddons,
GetConfiguredCitationAddons,
GetConfiguredLinkAddons,
GetConfiguredStorageAddons,
GetLinkAddons,
GetStorageAddons,
} from '@shared/stores/addons';

Expand All @@ -60,84 +62,126 @@ import {
export class AddonsComponent implements OnInit {
private route = inject(ActivatedRoute);
private destroyRef = inject(DestroyRef);
protected readonly tabOptions = ADDON_TAB_OPTIONS;
protected readonly categoryOptions = ADDON_CATEGORY_OPTIONS;
protected readonly AddonTabValue = AddonTabValue;
protected readonly defaultTabValue = AddonTabValue.ALL_ADDONS;
protected searchControl = new FormControl<string>('');
protected searchValue = signal<string>('');
protected selectedCategory = signal<string>(AddonCategory.EXTERNAL_STORAGE_SERVICES);
protected selectedTab = signal<number>(this.defaultTabValue);

protected currentUser = select(UserSelectors.getCurrentUser);
protected addonsResourceReference = select(AddonsSelectors.getAddonsResourceReference);
protected addonsUserReference = select(AddonsSelectors.getAddonsUserReference);
protected storageAddons = select(AddonsSelectors.getStorageAddons);
protected citationAddons = select(AddonsSelectors.getCitationAddons);
protected configuredStorageAddons = select(AddonsSelectors.getConfiguredStorageAddons);
protected configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);

protected isCurrentUserLoading = select(UserSelectors.getCurrentUserLoading);
protected isUserReferenceLoading = select(AddonsSelectors.getAddonsUserReferenceLoading);
protected isResourceReferenceLoading = select(AddonsSelectors.getAddonsResourceReferenceLoading);
protected isStorageAddonsLoading = select(AddonsSelectors.getStorageAddonsLoading);
protected isCitationAddonsLoading = select(AddonsSelectors.getCitationAddonsLoading);
protected isConfiguredStorageAddonsLoading = select(AddonsSelectors.getConfiguredStorageAddonsLoading);
protected isConfiguredCitationAddonsLoading = select(AddonsSelectors.getConfiguredCitationAddonsLoading);
protected isAddonsLoading = computed(() => {
readonly tabOptions = ADDON_TAB_OPTIONS;
readonly categoryOptions = ADDON_CATEGORY_OPTIONS;
readonly AddonTabValue = AddonTabValue;
readonly defaultTabValue = AddonTabValue.ALL_ADDONS;
searchControl = new FormControl<string>('');
searchValue = signal<string>('');
selectedCategory = signal<string>(AddonCategory.EXTERNAL_STORAGE_SERVICES);
selectedTab = signal<number>(this.defaultTabValue);

currentUser = select(UserSelectors.getCurrentUser);
addonsResourceReference = select(AddonsSelectors.getAddonsResourceReference);
addonsUserReference = select(AddonsSelectors.getAddonsUserReference);
storageAddons = select(AddonsSelectors.getStorageAddons);
citationAddons = select(AddonsSelectors.getCitationAddons);
linkAddons = select(AddonsSelectors.getLinkAddons);
configuredStorageAddons = select(AddonsSelectors.getConfiguredStorageAddons);
configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);
configuredLinkAddons = select(AddonsSelectors.getConfiguredLinkAddons);

isCurrentUserLoading = select(UserSelectors.getCurrentUserLoading);
isUserReferenceLoading = select(AddonsSelectors.getAddonsUserReferenceLoading);
isResourceReferenceLoading = select(AddonsSelectors.getAddonsResourceReferenceLoading);
isStorageAddonsLoading = select(AddonsSelectors.getStorageAddonsLoading);
isCitationAddonsLoading = select(AddonsSelectors.getCitationAddonsLoading);
isConfiguredStorageAddonsLoading = select(AddonsSelectors.getConfiguredStorageAddonsLoading);
isConfiguredCitationAddonsLoading = select(AddonsSelectors.getConfiguredCitationAddonsLoading);
isConfiguredLinkAddonsLoading = select(AddonsSelectors.getConfiguredLinkAddonsLoading);
isAddonsLoading = computed(() => {
return (
this.isStorageAddonsLoading() ||
this.isCitationAddonsLoading() ||
this.isLinkAddonsLoading() ||
this.isUserReferenceLoading() ||
this.isCurrentUserLoading()
);
});
protected isConfiguredAddonsLoading = computed(() => {
isConfiguredAddonsLoading = computed(() => {
return (
this.isConfiguredStorageAddonsLoading() ||
this.isConfiguredCitationAddonsLoading() ||
this.isConfiguredLinkAddonsLoading() ||
this.isResourceReferenceLoading() ||
this.isCurrentUserLoading()
);
});

protected actions = createDispatchMap({
isLinkAddonsLoading = select(AddonsSelectors.getLinkAddonsLoading);

currentAddonsLoading = computed(() => {
switch (this.selectedCategory()) {
case AddonCategory.EXTERNAL_STORAGE_SERVICES:
return this.isStorageAddonsLoading();
case AddonCategory.EXTERNAL_CITATION_SERVICES:
return this.isCitationAddonsLoading();
case AddonCategory.EXTERNAL_LINK_SERVICES:
return this.isLinkAddonsLoading();
default:
return this.isStorageAddonsLoading();
}
});

actions = createDispatchMap({
getStorageAddons: GetStorageAddons,
getCitationAddons: GetCitationAddons,
getLinkAddons: GetLinkAddons,
getConfiguredStorageAddons: GetConfiguredStorageAddons,
getConfiguredCitationAddons: GetConfiguredCitationAddons,
getConfiguredLinkAddons: GetConfiguredLinkAddons,
getAddonsUserReference: GetAddonsUserReference,
getAddonsResourceReference: GetAddonsResourceReference,
deleteAuthorizedAddon: DeleteAuthorizedAddon,
clearConfiguredAddons: ClearConfiguredAddons,
});

protected readonly userReferenceId = computed(() => {
readonly userReferenceId = computed(() => {
return this.addonsUserReference()[0]?.id;
});

protected allConfiguredAddons = computed(() => {
const authorizedAddons = [...this.configuredStorageAddons(), ...this.configuredCitationAddons()];
allConfiguredAddons = computed(() => {
const authorizedAddons = [
...this.configuredStorageAddons(),
...this.configuredCitationAddons(),
...this.configuredLinkAddons(),
];

const searchValue = this.searchValue().toLowerCase();
return authorizedAddons.filter((card) => card.displayName.toLowerCase().includes(searchValue));
});

protected resourceReferenceId = computed(() => {
resourceReferenceId = computed(() => {
return this.addonsResourceReference()[0]?.id;
});

protected currentAction = computed(() =>
this.selectedCategory() === AddonCategory.EXTERNAL_STORAGE_SERVICES
? this.actions.getStorageAddons
: this.actions.getCitationAddons
);
currentAction = computed(() => {
switch (this.selectedCategory()) {
case AddonCategory.EXTERNAL_STORAGE_SERVICES:
return this.actions.getStorageAddons;
case AddonCategory.EXTERNAL_CITATION_SERVICES:
return this.actions.getCitationAddons;
case AddonCategory.EXTERNAL_LINK_SERVICES:
return this.actions.getLinkAddons;
default:
return this.actions.getStorageAddons;
}
});

protected currentAddonsState = computed(() =>
this.selectedCategory() === AddonCategory.EXTERNAL_STORAGE_SERVICES ? this.storageAddons() : this.citationAddons()
);
currentAddonsState = computed(() => {
switch (this.selectedCategory()) {
case AddonCategory.EXTERNAL_STORAGE_SERVICES:
return this.storageAddons();
case AddonCategory.EXTERNAL_CITATION_SERVICES:
return this.citationAddons();
case AddonCategory.EXTERNAL_LINK_SERVICES:
return this.linkAddons();
default:
return this.storageAddons();
}
});

protected filteredAddonCards = computed(() => {
filteredAddonCards = computed(() => {
const searchValue = this.searchValue().toLowerCase();
return this.currentAddonsState().filter(
(card) =>
Expand All @@ -146,7 +190,7 @@ export class AddonsComponent implements OnInit {
);
});

protected onCategoryChange(value: Primitive): void {
onCategoryChange(value: Primitive): void {
if (typeof value === 'string') {
this.selectedCategory.set(value);
}
Expand All @@ -163,8 +207,9 @@ export class AddonsComponent implements OnInit {
if (this.currentUser()) {
const action = this.currentAction();
const addons = this.currentAddonsState();
const isLoading = this.currentAddonsLoading();

if (!addons?.length) {
if (!addons?.length && !isLoading) {
action();
}
}
Expand Down Expand Up @@ -199,5 +244,6 @@ export class AddonsComponent implements OnInit {
private fetchAllConfiguredAddons(resourceReferenceId: string): void {
this.actions.getConfiguredStorageAddons(resourceReferenceId);
this.actions.getConfiguredCitationAddons(resourceReferenceId);
this.actions.getConfiguredLinkAddons(resourceReferenceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="flex flex-column gap-4">
<div class="flex justify-content-between">
<h2 class="align-self-center inline-block">
{{ 'settings.addons.connectAddon.configure' | translate }} {{ addon()?.externalServiceName }}
{{ 'settings.addons.connectAddon.configure' | translate }} {{ addonServiceName() }}
</h2>
<p-button
[label]="'settings.addons.form.buttons.back' | translate"
Expand All @@ -14,7 +14,7 @@ <h2 class="align-self-center inline-block">
></p-button>
</div>

@if (selectedFolder()) {
@if (selectedStorageItem()) {
<div class="flex flex-column flex-1">
<p-card>
<div class="flex justify-content-between">
Expand All @@ -41,8 +41,8 @@ <h2 class="align-self-center inline-block">
</div>

<p class="font-normal mt-2">
{{ 'settings.addons.configureAddon.selectedFolder' | translate }}
<span class="font-bold">{{ selectedFolder()?.itemName }}</span>
{{ selectedItemLabel() | translate }}
<span class="font-bold">{{ selectedStorageItem()?.itemName }}</span>
</p>
</p-card>
</div>
Expand All @@ -54,7 +54,7 @@ <h2 class="align-self-center inline-block">
<section class="flex flex-column gap-5">
<div class="flex justify-content-between">
<h2 class="align-self-center">
{{ 'settings.addons.connectAddon.configure' | translate }} {{ addon()?.externalServiceName }}
{{ 'settings.addons.connectAddon.configure' | translate }} {{ addonServiceName() }}
</h2>
<p-button
[label]="'settings.addons.form.buttons.back' | translate"
Expand All @@ -63,14 +63,18 @@ <h2 class="align-self-center">
(keydown.enter)="toggleEditMode()"
></p-button>
</div>
<osf-folder-selector
<osf-storage-item-selector
[isGoogleFilePicker]="isGoogleDrive()"
[accountId]="addon()?.baseAccountId || ''"
[accountName]="addon()?.displayName || ''"
[operationInvocationResult]="operationInvocation()?.operationResult || []"
[accountNameControl]="accountNameControl"
[currentAddonType]="addonTypeString()"
[supportedResourceTypes]="supportedResourceTypes()"
(operationInvoke)="handleCreateOperationInvocation($event.operationName, $event.itemId)"
[(selectedRootFolderId)]="selectedRootFolderId"
[(selectedStorageItemId)]="selectedStorageItemId"
[(selectedStorageItemUrl)]="selectedStorageItemUrl"
[(selectedResourceType)]="selectedResourceType"
(save)="handleUpdateAddonConfiguration()"
(cancelSelection)="toggleEditMode()"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe('Component: Configure Addon', () => {
expect(component.baseUrl()).toBe('/project/abc123');
expect(component.resourceUri()).toBe('https://staging4.osf.io/mocked-id');
expect(component.addonTypeString()).toBe('storage');
expect(component.selectedRootFolderId()).toBeUndefined();
expect(component.selectedStorageItemId).toBeDefined();
expect(component.accountNameControl.value).toBeUndefined();
expect(component.isGoogleDrive()).toBeFalsy();
});
Expand Down
Loading
Loading