Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export class DashboardComponent implements OnInit {
closable: true,
})
.onClose.pipe(
filter((result) => result.project.id),
filter((result) => result?.project.id),
tap((result) => this.projectRedirectDialogService.showProjectRedirectDialog(result.project.id)),
takeUntilDestroyed(this.destroyRef)
)
Expand Down
2 changes: 1 addition & 1 deletion src/app/features/my-projects/my-projects.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export class MyProjectsComponent implements OnInit {
closable: true,
})
.onClose.pipe(
filter((result) => result.project.id),
filter((result) => result?.project.id),
tap((result) => this.projectRedirectDialogService.showProjectRedirectDialog(result.project.id)),
takeUntilDestroyed(this.destroyRef)
)
Expand Down
4 changes: 2 additions & 2 deletions src/app/features/project/addons/addons.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<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">
<p-tab [value]="AddonTabValue.ALL_ADDONS">
<p-tab [value]="AddonTabValue.ALL_ADDONS" data-test-all-addons-tab>
{{ 'settings.addons.tabs.allAddons' | translate }}
</p-tab>
<p-tab [value]="AddonTabValue.CONNECTED_ADDONS">
<p-tab [value]="AddonTabValue.CONNECTED_ADDONS" data-test-configured-addons-tab>
{{ 'settings.addons.tabs.connectedAddons' | translate }}
</p-tab>
</p-tablist>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
severity="info"
(click)="dialogRef.close()"
[disabled]="isSubmitting()"
data-test-addon-cancel-button
/>
<p-button
class="btn-full-width"
[label]="'common.buttons.confirm' | translate"
(onClick)="handleConnectAddonAccount()"
[loading]="isSubmitting()"
data-test-addon-connect-account-button
/>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ <h2>
severity="info"
class="w-10rem btn-full-width"
[routerLink]="[baseUrl() + '/addons']"
data-test-addon-cancel-button
></p-button>
<p-button
[label]="
Expand All @@ -41,6 +42,7 @@ <h2>
class="w-10rem btn-full-width"
[loading]="isAuthorizedAddonsLoading()"
(onClick)="handleAuthorizedAccountsPresenceCheck()"
data-test-addon-terms-confirm-button
></p-button>
</div>
</section>
Expand All @@ -57,19 +59,22 @@ <h2 class="align-self-center inline-block">{{ loginOrChooseAccountText() }}</h2>
severity="info"
class="w-7rem btn-full-width"
(click)="activateCallback(AddonStepperValue.TERMS)"
data-test-addon-back-button
></p-button>
</div>

<div class="flex mt-3 gap-4 justify-content-end">
<p-button
[label]="'settings.addons.form.buttons.existingAccount' | translate"
(click)="activateCallback(AddonStepperValue.CHOOSE_ACCOUNT)"
data-test-addon-existing-account-button
></p-button>

<p-button
[label]="'settings.addons.form.buttons.newAccount' | translate"
severity="secondary"
(click)="activateCallback(AddonStepperValue.SETUP_NEW_ACCOUNT)"
data-test-addon-new-account-button
></p-button>
</div>
</div>
Expand Down Expand Up @@ -99,12 +104,14 @@ <h2 class="pt-2">{{ 'settings.addons.connectAddon.chooseExistingAccount' | trans
[label]="'settings.addons.form.buttons.back' | translate"
severity="info"
(onClick)="activateCallback(AddonStepperValue.CHOOSE_CONNECTION)"
data-test-addon-back-button
></p-button>
<p-button
class="w-10rem btn-full-width"
[disabled]="!chosenAccountId()"
[label]="'settings.addons.form.buttons.next' | translate"
(onClick)="handleConfirmAccountConnection()"
data-test-addon-authenticate-button
></p-button>
</div>
</section>
Expand Down Expand Up @@ -166,6 +173,7 @@ <h2 class="align-self-center inline-block">
severity="info"
class="w-7rem btn-full-width"
[routerLink]="[baseUrl() + '/addons']"
data-test-addon-back-button
></p-button>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { RadioButtonModule } from 'primeng/radiobutton';
import { StepPanel, StepPanels, Stepper } from 'primeng/stepper';
import { TableModule } from 'primeng/table';

import { Component, computed, inject, signal, viewChild } from '@angular/core';
import { Component, computed, DestroyRef, inject, signal, viewChild } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';

Expand All @@ -26,7 +26,13 @@ import {
} from '@shared/components/addons';
import { AddonServiceNames } from '@shared/enums';
import { AddonModel, AddonTerm, AuthorizedAddonRequestJsonApi } from '@shared/models';
import { AddonDialogService, AddonFormService, AddonOperationInvocationService, ToastService } from '@shared/services';
import {
AddonDialogService,
AddonFormService,
AddonOAuthService,
AddonOperationInvocationService,
ToastService,
} from '@shared/services';
import {
AddonsSelectors,
CreateAddonOperationInvocation,
Expand Down Expand Up @@ -71,6 +77,8 @@ export class ConnectConfiguredAddonComponent {
private addonDialogService = inject(AddonDialogService);
private addonFormService = inject(AddonFormService);
private operationInvocationService = inject(AddonOperationInvocationService);
private oauthService = inject(AddonOAuthService);
private destroyRef = inject(DestroyRef);
private router = inject(Router);
private route = inject(ActivatedRoute);
private selectedAccount = signal<AuthorizedAccountModel>({} as AuthorizedAccountModel);
Expand Down Expand Up @@ -157,6 +165,10 @@ export class ConnectConfiguredAddonComponent {
this.router.navigate([`${this.baseUrl()}/addons`]);
}
this.addon.set(addon);

this.destroyRef.onDestroy(() => {
this.oauthService.stopOAuthTracking();
});
}

handleCreateConfiguredAddon() {
Expand Down Expand Up @@ -197,16 +209,11 @@ export class ConnectConfiguredAddonComponent {

this.actions.createAuthorizedAddon(payload, this.addonTypeString()).subscribe({
complete: () => {
const addon = this.createdAuthorizedAddon();
if (addon?.authUrl) {
this.addonAuthUrl.set(addon.authUrl);
window.open(addon.authUrl, '_blank');
this.stepper()?.value.set(ProjectAddonsStepperValue.AUTH);
const createdAddon = this.createdAuthorizedAddon();
if (createdAddon?.authUrl) {
this.startOauthFlow(createdAddon);
} else {
this.router.navigate([`${this.baseUrl()}/addons`]);
this.toastService.showSuccess('settings.addons.toast.createSuccess', {
addonName: AddonServiceNames[addon?.externalServiceName as keyof typeof AddonServiceNames],
});
this.refreshAccountsForOAuth();
}
},
});
Expand Down Expand Up @@ -331,4 +338,36 @@ export class ConnectConfiguredAddonComponent {
this.selectedStorageItemUrl.set('');
this.selectedResourceType.set('');
}

private startOauthFlow(createdAddon: AuthorizedAccountModel): void {
this.addonAuthUrl.set(createdAddon.authUrl!);
window.open(createdAddon.authUrl!, '_blank');
this.stepper()?.value.set(ProjectAddonsStepperValue.AUTH);

this.oauthService.startOAuthTracking(createdAddon, this.addonTypeString(), {
onSuccess: () => {
this.refreshAccountsForOAuth();
},
});
}

private refreshAccountsForOAuth(): void {
const requiredData = this.getDataForAccountCheck();
if (!requiredData) return;

const { addonType, referenceId, currentAddon } = requiredData;
const addonConfig = this.getAddonConfig(addonType, referenceId);

if (!addonConfig) return;

addonConfig.getAddons().subscribe({
complete: () => {
const authorizedAddons = addonConfig.getAuthorizedAddons();
const matchingAddons = this.findMatchingAddons(authorizedAddons, currentAddon);
this.currentAuthorizedAddonAccounts.set(matchingAddons);

this.stepper()?.value.set(ProjectAddonsStepperValue.CHOOSE_ACCOUNT);
},
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
severity="info"
(click)="dialogRef.close()"
[disabled]="isSubmitting()"
data-test-addon-cancel-button
/>
<p-button
class="btn-full-width"
Expand All @@ -22,5 +23,6 @@
(onClick)="handleDisconnectAddonAccount()"
[loading]="isSubmitting()"
[disabled]="isSubmitting()"
data-test-addon-disconnect-button
/>
</div>
4 changes: 2 additions & 2 deletions src/app/features/settings/addons/addons.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<section class="flex-column flex flex-1">
<p-tabs [value]="selectedTab()" class="flex-1">
<p-tablist class="pr-5 pl-5 hidden md:flex md:flex-column">
<p-tab [value]="AddonTabValue.ALL_ADDONS">
<p-tab [value]="AddonTabValue.ALL_ADDONS" data-test-addons-tab-all-addons>
{{ 'settings.addons.tabs.allAddons' | translate }}
</p-tab>

<p-tab [value]="AddonTabValue.CONNECTED_ADDONS">
<p-tab [value]="AddonTabValue.CONNECTED_ADDONS" data-test-addons-tab-connected-accounts>
{{ 'settings.addons.tabs.connectedAddons' | translate }}
</p-tab>
</p-tablist>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ <h2>
severity="info"
class="w-10rem btn-full-width"
routerLink="/settings/addons"
data-test-addon-cancel-button
></p-button>
<p-button
[label]="'settings.addons.form.buttons.next' | translate"
class="w-10rem btn-full-width"
(onClick)="activateCallback(ProjectAddonsStepperValue.SETUP_NEW_ACCOUNT)"
data-test-addon-terms-confirm-button
></p-button>
</div>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from 'primeng/button';
import { StepPanel, StepPanels, Stepper } from 'primeng/stepper';
import { TableModule } from 'primeng/table';

import { Component, computed, effect, inject, signal, viewChild } from '@angular/core';
import { Component, computed, DestroyRef, effect, inject, signal, viewChild } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';

Expand All @@ -15,7 +15,7 @@ import { AddonServiceNames, AddonType, ProjectAddonsStepperValue } from '@osf/sh
import { getAddonTypeString, isAuthorizedAddon } from '@osf/shared/helpers';
import { AddonSetupAccountFormComponent, AddonTermsComponent } from '@shared/components/addons';
import { AddonModel, AddonTerm, AuthorizedAccountModel, AuthorizedAddonRequestJsonApi } from '@shared/models';
import { ToastService } from '@shared/services';
import { AddonOAuthService, ToastService } from '@shared/services';
import { AddonsSelectors, CreateAuthorizedAddon, UpdateAuthorizedAddon } from '@shared/stores/addons';

@Component({
Expand All @@ -40,6 +40,8 @@ import { AddonsSelectors, CreateAuthorizedAddon, UpdateAuthorizedAddon } from '@
export class ConnectAddonComponent {
private readonly router = inject(Router);
private readonly toastService = inject(ToastService);
private readonly oauthService = inject(AddonOAuthService);
private readonly destroyRef = inject(DestroyRef);

readonly stepper = viewChild(Stepper);
readonly AddonType = AddonType;
Expand All @@ -52,26 +54,17 @@ export class ConnectAddonComponent {
addonsUserReference = select(AddonsSelectors.getAddonsUserReference);
createdAddon = select(AddonsSelectors.getCreatedOrUpdatedAuthorizedAddon);
isCreatingAuthorizedAddon = select(AddonsSelectors.getCreatedOrUpdatedStorageAddonSubmitting);
isAuthorized = computed(() => {
return isAuthorizedAddon(this.addon());
});
addonTypeString = computed(() => {
return getAddonTypeString(this.addon());
});

isAuthorized = computed(() => isAuthorizedAddon(this.addon()));
addonTypeString = computed(() => getAddonTypeString(this.addon()));
userReferenceId = computed(() => this.addonsUserReference()[0]?.id);
baseUrl = computed(() => this.router.url.split('/addons')[0]);

actions = createDispatchMap({
createAuthorizedAddon: CreateAuthorizedAddon,
updateAuthorizedAddon: UpdateAuthorizedAddon,
});

readonly userReferenceId = computed(() => {
return this.addonsUserReference()[0]?.id;
});
readonly baseUrl = computed(() => {
const currentUrl = this.router.url;
return currentUrl.split('/addons')[0];
});

constructor() {
const addon = this.router.getCurrentNavigation()?.extras.state?.['addon'] as AddonModel | AuthorizedAccountModel;
if (!addon) {
Expand All @@ -84,28 +77,47 @@ export class ConnectAddonComponent {
this.stepper()?.value.set(ProjectAddonsStepperValue.SETUP_NEW_ACCOUNT);
}
});

this.destroyRef.onDestroy(() => {
this.oauthService.stopOAuthTracking();
});
}

handleConnectAuthorizedAddon(payload: AuthorizedAddonRequestJsonApi): void {
if (!this.addon()) return;

(!this.isAuthorized()
? this.actions.createAuthorizedAddon(payload, this.addonTypeString())
: this.actions.updateAuthorizedAddon(payload, this.addonTypeString(), this.addon()!.id)
).subscribe({
const action = this.isAuthorized()
? this.actions.updateAuthorizedAddon(payload, this.addonTypeString(), this.addon()!.id)
: this.actions.createAuthorizedAddon(payload, this.addonTypeString());

action.subscribe({
complete: () => {
const createdAddon = this.createdAddon();
if (createdAddon?.authUrl) {
this.addonAuthUrl.set(createdAddon.authUrl);
window.open(createdAddon.authUrl, '_blank');
this.stepper()?.value.set(ProjectAddonsStepperValue.AUTH);
this.startOauthFlow(createdAddon);
} else {
this.router.navigate([`${this.baseUrl()}/addons`]);
this.toastService.showSuccess('settings.addons.toast.createSuccess', {
addonName: AddonServiceNames[createdAddon?.externalServiceName as keyof typeof AddonServiceNames],
});
this.showSuccessAndRedirect(createdAddon);
}
},
});
}

private startOauthFlow(createdAddon: AuthorizedAccountModel): void {
this.addonAuthUrl.set(createdAddon.authUrl!);
window.open(createdAddon.authUrl!, '_blank');
this.stepper()?.value.set(ProjectAddonsStepperValue.AUTH);

this.oauthService.startOAuthTracking(createdAddon, this.addonTypeString(), {
onSuccess: (updatedAddon) => {
this.showSuccessAndRedirect(updatedAddon);
},
});
}

private showSuccessAndRedirect(createdAddon: AuthorizedAccountModel | null): void {
this.router.navigate([`${this.baseUrl()}/addons`]);
this.toastService.showSuccess('settings.addons.toast.createSuccess', {
addonName: AddonServiceNames[createdAddon?.externalServiceName as keyof typeof AddonServiceNames],
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@
class="addon-image"
alt="Addon card image"
[src]="'assets/icons/addons/' + card()!.externalServiceName + '.svg'"
data-test-addon-card-logo
/>
}
</div>

<div class="flex flex-column gap-3">
<h3 class="text-center">{{ card()?.displayName }}</h3>
<h3 class="text-center" data-test-addon-card-title>{{ card()?.displayName }}</h3>

<div class="flex justify-content-center align-items-center md:gap-5 md:mt-3 btn-container">
@if (showDangerButton()) {
<p-button
[label]="'settings.addons.form.buttons.disable' | translate"
severity="danger"
(onClick)="showDisableDialog()"
data-test-addon-card-disconnect
></p-button>
}

Expand Down
Loading
Loading