Commit 4238b53f authored by Emiliano Balbuena's avatar Emiliano Balbuena

(wip): Pro settings; style tweaks

1 merge request!459WIP: (feat): Minds Pro
Pipeline #73234856 failed with stages
in 38 minutes and 3 seconds
......@@ -43,6 +43,15 @@ minds-button {
}
}
a.m-link-btn {
display: inline-block;
padding: 8px !important;
line-height: 1.2;
height: auto;
text-decoration: none;
font-weight: inherit;
}
.m-btn--slim {
height: 32px;
}
......
......@@ -69,8 +69,7 @@ export interface MindsUser {
tags?: Array<string>;
toaster_notifications?: boolean;
pro?: boolean;
pro_settings?: { [key: string]: string };
pro_styles?: { [key: string]: string };
pro_settings?: { styles?: { [key: string]: string }, [key: string]: string | { [key: string]: string } };
}
export interface MindsGroup {
......
......@@ -135,6 +135,18 @@
<span [hidden]="!editing" i18n="@@M__ACTION__SAVE">Save</span>
</button>
</div>
<ng-container *ngIf="session.getLoggedInUser()?.guid == user.guid">
<a *ngIf="!user.pro" class="m-btn m-link-btn m-btn--with-icon m-btn--slim m-btn--action" routerLink="/pro">
<i class="material-icons">business_center</i>
<span i18n>Become Pro</span>
</a>
<a *ngIf="user.pro" class="m-btn m-link-btn m-btn--with-icon m-btn--slim" routerLink="/pro/settings">
<i class="material-icons">business_center</i>
<span i18n>Pro Settings</span>
</a>
</ng-container>
<minds-button-boost *ngIf="session.getLoggedInUser().guid == user.guid" [object]="user"></minds-button-boost>
<m-channel--badges [user]="user"></m-channel--badges>
</div>
......
......@@ -13,17 +13,18 @@ import { Subscription } from "rxjs";
import { MindsUser } from "../../../interfaces/entities";
import { Client } from "../../../services/api/client";
import { Title } from "@angular/platform-browser";
import { ProService } from "../pro.service";
import { ProChannelService } from './channel.service';
@Component({
providers: [
ProService,
ProChannelService,
],
selector: 'm-pro--channel',
templateUrl: 'channel.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProChannelComponent implements OnInit, OnDestroy {
username: string;
channel: MindsUser;
......@@ -37,7 +38,7 @@ export class ProChannelComponent implements OnInit, OnDestroy {
constructor(
protected element: ElementRef,
protected session: Session,
protected proService: ProService,
protected channelService: ProChannelService,
protected client: Client,
protected title: Title,
protected router: Router,
......@@ -71,11 +72,11 @@ export class ProChannelComponent implements OnInit, OnDestroy {
this.detectChanges();
try {
this.channel = await this.proService.load(this.username);
this.channel = await this.channelService.load(this.username);
let title = this.channel.pro_settings.title || this.channel.name;
let title = this.channel.pro_settings.title as string || this.channel.name;
this.setCustomStyles();
this.bindCssVariables();
if (this.channel.pro_settings.headline) {
title += ` - ${this.channel.pro_settings.headline}`;
......@@ -89,14 +90,23 @@ export class ProChannelComponent implements OnInit, OnDestroy {
this.detectChanges();
}
setCustomStyles() {
const styles = this.channel.pro_styles;
bindCssVariables() {
const styles = this.channel.pro_settings.styles;
for (let style in styles) {
if (styles.hasOwnProperty(style) && styles[style].trim() !== '') {
const styleAttr = style.replace(/_/g, "-");
document.body.style.setProperty(`--${styleAttr}`, styles[style]);
for (const style in styles) {
if (!styles.hasOwnProperty(style)) {
continue;
}
let value = styles[style].trim();
if (!value) {
continue;
}
const styleAttr = style.replace(/_/g, "-");
this.element.nativeElement
.style.setProperty(`--${styleAttr}`, styles[style]);
}
}
......
import { Injectable } from '@angular/core';
import { MindsChannelResponse } from '../../../interfaces/responses';
import { MindsUser } from '../../../interfaces/entities';
import { Client } from '../../../services/api/client';
@Injectable()
export class ProChannelService {
currentChannel: MindsUser;
constructor(
protected client: Client,
) { }
async load(id: string) {
try {
this.currentChannel = void 0;
const response: MindsChannelResponse = await this.client.get(`api/v1/channel/${id}`) as MindsChannelResponse;
this.currentChannel = response.channel;
return this.currentChannel;
} catch (e) {
if (e.status === 0) {
throw new Error('Sorry, there was a timeout error.');
} else {
console.log('couldn\'t load channel', e);
throw new Error('Sorry, the channel couldn\'t be found');
}
}
}
}
......@@ -22,7 +22,7 @@
</m-newsfeed__entity>
</ng-container>
</li>
<li class="m-proChannelListContentList__seeMore" [routerLink]="['/', proService.currentChannel.username]">
<li class="m-proChannelListContentList__seeMore" [routerLink]="seeMoreRoute" i18n>
See more
</li>
</ul>
......
......@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@
import { ActivatedRoute } from "@angular/router";
import { Subscription } from "rxjs";
import { FeedsService } from "../../../../common/services/feeds.service";
import { ProService } from "../../pro.service";
import { ProChannelService } from '../channel.service';
@Component({
selector: 'm-pro--channel-list',
......@@ -17,7 +17,7 @@ export class ProChannelListComponent implements OnInit {
constructor(
public feedsService: FeedsService,
public proService: ProService,
protected channelService: ProChannelService,
protected route: ActivatedRoute,
protected cd: ChangeDetectorRef,
) {
......@@ -62,8 +62,8 @@ export class ProChannelListComponent implements OnInit {
try {
this.feedsService
.setEndpoint(`api/v2/feeds/channel/${this.proService.currentChannel.guid}/${this.type}`)
.setLimit(8)
.setEndpoint(`api/v2/feeds/channel/${this.channelService.currentChannel.guid}/${this.type}`)
.setLimit(9)
.fetch();
} catch (e) {
......@@ -81,4 +81,8 @@ export class ProChannelListComponent implements OnInit {
this.cd.markForCheck();
this.cd.detectChanges();
}
get seeMoreRoute() {
return ['/', this.channelService.currentChannel.username];
}
}
@import "defaults";
m-pro--channel-tile {
cursor: pointer;
position: relative;
width: 100%;
height: 320px;
img {
max-height: 320px;
height: 320px;
width: 100%;
object-fit: cover;
}
......@@ -16,9 +16,10 @@ m-pro--channel-tile {
bottom: 0;
background-color: white;
color: black;
min-width: stretch;
padding: $minds-padding*2 $minds-padding*2 0;
padding: 16px 16px 0;
font-size: 20px;
width: 100%;
box-sizing: border-box;
h2 {
font-size: 20px;
......
......@@ -16,7 +16,9 @@
</div>
<div class="m-marketing--hero--actions m-marketing--action-button">
<m-pro--subscription></m-pro--subscription>
<m-pro--subscription
(onEnable)="goToSettings()"
></m-pro--subscription>
</div>
</div>
</div>
......
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'm-pro--marketing',
......@@ -7,4 +8,13 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
})
export class ProMarketingComponent {
minds = window.Minds;
constructor(
protected router: Router,
) {
}
goToSettings() {
this.router.navigate(['/pro/settings']);
}
}
......@@ -13,19 +13,41 @@ import { ProChannelListComponent } from "./channel/list/list.component";
import { ProChannelDonateComponent } from './channel/donate/donate.component';
import { ProTileComponent } from "./channel/tile/tile.component";
import { NewsfeedModule } from "../newsfeed/newsfeed.module";
import { ProSettingsComponent } from './settings/settings.component';
const routes: Routes = [
{
path: 'pro',
children: [
{ path: '', component: ProMarketingComponent, },
{
path: ':username', component: ProChannelComponent,
path: '',
component: ProMarketingComponent,
},
{
path: 'settings',
component: ProSettingsComponent,
},
{
path: ':username',
component: ProChannelComponent,
children: [
{ path: '', redirectTo: 'articles', pathMatch: 'full' },
{ path: 'donate', component: ProChannelDonateComponent },
{ path: 'signup', component: ProChannelSignupComponent },
{ path: ':type', component: ProChannelListComponent },
{
path: '',
redirectTo: 'articles',
pathMatch: 'full'
},
{
path: 'donate',
component: ProChannelDonateComponent
},
{
path: 'signup',
component: ProChannelSignupComponent
},
{
path: ':type',
component: ProChannelListComponent
},
]
},
]
......@@ -47,6 +69,7 @@ const routes: Routes = [
],
declarations: [
ProMarketingComponent,
ProSettingsComponent,
ProSubscriptionComponent,
ProTileComponent,
ProChannelComponent,
......
import { Injectable } from '@angular/core';
import { Client } from '../../services/api/client';
import { MindsUser } from "../../interfaces/entities";
import { MindsChannelResponse } from "../../interfaces/responses";
@Injectable()
export class ProService {
currentChannel: MindsUser;
constructor(
protected client: Client,
) { }
......@@ -32,22 +28,12 @@ export class ProService {
return true;
}
async load(id: string) {
try {
this.currentChannel = void 0;
const response: MindsChannelResponse = await this.client.get(`api/v1/channel/${id}`) as MindsChannelResponse;
this.currentChannel = response.channel;
async get(): Promise<{ isActive, settings }> {
return await this.client.get('api/v2/pro/settings', {}, {cache: false}) as any;
}
return this.currentChannel;
} catch (e) {
if (e.status === 0) {
throw new Error('Sorry, there was a timeout error.');
} else {
console.log('couldn\'t load channel', e);
throw new Error('Sorry, the channel couldn\'t be found');
}
}
async set(settings): Promise<boolean> {
await this.client.post('api/v2/pro/settings', settings);
return true;
}
}
<div class="m-pro--settings">
<div class="m-page">
<div class="m-page--main m-pro--settings--page m-border">
<h4 i18n>Pro Settings</h4>
<div *ngIf="inProgress && !settings">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</div>
<ng-container *ngIf="settings">
<form (ngSubmit)="save()">
<div class="m-pro--settings--field">
<label for="domain" i18n>Domain</label>
<input type="text" id="domain" name="domain" [(ngModel)]="settings.domain">
</div>
<div class="m-pro--settings--field">
<label for="title" i18n>Title</label>
<input type="text" id="title" name="title" [(ngModel)]="settings.title">
</div>
<div class="m-pro--settings--field">
<label for="headline" i18n>Headline</label>
<input type="text" id="headline" name="headline" [(ngModel)]="settings.headline">
</div>
<div class="m-pro--settings--field">
<label for="text_color" i18n>Text Color</label>
<input type="color" id="text_color" name="text_color" [(ngModel)]="settings.text_color">
</div>
<div class="m-pro--settings--field">
<label for="primary_color" i18n>Primary Color</label>
<input type="color" id="primary_color" name="primary_color" [(ngModel)]="settings.primary_color">
</div>
<div class="m-pro--settings--field">
<label for="plain_background_color" i18n>Plain Background Color</label>
<input type="color" id="plain_background_color" name="plain_background_color" [(ngModel)]="settings.plain_background_color">
</div>
<div class="m-pro--settings--field">
<button
class="m-btn m-btn--slim m-btn--action"
type="submit"
[disabled]="inProgress"
i18n
>Save</button>
<span *ngIf="inProgress">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</span>
<a
*ngIf="saved"
class="m-btn m-link-btn m-btn--slim m-pro--settings--preview-btn"
[routerLink]="previewRoute"
i18n
>Preview your Pro site</a>
</div>
<div class="m-pro--settings--field">
<a
class="m-btn m-link-btn m-btn--slim m-btn--destructive"
routerLink="/pro"
>Cancel Pro</a>
</div>
</form>
</ng-container>
</div>
</div>
</div>
.m-pro--settings {
max-width: 1280px;
margin: auto;
}
.m-pro--settings--page {
margin-top: 16px;
padding-bottom: 24px;
@include m-theme() {
background-color: themed($m-white);
}
h4 {
margin: 0 0 8px;
}
form {
margin: 0;
padding: 0;
}
.m-pro--settings--field {
margin: 0 0 24px;
&:last-child {
margin: 0;
}
label {
display: block;
font-weight: 300;
margin-bottom: 4px;
@include m-theme() {
color: themed($m-grey-800);
}
}
input[type=text],
input[type=password],
input[type=search],
input[type=date] {
appearance: none;
border: 1px solid;
border-radius: 6px;
width: 100%;
padding: 8px;
font-size: 16px;
max-width: 40em;
@include m-theme() {
border-color: themed($m-grey-50);
}
}
}
.m-pro--settings--preview-btn {
margin-left: 0.35em;
@include m-theme() {
color: themed($m-grey-300);
border-color: themed($m-grey-300);
}
}
}
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ProService } from '../pro.service';
import { Session } from '../../../services/session';
import { Router } from '@angular/router';
@Component({
selector: 'm-pro--settings',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: 'settings.component.html'
})
export class ProSettingsComponent implements OnInit {
settings: any;
inProgress: boolean;
saved: boolean = false;
constructor(
protected service: ProService,
protected session: Session,
protected router: Router,
protected cd: ChangeDetectorRef,
) {
}
ngOnInit() {
this.load();
}
async load() {
this.inProgress = true;
this.detectChanges();
const { isActive, settings } = await this.service.get();
if (!isActive) {
this.router.navigate(['/pro'], { replaceUrl: true });
return;
}
this.settings = settings;
this.inProgress = false;
this.detectChanges();
}
async save() {
this.inProgress = true;
this.detectChanges();
await this.service.set(this.settings);
this.saved = true;
this.inProgress = false;
this.detectChanges();
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
get previewRoute() {
return ['/pro', this.session.getLoggedInUser().username];
}
}
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Session } from '../../services/session';
import { ProService } from './pro.service';
......@@ -9,6 +9,10 @@ import { ProService } from './pro.service';
})
export class ProSubscriptionComponent implements OnInit {
@Output() onEnable: EventEmitter<any> = new EventEmitter();
@Output() onDisable: EventEmitter<any> = new EventEmitter();
isLoggedIn: boolean = false;
inProgress: boolean = false;
......@@ -61,6 +65,7 @@ export class ProSubscriptionComponent implements OnInit {
await this.service.enable();
this.active = true;
this.minds.user.pro = true;
this.onEnable.emit(Date.now());
} catch (e) {
this.active = false;
this.minds.user.pro = false;
......@@ -80,6 +85,7 @@ export class ProSubscriptionComponent implements OnInit {
await this.service.disable();
this.active = false;
this.minds.user.pro = false;
this.onDisable.emit(Date.now());
} catch (e) {
this.active = true;
this.minds.user.pro = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment