...
 
Commits (21)
image: markharding/minds-front-base
services:
- docker:dind
stages:
- test
- build
......@@ -154,6 +151,8 @@ build:production:i18n:
prepare:review:
stage: prepare
image: minds/ci:latest
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker build -t $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID -f containers/front-init/Dockerfile dist/.
......@@ -179,6 +178,8 @@ prepare:review:sentry:
prepare:production:
stage: prepare
image: minds/ci:latest
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker build -t $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID -f containers/front-init/Dockerfile dist/.
......@@ -261,6 +262,8 @@ review:stop:
.deploy: &deploy
image: minds/ci:latest
services:
- docker:dind
script:
## Sync assets with CDN
- aws s3 sync dist $S3_REPOSITORY_URL
......
......@@ -6,7 +6,7 @@ Minds Front
Front-end web application for Minds. Please run inside of [the Minds repo](https://github.com/minds/minds).
## Documentation
Documentation for Minds can be found at [minds.org/docs](https://www.minds.org/docs)
Please see the documentation on [developers.minds.com](https://developers.minds.com) for instructions on how to [build the Minds Front-end](https://developers.minds.com/docs/guides/frontend).
### Building
Please see the documentation on Minds.org for instructions on how to [build the Minds Front-end](https://www.minds.org/docs/install/preparation.html#front-end).
......
import generateRandomId from '../support/utilities';
context('Newsfeed', () => {
before(() => {
cy.getCookie('minds_sess').then(sessionCookie => {
......@@ -14,7 +16,6 @@ context('Newsfeed', () => {
cy.route('POST', '**/api/v1/media').as('mediaPOST');
cy.route('POST', '**/api/v1/newsfeed/**').as('newsfeedEDIT');
cy.route('POST', '**/api/v1/media/**').as('mediaEDIT');
cy.visit('/newsfeed/subscriptions')
.location('pathname')
.should('eq', '/newsfeed/subscriptions');
......@@ -37,6 +38,19 @@ context('Newsfeed', () => {
cy.get('minds-newsfeed-poster textarea').type(content);
};
const attachRichEmbed = (embedUrl) => {
cy.get('minds-newsfeed-poster').should('be.visible');
cy.get('minds-newsfeed-poster textarea')
.type(embedUrl);
cy.route('GET', `**/api/v1/newsfeed/preview?url=${embedUrl}**`)
.as('previewGET')
.wait('@previewGET')
.then(xhr => {
expect(xhr.status).to.equal(200);
});
}
const attachImageToActivity = () => {
cy.uploadFile(
'#attachment-input-poster',
......@@ -511,4 +525,140 @@ context('Newsfeed', () => {
deleteActivityFromNewsfeed();
});
it('should show a rich embed post from youtube in a modal', () => {
const content = generateRandomId() + " ",
url = 'https://www.youtube.com/watch?v=jNQXAC9IVRw';
// set up post.
newActivityContent(content);
attachRichEmbed(url);
// post and await.
cy.get('.m-posterActionBar__PostButton')
.click()
.wait('@newsfeedPOST').then(xhr => {
expect(xhr.status).to.equal(200);
//get activity, click it.
cy.get(`[minds-data-activity-guid='${xhr.response.body.guid}']`)
.click();
//check modal is open.
cy.get('[data-cy=data-minds-media-modal]')
.contains(content);
// close modal and tidy.
cy.get('.m-overlay-modal--backdrop')
.click({force: true});
deleteActivityFromNewsfeed();
});
});
it('should not open vimeo in a modal', () => {
const content = generateRandomId() + " ",
url = 'https://vimeo.com/8733915';
// set up post.
newActivityContent(content);
attachRichEmbed(url);
// post and await.
cy.get('.m-posterActionBar__PostButton')
.click()
.wait('@newsfeedPOST').then(xhr => {
expect(xhr.status).to.equal(200);
//get activity, make assertions tht would not be true for modals.
cy.get(`[minds-data-activity-guid='${xhr.response.body.guid}']`)
.should('be.visible')
.get('iframe')
.should('be.visible')
.get('.minds-more')
.should('be.visible');
// tidy.
deleteActivityFromNewsfeed();
});
});
it('should not open soundcloud in a modal', () => {
const content = generateRandomId() + " ",
url = 'https://soundcloud.com/richarddjames/piano-un10-it-happened';
// set up post.
newActivityContent(content);
attachRichEmbed(url);
// post and await.
cy.get('.m-posterActionBar__PostButton')
.click()
.wait('@newsfeedPOST').then(xhr => {
expect(xhr.status).to.equal(200);
//get activity, make assertions tht would not be true for modals.
cy.get(`[minds-data-activity-guid='${xhr.response.body.guid}']`)
.should('be.visible')
.get('.m-rich-embed-action-overlay')
.should('be.visible')
.get('.minds-more')
.should('be.visible');
deleteActivityFromNewsfeed();
});
});
it('should not open spotify in a modal', () => {
const content = generateRandomId() + " ",
url = 'https://open.spotify.com/track/2MZSXhq4XDJWu6coGoXX1V?si=nvja0EfwR3q6GMQmYg6gPQ';
// set up post.
newActivityContent(content);
attachRichEmbed(url);
// post and await.
cy.get('.m-posterActionBar__PostButton')
.click()
.wait('@newsfeedPOST').then(xhr => {
expect(xhr.status).to.equal(200);
//get activity, make assertions tht would not be true for modals.
cy.get(`[minds-data-activity-guid='${xhr.response.body.guid}']`)
.should('be.visible')
.get('.m-rich-embed-action-overlay')
.should('be.visible')
.get('.minds-more')
.should('be.visible');
deleteActivityFromNewsfeed();
});
});
it('should not open spotify in a modal', () => {
const content = generateRandomId() + " ",
url = 'http://giphygifs.s3.amazonaws.com/media/IzVquL965ib4s/giphy.gif';
// set up post.
newActivityContent(content);
attachRichEmbed(url);
// post and await.
cy.get('.m-posterActionBar__PostButton')
.click()
.wait('@newsfeedPOST').then(xhr => {
expect(xhr.status).to.equal(200);
//get activity, make assertions tht would not be true for modals.
cy.get(`[minds-data-activity-guid='${xhr.response.body.guid}']`)
.should('be.visible')
.get('.m-rich-embed-action-overlay')
.should('be.visible')
.get('.minds-more')
.should('be.visible');
deleteActivityFromNewsfeed();
});
});
});
......@@ -41,13 +41,13 @@
display: flex;
flex-direction: row;
align-items: center;
padding: 0 8px 0 0;
list-style: none;
opacity: 1;
text-overflow: ellipsis;
text-align: left;
a {
> * {
padding: 0 8px 0 0;
text-decoration: none;
width: 100%;
font-weight: 400;
......@@ -94,11 +94,13 @@
.m-dropdown--list--item,
.m-dropdownList__item {
padding: 8px;
@include m-theme() {
border-bottom: 1px solid themed($m-grey-50);
}
> * {
padding: 8px;
}
&.m-dropdown--list--item--selected,
.m-dropdownList__item--selected {
@include m-theme() {
......
......@@ -31,6 +31,12 @@ export class NSFWSelectorComponent {
private storage: Storage
) {}
ngOnInit() {
if (this.service.reasons) {
this.service.reasons.map(r => this.toggle(r.value));
}
}
get service() {
switch (this.serviceRef) {
case 'editing':
......
......@@ -55,7 +55,7 @@
[href]="src.perma_url"
target="_blank"
rel="noopener noreferrer"
class="meta mdl-color-text--blue-grey-900"
class="meta"
[ngClass]="{ 'm-rich-embed-has-thumbnail': src.thumbnail_src, 'm-rich-embed--title--no-padding': hasInlineContentLoaded() }"
>
<h2
......@@ -74,10 +74,7 @@
<a class="thumbnail" *ngIf="preview.thumbnail">
<img src="{{preview.thumbnail}}" />
</a>
<a
class="meta mdl-color-text--blue-grey-900"
[ngClass]="{ 'm-has-thumbnail': preview.thumbnail }"
>
<a class="meta" [ngClass]="{ 'm-has-thumbnail': preview.thumbnail }">
<h2 class="m-rich-embed--title mdl-card__title-text">
{{preview.title | excerpt}}
</h2>
......
......@@ -16,9 +16,6 @@ minds-rich-embed {
left: 0;
width: 100%;
height: 100%;
@include m-theme() {
background-color: rgba(themed($m-black-always), 0.2);
}
&:hover {
background: transparent;
......
......@@ -3,11 +3,14 @@ import {
ElementRef,
ChangeDetectorRef,
ChangeDetectionStrategy,
Output,
EventEmitter,
} from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { RichEmbedService } from '../../../services/rich-embed';
import mediaProxyUrl from '../../../helpers/media-proxy-url';
import { FeaturesService } from '../../../services/features.service';
@Component({
moduleId: module.id,
......@@ -17,18 +20,22 @@ import mediaProxyUrl from '../../../helpers/media-proxy-url';
})
export class MindsRichEmbed {
type: string = '';
mediaSource: string = '';
src: any = {};
preview: any = {};
maxheight: number = 320;
inlineEmbed: any = null;
embeddedInline: boolean = false;
cropImage: boolean = false;
modalRequestSubscribed: boolean = false;
@Output() mediaModalRequested: EventEmitter<any> = new EventEmitter();
private lastInlineEmbedParsed: string;
constructor(
private sanitizer: DomSanitizer,
private service: RichEmbedService,
private cd: ChangeDetectorRef
private cd: ChangeDetectorRef,
protected featureService: FeaturesService
) {}
set _src(value: any) {
......@@ -65,6 +72,14 @@ export class MindsRichEmbed {
// Inline Embedding
let inlineEmbed = this.parseInlineEmbed(this.inlineEmbed);
if (
this.featureService.has('media-modal') &&
this.mediaSource === 'youtube'
) {
this.modalRequestSubscribed =
this.mediaModalRequested.observers.length > 0;
}
if (
inlineEmbed &&
inlineEmbed.id &&
......@@ -80,9 +95,35 @@ export class MindsRichEmbed {
}
this.inlineEmbed = inlineEmbed;
if (
this.modalRequestSubscribed &&
this.featureService.has('media-modal') &&
this.mediaSource === 'youtube'
) {
if (this.inlineEmbed && this.inlineEmbed.htmlProvisioner) {
this.inlineEmbed.htmlProvisioner().then(html => {
this.inlineEmbed.html = html;
this.detectChanges();
});
// @todo: catch any error here and forcefully window.open to destination
}
}
}
action($event) {
if (
this.modalRequestSubscribed &&
this.featureService.has('media-modal') &&
this.mediaSource === 'youtube'
) {
$event.preventDefault();
$event.stopPropagation();
this.mediaModalRequested.emit();
return;
}
if (this.inlineEmbed && !this.embeddedInline) {
$event.preventDefault();
$event.stopPropagation();
......@@ -120,6 +161,7 @@ export class MindsRichEmbed {
if ((matches = youtube.exec(url)) !== null) {
if (matches[1]) {
this.mediaSource = 'youtube';
return {
id: `video-youtube-${matches[1]}`,
className:
......@@ -138,12 +180,13 @@ export class MindsRichEmbed {
if ((matches = vimeo.exec(url)) !== null) {
if (matches[1]) {
this.mediaSource = 'vimeo';
return {
id: `video-vimeo-${matches[1]}`,
className:
'm-rich-embed-video m-rich-embed-video-iframe m-rich-embed-video-vimeo',
html: this.sanitizer.bypassSecurityTrustHtml(`<iframe
src="https://player.vimeo.com/video/${matches[1]}?autoplay=1&title=0&byline=0&portrait=0"
src="https://player.vimeo.com/video/${matches[1]}?title=0&byline=0&portrait=0"
frameborder="0"
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>`),
playable: true,
......@@ -156,6 +199,7 @@ export class MindsRichEmbed {
if ((matches = soundcloud.exec(url)) !== null) {
if (matches[1]) {
this.mediaSource = 'soundcloud';
return {
id: `audio-soundcloud-${matches[1]}`,
className:
......@@ -183,6 +227,7 @@ export class MindsRichEmbed {
if ((matches = spotify.exec(url)) !== null) {
if (matches[1]) {
this.mediaSource = 'spotify';
return {
id: `audio-spotify-${matches[1]}`,
className:
......@@ -207,7 +252,7 @@ export class MindsRichEmbed {
if (!id) {
return null;
}
this.mediaSource = 'giphy';
return {
id: `image-giphy-${matches[1]}`,
className:
......@@ -225,7 +270,11 @@ export class MindsRichEmbed {
}
hasInlineContentLoaded() {
return this.embeddedInline && this.inlineEmbed && this.inlineEmbed.html;
return this.featureService.has('media-modal')
? !this.modalRequestSubscribed &&
this.inlineEmbed &&
this.inlineEmbed.html
: this.embeddedInline && this.inlineEmbed && this.inlineEmbed.html;
}
detectChanges() {
......
......@@ -159,10 +159,12 @@
*ngIf="getCurrentUser()"
(click)="toggleTheme()"
>
<i class="material-icons" *ngIf="isDark">brightness_7</i>
<i class="material-icons" *ngIf="!isDark">brightness_2</i>
<span i18n *ngIf="isDark">Light Mode</span>
<span i18n *ngIf="!isDark">Dark Mode</span>
<div>
<i class="material-icons" *ngIf="isDark">brightness_7</i>
<i class="material-icons" *ngIf="!isDark">brightness_2</i>
<span i18n *ngIf="isDark">Light Mode</span>
<span i18n *ngIf="!isDark">Dark Mode</span>
</div>
</li>
</ng-container>
......
......@@ -219,7 +219,12 @@
<span i18n="@@M__COMMON__CONFIRM_18">Click to confirm you are 18+</span>
</span>
</div>
<minds-rich-embed [src]="activity" [maxheight]="480"></minds-rich-embed>
<minds-rich-embed
(mediaModalRequested)="openModal()"
[src]="activity"
[maxheight]="480"
>
</minds-rich-embed>
</div>
<div
......
<div class="m-mediaModal__wrapper">
<div class="m-mediaModal__wrapper" data-cy="data-minds-media-modal">
<div class="m-mediaModal__theater" (click)="clickedModal($event)">
<div
class="m-mediaModal m-mediaModal__clearFix"
......@@ -15,7 +15,10 @@
(touchend)="showOverlaysOnTablet()"
>
<!-- LOADING PANEL -->
<div class="m-mediaModal__loadingPanel" *ngIf="isLoading">
<div
class="m-mediaModal__loadingPanel"
*ngIf="isLoading && contentType !== 'rich-embed'"
>
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</div>
......@@ -65,6 +68,15 @@
</m-video>
</div>
<!-- RICH-EMBED -->
<div
class="m-mediaModal__mediaWrapper m-mediaModal__mediaWrapper--richEmbed"
*ngIf="contentType === 'rich-embed'"
>
<minds-rich-embed [src]="entity" [maxheight]="480">
</minds-rich-embed>
</div>
<!-- MEDIA: BLOG -->
<div
class="m-mediaModal__mediaWrapper m-mediaModal__mediaWrapper--blog"
......@@ -83,12 +95,12 @@
<!-- OVERLAY -->
<div
class="m-mediaModal__overlayContainer"
*ngIf="overlayVisible"
*ngIf="overlayVisible && contentType !== 'rich-embed'"
@fastFadeAnimation
>
<div
class="m-mediaModal__overlayTitleWrapper"
*ngIf="this.contentType !== 'blog'"
*ngIf="contentType !== 'blog'"
>
<!-- TITLE -->
<span
......
......@@ -99,6 +99,14 @@ m-overlay-modal {
}
}
.m-mediaModal__mediaWrapper--richEmbed {
width: 100%;
.meta {
display: none;
}
}
.m-mediaModal__mediaWrapper--blog {
line-height: initial;
overflow-y: auto;
......
import {
Component,
HostListener,
Injector,
Input,
OnDestroy,
OnInit,
ViewChild,
SkipSelf,
Injector,
ViewChild,
} from '@angular/core';
import { Location } from '@angular/common';
import { Event, NavigationStart, Router } from '@angular/router';
......@@ -26,6 +26,7 @@ import isMobileOrTablet from '../../../helpers/is-mobile-or-tablet';
import { ActivityService } from '../../../common/services/activity.service';
import { SiteService } from '../../../common/services/site.service';
import { ClientMetaService } from '../../../common/services/client-meta.service';
import { FeaturesService } from '../../../services/features.service';
export type MediaModalParams = {
redirectUrl?: string;
......@@ -119,66 +120,78 @@ export class MediaModalComponent implements OnInit, OnDestroy {
@ViewChild(MindsVideoComponent, { static: false })
videoComponent: MindsVideoComponent;
get videoDirectSrc() {
const sources = [
videoDirectSrc = [];
videoTorrentSrc = [];
constructor(
public session: Session,
public analyticsService: AnalyticsService,
private overlayModal: OverlayModalService,
private router: Router,
private location: Location,
private site: SiteService,
private clientMetaService: ClientMetaService,
private featureService: FeaturesService,
@SkipSelf() injector: Injector
) {
this.clientMetaService
.inherit(injector)
.setSource('single')
.setMedium('modal');
}
updateSources() {
this.videoDirectSrc = [
{
res: '720',
uri:
'api/v1/media/' + this.entity.entity_guid + '/play?s=modal&res=720',
'api/v1/media/' +
this.entity.entity_guid +
'/play/' +
Date.now() +
'?s=modal&res=720',
type: 'video/mp4',
},
{
res: '360',
uri: 'api/v1/media/' + this.entity.entity_guid + '/play?s=modal',
uri:
'api/v1/media/' +
this.entity.entity_guid +
'/play/' +
Date.now() +
'?s=modal',
type: 'video/mp4',
},
];
this.videoTorrentSrc = [
{ res: '720', key: this.entity.entity_guid + '/720.mp4' },
{ res: '360', key: this.entity.entity_guid + '/360.mp4' },
];
if (this.entity.custom_data.full_hd) {
sources.push({
this.videoDirectSrc.unshift({
res: '1080',
uri:
'api/v1/media/' + this.entity.entity_guid + '/play?s=modal&res=1080',
'api/v1/media/' +
this.entity.entity_guid +
'/play/' +
Date.now() +
'?s=modal&res=1080',
type: 'video/mp4',
});
}
return sources;
}
get videoTorrentSrc() {
const sources = [
{ res: '720', key: this.entity.entity_guid + '/720.mp4' },
{ res: '360', key: this.entity.entity_guid + '/360.mp4' },
];
if (this.entity.custom_data.full_hd) {
sources.push({ res: '1080', key: this.entity.entity_guid + '/1080.mp4' });
this.videoTorrentSrc.unshift({
res: '1080',
key: this.entity.entity_guid + '/1080.mp4',
});
}
return sources;
}
constructor(
public session: Session,
public analyticsService: AnalyticsService,
private overlayModal: OverlayModalService,
private router: Router,
private location: Location,
private site: SiteService,
private clientMetaService: ClientMetaService,
@SkipSelf() injector: Injector
) {
this.clientMetaService
.inherit(injector)
.setSource('single')
.setMedium('modal');
}
ngOnInit() {
// Prevent dismissal of modal when it's just been opened
this.isOpenTimeout = setTimeout(() => (this.isOpen = true), 20);
switch (this.entity.type) {
case 'activity':
this.title =
......@@ -190,6 +203,8 @@ export class MediaModalComponent implements OnInit, OnDestroy {
? this.entity.thumbnails.xlarge
: null;
this.updateSources();
switch (this.entity.custom_type) {
case 'video':
this.contentType = 'video';
......@@ -200,12 +215,41 @@ export class MediaModalComponent implements OnInit, OnDestroy {
? this.entity.custom_data.dimensions.height
: 720;
this.entity.thumbnail_src = this.entity.custom_data.thumbnail_src;
this.updateSources();
break;
case 'batch':
this.contentType = 'image';
this.entity.width = this.entity.custom_data[0].width;
this.entity.height = this.entity.custom_data[0].height;
break;
default:
if (
this.featureService.has('media-modal') &&
this.entity.perma_url &&
this.entity.title &&
!this.entity.entity_guid
) {
this.contentType = 'rich-embed';
this.entity.width = this.entity.custom_data.dimensions
? this.entity.custom_data.dimensions.width
: 1280;
this.entity.height = this.entity.custom_data.dimensions
? this.entity.custom_data.dimensions.height
: 720;
this.entity.thumbnail_src = this.entity.custom_data.thumbnail_src;
break;
} else {
// Modal not implemented, redirect.
this.router.navigate([
this.entity.route
? `/${this.entity.route}`
: `/blog/view/${this.entity.guid}`,
]);
// Close modal.
this.clickedBackdrop(null);
}
}
break;
case 'object':
switch (this.entity.subtype) {
......@@ -213,6 +257,9 @@ export class MediaModalComponent implements OnInit, OnDestroy {
this.contentType = 'video';
this.title = this.entity.title;
this.entity.entity_guid = this.entity.guid;
this.entity.custom_data = {
full_hd: this.entity.flags.full_hd,
};
break;
case 'image':
this.contentType = 'image';
......@@ -243,12 +290,10 @@ export class MediaModalComponent implements OnInit, OnDestroy {
if (this.redirectUrl) {
this.pageUrl = this.redirectUrl;
} else if (this.contentType !== 'blog') {
this.pageUrl = `/media/${this.entity.entity_guid}`;
} else if (this.contentType === 'rich-embed') {
this.pageUrl = `/newsfeed/${this.entity.guid}`;
} else {
this.pageUrl = this.entity.route
? `/${this.entity.route}`
: `/blog/view${this.entity.guid}`;
this.pageUrl = `/media/${this.entity.entity_guid}`;
}
this.boosted = this.entity.boosted || this.entity.p2p_boosted || false;
......@@ -559,7 +604,6 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// Show overlay and video controls
onMouseEnterStage() {
this.overlayVisible = true;
if (this.contentType === 'video') {
// Make sure progress bar seeker is updating when video controls are visible
this.videoComponent.stageHover = true;
......
import { Component, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { timer, Subscription } from 'rxjs';
import { Subscription, timer } from 'rxjs';
import { Client } from '../../../../services/api';
import { Session } from '../../../../services/session';
......@@ -71,8 +71,18 @@ export class MediaTheatreComponent {
@ViewChild(MindsVideoComponent, { static: false })
videoComponent: MindsVideoComponent;
get videoDirectSrc() {
const sources = [
videoDirectSrc = [];
videoTorrentSrc = [];
constructor(
public session: Session,
public client: Client,
public router: Router,
private recommended: RecommendedService
) {}
updateSources() {
this.videoDirectSrc = [
{
res: '720',
uri: 'api/v1/media/' + this.object.guid + '/play?s=modal&res=720',
......@@ -85,40 +95,29 @@ export class MediaTheatreComponent {
},
];
this.videoTorrentSrc = [
{ res: '720', key: this.object.guid + '/720.mp4' },
{ res: '360', key: this.object.guid + '/360.mp4' },
];
if (this.object.flags.full_hd) {
sources.push({
this.videoDirectSrc.unshift({
res: '1080',
uri: 'api/v1/media/' + this.object.guid + '/play?s=modal&res=1080',
type: 'video/mp4',
});
}
return sources;
}
get videoTorrentSrc() {
const sources = [
{ res: '720', key: this.object.guid + '/720.mp4' },
{ res: '360', key: this.object.guid + '/360.mp4' },
];
if (this.object.flags.full_hd) {
sources.push({ res: '1080', key: this.object.guid + '/1080.mp4' });
this.videoTorrentSrc.unshift({
res: '1080',
key: this.object.guid + '/1080.mp4',
});
}
return sources;
}
constructor(
public session: Session,
public client: Client,
public router: Router,
private recommended: RecommendedService
) {}
set _object(value: any) {
if (!value.guid) return;
this.object = value;
this.updateSources();
}
getThumbnail() {
......
......@@ -10,7 +10,7 @@ m-modal-signup-on-action {
left: 0;
width: 100%;
height: 100%;
z-index: 101;
z-index: 9999990;
}
.m-modal-container {
......
......@@ -2,6 +2,7 @@
[open]="open"
(closed)="onClose($event)"
*ngIf="!session.isLoggedIn() || display != 'initial'"
(click)="$event.stopPropagation()"
>
<div
class="mdl-card__title"
......
......@@ -6,7 +6,10 @@
></m-pro--channel--categories>
<div class="m-proChannelHome__section" *ngIf="featuredContent?.length">
<div class="m-proChannelHome__featuredContent">
<div
class="m-proChannelHome__featuredContent"
[class.m-proChannelHome__featuredContent--prominent]="false"
>
<m-pro--channel-tile
*ngFor="let entity of featuredContent"
[entity]="entity"
......
......@@ -56,15 +56,17 @@ m-proChannel__home {
.m-proChannelHome__featuredContent {
grid-template-columns: repeat(2, 1fr);
*:nth-child(1) {
grid-column: span 2;
}
@media screen and (max-width: $max-mobile) {
grid-template-columns: 100%;
}
&.m-proChannelHome__featuredContent--prominent {
*:nth-child(1) {
grid-column: initial;
grid-column: span 2;
@media screen and (max-width: $max-mobile) {
grid-column: initial;
}
}
}
}
......
......@@ -31,7 +31,7 @@
<i class="material-icons">people</i>
<div class="m-token-contributions--chart--contribution-text">
<span>Referrals</span>
<span>+50</span>
<span>+1</span>
</div>
</div>
<div class="m-token-contributions--chart--contribution">
......
......@@ -8,7 +8,7 @@
If your friend signs up for Minds within 24 hours of clicking the link you
shared with them, they’ll be added to your pending referrals. Once they sign
up for the rewards program by setting up their Minds wallet, the referral is
complete and you’ll <span>both</span> get +50 added to your contribution
complete and you’ll <span>both</span> get +1 added to your contribution
scores! (Hint: check out
<a [routerLink]="['/wallet/tokens/101']">Token 101</a> to learn how
contribution scores are converted into tokens)
......
......@@ -22,9 +22,6 @@ export class FeaturesService {
if (typeof this._features[feature] === 'undefined') {
if (isDevMode() && !this._hasWarned(feature)) {
console.warn(
`[FeaturedService] Feature '${feature}' is not declared. Assuming false.`
);
this._warnedCache[feature] = Date.now();
}
......