Commit 1dc55a2e authored by Emiliano Balbuena's avatar Emiliano Balbuena

(feat): Subscribe on hamburger menu; (chore): Subs to own component

1 merge request!528WIP: (feat): Minds Pro
Pipeline #80806978 failed with stages
in 4 minutes and 21 seconds
import { Component, ContentChild, Input, TemplateRef, } from '@angular/core';
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
import { DndDropEvent, EffectAllowed } from 'ngx-drag-drop';
@Component({
......@@ -43,8 +43,13 @@ export class DraggableListComponent {
}
onDrop(event: DndDropEvent) {
if (this.data && (event.dropEffect === 'copy' || event.dropEffect === 'move')) {
let dragIndex = this.data.findIndex(item => event.data[this.id] === item[this.id]);
if (
this.data &&
(event.dropEffect === 'copy' || event.dropEffect === 'move')
) {
let dragIndex = this.data.findIndex(
item => event.data[this.id] === item[this.id]
);
let dropIndex = event.index || this.data.length;
// remove element
this.data.splice(dragIndex, 1);
......@@ -55,7 +60,6 @@ export class DraggableListComponent {
}
this.data.splice(dropIndex, 0, event.data);
}
}
}
......@@ -75,27 +75,7 @@
>
<a class="m-proChannelTopbar__navItem" (click)="wire()" i18n>Wire</a>
<button
class="m-proChannelTopbar__subscribe"
[class.m-proChannelTopbar__subscribe--subscribed]="channel.subscribed"
(click)="toggleSubscription($event)"
>
<span class="m-proChannelTopbar__subscribe--label">
<ng-container
*ngIf="!channel.subscribed; else subscribedActionButton"
i18n
>
Subscribe
</ng-container>
<ng-template #subscribedActionButton>
<ng-container i18n>Subscribed</ng-container>
</ng-template>
</span>
<span class="m-proChannelTopbar__subscribe--counter">
{{ subscribersCount | abbr: 0 }}
</span>
</button>
<m-pro__subscribeButton></m-pro__subscribeButton>
</ng-container>
<ng-template #ownerNavLinks>
<a
......
......@@ -156,35 +156,6 @@ m-pro--channel {
}
}
.m-proChannelTopbar__subscribe {
appearance: none;
font-size: 12px;
letter-spacing: 1.25px;
text-transform: uppercase;
padding: 12px 16px;
font-family: 'Roboto', Helvetica, sans-serif;
cursor: pointer;
border-radius: 4px;
margin-left: 16px;
background: none transparent;
color: var(--m-pro--primary-color);
border: 1px solid var(--m-pro--primary-color);
font-weight: 600;
&.m-proChannelTopbar__subscribe--subscribed {
border: none;
color: var(--m-pro--text-color);
}
.m-proChannelTopbar__subscribe--label {
opacity: 0.85;
}
.m-proChannelTopbar__subscribe--counter {
margin-left: 0.65em;
}
}
.m-btn:disabled {
padding-left: 16px;
color: gray !important;
......
......@@ -18,9 +18,8 @@ import { MindsUser } from '../../../interfaces/entities';
import { Client } from '../../../services/api/client';
import { MindsTitle } from '../../../services/ux/title';
import { ProChannelService } from './channel.service';
import { SignupModalService } from '../../../modules/modals/signup/service';
import { SignupModalService } from '../../modals/signup/service';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { ProUnsubscribeModalComponent } from './unsubscribe-modal/modal.component';
import { OverlayModalComponent } from '../../../common/components/overlay-modal/overlay-modal.component';
import { SessionsStorageService } from '../../../services/session-storage.service';
import { SiteService } from '../../../services/site.service';
......@@ -46,17 +45,13 @@ export class ProChannelComponent implements OnInit, AfterViewInit, OnDestroy {
collapseNavItems: boolean;
params$: Subscription;
protected params$: Subscription;
channel$: Subscription;
subscribersCount: number;
protected loggedIn$: Subscription;
@ViewChild('overlayModal', { static: true })
protected overlayModal: OverlayModalComponent;
protected isLoggedIn$: Subscription;
constructor(
protected element: ElementRef,
protected session: Session,
......@@ -80,14 +75,6 @@ export class ProChannelComponent implements OnInit, AfterViewInit, OnDestroy {
this.listen();
this.onResize();
this.isLoggedIn$ = this.session.loggedinEmitter.subscribe(is => {
if (!is && this.channel) {
this.channel.subscribed = false;
}
this.detectChanges();
});
}
ngAfterViewInit() {
......@@ -119,12 +106,11 @@ export class ProChannelComponent implements OnInit, AfterViewInit, OnDestroy {
}
});
this.channel$ = this.channelService.subscriptionChange.subscribe(
subscribersCount => {
this.subscribersCount = subscribersCount;
this.load();
this.loggedIn$ = this.session.loggedinEmitter.subscribe(is => {
if (is) {
this.reload();
}
);
});
}
setTitle() {
......@@ -168,14 +154,7 @@ export class ProChannelComponent implements OnInit, AfterViewInit, OnDestroy {
}
ngOnDestroy() {
if (this.params$) {
this.params$.unsubscribe();
}
if (this.channel$) {
this.channel$.unsubscribe();
}
this.isLoggedIn$.unsubscribe();
this.params$.unsubscribe();
}
async load() {
......@@ -189,7 +168,6 @@ export class ProChannelComponent implements OnInit, AfterViewInit, OnDestroy {
try {
await this.channelService.auth();
this.channel = await this.channelService.load(this.username);
this.subscribersCount = this.channel.subscribers_count;
this.bindCssVariables();
this.setTitle();
} catch (e) {
......@@ -199,34 +177,14 @@ export class ProChannelComponent implements OnInit, AfterViewInit, OnDestroy {
this.detectChanges();
}
toggleSubscription($event) {
$event.preventDefault();
$event.stopPropagation();
if (!this.channel.subscribed) {
if (!this.session.isLoggedIn()) {
this.router.navigate(
this.site.isProDomain
? this.channelService.getRouterLink('login')
: ['/login']
);
return false;
}
this.channelService.subscribe();
} else {
this.modalService
.create(
ProUnsubscribeModalComponent,
this.channel,
{
class: 'm-overlayModal--unsubscribe',
},
this.injector
)
.present();
async reload() {
try {
this.channel = await this.channelService.reload(this.username);
} catch (e) {
this.error = e.getMessage();
}
this.detectChanges();
}
bindCssVariables() {
......
import { EventEmitter, Injectable } from '@angular/core';
import { Injectable, OnDestroy } from '@angular/core';
import { MindsChannelResponse } from '../../../interfaces/responses';
import { MindsUser } from '../../../interfaces/entities';
import { Client } from '../../../services/api/client';
......@@ -10,6 +10,7 @@ import { ActivatedRoute } from '@angular/router';
import { WireCreatorComponent } from '../../wire/creator/creator.component';
import { SessionsStorageService } from '../../../services/session-storage.service';
import { SiteService } from '../../../services/site.service';
import { Subscription } from 'rxjs';
export type RouterLinkToType =
| 'home'
......@@ -29,15 +30,15 @@ export interface NavItems {
}
@Injectable()
export class ProChannelService {
export class ProChannelService implements OnDestroy {
currentChannel: MindsUser;
subscriptionChange: EventEmitter<number> = new EventEmitter<number>();
protected featuredContent: Array<any> | null;
protected menuNavItems: Array<NavItems> = [];
protected isLoggedIn$: Subscription;
constructor(
protected client: Client,
protected entitiesService: EntitiesService,
......@@ -46,17 +47,27 @@ export class ProChannelService {
protected modalService: OverlayModalService,
protected sessionStorage: SessionsStorageService,
protected site: SiteService
) {}
) {
this.listen();
}
listen() {
this.isLoggedIn$ = this.session.loggedinEmitter.subscribe(is => {
if (!is && this.currentChannel) {
this.currentChannel.subscribed = false;
}
});
}
ngOnDestroy() {
this.isLoggedIn$.unsubscribe();
}
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;
await this.reload(id);
if (!this.currentChannel.pro_settings.tag_list) {
this.currentChannel.pro_settings.tag_list = [];
......@@ -75,6 +86,16 @@ export class ProChannelService {
}
}
async reload(id: string) {
const response: MindsChannelResponse = (await this.client.get(
`api/v1/channel/${id}`
)) as MindsChannelResponse;
this.currentChannel = response.channel;
return this.currentChannel;
}
async getFeaturedContent(): Promise<Array<any>> {
if (!this.currentChannel) {
throw new Error('No channel');
......@@ -232,34 +253,38 @@ export class ProChannelService {
async subscribe() {
this.currentChannel.subscribed = true;
this.currentChannel.subscribers_count += 1;
this.client
.post('api/v1/subscribe/' + this.currentChannel.guid, {})
.then((response: any) => {
if (response && response.error) {
throw 'error';
}
try {
const response = (await this.client.post(
'api/v1/subscribe/' + this.currentChannel.guid
)) as any;
this.currentChannel.subscribed = true;
this.subscriptionChange.emit(++this.currentChannel.subscribers_count);
})
.catch(e => {
this.currentChannel.subscribed = false;
alert("You can't subscribe to this user.");
});
if (!response || response.error) {
throw new Error(response.error || 'Invalid server response');
}
} catch (e) {
this.currentChannel.subscribed = false;
this.currentChannel.subscribers_count -= 1;
}
}
async unsubscribe() {
this.currentChannel.subscribed = false;
this.client
.delete('api/v1/subscribe/' + this.currentChannel.guid, {})
.then((response: any) => {
this.currentChannel.subscribed = false;
this.subscriptionChange.emit(--this.currentChannel.subscribers_count);
})
.catch(e => {
this.currentChannel.subscribed = true;
});
this.currentChannel.subscribers_count -= 1;
try {
const response = (await this.client.delete(
'api/v1/subscribe/' + this.currentChannel.guid
)) as any;
if (!response || response.error) {
throw new Error(response.error || 'Invalid server response');
}
} catch (e) {
this.currentChannel.subscribed = true;
this.currentChannel.subscribers_count += 1;
}
}
async auth() {
......
......@@ -16,6 +16,13 @@
/>
</li>
<li
class="m-pro__hamburger-menu-menu__subscribe"
*ngIf="currentUser?.guid != channel?.guid"
>
<m-pro__subscribeButton (onAction)="closeMenu()"></m-pro__subscribeButton>
</li>
<li>
<a
[routerLink]="feedRouterLink"
......@@ -66,7 +73,7 @@
>
</li>
<li>
<li *ngIf="currentUser?.guid != channel?.guid">
<a (click)="wire(); closeMenu()">
Wire
</a>
......
......@@ -59,6 +59,16 @@ m-pro__hamburger-menu {
}
}
&.m-pro__hamburger-menu-menu__subscribe {
padding: 0 0 16px;
text-align: center;
.m-pro__subscribeButton {
width: calc(100% - 32px);
margin: 0;
}
}
&.m-pro__hamburger-menu-menu__spacer {
height: 0;
border-top: 1px solid var(--m-pro--text-color);
......
import { ChangeDetectionStrategy, Component } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Output,
} from '@angular/core';
import { ProChannelService } from '../channel.service';
import { Session } from '../../../../services/session';
@Component({
selector: 'm-pro__hamburger-menu',
......@@ -7,7 +13,10 @@ import { ProChannelService } from '../channel.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProHamburgerMenu {
constructor(protected service: ProChannelService) {}
constructor(
protected service: ProChannelService,
protected session: Session
) {}
toggleMenu() {
if (document.body) {
......@@ -28,6 +37,10 @@ export class ProHamburgerMenu {
}
}
wire() {
this.service.wire();
}
get homeRouterLink() {
return this.service.getRouterLink('home');
}
......@@ -52,10 +65,6 @@ export class ProHamburgerMenu {
return this.service.getRouterLink('groups');
}
wire() {
this.service.wire();
}
get items() {
return this.service.getMenuNavItems();
}
......@@ -63,4 +72,12 @@ export class ProHamburgerMenu {
get channel() {
return this.service.currentChannel;
}
get currentUser() {
if (!this.session.isLoggedIn()) {
return null;
}
return this.session.getLoggedInUser();
}
}
<button
class="m-pro__subscribeButton"
[class.m-pro__subscribeButton--subscribed]="subscribed"
(click)="toggleSubscription(); $event.preventDefault()"
>
<span class="m-pro__subscribeButton--label">
<ng-container *ngIf="!subscribed; else subscribedActionButton" i18n>
Subscribe
</ng-container>
<ng-template #subscribedActionButton>
<ng-container i18n>Subscribed</ng-container>
</ng-template>
</span>
<span class="m-pro__subscribeButton--counter">
{{ count | abbr: 0 }}
</span>
</button>
.m-pro__subscribeButton {
appearance: none;
font-size: 12px;
letter-spacing: 1.25px;
text-transform: uppercase;
padding: 12px 16px;
font-family: 'Roboto', Helvetica, sans-serif;
cursor: pointer;
border-radius: 4px;
margin-left: 16px;
background: none transparent;
color: var(--m-pro--primary-color);
border: 1px solid var(--m-pro--primary-color);
font-weight: 600;
&.m-pro__subscribeButton--subscribed {
border: none;
color: var(--m-pro--text-color);
}
.m-pro__subscribeButton--label {
opacity: 0.85;
}
.m-pro__subscribeButton--counter {
margin-left: 0.65em;
}
}
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
Injector,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { Router } from '@angular/router';
import { Session } from '../../../../services/session';
import { SiteService } from '../../../../services/site.service';
import { ProChannelService } from '../channel.service';
import { ProUnsubscribeModalComponent } from '../unsubscribe-modal/modal.component';
import { MindsUser } from '../../../../interfaces/entities';
import { OverlayModalService } from '../../../../services/ux/overlay-modal';
import { Subscription } from 'rxjs';
@Component({
selector: 'm-pro__subscribeButton',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: 'subscribe-button.component.html',
})
export class SubscribeButtonComponent implements OnInit, OnDestroy {
@Output() onAction: EventEmitter<any> = new EventEmitter<any>();
count: number;
subscribed: boolean;
protected loggedIn$: Subscription;
constructor(
protected session: Session,
protected router: Router,
protected site: SiteService,
protected channelService: ProChannelService,
protected modalService: OverlayModalService,
protected injector: Injector,
protected cd: ChangeDetectorRef
) {}
ngOnInit() {
this.updateCount();
this.loggedIn$ = this.session.loggedinEmitter.subscribe(is => {
if (!is) {
this.subscribed = false;
this.detectChanges();
}
});
}
ngOnDestroy() {
this.loggedIn$.unsubscribe();
}
async toggleSubscription() {
if (!this.session.isLoggedIn()) {
this.router.navigate(
this.site.isProDomain
? this.channelService.getRouterLink('login')
: ['/login']
);
} else if (!this.channel.subscribed) {
await this.channelService.subscribe();
this.updateCount();
} else {
this.modalService
.create(
ProUnsubscribeModalComponent,
void 0,
{
class: 'm-overlayModal--unsubscribe',
},
this.injector
)
.onDidDismiss(() => {
this.updateCount();
})
.present();
}
this.onAction.emit();
}
updateCount() {
this.subscribed = this.channel.subscribed;
this.count = this.channel.subscribers_count;
this.detectChanges();
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
get channel(): MindsUser {
return this.channelService.currentChannel;
}
}
import { Component, Input, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { Session } from '../../../../services/session';
import { OverlayModalService } from '../../../../services/ux/overlay-modal';
import { MindsUser } from '../../../../interfaces/entities';
import { Client } from '../../../../services/api/client';
import { Router } from '@angular/router';
import { ProChannelService } from '../channel.service';
......@@ -10,13 +9,7 @@ import { ProChannelService } from '../channel.service';
selector: 'm-pro--unsubscribe-modal',
templateUrl: 'modal.component.html',
})
export class ProUnsubscribeModalComponent implements OnInit {
protected channel: MindsUser;
@Input('channel') set data(data) {
this.channel = data;
}
export class ProUnsubscribeModalComponent {
constructor(
protected session: Session,
private overlayModal: OverlayModalService,
......@@ -25,8 +18,6 @@ export class ProUnsubscribeModalComponent implements OnInit {
protected channelService: ProChannelService
) {}
ngOnInit() {}
close() {
this.overlayModal.dismiss();
}
......@@ -37,6 +28,6 @@ export class ProUnsubscribeModalComponent implements OnInit {
}
get channelName() {
return this.channel.username;
return this.channelService.currentChannel.username;
}
}
......@@ -32,6 +32,7 @@ import { CanDeactivateGuardService } from '../../services/can-deactivate-guard';
import { ForgotPasswordComponent } from '../auth/forgot-password/forgot-password.component';
import { AuthModule } from '../auth/auth.module';
import { ProHamburgerMenu } from './channel/hamburger-menu/hamburger-menu.component';
import { SubscribeButtonComponent } from './channel/subscribe-button/subscribe-button.component';
const routes: Routes = [
{
......@@ -154,6 +155,7 @@ export const PRO_DOMAIN_ROUTES = [
ProGroupTileComponent,
ProUnsubscribeModalComponent,
ProHamburgerMenu,
SubscribeButtonComponent,
],
exports: [ProChannelComponent],
entryComponents: [
......
......@@ -213,16 +213,10 @@
Set up your category filter hashtags here.
</p>
<m-draggable-list
[data]="settings.tag_list"
[id]="'tag'"
>
<m-draggable-list [data]="settings.tag_list" [id]="'tag'">
<ng-template let-tag="item" let-i="i">
<div class="m-pro--settings--field">
<label i18n>
Hashtag #{{ i + 1 }}
</label>
<label i18n> Hashtag #{{ i + 1 }} </label>
<div class="m-pro--settings--flex-inputs">
<input
......@@ -231,7 +225,7 @@
[id]="'tag-label-' + i"
[name]="'tag[' + i + '][label]'"
[(ngModel)]="tag.label"
>
/>
<input
type="text"
......@@ -239,7 +233,7 @@
[id]="'tag-tag-' + i"
[name]="'tag[' + i + '][tag]'"
[(ngModel)]="tag.tag"
>
/>
<i class="material-icons" (click)="removeTag(i)">
clear
......@@ -279,16 +273,10 @@
/>
</div>
<m-draggable-list
[data]="settings.footer_links"
[id]="'title'"
>
<m-draggable-list [data]="settings.footer_links" [id]="'title'">
<ng-template let-link="item" let-i="i">
<div class="m-pro--settings--field">
<label i18n>
Link #{{ i + 1 }}
</label>
<label i18n> Link #{{ i + 1 }} </label>
<div class="m-pro--settings--flex-inputs">
<input
......@@ -297,7 +285,7 @@
[id]="'footer_link-title-' + i"
[name]="'footer_link[' + i + '][title]'"
[(ngModel)]="link.title"
>
/>
<input
type="text"
......@@ -305,7 +293,7 @@
[id]="'footer_link-href-' + i"
[name]="'footer_link[' + i + '][href]'"
[(ngModel)]="link.href"
>
/>
<i class="material-icons" (click)="removeFooterLink(i)">
clear
......@@ -358,7 +346,7 @@
<a
class="m-btn m-link-btn m-btn--slim m-btn--destructive"
routerLink="/pro"
>Cancel Pro</a
>Cancel Pro</a
>
</div>
</ng-template>
......@@ -385,7 +373,7 @@
class="m-btn m-link-btn m-btn--slim m-pro--settings--preview-btn"
[routerLink]="previewRoute"
i18n
>View your Pro channel</a
>View your Pro channel</a
>
</div>
</form>
......
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnInit,
} from '@angular/core';
import { ProService } from '../pro.service';
import { Session } from '../../../services/session';
import { Router } from '@angular/router';
......
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