...
 
Commits (2)
.m-overlay-modal {
position: fixed;
right: 2vw;
left: 2vw;
margin: auto;
z-index: 9999999;
top: 50%;
max-height: 98vh;
&:not(.m-overlayModal--media) {
position: fixed;
right: 2vw;
left: 2vw;
margin: auto;
z-index: 9999999;
top: 50%;
max-height: 98vh;
display: block;
box-sizing: border-box;
width: 100%;
max-width: 990px;
padding: ($minds-padding * 4);
display: block;
box-sizing: border-box;
width: 100%;
max-width: 990px;
padding: ($minds-padding * 4);
outline: 0;
border-radius: 6px;
outline: 0;
border-radius: 6px;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
font-family: 'Roboto', sans-serif;
font-family: 'Roboto', sans-serif;
@include m-theme(){
color: themed($m-grey-700);
background: none rgba(themed($m-white), 0.95);
}
@include m-theme(){
color: themed($m-grey-700);
background: none rgba(themed($m-white), 0.95);
}
&.m-overlay-modal--large {
max-width: 990px;
}
&.m-overlay-modal--large {
max-width: 990px;
}
&.m-overlay-modal--medium-large {
max-width: 800px;
}
&.m-overlay-modal--medium-large {
max-width: 800px;
}
&.m-overlay-modal--medium {
max-width: 600px;
}
&.m-overlay-modal--medium {
max-width: 600px;
}
&.m-overlay-modal--small {
max-width: 480px;
padding: 16px;
@include m-theme(){
background-color: themed($m-white);
&.m-overlay-modal--small {
max-width: 480px;
padding: 16px;
@include m-theme(){
background-color: themed($m-white);
}
}
}
&.m-overlay-modal--no-padding {
padding: 0px;
&.m-overlay-modal--no-padding {
padding: 0px;
.post {
margin: 0px;
.post {
margin: 0px;
}
}
}
&:not(.m-overlay-modal--top) {
transform: translateY(-50%);
}
&.m-overlay-modal--top {
top: 150px;
}
&:not(.m-overlay-modal--top):not(.m-overlayModal--media) {
transform: translateY(-50%);
}
&.m-overlay-modal--top {
top: 150px;
}
@media screen and (max-width: $max-mobile) {
padding: ($minds-padding * 2) ($minds-padding * 4);
}
@media screen and (max-width: $max-mobile) {
padding: ($minds-padding * 2) ($minds-padding * 4);
}
@media screen and (max-height: 850px) {
max-height: 96vh;
}
@media screen and (max-height: 850px) {
max-height: 96vh;
}
@media screen and (max-height: 750px) {
max-height: 98vh;
}
@media screen and (max-height: 750px) {
max-height: 98vh;
}
&::-webkit-scrollbar {
display: none;
&::-webkit-scrollbar {
display: none;
}
}
}
.m-overlay-modal--overflow {
......
import { Directive, ElementRef, ContentChild, ChangeDetectorRef } from '@angular/core';
import { Directive, ElementRef, ContentChild, ChangeDetectorRef, Input } from '@angular/core';
import { ReadMoreButtonComponent } from './button.component';
@Directive({
......@@ -8,28 +8,34 @@ export class ReadMoreDirective {
_element: any;
realHeight: any;
maxHeightAllowed: number = 320;
expandable: boolean = false;
@ContentChild(ReadMoreButtonComponent, { 'static': false}) button;
@Input() maxHeightAllowed: number;
constructor(private element: ElementRef, private cd: ChangeDetectorRef) {
this._element = element.nativeElement;
}
ngAfterViewInit() {
this.realHeight = this._element.clientHeight;
if (this.button && !this.button.content)
this.button.content = this;
if (this.realHeight > this.maxHeightAllowed) {
this._element.style.maxHeight = this.maxHeightAllowed + 'px';
this._element.style.position = 'relative';
setTimeout(() => {
this.expandable = true;
this.detectChanges();
}, 1);
if (!this.maxHeightAllowed) {
this.maxHeightAllowed = 320;
}
setTimeout(() => {
this.realHeight = this._element.clientHeight;
if (this.button && !this.button.content) {
this.button.content = this;
}
if (this.realHeight > this.maxHeightAllowed) {
this._element.style.maxHeight = this.maxHeightAllowed + 'px';
this._element.style.position = 'relative';
setTimeout(() => {
this.expandable = true;
this.detectChanges();
}, 1);
}
}, 1);
}
expand() {
......
......@@ -155,7 +155,7 @@ m-blockchain--marketing {
m-video .minds-video-play-icon {
@include m-theme(){
text-shadow: 0 0 3px themed($m-grey-900);
text-shadow: 0 0 3px rgba(themed($m-black-always), 0.6);
}
}
......
......@@ -13,7 +13,7 @@
<div class="m-mature-overlay" (click)="blog.mature_visibility = !blog.mature_visibility">
<span class="m-mature-overlay-note">
<i class="material-icons" title="Mature content" i18n-title="@@M__COMMON__MATURE_CONTENT">explicit</i>
<span i18n="@@M__COMMON__CONFIRM_18">Click to confirm your are 18+</span>
<span i18n="@@M__COMMON__CONFIRM_18">Click to confirm you are 18+</span>
</span>
</div>
<minds-banner [src]="blog.thumbnail_src" [object]="blog" *ngIf="blog.header_bg"></minds-banner>
......
......@@ -101,6 +101,12 @@
}
}
&.selected:hover {
@include m-theme(){
color: themed($m-blue-dark) !important;
}
}
span {
vertical-align: middle;
line-height: 16px;
......
......@@ -33,7 +33,7 @@
> a {
display: block;
height: 16px;
height: 16px;
line-height: 16px;
font-size: 13px;
font-weight: 600;
......@@ -104,6 +104,12 @@
}
}
&.selected:hover {
@include m-theme(){
color: themed($m-blue-dark) !important;
}
}
span {
vertical-align: middle;
line-height: 16px;
......
......@@ -10,5 +10,16 @@
}
}
}
.m-comments-composer--overflow {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 100px;
height: 24px;
@include m-theme(){
background: linear-gradient(to right, rgba(themed($m-white-always), 0) 0%, rgba(themed($m-white), 1) 25%);
}
}
}
\ No newline at end of file
......@@ -23,7 +23,7 @@
<span *ngIf="!descendingInProgress && loaded">
<i class="material-icons">update</i> <ng-container i18n="@@MINDS__COMMENTS__LOAD_EARLIER_ACTION">Load earlier</ng-container>
</span>
</div>
<p class="m-comments--start-conversation-label"
......@@ -100,6 +100,8 @@
</div>
<div class="mdl-card__actions">
<!-- Overflow -->
<div class="m-comments-composer--overflow"></div>
<!-- Attachements -->
<div class="attachment-button" [ngClass]="{ 'mdl-color-text--amber-500': attachment.hasFile() }">
<i class="material-icons">attachment</i>
......
......@@ -39,8 +39,9 @@
</div>
<div class="mdl-card__actions">
<!-- Overflow -->
<div class="m-comments-composer--overflow"></div>
<!-- Attachements -->
<div class="attachment-button" [ngClass]="{ 'mdl-color-text--amber-500': attachment.hasFile() }">
<i class="material-icons">attachment</i>
<input type="file" id="file" #file name="attachment" (change)="uploadAttachment(file, $event)"/>
......
......@@ -218,7 +218,7 @@ m-hashtags--sidebar-selector {
width: 48px;
z-index: 1;
@include m-theme(){
background: linear-gradient(to right, transparent 0%, rgba(themed($m-body-bg), 1) 50%);
background: linear-gradient(to right, rgba(themed($m-white-always), 0) 0%, rgba(themed($m-body-bg), 1) 50%);
}
.m-hashtagsSidebarSelectorCompactListOverflow__Arrow {
......
......@@ -31,6 +31,12 @@ minds-activity {
}
}
.m-activity--image-link {
&:hover {
cursor: pointer;
}
}
m-safe-toggle {
.m-safe-toggle {
display: inline-block;
......
......@@ -153,6 +153,7 @@
<button class="mdl-button mdl-button--colored mdl-button--raised" (click)="activity.title = titleEdit.value; save();" i18n="@@M__ACTION__SAVE">Save</button>
</div>
<!-- Video -->
<div class="item item-image item-image-video m-activity--video"
[ngClass]="{ 'm-mature-content': attachment.shouldBeBlurred(activity), 'm-mature-content-shown': attachment.isForcefullyShown(activity) }"
*ngIf="activity.custom_type == 'video'">
......@@ -172,6 +173,9 @@
[guid]="activity.custom_data.guid"
[playCount]="activity['play:count']"
[torrent]="[{ res: '360', key: activity.custom_data.guid + '/360.mp4' }]"
[isActivity]="true"
(videoMetadataLoaded)="setVideoDimensions($event)"
(mediaModalRequested)="showMediaModal()"
#player>
<video-ads [player]="player" *ngIf="activity.monetized"></video-ads>
</m-video>
......@@ -188,7 +192,7 @@
</span>
</div>
<a [routerLink]="['/media', activity.entity_guid]">
<a class="m-activity--image-link">
<img [src]="activity.thumbnail_src" (error)="activity.thumbnail_src = null">
</a>
</div>
......@@ -203,11 +207,13 @@
<span i18n="@@M__COMMON__CONFIRM_18">Click to confirm your are 18+</span>
</span>
</div>
<a [routerLink]="['/media', activity.entity_guid]">
<img [src]="activity.custom_data[0].src" style="width:100%"
(error)="activity.custom_data[0].src = minds.cdn_assets_url + 'assets/logos/placeholder-bulb.jpg'"
<a class="m-activity--image-link">
<img [src]="activity.custom_data[0].src"
style="width:100%"
(error)="activity.custom_data[0].src = minds.cdn_assets_url + 'assets/logos/placeholder-bulb.jpg'"
*ngIf="activity.custom_data"
(click)="showMediaModal()"
#batchImage
>
</a>
</div>
......@@ -219,6 +225,7 @@
[events]="childEventsEmitter"
[boosted]="activity.boosted"
(matureVisibilityChange)="onRemindMatureVisibilityChange()"
(videoMetadataLoaded)="setVideoDimensions($event)"
></minds-remind>
</ng-container>
<ng-template #blockedRemindTemplate>
......@@ -233,6 +240,7 @@
<ng-content select="[bottom-content]"></ng-content>
<!-- Action buttons -->
<div class="tabs" *ngIf="!hideTabs && !isPending(activity)">
<minds-button-thumbs-up [object]="activity"></minds-button-thumbs-up>
<minds-button-thumbs-down [object]="activity"></minds-button-thumbs-down>
......@@ -249,7 +257,7 @@
<ng-container i18n="verb|@@M__ACTION__BOOST">Boost</ng-container>
</a>
</div>
<!-- Activity metrics -->
<div class="impressions-tag m-activity--metrics" [class.m-activity--metrics-wire]="!session.getLoggedInUser() || session.getLoggedInUser().guid != activity.owner_guid" *ngIf="!activity.hide_impressions && !hideTabs">
<div class="m-activity--metrics-inner m-border">
<div class="m-activity--metrics-metric" (click)="showWire()">
......
......@@ -16,6 +16,7 @@ import { Session } from '../../../../../services/session';
import { AttachmentService } from '../../../../../services/attachment';
import { TranslationService } from '../../../../../services/translation';
import { OverlayModalService } from '../../../../../services/ux/overlay-modal';
import { MediaModalComponent } from '../../../../media/modal/modal.component';
import { BoostCreatorComponent } from '../../../../boost/creator/creator.component';
import { WireCreatorComponent } from '../../../../wire/creator/creator.component';
import { MindsVideoComponent } from '../../../../media/components/video/video.component';
......@@ -26,6 +27,8 @@ import { ActivityAnalyticsOnViewService } from "./activity-analytics-on-view.ser
import { NewsfeedService } from "../../../../newsfeed/services/newsfeed.service";
import { ClientMetaService } from "../../../../../common/services/client-meta.service";
import { AutocompleteSuggestionsService } from "../../../../suggestions/services/autocomplete-suggestions.service";
import { FeaturesService } from '../../../../../services/features.service';
import isMobile from '../../../../../helpers/is-mobile';
@Component({
moduleId: module.id,
......@@ -87,6 +90,8 @@ export class Activity implements OnInit {
blockedUsers: string[] = [];
videoDimensions: Array<any> = null;
get menuOptions(): Array<string> {
if (!this.activity || !this.activity.ephemeral) {
if (this.showBoostMenuOptions) {
......@@ -100,6 +105,7 @@ export class Activity implements OnInit {
}
@ViewChild('player', { static: false }) player: MindsVideoComponent;
@ViewChild('batchImage', { static: false }) batchImage: ElementRef;
constructor(
public session: Session,
......@@ -114,6 +120,7 @@ export class Activity implements OnInit {
protected activityAnalyticsOnViewService: ActivityAnalyticsOnViewService,
protected newsfeedService: NewsfeedService,
protected clientMetaService: ClientMetaService,
protected featuresService: FeaturesService,
public suggestions: AutocompleteSuggestionsService,
@SkipSelf() injector: Injector,
elementRef: ElementRef,
......@@ -149,7 +156,7 @@ export class Activity implements OnInit {
this.activityAnalyticsOnViewService.setEntity(this.activity);
if (
this.activity.custom_type == 'batch'
this.activity.custom_type === 'batch'
&& this.activity.custom_data
&& this.activity.custom_data[0].src
) {
......@@ -432,6 +439,42 @@ export class Activity implements OnInit {
this.activity.mature_visibility = !this.activity.mature_visibility;
}
setVideoDimensions($event) {
this.videoDimensions = $event.dimensions;
}
setImageDimensions() {
const img: HTMLImageElement = this.batchImage.nativeElement;
this.activity.custom_data[0].width = img.naturalWidth;
this.activity.custom_data[0].height = img.naturalHeight;
}
showMediaModal() {
if (this.featuresService.has('media-modal')) {
// Mobile (not tablet) users go to media page instead of modal
if (isMobile() && Math.min(screen.width, screen.height) < 768) {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
}
if (this.activity.custom_type === 'video') {
this.activity.custom_data.dimensions = this.videoDimensions;
} else { // Image
// Set image dimensions if they're not already there
if (this.activity.custom_data[0].width === '0' || this.activity.custom_data[0].height === '0') {
this.setImageDimensions();
}
}
this.activity.modal_source_url = this.router.url;
this.overlayModal.create(MediaModalComponent, this.activity, {
class: 'm-overlayModal--media'
}).present();
} else {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
}
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......
......@@ -74,7 +74,7 @@ minds-card-user, .minds-banner-card{
text-overflow: ellipsis;
font-size: 16px;
line-height: 18px;
padding: 0;
padding: 0 0 0 1px;
margin: 0;
@include m-theme(){
text-shadow: -1px -1px 0 themed($m-black-always), 1px -1px 0 themed($m-black-always), -1px 1px 0 themed($m-black-always), 1px 1px 0 themed($m-black-always);
......@@ -85,6 +85,7 @@ minds-card-user, .minds-banner-card{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0 0 0 1px;
@include m-theme(){
text-shadow: -1px -1px 0 themed($m-black-always), 1px -1px 0 themed($m-black-always), -1px 1px 0 themed($m-black-always), 1px 1px 0 themed($m-black-always);
}
......@@ -305,7 +306,7 @@ minds-activity.mdl-card, minds-activity, minds-activity-preview{
.m-owner-block--remind{
overflow: visible;
i{
font-size: 18px;
margin-right: 12px;
......@@ -564,7 +565,7 @@ minds-activity.mdl-card, minds-activity, minds-activity-preview{
@include m-theme(){
color: themed($m-blue) !important;
}
&:hover {
@include m-theme(){
color: rgba(themed($m-blue-dark),0.9) !important;
......
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, EventEmitter, Input, Output } from '@angular/core';
import {
Component,
ChangeDetectionStrategy,
ChangeDetectorRef,
EventEmitter,
Input,
Output,
ViewChild,
ElementRef,
} from '@angular/core';
import { Router } from "@angular/router";
import { Client } from '../../../../../services/api';
import { Session } from '../../../../../services/session';
import { AttachmentService } from '../../../../../services/attachment';
import { OverlayModalService } from '../../../../../services/ux/overlay-modal';
import { MediaModalComponent } from '../../../../media/modal/modal.component';
import { FeaturesService } from '../../../../../services/features.service';
import isMobile from '../../../../../helpers/is-mobile';
@Component({
moduleId: module.id,
......@@ -32,14 +47,20 @@ export class Remind {
isTranslatable: boolean = false;
menuOptions: any = [];
canDelete: boolean = false;
videoDimensions: Array<any> = null;
@Output('matureVisibilityChange') onMatureVisibilityChange: EventEmitter<any> = new EventEmitter<any>();
@ViewChild('batchImage', { static: false }) batchImage: ElementRef;
constructor(
public session: Session,
public client: Client,
public attachment: AttachmentService,
private changeDetectorRef: ChangeDetectorRef
private changeDetectorRef: ChangeDetectorRef,
private overlayModal: OverlayModalService,
private router: Router,
protected featuresService: FeaturesService,
) {
this.hideTabs = true;
}
......@@ -67,8 +88,8 @@ export class Remind {
this.activity.boosted = this.boosted;
if (
this.activity.custom_type == 'batch'
&& this.activity.custom_data
this.activity.custom_type == 'batch'
&& this.activity.custom_data
&& this.activity.custom_data[0].src
) {
this.activity.custom_data[0].src = this.activity.custom_data[0].src.replace(this.minds.site_url, this.minds.cdn_url);
......@@ -127,4 +148,40 @@ export class Remind {
this.onMatureVisibilityChange.emit();
}
setVideoDimensions($event) {
this.videoDimensions = $event.dimensions;
}
setImageDimensions() {
const img: HTMLImageElement = this.batchImage.nativeElement;
this.activity.custom_data[0].width = img.naturalWidth;
this.activity.custom_data[0].height = img.naturalHeight;
}
showMediaModal() {
if (this.featuresService.has('media-modal')) {
// Mobile (not tablet) users go to media page instead of modal
if (isMobile() && Math.min(screen.width, screen.height) < 768) {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
}
if (this.activity.custom_type === 'video') {
this.activity.custom_data.dimensions = this.videoDimensions;
} else { // Image
// Set image dimensions if they're not already there
if (this.activity.custom_data[0].width === '0' || this.activity.custom_data[0].height === '0') {
this.setImageDimensions();
}
}
this.activity.modal_source_url = this.router.url;
this.overlayModal.create(MediaModalComponent, this.activity, {
class: 'm-overlayModal--media'
}).present();
} else {
this.router.navigate([`/media/${this.activity.entity_guid}`]);
}
}
}
<video
[src]="src"
(click)="toggle()"
[autoplay]="autoplay"
[poster]="poster"
[muted]="muted"
......
......@@ -16,6 +16,7 @@ export class MindsVideoDirectHttpPlayer implements OnInit, OnDestroy, MindsPlaye
@Input() muted: boolean = false;
@Input() poster: string = '';
@Input() autoplay: boolean = false;
@Input() guid: string | number;
src: string;
@Input('src') set _src(src: string) {
......@@ -35,25 +36,26 @@ export class MindsVideoDirectHttpPlayer implements OnInit, OnDestroy, MindsPlaye
@Output() onPause: EventEmitter<HTMLVideoElement> = new EventEmitter();
@Output() onEnd: EventEmitter<HTMLVideoElement> = new EventEmitter();
@Output() onError: EventEmitter<{ player: HTMLVideoElement, e }> = new EventEmitter();
@Output() onCanPlayThrough: EventEmitter<any> = new EventEmitter();
@Output() onLoadedMetadata: EventEmitter<any> = new EventEmitter();
loading: boolean = false;
constructor(
protected cd: ChangeDetectorRef
protected cd: ChangeDetectorRef,
) { }
protected _emitPlay = () => this.onPlay.emit(this.getPlayer());
protected _emitPause = () => this.onPause.emit(this.getPlayer());
protected _emitEnd = () => this.onEnd.emit(this.getPlayer());
protected _emitError = e => this.onError.emit({ player: this.getPlayer(), e });
protected _emitCanPlayThrough = () => this.onCanPlayThrough.emit(this.getPlayer());
protected _emitLoadedMetadata = () => this.onLoadedMetadata.emit(this.getPlayer());
protected _canPlayThrough = () => {
this.loading = false;
this.detectChanges();
};
protected _dblClick = () => {
this.requestFullScreen();
this._emitCanPlayThrough();
};
protected _onPlayerError = e => {
......@@ -70,12 +72,12 @@ export class MindsVideoDirectHttpPlayer implements OnInit, OnDestroy, MindsPlaye
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);
player.addEventListener('error', this._onPlayerError);
player.addEventListener('canplaythrough', this._canPlayThrough);
player.addEventListener('loadedmetadata', this._emitLoadedMetadata);
this.loading = true;
}
......@@ -84,12 +86,12 @@ export class MindsVideoDirectHttpPlayer implements OnInit, OnDestroy, MindsPlaye
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);
player.removeEventListener('error', this._onPlayerError);
player.removeEventListener('canplaythrough', this._canPlayThrough);
player.removeEventListener('loadedmetadata', this._emitLoadedMetadata);
}
}
......@@ -170,4 +172,5 @@ export class MindsVideoDirectHttpPlayer implements OnInit, OnDestroy, MindsPlaye
this.cd.markForCheck();
this.cd.detectChanges();
}
}
<video
(click)="toggle()"
[poster]="poster"
[muted]="muted"
preload="none"
......
......@@ -20,6 +20,7 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
@Input() muted: boolean = false;
@Input() poster: string = '';
@Input() autoplay: boolean = false;
@Input() guid: string | number;
src: string;
@Input('src') set _src(src: string) {
......@@ -38,9 +39,12 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
@Output() onPause: EventEmitter<HTMLVideoElement> = new EventEmitter();
@Output() onEnd: EventEmitter<HTMLVideoElement> = new EventEmitter();
@Output() onError: EventEmitter<{ player, e }> = new EventEmitter();
@Output() onCanPlayThrough: EventEmitter<any> = new EventEmitter();
@Output() onLoadedMetadata: EventEmitter<any> = new EventEmitter();
initialized: boolean = false;
loading: boolean = false;
isModal: boolean = false;
protected torrentId: string;
protected torrentReady: boolean = false;
......@@ -67,10 +71,13 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
protected _emitPause = () => this.onPause.emit(this.getPlayer());
protected _emitEnd = () => this.onEnd.emit(this.getPlayer());
protected _emitError = e => this.onError.emit({ player: this.getPlayer(), e});
protected _emitCanPlayThrough = () => this.onCanPlayThrough.emit(this.getPlayer());
protected _emitLoadedMetadata = () => this.onLoadedMetadata.emit(this.getPlayer());
protected _canPlayThrough = () => {
this.loading = false;
this.detectChanges();
this._emitCanPlayThrough();
};
protected _dblClick = () => {
......@@ -130,8 +137,10 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
player.addEventListener('ended', this._emitEnd);
player.addEventListener('error', this._onPlayerError);
player.addEventListener('canplaythrough', this._canPlayThrough);
player.addEventListener('loadedmetadata', this._emitLoadedMetadata);
this.infoTimer$ = setInterval(this._refreshInfo, 1000);
this.isModal = document.body.classList.contains('m-overlay-modal--shown');
}
ngAfterViewInit() {
......@@ -158,6 +167,7 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
player.removeEventListener('ended', this._emitEnd);
player.removeEventListener('error', this._onPlayerError);
player.removeEventListener('canplaythrough', this._canPlayThrough);
player.removeEventListener('loadedmetadata', this._emitLoadedMetadata);
}
}
......@@ -353,4 +363,5 @@ export class MindsVideoTorrentPlayer implements OnInit, AfterViewInit, OnDestroy
this.torrentReady = false;
}
}
}
......@@ -53,7 +53,7 @@ describe('MindsVideoProgressBar', () => {
TestBed.configureTestingModule({
declarations: [ MindsVideoProgressBar ], // declare the test component
imports: [
imports: [
FormsModule,
RouterTestingModule,
NgCommonModule ],
......@@ -98,14 +98,14 @@ describe('MindsVideoProgressBar', () => {
jasmine.clock().uninstall();
});
it('should have a Play icon and a Control bar', () => {
const seeker = fixture.debugElement.query(By.css('#seeker'));
const seekerBall = fixture.debugElement.query(By.css('.seeker-ball'));
const stamps = fixture.debugElement.query(By.css('.progress-stamps'));
expect(seeker).not.toBeNull();
expect(seekerBall).not.toBeNull();
expect(stamps).not.toBeNull();
});
// it('should have a Play icon and a Control bar', () => {
// const seeker = fixture.debugElement.query(By.css('#seeker'));
// const seekerBall = fixture.debugElement.query(By.css('.seeker-ball'));
// const stamps = fixture.debugElement.query(By.css('.progress-stamps'));
// expect(seeker).not.toBeNull();
// expect(seekerBall).not.toBeNull();
// expect(stamps).not.toBeNull();
// });
it('time is properly calculated', () => {
comp.duration = 111;
......@@ -212,5 +212,5 @@ describe('MindsVideoProgressBar', () => {
fixture.detectChanges();
expect(comp.elapsed).toEqual({minutes:'00', seconds:11});
});
});
......@@ -5,10 +5,14 @@
[poster]="poster"
[muted]="muted"
[autoplay]="autoplay"
[guid]="guid"
(onPlay)="onPlay()"
(onPause)="onPause()"
(onEnd)="onEnd()"
(onError)="onError()"
(onCanPlayThrough)="onCanPlayThrough()"
(onLoadedMetadata)="loadedMetadata()"
(click)="isModal ? toggle() : requestMediaModal()"
#player
></m-video--direct-http-player>
......@@ -18,19 +22,22 @@
[poster]="poster"
[muted]="muted"
[autoplay]="autoplay"
[guid]="guid"
(onPlay)="onPlay()"
(onPause)="onPause()"
(onEnd)="onEnd()"
(onError)="onError()"
(onCanPlayThrough)="onCanPlayThrough()"
(onLoadedMetadata)="loadedMetadata()"
(click)="isModal ? toggle() : requestMediaModal()"
#player
></m-video--torrent-player>
<ng-container *ngIf="playerRef">
<i *ngIf="!playerRef.isPlaying() && !playerRef.isLoading()"
<i *ngIf="!playerRef.isPlaying() && !playerRef.isLoading() || isActivity && metadataLoaded"
class="material-icons minds-video-play-icon"
(click)="playerRef.play()"
(click)="isModal ? toggle() : requestMediaModal()"
>play_circle_outline</i>
<ng-content></ng-content>
<div *ngIf="transcoding" class="minds-video-bar-top">
......@@ -41,10 +48,10 @@
<span i18n="@@MEDIA__VIDEO__TRANSCODING_NOTICE">{{transcodingError}}</span>
</div>
<div class="minds-video-bar-full">
<i class="material-icons" *ngIf="!playerRef.isLoading(); else loadingSpinner"
(click)="playerRef.toggle()"
>{{ playerRef.isPlaying() ? 'pause' : 'play_arrow' }}</i>
<div class="minds-video-bar-full" [@fadeAnimation]="showControls ? 'in' : 'out'">
<i class="material-icons"
(click)="controlBarToggle($event)"
>{{ playerRef.isPlaying() || playerRef.isLoading() ? 'pause' : 'play_arrow' }}</i>
<ng-template #loadingSpinner>
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</ng-template>
......@@ -53,7 +60,7 @@
<m-video--volume-slider #volumeSlider [player]="playerRef"></m-video--volume-slider>
<a class="material-icons m-video-full-page minds-video--open-new"
*ngIf="guid"
*ngIf="guid && !isModal"
[routerLink]="['/media', guid]"
target="_blank"
(click)="playerRef.pause()">
......@@ -90,7 +97,7 @@
(select)="selectedQuality($event)"
></m-video--quality-selector>
<i class="material-icons" (click)="playerRef.requestFullScreen()">tv</i>
<i *ngIf="!isModal" class="material-icons" (click)="playerRef.requestFullScreen()">tv</i>
</div>
<div class="m-video--torrent-info" *ngIf="torrentInfo && current.type === 'torrent'">
......
......@@ -6,6 +6,16 @@
position: relative;
display:block;
&.clickable {
cursor:pointer;
}
&:hover {
.minds-video-play-icon {
opacity: 1;
}
}
video{
width:100%;
}
......@@ -43,6 +53,7 @@
}
}
.minds-video-play-icon{
opacity: 0.8;
display: block;
text-align: center;
top: 50%;
......@@ -51,18 +62,21 @@
position: absolute;
cursor: pointer;
width: 100%;
transition: opacity 0.3s cubic-bezier(.23, 1, .32, 1);
@include m-theme(){
color: themed($m-white-always);
text-shadow: 0 0 3px rgba(themed($m-black-always), 0.6);
}
}
.minds-video-bar-full{
display: none;
opacity: 0;
visibility: hidden;
display: flex;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
box-sizing: border-box;
//padding: $minds-padding;
text-align: center;
align-items: center;
@include m-theme(){
......@@ -75,7 +89,7 @@
color:themed($m-white-always);
}
}
.mdl-spinner {
margin: 0 8px;
}
......@@ -91,9 +105,10 @@
.minds-video-bar-min{
display: none;
}
.minds-video-bar-full{
display: flex;
}
// .minds-video-bar-full{
// display: flex;
// }
}
.m-video--torrent-info {
......
......@@ -20,6 +20,8 @@ import { TooltipComponent } from '../../../../common/components/tooltip/tooltip.
import { WebtorrentService } from '../../../webtorrent/webtorrent.service';
import { webtorrentServiceMock } from '../../../../../tests/webtorrent-service-mock.spec';
import { MindsPlayerInterface } from './players/player.interface';
import { MediaModalComponent } from '../../modal/modal.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@Component({
selector: 'm-video--direct-http-player',
......@@ -29,6 +31,7 @@ class MindsVideoDirectHttpPlayerMock implements MindsPlayerInterface {
@Input() muted: boolean;
@Input() poster: string;
@Input() autoplay: boolean;
@Input() guid: string;
@Input() src: string;
@Output() onPlay: EventEmitter<HTMLVideoElement> = new EventEmitter();
......@@ -70,6 +73,7 @@ class MindsVideoTorrentPlayerMock implements MindsPlayerInterface {
@Input() muted: boolean;
@Input() poster: string;
@Input() autoplay: boolean;
@Input() guid: string;
@Input() src: string;
@Output() onPlay: EventEmitter<HTMLVideoElement> = new EventEmitter();
......@@ -132,6 +136,7 @@ export class MindsVideoProgressBarMock {
}
bindToElement() {}
enableKeyControls() {}
}
@Directive({
......@@ -164,6 +169,7 @@ describe('MindsVideo', () => {
FormsModule,
RouterTestingModule,
NgCommonModule,
BrowserAnimationsModule,
],
providers: [
{ provide: ScrollService, useValue: scrollServiceMock },
......@@ -194,6 +200,8 @@ describe('MindsVideo', () => {
// video.src = 'thisisavideo.mp4';
const video = new HTMLVideoElementMock();
comp.playerRef.getPlayer = () => <any>video;
comp.isActivity = false;
comp.showControls = true;
fixture.detectChanges(); // re-render
......@@ -221,18 +229,18 @@ describe('MindsVideo', () => {
expect(videoBar).not.toBeNull();
});
// it('On hover Control bar should be visible', () => {
// expect(comp.playerRef.getPlayer()).not.toBeNull();
// comp.playerRef.getPlayer().dispatchEvent(new Event('hover'));
// const videoBar = fixture.debugElement.query(By.css('.minds-video-bar-full'));
// expect(videoBar.nativeElement.hasAttribute('hidden')).toEqual(false);
// const quality = fixture.debugElement.query(By.css('m-video--quality-selector'));
// const volume = fixture.debugElement.query(By.css('m-video--volume-slider'));
// const progressBar = fixture.debugElement.query(By.css('m-video--progress-bar'));
// expect(progressBar).not.toBeNull();
// expect(quality).toBeNull();
// expect(volume).not.toBeNull();
// });
it('On mouse enter Control bar should be visible', () => {
expect(comp.playerRef.getPlayer()).not.toBeNull();
comp.onMouseEnter();
const videoBar = fixture.debugElement.query(By.css('.minds-video-bar-full'));
expect(videoBar.nativeElement.hasAttribute('hidden')).toEqual(false);
const quality = fixture.debugElement.query(By.css('m-video--quality-selector'));
const volume = fixture.debugElement.query(By.css('m-video--volume-slider'));
const progressBar = fixture.debugElement.query(By.css('m-video--progress-bar'));
expect(progressBar).not.toBeNull();
expect(quality).toBeNull();
expect(volume).not.toBeNull();
});
it('Should call counter', () => {
const video = fixture.debugElement.query(By.css('video'));
......@@ -276,7 +284,7 @@ describe('MindsVideo', () => {
// fixture.detectChanges();
// expect(comp.loop).toEqual(true);
// });
//
// it('should sets visibleplay', () => {
// comp.visibleplay = false;
// fixture.detectChanges();
......
import { Component, ElementRef, Input, Output, EventEmitter, ViewChild, ChangeDetectorRef } from '@angular/core';
import { Component, ElementRef, Input, Output, EventEmitter, ViewChild, ChangeDetectorRef, OnDestroy } 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';
import { MindsVideoVolumeSlider } from './volume-slider/volume-slider.component';
......@@ -7,24 +9,47 @@ 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 isMobile from '../../../../helpers/is-mobile';
@Component({
selector: 'm-video',
host: {
'(mouseenter)': 'onMouseEnter()',
'(mouseleave)': 'onMouseLeave()'
'(mouseleave)': 'onMouseLeave()',
'[class.clickable]':'canPlayThrough'
},
templateUrl: 'video.component.html',
animations: [
trigger('fadeAnimation', [
state('in', style({
visibility: 'visible',
opacity: 1
})),
state('out', style({
visibility: 'hidden',
opacity: 0
})),
transition('in <=> out', [
animate('300ms ease-in')
]),
]),
],
})
export class MindsVideoComponent {
export class MindsVideoComponent implements OnDestroy {
@Input() guid: string | number;
@Input() log: string | number;
@Input() muted: boolean = false;
@Input() poster: string = '';
@Input() isActivity: boolean = false;
@Input() isModal: boolean = false;
@Output('finished') finished: EventEmitter<any> = new EventEmitter();
@Output() videoMetadataLoaded: EventEmitter<any> = new EventEmitter();
@Output() videoCanPlayThrough: EventEmitter<any> = new EventEmitter();
@Output() mediaModalRequested: EventEmitter<any> = new EventEmitter();
@ViewChild('progressBar', { static: false }) progressBar: MindsVideoProgressBar;
@ViewChild('volumeSlider', { static: false }) volumeSlider: MindsVideoVolumeSlider;
@ViewChild('player', { static: false }) playerRef: MindsPlayerInterface;
......@@ -52,6 +77,11 @@ export class MindsVideoComponent {
playedOnce: boolean = false;
playCount: number = -1;
playCountDisabled: boolean = false;
stageHover: boolean = false;
showControls: boolean = false;
stopSeekerTimeout: any = null;
metadataLoaded: boolean = false;
canPlayThrough: boolean = false;
current: { type: 'torrent' | 'direct-http', src: string };
protected candidates: SourceCandidates = new SourceCandidates();
......@@ -70,6 +100,7 @@ export class MindsVideoComponent {
public client: Client,
protected webtorrent: WebtorrentService,
protected cd: ChangeDetectorRef,
private router: Router,
) { }
ngOnInit() {
......@@ -154,13 +185,27 @@ export class MindsVideoComponent {
}
onMouseEnter() {
if (this.isActivity) {
return;
}
this.progressBar.getSeeker();
this.progressBar.enableKeyControls();
this.showControls = true;
}
onMouseLeave() {
this.progressBar.stopSeeker();
if (this.stageHover || this.isActivity) {
return;
}
clearTimeout(this.stopSeekerTimeout);
this.stopSeekerTimeout = setTimeout(() => {
this.progressBar.stopSeeker();
}, 300);
this.progressBar.disableKeyControls();
this.showControls = false;
}
selectedQuality(quality) {
......@@ -210,6 +255,10 @@ export class MindsVideoComponent {
ngOnDestroy() {
if (this.scroll_listener)
this.scroll.unListen(this.scroll_listener);
if (this.stopSeekerTimeout) {
clearTimeout(this.stopSeekerTimeout);
}
}
pause() {
......@@ -224,8 +273,21 @@ export class MindsVideoComponent {
this.playerRef.toggle();
}
// Sources
loadedMetadata() {
const dimensions = {
'width' : this.playerRef.getPlayer().videoWidth,
'height' : this.playerRef.getPlayer().videoHeight
};
this.metadataLoaded = true;
this.videoMetadataLoaded.emit({dimensions: dimensions});
}
onCanPlayThrough() {
this.canPlayThrough = true;
this.videoCanPlayThrough.emit();
}
// Sources
async fallback() {
this.candidates.setAsBlacklisted(this.current.type, this.current.src);
const success = this.pickNextBestSource();
......@@ -284,7 +346,7 @@ export class MindsVideoComponent {
// Qualities
updateAvailableQualities() {
let qualities = [];
const qualities = [];
if (this.src && this.src.length) {
this.src.forEach(item => qualities.push(item.res));
......@@ -323,11 +385,34 @@ export class MindsVideoComponent {
}
}
// Prevent extra toggles from bubbling up when click control bar that overlays player
controlBarToggle($event) {
$event.stopPropagation();
this.toggle();
}
requestMediaModal() {
if (!this.canPlayThrough) {
return;
}
if (this.isModal) {
this.toggle();
return;
}
// Mobile (not tablet) users go to media page instead of modal
if (isMobile() && Math.min(screen.width, screen.height) < 768) {
this.router.navigate([`/media/${this.guid}`]);
}
this.mediaModalRequested.emit();
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
}
export { VideoAds } from './ads.component';
......@@ -5,6 +5,7 @@
<i class="material-icons" *ngIf="element.volume >= 0.9 && !element.muted" (click)="element.muted = true">volume_up</i>
<div class="m-video--volume-control">
<div class="m-video--volume-control--background"></div>
<input type="range" [(ngModel)]="element.volume" class="m-video--volume-control-selector" min="0" max="1" step="0.05" />
</div>
</div>
......@@ -61,6 +61,9 @@ m-video--volume-slider{
position: absolute;
top: 35px;
right: -18px;
@include m-theme(){
background-color: themed($m-white-always);
}
}
.m-video--volume-control{
......@@ -72,6 +75,14 @@ m-video--volume-slider{
height: 80px;
position: absolute;
margin: 0;
}
.m-video--volume-control--background {
width:40px;
height:72px;
position: absolute;
left: calc(50% - 20px);
bottom: 16px;
@include m-theme(){
background-color: rgba(themed($m-black-always), 0.4);
}
......
......@@ -21,6 +21,7 @@ import { MediaEditComponent } from './edit/edit.component';
import { MediaTheatreComponent } from './view/views/theatre.component';
import { MediaGridComponent } from './view/views/grid.component';
import { MediaViewRecommendedComponent } from './view/recommended/recommended.component';
import { MediaModalComponent } from './modal/modal.component';
import { ThumbnailSelectorComponent } from './components/thumbnail-selector.component';
import { CommentsModule } from '../comments/comments.module';
import { HashtagsModule } from '../hashtags/hashtags.module';
......@@ -68,12 +69,14 @@ const routes: Routes = [
MediaGridComponent,
MediaViewRecommendedComponent,
ThumbnailSelectorComponent,
MediaModalComponent,
],
entryComponents: [
MediaVideosListComponent,
MediaImagesListComponent,
MediaEditComponent,
MediaViewComponent,
MediaModalComponent,
]
})
......
......@@ -574,8 +574,8 @@ m-media--grid{
left: $minds-padding;
top: $minds-padding;
cursor: pointer;
opacity: 0.35;
visibility: hidden;
opacity: 0;
transition: opacity 300ms;
@include m-theme(){
color: themed($m-white);
text-shadow: 1px 1px 3px rgba(themed($m-black), 0.65);
......@@ -589,6 +589,7 @@ m-media--grid{
m-media--theatre:hover & {
visibility: visible;
opacity: 0.35;
}
}
......
<div class="m-mediaModal__wrapper">
<div class="m-mediaModal__theater"
(click)="clickedModal($event)"
>
<div class="m-mediaModal m-mediaModal__clearFix"
[style.width]="modalWidth + 'px'"
[style.height]="stageHeight + 'px'"
>
<!-- The stageWrapper is the element that goes into fullscreen -->
<div class="m-mediaModal__stageWrapper"
[style.width]="stageWidth + 'px'"
[style.line-height]="stageHeight + 'px'"
(mouseenter)="onMouseEnterStage()"
(mouseleave)="onMouseLeaveStage()"
(touchend)="showOverlays()"
>
<!-- LOADING PANEL -->
<div class="m-mediaModal__loadingPanel" *ngIf="isLoading">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</div>
<!-- MEDIA STAGE -->
<div class="m-mediaModal__stage">
<!-- MEDIA: IMAGE -->
<div class="m-mediaModal__mediaWrapper m-mediaModal__mediaWrapper--image"
*ngIf="!isVideo"
[style.width]="mediaWidth + 'px'"
[style.height]="mediaHeight + 'px'"
[@slowFadeAnimation]="isLoading ? 'out' : 'in'"
>
<img class="m-mediaModal__media--image"
[src]="thumbnail"
(load)="isLoaded()"
[style.height]="entity.height + 'px'"
[style.width]="entity.width + 'px'"
/>
</div>
<!-- MEDIA: VIDEO -->
<div class="m-mediaModal__mediaWrapper m-mediaModal__mediaWrapper--video"
*ngIf="isVideo"
[style.width]="mediaWidth + 'px'"
[style.height]="mediaHeight + 'px'"
>
<m-video class="m-mediaModal__media--video"
[style.height]="entity.height + 'px'"
[style.width]="entity.width + 'px'"
[isModal]="true"
[autoplay]="true"
[muted]="false"
[poster]="entity.custom_data.thumbnail_src"
[src]="[{ 'res': '360', 'uri': 'api/v1/media/' + entity.custom_data.guid + '/play', 'type': 'video/mp4' }]"
[guid]="entity.custom_data.guid"
[playCount]="entity['play:count']"
[torrent]="[{ res: '360', key: entity.custom_data.guid + '/360.mp4' }]"
(videoCanPlayThrough)="isLoaded()"
[@slowFadeAnimation]="isLoading ? 'out' : 'in'"
>
<video-ads [player]="player" *ngIf="entity.monetized"></video-ads>
</m-video>
</div>
<!-- OVERLAY -->
<div class="m-mediaModal__overlayContainer"
*ngIf="showOverlay"
@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>
</span>
<!-- TITLE: FULLSCREEN -->
<span class="m-mediaModal__overlayTitle m-mediaModal__overlayTitle--fullscreen" *ngIf="isFullscreen">
<a [routerLink]="['/', entity.ownerObj.username]">
<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>
</span>
</div>
<!-- FULLSCREEN BUTTON -->
<div class="m-mediaModal__fullscreenButtonWrapper">
<div class="m-mediaModal__fullscreenButton"
(click)="toggleFullscreen()"
(mouseenter)="fullscreenHovering=true"
(mouseleave)="fullscreenHovering=false"
[class.m-mediaModal__fullscreenButton--hovering]="fullscreenHovering"
>
<i *ngIf="!isFullscreen" class="material-icons m-mediaModal__fullscreenIcon--enable">fullscreen</i>
<i *ngIf="isFullscreen" class="material-icons m-mediaModal__fullscreenIcon--disable">fullscreen_exit</i>
</div>
</div>
</div>
<!-- PAGER (DISABLED) -->
<!-- <a class="m-mediaModal__pager m-mediaModal__pager--previous">
<i class="material-icons">chevron-left</i>
</a>
<a class="m-mediaModal__pager m-mediaModal__pager--next">
<i class="material-icons">chevron-right</i>
</a> -->
</div>
</div>
<!-- CONTENT -->
<div class="m-mediaModal__contentContainer">
<div class="m-mediaModal__contentBody">
<div class="m-mediaModal__scrollableAreaContainer m-mediaModal__contentAfter">
<div class="m-mediaModal__scrollableAreaWrapper">
<!-- OWNER BLOCK -->
<div class="m-mediaModal__ownerBlock m-mediaModal__clearfix m-owner-block">
<!-- OWNER BLOCK AVATAR -->
<div class="m-mediaModal__ownerBlockAvatar avatar" [hovercard]="entity.ownerObj.guid">
<a [routerLink]="['/', entity.ownerObj.username]">
<img [src]="minds.cdn_url + 'icon/' + entity.ownerObj.guid + '/medium/' + ownerIconTime" class="mdl-shadow--2dp"/>
</a>
</div>
<!-- OWNER BLOCK BODY -->
<div class="m-mediaModal__ownerBlockBody">
<a class="m-ownerBlock__channelLink" [routerLink]="['/', entity.ownerObj.username]">
<strong title={{entity.ownerObj.name}}>{{entity.ownerObj.name}}</strong>
<m-channel--badges class="m-channel--badges-activity" [user]="entity.ownerObj" badges="[ 'admin', 'verified' ]"></m-channel--badges>
</a>
<a *ngIf="entity.containerObj && entity.containerObj.type == 'group'"
[routerLink]="['/groups/profile', entity.containerObj.guid]"
class="m-ownerBlock__groupLink mdl-color-text--blue-grey-300">
<strong>({{entity.containerObj.name}})</strong>
</a>
<!-- PERMALINK -->
<a [routerLink]="['/newsfeed', permalinkGuid]" class="permalink m-ownerBlock__permalink">
<span class="m-ownerBlock__permalinkDate">{{entity.time_created * 1000 | date:'medium'}}</span>
<span *ngIf="entity.edited" class="m-ownerBlock__permalinkFlag m-ownerBlock__permalinkFlag--edited" i18n="@@M__COMMON__EDITED">edited</span>
<div *ngIf="boosted"
class="mdl-card__supporting-text m-ownerBlock__permalinkFlag m-ownerBlock__permalinkFlag--boosted"
[class.m-ownerBlock__permalinkFlag--onchain]="entity.boosted_onchain"
>
<i class="material-icons">trending_up</i>
<a i18n="@@MINDS__CARDS__entity__BOOSTED_LABEL" [hidden]="entity.boosted_onchain">Boosted</a>
<a i18n="@@MINDS__CARDS__entity__BOOSTED_LABEL_ONCHAIN" [hidden]="!entity.boosted_onchain" [routerLink]="['/token']">Boosted Onchain</a>
</div>
</a>
</div>
</div>
<!-- MESSAGE -->
<div class="m-mediaModal__message mdl-card__supporting-text"
m-read-more *ngIf="hasMessage"
[maxHeightAllowed]="136"
>
<span class="m-mature-message-content" [innerHtml]="message | tags">
</span>
<m-read-more--button></m-read-more--button>
</div>
<!-- ACTION BUTTONS -->
<div class="m-mediaModal__actionButtonsWrapper">
<div class="m-mediaModal__actionButtonsRow m-action-tabs">
<m-wire-button *ngIf="session.getLoggedInUser().guid != entity.owner_guid"
[object]="entity"
(done)="wireSubmitted($event)"
></m-wire-button>
<minds-button-thumbs-up [object]="entity"></minds-button-thumbs-up>
<minds-button-thumbs-down [object]="entity"></minds-button-thumbs-down>
<minds-button-remind [object]="entity"></minds-button-remind>
</div>
</div>
<!-- COMMENTS -->
<div class="m-mediaModal__comments" *ngIf="entity.guid">
<m-comments__tree
[limit]="24"
[entity]="entity"
[scrollable]="true"
>
</m-comments__tree>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
This diff is collapsed.
This diff is collapsed.
......@@ -58,35 +58,35 @@
<h1 class="m-media-content--heading" [innerHtml]="entity.title | tags" [ngClass]="{'m-media--long-heading': entity.title && entity.title.length > 80}" >
</h1>
</div>
<!-- Additional block -->
<div class="m-media-content--info mdl-cell mdl-cell--4-col mdl-cell--6-col-phone">
<div class="m-media-content--play-count" *ngIf="entity['play:count'] || entity['play:count'] === 0">
<span i18n="@@MINDS__MEDIA__PLAYS_COUNTER">{{ entity['play:count'] | number }} plays</span>
</div>
<div class="m-media-content--play-count" *ngIf="entity['impressions'] || entity['impressions'] === 0">
<span i18n="@@M__COMMON__VIEWS_WITH_COUNT">{{ entity['impressions'] | number }} views</span>
</div>
<div class="m-visibility-info" *ngIf="entity.ownerObj.guid == session.getLoggedInUser().guid || session.isAdmin()">
<i class="material-icons">visibility</i>
<span [hidden]="entity.access_id != 2" i18n="@@M__COMMON__VISIBILITY_PUBLIC">Public</span>
<span [hidden]="entity.access_id != 0" i18n="@@M__COMMON__VISIBILITY_UNLISTED">Unlisted</span>
</div>
<div class="m-mature-info" *ngIf="entity.flags && entity.flags.mature">
<i class="material-icons">explicit</i>
<span i18n="@@M__COMMON__MATURE_CONTENT">Mature content</span>
</div>
<div class="m-license-info">
<i class="material-icons">public</i>
<span [hidden]="!entity.license">{{entity.license}}</span>
<span [hidden]="entity.license" i18n="@@M__COMMON__LICENSE_ALL_RIGHT_RESERVED">All Rights Reserved</span>
</div>
</div>
</div>
......@@ -118,18 +118,18 @@
<!-- Actions block -->
<div class="mdl-cell mdl-cell--6-col m-media-content--actions">
<div class="mdl-layout-spacer"></div>
<span class="minds-button-edit" [routerLink]="['/media/edit', entity.guid]" *ngIf="entity.ownerObj.guid == session.getLoggedInUser().guid || session.isAdmin()">
<button class="material-icons">edit</button>
</span>
<!--<span class="minds-button-edit" (click)="deleteToggle = true" *ngIf="entity.ownerObj.guid == session.getLoggedInUser().guid || session.isAdmin()">
<button class="material-icons">close</button>
</span>-->
<minds-button-feature [object]="entity" *ngIf="session.isAdmin()"></minds-button-feature>
<minds-button-monetize [object]="entity" *ngIf="session.getLoggedInUser().monetized"></minds-button-monetize>
<m-wire-button *ngIf="entity.ownerObj.guid != session.getLoggedInUser()?.guid"
[object]="entity"
></m-wire-button>
......@@ -163,12 +163,12 @@
[opts]="{ current: entity.guid, next: getNext(), channel: entity.ownerObj.guid, type: entity.subtype }"
limit="6"
></m-media--recommended>
<!--<m-ads-boost limit="4"></m-ads-boost>-->
</div>
</div>
</div>
<div class="m-media-recommended"
*ngIf="!theaterMode && entity.guid && entity.subtype != 'album'"
>
......
import { Component } from '@angular/core';
import { Component, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { timer, Subscription } from 'rxjs';
......@@ -6,6 +6,7 @@ import { Client } from '../../../../services/api';
import { Session } from '../../../../services/session';
import { RecommendedService } from '../../components/video/recommended.service';
import { MindsVideoComponent } from '../../components/video/video.component';
@Component({
selector: 'm-media--theatre',
......@@ -41,9 +42,10 @@ import { RecommendedService } from '../../components/video/recommended.service';
[torrent]="[{ res: '720', key: object.guid + '/720.mp4' }, { res: '360', key: object.guid + '/360.mp4' }]"
[log]="object.guid"
[playCount]="false"
(click)="togglePlay($event)"
#player>
<video-ads [player]="player" *ngIf="object.monetized"></video-ads>
</m-video>
</div>
......@@ -67,9 +69,11 @@ export class MediaTheatreComponent {
minds = window.Minds;
@ViewChild( MindsVideoComponent, { static: false }) videoComponent: MindsVideoComponent;
constructor(
public session: Session,
public client: Client,
public client: Client,
public router: Router,
private recommended: RecommendedService
) {
......@@ -136,6 +140,22 @@ export class MediaTheatreComponent {
this.timerSubscribe.unsubscribe();
}
togglePlay($event) {
this.videoComponent.toggle();
}
// Show video controls
onMouseEnterStage() {
this.videoComponent.stageHover = true;
this.videoComponent.onMouseEnter();
}
// Hide video controls
onMouseLeaveStage() {
this.videoComponent.stageHover = false;
this.videoComponent.onMouseLeave();
}
ngOnDestroy() {
if (this.timerSubscribe) {
this.timerSubscribe.unsubscribe();
......
......@@ -20,7 +20,7 @@ export class SignupModalService {
}
constructor(private router: Router, public scroll: ScrollService) {
console.log('modal service constructed');
// console.log('modal service constructed');
this.initOnScroll();
}
......
......@@ -17,7 +17,7 @@ export class NoticesService {
return this.notices;
const timestamp = Date.now();
this.notices = (<{ notices }>await this.client.get(`${NOTICES_JSON_URL}?t=${timestamp}`).toPromise()).notices;
console.log(this.notices);
// console.log(this.notices);
return this.notices;
}
}
......@@ -404,7 +404,7 @@
<span class="pseudo-link mdl-color-text--blue-grey-400" *ngIf="notification.entityObj && !notification.entityObj.title && notification.entityObj.name">{{notification.entityObj.name}}</span>
<span class="pseudo-link mdl-color-text--blue-grey-400" *ngIf="notification.entityObj && !notification.entityObj.title && !notification.entityObj.name && notification.entityObj.type !== 'user'" i18n="@@NOTIFICATIONS__NOTIFICATION__YOUR_POST">your post.</span>
<span class="pseudo-link mdl-color-text--blue-grey-400" *ngIf="notification.entityObj && !notification.entityObj.title && !notification.entityObj.name && notification.entityObj.type === 'user'" i18n="@@NOTIFICATIONS__NOTIFICATION__YOUR_CHANNEL">your channel.</span>
<ng-container i18n="@@NOTIFICATIONS__NOTIFICATION__BOOST_REVOKED__REFUNDED">Your offer has been refunded.</ng-container>
<ng-container i18n="@@NOTIFICATIONS__NOTIFICATION__BOOST_REVOKED__REFUNDED"> Your offer has been refunded.</ng-container>
</p>
</a>
</ng-template>
......