Browse Source

Feature/trial variation of subscription interstitials (#8258)

* Extend variations

* Update changelog
pull/8275/head
Thomas Kaul 2 days ago
committed by GitHub
parent
commit
44bcbf6147
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 18
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts
  3. 42
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html
  4. 10
      apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
  5. 5
      apps/client/src/app/components/user-account-membership/user-account-membership.html
  6. 14
      libs/common/src/lib/config.ts

1
CHANGELOG.md

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Extended the variations of the interstitials for the subscription by a trial
- Deprecated the `api/v1/auth-device` endpoint in favor of the `api/v1/auth-devices` endpoint
- Deprecated the `api/v1/exchange-rate` endpoint in favor of the `api/v1/exchange-rates` endpoint
- Upgraded `@modelcontextprotocol/core` and `@modelcontextprotocol/server` from version `2.0.0` to `2.3.1`

18
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts

@ -1,3 +1,5 @@
import { TRY_SUBSCRIPTION_MAIL_HREF } from '@ghostfolio/common/config';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfMembershipCardComponent } from '@ghostfolio/ui/membership-card';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
@ -51,6 +53,7 @@ export class GfSubscriptionInterstitialDialogComponent implements OnInit {
public remainingSkipButtonDelay =
GfSubscriptionInterstitialDialogComponent.SKIP_BUTTON_DELAY_IN_SECONDS;
public routerLinkPricing = publicRoutes.pricing.routerLink;
public trySubscriptionMailHref = TRY_SUBSCRIPTION_MAIL_HREF;
public variantIndex: number;
public constructor(
@ -59,9 +62,18 @@ export class GfSubscriptionInterstitialDialogComponent implements OnInit {
private destroyRef: DestroyRef,
public dialogRef: MatDialogRef<GfSubscriptionInterstitialDialogComponent>
) {
this.variantIndex = Math.floor(
Math.random() * GfSubscriptionInterstitialDialogComponent.VARIANTS_COUNT
);
const isEligibleForTrial =
!this.data.user?.subscription?.subscribedAt &&
hasPermission(
this.data.user?.permissions,
permissions.updateUserSettings
);
const variantsCount = isEligibleForTrial
? GfSubscriptionInterstitialDialogComponent.VARIANTS_COUNT + 1
: GfSubscriptionInterstitialDialogComponent.VARIANTS_COUNT;
this.variantIndex = Math.floor(Math.random() * variantsCount);
addIcons({ arrowForwardOutline, checkmarkCircleOutline });
}

42
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html

@ -195,4 +195,46 @@
</a>
</div>
}
@case (4) {
<div class="flex-grow-1 pt-0" mat-dialog-content>
<div class="intro-container">
<div class="intro-inner-container mx-auto">
<div class="h-100 intro w-100"></div>
</div>
</div>
<div>
<h5 class="align-items-center d-flex justify-content-center mb-3">
<span>Ghostfolio Premium</span>
<gf-premium-indicator class="ms-1" [enableLink]="false" />
</h5>
<div class="fw-normal h5 mb-3 text-center" i18n>
Request a free trial
</div>
<p class="m-0 text-center" i18n>
No credit card required. The trial ends automatically.
</p>
</div>
</div>
<div class="justify-content-end" mat-dialog-actions>
<button
mat-button
[disabled]="remainingSkipButtonDelay > 0"
(click)="closeDialog()"
>
<ng-container i18n>Skip</ng-container>
@if (remainingSkipButtonDelay > 0) {
({{ remainingSkipButtonDelay }})
}
</button>
<a
color="primary"
mat-flat-button
[href]="trySubscriptionMailHref"
(click)="closeDialog()"
>
<span i18n>Try Premium</span>
<ion-icon class="ms-1" name="arrow-forward-outline" />
</a>
</div>
}
}

10
apps/client/src/app/components/user-account-membership/user-account-membership.component.ts

@ -1,5 +1,5 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { E_MAIL_LINE_BREAK } from '@ghostfolio/common/config';
import { TRY_SUBSCRIPTION_MAIL_HREF } from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getDateFormatString } from '@ghostfolio/common/helper';
import { User } from '@ghostfolio/common/interfaces';
@ -49,13 +49,7 @@ export class GfUserAccountMembershipComponent {
protected hasPermissionToCreateApiKey: boolean;
protected hasPermissionToUpdateUserSettings: boolean;
protected price: number;
protected readonly trySubscriptionMailHref = `mailto:hi@ghostfol.io?subject=Ghostfolio Premium Trial&body=${[
'Hello',
'',
'I am interested in Ghostfolio Premium. Can you please send me a coupon code to try it for some time?',
'',
'Kind regards'
].join(E_MAIL_LINE_BREAK)}`;
protected readonly trySubscriptionMailHref = TRY_SUBSCRIPTION_MAIL_HREF;
protected user: User;
private couponId: string | undefined;

5
apps/client/src/app/components/user-account-membership/user-account-membership.html

@ -50,7 +50,10 @@
}
}
<div class="align-items-center d-flex justify-content-center mt-4">
@if (!user?.subscription?.subscribedAt) {
@if (
!user?.subscription?.subscribedAt &&
hasPermissionToUpdateUserSettings
) {
<a
class="mx-1"
mat-stroked-button

14
libs/common/src/lib/config.ts

@ -422,4 +422,18 @@ export const THROTTLE_DEFAULT_TTL = ms('1 minute');
export const THROTTLE_SIGNUP_LIMIT = 5;
export const THROTTLE_SIGNUP_TTL = ms('1 hour');
export const TRY_SUBSCRIPTION_MAIL_HREF = `mailto:hi@ghostfol.io?subject=${encodeURIComponent(
'Ghostfolio Premium Trial'
)}&body=${[
'Hello',
'',
'I am interested in Ghostfolio Premium. Can you please send me a coupon code to try it for some time?',
'',
'Kind regards'
]
.map((line) => {
return encodeURIComponent(line);
})
.join(E_MAIL_LINE_BREAK)}`;
export const UNKNOWN_KEY = 'UNKNOWN';

Loading…
Cancel
Save