Skip to content
Open
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 @@ -11,7 +11,7 @@ <h2>
</h2>

<div class="mt-4">
<osf-addon-terms [addon]="addon()" />
<osf-addon-terms [addon]="addon()" [redirectUrl]="redirectUrl()" />
</div>

<div class="flex flex-column gap-2 mt-4">
Expand All @@ -33,17 +33,29 @@ <h2>
[routerLink]="[baseUrl() + '/addons']"
data-test-addon-cancel-button
></p-button>
<p-button
[label]="
!isAuthorizedAddonsLoading()
? ('settings.addons.form.buttons.next' | translate)
: ('settings.addons.form.buttons.acceptingTerms' | translate)
"
class="w-10rem btn-full-width"
[loading]="isAuthorizedAddonsLoading()"
(onClick)="handleAuthorizedAccountsPresenceCheck()"
data-test-addon-terms-confirm-button
></p-button>
@if (addonTypeString() === AddonType.REDIRECT) {
<p-button
[label]="
'settings.addons.connectAddon.redirectAddons.goToService'
| translate: { serviceName: addon()?.displayName }
"
class="w-10rem btn-full-width"
(onClick)="goToService()"
data-test-addon-redirect-button
/>
} @else {
<p-button
[label]="
!isAuthorizedAddonsLoading()
? ('settings.addons.form.buttons.next' | translate)
: ('settings.addons.form.buttons.acceptingTerms' | translate)
"
class="w-10rem btn-full-width"
[loading]="isAuthorizedAddonsLoading()"
(onClick)="handleAuthorizedAccountsPresenceCheck()"
data-test-addon-terms-confirm-button
></p-button>
}
</div>
</section>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ export class ConnectConfiguredAddonComponent {

addonTypeString = computed(() => getAddonTypeString(this.addon()) as AddonType);

redirectUrl = computed(() => {
const addon = this.addon();
if (!addon || !addon.redirectUrl) {
return null;
}
const openURL = new URL(addon.redirectUrl);
openURL.searchParams.set('nodeIri', this.resourceUri());
openURL.searchParams.set('userIri', this.addonsUserReference()[0]?.attributes.user_uri);
return openURL.toString();
});

readonly baseUrl = computed(() => {
const currentUrl = this.router.url;
return currentUrl.split('/addons')[0];
Expand Down Expand Up @@ -255,6 +266,22 @@ export class ConnectConfiguredAddonComponent {
});
}

goToService() {
if (!this.redirectUrl()) return;

const newWindow = window.open(
this.redirectUrl()!.toString(),
'_blank',
'popup,width=600,height=600,scrollbars=yes,resizable=yes'
);
if (newWindow) {
this.router.navigate([`${this.baseUrl()}/addons`]);
newWindow.focus();
} else {
this.toastService.showError('addons.redirect.popUpError');
}
}

private getDataForAccountCheck() {
const addonType = this.addonTypeString();
const referenceId = this.userReferenceId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
GetConfiguredLinkAddons,
GetConfiguredStorageAddons,
GetLinkAddons,
GetRedirectAddons,
GetStorageAddons,
} from '@shared/stores/addons';
import { CurrentResourceSelectors } from '@shared/stores/current-resource';
Expand Down Expand Up @@ -91,6 +92,7 @@ export class ProjectAddonsComponent implements OnInit {
storageAddons = select(AddonsSelectors.getStorageAddons);
citationAddons = select(AddonsSelectors.getCitationAddons);
linkAddons = select(AddonsSelectors.getLinkAddons);
redirectAddons = select(AddonsSelectors.getRedirectAddons);
configuredStorageAddons = select(AddonsSelectors.getConfiguredStorageAddons);
configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);
configuredLinkAddons = select(AddonsSelectors.getConfiguredLinkAddons);
Expand All @@ -100,6 +102,8 @@ export class ProjectAddonsComponent implements OnInit {
isResourceReferenceLoading = select(AddonsSelectors.getAddonsResourceReferenceLoading);
isStorageAddonsLoading = select(AddonsSelectors.getStorageAddonsLoading);
isCitationAddonsLoading = select(AddonsSelectors.getCitationAddonsLoading);
isLinkAddonsLoading = select(AddonsSelectors.getLinkAddonsLoading);
isRedirectAddonsLoading = select(AddonsSelectors.getRedirectAddonsLoading);
isConfiguredStorageAddonsLoading = select(AddonsSelectors.getConfiguredStorageAddonsLoading);
isConfiguredCitationAddonsLoading = select(AddonsSelectors.getConfiguredCitationAddonsLoading);
isConfiguredLinkAddonsLoading = select(AddonsSelectors.getConfiguredLinkAddonsLoading);
Expand All @@ -108,6 +112,7 @@ export class ProjectAddonsComponent implements OnInit {
this.isStorageAddonsLoading() ||
this.isCitationAddonsLoading() ||
this.isLinkAddonsLoading() ||
this.isRedirectAddonsLoading() ||
this.isUserReferenceLoading() ||
this.isCurrentUserLoading()
);
Expand Down Expand Up @@ -135,8 +140,6 @@ export class ProjectAddonsComponent implements OnInit {
return categoryLoading || this.isResourceReferenceLoading() || this.isCurrentUserLoading();
});

isLinkAddonsLoading = select(AddonsSelectors.getLinkAddonsLoading);

currentAddonsLoading = computed(() => {
switch (this.selectedCategory()) {
case AddonCategory.EXTERNAL_STORAGE_SERVICES:
Expand All @@ -145,6 +148,8 @@ export class ProjectAddonsComponent implements OnInit {
return this.isCitationAddonsLoading();
case AddonCategory.EXTERNAL_LINK_SERVICES:
return this.isLinkAddonsLoading();
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
return this.isRedirectAddonsLoading();
default:
return this.isStorageAddonsLoading();
}
Expand All @@ -158,6 +163,7 @@ export class ProjectAddonsComponent implements OnInit {
getStorageAddons: GetStorageAddons,
getCitationAddons: GetCitationAddons,
getLinkAddons: GetLinkAddons,
getRedirectAddons: GetRedirectAddons,
getConfiguredStorageAddons: GetConfiguredStorageAddons,
getConfiguredCitationAddons: GetConfiguredCitationAddons,
getConfiguredLinkAddons: GetConfiguredLinkAddons,
Expand Down Expand Up @@ -221,6 +227,8 @@ export class ProjectAddonsComponent implements OnInit {
return this.actions.getCitationAddons;
case AddonCategory.EXTERNAL_LINK_SERVICES:
return this.actions.getLinkAddons;
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
return this.actions.getRedirectAddons;
default:
return this.actions.getStorageAddons;
}
Expand All @@ -234,6 +242,8 @@ export class ProjectAddonsComponent implements OnInit {
return this.citationAddons();
case AddonCategory.EXTERNAL_LINK_SERVICES:
return this.linkAddons();
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
return this.redirectAddons();
default:
return this.storageAddons();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
<p-table [value]="terms()" class="addon-table">
<ng-template pTemplate="header">
<tr>
<th>
{{ 'settings.addons.connectAddon.table.function' | translate }}
</th>
<th>
{{ 'settings.addons.connectAddon.table.status' | translate }}
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-term>
<tr
[ngClass]="{
'background-warning': term.type === 'warning',
'background-success': term.type === 'info',
'background-danger': term.type === 'danger',
}"
>
<td>{{ term.function }}</td>
<td>{{ term.status }}</td>
</tr>
</ng-template>
</p-table>
@if (isRedirectService()) {
<p class="mb-4">
{{ 'settings.addons.connectAddon.redirectAddons.terms' | translate }}
</p>
<em
class="ml-2"
[innerHTML]="
'settings.addons.connectAddon.redirectAddons.tip'
| translate: { serviceName: addon()?.displayName, serviceUrl: redirectUrl() }
"
></em>
} @else {
<p-table [value]="terms()" class="addon-table">
<ng-template pTemplate="header">
<tr>
<th>
{{ 'settings.addons.connectAddon.table.function' | translate }}
</th>
<th>
{{ 'settings.addons.connectAddon.table.status' | translate }}
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-term>
<tr
[ngClass]="{
'background-warning': term.type === 'warning',
'background-success': term.type === 'info',
'background-danger': term.type === 'danger',
}"
>
<td>{{ term.function }}</td>
<td>{{ term.status }}</td>
</tr>
</ng-template>
</p-table>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ADDON_TERMS } from '@osf/shared/constants/addon-terms.const';
import { isCitationAddon } from '@osf/shared/helpers/addon-type.helper';
import { isCitationAddon, isRedirectAddon } from '@osf/shared/helpers/addon-type.helper';
import { AddonModel } from '@osf/shared/models/addons/addon.model';
import { AddonTerm } from '@osf/shared/models/addons/addon-utils.models';

Expand All @@ -12,12 +12,14 @@ import { OSFTestingModule } from '@testing/osf.testing.module';

jest.mock('@shared/helpers/addon-type.helper.ts', () => ({
isCitationAddon: jest.fn(),
isRedirectAddon: jest.fn(),
}));

describe('AddonTermsComponent', () => {
let component: AddonTermsComponent;
let fixture: ComponentFixture<AddonTermsComponent>;
const mockIsCitationAddon = isCitationAddon as jest.MockedFunction<typeof isCitationAddon>;
const mockIsRedirectAddon = isRedirectAddon as jest.MockedFunction<typeof isRedirectAddon>;
const mockAddon: AddonModel = MOCK_ADDON;

beforeEach(async () => {
Expand All @@ -29,6 +31,7 @@ describe('AddonTermsComponent', () => {
component = fixture.componentInstance;

mockIsCitationAddon.mockReturnValue(false);
mockIsRedirectAddon.mockReturnValue(false);
});

it('should create', () => {
Expand Down Expand Up @@ -212,4 +215,24 @@ describe('AddonTermsComponent', () => {

expect(hasInfoTerm || hasWarningTerm || hasDangerTerm).toBe(true);
});

it('should handle redirect terms correctly', () => {
const redirectAddon: AddonModel = {
...mockAddon,
type: 'redirect',
};

mockIsRedirectAddon.mockReturnValue(true);
fixture.componentRef.setInput('addon', redirectAddon);
fixture.detectChanges();

const terms = component.terms();
expect(terms).toEqual([]);

const termsElement: HTMLElement = fixture.nativeElement;
expect(termsElement.querySelectorAll('tr').length).toBe(0);
expect(termsElement.querySelectorAll('p').length).toBe(1);
expect(termsElement.querySelectorAll('em').length).toBe(1);
expect(termsElement.textContent).toContain('settings.addons.connectAddon.redirectAddons.terms');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NgClass } from '@angular/common';
import { Component, computed, input } from '@angular/core';

import { ADDON_TERMS } from '@osf/shared/constants/addon-terms.const';
import { isCitationAddon } from '@osf/shared/helpers/addon-type.helper';
import { isCitationAddon, isRedirectAddon } from '@osf/shared/helpers/addon-type.helper';
import { AddonModel } from '@shared/models/addons/addon.model';
import { AddonTerm } from '@shared/models/addons/addon-utils.models';
import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account.model';
Expand All @@ -19,6 +19,9 @@ import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account
})
export class AddonTermsComponent {
addon = input<AddonModel | AuthorizedAccountModel | null>(null);
redirectUrl = input<string | null>(null);

isRedirectService = computed(() => isRedirectAddon(this.addon()));
terms = computed(() => {
const addon = this.addon();
if (!addon) {
Expand All @@ -31,6 +34,9 @@ export class AddonTermsComponent {
const supportedFeatures = addon.supportedFeatures || [];
const provider = addon.providerName;
const isCitationService = isCitationAddon(addon);
if (isRedirectAddon(addon)) {
return [];
}

const relevantTerms = isCitationService ? ADDON_TERMS.filter((term) => term.citation) : ADDON_TERMS;

Expand Down
4 changes: 4 additions & 0 deletions src/app/shared/constants/addons-category-options.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ export const ADDON_CATEGORY_OPTIONS: SelectOption[] = [
label: 'settings.addons.categories.linkedServices',
value: AddonCategory.EXTERNAL_LINK_SERVICES,
},
{
label: 'settings.addons.categories.otherServices',
value: AddonCategory.EXTERNAL_REDIRECT_SERVICES,
},
];
1 change: 1 addition & 0 deletions src/app/shared/enums/addon-type.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum AddonType {
STORAGE = 'storage',
CITATION = 'citation',
LINK = 'link',
REDIRECT = 'redirect', // Redirect addons will not have authorized accounts or configured addons
}

export enum AuthorizedAccountType {
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/enums/addons-category.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum AddonCategory {
EXTERNAL_STORAGE_SERVICES = 'external-storage-services',
EXTERNAL_CITATION_SERVICES = 'external-citation-services',
EXTERNAL_LINK_SERVICES = 'external-link-services',
EXTERNAL_REDIRECT_SERVICES = 'external-redirect-services',
}
6 changes: 6 additions & 0 deletions src/app/shared/helpers/addon-type.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export function isLinkAddon(addon: AddonModel | AuthorizedAccountModel | Configu
);
}

export function isRedirectAddon(addon: AddonModel | AuthorizedAccountModel | ConfiguredAddonModel | null): boolean {
if (!addon) return false;

return addon.type === AddonCategory.EXTERNAL_REDIRECT_SERVICES;
}

export function getAddonTypeString(addon: AddonModel | AuthorizedAccountModel | ConfiguredAddonModel | null): string {
if (!addon) return '';

Expand Down
1 change: 1 addition & 0 deletions src/app/shared/mappers/addon.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class AddonMapper {
credentialsFormat: response.attributes.credentials_format,
providerName: response.attributes.display_name,
iconUrl: response.attributes.icon_url,
redirectUrl: response.attributes.redirect_url,
configurableApiRoot: response.attributes.configurable_api_root,
};
}
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/models/addons/addon-json-api.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface AddonGetResponseJsonApi {
credentials_format: string;
wb_key: string;
icon_url: string;
redirect_url?: string;
configurable_api_root: boolean;
[key: string]: unknown;
};
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/models/addons/addon.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export interface AddonModel {
credentialsAvailable?: boolean;
supportedResourceTypes?: string[];
wbKey?: string;
redirectUrl?: string;
}
4 changes: 4 additions & 0 deletions src/app/shared/stores/addons/addons.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class GetLinkAddons {
static readonly type = '[Addons] Get Link Addons';
}

export class GetRedirectAddons {
static readonly type = '[Addons] Get Other Addons';
}

export class GetAuthorizedStorageAddons {
static readonly type = '[Addons] Get Authorized Storage Addons';

Expand Down
Loading