Redesign channel page
This commit is contained in:
parent
4097c6d66c
commit
60c35932f6
|
@ -1,22 +0,0 @@
|
||||||
<div class="margin-content">
|
|
||||||
<div *ngIf="videoChannel" class="row no-gutters">
|
|
||||||
<div class="description col-md-6 col-sm-12 pr-2">
|
|
||||||
<div class="block">
|
|
||||||
<div i18n class="small-title">DESCRIPTION</div>
|
|
||||||
<div class="content" [innerHtml]="getVideoChannelDescription()"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="block" *ngIf="supportHTML">
|
|
||||||
<div i18n class="small-title">SUPPORT THIS CHANNEL</div>
|
|
||||||
<div class="content" [innerHtml]="supportHTML"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stats col-md-6 col-sm-12">
|
|
||||||
<div class="block">
|
|
||||||
<div i18n class="small-title">STATS</div>
|
|
||||||
<div i18n class="content">Created {{ videoChannel.createdAt | date }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,12 +0,0 @@
|
||||||
@import '_variables';
|
|
||||||
@import '_mixins';
|
|
||||||
|
|
||||||
.block {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
|
|
||||||
.small-title {
|
|
||||||
@include in-content-small-title;
|
|
||||||
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { Subscription } from 'rxjs'
|
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
|
||||||
import { MarkdownService } from '@app/core'
|
|
||||||
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-video-channel-about',
|
|
||||||
templateUrl: './video-channel-about.component.html',
|
|
||||||
styleUrls: [ './video-channel-about.component.scss' ]
|
|
||||||
})
|
|
||||||
export class VideoChannelAboutComponent implements OnInit, OnDestroy {
|
|
||||||
videoChannel: VideoChannel
|
|
||||||
descriptionHTML = ''
|
|
||||||
supportHTML = ''
|
|
||||||
|
|
||||||
private videoChannelSub: Subscription
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
private videoChannelService: VideoChannelService,
|
|
||||||
private markdownService: MarkdownService
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit () {
|
|
||||||
// Parent get the video channel for us
|
|
||||||
this.videoChannelSub = this.videoChannelService.videoChannelLoaded
|
|
||||||
.subscribe(async videoChannel => {
|
|
||||||
this.videoChannel = videoChannel
|
|
||||||
|
|
||||||
this.descriptionHTML = await this.markdownService.textMarkdownToHTML(this.videoChannel.description)
|
|
||||||
this.supportHTML = await this.markdownService.enhancedMarkdownToHTML(this.videoChannel.support)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy () {
|
|
||||||
if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
getVideoChannelDescription () {
|
|
||||||
if (this.descriptionHTML) return this.descriptionHTML
|
|
||||||
|
|
||||||
return $localize`No description`
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router'
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
import { MetaGuard } from '@ngx-meta/core'
|
import { MetaGuard } from '@ngx-meta/core'
|
||||||
import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component'
|
|
||||||
import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component'
|
import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component'
|
||||||
import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
|
import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
|
||||||
import { VideoChannelsComponent } from './video-channels.component'
|
import { VideoChannelsComponent } from './video-channels.component'
|
||||||
|
@ -38,15 +37,6 @@ const videoChannelsRoutes: Routes = [
|
||||||
title: $localize`Video channel playlists`
|
title: $localize`Video channel playlists`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'about',
|
|
||||||
component: VideoChannelAboutComponent,
|
|
||||||
data: {
|
|
||||||
meta: {
|
|
||||||
title: $localize`About video channel`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,114 @@
|
||||||
<div *ngIf="videoChannel" class="row">
|
<div class="root" *ngIf="videoChannel">
|
||||||
<div class="sub-menu">
|
<div class="channel-info">
|
||||||
|
|
||||||
<div class="actor">
|
<ng-template #buttonsTemplate>
|
||||||
<img [src]="videoChannel.avatarUrl" alt="Avatar" />
|
<a *ngIf="isManageable() && !isInSmallView()" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="peertube-button-link orange-button" i18n>
|
||||||
|
Manage channel
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="actor-info">
|
<my-subscribe-button #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button>
|
||||||
<div class="actor-names">
|
</ng-template>
|
||||||
<div class="actor-display-name">{{ videoChannel.displayName }}</div>
|
|
||||||
<div class="actor-name">
|
<ng-template #ownerTemplate>
|
||||||
<span>{{ videoChannel.nameWithHost }}</span>
|
<div class="owner-block">
|
||||||
<button [cdkCopyToClipboard]="videoChannel.nameWithHostForced" (click)="activateCopiedMessage()"
|
<div class="avatar-row">
|
||||||
class="btn btn-outline-secondary btn-sm copy-button"
|
<img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" />
|
||||||
>
|
|
||||||
<span class="glyphicon glyphicon-duplicate"></span>
|
<div class="actor-info">
|
||||||
</button>
|
<h4>{{ videoChannel.ownerAccount.displayName }}</h4>
|
||||||
|
|
||||||
|
<div class="actor-handle">@{{ videoChannel.ownerBy }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="owner-description">
|
||||||
<a *ngIf="isChannelManageable && !isInSmallView" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="btn btn-outline-tertiary mr-2" i18n>
|
<div class="description-html" [innerHTML]="ownerDescriptionHTML"></div>
|
||||||
Manage channel
|
|
||||||
</a>
|
|
||||||
<my-subscribe-button #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actor-lower">
|
<a class="view-account short" [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n>
|
||||||
<div class="actor-followers" i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div>
|
View account
|
||||||
|
</a>
|
||||||
|
|
||||||
<a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Go the owner account page" class="actor-owner">
|
<a class="view-account complete" [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n>
|
||||||
<span class="d-inline-flex"><span i18n class="d-none d-sm-block mr-1">Created by</span>{{ videoChannel.ownerBy }}</span>
|
View owner account
|
||||||
<img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" />
|
</a>
|
||||||
</a>
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="channel-avatar-row">
|
||||||
|
<img [src]="videoChannel.avatarUrl" alt="Avatar" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="section-label" i18n>VIDEO CHANNEL</div>
|
||||||
|
|
||||||
|
<div class="actor-info">
|
||||||
|
<div>
|
||||||
|
<div class="actor-display-name">
|
||||||
|
<h1>{{ videoChannel.displayName }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actor-handle">
|
||||||
|
<span>@{{ videoChannel.nameWithHost }}</span>
|
||||||
|
<button [cdkCopyToClipboard]="videoChannel.nameWithHostForced" (click)="activateCopiedMessage()"
|
||||||
|
class="btn btn-outline-secondary btn-sm copy-button" title="Copy channel handle" i18n-title
|
||||||
|
>
|
||||||
|
<span class="glyphicon glyphicon-duplicate"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actor-counters">
|
||||||
|
<span i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</span>
|
||||||
|
|
||||||
|
<span class="videos-count" *ngIf="channelVideosCount !== undefined" i18n>
|
||||||
|
{channelVideosCount, plural, =1 {1 videos} other {{{ channelVideosCount }} videos}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="channel-buttons right">
|
||||||
|
<ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="links w-100">
|
<div class="channel-description" [ngClass]="{ expanded: channelDescriptionExpanded }">
|
||||||
<ng-template #linkTemplate let-item="item">
|
<div class="description-html" [innerHTML]="channelDescriptionHTML"></div>
|
||||||
<a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow>
|
<div class="created-at" i18n>Channel created on {{ videoChannel.createdAt | date }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!channelDescriptionExpanded" class="show-more" role="button"
|
||||||
|
(click)="channelDescriptionExpanded = !channelDescriptionExpanded"
|
||||||
|
title="Show the complete description" i18n-title i18n
|
||||||
|
>
|
||||||
|
Show more...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="channel-buttons bottom">
|
||||||
|
<ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="owner-card">
|
||||||
|
<div class="section-label" i18n>OWNER ACCOUNT</div>
|
||||||
|
|
||||||
|
<ng-template *ngTemplateOutlet="ownerTemplate"></ng-template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="margin-content">
|
<div class="bottom-owner">
|
||||||
<router-outlet></router-outlet>
|
<div class="section-label" i18n>OWNER ACCOUNT</div>
|
||||||
|
|
||||||
|
<ng-template *ngTemplateOutlet="ownerTemplate"></ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="links">
|
||||||
|
<ng-template #linkTemplate let-item="item">
|
||||||
|
<a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,89 +1,345 @@
|
||||||
// Bootstrap grid utilities require functions, variables and mixins
|
|
||||||
@import 'node_modules/bootstrap/scss/functions';
|
|
||||||
@import 'node_modules/bootstrap/scss/variables';
|
|
||||||
@import 'node_modules/bootstrap/scss/mixins';
|
|
||||||
@import 'node_modules/bootstrap/scss/grid';
|
|
||||||
|
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
@import '_miniature';
|
||||||
|
|
||||||
.sub-menu {
|
.root {
|
||||||
@include sub-menu-with-actor;
|
--myGlobalPadding: 60px;
|
||||||
|
--myChannelImgMargin: 30px;
|
||||||
|
--myFontSize: 16px;
|
||||||
|
--myGreyChannelFontSize: 16px;
|
||||||
|
--myGreyOwnerFontSize: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.actor, .actor-info {
|
.section-label {
|
||||||
width: 100%;
|
color: pvar(--mainColor);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
@include fluid-videos-miniature-layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-info {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
|
||||||
|
background-color: pvar(--channelBackgroundColor);
|
||||||
|
margin-bottom: 45px;
|
||||||
|
padding: var(--myGlobalPadding) var(--myGlobalPadding) 0 var(--myGlobalPadding);
|
||||||
|
font-size: var(--myFontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-avatar-row {
|
||||||
|
display: flex;
|
||||||
|
grid-column: 1;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include channel-avatar(120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-left: var(--myChannelImgMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-info {
|
.actor-info {
|
||||||
display: grid !important;
|
display: flex;
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
grid-template-rows: 1fr auto / 1fr auto;
|
|
||||||
grid-template-areas: "name buttons" "lower buttons";
|
|
||||||
|
|
||||||
@include media-breakpoint-down(lg) {
|
> div:first-child {
|
||||||
grid-template-areas: "name name" "lower buttons";
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-names {
|
.actor-display-name {
|
||||||
grid-area: name;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-name {
|
h1 {
|
||||||
flex-grow: 1;
|
font-size: 28px;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.copy-button {
|
.actor-handle,
|
||||||
border: none;
|
.actor-counters {
|
||||||
padding: 5px;
|
color: pvar(--greyForegroundColor);
|
||||||
margin-top: -2px;
|
font-size: var(--myGreyChannelFontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actor-counters > *:not(:last-child)::after {
|
||||||
|
content: '•';
|
||||||
|
margin: 0 10px;
|
||||||
|
color: pvar(--mainColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-content {
|
.channel-description {
|
||||||
// margin-content is required, but child views have their own margins
|
grid-column: 1;
|
||||||
// that match views outside the scope of accounts, so we only align
|
|
||||||
// them with the margins of .sub-menu when required.
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-buttons {
|
.show-more {
|
||||||
|
display: none;
|
||||||
|
color: pvar(--mainColor);
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 10px auto 45px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.channel-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: max-content;
|
flex-wrap: wrap;
|
||||||
margin-left: auto;
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
grid-row: buttons-start / span buttons-end;
|
> *:not(:last-child) {
|
||||||
grid-column: buttons-start;
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include media-breakpoint-down(lg) {
|
.channel-buttons.right {
|
||||||
flex-flow: column-reverse;
|
margin-left: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
// Only used by mobile
|
||||||
margin-top: 0.25rem;
|
.channel-buttons.bottom {
|
||||||
margin-right: 0 !important;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.created-at {
|
||||||
|
margin-top: 15px;
|
||||||
|
color: pvar(--greyForegroundColor);
|
||||||
|
padding-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-card {
|
||||||
|
margin-left: 105px;
|
||||||
|
grid-column: 2;
|
||||||
|
// Takes all the column
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
place-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only used on mobile
|
||||||
|
.bottom-owner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-block {
|
||||||
|
background-color: pvar(--mainBackgroundColor);
|
||||||
|
padding: 30px;
|
||||||
|
width: 300px;
|
||||||
|
font-size: var(--myFontSize);
|
||||||
|
|
||||||
|
.avatar-row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include avatar(48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-info {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-handle {
|
||||||
|
font-size: var(--myGreyOwnerFontSize);
|
||||||
|
color: pvar(--greyForegroundColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
.owner-description {
|
||||||
@include peertube-button-outline;
|
height: 140px;
|
||||||
line-height: 1.8;
|
|
||||||
|
@include fade-text(120px, pvar(--mainBackgroundColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-account.short {
|
||||||
|
@include peertube-button-link;
|
||||||
|
@include orange-button-inverted;
|
||||||
|
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-account.complete {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1400px) {
|
||||||
|
// Takes all the row width
|
||||||
|
.channel-avatar-row {
|
||||||
|
grid-column: 1 / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
my-subscribe-button {
|
.owner-card {
|
||||||
height: min-content;
|
grid-row: 2;
|
||||||
|
margin-left: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1100px) {
|
||||||
|
.root {
|
||||||
|
--myGlobalPadding: 45px;
|
||||||
|
--myChannelImgMargin: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-description:not(.expanded) {
|
||||||
|
max-height: 70px;
|
||||||
|
|
||||||
|
@include fade-text(30px, pvar(--channelBackgroundColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-more {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-buttons.bottom {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-buttons.right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-card {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-owner {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 2px solid $separator-border-color;
|
||||||
|
padding: var(--myGlobalPadding) 45px;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-block {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.avatar-row {
|
||||||
|
grid-column: 1;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-description {
|
||||||
|
grid-column: 2;
|
||||||
|
max-height: 70px;
|
||||||
|
|
||||||
|
@include fade-text(30px, pvar(--mainBackgroundColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-account {
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-account.complete {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: pvar(--mainColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-account.short {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-view) {
|
@media screen and (max-width: $mobile-view) {
|
||||||
.sub-menu {
|
.root {
|
||||||
.actor {
|
--myGlobalPadding: 15px;
|
||||||
flex-direction: column;
|
--myFontSize: 14px;
|
||||||
|
--myGreyChannelFontSize: 13px;
|
||||||
|
--myGreyOwnerFontSize: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.actor-info .actor-names {
|
.links {
|
||||||
flex-direction: column;
|
margin: auto !important;
|
||||||
align-items: normal;
|
width: min-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-label {
|
||||||
|
font-size: 10px;
|
||||||
|
letter-spacing: 2.1px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-avatar-row {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include channel-avatar(80px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-more {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-owner {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
.section-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-block {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.avatar-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actor-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include channel-avatar(64px);
|
||||||
|
|
||||||
|
margin: -30px 0 0 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-description {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { Subscription } from 'rxjs'
|
||||||
import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators'
|
import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators'
|
||||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { AuthService, Notifier, RestExtractor, ScreenService } from '@app/core'
|
import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService } from '@app/core'
|
||||||
import { ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main'
|
import { ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
|
||||||
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
|
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
|
||||||
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
||||||
|
|
||||||
|
@ -20,6 +20,11 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
links: ListOverflowItem[] = []
|
links: ListOverflowItem[] = []
|
||||||
isChannelManageable = false
|
isChannelManageable = false
|
||||||
|
|
||||||
|
channelVideosCount: number
|
||||||
|
ownerDescriptionHTML = ''
|
||||||
|
channelDescriptionHTML = ''
|
||||||
|
channelDescriptionExpanded = false
|
||||||
|
|
||||||
private routeSub: Subscription
|
private routeSub: Subscription
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
@ -27,9 +32,11 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private videoChannelService: VideoChannelService,
|
private videoChannelService: VideoChannelService,
|
||||||
|
private videoService: VideoService,
|
||||||
private restExtractor: RestExtractor,
|
private restExtractor: RestExtractor,
|
||||||
private hotkeysService: HotkeysService,
|
private hotkeysService: HotkeysService,
|
||||||
private screenService: ScreenService
|
private screenService: ScreenService,
|
||||||
|
private markdown: MarkdownService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
@ -43,16 +50,14 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
HttpStatusCode.NOT_FOUND_404
|
HttpStatusCode.NOT_FOUND_404
|
||||||
]))
|
]))
|
||||||
)
|
)
|
||||||
.subscribe(videoChannel => {
|
.subscribe(async videoChannel => {
|
||||||
|
this.channelDescriptionHTML = await this.markdown.textMarkdownToHTML(videoChannel.description)
|
||||||
|
this.ownerDescriptionHTML = await this.markdown.textMarkdownToHTML(videoChannel.ownerAccount.description)
|
||||||
|
|
||||||
|
// After the markdown renderer to avoid layout changes
|
||||||
this.videoChannel = videoChannel
|
this.videoChannel = videoChannel
|
||||||
|
|
||||||
if (this.authService.isLoggedIn()) {
|
this.loadChannelVideosCount()
|
||||||
this.authService.userInformationLoaded
|
|
||||||
.subscribe(() => {
|
|
||||||
const channelUserId = this.videoChannel.ownerAccount.userId
|
|
||||||
this.isChannelManageable = channelUserId && channelUserId === this.authService.getUser().id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.hotkeys = [
|
this.hotkeys = [
|
||||||
|
@ -67,8 +72,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.links = [
|
this.links = [
|
||||||
{ label: $localize`VIDEOS`, routerLink: 'videos' },
|
{ label: $localize`VIDEOS`, routerLink: 'videos' },
|
||||||
{ label: $localize`VIDEO PLAYLISTS`, routerLink: 'video-playlists' },
|
{ label: $localize`VIDEO PLAYLISTS`, routerLink: 'video-playlists' }
|
||||||
{ label: $localize`ABOUT`, routerLink: 'about' }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +83,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
if (this.isUserLoggedIn()) this.hotkeysService.remove(this.hotkeys)
|
if (this.isUserLoggedIn()) this.hotkeysService.remove(this.hotkeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
get isInSmallView () {
|
isInSmallView () {
|
||||||
return this.screenService.isInSmallView()
|
return this.screenService.isInSmallView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,12 +91,24 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
return this.authService.isLoggedIn()
|
return this.authService.isLoggedIn()
|
||||||
}
|
}
|
||||||
|
|
||||||
get isManageable () {
|
isManageable () {
|
||||||
if (!this.isUserLoggedIn()) return false
|
if (!this.isUserLoggedIn()) return false
|
||||||
|
|
||||||
return this.videoChannel.ownerAccount.userId === this.authService.getUser().id
|
return this.videoChannel.ownerAccount.userId === this.authService.getUser().id
|
||||||
}
|
}
|
||||||
|
|
||||||
activateCopiedMessage () {
|
activateCopiedMessage () {
|
||||||
this.notifier.success($localize`Username copied`)
|
this.notifier.success($localize`Username copied`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadChannelVideosCount () {
|
||||||
|
this.videoService.getVideoChannelVideos({
|
||||||
|
videoChannel: this.videoChannel,
|
||||||
|
videoPagination: {
|
||||||
|
currentPage: 1,
|
||||||
|
itemsPerPage: 0
|
||||||
|
},
|
||||||
|
sort: '-publishedAt'
|
||||||
|
}).subscribe(res => this.channelVideosCount = res.total)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { SharedMainModule } from '@app/shared/shared-main'
|
||||||
import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription'
|
import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription'
|
||||||
import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
|
import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
|
||||||
import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist'
|
import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist'
|
||||||
import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component'
|
|
||||||
import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component'
|
import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component'
|
||||||
import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
|
import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
|
||||||
import { VideoChannelsRoutingModule } from './video-channels-routing.module'
|
import { VideoChannelsRoutingModule } from './video-channels-routing.module'
|
||||||
|
@ -26,7 +25,6 @@ import { VideoChannelsComponent } from './video-channels.component'
|
||||||
declarations: [
|
declarations: [
|
||||||
VideoChannelsComponent,
|
VideoChannelsComponent,
|
||||||
VideoChannelVideosComponent,
|
VideoChannelVideosComponent,
|
||||||
VideoChannelAboutComponent,
|
|
||||||
VideoChannelPlaylistsComponent
|
VideoChannelPlaylistsComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ body {
|
||||||
|
|
||||||
--menuBackgroundColor: #{$menu-background};
|
--menuBackgroundColor: #{$menu-background};
|
||||||
--menuForegroundColor: #{$menu-color};
|
--menuForegroundColor: #{$menu-color};
|
||||||
|
|
||||||
--submenuColor: #{$sub-menu-color};
|
--submenuColor: #{$sub-menu-color};
|
||||||
|
--channelBackgroundColor: #{$channel-background-color};
|
||||||
|
|
||||||
--inputForegroundColor: #{$input-foreground-color};
|
--inputForegroundColor: #{$input-foreground-color};
|
||||||
--inputBackgroundColor: #{$input-background-color};
|
--inputBackgroundColor: #{$input-background-color};
|
||||||
|
@ -277,11 +279,6 @@ my-input-toggle-hidden ::ng-deep input {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: scale(1) rotate(0deg);}
|
|
||||||
to { transform: scale(1) rotate(360deg);}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In tables, don't have a hover different background
|
// In tables, don't have a hover different background
|
||||||
table {
|
table {
|
||||||
.action-button-edit, .action-button-delete {
|
.action-button-edit, .action-button-delete {
|
||||||
|
@ -468,3 +465,21 @@ ngx-loading-bar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
|
||||||
|
.peertube-button {
|
||||||
|
@include peertube-button;
|
||||||
|
}
|
||||||
|
|
||||||
|
.peertube-button-link {
|
||||||
|
@include peertube-button-link;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-button {
|
||||||
|
@include orange-button;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-button-inverted {
|
||||||
|
@include orange-button-inverted;
|
||||||
|
}
|
||||||
|
|
|
@ -31,9 +31,19 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin prefix($property, $parameters...) {
|
@mixin fade-text ($fade-after, $background-color) {
|
||||||
@each $prefix in -webkit-, -moz-, -ms-, -o-, "" {
|
position: relative;
|
||||||
#{$prefix}#{$property}: $parameters;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background: linear-gradient(transparent $fade-after, $background-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +148,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin orange-button-inverted {
|
||||||
|
@include button-focus(pvar(--mainColorLightest));
|
||||||
|
|
||||||
|
border: 2px solid pvar(--mainColor);
|
||||||
|
font-weight: $font-regular;
|
||||||
|
|
||||||
|
&, &:active, &:focus {
|
||||||
|
color: pvar(--mainColor);
|
||||||
|
background-color: pvar(--mainBackgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: pvar(--mainColor);
|
||||||
|
background-color: pvar(--mainColorLightest);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled], &.disabled {
|
||||||
|
cursor: default;
|
||||||
|
color: pvar(--mainColor);
|
||||||
|
background-color: #C6C6C6;
|
||||||
|
}
|
||||||
|
|
||||||
|
my-global-icon {
|
||||||
|
@include apply-svg-color(pvar(--mainColor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@mixin tertiary-button {
|
@mixin tertiary-button {
|
||||||
@include button-focus($grey-button-outline-color);
|
@include button-focus($grey-button-outline-color);
|
||||||
|
|
||||||
|
@ -509,6 +546,13 @@
|
||||||
min-height: $size;
|
min-height: $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin channel-avatar ($size) {
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
min-width: $size;
|
||||||
|
min-height: $size;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin chevron ($size, $border-width) {
|
@mixin chevron ($size, $border-width) {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: $border-width $border-width 0 0;
|
border-width: $border-width $border-width 0 0;
|
||||||
|
|
|
@ -16,9 +16,10 @@ $grey-foreground-hover-color: #303030;
|
||||||
$grey-button-outline-color: scale-color($grey-foreground-color, $alpha: -95%);
|
$grey-button-outline-color: scale-color($grey-foreground-color, $alpha: -95%);
|
||||||
|
|
||||||
$main-color: hsl(24, 90%, 50%);
|
$main-color: hsl(24, 90%, 50%);
|
||||||
$main-hover-color: lighten($main-color, 5%);
|
|
||||||
$main-color-lighter: lighten($main-color, 10%);
|
$main-color-lighter: lighten($main-color, 10%);
|
||||||
$main-color-lightest: lighten($main-color, 40%);
|
$main-color-lightest: lighten($main-color, 40%);
|
||||||
|
$main-hover-color: lighten($main-color, 5%);
|
||||||
|
|
||||||
$secondary-color: hsl(187, 77%, 34%);
|
$secondary-color: hsl(187, 77%, 34%);
|
||||||
|
|
||||||
$support-button: inherit;
|
$support-button: inherit;
|
||||||
|
@ -50,6 +51,8 @@ $menu-lateral-padding: 26px;
|
||||||
$sub-menu-color: #F7F7F7;
|
$sub-menu-color: #F7F7F7;
|
||||||
$sub-menu-height: 81px;
|
$sub-menu-height: 81px;
|
||||||
|
|
||||||
|
$channel-background-color: #f6ede8;
|
||||||
|
|
||||||
$footer-height: 30px;
|
$footer-height: 30px;
|
||||||
$footer-margin: 30px;
|
$footer-margin: 30px;
|
||||||
|
|
||||||
|
@ -98,7 +101,9 @@ $variables: (
|
||||||
|
|
||||||
--menuBackgroundColor: var(--menuBackgroundColor),
|
--menuBackgroundColor: var(--menuBackgroundColor),
|
||||||
--menuForegroundColor: var(--menuForegroundColor),
|
--menuForegroundColor: var(--menuForegroundColor),
|
||||||
|
|
||||||
--submenuColor: var(--submenuColor),
|
--submenuColor: var(--submenuColor),
|
||||||
|
--channelBackgroundColor: var(--channelBackgroundColor),
|
||||||
|
|
||||||
--inputForegroundColor: var(--inputForegroundColor),
|
--inputForegroundColor: var(--inputForegroundColor),
|
||||||
--inputBackgroundColor: var(--inputBackgroundColor),
|
--inputBackgroundColor: var(--inputBackgroundColor),
|
||||||
|
|
Loading…
Reference in New Issue