117 lines
5.0 KiB
TypeScript
117 lines
5.0 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
|
import { AuthService } from '../services/auth.service';
|
|
import { SubscriptionService } from '../services/subscription.service';
|
|
import { catchError, map } from 'rxjs/operators';
|
|
import { Observable, of } from 'rxjs';
|
|
import { StripeSubscription } from '../models/subscription.model';
|
|
import { SUB, SubStripe } from '@app/profile/common';
|
|
import { AppMessageService } from '@app/shared/app-message.service';
|
|
import { AC, globals } from '@app/shared/global';
|
|
import { RouterUtilsService } from '@app/shared/router-utils.service';
|
|
|
|
/**
|
|
This guards against unresolved subscriptions when the user is subscribing to a new subscription in the
|
|
main flow (services <->billing-detail<->checkout<->checkout-review<->checkout-confirm). When the user has an unresolved subscription, the guard forces user to enter the resolving subscription flow.
|
|
*/
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class SubscriptionGuard implements CanActivate {
|
|
|
|
constructor(
|
|
private readonly authSvc: AuthService,
|
|
private readonly subSvc: SubscriptionService,
|
|
private readonly router: Router,
|
|
private readonly msgSvc: AppMessageService,
|
|
private readonly routerUtils: RouterUtilsService
|
|
) { }
|
|
|
|
canActivate(activatedRoute: ActivatedRouteSnapshot): Observable<boolean> {
|
|
|
|
return this.subSvc.fetchSubscriptions(this.authSvc.user?.membership?.custId).pipe(
|
|
map((subs) => {
|
|
const fromPath = this.routerUtils.getCurrentUrl().split('/').pop();
|
|
const toPath = activatedRoute.url[0]?.path;
|
|
|
|
const hasLatestSubs = subs?.length > 0;
|
|
const hasUnresolvedSubs = this.hasUnresolvedSubs(subs);
|
|
const hasAllActiveSubs = hasLatestSubs && !hasUnresolvedSubs;
|
|
|
|
const isSubscribing = (!hasLatestSubs || hasAllActiveSubs)
|
|
&& (this.isInMainFlow(fromPath, toPath)
|
|
|| this.isPageReload(fromPath, toPath));
|
|
const isResolvingSubs = hasUnresolvedSubs
|
|
&& (this.isInResolvingFlow(fromPath, toPath)
|
|
|| this.isPageReload(fromPath, toPath));
|
|
|
|
const viewPayment = this.isPaymentPage(toPath);
|
|
const viewPaymentMethod = this.isPaymentMethodPage(toPath);
|
|
const viewAC = this.isACPage(toPath) && !hasUnresolvedSubs;
|
|
|
|
const canNavigate = isSubscribing || viewPayment || viewPaymentMethod || viewAC || true;
|
|
const shouldNavigateToMyServices = hasUnresolvedSubs && !isResolvingSubs;
|
|
|
|
if (canNavigate) {
|
|
return true;
|
|
} else if (shouldNavigateToMyServices) {
|
|
this.router.navigate([SUB.PROFILE, SUB.MY_SERVICES]);
|
|
} else if (isResolvingSubs) {
|
|
return true;
|
|
} else {
|
|
this.router.navigate(['/', SUB.HOME]);
|
|
}
|
|
}),
|
|
catchError(err => {
|
|
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.load).replace('#thing#', globals.subscription));
|
|
return of(false);
|
|
})
|
|
);
|
|
}
|
|
|
|
private isInMainFlow(fromPath: string, toPath: string): boolean {
|
|
return (fromPath === SUB.SERVICES && toPath === SUB.BILL_ADR) ||
|
|
(fromPath === SUB.BILL_ADR && toPath === SUB.CHKOUT) ||
|
|
(fromPath === SUB.CHKOUT && toPath === SUB.BILL_ADR) ||
|
|
(fromPath === SUB.CHKOUT && toPath === SUB.CHKOUT_REV) ||
|
|
(fromPath === SUB.CHKOUT_REV && toPath === SUB.CHKOUT_CONF) ||
|
|
(fromPath === SUB.CHKOUT_REV && toPath === SUB.CHKOUT) ||
|
|
this.isTrialFLow(fromPath, toPath);
|
|
}
|
|
|
|
private isInResolvingFlow(fromPath: string, toPath: string): boolean {
|
|
return (fromPath === SUB.UNPAID_SUB && toPath === SUB.BILL_ADR) ||
|
|
(fromPath === SUB.BILL_ADR && toPath === SUB.CHKOUT) ||
|
|
(fromPath === SUB.BILL_ADR && toPath === SUB.UNPAID_SUB) ||
|
|
(fromPath === SUB.CHKOUT && toPath === SUB.BILL_ADR) ||
|
|
(fromPath === SUB.CHKOUT && toPath === SUB.CHKOUT_REV) ||
|
|
(fromPath === SUB.CHKOUT_REV && toPath === SUB.CHKOUT) ||
|
|
(fromPath === SUB.CHKOUT_REV && toPath === SUB.CHKOUT_CONF) ||
|
|
(fromPath === SUB.MY_SERVICES && toPath === SUB.CHKOUT_REV) ||
|
|
(fromPath === SUB.MY_SERVICES && toPath === SUB.UNPAID_SUB);
|
|
}
|
|
|
|
private isTrialFLow(fromPath: string, toPath: string): boolean {
|
|
return (fromPath === SUB.CHKOUT && toPath === SUB.CHKOUT_CONF) ||
|
|
(fromPath === SUB.MY_SERVICES && toPath === SUB.BILL_ADR);
|
|
}
|
|
|
|
private isPageReload(fromPath: string, toPath: string): boolean {
|
|
return (!fromPath && (toPath === SUB.BILL_ADR || toPath === SUB.CHKOUT || toPath === SUB.CHKOUT_REV || toPath === SUB.CHKOUT_CONF));
|
|
}
|
|
|
|
private hasUnresolvedSubs(subs: StripeSubscription[]): boolean {
|
|
return subs?.some(sub => sub.status === SubStripe.PAST_DUE || sub.status === SubStripe.OVERDUE || sub.status === SubStripe.UNPAID || sub.status === SubStripe.INCOMPLETE);
|
|
}
|
|
|
|
private isPaymentPage(path: string): boolean {
|
|
return path === SUB.PM_HISTORY || path === SUB.PM_DETAIL;
|
|
}
|
|
|
|
private isPaymentMethodPage(path: string): boolean {
|
|
return path === SUB.PM_LIST;
|
|
}
|
|
|
|
private isACPage(path: string): boolean {
|
|
return path === AC;
|
|
}
|
|
} |