Loading src/app/app.component.scss +12 −0 Original line number Original line Diff line number Diff line Loading @@ -344,6 +344,18 @@ m-app { font-size: 16px; font-size: 16px; font-weight: bold; font-weight: bold; padding: 4px; padding: 4px; .m-tooltip i.material-icons { vertical-align: middle; font-size: 1em; margin-left: 8px; position: relative; top: -1px; @include m-theme(){ color: themed($m-red); } } } } } } Loading src/app/modules/boost/campaigns/campaigns.service.ts +15 −1 Original line number Original line Diff line number Diff line import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core'; import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignType } from './campaigns.type'; import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignPreview, CampaignType } from './campaigns.type'; import { Client } from '../../../services/api/client'; import { Client } from '../../../services/api/client'; import isInt from '../../../helpers/is-int'; import isInt from '../../../helpers/is-int'; import getGuidFromUrn from '../../../helpers/get-guid-from-urn'; import getGuidFromUrn from '../../../helpers/get-guid-from-urn'; Loading Loading @@ -84,6 +84,20 @@ export class CampaignsService { !!campaign.type; !!campaign.type; } } async preview(campaign: Campaign): Promise<CampaignPreview | false> { if ( !campaign.type || !campaign.entity_urns || !campaign.entity_urns.length || !campaign.budget || !campaign.budget_type ) { return false; } return (await this.client.post(`api/v2/boost/campaigns/preview`, campaign) as any).preview; } async prepare(campaign: Campaign): Promise<{ checksum: string, guid: string }> { async prepare(campaign: Campaign): Promise<{ checksum: string, guid: string }> { if (campaign.entity_urns.length !== 1) { if (campaign.entity_urns.length !== 1) { throw new Error('Campaigns without a single entity are unsupported'); throw new Error('Campaigns without a single entity are unsupported'); Loading src/app/modules/boost/campaigns/campaigns.type.ts +4 −0 Original line number Original line Diff line number Diff line Loading @@ -40,3 +40,7 @@ export type CampaignPayment = { txHash: string, txHash: string, amount: number, amount: number, }; }; export type CampaignPreview = { cannot_fulfill_daily?: boolean, }; src/app/modules/boost/campaigns/creator/creator.component.html +33 −10 Original line number Original line Diff line number Diff line Loading @@ -24,7 +24,13 @@ <m-tooltip icon="help" i18n>TBD</m-tooltip> <m-tooltip icon="help" i18n>TBD</m-tooltip> </label> </label> <input id="boost-creator__name" type="text" name="name" [(ngModel)]="campaign.name"> <input id="boost-creator__name" type="text" name="name" [ngModel]="campaign.name" (ngModelChange)="campaign.name = $event; triggerPreview()" > </div> </div> <div class="m-form--field"> <div class="m-form--field"> Loading @@ -41,7 +47,7 @@ [class.m-btn--readonly]="isEditing" [class.m-btn--readonly]="isEditing" [for]="'type__' + type.id" [for]="'type__' + type.id" [tabindex]="!type.disabled ? 0 : -1" [tabindex]="!type.disabled ? 0 : -1" (keydown.space)="campaign.type = type.id" (keydown.space)="campaign.type = type.id; triggerPreview()" > > <input <input type="radio" type="radio" Loading @@ -49,7 +55,7 @@ [value]="type.id" [value]="type.id" [checked]="type.id === campaign.type" [checked]="type.id === campaign.type" [disabled]="isEditing || type.disabled" [disabled]="isEditing || type.disabled" (change)="campaign.type = type.id" (change)="campaign.type = type.id; triggerPreview()" name="type" name="type" > > Loading Loading @@ -84,7 +90,8 @@ <m-boost-campaigns-creator--content-selector <m-boost-campaigns-creator--content-selector *ngIf="campaign.type === 'newsfeed' || campaign.type === 'content'" *ngIf="campaign.type === 'newsfeed' || campaign.type === 'content'" [type]="campaign.type" [type]="campaign.type" [(content)]="campaign.entity_urns" [content]="campaign.entity_urns" (contentChange)="campaign.entity_urns = $event; triggerPreview()" [readonly]="isEditing" [readonly]="isEditing" ></m-boost-campaigns-creator--content-selector> ></m-boost-campaigns-creator--content-selector> </div> </div> Loading @@ -109,8 +116,8 @@ buttonClass="m-boost-campaigns-creator--hashtag-selector-button" buttonClass="m-boost-campaigns-creator--hashtag-selector-button" [useContent]="true" [useContent]="true" [tags]="campaign.hashtags" [tags]="campaign.hashtags" (tagsAdded)="onTagsAdded($event)" (tagsAdded)="onTagsAdded($event); triggerPreview()" (tagsRemoved)="onTagsRemoved($event)" (tagsRemoved)="onTagsRemoved($event); triggerPreview()" > > <i class="material-icons">check</i> <i class="material-icons">check</i> </m-hashtags-selector> </m-hashtags-selector> Loading @@ -124,7 +131,12 @@ <m-tooltip icon="help" i18n>TBD</m-tooltip> <m-tooltip icon="help" i18n>TBD</m-tooltip> </label> </label> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.start" (dateChange)="onStartDateChange($event)"> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.start" (dateChange)="onStartDateChange($event); triggerPreview()" > <input id="boost-creator__start" type="text" placeholder="Now" i18n [value]="campaign.start | date:'MMM dd'" readonly> <input id="boost-creator__start" type="text" placeholder="Now" i18n [value]="campaign.start | date:'MMM dd'" readonly> <i class="material-icons">keyboard_arrow_down</i> <i class="material-icons">keyboard_arrow_down</i> </div> </div> Loading @@ -136,7 +148,11 @@ <m-tooltip icon="help" i18n>TBD</m-tooltip> <m-tooltip icon="help" i18n>TBD</m-tooltip> </label> </label> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.end" (dateChange)="onEndDateChange($event)"> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.end" (dateChange)="onEndDateChange($event); triggerPreview()" > <input id="boost-creator__end" type="text" placeholder="End of times" i18n [value]="campaign.end | date:'MMM dd'" readonly> <input id="boost-creator__end" type="text" placeholder="End of times" i18n [value]="campaign.end | date:'MMM dd'" readonly> <i class="material-icons">keyboard_arrow_down</i> <i class="material-icons">keyboard_arrow_down</i> </div> </div> Loading @@ -155,7 +171,7 @@ type="number" type="number" name="budget" name="budget" [ngModel]="campaign.budget" [ngModel]="campaign.budget" (ngModelChange)="campaign.budget = $event; calcImpressions()" (ngModelChange)="campaign.budget = $event; triggerPreview()" > > <span>TOKENS</span> <span>TOKENS</span> Loading @@ -169,7 +185,14 @@ </label> </label> <div class="m-form--read-only-value"> <div class="m-form--read-only-value"> <span>{{campaign.impressions | number}}</span> <span> {{campaign.impressions | number}} <m-tooltip icon="warning" *ngIf="preview?.cannot_fulfill_daily" i18n> There might be issues trying to fulfill the impressions per day goal for this campaign. </m-tooltip> </span> </div> </div> </div> </div> Loading src/app/modules/boost/campaigns/creator/creator.component.ts +22 −2 Original line number Original line Diff line number Diff line import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router'; import { Campaign, CampaignDeliveryStatus, CampaignPayment } from "../campaigns.type"; import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignPreview } from "../campaigns.type"; import { CampaignsService } from '../campaigns.service'; import { CampaignsService } from '../campaigns.service'; import { Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs'; import { Tag } from '../../../hashtags/types/tag'; import { Tag } from '../../../hashtags/types/tag'; import { CampaignPaymentsService } from '../campaign-payments.service'; import { CampaignPaymentsService } from '../campaign-payments.service'; import { debounceTime } from 'rxjs/operators'; @Component({ @Component({ providers: [CampaignsService, CampaignPaymentsService], providers: [CampaignsService, CampaignPaymentsService], Loading @@ -24,8 +25,14 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy { currentError: string = ''; currentError: string = ''; preview: CampaignPreview = {}; protected route$: Subscription; protected route$: Subscription; protected previewSubject: Subject<Campaign> = new Subject(); protected preview$: Subscription; constructor( constructor( protected service: CampaignsService, protected service: CampaignsService, protected payments: CampaignPaymentsService, protected payments: CampaignPaymentsService, Loading @@ -48,10 +55,18 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy { this.load(); this.load(); } } }); }); this.preview$ = this.previewSubject .pipe(debounceTime(600)) .subscribe(async (campaign: Campaign) => { this.preview = (await this.service.preview(campaign)) || {}; this.detectChanges(); }); } } ngOnDestroy() { ngOnDestroy() { this.route$.unsubscribe(); this.route$.unsubscribe(); this.preview$.unsubscribe(); } } createFrom({ type, from }) { createFrom({ type, from }) { Loading Loading @@ -156,6 +171,11 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy { return this.service.getTypes(); return this.service.getTypes(); } } triggerPreview() { this.calcImpressions(); this.previewSubject.next(this.campaign); } async submit() { async submit() { if (!this.canSubmit() || this.inProgress) { if (!this.canSubmit() || this.inProgress) { return; return; Loading Loading
src/app/app.component.scss +12 −0 Original line number Original line Diff line number Diff line Loading @@ -344,6 +344,18 @@ m-app { font-size: 16px; font-size: 16px; font-weight: bold; font-weight: bold; padding: 4px; padding: 4px; .m-tooltip i.material-icons { vertical-align: middle; font-size: 1em; margin-left: 8px; position: relative; top: -1px; @include m-theme(){ color: themed($m-red); } } } } } } Loading
src/app/modules/boost/campaigns/campaigns.service.ts +15 −1 Original line number Original line Diff line number Diff line import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core'; import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignType } from './campaigns.type'; import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignPreview, CampaignType } from './campaigns.type'; import { Client } from '../../../services/api/client'; import { Client } from '../../../services/api/client'; import isInt from '../../../helpers/is-int'; import isInt from '../../../helpers/is-int'; import getGuidFromUrn from '../../../helpers/get-guid-from-urn'; import getGuidFromUrn from '../../../helpers/get-guid-from-urn'; Loading Loading @@ -84,6 +84,20 @@ export class CampaignsService { !!campaign.type; !!campaign.type; } } async preview(campaign: Campaign): Promise<CampaignPreview | false> { if ( !campaign.type || !campaign.entity_urns || !campaign.entity_urns.length || !campaign.budget || !campaign.budget_type ) { return false; } return (await this.client.post(`api/v2/boost/campaigns/preview`, campaign) as any).preview; } async prepare(campaign: Campaign): Promise<{ checksum: string, guid: string }> { async prepare(campaign: Campaign): Promise<{ checksum: string, guid: string }> { if (campaign.entity_urns.length !== 1) { if (campaign.entity_urns.length !== 1) { throw new Error('Campaigns without a single entity are unsupported'); throw new Error('Campaigns without a single entity are unsupported'); Loading
src/app/modules/boost/campaigns/campaigns.type.ts +4 −0 Original line number Original line Diff line number Diff line Loading @@ -40,3 +40,7 @@ export type CampaignPayment = { txHash: string, txHash: string, amount: number, amount: number, }; }; export type CampaignPreview = { cannot_fulfill_daily?: boolean, };
src/app/modules/boost/campaigns/creator/creator.component.html +33 −10 Original line number Original line Diff line number Diff line Loading @@ -24,7 +24,13 @@ <m-tooltip icon="help" i18n>TBD</m-tooltip> <m-tooltip icon="help" i18n>TBD</m-tooltip> </label> </label> <input id="boost-creator__name" type="text" name="name" [(ngModel)]="campaign.name"> <input id="boost-creator__name" type="text" name="name" [ngModel]="campaign.name" (ngModelChange)="campaign.name = $event; triggerPreview()" > </div> </div> <div class="m-form--field"> <div class="m-form--field"> Loading @@ -41,7 +47,7 @@ [class.m-btn--readonly]="isEditing" [class.m-btn--readonly]="isEditing" [for]="'type__' + type.id" [for]="'type__' + type.id" [tabindex]="!type.disabled ? 0 : -1" [tabindex]="!type.disabled ? 0 : -1" (keydown.space)="campaign.type = type.id" (keydown.space)="campaign.type = type.id; triggerPreview()" > > <input <input type="radio" type="radio" Loading @@ -49,7 +55,7 @@ [value]="type.id" [value]="type.id" [checked]="type.id === campaign.type" [checked]="type.id === campaign.type" [disabled]="isEditing || type.disabled" [disabled]="isEditing || type.disabled" (change)="campaign.type = type.id" (change)="campaign.type = type.id; triggerPreview()" name="type" name="type" > > Loading Loading @@ -84,7 +90,8 @@ <m-boost-campaigns-creator--content-selector <m-boost-campaigns-creator--content-selector *ngIf="campaign.type === 'newsfeed' || campaign.type === 'content'" *ngIf="campaign.type === 'newsfeed' || campaign.type === 'content'" [type]="campaign.type" [type]="campaign.type" [(content)]="campaign.entity_urns" [content]="campaign.entity_urns" (contentChange)="campaign.entity_urns = $event; triggerPreview()" [readonly]="isEditing" [readonly]="isEditing" ></m-boost-campaigns-creator--content-selector> ></m-boost-campaigns-creator--content-selector> </div> </div> Loading @@ -109,8 +116,8 @@ buttonClass="m-boost-campaigns-creator--hashtag-selector-button" buttonClass="m-boost-campaigns-creator--hashtag-selector-button" [useContent]="true" [useContent]="true" [tags]="campaign.hashtags" [tags]="campaign.hashtags" (tagsAdded)="onTagsAdded($event)" (tagsAdded)="onTagsAdded($event); triggerPreview()" (tagsRemoved)="onTagsRemoved($event)" (tagsRemoved)="onTagsRemoved($event); triggerPreview()" > > <i class="material-icons">check</i> <i class="material-icons">check</i> </m-hashtags-selector> </m-hashtags-selector> Loading @@ -124,7 +131,12 @@ <m-tooltip icon="help" i18n>TBD</m-tooltip> <m-tooltip icon="help" i18n>TBD</m-tooltip> </label> </label> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.start" (dateChange)="onStartDateChange($event)"> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.start" (dateChange)="onStartDateChange($event); triggerPreview()" > <input id="boost-creator__start" type="text" placeholder="Now" i18n [value]="campaign.start | date:'MMM dd'" readonly> <input id="boost-creator__start" type="text" placeholder="Now" i18n [value]="campaign.start | date:'MMM dd'" readonly> <i class="material-icons">keyboard_arrow_down</i> <i class="material-icons">keyboard_arrow_down</i> </div> </div> Loading @@ -136,7 +148,11 @@ <m-tooltip icon="help" i18n>TBD</m-tooltip> <m-tooltip icon="help" i18n>TBD</m-tooltip> </label> </label> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.end" (dateChange)="onEndDateChange($event)"> <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.end" (dateChange)="onEndDateChange($event); triggerPreview()" > <input id="boost-creator__end" type="text" placeholder="End of times" i18n [value]="campaign.end | date:'MMM dd'" readonly> <input id="boost-creator__end" type="text" placeholder="End of times" i18n [value]="campaign.end | date:'MMM dd'" readonly> <i class="material-icons">keyboard_arrow_down</i> <i class="material-icons">keyboard_arrow_down</i> </div> </div> Loading @@ -155,7 +171,7 @@ type="number" type="number" name="budget" name="budget" [ngModel]="campaign.budget" [ngModel]="campaign.budget" (ngModelChange)="campaign.budget = $event; calcImpressions()" (ngModelChange)="campaign.budget = $event; triggerPreview()" > > <span>TOKENS</span> <span>TOKENS</span> Loading @@ -169,7 +185,14 @@ </label> </label> <div class="m-form--read-only-value"> <div class="m-form--read-only-value"> <span>{{campaign.impressions | number}}</span> <span> {{campaign.impressions | number}} <m-tooltip icon="warning" *ngIf="preview?.cannot_fulfill_daily" i18n> There might be issues trying to fulfill the impressions per day goal for this campaign. </m-tooltip> </span> </div> </div> </div> </div> Loading
src/app/modules/boost/campaigns/creator/creator.component.ts +22 −2 Original line number Original line Diff line number Diff line import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router'; import { Campaign, CampaignDeliveryStatus, CampaignPayment } from "../campaigns.type"; import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignPreview } from "../campaigns.type"; import { CampaignsService } from '../campaigns.service'; import { CampaignsService } from '../campaigns.service'; import { Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs'; import { Tag } from '../../../hashtags/types/tag'; import { Tag } from '../../../hashtags/types/tag'; import { CampaignPaymentsService } from '../campaign-payments.service'; import { CampaignPaymentsService } from '../campaign-payments.service'; import { debounceTime } from 'rxjs/operators'; @Component({ @Component({ providers: [CampaignsService, CampaignPaymentsService], providers: [CampaignsService, CampaignPaymentsService], Loading @@ -24,8 +25,14 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy { currentError: string = ''; currentError: string = ''; preview: CampaignPreview = {}; protected route$: Subscription; protected route$: Subscription; protected previewSubject: Subject<Campaign> = new Subject(); protected preview$: Subscription; constructor( constructor( protected service: CampaignsService, protected service: CampaignsService, protected payments: CampaignPaymentsService, protected payments: CampaignPaymentsService, Loading @@ -48,10 +55,18 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy { this.load(); this.load(); } } }); }); this.preview$ = this.previewSubject .pipe(debounceTime(600)) .subscribe(async (campaign: Campaign) => { this.preview = (await this.service.preview(campaign)) || {}; this.detectChanges(); }); } } ngOnDestroy() { ngOnDestroy() { this.route$.unsubscribe(); this.route$.unsubscribe(); this.preview$.unsubscribe(); } } createFrom({ type, from }) { createFrom({ type, from }) { Loading Loading @@ -156,6 +171,11 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy { return this.service.getTypes(); return this.service.getTypes(); } } triggerPreview() { this.calcImpressions(); this.previewSubject.next(this.campaign); } async submit() { async submit() { if (!this.canSubmit() || this.inProgress) { if (!this.canSubmit() || this.inProgress) { return; return; Loading