Skip to content

Commit a4bb706

Browse files
authored
[ENG-8975] Redirect addons support (#696)
* feat(addons): Allow fetching redirect addons * feat(addons): Update terms page for redirect addons * test(addons): add tests for redirect addons terms * feat(addons): Move redirect service behind feature flag * feat(addons): Add redirect service support for user settings page * feat(addons): Update redirect URL for user settings page
1 parent c75c518 commit a4bb706

23 files changed

+254
-48
lines changed

src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.html

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ <h2>
1111
</h2>
1212

1313
<div class="mt-4">
14-
<osf-addon-terms [addon]="addon()" />
14+
<osf-addon-terms [addon]="addon()" [redirectUrl]="redirectUrl()" />
1515
</div>
1616

1717
<div class="flex flex-column gap-2 mt-4">
@@ -33,17 +33,29 @@ <h2>
3333
[routerLink]="[baseUrl() + '/addons']"
3434
data-test-addon-cancel-button
3535
></p-button>
36-
<p-button
37-
[label]="
38-
!isAuthorizedAddonsLoading()
39-
? ('settings.addons.form.buttons.next' | translate)
40-
: ('settings.addons.form.buttons.acceptingTerms' | translate)
41-
"
42-
class="w-10rem btn-full-width"
43-
[loading]="isAuthorizedAddonsLoading()"
44-
(onClick)="handleAuthorizedAccountsPresenceCheck()"
45-
data-test-addon-terms-confirm-button
46-
></p-button>
36+
@if (addonTypeString() === AddonType.REDIRECT) {
37+
<p-button
38+
[label]="
39+
'settings.addons.connectAddon.redirectAddons.goToService'
40+
| translate: { serviceName: addon()?.displayName }
41+
"
42+
class="w-10rem btn-full-width"
43+
(onClick)="goToService()"
44+
data-test-addon-redirect-button
45+
/>
46+
} @else {
47+
<p-button
48+
[label]="
49+
!isAuthorizedAddonsLoading()
50+
? ('settings.addons.form.buttons.next' | translate)
51+
: ('settings.addons.form.buttons.acceptingTerms' | translate)
52+
"
53+
class="w-10rem btn-full-width"
54+
[loading]="isAuthorizedAddonsLoading()"
55+
(onClick)="handleAuthorizedAccountsPresenceCheck()"
56+
data-test-addon-terms-confirm-button
57+
></p-button>
58+
}
4759
</div>
4860
</section>
4961
</ng-template>

src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,17 @@ export class ConnectConfiguredAddonComponent {
146146

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

149+
redirectUrl = computed(() => {
150+
const addon = this.addon();
151+
if (!addon || !addon.redirectUrl) {
152+
return null;
153+
}
154+
const openURL = new URL(addon.redirectUrl);
155+
openURL.searchParams.set('nodeIri', this.resourceUri());
156+
openURL.searchParams.set('userIri', this.addonsUserReference()[0]?.attributes.user_uri);
157+
return openURL.toString();
158+
});
159+
149160
readonly baseUrl = computed(() => {
150161
const currentUrl = this.router.url;
151162
return currentUrl.split('/addons')[0];
@@ -261,6 +272,22 @@ export class ConnectConfiguredAddonComponent {
261272
});
262273
}
263274

275+
goToService() {
276+
if (!this.redirectUrl()) return;
277+
278+
const newWindow = window.open(
279+
this.redirectUrl()!.toString(),
280+
'_blank',
281+
'popup,width=600,height=600,scrollbars=yes,resizable=yes'
282+
);
283+
if (newWindow) {
284+
this.router.navigate([`${this.baseUrl()}/addons`]);
285+
newWindow.focus();
286+
} else {
287+
this.toastService.showError('addons.redirect.popUpError');
288+
}
289+
}
290+
264291
private getDataForAccountCheck() {
265292
const addonType = this.addonTypeString();
266293
const referenceId = this.userReferenceId();

src/app/features/project/project-addons/project-addons.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
{{ 'settings.addons.description' | translate }}
2323
</p>
2424
<osf-addons-toolbar
25-
[categoryOptions]="categoryOptions"
25+
[categoryOptions]="categoryOptions()"
2626
[searchControl]="searchControl"
2727
[(selectedCategory)]="selectedCategory"
2828
/>
@@ -43,7 +43,7 @@
4343
{{ 'settings.addons.connectedDescription' | translate }}
4444
</p>
4545
<osf-addons-toolbar
46-
[categoryOptions]="categoryOptions"
46+
[categoryOptions]="categoryOptions()"
4747
[searchControl]="searchControl"
4848
[(selectedCategory)]="selectedCategory"
4949
/>

src/app/features/project/project-addons/project-addons.component.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
GetConfiguredLinkAddons,
4848
GetConfiguredStorageAddons,
4949
GetLinkAddons,
50+
GetRedirectAddons,
5051
GetStorageAddons,
5152
} from '@shared/stores/addons';
5253
import { CurrentResourceSelectors } from '@shared/stores/current-resource';
@@ -76,7 +77,6 @@ export class ProjectAddonsComponent implements OnInit {
7677
private destroyRef = inject(DestroyRef);
7778
private queryParamsService = inject(AddonsQueryParamsService);
7879
readonly tabOptions = ADDON_TAB_OPTIONS;
79-
readonly categoryOptions = ADDON_CATEGORY_OPTIONS;
8080
readonly AddonTabValue = AddonTabValue;
8181
readonly defaultTabValue = AddonTabValue.ALL_ADDONS;
8282
searchControl = new FormControl<string>('');
@@ -91,6 +91,7 @@ export class ProjectAddonsComponent implements OnInit {
9191
storageAddons = select(AddonsSelectors.getStorageAddons);
9292
citationAddons = select(AddonsSelectors.getCitationAddons);
9393
linkAddons = select(AddonsSelectors.getLinkAddons);
94+
redirectAddons = select(AddonsSelectors.getRedirectAddons);
9495
configuredStorageAddons = select(AddonsSelectors.getConfiguredStorageAddons);
9596
configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);
9697
configuredLinkAddons = select(AddonsSelectors.getConfiguredLinkAddons);
@@ -100,14 +101,32 @@ export class ProjectAddonsComponent implements OnInit {
100101
isResourceReferenceLoading = select(AddonsSelectors.getAddonsResourceReferenceLoading);
101102
isStorageAddonsLoading = select(AddonsSelectors.getStorageAddonsLoading);
102103
isCitationAddonsLoading = select(AddonsSelectors.getCitationAddonsLoading);
104+
isLinkAddonsLoading = select(AddonsSelectors.getLinkAddonsLoading);
105+
isRedirectAddonsLoading = select(AddonsSelectors.getRedirectAddonsLoading);
103106
isConfiguredStorageAddonsLoading = select(AddonsSelectors.getConfiguredStorageAddonsLoading);
104107
isConfiguredCitationAddonsLoading = select(AddonsSelectors.getConfiguredCitationAddonsLoading);
105108
isConfiguredLinkAddonsLoading = select(AddonsSelectors.getConfiguredLinkAddonsLoading);
109+
activeFlags = select(UserSelectors.getActiveFlags);
110+
111+
readonly categoryOptions = computed(() => {
112+
if (this.activeFlags().includes('gravy_redirect')) {
113+
return [
114+
...ADDON_CATEGORY_OPTIONS,
115+
{
116+
label: 'settings.addons.categories.otherServices',
117+
value: AddonCategory.EXTERNAL_REDIRECT_SERVICES,
118+
},
119+
];
120+
}
121+
return ADDON_CATEGORY_OPTIONS;
122+
});
123+
106124
isAddonsLoading = computed(() => {
107125
return (
108126
this.isStorageAddonsLoading() ||
109127
this.isCitationAddonsLoading() ||
110128
this.isLinkAddonsLoading() ||
129+
this.isRedirectAddonsLoading() ||
111130
this.isUserReferenceLoading() ||
112131
this.isCurrentUserLoading()
113132
);
@@ -135,8 +154,6 @@ export class ProjectAddonsComponent implements OnInit {
135154
return categoryLoading || this.isResourceReferenceLoading() || this.isCurrentUserLoading();
136155
});
137156

138-
isLinkAddonsLoading = select(AddonsSelectors.getLinkAddonsLoading);
139-
140157
currentAddonsLoading = computed(() => {
141158
switch (this.selectedCategory()) {
142159
case AddonCategory.EXTERNAL_STORAGE_SERVICES:
@@ -145,6 +162,8 @@ export class ProjectAddonsComponent implements OnInit {
145162
return this.isCitationAddonsLoading();
146163
case AddonCategory.EXTERNAL_LINK_SERVICES:
147164
return this.isLinkAddonsLoading();
165+
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
166+
return this.isRedirectAddonsLoading();
148167
default:
149168
return this.isStorageAddonsLoading();
150169
}
@@ -158,6 +177,7 @@ export class ProjectAddonsComponent implements OnInit {
158177
getStorageAddons: GetStorageAddons,
159178
getCitationAddons: GetCitationAddons,
160179
getLinkAddons: GetLinkAddons,
180+
getRedirectAddons: GetRedirectAddons,
161181
getConfiguredStorageAddons: GetConfiguredStorageAddons,
162182
getConfiguredCitationAddons: GetConfiguredCitationAddons,
163183
getConfiguredLinkAddons: GetConfiguredLinkAddons,
@@ -221,6 +241,8 @@ export class ProjectAddonsComponent implements OnInit {
221241
return this.actions.getCitationAddons;
222242
case AddonCategory.EXTERNAL_LINK_SERVICES:
223243
return this.actions.getLinkAddons;
244+
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
245+
return this.actions.getRedirectAddons;
224246
default:
225247
return this.actions.getStorageAddons;
226248
}
@@ -234,6 +256,8 @@ export class ProjectAddonsComponent implements OnInit {
234256
return this.citationAddons();
235257
case AddonCategory.EXTERNAL_LINK_SERVICES:
236258
return this.linkAddons();
259+
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
260+
return this.redirectAddons();
237261
default:
238262
return this.storageAddons();
239263
}

src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ <h2>
1111
</h2>
1212

1313
<div class="mt-4">
14-
<osf-addon-terms [addon]="addon()" />
14+
<osf-addon-terms [addon]="addon()" [redirectUrl]="redirectUrl()" />
1515
</div>
1616

1717
<div class="flex flex-column gap-2 mt-4">

src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ export class ConnectAddonComponent {
7272
updateAuthorizedAddon: UpdateAuthorizedAddon,
7373
});
7474

75+
redirectUrl = computed(() => {
76+
const addon = this.addon();
77+
if (!addon || !addon.redirectUrl) {
78+
return null;
79+
}
80+
const openURL = new URL(addon.redirectUrl);
81+
openURL.searchParams.set('userIri', this.addonsUserReference()[0]?.attributes.user_uri);
82+
return openURL.toString();
83+
});
84+
7585
constructor() {
7686
const addon = this.router.getCurrentNavigation()?.extras.state?.['addon'] as AddonModel | AuthorizedAccountModel;
7787
if (!addon) {

src/app/features/settings/settings-addons/settings-addons.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</p>
2525

2626
<osf-addons-toolbar
27-
[categoryOptions]="categoryOptions"
27+
[categoryOptions]="categoryOptions()"
2828
[searchControl]="searchControl"
2929
[(selectedCategory)]="selectedCategory"
3030
/>
@@ -43,7 +43,7 @@
4343
{{ 'settings.addons.connectedDescription' | translate }}
4444
</p>
4545
<osf-addons-toolbar
46-
[categoryOptions]="categoryOptions"
46+
[categoryOptions]="categoryOptions()"
4747
[searchControl]="searchControl"
4848
[(selectedCategory)]="selectedCategory"
4949
/>

src/app/features/settings/settings-addons/settings-addons.component.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
GetAuthorizedStorageAddons,
4646
GetCitationAddons,
4747
GetLinkAddons,
48+
GetRedirectAddons,
4849
GetStorageAddons,
4950
UpdateAuthorizedAddon,
5051
} from '@shared/stores/addons';
@@ -74,7 +75,6 @@ export class SettingsAddonsComponent implements OnInit {
7475
private readonly route = inject(ActivatedRoute);
7576
private readonly queryParamsService = inject(AddonsQueryParamsService);
7677
readonly tabOptions = ADDON_TAB_OPTIONS;
77-
readonly categoryOptions = ADDON_CATEGORY_OPTIONS;
7878
readonly AddonTabValue = AddonTabValue;
7979
readonly defaultTabValue = AddonTabValue.ALL_ADDONS;
8080
searchControl = new FormControl<string>('');
@@ -87,6 +87,7 @@ export class SettingsAddonsComponent implements OnInit {
8787
storageAddons = select(AddonsSelectors.getStorageAddons);
8888
citationAddons = select(AddonsSelectors.getCitationAddons);
8989
linkAddons = select(AddonsSelectors.getLinkAddons);
90+
redirectAddons = select(AddonsSelectors.getRedirectAddons);
9091
authorizedStorageAddons = select(AddonsSelectors.getAuthorizedStorageAddons);
9192
authorizedCitationAddons = select(AddonsSelectors.getAuthorizedCitationAddons);
9293
authorizedLinkAddons = select(AddonsSelectors.getAuthorizedLinkAddons);
@@ -96,9 +97,24 @@ export class SettingsAddonsComponent implements OnInit {
9697
isStorageAddonsLoading = select(AddonsSelectors.getStorageAddonsLoading);
9798
isCitationAddonsLoading = select(AddonsSelectors.getCitationAddonsLoading);
9899
isLinkAddonsLoading = select(AddonsSelectors.getLinkAddonsLoading);
100+
isRedirectAddonsLoading = select(AddonsSelectors.getRedirectAddonsLoading);
99101
isAuthorizedStorageAddonsLoading = select(AddonsSelectors.getAuthorizedStorageAddonsLoading);
100102
isAuthorizedCitationAddonsLoading = select(AddonsSelectors.getAuthorizedCitationAddonsLoading);
101103
isAuthorizedLinkAddonsLoading = select(AddonsSelectors.getAuthorizedLinkAddonsLoading);
104+
activeFlags = select(UserSelectors.getActiveFlags);
105+
106+
readonly categoryOptions = computed(() => {
107+
if (this.activeFlags().includes('gravy_redirect')) {
108+
return [
109+
...ADDON_CATEGORY_OPTIONS,
110+
{
111+
label: 'settings.addons.categories.otherServices',
112+
value: AddonCategory.EXTERNAL_REDIRECT_SERVICES,
113+
},
114+
];
115+
}
116+
return ADDON_CATEGORY_OPTIONS;
117+
});
102118

103119
currentAddonsLoading = computed(() => {
104120
switch (this.selectedCategory()) {
@@ -108,6 +124,8 @@ export class SettingsAddonsComponent implements OnInit {
108124
return this.isCitationAddonsLoading();
109125
case AddonCategory.EXTERNAL_LINK_SERVICES:
110126
return this.isLinkAddonsLoading();
127+
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
128+
return this.isRedirectAddonsLoading();
111129
default:
112130
return this.isStorageAddonsLoading();
113131
}
@@ -144,6 +162,7 @@ export class SettingsAddonsComponent implements OnInit {
144162
getStorageAddons: GetStorageAddons,
145163
getCitationAddons: GetCitationAddons,
146164
getLinkAddons: GetLinkAddons,
165+
getRedirectAddons: GetRedirectAddons,
147166
getAuthorizedStorageAddons: GetAuthorizedStorageAddons,
148167
getAuthorizedCitationAddons: GetAuthorizedCitationAddons,
149168
getAuthorizedLinkAddons: GetAuthorizedLinkAddons,
@@ -193,6 +212,8 @@ export class SettingsAddonsComponent implements OnInit {
193212
return this.actions.getCitationAddons;
194213
case AddonCategory.EXTERNAL_LINK_SERVICES:
195214
return this.actions.getLinkAddons;
215+
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
216+
return this.actions.getRedirectAddons;
196217
default:
197218
return this.actions.getStorageAddons;
198219
}
@@ -206,6 +227,8 @@ export class SettingsAddonsComponent implements OnInit {
206227
return this.citationAddons();
207228
case AddonCategory.EXTERNAL_LINK_SERVICES:
208229
return this.linkAddons();
230+
case AddonCategory.EXTERNAL_REDIRECT_SERVICES:
231+
return this.redirectAddons();
209232
default:
210233
return this.storageAddons();
211234
}
Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
1-
<p-table [value]="terms()" class="addon-table">
2-
<ng-template pTemplate="header">
3-
<tr>
4-
<th>
5-
{{ 'settings.addons.connectAddon.table.function' | translate }}
6-
</th>
7-
<th>
8-
{{ 'settings.addons.connectAddon.table.status' | translate }}
9-
</th>
10-
</tr>
11-
</ng-template>
12-
<ng-template pTemplate="body" let-term>
13-
<tr
14-
[ngClass]="{
15-
'background-warning': term.type === 'warning',
16-
'background-success': term.type === 'info',
17-
'background-danger': term.type === 'danger',
18-
}"
19-
>
20-
<td>{{ term.function }}</td>
21-
<td>{{ term.status }}</td>
22-
</tr>
23-
</ng-template>
24-
</p-table>
1+
@if (isRedirectService()) {
2+
<p class="mb-4">
3+
{{ 'settings.addons.connectAddon.redirectAddons.terms' | translate }}
4+
</p>
5+
<em
6+
class="ml-2"
7+
[innerHTML]="
8+
'settings.addons.connectAddon.redirectAddons.tip'
9+
| translate: { serviceName: addon()?.displayName, serviceUrl: redirectUrl() }
10+
"
11+
></em>
12+
} @else {
13+
<p-table [value]="terms()" class="addon-table">
14+
<ng-template pTemplate="header">
15+
<tr>
16+
<th>
17+
{{ 'settings.addons.connectAddon.table.function' | translate }}
18+
</th>
19+
<th>
20+
{{ 'settings.addons.connectAddon.table.status' | translate }}
21+
</th>
22+
</tr>
23+
</ng-template>
24+
<ng-template pTemplate="body" let-term>
25+
<tr
26+
[ngClass]="{
27+
'background-warning': term.type === 'warning',
28+
'background-success': term.type === 'info',
29+
'background-danger': term.type === 'danger',
30+
}"
31+
>
32+
<td>{{ term.function }}</td>
33+
<td>{{ term.status }}</td>
34+
</tr>
35+
</ng-template>
36+
</p-table>
37+
}

0 commit comments

Comments
 (0)