import { MetaGuard } from '@ngx-meta/core'
import { AccountComponent } from './account.component'
import { AccountVideosComponent } from './account-videos/account-videos.component'
-import { AccountAboutComponent } from '@app/+account/account-about/account-about.component'
+import { AccountAboutComponent } from './account-about/account-about.component'
+import { AccountVideoChannelsComponent } from './account-video-channels/account-video-channels.component'
const accountRoutes: Routes = [
{
}
}
},
+ {
+ path: 'video-channels',
+ component: AccountVideoChannelsComponent,
+ data: {
+ meta: {
+ title: 'Account video channels'
+ }
+ }
+ },
{
path: 'about',
component: AccountAboutComponent,
--- /dev/null
+<div *ngIf="account" class="row">
+ <a
+ *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.uuid ]"
+ class="video-channel" title="See this video channel"
+ >
+ <img [src]="videoChannel.avatarUrl" alt="Avatar" />
+
+ <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
+ <div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
+ </a>
+</div>
\ No newline at end of file
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+.row {
+ text-align: center;
+}
+
+a.video-channel {
+ @include disable-default-a-behaviour;
+
+ display: inline-block;
+ text-align: center;
+ color: #000;
+ margin: 10px 30px;
+
+ img {
+ @include avatar(80px);
+
+ margin-bottom: 10px;
+ }
+
+ .video-channel-display-name {
+ font-size: 20px;
+ font-weight: $font-bold;
+ }
+
+ .video-channel-followers {
+ font-size: 15px;
+ }
+}
\ No newline at end of file
--- /dev/null
+import { Component, OnInit } from '@angular/core'
+import { ActivatedRoute } from '@angular/router'
+import 'rxjs/add/observable/from'
+import 'rxjs/add/operator/concatAll'
+import { Account } from '@app/shared/account/account.model'
+import { AccountService } from '@app/shared/account/account.service'
+import { VideoChannel } from '../../../../../shared/models/videos'
+import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
+
+@Component({
+ selector: 'my-account-video-channels',
+ templateUrl: './account-video-channels.component.html',
+ styleUrls: [ './account-video-channels.component.scss' ]
+})
+export class AccountVideoChannelsComponent implements OnInit {
+ account: Account
+ videoChannels: VideoChannel[] = []
+
+ constructor (
+ protected route: ActivatedRoute,
+ private accountService: AccountService,
+ private videoChannelService: VideoChannelService
+ ) { }
+
+ ngOnInit () {
+ // Parent get the account for us
+ this.accountService.accountLoaded
+ .do(account => this.account = account)
+ .flatMap(account => this.videoChannelService.getVideoChannels(account.id))
+ .map(res => res.data)
+ .subscribe(videoChannels => this.videoChannels = videoChannels)
+ }
+}
<div class="sub-menu">
<div class="account">
- <img [src]="getAvatarUrl()" alt="Avatar" />
+ <img [src]="account.avatarUrl" alt="Avatar" />
<div class="account-info">
<div class="account-display-name">{{ account.displayName }}</div>
<div class="links">
<a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
+ <a routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a>
+
<a routerLink="about" routerLinkActive="active" class="title-page">About</a>
</div>
</div>
this.accountService.getAccount(accountId)
.subscribe(account => this.account = account)
}
-
- getAvatarUrl () {
- return Account.GET_ACCOUNT_AVATAR_URL(this.account)
- }
}
import { AccountComponent } from './account.component'
import { AccountVideosComponent } from './account-videos/account-videos.component'
import { AccountAboutComponent } from './account-about/account-about.component'
+import { AccountVideoChannelsComponent } from './account-video-channels/account-video-channels.component'
@NgModule({
imports: [
declarations: [
AccountComponent,
AccountVideosComponent,
+ AccountVideoChannelsComponent,
AccountAboutComponent
],
<menu>
<div *ngIf="isLoggedIn" class="logged-in-block">
<a routerLink="/account/settings">
- <img [src]="getUserAvatarUrl()" alt="Avatar" />
+ <img [src]="user.accountAvatarUrl" alt="Avatar" />
</a>
<div class="logged-in-info">
)
}
- getUserAvatarUrl () {
- return this.user.getAvatarUrl()
- }
-
isRegistrationAllowed () {
return this.serverService.getConfig().signup.allowed
}
<div class="user">
- <img [src]="getAvatarUrl()" alt="Avatar" />
+ <img [src]="user.accountAvatarUrl" alt="Avatar" />
<div class="user-info">
<div class="user-info-username">{{ user.username }}</div>
.subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed)
}
- getAvatarUrl () {
- return this.user.getAvatarUrl()
- }
-
changeAvatar () {
const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ]
import { Account as ServerAccount } from '../../../../../shared/models/actors/account.model'
-import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
-import { getAbsoluteAPIUrl } from '../misc/utils'
+import { Actor } from '../actor/actor.model'
-export class Account implements ServerAccount {
- id: number
- uuid: string
- url: string
- name: string
+export class Account extends Actor implements ServerAccount {
displayName: string
description: string
- host: string
- followingCount: number
- followersCount: number
- createdAt: Date
- updatedAt: Date
- avatar: Avatar
-
- static GET_ACCOUNT_AVATAR_URL (account: Account) {
- const absoluteAPIUrl = getAbsoluteAPIUrl()
-
- if (account && account.avatar) return absoluteAPIUrl + account.avatar.path
-
- return window.location.origin + '/client/assets/images/default-avatar.png'
- }
-
- static CREATE_BY_STRING (accountName: string, host: string) {
- const absoluteAPIUrl = getAbsoluteAPIUrl()
- const thisHost = new URL(absoluteAPIUrl).host
-
- if (host.trim() === thisHost) return accountName
-
- return accountName + '@' + host
- }
constructor (hash: ServerAccount) {
- this.id = hash.id
- this.uuid = hash.uuid
- this.url = hash.url
- this.name = hash.name
+ super(hash)
+
this.displayName = hash.displayName
this.description = hash.description
- this.host = hash.host
- this.followingCount = hash.followingCount
- this.followersCount = hash.followersCount
- this.createdAt = new Date(hash.createdAt.toString())
- this.updatedAt = new Date(hash.updatedAt.toString())
- this.avatar = hash.avatar
}
}
--- /dev/null
+import { Actor as ActorServer } from '../../../../../shared/models/actors/actor.model'
+import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
+import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
+
+export abstract class Actor implements ActorServer {
+ id: number
+ uuid: string
+ url: string
+ name: string
+ host: string
+ followingCount: number
+ followersCount: number
+ createdAt: Date
+ updatedAt: Date
+ avatar: Avatar
+
+ avatarUrl: string
+
+ static GET_ACTOR_AVATAR_URL (actor: { avatar: Avatar }) {
+ const absoluteAPIUrl = getAbsoluteAPIUrl()
+
+ if (actor && actor.avatar) return absoluteAPIUrl + actor.avatar.path
+
+ return window.location.origin + '/client/assets/images/default-avatar.png'
+ }
+
+ static CREATE_BY_STRING (accountName: string, host: string) {
+ const absoluteAPIUrl = getAbsoluteAPIUrl()
+ const thisHost = new URL(absoluteAPIUrl).host
+
+ if (host.trim() === thisHost) return accountName
+
+ return accountName + '@' + host
+ }
+
+ protected constructor (hash: ActorServer) {
+ this.id = hash.id
+ this.uuid = hash.uuid
+ this.url = hash.url
+ this.name = hash.name
+ this.host = hash.host
+ this.followingCount = hash.followingCount
+ this.followersCount = hash.followersCount
+ this.createdAt = new Date(hash.createdAt.toString())
+ this.updatedAt = new Date(hash.updatedAt.toString())
+ this.avatar = hash.avatar
+
+ this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this)
+ }
+}
import { VideoThumbnailComponent } from './video/video-thumbnail.component'
import { VideoService } from './video/video.service'
import { AccountService } from '@app/shared/account/account.service'
+import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
@NgModule({
imports: [
UserService,
VideoService,
AccountService,
- MarkdownService
+ MarkdownService,
+ VideoChannelService
]
})
export class SharedModule { }
-import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
-import { Account } from '../account/account.model'
+import { Account, hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type'
+import { Actor } from '@app/shared/actor/actor.model'
export type UserConstructorHash = {
id: number,
account: Account
videoChannels: VideoChannel[]
createdAt: Date
+ accountAvatarUrl: string
constructor (hash: UserConstructorHash) {
this.id = hash.id
if (hash.createdAt !== undefined) {
this.createdAt = hash.createdAt
}
+
+ this.updateComputedAttributes()
}
hasRight (right: UserRight) {
return hasUserRight(this.role, right)
}
- getAvatarUrl () {
- return Account.GET_ACCOUNT_AVATAR_URL(this.account)
- }
-
patch (obj: UserServerModel) {
for (const key of Object.keys(obj)) {
this[key] = obj[key]
}
+
+ this.updateComputedAttributes()
+ }
+
+ private updateComputedAttributes () {
+ this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account)
}
}
--- /dev/null
+import { VideoChannel as ServerVideoChannel } from '../../../../../shared/models/videos/video-channel.model'
+import { Actor } from '../actor/actor.model'
+
+export class VideoChannel extends Actor implements ServerVideoChannel {
+ displayName: string
+ description: string
+ support: string
+ isLocal: boolean
+ ownerAccount?: {
+ id: number
+ uuid: string
+ }
+
+ constructor (hash: ServerVideoChannel) {
+ super(hash)
+
+ this.displayName = hash.displayName
+ this.description = hash.description
+ this.support = hash.support
+ this.isLocal = hash.isLocal
+ this.ownerAccount = hash.ownerAccount
+ }
+}
--- /dev/null
+import { Injectable } from '@angular/core'
+import 'rxjs/add/operator/catch'
+import 'rxjs/add/operator/map'
+import { Observable } from 'rxjs/Observable'
+import { RestExtractor } from '../rest/rest-extractor.service'
+import { RestService } from '../rest/rest.service'
+import { HttpClient } from '@angular/common/http'
+import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos'
+import { AccountService } from '../account/account.service'
+import { ResultList } from '../../../../../shared'
+import { VideoChannel } from './video-channel.model'
+
+@Injectable()
+export class VideoChannelService {
+ constructor (
+ private authHttp: HttpClient,
+ private restExtractor: RestExtractor,
+ private restService: RestService
+ ) {}
+
+ getVideoChannels (accountId: number): Observable<ResultList<VideoChannel>> {
+ return this.authHttp.get<ResultList<VideoChannelServer>>(AccountService.BASE_ACCOUNT_URL + accountId + '/video-channels')
+ .map(res => this.extractVideoChannels(res))
+ .catch((res) => this.restExtractor.handleError(res))
+ }
+
+ private extractVideoChannels (result: ResultList<VideoChannelServer>) {
+ const videoChannels: VideoChannel[] = []
+
+ for (const videoChannelJSON of result.data) {
+ videoChannels.push(new VideoChannel(videoChannelJSON))
+ }
+
+ return { data: videoChannels, total: result.total }
+ }
+}
-import { Account } from '@app/shared/account/account.model'
import { User } from '../'
import { Video as VideoServerModel, VideoPrivacy } from '../../../../../shared'
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
import { VideoConstant } from '../../../../../shared/models/videos/video.model'
import { getAbsoluteAPIUrl } from '../misc/utils'
import { ServerConfig } from '../../../../../shared/models'
+import { Actor } from '@app/shared/actor/actor.model'
export class Video implements VideoServerModel {
by: string
+ accountAvatarUrl: string
createdAt: Date
updatedAt: Date
publishedAt: Date
this.nsfw = hash.nsfw
this.account = hash.account
- this.by = Account.CREATE_BY_STRING(hash.account.name, hash.account.host)
+ this.by = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host)
+ this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account)
}
isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
<div class="avatar-and-textarea">
- <img [src]="getUserAvatarUrl()" alt="Avatar" />
+ <img [src]="user.accountAvatarUrl" alt="Avatar" />
<div class="form-group">
<textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
return this.form.value['text']
}
- getUserAvatarUrl () {
- return this.user.getAvatarUrl()
- }
-
private addCommentReply (commentCreate: VideoCommentCreate) {
return this.videoCommentService
.addCommentReply(this.video.id, this.parentComment.id, commentCreate)
<div class="root-comment">
- <img [src]="getAvatarUrl(comment.account)" alt="Avatar" />
+ <img [src]="comment.accountAvatarUrl" alt="Avatar" />
<div class="comment">
<div *ngIf="highlightedComment === true" class="highlighted-comment">Highlighted comment</div>
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
import { LinkifierService } from '@app/videos/+video-watch/comment/linkifier.service'
import * as sanitizeHtml from 'sanitize-html'
-import { Account as AccountInterface } from '../../../../../../shared/models/actors'
import { UserRight } from '../../../../../../shared/models/users'
import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
import { AuthService } from '../../../core/auth'
-import { Account } from '../../../shared/account/account.model'
import { Video } from '../../../shared/video/video.model'
import { VideoComment } from './video-comment.model'
this.resetReply.emit()
}
- getAvatarUrl (account: AccountInterface) {
- return Account.GET_ACCOUNT_AVATAR_URL(account)
- }
-
isRemovableByUser () {
return this.isUserLoggedIn() &&
(
-import { Account } from '@app/shared/account/account.model'
import { Account as AccountInterface } from '../../../../../../shared/models/actors'
import { VideoComment as VideoCommentServerModel } from '../../../../../../shared/models/videos/video-comment.model'
+import { Actor } from '@app/shared/actor/actor.model'
export class VideoComment implements VideoCommentServerModel {
id: number
account: AccountInterface
totalReplies: number
by: string
+ accountAvatarUrl
constructor (hash: VideoCommentServerModel) {
this.id = hash.id
this.account = hash.account
this.totalReplies = hash.totalReplies
- this.by = Account.CREATE_BY_STRING(this.account.name, this.account.host)
+ this.by = Actor.CREATE_BY_STRING(this.account.name, this.account.host)
+ this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account)
}
}
<div class="video-info-by">
<a [routerLink]="[ '/account', video.account.id ]" title="Go the account page">
<span>By {{ video.by }}</span>
- <img [src]="getAvatarPath()" alt="Account avatar" />
+ <img [src]="video.accountAvatarUrl" alt="Account avatar" />
</a>
</div>
</div>
return this.video.isBlackistableBy(this.user)
}
- getAvatarPath () {
- return Account.GET_ACCOUNT_AVATAR_URL(this.video.account)
- }
-
getVideoPoster () {
if (!this.video) return ''