...
 
......@@ -7,13 +7,13 @@ context('Pro Settings', () => {
// required to run tests against pro user only.
const title = '#title';
const headline = '#headline';
// const previewButton = '.m-proSettings__previewBtn';
const activityContainer = 'minds-activity';
const footerText = '#footer_text';
const theme = {
primaryColor: '#primary_color',
plainBackgroundColor: '#plain_background_color',
textColor: '#textColor',
primaryColor: '#primaryColor',
plainBackgroundColor: '#plainBgColor',
schemeLight: '#scheme_light',
schemeDark: '#scheme_dark',
aspectRatio: {
......@@ -55,9 +55,13 @@ context('Pro Settings', () => {
});
after(() => {
cy.visit('/pro/settings')
// cy.visit(`/${Cypress.env().username}`);
cy.visit('/pro/' + Cypress.env().pro_username + '/settings/hashtags')
.location('pathname')
.should('eq', '/pro/settings');
.should(
'eq',
'/pro/' + Cypress.env().pro_username + '/settings/hashtags'
);
clearHashtags();
});
......@@ -66,9 +70,12 @@ context('Pro Settings', () => {
cy.server();
cy.route('POST', '**/api/v2/pro/settings').as('settings');
cy.visit('/pro/settings')
cy.visit('/pro/' + Cypress.env().pro_username + '/settings/general')
.location('pathname')
.should('eq', '/pro/settings');
.should(
'eq',
'/pro/' + Cypress.env().pro_username + '/settings/general'
);
});
it('should update the title and headline', () => {
......@@ -124,7 +131,7 @@ context('Pro Settings', () => {
it('should allow the user to set category hashtags', () => {
cy.contains('Hashtags').click();
cy.contains('+ Add Tag').click();
cy.contains('Add').click();
cy.get(hashtags.labelInput0)
.clear()
......@@ -134,7 +141,7 @@ context('Pro Settings', () => {
.clear()
.type(hashtags.hashtag1);
cy.contains('+ Add Tag').click();
cy.contains('Add').click();
cy.get(hashtags.labelInput1)
.first()
......@@ -189,13 +196,13 @@ context('Pro Settings', () => {
});
//go to pro page
// cy.get(previewButton).click();
cy.contains('View Pro Channel').click();
}
function clearHashtags() {
cy.contains('Hashtags').click();
cy.contains('+ Add Tag').click();
cy.contains('Add').click();
cy.contains('clear').click({ multiple: true });
saveAndPreview();
......@@ -218,9 +225,7 @@ context('Pro Settings', () => {
// });
// //go to pro page
// cy.get(previewButton)
// .click();
// });
// cy.contains('View Pro Channel').click();
// })
}
......
......@@ -1968,6 +1968,12 @@
"integrity": "sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ==",
"dev": true
},
"@types/sizzle": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
"integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
"dev": true
},
"@types/source-list-map": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
......@@ -5322,13 +5328,14 @@
"dev": true
},
"cypress": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-3.4.1.tgz",
"integrity": "sha512-1HBS7t9XXzkt6QHbwfirWYty8vzxNMawGj1yI+Fu6C3/VZJ8UtUngMW6layqwYZzLTZV8tiDpdCNBypn78V4Dg==",
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-3.6.1.tgz",
"integrity": "sha512-6n0oqENdz/oQ7EJ6IgESNb2M7Bo/70qX9jSJsAziJTC3kICfEMmJUlrAnP9bn+ut24MlXQST5nRXhUP5nRIx6A==",
"dev": true,
"requires": {
"@cypress/listr-verbose-renderer": "0.4.1",
"@cypress/xvfb": "1.2.4",
"@types/sizzle": "2.3.2",
"arch": "2.1.1",
"bluebird": "3.5.0",
"cachedir": "1.3.0",
......@@ -5355,6 +5362,7 @@
"request-progress": "3.0.0",
"supports-color": "5.5.0",
"tmp": "0.1.0",
"untildify": "3.0.3",
"url": "0.11.0",
"yauzl": "2.10.0"
},
......@@ -19115,6 +19123,12 @@
}
}
},
"untildify": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz",
"integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==",
"dev": true
},
"upath": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
......
......@@ -63,7 +63,7 @@
"@types/jasminewd2": "~2.0.4",
"@types/node": "~10.12.18",
"codelyzer": "^4.5.0",
"cypress": "^3.4.1",
"cypress": "^3.6.1",
"cypress-file-upload": "^3.3.3",
"gulp": "~4.0.0",
"gulp-autoprefixer": "^6.0.0",
......
......@@ -15,6 +15,13 @@ m-dashboardLayout {
position: relative;
display: block;
width: 100%;
a {
font-weight: 400;
text-decoration: none;
@include m-theme() {
color: themed($m-blue);
}
}
}
@media screen and (max-width: $min-tablet) {
......
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'm-dashboardLayout',
templateUrl: './dashboard-layout.component.html',
})
export class DashboardLayoutComponent implements OnInit {
export class DashboardLayoutComponent {
constructor() {}
ngOnInit() {}
}
<div class="m-formToast__toastsContainer">
<ng-container *ngFor="let toast of toasts; let i = index">
<div class="m-formToast__wrapper" [ngClass]="{ active: toast.visible }">
<div
class="m-formToast__wrapper"
[ngClass]="{ dismissed: toast.dismissed }"
>
<i
class="material-icons m-formToast__icon--success"
*ngIf="toast.type === 'success'"
......@@ -24,7 +27,7 @@
<div class="m-formToast__iconWrapper">
<i
class="material-icons m-formToast__icon--close"
(click)="toast.visible = false"
(click)="toast.dismissed = true"
>clear</i
>
</div>
......
m-formToast {
margin: 37px 70px 0 70px;
display: flex;
flex-flow: column nowrap;
min-width: 300px;
max-width: 522px;
position: fixed;
bottom: 24;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
max-width: 522px;
width: 60%;
transition: all 0.3s ease;
}
.m-formToast__toastsContainer {
height: auto;
transition: all 0.3s ease;
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
}
.m-formToast__wrapper {
box-sizing: border-box;
width: 100%;
font-size: 15px;
line-height: 20px;
padding: 13px;
margin-bottom: 16px;
display: flex;
opacity: 0;
transition: opacity 0.4s cubic-bezier(0.23, 1, 0.32, 1);
opacity: 1;
animation: 0.4s cubic-bezier(0.23, 1, 0.32, 1) 1 forward fadeIn;
@include m-theme() {
color: themed($m-grey-600);
background-color: themed($m-white);
box-shadow: 0 0 15px 0 rgba(themed($m-black), 0.2);
}
&.active {
opacity: 1;
&.dismissed {
display: none;
}
p {
flex-grow: 1;
......@@ -34,6 +37,15 @@ m-formToast {
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
[class*='m-formToast__icon--'] {
margin-right: 10px;
}
......@@ -74,11 +86,9 @@ m-formToast {
}
}
@media screen and (max-width: $min-tablet) {
@media screen and (max-width: $max-mobile) {
m-formToast {
margin: 0 auto;
max-width: 85%;
position: absolute;
bottom: 48px;
width: 75%;
}
}
......@@ -34,21 +34,16 @@ export class FormToastComponent implements OnInit, OnDestroy {
console.log(toastIndex);
this.detectChanges();
// TODOOJM something is wrong here
const toastTimeout = setTimeout(() => {
this.toasts[toastIndex].visible = false;
this.toasts[toastIndex]['dismissed'] = true;
this.detectChanges();
}, 3000);
}, 3400);
this.timeoutIds.push(setTimeout(() => toastTimeout));
});
}
dismiss(index) {
this.toasts[index].visible = false;
this.detectChanges();
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......
m-shadowboxHeader {
min-height: 116px;
display: block;
a {
font-weight: 400;
text-decoration: none;
@include m-theme() {
color: themed($m-blue);
}
}
}
.m-shadowboxHeader__section {
position: relative;
......
......@@ -18,8 +18,8 @@ export class ShadowboxHeaderComponent implements AfterViewInit {
@Input() isScrollable: boolean = true;
@Input() itemActivated;
@ViewChild('shadowboxHeaderContainer', { static: false })
shadowboxHeaderContainerEl: ElementRef;
shadowboxHeaderContainer;
containerEl: ElementRef;
container;
childClientWidth: number;
faderWidth = 24;
......@@ -74,38 +74,32 @@ export class ShadowboxHeaderComponent implements AfterViewInit {
// TODO: figure out how to avoid test failure "Cannot read property 'clientWidth' of null"
this.childClientWidth = firstMetric ? firstMetric.clientWidth : 160;
this.shadowboxHeaderContainer = this.shadowboxHeaderContainerEl.nativeElement;
this.container = this.containerEl.nativeElement;
this.isOverflown =
this.shadowboxHeaderContainer.scrollWidth -
this.shadowboxHeaderContainer.clientWidth >
0;
this.container.scrollWidth - this.container.clientWidth > 0;
this.isAtScrollStart =
this.shadowboxHeaderContainer.scrollLeft < this.faderWidth;
this.isAtScrollStart = this.container.scrollLeft < this.faderWidth;
this.showButton.left = this.isOverflown && !this.isAtScrollStart;
this.isAtScrollEnd =
!this.isOverflown ||
this.shadowboxHeaderContainer.scrollWidth -
(this.shadowboxHeaderContainer.scrollLeft +
this.shadowboxHeaderContainer.clientWidth) <
this.container.scrollWidth -
(this.container.scrollLeft + this.container.clientWidth) <
this.faderWidth;
this.showButton.right =
this.isOverflown &&
this.shadowboxHeaderContainer.scrollLeft >= 0 &&
!this.isAtScrollEnd;
this.isOverflown && this.container.scrollLeft >= 0 && !this.isAtScrollEnd;
this.detectChanges();
}
slide(direction) {
let currentScrollLeft = this.shadowboxHeaderContainer.scrollLeft;
let currentScrollLeft = this.container.scrollLeft;
let targetScrollLeft;
let scrollEndOffset = 0;
const partiallyVisibleMetricWidth =
this.shadowboxHeaderContainer.clientWidth % this.childClientWidth;
this.container.clientWidth % this.childClientWidth;
const completelyVisibleMetricsWidth =
this.shadowboxHeaderContainer.clientWidth - partiallyVisibleMetricWidth;
this.container.clientWidth - partiallyVisibleMetricWidth;
if (direction === 'right') {
if (currentScrollLeft < this.faderWidth) {
......@@ -113,8 +107,7 @@ export class ShadowboxHeaderComponent implements AfterViewInit {
}
targetScrollLeft = Math.min(
currentScrollLeft + completelyVisibleMetricsWidth,
this.shadowboxHeaderContainer.scrollWidth -
completelyVisibleMetricsWidth
this.container.scrollWidth - completelyVisibleMetricsWidth
);
} else {
if (this.isAtScrollEnd) {
......@@ -126,7 +119,7 @@ export class ShadowboxHeaderComponent implements AfterViewInit {
);
}
this.shadowboxHeaderContainer.scrollTo({
this.container.scrollTo({
top: 0,
left: targetScrollLeft,
behavior: 'smooth',
......
......@@ -3,18 +3,10 @@
[isScrollable]="scrollableHeader"
[ngClass]="{ isScrollable: scrollableHeader }"
>
<div class="m-shadowboxLayout__header hasTitle" *ngIf="headerTitle">
<h3 class="m-shadowboxHeader__title" i18n>{{ headerTitle }}</h3>
<h4 *ngIf="headerSubtitle" class="m-shadowboxHeader__subtitle" i18n>
{{ headerSubtitle }}
</h4>
</div>
<ng-container>
<ng-content
select=".m-shadowboxLayout__header"
ngProjectAs=".m-shadowboxLayout__header"
></ng-content>
</ng-container>
<ng-content
select=".m-shadowboxLayout__header"
ngProjectAs=".m-shadowboxLayout__header"
></ng-content>
></m-shadowboxHeader
>
......@@ -28,7 +20,5 @@
class="m-shadowboxLayout__bottom"
select=".m-shadowboxLayout__bottom"
>
<!-- <ng-content select=".m-shadowboxLayout__body"></ng-content>
<ng-content select=".m-shadowboxLayout__footer"></ng-content> -->
</ng-content>
</ng-container>
import { Component, OnInit, Input, HostBinding } from '@angular/core';
import { Component, Input, HostBinding } from '@angular/core';
@Component({
selector: 'm-shadowboxLayout',
templateUrl: './shadowbox-layout.component.html',
})
export class ShadowboxLayoutComponent implements OnInit {
export class ShadowboxLayoutComponent {
@Input() scrollableHeader: boolean = true;
@Input() hasHeader: boolean = true;
@Input() headerTitle: string;
@Input() headerSubtitle: string;
@HostBinding('class.isForm') @Input() isForm: boolean = false;
constructor() {}
ngOnInit() {}
}
<div class="m-shadowboxLayout__footer">
<button class="m-shadowboxSubmitButton" type="submit" [disabled]="disabled">
<ng-content></ng-content>
</button>
</div>
<button
class="m-shadowboxSubmitButton"
type="submit"
[disabled]="disabled"
[ngClass]="{ saving: saveStatus === 'saving' }"
>
<ng-content></ng-content>
</button>
......@@ -37,8 +37,10 @@
&:disabled,
&[disabled] {
cursor: default;
@include m-theme() {
background-color: themed($m-grey-100);
&:not(.saving) {
@include m-theme() {
background-color: themed($m-grey-100);
}
}
}
}
......
......@@ -17,6 +17,15 @@ m-sidebarMenu {
.m-sidebarMenu__sidebar {
position: fixed;
top: 157px;
bottom: 0;
overflow-x: hidden;
overflow-y: scroll;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
.m-sidebarMenu__header {
......@@ -61,6 +70,9 @@ m-sidebarMenu {
color: themed($m-blue);
}
}
&:last-child {
padding-bottom: 36px;
}
}
}
}
......
......@@ -4,7 +4,6 @@ import { Observable, Subject } from 'rxjs';
export interface FormToast {
type?: 'success' | 'error' | 'warning' | 'info' | null;
message?: string;
visible?: boolean;
}
@Injectable()
......@@ -49,7 +48,6 @@ export class FormToastService {
}
trigger(toast: FormToast) {
toast['visible'] = true;
if (!toast.type) {
toast.type = 'info';
}
......
......@@ -53,13 +53,6 @@ m-analytics__dashboard {
@include m-theme() {
color: themed($m-grey-300);
}
a {
font-weight: 400;
text-decoration: none;
@include m-theme() {
color: themed($m-blue);
}
}
}
@media screen and (max-width: $min-tablet) {
......
......@@ -235,14 +235,19 @@ m-proSettings {
}
.m-proSettings__colorPreview {
cursor: pointer;
height: 40px;
width: 40px;
margin-left: 15px;
border-radius: 2px;
transition: background-color 0.2s ease;
@include m-theme() {
box-shadow: 0 1px 4px 0 rgba(themed($m-black), 0.1);
border: 1px solid themed($m-grey-50);
}
input {
visibility: hidden;
}
}
[class*='m-proSettings__filePreview'] {
......@@ -384,12 +389,13 @@ m-proSettings {
}
}
}
[class*='m-proSettings__customInput'] {
[class*='m-proSettings__customInput--'] {
position: absolute;
top: 0;
left: 0;
height: 20px;
width: 20px;
transition: all 0.3s ease;
@include m-theme() {
background-color: themed($m-white);
border: 1px solid themed($m-grey-100);
......@@ -444,11 +450,11 @@ m-proSettings {
@media screen and (max-width: 1000px) {
m-proSettings {
.stretchedField {
// .m-proSettings__row--label m-tooltip {
// .m-tooltip--bubble {
// right: 12px;
// }
// }
.m-proSettings__row--label m-tooltip {
.m-tooltip--bubble {
right: 12px;
}
}
[class*='m-proSettings__row'] {
flex-flow: row wrap;
......@@ -471,14 +477,14 @@ m-proSettings {
}
}
}
@media screen and (max-width: $min-tablet) {
m-proSettings {
.stretchedField {
.m-proSettings__row--label m-tooltip {
.m-tooltip--bubble {
right: 12px;
}
}
}
}
}
// @media screen and (max-width: $min-tablet) {
// m-proSettings {
// .stretchedField {
// .m-proSettings__row--label m-tooltip {
// .m-tooltip--bubble {
// right: 12px;
// }
// }
// }
// }
// }
......@@ -36,50 +36,19 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
menu: Menu = sidebarMenu;
activeTab: any;
tabs = [
{
id: 'general',
title: 'General',
subtitle: 'Customize your title and headline',
},
{
id: 'theme',
title: 'Theme',
subtitle: "Set up your site's color theme",
},
{
id: 'assets',
title: 'Assets',
subtitle: 'Upload custom logo and background images',
},
{
id: 'hashtags',
title: 'Hashtags',
subtitle: 'Set up your category filter hashtags here',
},
{
id: 'footer',
title: 'Footer',
subtitle: "Set up your site's footer links",
},
{
id: 'domain',
title: 'Domain',
subtitle: 'Customize your site domain',
},
{
id: 'payouts',
title: 'Payouts',
subtitle:
'Select the currency type you wish you be paid out in. Please note payouts only occur once you have received the equivalent of $100 or greater.',
},
'general',
'theme',
'assets',
'hashtags',
'footer',
'domain',
'payouts',
];
settings: any;
inProgress: boolean;
saved: boolean = false;
saveStatus: string = 'unsaved';
user: string | null = null;
......@@ -88,14 +57,16 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
error: string;
hexPattern = '^#([0-9A-Fa-f]{3}){1,2}$'; // accepts both 3- and 6-digit codes, hash required
textColorPickerVal: string;
primaryColorPickerVal: string;
plainBgColorPickerVal: string;
hexPattern = '^#([0-9A-Fa-f]{6})$'; // accepts 6-digit codes only, hash required
domainValidationSubject: Subject<any> = new Subject<any>();
protected paramMap$: Subscription;
protected formChanges$: Subscription;
@ViewChild('logoField', { static: false })
protected logoField: ElementRef<HTMLInputElement>;
......@@ -108,8 +79,11 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
published: [''],
theme: this.fb.group({
text_color: [''],
text_color_picker: [''],
primary_color: [''],
primary_color_picker: [''],
plain_background_color: [''],
plain_background_color_picker: [''],
scheme: [''],
tile_ratio: [''],
}),
......@@ -147,7 +121,7 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
ngOnInit() {
this.paramMap$ = this.route.paramMap.subscribe((params: ParamMap) => {
const activeTabParam = params.get('tab');
this.activeTab = this.tabs.find(tab => tab.id === activeTabParam);
this.activeTab = this.tabs.find(tab => tab === activeTabParam);
this.saveStatus = 'unsaved';
if (this.session.isAdmin()) {
......@@ -157,18 +131,10 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
this.detectChanges();
this.load();
});
this.formChanges$ = this.form.valueChanges.subscribe((value: any) => {
if (this.form.dirty && this.saveStatus === 'saved') {
this.saveStatus = 'unsaved';
this.detectChanges();
}
});
}
ngOnDestroy() {
this.paramMap$.unsubscribe();
this.formChanges$.unsubscribe();
}
async load() {
......@@ -188,8 +154,11 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
published: settings.published,
theme: {
text_color: settings.text_color,
text_color_picker: settings.text_color,
primary_color: settings.primary_color,
primary_color_picker: settings.primary_color,
plain_background_color: settings.plain_background_color,
plain_background_color_picker: settings.plain_background_color,
scheme: settings.scheme,
tile_ratio: settings.tile_ratio,
},
......@@ -322,8 +291,7 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
published: this.form.value.published,
text_color: this.form.value.theme.text_color,
primary_color: this.form.value.theme.primary_color,
primary_background_color: this.form.value.theme
.primary_background_color,
plain_background_color: this.form.value.theme.plain_background_color,
scheme: this.form.value.theme.scheme,
tile_ratio: this.form.value.theme.tile_ratio,
domain: this.form.value.domain.domain,
......@@ -337,15 +305,12 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
this.formToastService.success(
'Pro settings have been successfully updated'
);
this.saveStatus = 'saved';
this.form.markAsPristine();
} catch (e) {
this.error = e.message;
this.formToastService.error('Error: ' + this.error);
this.saveStatus = 'unsaved';
}
this.saved = true;
this.saveStatus = 'unsaved';
this.inProgress = false;
this.detectChanges();
}
......@@ -397,6 +362,30 @@ export class ProSettingsComponent implements OnInit, OnDestroy {
this.detectChanges();
}
updateColorText(colorTextControl, updatedColor) {
if (updatedColor !== this.form.value.theme[colorTextControl]) {
this.form
.get('theme')
.get(colorTextControl)
.setValue(updatedColor);
}
}
updateColorPicker(colorTextControlName, updatedColor) {
console.log(updatedColor);
const colorTextControl = this.form.get('theme').get(colorTextControlName);
console.log(colorTextControl);
const colorPickerControl = this.form
.get('theme')
.get(`${colorTextControlName}_picker`);
if (
colorTextControl.valid &&
colorTextControl.value !== colorPickerControl.value
) {
colorPickerControl.setValue(updatedColor);
}
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......