...
 
Commits (2)
.m-marketing__main,
.m-marketing__section {
// Common
overflow-x: hidden;
.m-marketing--hideMobile {
@media screen and (max-width: $m-grid-min-vp) {
......@@ -107,6 +108,8 @@
}
.m-marketing__wrapper {
position: relative;
z-index: 0;
padding: 95px 0 85px;
@media screen and (max-width: $m-grid-min-vp) {
......@@ -115,10 +118,158 @@
}
}
.m-marketing__body {
position: relative;
&::after {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
width: 383px;
height: 388px;
transform: translate(-290px, 97px);
background: url('<%= APP_CDN %>/assets/marketing/deco_2.svg') no-repeat;
z-index: -1;
opacity: 0.6;
@media screen and (max-width: $m-grid-min-vp) {
transform: translate(170px, -180px);
left: auto;
right: 0;
}
}
}
h2,
.m-marketing__subtitle {
@media screen and (max-width: $m-grid-min-vp) {
padding: 0 30px;
}
}
p.m-marketing__description {
@include m-theme() {
color: themed($m-grey-300);
}
@media screen and (max-width: $m-grid-min-vp) {
padding: 0 30px;
}
}
ul.m-marketing__points {
@media screen and (max-width: $m-grid-min-vp) {
padding: 0 30px;
}
}
.m-marketing__image {
position: relative;
align-self: center;
z-index: 0;
span {
display: block;
position: relative;
width: 528px;
height: 415px;
@media screen and (max-width: $m-grid-min-vp) {
width: 313px;
height: 237px;
margin: 0 auto;
}
// Deco
&::before {
content: '';
display: block;
position: absolute;
width: 393px;
height: 193px;
top: 0;
left: 0;
transform: translate(61px, 179px);
background: url('<%= APP_CDN %>/assets/marketing/deco_1.svg')
no-repeat;
z-index: -1;
@media screen and (max-width: $m-grid-min-vp) {
top: 0;
left: 0;
width: 377px;
height: 193px;
transform: translate(-32px, 18px);
background-size: cover;
}
}
&::after {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
width: 383px;
height: 383px;
transform: translate(178px, -95px);
background: url('<%= APP_CDN %>/assets/marketing/deco_2.svg')
no-repeat;
z-index: -1;
@media screen and (max-width: $m-grid-min-vp) {
content: initial;
display: none;
}
}
}
img {
position: absolute;
object-fit: cover;
}
.m-marketing__image--1 {
top: 0;
left: 90px;
width: 327px;
height: 327px;
@media screen and (max-width: $m-grid-min-vp) {
top: 0;
left: 0;
width: 190px;
height: 190px;
}
}
.m-marketing__image--2 {
top: 295px;
left: 0;
width: 181px;
height: 120px;
@media screen and (max-width: $m-grid-min-vp) {
display: none;
}
}
.m-marketing__image--3 {
top: 163px;
left: 358px;
width: 170px;
height: 198px;
@media screen and (max-width: $m-grid-min-vp) {
top: 65px;
left: 165px;
width: 148px;
height: 172px;
}
}
}
}
......
......@@ -31,6 +31,14 @@
}
}
&.mf-button--destructive {
@include m-theme() {
background: themed($m-red);
border-color: themed($m-red);
color: themed($m-white-always);
}
}
@media screen and (max-width: $m-grid-min-vp) {
display: block;
font-size: 15px;
......
<div class="m-marketing">
<div class="m-marketing--hero">
<div class="m-marketing--hero--video">
<img [src]="minds.cdn_assets_url + 'assets/photos/fractal.jpg'" />
</div>
<m-marketing class="m-plus__marketing" pageTitle="Minds Plus" i18n-pageTitle>
<div class="m-marketing__main m-marketing__section--style-2">
<div class="m-grid m-marketing__wrapper">
<div class="m-grid__column-7 m-grid__column-12--mobile m-marketing__body">
<h1 i18n>
Minds Plus
</h1>
<div class="m-marketing--hero--inner">
<div class="m-marketing--hero--overlay"></div>
<h2 ngPreserveWhitespaces i18n>
Upgrade your channel and <em>unlock premium features</em>
</h2>
<div class="m-marketing--hero--slogans">
<h1 i18n="@@PLUS__MKT__PLUS_TITLE">Minds Plus</h1>
<h3 i18n="@@PLUS__MKT__PLUS_DESC">
Upgrade your channel and unlock premium features
</h3>
</div>
<p class="m-marketing__description" i18n>
Support Minds and unlock features such as hiding ads, accessing
exclusive content, receiving a badge and verifying your channel.
</p>
<div class="m-marketing--hero--actions m-marketing--action-button">
<m-plus--subscription></m-plus--subscription>
<div class="m-plusMarketing__subscription">
<m-plus--subscription></m-plus--subscription>
</div>
</div>
</div>
</div>
<div class="m-marketing--contents m-layout--row">
<div
class="mdl-cell mdl-cell--4-col mdl-cell--4-col-tablet mdl-cell--6-col-phone"
>
<i class="material-icons mdl-color-text--blue-grey-700">trending_up</i>
<h2 i18n="@@PLUS__MKT__HIDE_BOOST_TITLE">Hide Boosts</h2>
<span i18n="@@PLUS__MKT__HIDE_BOOST_DESC"
>Hide all boosted content from your newsfeed and sidebars
(optional)</span
>
</div>
<div
class="mdl-cell mdl-cell--4-col mdl-cell--4-col-tablet mdl-cell--6-col-phone"
>
<i class="material-icons mdl-color-text--blue-grey-700">favorite</i>
<h2 i18n="@@PLUS__MKT__EXCLUSIVE_TITLE">Exclusive</h2>
<span i18n="@@PLUS__MKT__EXCLUSIVE_DESC"
>Access exclusive Minds content</span
>
</div>
<div
class="mdl-cell mdl-cell--4-col mdl-cell--4-col-tablet mdl-cell--6-col-phone"
>
<i class="material-icons mdl-color-text--blue-grey-700">verified_user</i>
<h2 i18n="@@PLUS__MKT__VERIFIED_TITLE">Verified</h2>
<span *ngIf="!user || !user.plus" i18n="@@PLUS__MKT__VERIFIED_DESC"
>Request to verify your channel</span
>
<span
*ngIf="user && user.plus && !user.verified"
i18n="@@PLUS__MKT__VERIFY_LINK"
>
<a (click)="showVerify = true; detectChanges()" style="cursor:pointer"
>Click here</a
>
to request to be verified
</span>
<span
*ngIf="user && user.plus && user.verified"
i18n="@@PLUS__MKT__YOURE_VERIFIED_LEGEND"
>You are verified!</span
>
</div>
<div
class="mdl-cell mdl-cell--4-col mdl-cell--4-col-tablet mdl-cell--6-col-phone"
>
<i class="material-icons mdl-color-text--blue-grey-700"
>add_circle_outline</i
>
<h2 i18n="@@PLUS__MKT__BADGE_TITLE">Badge</h2>
<span i18n="@@PLUS__MKT__BADGE_DESC"
>Make your channel stand out with a Minds Plus badge</span
<div
class="m-grid__column-5 m-grid__column-12--mobile m-marketing__image"
>
<span>
<img
[src]="
cdnAssetsUrl + 'assets/photos/browsing-mobileapp-discovery.jpg'
"
/>
</span>
</div>
</div>
</div>
<div class="m-marketing--faq">
<m-faq category="plus"></m-faq>
</div>
<m-plus--verify
*ngIf="showVerify"
(closed)="showVerify = false; detectChanges()"
></m-plus--verify>
</div>
<m-footer></m-footer>
<m-marketing__asFeaturedIn slot="2"></m-marketing__asFeaturedIn>
</m-marketing>
.m-plus--marketing-header {
background: url('<%= APP_CDN %>/assets/photos/fractal.jpg');
width: 100%;
text-align: center;
display: flex;
align-content: center;
flex-direction: column;
padding: 224px 36px;
box-sizing: border-box;
position: relative;
@media only screen and (max-width: 400px) {
padding: 110px 0;
}
h1,
h3 {
font-family: 'Roboto', Helvetica, sans-serif;
.m-plus__marketing {
.m-marketing--hero--slogans ul {
font-size: 20px;
padding: 0 0 0 16px;
margin: 32px 0;
@include m-theme() {
color: themed($m-white);
text-shadow: 0 0 3px themed($m-grey-900);
}
@media only screen and (max-width: 400px) {
margin: 0;
}
z-index: 1;
}
h1 {
word-spacing: 25px;
letter-spacing: 4px;
text-transform: uppercase;
@media only screen and (max-width: 400px) {
font-size: 30px;
}
}
h3 {
letter-spacing: 2px;
word-spacing: 3px;
font-weight: 300;
@media only screen and (max-width: 400px) {
font-size: 14px;
}
}
li {
margin-bottom: 8px;
.m-plus--marketing-action-button {
button {
letter-spacing: 3px;
font-size: 18px;
line-height: 35px;
height: 53px;
padding: 0 24px;
font-weight: 300;
font-family: 'Roboto', Helvetica, sans-serif;
@include m-theme() {
color: themed($m-white);
&:last-child {
margin-bottom: 0;
}
}
z-index: 2;
}
.m-plus--overlay {
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 0;
@include m-theme() {
background-color: rgba(themed($m-black), 0.5);
}
}
}
.m-plus--marketing-contents {
max-width: 990px;
padding: 64px 0 !important;
justify-content: center;
.mdl-cell {
padding: 32px 16px;
text-align: center;
i {
font-size: 64px;
}
h2 {
padding: 0;
margin: 24px 0;
text-transform: uppercase;
font-family: 'Roboto', Helvetica, sans-serif;
font-weight: 300;
letter-spacing: 2px;
font-size: 30px;
}
span {
font-family: 'Roboto', Helvetica, sans-serif;
letter-spacing: 1px;
}
}
}
.m-plus--marketing m-plus--onboarding {
margin: -115px auto 0;
display: block;
position: relative;
max-width: 990px;
form {
padding: 16px;
}
}
.m-plus--marketing-faq {
max-width: 900px;
margin: auto;
padding: 16px;
}
.m-plus--marketing m-plus--terms {
max-width: 600px;
margin: 16px auto;
display: block;
padding: 16px;
max-height: 600px;
overflow: scroll;
@include m-theme() {
background-color: themed($m-white);
}
}
.m-marketing--action-button button {
margin: 5px;
}
///<reference path="../../../../node_modules/@types/jasmine/index.d.ts"/>
import {
async,
ComponentFixture,
fakeAsync,
TestBed,
tick,
} from '@angular/core/testing';
import {
Component,
DebugElement,
EventEmitter,
Input,
Output,
} from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Client } from '../../services/api/client';
import { clientMock } from '../../../tests/client-mock.spec';
import { PlusVerify } from '../../mocks/modules/plus/verify';
import { FooterComponentMock } from '../../mocks/modules/plus/footer';
import { FaqMock } from '../../mocks/modules/plus/faq';
import { PlusSubscription } from '../../mocks/modules/plus/subscription';
import { PlusMarketingComponent } from './marketing.component';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { By } from '@angular/platform-browser';
describe('PlusMarketingComponent', () => {
let comp: PlusMarketingComponent;
let fixture: ComponentFixture<PlusMarketingComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
PlusMarketingComponent,
PlusSubscription,
FooterComponentMock,
PlusVerify,
FaqMock,
],
imports: [RouterTestingModule, FormsModule, ReactiveFormsModule],
providers: [{ provide: Client, useValue: clientMock }],
}).compileComponents();
}));
beforeEach(done => {
jasmine.MAX_PRETTY_PRINT_DEPTH = 10;
jasmine.clock().uninstall();
jasmine.clock().install();
fixture = TestBed.createComponent(PlusMarketingComponent);
comp = fixture.componentInstance;
window.Minds.user = {
guid: '732337264197111809',
type: 'user',
subtype: false,
time_created: '1499978809',
time_updated: false,
container_guid: '0',
owner_guid: '0',
site_guid: false,
access_id: '2',
name: 'minds',
username: 'minds',
language: 'en',
icontime: '1506690756',
legacy_guid: false,
featured_id: false,
banned: 'no',
website: '',
dob: '',
gender: '',
city: '',
merchant: {},
boostProPlus: false,
fb: false,
mature: 0,
monetized: '',
signup_method: false,
social_profiles: [],
feature_flags: false,
programs: ['affiliate'],
plus: true,
verified: false,
disabled_boost: false,
show_boosts: false,
chat: true,
subscribed: false,
subscriber: false,
subscriptions_count: 1,
impressions: 10248,
boost_rating: '2',
spam: 0,
deleted: 0,
};
// Set up mock HTTP client
clientMock.response = {};
clientMock.response['api/v1/plus'] = {
status: 'success',
active: false,
};
fixture.detectChanges();
if (fixture.isStable()) {
done();
} else {
fixture.whenStable().then(() => {
done();
});
}
});
afterEach(() => {
// reset jasmine clock after each test
jasmine.clock().uninstall();
});
it('Should load correctly', () => {
const marketing = fixture.debugElement.query(By.css('.m-marketing--hero'));
const marketingInner = fixture.debugElement.query(
By.css('.m-marketing--hero--inner')
);
expect(marketing).not.toBeNull();
expect(marketingInner).not.toBeNull();
});
});
import {
Component,
ChangeDetectionStrategy,
ChangeDetectorRef,
ViewChild,
ElementRef,
} from '@angular/core';
import { PlusSubscriptionComponent } from './subscription.component';
import { Client } from '../../common/api/client.service';
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'm-plus--marketing',
......@@ -15,12 +6,5 @@ import { Client } from '../../common/api/client.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlusMarketingComponent {
@ViewChild('subscription', { static: false })
private subscription: PlusSubscriptionComponent;
user = window.Minds.user;
minds = window.Minds;
showVerify: boolean = false;
constructor(private client: Client, private cd: ChangeDetectorRef) {}
readonly cdnAssetsUrl: string = window.Minds.cdn_assets_url;
}
......@@ -7,8 +7,9 @@ import { CommonModule } from '../../common/common.module';
import { CheckoutModule } from '../checkout/checkout.module';
import { FaqModule } from '../faq/faq.module';
import { PlusMarketingComponent } from './marketing.component';
import { PlusSubscriptionComponent } from './subscription.component';
import { PlusSubscriptionComponent } from './subscription/subscription.component';
import { PlusVerifyComponent } from './verify/verify.component';
import { PlusService } from './plus.service';
const plusRoutes: Routes = [
{ path: 'plus', component: PlusMarketingComponent },
......@@ -30,6 +31,7 @@ const plusRoutes: Routes = [
PlusVerifyComponent,
],
exports: [PlusSubscriptionComponent, PlusVerifyComponent],
providers: [PlusService],
entryComponents: [PlusMarketingComponent],
})
export class PlusModule {}
import { Injectable } from '@angular/core';
import { Client } from '../../services/api/client';
@Injectable()
export class PlusService {
constructor(protected client: Client) {}
async isActive(): Promise<boolean> {
const result: any = await this.client.get('api/v1/plus');
if (!result || typeof result.active === 'undefined') {
throw new Error('Unable to check your Plus status');
}
return Boolean(result.active);
}
async disable(): Promise<boolean> {
await this.client.delete('api/v1/plus/subscription');
return true;
}
}
<button
class="mdl-button mdl-button--colored mdl-color--green"
*ngIf="user && !isPlus()"
(click)="purchase()"
[hidden]="active"
>
Upgrade - {{ minds.upgrades.plus['monthly']['tokens'] }} Tokens/month
</button>
<!--<button-->
<!-- class="mdl-button mdl-button&#45;&#45;colored mdl-color&#45;&#45;green"-->
<!-- *ngIf="user && !isPlus() && false"-->
<!-- (click)="purchase(200, 'year')"-->
<!-- [hidden]="active"-->
<!--&gt;-->
<!-- Upgrade - 200 Tokens/year-->
<!--</button>-->
<button
class="mdl-button mdl-button--colored mdl-color--green"
*ngIf="user && isPlus()"
(click)="cancel()"
i18n="@@PLUS__MKT__CANCEL_ACTION"
>
Cancel Plus
</button>
<button
class="mdl-button mdl-button--colored mdl-color--green"
*ngIf="!user"
routerLink="/login"
>
Upgrade - {{ minds.upgrades.plus['monthly']['tokens'] }} Tokens/month
</button>
<div class="m-plus--subscription mdl-card mdl-shadow--4dp" *ngIf="active">
<p
class="mdl-card__supporting-text"
i18n="@@PLUS__SUBSCRIPTION__CURRENTLY_MEMBER"
>
You are a currently a Plus member.
</p>
<button
(click)="cancel()"
class="mdl-button mdl-button--colored mdl-button--raised"
style="width:200px; margin: 0 16px 16px;"
i18n="@@PLUS__SUBSCRIPTION__CANCEL_PLUS_ACTION"
>
Cancel Plus
</button>
</div>
<div
class="m-plus--subscription mdl-card mdl-shadow--4dp"
*ngIf="completed && isPlus()"
>
<p
class="mdl-card__supporting-text"
i18n="@@PLUS__SUBSCRIPTION__CONGRATULATE_PLUS"
>
Congratulations, you are now a Minds Plus member!
</p>
</div>
<div
class="m-plus--subscription mdl-card mdl-shadow--4dp"
*ngIf="completed && !isPlus()"
>
<p
class="mdl-card__supporting-text"
i18n="@@PLUS__SUBSCRIPTION__REMOVED_PLUS"
>
You have stopped being a Minds Plus Member.
</p>
</div>
import {
Component,
ChangeDetectionStrategy,
ChangeDetectorRef,
EventEmitter,
Output,
Input,
} from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Subscription } from 'rxjs';
import { Client } from '../../common/api/client.service';
import { Web3WalletService } from '../blockchain/web3-wallet.service';
import { TokenContractService } from '../blockchain/contracts/token-contract.service';
import { WireService } from '../wire/wire.service';
import { WireStruc } from '../wire/creator/creator.component';
import { OverlayModalService } from '../../services/ux/overlay-modal';
import { SignupModalService } from '../modals/signup/service';
import { Session } from '../../services/session';
import { WirePaymentsCreatorComponent } from '../wire/creator/payments/payments.creator.component';
import { WirePaymentHandlersService } from '../wire/wire-payment-handlers.service';
@Component({
selector: 'm-plus--subscription',
templateUrl: 'subscription.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlusSubscriptionComponent {
user = window.Minds.user;
blockchain = window.Minds.blockchain;
source: string;
error: string;
inProgress: boolean = true;
completed: boolean = false;
active: boolean = false;
@Output('completed') completed$: EventEmitter<any> = new EventEmitter();
@Input('showSubscription') showSubscription: boolean;
payload: any;
minds = window.Minds;
currency: string;
interval: string;
paramSubscription: Subscription;
constructor(
private client: Client,
private tokenContract: TokenContractService,
private wireService: WireService,
private web3Wallet: Web3WalletService,
private overlayModal: OverlayModalService,
private modal: SignupModalService,
private wirePaymentHandlers: WirePaymentHandlersService,
public session: Session,
private cd: ChangeDetectorRef,
private route: ActivatedRoute
) {}
ngOnInit() {
this.paramSubscription = this.route.queryParams.subscribe(
(params: Params) => {
this.currency = params.c || 'tokens';
this.interval = params.i || 'yearly';
if (params.c || params.i) this.purchase();
}
);
}
load(): Promise<any> {
return this.client
.get('api/v1/plus')
.then(({ active }) => {
if (active) this.active = true;
return active;
})
.catch(e => {
throw e;
});
}
isPlus() {
if (this.active || (this.user && this.user.plus)) return true;
return false;
}
setSource(source: string) {
this.source = source;
this.purchase();
}
async purchase(/*amount: number = 20, period: 'month' | 'year' = 'month'*/) {
if (!this.session.isLoggedIn()) {
this.modal.open();
return;
}
const creator = this.overlayModal.create(
WirePaymentsCreatorComponent,
await this.wirePaymentHandlers.get('plus'),
{
interval: this.interval,
currency: this.currency,
amount: this.minds.upgrades.plus[this.interval][this.currency],
onComplete: wire => {
this.completed = true;
this.user.plus = true;
this.active = true;
this.detectChanges();
this.completed$.next(true);
},
}
);
creator.present();
}
cancel() {
this.inProgress = true;
this.error = '';
this.detectChanges();
return this.client
.delete('api/v1/plus/subscription')
.then((response: any) => {
this.inProgress = false;
this.user.plus = false;
this.active = false;
this.detectChanges();
});
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
ngOnDestroy() {
this.paramSubscription.unsubscribe();
}
}
m-plus--subscription {
max-width: 600px;
display: block;
margin: -120px auto 0;
z-index: 5;
position: relative;
button.mdl-color--green {
height: auto !important;
}
.m-plus--subscription--select-payment-option {
margin: 10px;
letter-spacing: 0.5px;
}
.m-plus--subscription-currencies {
display: flex;
align-items: center;
justify-content: space-around;
margin: 0;
padding: 0;
list-style: none;
li {
padding: 8px 24px;
padding-left: 24px;
cursor: pointer;
user-select: none;
h4,
span {
margin: 0;
font-weight: 300;
font-size: 24px;
font-weight: 400;
letter-spacing: 0.5px;
}
b {
font-weight: 400 !important;
letter-spacing: 3px;
}
&:first-child {
padding-left: 0;
}
}
}
m--crypto-token-symbol {
font-size: 32px;
.m--crypto-token-symbol--stroke {
@include m-theme() {
stroke: themed($m-grey-700);
}
}
.m--crypto-token-symbol--fill {
@include m-theme() {
fill: themed($m-grey-700);
}
}
}
}
<ng-container *ngIf="!inProgress; else inProgressSpinner">
<div *ngIf="!active; else cancelButton">
<div class="m-plusSubscriptionPlan__toggleContainer">
<div class="m-plusSubscriptionPlan__toggle">
<span i18n>Yearly</span>
<span
><m-toggle
[(mModel)]="interval"
leftValue="yearly"
rightValue="monthly"
></m-toggle
></span>
<span i18n>Monthly</span>
</div>
<div class="m-plusSubscriptionPlan__toggle">
<span i18n>USD</span>
<span
><m-toggle
[(mModel)]="currency"
leftValue="usd"
rightValue="tokens"
></m-toggle
></span>
<span i18n>Tokens</span>
</div>
</div>
<div class="m-plusSubscriptionPlan__pricing">
<span class="m-plusSubscriptionPlanPricing__amount" i18n>
<span>{{ pricing.amount }}</span> per month
</span>
<span
class="m-plusSubscriptionPlanPricing__offer"
*ngIf="pricing.offerFrom"
i18n
>
{{ pricing.offerFrom }} per month
</span>
</div>
<div>
<button
class="mf-button mf-button--alt"
[disabled]="inProgress || criticalError"
(click)="enable()"
i18n
>
Upgrade to Plus
</button>
</div>
</div>
<ng-template #cancelButton>
<div>
<button
class="mf-button mf-button--destructive"
[disabled]="inProgress || criticalError"
(click)="disable()"
i18n
>
Cancel Plus
</button>
</div>
</ng-template>
<span *ngIf="error" class="m-plusSubscription__error">
{{ error }}
</span>
</ng-container>
<ng-template #inProgressSpinner>
<div class="m-plusSubscription__inProgress">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</div>
</ng-template>
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
OnInit,
Output,
} from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Subscription } from 'rxjs';
import { Session } from '../../../services/session';
import { PlusService } from '../plus.service';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { WirePaymentsCreatorComponent } from '../../wire/creator/payments/payments.creator.component';
import { WirePaymentHandlersService } from '../../wire/wire-payment-handlers.service';
import {
UpgradeOptionCurrency,
UpgradeOptionInterval,
} from '../../upgrades/upgrade-options.component';
import currency from '../../../helpers/currency';
@Component({
selector: 'm-plus--subscription',
templateUrl: 'subscription.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlusSubscriptionComponent implements OnInit {
@Output() onEnable: EventEmitter<any> = new EventEmitter();
@Output() onDisable: EventEmitter<any> = new EventEmitter();
interval: UpgradeOptionInterval = 'yearly';
currency: UpgradeOptionCurrency = 'usd';
paramSubscription: Subscription;
isLoggedIn: boolean = false;
inProgress: boolean = false;
active: boolean;
criticalError: boolean = false;
error: string = '';
minds = window.Minds;
constructor(
protected service: PlusService,
protected session: Session,
protected overlayModal: OverlayModalService,
protected wirePaymentHandlers: WirePaymentHandlersService,
protected cd: ChangeDetectorRef,
protected route: ActivatedRoute
) {}
ngOnInit() {
this.isLoggedIn = this.session.isLoggedIn();
if (this.isLoggedIn) {
this.load();
}
this.paramSubscription = this.route.queryParams.subscribe(
(params: Params) => {
this.currency = params.c || 'usd';
this.interval = params.i || 'yearly';
if (params.c || params.i) this.enable();
}
);
}
async load() {
this.inProgress = true;
this.error = '';
this.detectChanges();
try {
this.active = await this.service.isActive();
} catch (e) {
this.criticalError = true;
this.error = (e && e.message) || 'Unknown error';
}
this.inProgress = false;
this.detectChanges();
}
async enable() {
this.inProgress = true;
this.error = '';
this.detectChanges();
try {
this.overlayModal
.create(
WirePaymentsCreatorComponent,
await this.wirePaymentHandlers.get('plus'),
{
interval: this.interval,
currency: this.currency,
amount: this.minds.upgrades.plus[this.interval][this.currency],
onComplete: () => {
this.active = true;
this.minds.user.plus = true;
this.onEnable.emit(Date.now());
this.inProgress = false;
this.detectChanges();
},
}
)
.onDidDismiss(() => {
this.inProgress = false;
this.detectChanges();
})
.present();
} catch (e) {
this.active = false;
this.minds.user.plus = false;
this.error = (e && e.message) || 'Unknown error';
this.inProgress = false;
}
this.detectChanges();
}
async disable() {
this.inProgress = true;
this.error = '';
this.detectChanges();
try {
await this.service.disable();
this.active = false;
this.minds.user.plus = false;
this.onDisable.emit(Date.now());
} catch (e) {
this.active = true;
this.minds.user.plus = true;
this.error = (e && e.message) || 'Unknown error';
}
this.inProgress = false;
this.detectChanges();
}
get pricing() {
if (this.interval === 'yearly') {
return {
amount: currency(
this.minds.upgrades.plus.yearly[this.currency] / 12,
this.currency
),
offerFrom: currency(
this.minds.upgrades.plus.monthly[this.currency],
this.currency
),
};
} else if (this.interval === 'monthly') {
return {
amount: currency(
this.minds.upgrades.plus.monthly[this.currency],
this.currency
),
offerFrom: null,
};
}
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
ngOnDestroy() {
this.paramSubscription.unsubscribe();
}
}
@import '../../../foundation/grid-values';
m-plus--subscription {
.m-plusSubscriptionPlan__toggleContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
margin-bottom: 23px;
@media screen and (max-width: $m-grid-min-vp) {
justify-content: center;
margin-bottom: 40px;
}
}
.m-plusSubscriptionPlan__toggle {
display: flex;
flex-direction: row;
align-items: center;
justify-content: stretch;
margin-right: 50px;
&:last-child {
margin-right: 0;
}
}
.m-plusSubscriptionPlan__pricing {
margin-bottom: 24px;
@media screen and (max-width: $m-grid-min-vp) {
margin-bottom: 40px;
text-align: center;
}
.m-plusSubscriptionPlanPricing__amount {
display: inline-block;
font-size: 18px;
font-weight: bold;
line-height: 24px;
@include m-theme() {
color: themed($m-grey-600);
}
@media screen and (max-width: $m-grid-min-vp) {
display: block;
}
span {
font-size: 24px;
@include m-theme() {
color: themed($m-black);
}
}
}
.m-plusSubscriptionPlanPricing__offer {
display: inline-block;
font-size: 14px;
text-decoration: line-through;
margin-left: 10px;
@include m-theme() {
color: themed($m-red);
}
@media screen and (max-width: $m-grid-min-vp) {
display: block;
margin-left: 0;
}
}
}
.m-plusSubscription__error {
display: block;
font-weight: bold;
margin: 8px 0 0 5px;
@include m-theme() {
color: themed($m-red);
}
}
}
......@@ -7,8 +7,7 @@
</h1>
<h2 ngPreserveWhitespaces i18n>
The ultimate platform for <em>creators</em> <em>and</em>
<em>brands</em>
The ultimate platform for <em>creators and brands</em>
</h2>
<p class="m-marketing__description" i18n>
......@@ -17,7 +16,7 @@
what you love.
</p>
<div class="">
<div class="m-proMarketing__subscription">
<m-pro--subscription
(onEnable)="goToSettings()"
></m-pro--subscription>
......
.m-marketing__Pro {
.m-pro__marketing {
.m-marketing--hero--slogans ul {
font-size: 20px;
padding: 0 0 0 16px;
......
<div *ngIf="isLoggedIn || true">
<ng-container *ngIf="!inProgress; else inProgressSpinner">
<div>
<div class="m-proSubscriptionPlan__toggleContainer">
<div class="m-proSubscriptionPlan__toggle">
<span>Yearly</span>
<span
><m-toggle
[(mModel)]="interval"
leftValue="yearly"
rightValue="monthly"
></m-toggle
></span>
<span>Monthly</span>
</div>
<div class="m-proSubscriptionPlan__toggle">
<span>USD</span>
<span
><m-toggle
[(mModel)]="currency"
leftValue="usd"
rightValue="tokens"
></m-toggle
></span>
<span>Tokens</span>
</div>
</div>
<div class="m-proSubscriptionPlan__pricing">
<span class="m-proSubscriptionPlanPricing__amount">
<span>{{ pricing.amount }}</span> per month
</span>
<ng-container *ngIf="!inProgress; else inProgressSpinner">
<div *ngIf="!active; else cancelButton">
<div class="m-proSubscriptionPlan__toggleContainer">
<div class="m-proSubscriptionPlan__toggle">
<span i18n>Yearly</span>
<span
class="m-proSubscriptionPlanPricing__offer"
*ngIf="pricing.offerFrom"
>
{{ pricing.offerFrom }} per month
</span>
><m-toggle
[(mModel)]="interval"
leftValue="yearly"
rightValue="monthly"
></m-toggle
></span>
<span i18n>Monthly</span>
</div>
<div>
<button
*ngIf="!active"
class="mf-button mf-button--alt"
[disabled]="inProgress || criticalError"
(click)="enable()"
>
Upgrade to Pro
</button>
<div class="m-proSubscriptionPlan__toggle">
<span i18n>USD</span>
<span
><m-toggle
[(mModel)]="currency"
leftValue="usd"
rightValue="tokens"
></m-toggle
></span>
<span i18n>Tokens</span>
</div>
</div>
<button
*ngIf="active"
class="mdl-button mdl-button--colored mdl-color--red"
[disabled]="inProgress || criticalError"
(click)="disable()"
>
Cancel Pro
</button>
<div class="m-proSubscriptionPlan__pricing">
<span class="m-proSubscriptionPlanPricing__amount" i18n>
<span>{{ pricing.amount }}</span> per month
</span>
<span *ngIf="error" class="m-proSubscription__error">
{{ error }}
</span>
</ng-container>
<span
class="m-proSubscriptionPlanPricing__offer"
*ngIf="pricing.offerFrom"
i18n
>
{{ pricing.offerFrom }} per month
</span>
</div>
<ng-template #inProgressSpinner>
<div class="m-proSubscription__inProgress">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
<div>
<button
class="mf-button mf-button--alt"
[disabled]="inProgress || criticalError"
(click)="enable()"
i18n
>
Upgrade to Pro
</button>
</div>
</div>
<ng-template #cancelButton>
<div>
<button
class="mf-button mf-button--destructive"
[disabled]="inProgress || criticalError"
(click)="disable()"
i18n
>
Cancel Pro
</button>
</div>
</ng-template>
</div>
<span *ngIf="error" class="m-proSubscription__error">
{{ error }}
</span>
</ng-container>
<ng-template #inProgressSpinner>
<div class="m-proSubscription__inProgress">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</div>
</ng-template>
......@@ -18,7 +18,7 @@
</div>
<div class="m-upgradesBuyTokens__form">
<div class="upgradesBuyTokens__amount">
<div class="upgradesBuyTokens__amount" style="visibility: hidden;">
<label for="m-upgradesBuyTokens__amountInput">$</label>
<input
type="number"
......@@ -32,7 +32,12 @@
</div>
<div class="upgradesBuyTokens__action">
<button class="mf-button">Buy Tokens</button>
<button
class="mf-button"
(click)="navigateToTokensPage(); $event.preventDefault()"
>
Buy Tokens
</button>
</div>
</div>
......
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'm-upgrades__buyTokens',
......@@ -7,4 +8,10 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
})
export class BuyTokensComponent {
readonly cdnAssetsUrl: string = window.Minds.cdn_assets_url;
constructor(protected router: Router) {}
navigateToTokensPage() {
this.router.navigate(['/token']);
}
}
......@@ -5,7 +5,7 @@
>
<div class="m-marketing__main m-marketing__section--style-1">
<div class="m-grid m-marketing__wrapper">
<div class="m-grid__column-5 m-grid__column-12--mobile m-upgrades__body">
<div class="m-grid__column-5 m-grid__column-12--mobile m-marketing__body">
<h2 class="m-marketing__subtitle--asTitle" i18n>
Upgrade your Minds experience
</h2>
......@@ -32,8 +32,25 @@
</a>
</div>
<div class="m-grid__column-7 m-grid__column-12--mobile m-upgrades__image">
<!-- Image -->
<div
class="m-grid__column-7 m-grid__column-12--mobile m-marketing__image"
>
<span>
<img
class="m-marketing__image--1"
[src]="cdnAssetsUrl + 'assets/marketing/upgrades-1.jpg'"
/>
<img
class="m-marketing__image--2"
[src]="cdnAssetsUrl + 'assets/marketing/upgrades-2.jpg'"
/>
<img
class="m-marketing__image--3"
[src]="cdnAssetsUrl + 'assets/marketing/upgrades-3.jpg'"
/>
</span>
</div>
</div>
</div>
......
......@@ -11,6 +11,8 @@ import {
templateUrl: 'upgrades.component.html',
})
export class UpgradesComponent {
readonly cdnAssetsUrl: string = window.Minds.cdn_assets_url;
@ViewChild('upgradeOptionsAnchor', { static: false })
readonly upgradeOptionsAnchor: ElementRef;
......
src/assets/marketing/upgrades-1.jpg

76.7 KB

src/assets/marketing/upgrades-2.jpg

40.3 KB

src/assets/marketing/upgrades-3.jpg

55.9 KB

src/assets/photos/browsing-mobileapp-discovery.jpg

42.4 KB