...
 
Commits (8)
......@@ -70,7 +70,7 @@ export class MindsHttpClient {
* Build the options
*/
private buildOptions(options: Object) {
const XSRF_TOKEN = this.cookie.get('XSRF-TOKEN');
const XSRF_TOKEN = this.cookie.get('XSRF-TOKEN') || '';
const headers = new HttpHeaders({
'X-XSRF-TOKEN': XSRF_TOKEN,
......
......@@ -153,7 +153,7 @@ export class EntitiesService {
if (!this.entities.has(urn)) {
this.entities.set(urn, new BehaviorSubject(null));
}
console.warn(`Entity ${urn} not found`);
this.entities.get(urn).error("Not found");
}
static _(client: Client, blockListService: BlockListService) {
......
......@@ -53,6 +53,8 @@ export class UpdateMarkersService {
if (this.isLoggedIn) {
this.get()
.subscribe((markers: any) => {
if (!markers)
return;
this.data = markers; //cache
for (let i in this.data) {
......
......@@ -471,7 +471,9 @@ export class Activity implements OnInit {
class: 'm-overlayModal--media'
}).present();
} else {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
if (this.activity.custom_type !== 'video'){
this.router.navigate([`/media/${this.activity.entity_guid}`]);
}
}
}
......
......@@ -181,7 +181,9 @@ export class Remind {
class: 'm-overlayModal--media'
}).present();
} else {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
if (this.activity.custom_type !== 'video') {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
}
}
}
}
......@@ -80,10 +80,6 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
this._emitCanPlayThrough();
};
protected _dblClick = () => {
this.requestFullScreen();
};
protected _onError = e => {
this.loading = false;
this.detectChanges();
......@@ -131,7 +127,6 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
ngOnInit() {
const player = this.getPlayer();
player.addEventListener('dblclick', this._dblClick);
player.addEventListener('playing', this._emitPlay);
player.addEventListener('pause', this._emitPause);
player.addEventListener('ended', this._emitEnd);
......@@ -161,7 +156,6 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
const player = this.getPlayer();
if (player) {
player.removeEventListener('dblclick', this._dblClick);
player.removeEventListener('playing', this._emitPlay);
player.removeEventListener('pause', this._emitPause);
player.removeEventListener('ended', this._emitEnd);
......
......@@ -34,7 +34,7 @@
></m-video--torrent-player>
<ng-container *ngIf="playerRef">
<i *ngIf="!playerRef.isPlaying() && !playerRef.isLoading() || isActivity && metadataLoaded"
<i *ngIf="(!playerRef.isPlaying() && !playerRef.isLoading()) || (isActivity && metadataLoaded && !playerRef.isPlaying())"
class="material-icons minds-video-play-icon"
(click)="isModal ? toggle() : requestMediaModal()"
>play_circle_outline</i>
......
......@@ -22,6 +22,10 @@ import { webtorrentServiceMock } from '../../../../../tests/webtorrent-service-m
import { MindsPlayerInterface } from './players/player.interface';
import { MediaModalComponent } from '../../modal/modal.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FeaturesService } from '../../../../services/features.service';
import { featuresServiceMock } from '../../../../../tests/features-service-mock.spec';
@Component({
selector: 'm-video--direct-http-player',
......@@ -174,7 +178,8 @@ describe('MindsVideo', () => {
providers: [
{ provide: ScrollService, useValue: scrollServiceMock },
{ provide: Client, useValue: clientMock },
{ provide: WebtorrentService, useValue: webtorrentServiceMock }
{ provide: WebtorrentService, useValue: webtorrentServiceMock },
{ provide: FeaturesService, useValue: featuresServiceMock },
]
})
.compileComponents(); // compile template and css
......
import { Component, ElementRef, Input, Output, EventEmitter, ViewChild, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { Component, ElementRef, Input, Output, EventEmitter, ViewChild, ChangeDetectorRef, OnDestroy, HostListener } from '@angular/core';
import { Router } from '@angular/router';
import { trigger, state, style, animate, transition } from '@angular/animations';
import { MindsVideoProgressBar } from './progress-bar/progress-bar.component';
......@@ -9,6 +9,7 @@ import { ScrollService } from '../../../../services/ux/scroll';
import { MindsPlayerInterface } from './players/player.interface';
import { WebtorrentService } from '../../../webtorrent/webtorrent.service';
import { SOURCE_CANDIDATE_PICK_ZIGZAG, SourceCandidates } from './source-candidates';
import { FeaturesService } from '../../../../services/features.service';
import isMobile from '../../../../helpers/is-mobile';
@Component({
......@@ -16,7 +17,7 @@ import isMobile from '../../../../helpers/is-mobile';
host: {
'(mouseenter)': 'onMouseEnter()',
'(mouseleave)': 'onMouseLeave()',
'[class.clickable]':'canPlayThrough'
'[class.clickable]':'metadataLoaded'
},
templateUrl: 'video.component.html',
animations: [
......@@ -43,6 +44,8 @@ export class MindsVideoComponent implements OnDestroy {
@Input() poster: string = '';
@Input() isActivity: boolean = false;
@Input() isModal: boolean = false;
// @Input() isTheatre: boolean = false;
@Output('finished') finished: EventEmitter<any> = new EventEmitter();
......@@ -82,6 +85,7 @@ export class MindsVideoComponent implements OnDestroy {
stopSeekerTimeout: any = null;
metadataLoaded: boolean = false;
canPlayThrough: boolean = false;
isFullscreen: boolean = false;
current: { type: 'torrent' | 'direct-http', src: string };
protected candidates: SourceCandidates = new SourceCandidates();
......@@ -100,6 +104,7 @@ export class MindsVideoComponent implements OnDestroy {
public client: Client,
protected webtorrent: WebtorrentService,
protected cd: ChangeDetectorRef,
protected featuresService: FeaturesService,
private router: Router,
) { }
......@@ -185,17 +190,18 @@ export class MindsVideoComponent implements OnDestroy {
}
onMouseEnter() {
if (this.isActivity) {
if (this.isActivity && this.featuresService.has('media-modal')) {
return;
}
this.progressBar.getSeeker();
this.progressBar.enableKeyControls();
this.showControls = true;
if (this.videoMetadataLoaded){
this.progressBar.getSeeker();
this.progressBar.enableKeyControls();
this.showControls = true;
}
}
onMouseLeave() {
if (this.stageHover || this.isActivity) {
if (this.featuresService.has('media-modal') && (this.stageHover || this.isActivity)) {
return;
}
......@@ -392,17 +398,22 @@ export class MindsVideoComponent implements OnDestroy {
}
requestMediaModal() {
if (!this.canPlayThrough) {
if (!this.metadataLoaded) {
return;
}
if (this.isModal) {
let isMediaPage = false;
if (!this.isModal && !this.isActivity) {
isMediaPage = true;
}
if (this.isModal || (!isMediaPage && !this.featuresService.has('media-modal'))) {
this.toggle();
return;
}
// Mobile (not tablet) users go to media page instead of modal
if (isMobile() && Math.min(screen.width, screen.height) < 768) {
if (isMobile() && !isMediaPage && Math.min(screen.width, screen.height) < 768) {
this.router.navigate([`/media/${this.guid}`]);
}
......@@ -413,6 +424,61 @@ export class MindsVideoComponent implements OnDestroy {
this.cd.markForCheck();
this.cd.detectChanges();
}
// * FULLSCREEN * --------------------------------------------------------------------------------
// Listen for fullscreen change event in case user enters/exits full screen without clicking button
@HostListener('document:fullscreenchange', ['$event'])
@HostListener('document:webkitfullscreenchange', ['$event'])
@HostListener('document:mozfullscreenchange', ['$event'])
@HostListener('document:MSFullscreenChange', ['$event'])
onFullscreenChange(event) {
if ( !document.fullscreenElement &&
!document['webkitFullscreenElement'] &&
!document['mozFullScreenElement'] &&
!document['msFullscreenElement'] ) {
this.isFullscreen = false;
} else {
this.isFullscreen = true;
}
}
toggleFullscreen() {
const elem = document.querySelector('m-video');
// this.fullscreenHovering = false;
// If fullscreen is not already enabled
if ( !document['fullscreenElement'] &&
!document['webkitFullscreenElement'] &&
!document['mozFullScreenElement'] &&
!document['msFullscreenElement'] ) {
// Request full screen
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem['webkitRequestFullscreen']) {
elem['webkitRequestFullscreen']();
} else if (elem['mozRequestFullScreen']) {
elem['mozRequestFullScreen']();
} else if (elem['msRequestFullscreen']) {
elem['msRequestFullscreen']();
}
this.isFullscreen = true;
return;
}
// If fullscreen is already enabled, exit it
if ( document.exitFullscreen ) {
document.exitFullscreen();
} else if (document['webkitExitFullscreen']) {
document['webkitExitFullscreen']();
} else if (document['mozCancelFullScreen']) {
document['mozCancelFullScreen']();
} else if (document['msExitFullscreen']) {
document['msExitFullscreen']();
}
this.isFullscreen = false;
}
}
export { VideoAds } from './ads.component';
......@@ -596,3 +596,9 @@ m-media--grid{
.m-media-content--button-boost{
padding-left: $minds-padding;
}
.m-comment__attachment {
img, minds-video {
max-width: 50%;
}
}
......@@ -12,7 +12,7 @@
[style.line-height]="stageHeight + 'px'"
(mouseenter)="onMouseEnterStage()"
(mouseleave)="onMouseLeaveStage()"
(touchend)="showOverlays()"
(touchend)="showOverlaysOnTablet()"
>
<!-- LOADING PANEL -->
<div class="m-mediaModal__loadingPanel" *ngIf="isLoading">
......@@ -31,8 +31,8 @@
<img class="m-mediaModal__media--image"
[src]="thumbnail"
(load)="isLoaded()"
[style.height]="entity.height + 'px'"
[style.width]="entity.width + 'px'"
[style.height]="entityHeight + 'px'"
[style.width]="entityWidth + 'px'"
/>
</div>
......@@ -43,8 +43,8 @@
[style.height]="mediaHeight + 'px'"
>
<m-video class="m-mediaModal__media--video"
[style.height]="entity.height + 'px'"
[style.width]="entity.width + 'px'"
[style.height]="entityHeight + 'px'"
[style.width]="entityWidth + 'px'"
[isModal]="true"
[autoplay]="true"
[muted]="false"
......@@ -62,22 +62,28 @@
<!-- OVERLAY -->
<div class="m-mediaModal__overlayContainer"
*ngIf="showOverlay"
*ngIf="overlayVisible"
@fastFadeAnimation
>
<div class="m-mediaModal__overlayTitleWrapper">
<!-- TITLE -->
<span class="m-mediaModal__overlayTitle m-mediaModal__overlayTitle--notFullscreen" *ngIf="!isFullscreen">
<a [routerLink]="['/media', entity.entity_guid]">{{title}}</a>
<a [routerLink]="['/media', entity.entity_guid]"
(click)="$event.stopPropagation()"
>{{title}}</a>
</span>
<!-- TITLE: FULLSCREEN -->
<span class="m-mediaModal__overlayTitle m-mediaModal__overlayTitle--fullscreen" *ngIf="isFullscreen">
<a [routerLink]="['/', entity.ownerObj.username]">
<a [routerLink]="['/', entity.ownerObj.username]"
(click)="$event.stopPropagation()"
>
<img class="avatar" [src]="minds.cdn_url + 'icon/' + entity.ownerObj.guid + '/small/' + ownerIconTime" class="mdl-shadow--2dp"/>
<span title={{entity.ownerObj.name}}>{{entity.ownerObj.name}}</span>
</a>
<div class="m-mediaModal__overlayTitleSeparator"></div>
<a [routerLink]="['/media', entity.entity_guid]">{{title}}</a>
<a [routerLink]="['/media', entity.entity_guid]"
(click)="$event.stopPropagation()"
>{{title}}</a>
</span>
</div>
<!-- FULLSCREEN BUTTON -->
......
......@@ -55,6 +55,8 @@ export class MediaModalComponent implements OnInit, OnDestroy {
stageHeight: number;
mediaWidth: number;
mediaHeight: number;
entityWidth: number;
entityHeight: number;
maxStageWidth: number;
maxHeight: number;
......@@ -76,15 +78,15 @@ export class MediaModalComponent implements OnInit, OnDestroy {
isOpen: boolean = false;
isOpenTimeout: any = null;
showOverlay: boolean = false;
overlayVisible: boolean = false;
tabletOverlayTimeout: any = null;
routerSubscription: Subscription;
@Input('entity') set data(entity) {
this.entity = entity;
this.entity.width = 0;
this.entity.height = 0;
this.entityWidth = 0;
this.entityHeight = 0;
}
// Used to make sure video progress bar seeker / hover works
......@@ -145,7 +147,6 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// (but don't actually redirect)
this.location.replaceState(`/media/${this.entity.entity_guid}`);
// When user clicks a link from inside the modal
this.routerSubscription = this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
......@@ -168,16 +169,16 @@ export class MediaModalComponent implements OnInit, OnDestroy {
if (!this.isVideo) {
// Image
this.entity.width = this.entity.custom_data[0].width;
this.entity.height = this.entity.custom_data[0].height;
this.entityWidth = this.entity.custom_data[0].width;
this.entityHeight = this.entity.custom_data[0].height;
this.thumbnail = `${this.minds.cdn_url}fs/v1/thumbnail/${this.entity.entity_guid}/xlarge`;
} else {
this.entity.width = this.entity.custom_data.dimensions.width;
this.entity.height = this.entity.custom_data.dimensions.height;
this.entityWidth = this.entity.custom_data.dimensions.width;
this.entityHeight = this.entity.custom_data.dimensions.height;
this.thumbnail = this.entity.custom_data.thumbnail_src; // Not currently used
}
this.aspectRatio = this.entity.width / this.entity.height;
this.aspectRatio = this.entityWidth / this.entityHeight;
this.calculateDimensions();
}
......@@ -211,7 +212,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// If black stage background is visible on top/bottom, each strip should be at least 20px high
const heightDiff = this.stageHeight - this.mediaHeight;
if ( 0 < heightDiff && heightDiff <= this.padding * 2) {
this.stageHeight += 40;
this.stageHeight += (this.padding * 2);
}
} else { // isFullscreen
......@@ -221,8 +222,15 @@ export class MediaModalComponent implements OnInit, OnDestroy {
this.stageWidth = windowWidth;
this.stageHeight = windowHeight;
// Set mediaHeight as tall as possible but not taller than instrinsic height
this.mediaHeight = this.entity.height < windowHeight ? this.entity.height : windowHeight;
if (this.entity.custom_type === 'image') {
// For images, set mediaHeight as tall as possible but not taller than instrinsic height
this.mediaHeight = this.entityHeight < windowHeight ? this.entityHeight : windowHeight;
} else {
// It's ok if videos are taller than intrinsic height
this.mediaHeight = windowHeight;
}
this.mediaWidth = this.scaleWidth();
if ( this.mediaWidth > windowWidth ) {
......@@ -233,8 +241,8 @@ export class MediaModalComponent implements OnInit, OnDestroy {
}
if (this.isVideo) {
this.entity.height = this.mediaHeight;
this.entity.width = this.mediaWidth;
this.entityHeight = this.mediaHeight;
this.entityWidth = this.mediaWidth;
}
this.modalWidth = this.stageWidth + this.contentWidth;
......@@ -248,13 +256,13 @@ export class MediaModalComponent implements OnInit, OnDestroy {
this.stageHeight = Math.max(this.maxHeight, this.minStageHeight);
// Set mediaHeight as tall as stage but no larger than intrinsic height
if (!this.isVideo && this.entity.height < this.stageHeight) {
if (!this.isVideo && this.entityHeight < this.stageHeight) {
// Image is shorter than stage; scale down stage
this.mediaHeight = this.entity.height;
this.mediaHeight = this.entityHeight;
this.stageHeight = Math.max(this.mediaHeight, this.minStageHeight);
} else {
// Image is taller than stage; scale it down so it fits inside stage
// All videos should be as tall as possible but not taller than stage
// Either: Image is taller than stage; scale it down so it fits inside stage
// Or: Video should be as tall as possible but not taller than stage
this.mediaHeight = this.stageHeight;
}
......@@ -276,13 +284,11 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// shrink vertically until it hits minStageHeight
// When window is narrower than this, start to shrink height
const verticalShrinkWidthThreshold = this.mediaWidth + this.contentWidth + (this.padding * 4); // + 2;
const verticalShrinkWidthThreshold = this.mediaWidth + this.contentWidth + (this.padding * 4);
const widthDiff = verticalShrinkWidthThreshold - window.innerWidth;
// Is window narrow enough to start shrinking vertically?
if ( widthDiff >= 1 ) {
if (widthDiff >= 1) {
// What mediaHeight would be if it shrunk proportionally to difference in width
const mediaHeightPreview = Math.round((this.mediaWidth - widthDiff) / this.aspectRatio);
......@@ -293,7 +299,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
this.stageHeight = this.mediaHeight;
} else {
this.stageHeight = this.minStageHeight;
this.mediaHeight = Math.min(this.minStageHeight, this.entity.height);
this.mediaHeight = Math.min(this.minStageHeight, this.entityHeight);
this.mediaWidth = this.scaleWidth();
}
}
......@@ -316,7 +322,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
onFullscreenChange(event) {
this.calculateDimensions();
if ( !document.fullscreenElement &&
!document['webkitFullScreenElement'] &&
!document['webkitFullscreenElement'] &&
!document['mozFullScreenElement'] &&
!document['msFullscreenElement'] ) {
this.isFullscreen = false;
......@@ -332,10 +338,10 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// If fullscreen is not already enabled
if ( !document['fullscreenElement'] &&
!document['webkitFullScreenElement'] &&
!document['webkitFullscreenElement'] &&
!document['mozFullScreenElement'] &&
!document['msFullscreenElement'] ) {
// Request full screen
// Request full screen
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem['webkitRequestFullscreen']) {
......@@ -384,7 +390,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// Show overlay and video controls
onMouseEnterStage() {
this.showOverlay = true;
this.overlayVisible = true;
if (this.isVideo) {
// Make sure progress bar seeker is updating when video controls are visible
......@@ -393,9 +399,8 @@ export class MediaModalComponent implements OnInit, OnDestroy {
}
}
// Hide overlay and video controls
onMouseLeaveStage() {
this.showOverlay = false;
this.overlayVisible = false;
if (this.isVideo) {
// Stop updating progress bar seeker when controls aren't visible
......@@ -407,7 +412,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
// * TABLETS ONLY: SHOW OVERLAY & VIDEO CONTROLS * -------------------------------------------
// Briefly display title overlay and video controls when finished loading and stage touch
showOverlays() {
showOverlaysOnTablet() {
this.onMouseEnterStage();
if (this.tabletOverlayTimeout) {
......@@ -425,7 +430,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
this.isLoading = false;
if ( this.isTablet ) {
this.showOverlays();
this.showOverlaysOnTablet();
}
}
......
<div class="m-marketing m-mobile--marketing">
<!--Top / Hero -->
<div class="m-marketing--hero">
<div class="m-marketing--hero--video">
<img [src]="minds.cdn_assets_url + 'assets/photos/circles.png'">
</div>
<div class="m-marketing--hero--inner">
<div class="m-marketing--hero--slogans">
<h2>
Minds Android App
</h2>
<h1>
Minds Mobile App
</h1>
</div>
<div class="m-marketing--hero--actions">
<i class="material-icons m-mobile__giantDroid">android</i>
<!-- Space for an icon -->
</div>
</div>
</div>
<section class="m-marketing--section mdl-color--white" style="padding-bottom: 0; padding-top: 0">
<div class="m-marketing--downloadPlatform">
<h2>
For Android:
</h2>
<!--Android APK -->
<div class="m-marketing--downloadOption">
<div class="m-marketing--downloadButton">
<a [href]="latestRelease.href" target="_blank"><img [src]="minds.cdn_assets_url + 'assets/marketing/mobile-dl-button.png'"/></a>
<span>(recommended)</span>
</div>
<p>Download the mobile app directly from Minds for the best experience. All content is accessible.
<p>In order to install this version you must:</p>
<ul>
<li>Update Phone settings to "enable downloads from unverified source"</li>
<li>Download and install!</li>
</ul>
</div>
<!--Play Store -->
<div class="m-marketing--downloadOption">
<div class="m-marketing--downloadButton">
<a href='https://play.google.com/store/apps/details?id=com.minds.mobile&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1' target="_blank"><img alt='Get it on Google Play' src='https://play.google.com/intl/en_gb/badges/images/generic/en_badge_web_generic.png'/></a>
</div>
<p>You may also download the mobile app from the Google Play Store. Due to the Google Play Store terms of service, NSFW content is not accessible in this version.</p>
</div>
</div>
<!--iOS Download -->
<div class="m-marketing--downloadPlatform">
<h2>
For iPhone:
</h2>
<div class="m-marketing--downloadOption">
<div class="m-marketing--downloadButton m-marketing--downloadButton--iOS">
<a href="https://geo.itunes.apple.com/us/app/minds-com/id961771928?mt=8&amp;uo=6" target="_blank"><img alt="iOS App" src="https://devimages-cdn.apple.com/app-store/marketing/guidelines/images/badge-download-on-the-app-store.svg"></a>
</div>
<p>iPhone users may download the mobile app directly from the iOS App Store. All features of the Minds Token (wallet, boost, wire, etc) are not currently supported in this version. All content is accessible.</p>
</div>
</div>
<div class="m-marketing--section--subsection m-marketing--section--subsection--first">
<div class="m-marketing--section--subsection-container">
<div class="m-marketing--section--subsection-left m-marketing--section--subsection-text">
<div class="mdl-grid">
<h2>
Release History:
</h2>
<div class="mdl-cell mdl-cell--12-col m-mobile--marketing--spinner" *ngIf="inProgress">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</div>
......@@ -43,7 +75,6 @@
<ng-container *ngIf="release.unstable">Canary -</ng-container>
{{release.version}}
</h2>
<p>Released: {{release.timestamp * 1000 | date:'longDate'}} -
<a [href]="release.href" target="_blank">
Download
......
.m-mobile--marketing {
.m-marketing--hero {
padding:52px 0 !important;
margin-bottom: 32px;
padding: 0;
@media screen and (min-width: $max-mobile) {
padding:52px 0 !important;
}
.m-marketing--hero--inner {
padding-top: 52px;
padding-bottom: 52px;
flex-wrap: wrap;
padding: 52 0 52 0;
.m-marketing--hero--slogans h1 {
text-align: left;
padding-left: $minds-padding * 2;
font-size: xx-large;
font-weight: 700;
@media screen and (min-width: $min-tablet) {
margin-left: 120px;
font-size: 42px;
}
@include m-theme(){
color: themed($m-white-always);
}
}
}
img {
......@@ -24,7 +41,31 @@
}
.m-marketing--section--subsection--first .m-marketing--section--subsection-container {
padding-top: 32px !important;
padding-top: 0;
display: flex;
flex-direction: row;
align-items: center;
text-align: left;
width: 100%;
@media screen and (min-width: $min-tablet) {
width: unset;
margin: 0 25%;
}
.m-marketing--section--subsection-text {
max-width: 100%;
min-width: 100%;
margin: 0;
padding: 0;
h2 {
text-align: left;
font-size: x-large;
font-weight: 700;
padding: $minds-padding * 1.5 0;
margin: 0 0 ($minds-padding * 2) ($minds-padding * 2);
}
}
}
.m-marketing--hero--actions img {
......@@ -47,6 +88,72 @@
}
}
.m-marketing--section {
text-align: left;
margin-left: $minds-padding * 2;
h2 {
text-align: left;
font-size: x-large;
font-weight: 700;
padding-left: 12px;
}
.m-marketing--downloadPlatform {
@media screen and (min-width: $min-tablet) {
margin: 0 25%;
}
.m-marketing--downloadOption {
padding-bottom: $minds-padding * 2;
@media screen and (max-width: $min-tablet) {
padding-bottom: $minds-padding * 1;
}
.m-marketing--downloadButton--iOS {
img {
padding-left: 14px;
}
}
.m-marketing--downloadButton {
display: flex;
flex-wrap: wrap;
padding-bottom: $minds-padding * 4;
@media screen and (max-width: $min-tablet) {
padding-bottom: $minds-padding * 2;
}
img {
max-width: 200px;
min-width: 175px;
}
}
span {
font-size: large;
align-self: center;
padding-left: $minds-padding * 2;
font-weight: 400;
}
p {
padding: 0 ($minds-padding * 1.5);
line-height: 24pt;
@include m-theme(){
color: themed($m-grey-600);
}
}
li {
@include m-theme(){
color: themed($m-grey-600);
}
}
}
}
}
@media screen and (min-width: 500px) {
.m-marketing--hero--slogans h2 {
font-size: 60px !important;
......@@ -94,10 +201,3 @@
}
}
.m-mobile__giantDroid {
font-size: 152px;
@include m-theme(){
color: themed($m-white);
}
}
......@@ -2,6 +2,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from '@angular/co
import {MindsTitle} from '../../../services/ux/title';
import {Session} from '../../../services/session';
import {MobileService} from "../mobile.service";
import { first } from 'lodash';
@Component({
selector: 'm-mobile--marketing',
......@@ -15,6 +16,9 @@ export class MobileMarketingComponent {
releases: any[] = [];
inProgress: boolean = false;
error: string;
latestRelease: any = {
href: null,
};
constructor(
protected title: MindsTitle,
......@@ -26,18 +30,20 @@ export class MobileMarketingComponent {
ngOnInit() {
this.title.setTitle('Mobile');
this.user = this.session.getLoggedInUser();
this.load();
}
/**
* Gets releases for view.
*/
async load() {
try {
this.inProgress = true;
this.detectChanges();
this.releases = await this.service.getReleases();
this.latestRelease = await first(this.releases.filter(rel => rel.latest && !rel.unstable));
} catch (e) {
console.error(e);
this.error = e.message || 'Unknown error';
......
......@@ -269,7 +269,7 @@
<ng-template ngSwitchCase="remind">
<div *ngIf="notification.entityObj">
<a [routerLink]="['/newsfeed', notification.entityObj.guid]" *ngIf="notification.entityObj.type == 'object'">
<p><ng-container i18n="@@NOTIFICATIONS__NOTIFICATION__REMIND__REMINDED">{{notification.fromObj.name}} reminded</ng-container>
<p><ng-container i18n="@@NOTIFICATIONS__NOTIFICATION__REMIND__REMINDED">{{notification.fromObj.name}} reminded </ng-container>
<span class="pseudo-link mdl-color-text--blue-grey-400" *ngIf="notification.entityObj.title">{{notification.entityObj.title | excerpt}}</span>
<span class="pseudo-link mdl-color-text--blue-grey-400" *ngIf="!notification.entityObj.title" i18n="object belonging to current user@@NOTIFICATIONS__NOTIFICATION__OWN_OBJECT">your {{notification.entityObj.subtype}}</span>
</p>
......
......@@ -178,7 +178,7 @@ export class Client {
* Build the options
*/
private buildOptions(options: Object) {
const XSRF_TOKEN = this.cookie.get('XSRF-TOKEN');
const XSRF_TOKEN = this.cookie.get('XSRF-TOKEN') || '';
const headers = new HttpHeaders({
'X-XSRF-TOKEN': XSRF_TOKEN,
......
src/assets/marketing/mobile-dl-button.png

8.2 KB