...
 
Commits (23)
### Summary
(What is the Merge request intending to do, in plain language)
(Be sure to associate any related issues or merge requests)
### Steps to test
(Steps to demonstrate merge achieves goal)
(Include any platform specific directions)
### Estimated Regression Scope
(What features do these changes effect in your estimation?)
......@@ -121,6 +121,7 @@ import { PageLayoutComponent } from './components/page-layout/page-layout.compon
import { DashboardLayoutComponent } from './components/dashboard-layout/dashboard-layout.component';
import { ShadowboxLayoutComponent } from './components/shadowbox-layout/shadowbox-layout.component';
import { ShadowboxHeaderComponent } from './components/shadowbox-header/shadowbox-header.component';
import { DropdownSelectorComponent } from './components/dropdown-selector/dropdown-selector.component';
import { ShadowboxSubmitButtonComponent } from './components/shadowbox-submit-button/shadowbox-submit-button.component';
import { FormDescriptorComponent } from './components/form-descriptor/form-descriptor.component';
import { FormToastComponent } from './components/form-toast/form-toast.component';
......@@ -236,6 +237,7 @@ PlotlyModule.plotlyjs = PlotlyJS;
DashboardLayoutComponent,
ShadowboxLayoutComponent,
ShadowboxHeaderComponent,
DropdownSelectorComponent,
FormDescriptorComponent,
FormToastComponent,
ShadowboxSubmitButtonComponent,
......@@ -334,6 +336,7 @@ PlotlyModule.plotlyjs = PlotlyJS;
PageLayoutComponent,
DashboardLayoutComponent,
ShadowboxLayoutComponent,
DropdownSelectorComponent,
FormDescriptorComponent,
FormToastComponent,
ShadowboxSubmitButtonComponent,
......
......@@ -241,7 +241,7 @@ export class ChartV2Component implements OnInit, OnDestroy {
margin: {
t: this.isMini ? 0 : 16,
b: this.isMini ? 0 : 80,
l: this.isMini ? 0 : 0,
l: 0,
r: this.isMini ? 0 : 80,
pad: 16,
},
......@@ -377,7 +377,7 @@ export class ChartV2Component implements OnInit, OnDestroy {
return rows.map(row => {
if (key === 'date') {
return row[key].slice(0, 10);
} else if (this.segments[0].unit === 'usd') {
} else if (this.rawData.unit && this.rawData.unit === 'usd') {
return row[key] / 100;
} else {
return row[key];
......
......@@ -25,6 +25,7 @@
[dndDraggable]="item"
[dndEffectAllowed]="'move'"
[dndDragImageOffsetFunction]="dragImageOffsetRight"
[dndDisableIf]="disabled"
class="m-draggableList__listItem"
>
<ng-container
......
......@@ -22,6 +22,7 @@ export class DraggableListComponent {
@Input() dndEffectAllowed: EffectAllowed = 'copyMove';
@Input() id: string;
@Input() headers: string[];
@Input() disabled: boolean;
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
@Output() emptyListHeaderRowClicked: EventEmitter<any> = new EventEmitter();
@Output() arrayChanged: EventEmitter<any> = new EventEmitter();
......@@ -77,4 +78,19 @@ export class DraggableListComponent {
y: event.offsetY + 10,
};
};
/**
* If input is focused then disable dragging
*/
onFocusIn(e: FocusEvent | MouseEvent) {
this.disabled = true;
}
/**
* Re-enable when input not focused
* TODO: Make this smarter.. what if something else disabled the dragging?
*/
onFocusOut(e: FocusEvent | MouseEvent) {
this.disabled = false;
}
}
<div class="m-analyticsFilter__labelWrapper" *ngIf="showLabel">
<div class="m-dropdownSelector__labelWrapper" *ngIf="showLabel">
<span>{{ filter.label }}</span>
<m-tooltip icon="help">
<div>{{ filter?.description }}</div>
......@@ -13,7 +13,7 @@
</m-tooltip>
</div>
<div
class="m-analyticsFilter__wrapper"
class="m-dropdownSelector__wrapper"
[ngClass]="{
expanded: expanded,
dropUp: dropUp
......@@ -21,27 +21,23 @@
(blur)="expanded = false"
tabindex="0"
>
<div
class="m-analyticsFilter__header m-analyticsFilter__row"
(click)="expanded = !expanded"
>
<span class="m-analyticsFilter__option m-analyticsFilter__option--selected">
<div class="m-dropdownSelector__header" (click)="expanded = !expanded">
<span class="m-dropdownSelector__option">
{{ selectedOption.label }}
</span>
<i class="material-icons" *ngIf="!expanded">keyboard_arrow_down</i>
<i class="material-icons" *ngIf="expanded">keyboard_arrow_up</i>
</div>
<div class="m-analyticsFilter__optionsContainer">
<div class="m-dropdownSelector__optionsContainer">
<ng-container *ngFor="let option of filter.options">
<div
class="m-analyticsFilter__option m-analyticsFilter__row"
class="m-dropdownSelector__option"
(click)="updateFilter(option)"
[ngClass]="{
unavailable: option.available === false
}"
>
{{ option.label }}
<!-- <span>{{ option.label }}</span> -->
</div>
</ng-container>
</div>
......
$rounded-top: 3px 3px 0 0;
$rounded-bottom: 0 0 3px 3px;
m-analytics__filter {
m-dropdownSelector {
position: relative;
margin: 0 24px 36px 0;
z-index: 2;
display: block;
}
.m-analyticsFilter__labelWrapper {
.m-dropdownSelector__labelWrapper {
position: absolute;
bottom: 115%;
white-space: nowrap;
......@@ -45,168 +45,179 @@ m-analytics__filter {
}
}
.m-analyticsFilter__wrapper {
.m-dropdownSelector__wrapper {
cursor: pointer;
&:focus {
outline: 0;
}
> * {
width: 180px;
box-sizing: border-box;
}
.m-analyticsFilter__optionsContainer {
padding: 8px 0;
.m-analyticsFilter__option {
transform: translateY(25%);
}
}
&.expanded {
@include m-theme() {
box-shadow: 0px 1px 15px 0 rgba(themed($m-black), 0.15);
}
.m-analyticsFilter__header {
.m-dropdownSelector__header {
@include m-theme() {
border-color: themed($m-blue);
}
}
.m-analyticsFilter__optionsContainer {
.m-dropdownSelector__optionsContainer {
display: block;
}
&:not(.dropUp) {
.m-analyticsFilter__header {
.m-dropdownSelector__header {
@include m-theme() {
border-radius: $rounded-top;
}
}
.m-analyticsFilter__optionsContainer {
.m-dropdownSelector__optionsContainer {
border-top: none;
border-radius: $rounded-bottom;
}
}
&.dropUp {
.m-analyticsFilter__header {
.m-dropdownSelector__header {
border-radius: $rounded-bottom;
}
.m-analyticsFilter__optionsContainer {
.m-dropdownSelector__optionsContainer {
bottom: 100%;
border-radius: $rounded-top;
border-bottom: none;
@include m-theme() {
box-shadow: 0px -4px 16px -4px rgba(themed($m-black), 0.15);
}
}
}
}
.m-analyticsFilter__header {
position: relative;
border-radius: 3px;
transition: all 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
> * {
width: 180px;
box-sizing: border-box;
}
}
.m-dropdownSelector__header {
position: relative;
border-radius: 3px;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
@include m-theme() {
background-color: themed($m-white);
color: themed($m-grey-300);
}
@include m-theme() {
border: 1px solid themed($m-grey-100);
}
.m-dropdownSelector__label {
margin-right: 10px;
}
i {
flex-grow: 0;
width: 24px;
height: 24px;
padding-top: 2px;
}
.m-dropdownSelector__option {
@include m-theme() {
background-color: themed($m-white);
color: themed($m-grey-300);
}
@include m-theme() {
border: 1px solid themed($m-grey-100);
color: themed($m-grey-500);
}
.m-analyticsFilter__label {
margin-right: 10px;
}
i {
flex-grow: 0;
width: 24px;
height: 24px;
}
.m-analyticsFilter__option--selected {
}
}
.m-dropdownSelector__optionsContainer {
box-sizing: border-box;
position: absolute;
display: none;
border-radius: 3px;
left: 0px;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
@include m-theme() {
border: 1px solid themed($m-blue);
border-top: 1px solid themed($m-blue);
background-color: themed($m-white);
box-shadow: 0px 8px 16px 0px rgba(themed($m-black), 0.15);
}
.m-dropdownSelector__option {
&:hover:not(.unavailable) {
@include m-theme() {
color: themed($m-grey-500);
background-color: rgba(themed($m-grey-100), 0.2);
}
}
}
.m-analyticsFilter__optionsContainer {
position: absolute;
display: none;
border-radius: 3px;
left: 0px;
transition: box-shadow 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
@include m-theme() {
border: 1px solid themed($m-blue);
border-top: 1px solid themed($m-blue);
background-color: themed($m-white);
&:first-child {
padding-top: 14px;
}
&:last-child {
padding-bottom: 14px;
}
}
}
.m-analyticsFilter__row {
display: flex;
justify-content: space-between;
align-items: center;
height: 46px;
padding: 0 20px;
&.m-analyticsFilter__header {
padding-right: 10px;
}
.m-dropdownSelector__header {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 10px;
}
.m-dropdownSelector__option {
display: inline-block;
padding: 10px 20px;
box-sizing: border-box;
width: inherit;
border-radius: 3px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@include m-theme() {
color: themed($m-grey-300);
}
.m-analyticsFilter__option {
display: inline-block;
box-sizing: border-box;
width: inherit;
border-radius: 3px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&.unavailable {
display: none;
text-decoration: line-through;
@include m-theme() {
color: themed($m-grey-300);
}
&.unavailable {
text-decoration: line-through;
@include m-theme() {
color: themed($m-grey-50);
}
}
&:hover:not(.unavailable) {
@include m-theme() {
color: themed($m-grey-600);
}
color: themed($m-grey-50);
}
}
}
@media screen and (max-width: $min-tablet) {
m-analytics__filter {
.m-analyticsFilter__labelWrapper {
m-dropdownSelector {
.m-dropdownSelector__labelWrapper {
.m-tooltip--bubble {
width: 120px;
}
}
.m-analyticsFilter__wrapper {
> * {
width: 140px;
}
}
}
}
@media screen and (max-width: $max-mobile) {
m-analytics__filter {
.m-analyticsFilter__wrapper {
.m-analyticsFilter__header {
m-dropdownSelector {
.m-dropdownSelector__wrapper {
> * {
width: 160px;
}
.m-dropdownSelector__header {
padding-right: 10px;
i {
display: none;
}
}
.m-analyticsFilter__row {
padding: 0 18px;
height: 40px;
&.m-analyticsFilter__header {
padding-right: 10px;
.m-dropdownSelector__optionsContainer {
.m-dropdownSelector__option {
&:first-child {
padding-top: 11px;
}
&:last-child {
padding-bottom: 11px;
}
}
}
.m-analyticsFilter__option--selected {
.m-dropdownSelector__option {
margin-right: 0;
padding: 8px 18px;
}
}
}
......
......@@ -2,32 +2,30 @@ import {
Component,
OnInit,
Input,
Output,
ChangeDetectionStrategy,
EventEmitter,
} from '@angular/core';
import {
AnalyticsDashboardService,
Filter,
Option,
} from '../../dashboard.service';
import { Session } from '../../../../../services/session';
import { Session } from '../../../services/session';
import { Filter, Option } from '../../../interfaces/dashboard';
@Component({
selector: 'm-analytics__filter',
templateUrl: 'filter.component.html',
selector: 'm-dropdownSelector',
templateUrl: './dropdown-selector.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AnalyticsFilterComponent implements OnInit {
export class DropdownSelectorComponent implements OnInit {
@Input() filter: Filter;
@Input() dropUp: boolean = false;
@Input() showLabel: boolean = true;
@Output() selectionMade: EventEmitter<any> = new EventEmitter();
expanded = false;
options: Array<any> = [];
selectedOption: Option;
constructor(
private analyticsService: AnalyticsDashboardService,
public session: Session
) {}
constructor(public session: Session) {}
ngOnInit() {
this.selectedOption =
......@@ -42,25 +40,9 @@ export class AnalyticsFilterComponent implements OnInit {
}
this.selectedOption = option;
if (this.filter.id === 'timespan') {
this.analyticsService.updateTimespan(option.id);
console.log('upDateFilter ', option.id);
return;
}
const selectedFilterStr = `${this.filter.id}::${option.id}`;
this.analyticsService.updateFilter(selectedFilterStr);
this.selectionMade.emit({
option: this.selectedOption,
filterId: this.filter.id,
});
}
// clickHeader() {
// if (this.expanded) {
// console.log('its expanded');
// setTimeout(() => {
// this.expanded = false;
// });
// } else {
// console.log('itsnot expanded');
// }
// document.getElementById("myAnchor").blur();
// }
}
......@@ -47,4 +47,8 @@ m-marketing {
margin: 0 auto;
box-sizing: border-box;
}
.m-marketing__subText {
font-size: 11px;
}
}
......@@ -54,7 +54,6 @@ import { AnalyticsLayoutChartComponent } from './v2/layouts/layout-chart/layout-
import { AnalyticsLayoutSummaryComponent } from './v2/layouts/layout-summary/layout-summary.component';
import { AnalyticsMetricsComponent } from './v2/components/metrics/metrics.component';
import { AnalyticsFiltersComponent } from './v2/components/filters/filters.component';
import { AnalyticsFilterComponent } from './v2/components/filter/filter.component';
import { AnalyticsChartComponent } from './v2/components/chart/chart.component';
import { AnalyticsTableComponent } from './v2/components/table/table.component';
import { AnalyticsDashboardService } from './v2/dashboard.service';
......@@ -156,7 +155,6 @@ const routes: Routes = [
AnalyticsLayoutSummaryComponent,
AnalyticsMetricsComponent,
AnalyticsFiltersComponent,
AnalyticsFilterComponent,
AnalyticsChartComponent,
AnalyticsTableComponent,
AnalyticsSearchComponent,
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AnalyticsFilterComponent } from './filter.component';
describe('AnalyticsFilterComponent', () => {
let component: AnalyticsFilterComponent;
let fixture: ComponentFixture<AnalyticsFilterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AnalyticsFilterComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsFilterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
xit('should create', () => {
expect(component).toBeTruthy();
});
});
<div class="m-analytics__filtersContainer">
<!-- <ng-container *ngFor="let filter of filters$ | async"> -->
<ng-container *ngFor="let filter of filters">
<m-analytics__filter
class="filter"
<m-dropdownSelector
[filter]="filter"
[dropUp]="true"
></m-analytics__filter>
(selectionMade)="selectionMade($event)"
></m-dropdownSelector>
</ng-container>
</div>
......@@ -43,7 +43,12 @@ export class AnalyticsFiltersComponent implements OnInit, OnDestroy {
});
}
// TODO: remove all of this once channel search is ready
selectionMade($event) {
this.analyticsService.updateFilter(
`${$event.filterId}::${$event.option.id}`
);
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......
......@@ -16,13 +16,14 @@
<m-analytics__search></m-analytics__search>
</div> -->
<!-- <div class="m-analyticsDashboard__channelFilter" *ngIf="session.isAdmin()">
<m-analytics__filter [filter]="channelFilter"></m-analytics__filter>
<m-dropdownSelector [filter]="channelFilter"></m-dropdownSelector>
</div> -->
<div class="m-analyticsDashboard__timespanFilter">
<m-analytics__filter
<m-dropdownSelector
[filter]="timespanFilter"
[showLabel]="false"
></m-analytics__filter>
(selectionMade)="filterSelectionMade($event)"
></m-dropdownSelector>
</div>
</div>
</ng-container>
......
......@@ -30,22 +30,14 @@ m-analytics__dashboard {
}
}
}
m-analytics__filter {
m-dropdownSelector {
margin: 0;
}
.m-analyticsFilter__wrapper {
.m-dropdownSelector__wrapper {
> * {
width: 180px;
}
}
.m-analyticsDashboard__timespanFilter {
.m-analytics__filterWrapper {
margin-top: 0px;
}
.m-analytics__filterLabel {
display: none;
}
}
}
.m-analyticsDashboard__description {
margin: 8px 16px 32px 0;
......@@ -57,11 +49,11 @@ m-analytics__dashboard {
@media screen and (max-width: $min-tablet) {
.m-dashboardLayout__header {
m-analytics__filter {
m-dropdownSelector {
margin: 0 16px 8px 0;
.m-analyticsFilter__wrapper {
.m-dropdownSelector__wrapper {
> * {
width: 160px;
width: 180px;
}
}
}
......@@ -73,9 +65,11 @@ m-analytics__dashboard {
@media screen and (max-width: $max-mobile) {
.m-dashboardLayout__header {
.m-analyticsFilter__wrapper {
> * {
width: 140px;
m-dropdownSelector {
.m-dropdownSelector__wrapper {
> * {
width: 160px;
}
}
}
}
......
......@@ -104,6 +104,12 @@ export class AnalyticsDashboardComponent implements OnInit, OnDestroy {
}
}
filterSelectionMade($event) {
if ($event.filterId === 'timespan') {
this.analyticsService.updateTimespan($event.option.id);
}
}
updateTimespan(timespanId) {
// TODO: update url
// this.analyticsService.updateTimespan(timespanId);
......
......@@ -148,6 +148,10 @@ export class ChannelSortedComponent implements OnInit {
return;
}
if (activity.time_created > Date.now() / 1000) {
this.scheduledCount += 1;
}
this.entities.unshift(activity);
let feedItem = {
......
......@@ -117,7 +117,9 @@
>
<div
class="m-media-content--play-count"
*ngIf="entity['play:count'] || entity['play:count'] === 0"
*ngIf="
(false && entity['play:count']) || entity['play:count'] === 0
"
>
<span i18n="@@MINDS__MEDIA__PLAYS_COUNTER"
>{{ entity['play:count'] | number }} plays</span
......
<form class="m-form">
<p>
You can receive Bitcoin (BTC) payments via wire by inputing a receiver
You can receive Bitcoin (BTC) payments via wire by inputting a receiver
address below. Note: You may want to rotate this address frequently to avoid
3rd parties tracking your transactions.
</p>
......
......@@ -157,7 +157,7 @@ export class ProChannelService implements OnDestroy {
throw new Error('No channel');
}
const endpoint = `api/v2/pro/content/${this.currentChannel.guid}/all/top`;
const endpoint = `api/v2/pro/content/${this.currentChannel.guid}/all`;
const qs = {
limit: params.limit || 24,
from_timestamp: params.offset || '',
......
......@@ -34,8 +34,6 @@ export class ProChannelListComponent implements OnInit, OnDestroy {
entities: any[] = [];
algorithm: string;
query: string;
period: string;
......@@ -97,7 +95,7 @@ export class ProChannelListComponent implements OnInit, OnDestroy {
default:
throw new Error('Unknown type');
}
this.algorithm = params['algorithm'] || 'top';
this.query = params['query'] || '';
this.period = params['period'] || '';
this.selectedHashtag = params['hashtag'] || 'all';
......@@ -149,7 +147,7 @@ export class ProChannelListComponent implements OnInit, OnDestroy {
params.sync = 1;
}
let url = `api/v2/pro/content/${this.channelService.currentChannel.guid}/${this.type}/${this.algorithm}`;
let url = `api/v2/pro/content/${this.channelService.currentChannel.guid}/${this.type}`;
try {
this.feedsService
......
......@@ -58,8 +58,8 @@
<ul class="m-marketing__points">
<li i18n>
$1 for every 1,000 pageviews ($5 per 1,000 pageviews when monthly
total is between 10,000 and 1,000,000 pageviews)
$1 for every 1,000 pageviews on your content (increases to $5
between 100K and 1M pageviews/month)
</li>
<li i18n>
......@@ -71,6 +71,12 @@
<a routerLink="/tokens">Minds Tokens</a>
</li>
</ul>
<a
class="m-marketing__subText"
routerLink="/minds/blog/how-to-earn-money-with-pro-1046186757943361536"
>Learn More</a
>
</div>
<div
class="m-grid__column-6 m-grid__column-12--mobile m-marketing__image"
......
......@@ -336,6 +336,7 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
for (const tag of tags) {
this.addTag(tag.label, tag.tag);
}
this.form.markAsDirty();
this.detectChanges();
}
......@@ -360,6 +361,7 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
for (let link of links) {
this.addFooterLink(link.title, link.href);
}
this.form.markAsDirty();
this.detectChanges();
}
......
......@@ -56,6 +56,14 @@
Coming soon
</button>
</ng-template>
<p class="m-marketing__subText">
By purchasing PRO you agree to
<a
href="https://cdn-assets.minds.com/pro-terms-27-11-2019.pdf"
target="_blank"
>Terms &amp; Conditions</a
>.
</p>
</div>
</div>
......
......@@ -636,6 +636,7 @@ describe('WireCreatorComponent', () => {
payload: { receiver: '0x1234', address: '' },
payloadType: 'onchain',
recurring: false,
recurringInterval: 'monthly',
});
}));
......
......@@ -36,6 +36,7 @@ export interface WireStruc {
payloadType: PayloadType | null;
guid: any;
recurring: boolean;
recurringInterval?: 'once' | 'monthly' | 'yearly' | null;
payload: any;
}
......@@ -53,7 +54,7 @@ export class WireCreatorComponent {
payloadType: 'onchain',
guid: null,
recurring: true,
recurringInterval: 'monthly',
// Payment
payload: null,
};
......
......@@ -11,6 +11,7 @@ export class WirePaymentsCreatorComponent extends WireCreatorComponent {
@Input('opts') set opts(opts) {
this._opts = opts;
this.wire.amount = opts.amount;
this.wire.recurringInterval = opts.interval;
switch (opts.currency) {
case 'tokens':
this.wire.payloadType = 'offchain';
......
......@@ -39,6 +39,7 @@ describe('WireService', () => {
payload: { receiver: '0x1234', address: '' },
payloadType: 'onchain',
recurring: false,
recurringInterval: 'once',
});
tick();
......@@ -59,6 +60,7 @@ describe('WireService', () => {
},
method: 'onchain',
recurring: false,
recurring_interval: 'once',
});
}));
......@@ -69,6 +71,7 @@ describe('WireService', () => {
payload: null,
payloadType: 'offchain',
recurring: false,
recurringInterval: 'once',
});
tick();
......@@ -80,6 +83,7 @@ describe('WireService', () => {
payload: { address: 'offchain', method: 'offchain' },
method: 'offchain',
recurring: false,
recurring_interval: 'once',
});
}));
......@@ -90,6 +94,7 @@ describe('WireService', () => {
payload: { address: 'offchain', token: 'tok_KPte7942xySKBKyrBu11yEpf' },
payloadType: 'usd',
recurring: false,
recurringInterval: 'once',
});
tick();
......@@ -105,6 +110,7 @@ describe('WireService', () => {
},
method: 'usd',
recurring: false,
recurring_interval: 'once',
});
}));
});
......@@ -110,6 +110,7 @@ export class WireService {
method: payload.method,
amount: wire.amount,
recurring: wire.recurring,
recurring_interval: wire.recurringInterval,
});
this.wireSent.next(wire);
......