...
 
Commits (2)
This diff is collapsed.
......@@ -25,7 +25,6 @@ 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';
import { ProContentModalComponent } from "../pro/channel/content-modal/modal.component";
const routes: Routes = [
{ path: 'media/videos/:filter', component: MediaVideosListComponent },
......@@ -78,7 +77,6 @@ const routes: Routes = [
MediaEditComponent,
MediaViewComponent,
MediaModalComponent,
ProContentModalComponent,
]
})
......
......@@ -158,4 +158,4 @@
</div>
</div>
<m-overlay-modal #overlayModal></m-overlay-modal>
<m-overlay-modal #overlayModal style="z-index:99999999"></m-overlay-modal>
......@@ -4,13 +4,21 @@ import { MindsUser } from '../../../interfaces/entities';
import { Client } from '../../../services/api/client';
import { EntitiesService } from '../../../common/services/entities.service';
import normalizeUrn from '../../../helpers/normalize-urn';
import { ProContentModalComponent } from './content-modal/modal.component';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { BlogView } from "../../blogs/view/view";
import { Session } from '../../../services/session';
import { ActivatedRoute } from '@angular/router';
export type RouterLinkToType = 'home' | 'all' | 'feed' | 'videos' | 'images' | 'articles' | 'groups' | 'donate' | 'login';
export type RouterLinkToType =
'home'
| 'all'
| 'feed'
| 'videos'
| 'images'
| 'articles'
| 'groups'
| 'donate'
| 'login';
@Injectable()
export class ProChannelService {
......@@ -175,12 +183,6 @@ export class ProChannelService {
case 'object:blog':
modalServiceContext.create(BlogView, entity).present();
break;
case 'object:image':
case 'object:video':
modalServiceContext.create(ProContentModalComponent, entity, {
class: 'm-overlayModal--media'
}).present();
break;
}
}
......
<div class="m-mediaModal mdl-shadow--4dp">
<!-- This is the element that goes into fullscreen -->
<!-- <div class="m-mediaModal__theater"> -->
<!-- Media: image -->
<div class="m-mediaModal__theater m-mediaModal__theater--image" *ngIf="entity.subtype === 'image'">
<img [src]="entity.thumbnail_src" (load)="inProgress = false"/>
<div class="mdl-spinner mdl-js-spinner is-active" [mdl] *ngIf="inProgress"></div>
<div class="m-mediaModal__theaterOverlay">
<a class="m-mediaModal__theaterOverlayTitle" [routerLink]="['/media', entity.entity_guid]">{{title}}</a>
<div class="m-mediaModal__fullscreenButton"
(click)="toggleFullscreen()"
(mouseenter)="hovering=true"
(mouseleave)="hovering=false"
[class.m-mediaModal__fullscreenButton--active]="hovering"
>
<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>
<!-- Media: video -->
<div class="m-mediaModal__theater m-mediaModal__theater--video" *ngIf="entity.subtype === 'video'">
<m-video
width="100%"
height="300px"
[muted]="false"
[poster]="entity.thumbnail_src"
[src]="[{ 'res': '360', 'uri': 'api/v1/media/' + entity.guid + '/play', 'type': 'video/mp4' }]"
[guid]="entity.guid"
[playCount]="entity['play:count']"
[torrent]="[{ res: '360', key: entity.guid + '/360.mp4' }]"
#player>
<video-ads [player]="player" *ngIf="entity.monetized"></video-ads>
</m-video>
</div>
<!-- </div> -->
<div class="m-mediaModal__contentWrapper">
<!-- Owner block -->
<div class="m-mediaModal__ownerBlock m-owner-block">
<div class="m-ownerBlockAvatar avatar" [hovercard]="entity.ownerObj.guid">
<a [routerLink]="['/', entity.ownerObj.username]">
<img [src]="minds.cdn_url + 'icon/' + entity.ownerObj.guid + '/medium/' + getOwnerIconTime()" class="mdl-shadow--2dp"/>
</a>
</div>
<div class="m-ownerBlockBody body">
<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-entity" [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', entity.guid]" class="permalink m-ownerBlock__permalink" *ngIf="entity.guid && !entity.remind_object">
<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>
</a>
</div>
</div>
</div>
</div>
.minds-avatar-hovercard {
z-index: 9999999;
}
.m-overlay-modal.m-overlayModal--media {
padding: 0px;
max-width: 95%;
z-index: 9999998;
}
m-media--modal {
// display: block;
// position: relative;
}
.m-mediaModal {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
max-height: 100%;
max-height: 98vh;
.m-mediaModal__theater {
flex: 1 1 auto;
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 100%;
overflow: hidden; // .????
@include m-theme() {
background-color: rgba(themed($m-black-always), 0.95);
}
&:hover {
.m-mediaModal__theaterOverlay {
opacity: 1;
}
}
img {
object-fit: contain;
flex: 1;
overflow: hidden;
}
m-video {
@include m-theme() {
background-color: themed($m-black-always);
}
video {
max-width: 100%;
height: 100%;
}
}
.m-mediaModal__theaterOverlay {
display: flex;
justify-content: space-between;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 70px;
padding: $minds-padding * 2;
opacity: 0;
transition: opacity 500ms cubic-bezier(.22, 1, .33, 1);
box-sizing: border-box;
font-size: 24px;
@include m-theme() {
background: linear-gradient(rgba(themed($m-black-always), 0.5), transparent);
}
&:hover {
cursor: pointer;
}
.m-mediaModal__theaterOverlayTitle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-decoration: none;
color: var(--m-pro--text-color);
&:hover {
text-decoration: underline;
}
}
// TODO OJM fullscreen styles e.g. .myClass:not(:fullscreen)
.m-mediaModal__fullscreenButton {
color: var(--m-pro--text-color);
transition: transform 150ms ease;
// .m-tooltip--bubble {
// right: 8px;
// }
&.m-mediaModal__fullscreenButton--active {
.m-mediaModal__fullscreenIcon--enable {
transform: scale(1.2);
}
.m-mediaModal__fullscreenIcon--disable {
transform: scale(0.83);
}
}
.m-mediaModal__fullscreenIcon--disable {
font-size: 28px;
}
.m-mediaModal__fullscreenIcon--disable {
font-size: 34px;
}
}
}
}
// * Content ********************************************
.m-mediaModal__contentWrapper {
// border: 1px solid green;
display: flex;
flex-direction: column;
width: calc(33% - 12px); //1/3rd of screen
// width: 300px;
min-width: 300px;
height: 100%;
height: 90vh;
padding: 16px;
font-family: 'Roboto', Helvetica, sans-serif;
background-color: var(--m-pro--plain-background-color);
.m-mediaModal__ownerBlock {
padding: 0;
strong {
vertical-align: middle;
letter-spacing: 0.25px;
text-rendering: optimizeLegibility;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 500;
}
.m-ownerBlock__channelLink, .m-ownerBlock__groupLink {
text-decoration: none;
}
.m-ownerBlock__permalink {
font-weight: 500;
text-decoration: none;
text-transform: uppercase;
letter-spacing: .75px;
text-rendering: optimizeLegibility;
text-overflow: unset;
white-space: pre-wrap;
line-height: 18px;
-webkit-font-smoothing: antialiased;
color: var(--m-pro--text-color);
> * {
padding-right: 8px;
}
&:hover {
.m-ownerBlock__permalinkDate {
text-decoration: underline;
}
}
.m-ownerBlock__permalinkFlag {
text-transform: uppercase;
padding: 0 8px 0 0;
letter-spacing: 0.5px;
font-weight: 700;
font-size: 11px;
line-height: 1;
color: var(--m-pro--text-color);
}
.m-ownerBlock__permalinkFlag--boosted {
display: inline-block;
width: auto;
vertical-align: middle;
i {
font-size: 18px;
position: relative;
vertical-align: middle;
}
a {
vertical-align: middle;
color: var(--m-pro--text-color);
}
&.m-ownerBlock__permalinkFlag--onchain {
color: var(--m-pro--primary-color);
a {
@include m-theme() {
color: var(--m-pro--primary-color);
}
}
}
}
}
}
.m-mediaModal__actionButtonsWrapper {
.m-mediaModal__actionButtonsRow {
display: flex;
justify-content: space-around;
padding-top: 16px;
}
minds-button-boost {
button {
margin-top: 8px;
}
}
}
// * Comments ********************************************
.m-mediaModal__commentsWrapper {
padding-top: $minds-padding;
@include m-theme() {
border-top: 1px solid themed($m-grey-100);
}
display: block;
margin-bottom: 0;
flex: 1;
m-comments__tree {
height: 100%;
// height: calc(100vh - 45px);
position: relative;
padding: 0 0 24px;
margin: 0;
flex-direction: column;
box-sizing: border-box;
width: 100%;
m-comments__thread {
height: 100%;
// height: calc(100vh - 45px);
position: relative;
margin: 0;
flex-direction: column;
box-sizing: border-box;
.m-comments__thread {
overflow-y: auto;
max-height: auto;
flex: 1;
display: flex;
flex-direction: column;
.minds-avatar {
width: 36px;
height: 36px;
margin-left: 0;
}
.m-comment-attachment .item-image {
img, minds-video {
max-width: 50%;
}
}
}
.minds-comment {
padding: 8px 0px;
.mdl-card__menu {
display: none;
}
&:hover .mdl-card__menu {
display: block;
}
.m-comment-attachment .m-rich-embed {
max-width: 63%;
.m-blurb {
display: block;
}
}
}
.minds-comment-post {
flex-shrink: 0;
}
.post-preview {
width: 63%;
height: auto;
.attachment-preview {
height: 140px;
object-fit: cover;
}
}
.minds-comments-container {
overflow-y: visible;
height: auto;
max-height: none;
}
}
}
}
}
}
import { Component, Input, OnInit } from '@angular/core';
import { Session } from "../../../../services/session";
@Component({
selector: 'm-pro--content-modal',
templateUrl: 'modal.component.html'
})
export class ProContentModalComponent implements OnInit {
minds = window.Minds;
entity: any;
inProgress: boolean;
hovering: boolean;
isFullscreen: boolean;
title: string;
@Input('entity') set data(data) {
this.entity = data;
this.title = this.entity.title || this.entity.message || `${this.entity.ownerObj.name}'s media`;
}
constructor(
protected session: Session
) {
}
ngOnInit() {
}
toggleFullscreen() {
}
getOwnerIconTime() {
const session = this.session.getLoggedInUser();
if (session && session.guid === this.entity.ownerObj.guid) {
return session.icontime;
} else {
return this.entity.ownerObj.icontime;
}
}
}
......@@ -6,7 +6,7 @@ import { OverlayModalService } from '../../../../services/ux/overlay-modal';
@Component({
selector: 'm-pro--channel-home',
changeDetection: ChangeDetectionStrategy.OnPush,
changeDetection: ChangeDetectionStrategy.Default,
templateUrl: 'home.component.html',
})
export class ProChannelHomeComponent implements OnInit {
......
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, ViewChild } from '@angular/core';
import { ProChannelService } from '../channel.service';
import { FeedsService } from '../../../../common/services/feeds.service';
import { ProContentModalComponent } from '../content-modal/modal.component';
import { OverlayModalService } from '../../../../services/ux/overlay-modal';
import { OverlayModalComponent } from '../../../../common/components/overlay-modal/overlay-modal.component';
......
......@@ -16,7 +16,6 @@
<ng-container *ngIf="type === 'all' || type === 'images' || type === 'videos' || type === 'blogs'">
<m-pro--channel-tile
[entity]="entity"
(click)="onTileClicked(entity)"
></m-pro--channel-tile>
</ng-container>
<ng-container *ngIf="type === 'groups'">
......
......@@ -5,9 +5,7 @@ import { FeedsService } from "../../../../common/services/feeds.service";
import { ProChannelService, RouterLinkToType } from '../channel.service';
import { first } from "rxjs/operators";
import { OverlayModalService } from "../../../../services/ux/overlay-modal";
import { ProContentModalComponent } from "../content-modal/modal.component";
import { ProChannelListModal } from '../list-modal/list-modal.component';
import { Tag } from "../../../../interfaces/entities";
@Component({
selector: 'm-pro--channel-list',
......
import { Component, Input } from '@angular/core';
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
import { MediaModalComponent } from "../../../../media/modal/modal.component";
import { FeaturesService } from "../../../../../services/features.service";
import { OverlayModalService } from "../../../../../services/ux/overlay-modal";
import { Router } from "@angular/router";
import toMockActivity from "../../util/mock-activity";
@Component({
selector: 'm-pro--channel-tile',
template: `
<img [src]="entity.thumbnail_src">
<img
[src]="entity.thumbnail_src"
(click)="showMediaModal()"
*ngIf="getType(entity) === 'object:image'; else videoBlock"
#img
>
<ng-template #videoBlock>
<m-video
width="100%"
height="300px"
[muted]="false"
[poster]="entity.thumbnail_src"
[src]="[{ 'res': '360', 'uri': 'api/v1/media/' + entity.guid + '/play', 'type': 'video/mp4' }]"
[guid]="entity.guid"
[playCount]="entity['play:count']"
[torrent]="[{ res: '360', key: entity.guid + '/360.mp4' }]"
[isActivity]="true"
(videoMetadataLoaded)="setVideoDimensions($event)"
(mediaModalRequested)="showMediaModal()"
#player>
<video-ads [player]="player" *ngIf="entity.monetized"></video-ads>
</m-video>
</ng-template>
<div class="m-proChannelTile__text" *ngIf="getTitle() || getText()">
<h2 [title]="getTitle()">{{ getTitle() }}</h2>
</div>
......@@ -12,13 +40,23 @@ import { Component, Input } from '@angular/core';
export class ProTileComponent {
@Input() entity: any;
@ViewChild('img', { static: false }) img: ElementRef;
videoDimensions: Array<any> = null;
constructor(
protected featuresService: FeaturesService,
protected modalService: OverlayModalService,
protected router: Router,
) {
}
static getType(entity: any) {
getType(entity: any) {
return entity.type === 'object' ? `${entity.type}:${entity.subtype}` : entity.type;
}
getTitle() {
switch (ProTileComponent.getType(this.entity)) {
switch (this.getType(this.entity)) {
case 'object:blog':
return this.entity.title;
case 'object:image':
......@@ -30,7 +68,7 @@ export class ProTileComponent {
}
getText() {
switch (ProTileComponent.getType(this.entity)) {
switch (this.getType(this.entity)) {
case 'object:blog':
return this.entity.excerpt;
case 'object:image':
......@@ -41,4 +79,35 @@ export class ProTileComponent {
}
}
setVideoDimensions($event) {
this.videoDimensions = $event.dimensions;
}
showMediaModal() {
const activity = toMockActivity(this.entity);
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.entity.guid}`]);
// }
if (activity.custom_type === 'video') {
activity.custom_data.dimensions = this.videoDimensions;
} else { // Image
// Set image dimensions if they're not already there
const img: HTMLImageElement = this.img.nativeElement;
activity.custom_data[0].width = img.naturalWidth;
activity.custom_data[0].height = img.naturalHeight;
}
activity.modal_source_url = this.router.url;
this.modalService.create(MediaModalComponent, activity, {
class: 'm-overlayModal--media'
}).present();
} else {
this.router.navigate([`/media/${activity.entity_guid}`]);
}
}
}
/**
* generates an activity from an image or video
* @param entity
*/
export default function toMockActivity(entity: any) {
let obj = {
...entity,
entity_guid: entity.guid,
custom_type: entity.subtype,
};
if (entity.subtype === 'video') {
obj.custom_data = {
...entity,
dimensions: this.videoDimensions
};
} else {
obj.custom_data = [{
...entity,
width: 0,
height: 0
}];
}
return obj;
}
......@@ -16,7 +16,6 @@ import { ProSettingsComponent } from './settings/settings.component';
import { ProChannelFooterComponent } from './channel/footer/footer.component';
import { LegacyModule } from "../legacy/legacy.module";
import { WireModule } from "../wire/wire.module";
import { ProContentModalComponent } from "./channel/content-modal/modal.component";
import { VideoModule } from "../media/components/video/video.module";
import { ProChannelListModal } from './channel/list-modal/list-modal.component';
import { ProChannelHomeComponent } from './channel/home/home.component';
......@@ -24,6 +23,7 @@ import { ProGroupTileComponent } from "./channel/tiles/group/group-tile.componen
import { ProUnsubscribeModalComponent } from './channel/unsubscribe-modal/modal.component';
import { ProCategoriesComponent } from "./channel/categories/categories.component";
import { BlogView } from "../blogs/view/view";
import { MediaModalComponent } from "../media/modal/modal.component";
const routes: Routes = [
{
......@@ -101,7 +101,6 @@ export const STANDALONE_ROUTES = [
ProSettingsComponent,
ProSubscriptionComponent,
ProTileComponent,
ProContentModalComponent,
ProChannelHomeComponent,
ProCategoriesComponent,
ProChannelListModal,
......@@ -116,6 +115,7 @@ export const STANDALONE_ROUTES = [
ProChannelComponent
],
entryComponents: [
MediaModalComponent,
ProChannelListModal,
ProUnsubscribeModalComponent,
BlogView,
......