Add avatar in comments
This commit is contained in:
parent
265ba139eb
commit
cf117aaafc
|
@ -1,6 +1,8 @@
|
||||||
<menu>
|
<menu>
|
||||||
<div *ngIf="isLoggedIn" class="logged-in-block">
|
<div *ngIf="isLoggedIn" class="logged-in-block">
|
||||||
|
<a routerLink="/account/settings">
|
||||||
<img [src]="getUserAvatarUrl()" alt="Avatar" />
|
<img [src]="getUserAvatarUrl()" alt="Avatar" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="logged-in-info">
|
<div class="logged-in-info">
|
||||||
<a routerLink="/account/settings" class="logged-in-username">{{ user.username }}</a>
|
<a routerLink="/account/settings" class="logged-in-username">{{ user.username }}</a>
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
|
<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
|
||||||
|
<div class="avatar-and-textarea">
|
||||||
|
<img [src]="getUserAvatarUrl()" alt="Avatar" />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" #textarea>
|
<textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" #textarea>
|
||||||
</textarea>
|
</textarea>
|
||||||
|
@ -6,6 +9,7 @@
|
||||||
{{ formErrors.text }}
|
{{ formErrors.text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="submit-comment">
|
<div class="submit-comment">
|
||||||
<button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid }">
|
<button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid }">
|
||||||
|
|
|
@ -1,12 +1,25 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.form-group {
|
.avatar-and-textarea {
|
||||||
|
display: flex;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
img {
|
||||||
@include peertube-textarea(100%, 150px);
|
@include avatar(36px);
|
||||||
|
|
||||||
|
vertical-align: top;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
@include peertube-textarea(100%, 60px);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-comment {
|
.submit-comment {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Observable } from 'rxjs/Observable'
|
||||||
import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model'
|
import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model'
|
||||||
import { FormReactive } from '../../../shared'
|
import { FormReactive } from '../../../shared'
|
||||||
import { VIDEO_COMMENT_TEXT } from '../../../shared/forms/form-validators/video-comment'
|
import { VIDEO_COMMENT_TEXT } from '../../../shared/forms/form-validators/video-comment'
|
||||||
|
import { User } from '../../../shared/users'
|
||||||
import { Video } from '../../../shared/video/video.model'
|
import { Video } from '../../../shared/video/video.model'
|
||||||
import { VideoComment } from './video-comment.model'
|
import { VideoComment } from './video-comment.model'
|
||||||
import { VideoCommentService } from './video-comment.service'
|
import { VideoCommentService } from './video-comment.service'
|
||||||
|
@ -15,6 +16,7 @@ import { VideoCommentService } from './video-comment.service'
|
||||||
styleUrls: ['./video-comment-add.component.scss']
|
styleUrls: ['./video-comment-add.component.scss']
|
||||||
})
|
})
|
||||||
export class VideoCommentAddComponent extends FormReactive implements OnInit {
|
export class VideoCommentAddComponent extends FormReactive implements OnInit {
|
||||||
|
@Input() user: User
|
||||||
@Input() video: Video
|
@Input() video: Video
|
||||||
@Input() parentComment: VideoComment
|
@Input() parentComment: VideoComment
|
||||||
@Input() focusOnInit = false
|
@Input() focusOnInit = false
|
||||||
|
@ -79,6 +81,10 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
|
||||||
return this.form.value['text']
|
return this.form.value['text']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUserAvatarUrl () {
|
||||||
|
return this.user.getAvatarUrl()
|
||||||
|
}
|
||||||
|
|
||||||
private addCommentReply (commentCreate: VideoCommentCreate) {
|
private addCommentReply (commentCreate: VideoCommentCreate) {
|
||||||
return this.videoCommentService
|
return this.videoCommentService
|
||||||
.addCommentReply(this.video.id, this.parentComment.id, commentCreate)
|
.addCommentReply(this.video.id, this.parentComment.id, commentCreate)
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
<div class="comment">
|
<div class="root-comment">
|
||||||
|
<img [src]="getAvatarUrl(comment.account)" alt="Avatar" />
|
||||||
|
|
||||||
|
<div class="comment">
|
||||||
<div class="comment-account-date">
|
<div class="comment-account-date">
|
||||||
<div class="comment-account">{{ comment.by }}</div>
|
<div class="comment-account">{{ comment.by }}</div>
|
||||||
<div class="comment-date">{{ comment.createdAt | myFromNow }}</div>
|
<div class="comment-date">{{ comment.createdAt | myFromNow }}</div>
|
||||||
|
@ -10,7 +13,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<my-video-comment-add
|
<my-video-comment-add
|
||||||
*ngIf="isUserLoggedIn() && inReplyToCommentId === comment.id" [video]="video" [parentComment]="comment" [focusOnInit]="true"
|
*ngIf="isUserLoggedIn() && inReplyToCommentId === comment.id"
|
||||||
|
[user]="user"
|
||||||
|
[video]="video"
|
||||||
|
[parentComment]="comment"
|
||||||
|
[focusOnInit]="true"
|
||||||
(commentCreated)="onCommentReplyCreated($event)"
|
(commentCreated)="onCommentReplyCreated($event)"
|
||||||
></my-video-comment-add>
|
></my-video-comment-add>
|
||||||
|
|
||||||
|
@ -26,4 +33,5 @@
|
||||||
></my-video-comment>
|
></my-video-comment>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.comment {
|
.root-comment {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
margin-top: 30px;
|
display: flex;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include avatar(36px);
|
||||||
|
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
.comment-account-date {
|
.comment-account-date {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -27,12 +37,5 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.children {
|
|
||||||
margin-left: 20px;
|
|
||||||
|
|
||||||
.comment {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||||
|
import { Account as AccountInterface } from '../../../../../../shared/models/actors'
|
||||||
import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
|
import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
|
||||||
import { AuthService } from '../../../core/auth'
|
import { AuthService } from '../../../core/auth'
|
||||||
|
import { Account } from '../../../shared/account/account.model'
|
||||||
import { Video } from '../../../shared/video/video.model'
|
import { Video } from '../../../shared/video/video.model'
|
||||||
import { VideoComment } from './video-comment.model'
|
import { VideoComment } from './video-comment.model'
|
||||||
|
|
||||||
|
@ -18,7 +20,10 @@ export class VideoCommentComponent {
|
||||||
@Output() wantedToReply = new EventEmitter<VideoComment>()
|
@Output() wantedToReply = new EventEmitter<VideoComment>()
|
||||||
@Output() resetReply = new EventEmitter()
|
@Output() resetReply = new EventEmitter()
|
||||||
|
|
||||||
constructor (private authService: AuthService) {
|
constructor (private authService: AuthService) {}
|
||||||
|
|
||||||
|
get user () {
|
||||||
|
return this.authService.getUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
onCommentReplyCreated (createdComment: VideoComment) {
|
onCommentReplyCreated (createdComment: VideoComment) {
|
||||||
|
@ -52,4 +57,8 @@ export class VideoCommentComponent {
|
||||||
onResetReply () {
|
onResetReply () {
|
||||||
this.resetReply.emit()
|
this.resetReply.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAvatarUrl (account: AccountInterface) {
|
||||||
|
return Account.GET_ACCOUNT_AVATAR_URL(account)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Account } from '../../../../../../shared/models/actors'
|
||||||
import { VideoComment as VideoCommentServerModel } from '../../../../../../shared/models/videos/video-comment.model'
|
import { VideoComment as VideoCommentServerModel } from '../../../../../../shared/models/videos/video-comment.model'
|
||||||
|
|
||||||
export class VideoComment implements VideoCommentServerModel {
|
export class VideoComment implements VideoCommentServerModel {
|
||||||
|
@ -9,10 +10,7 @@ export class VideoComment implements VideoCommentServerModel {
|
||||||
videoId: number
|
videoId: number
|
||||||
createdAt: Date | string
|
createdAt: Date | string
|
||||||
updatedAt: Date | string
|
updatedAt: Date | string
|
||||||
account: {
|
account: Account
|
||||||
name: string
|
|
||||||
host: string
|
|
||||||
}
|
|
||||||
totalReplies: number
|
totalReplies: number
|
||||||
|
|
||||||
by: string
|
by: string
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<my-video-comment-add
|
<my-video-comment-add
|
||||||
*ngIf="isUserLoggedIn()"
|
*ngIf="isUserLoggedIn()"
|
||||||
[video]="video"
|
[video]="video"
|
||||||
|
[user]="user"
|
||||||
(commentCreated)="onCommentThreadCreated($event)"
|
(commentCreated)="onCommentThreadCreated($event)"
|
||||||
></my-video-comment-add>
|
></my-video-comment-add>
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
margin-left: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyphicon, .comment-thread-loading {
|
.glyphicon, .comment-thread-loading {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { ComponentPagination } from '../../../shared/rest/component-pagination.m
|
||||||
import { User } from '../../../shared/users'
|
import { User } from '../../../shared/users'
|
||||||
import { SortField } from '../../../shared/video/sort-field.type'
|
import { SortField } from '../../../shared/video/sort-field.type'
|
||||||
import { VideoDetails } from '../../../shared/video/video-details.model'
|
import { VideoDetails } from '../../../shared/video/video-details.model'
|
||||||
import { Video } from '../../../shared/video/video.model'
|
|
||||||
import { VideoComment } from './video-comment.model'
|
import { VideoComment } from './video-comment.model'
|
||||||
import { VideoCommentService } from './video-comment.service'
|
import { VideoCommentService } from './video-comment.service'
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,15 @@
|
||||||
<div class="video-info-actions">
|
<div class="video-info-actions">
|
||||||
<div
|
<div
|
||||||
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
|
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
|
||||||
class="action-button action-button-like">
|
class="action-button action-button-like"
|
||||||
|
>
|
||||||
<span class="icon icon-like" title="Like this video" ></span>
|
<span class="icon icon-like" title="Like this video" ></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
|
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
|
||||||
class="action-button action-button-dislike">
|
class="action-button action-button-dislike"
|
||||||
|
>
|
||||||
<span class="icon icon-dislike" title="Dislike this video"></span>
|
<span class="icon icon-dislike" title="Dislike this video"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -176,9 +176,9 @@
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 16px;
|
@include avatar(18px);
|
||||||
height: 16px;
|
|
||||||
margin-left: 3px;
|
margin-left: 7px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../../initializers'
|
import { CONSTRAINTS_FIELDS } from '../../../initializers'
|
||||||
import { isAccountNameValid } from '../accounts'
|
|
||||||
import { exists } from '../misc'
|
import { exists } from '../misc'
|
||||||
import { isVideoChannelNameValid } from '../video-channels'
|
|
||||||
import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
|
import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
|
||||||
|
|
||||||
function isActorEndpointsObjectValid (endpointObject: any) {
|
function isActorEndpointsObjectValid (endpointObject: any) {
|
||||||
|
@ -32,10 +30,6 @@ function isActorPreferredUsernameValid (preferredUsername: string) {
|
||||||
return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
|
return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isActorNameValid (name: string) {
|
|
||||||
return isAccountNameValid(name) || isVideoChannelNameValid(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isActorPrivateKeyValid (privateKey: string) {
|
function isActorPrivateKeyValid (privateKey: string) {
|
||||||
return exists(privateKey) &&
|
return exists(privateKey) &&
|
||||||
typeof privateKey === 'string' &&
|
typeof privateKey === 'string' &&
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { isActivityPubUrlValid } from '../../helpers/custom-validators/activityp
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||||
import { AccountModel } from '../account/account'
|
import { AccountModel } from '../account/account'
|
||||||
import { ActorModel } from '../activitypub/actor'
|
import { ActorModel } from '../activitypub/actor'
|
||||||
|
import { AvatarModel } from '../avatar/avatar'
|
||||||
import { ServerModel } from '../server/server'
|
import { ServerModel } from '../server/server'
|
||||||
import { getSort, throwIfNotValid } from '../utils'
|
import { getSort, throwIfNotValid } from '../utils'
|
||||||
import { VideoModel } from './video'
|
import { VideoModel } from './video'
|
||||||
|
@ -46,6 +47,10 @@ enum ScopeNames {
|
||||||
{
|
{
|
||||||
model: () => ServerModel,
|
model: () => ServerModel,
|
||||||
required: false
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: () => AvatarModel,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -243,10 +248,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt,
|
updatedAt: this.updatedAt,
|
||||||
totalReplies: this.get('totalReplies') || 0,
|
totalReplies: this.get('totalReplies') || 0,
|
||||||
account: {
|
account: this.Account.toFormattedJSON()
|
||||||
name: this.Account.name,
|
|
||||||
host: this.Account.Actor.getHost()
|
|
||||||
}
|
|
||||||
} as VideoComment
|
} as VideoComment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
|
import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
|
||||||
import { dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../utils/index'
|
import { testVideoImage } from '../../utils'
|
||||||
|
import {
|
||||||
|
dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, updateMyAvatar,
|
||||||
|
uploadVideo
|
||||||
|
} from '../../utils/index'
|
||||||
import {
|
import {
|
||||||
addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads,
|
addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads,
|
||||||
getVideoThreadComments
|
getVideoThreadComments
|
||||||
|
@ -29,6 +33,12 @@ describe('Test video comments', function () {
|
||||||
const res = await uploadVideo(server.url, server.accessToken, {})
|
const res = await uploadVideo(server.url, server.accessToken, {})
|
||||||
videoUUID = res.body.video.uuid
|
videoUUID = res.body.video.uuid
|
||||||
videoId = res.body.video.id
|
videoId = res.body.video.id
|
||||||
|
|
||||||
|
await updateMyAvatar({
|
||||||
|
url: server.url,
|
||||||
|
accessToken: server.accessToken,
|
||||||
|
fixture: 'avatar.png'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not have threads on this video', async function () {
|
it('Should not have threads on this video', async function () {
|
||||||
|
@ -70,6 +80,10 @@ describe('Test video comments', function () {
|
||||||
expect(comment.id).to.equal(comment.threadId)
|
expect(comment.id).to.equal(comment.threadId)
|
||||||
expect(comment.account.name).to.equal('root')
|
expect(comment.account.name).to.equal('root')
|
||||||
expect(comment.account.host).to.equal('localhost:9001')
|
expect(comment.account.host).to.equal('localhost:9001')
|
||||||
|
|
||||||
|
const test = await testVideoImage(server.url, 'avatar-resized', comment.account.avatar.path, '.png')
|
||||||
|
expect(test).to.equal(true)
|
||||||
|
|
||||||
expect(comment.totalReplies).to.equal(0)
|
expect(comment.totalReplies).to.equal(0)
|
||||||
expect(dateIsValid(comment.createdAt as string)).to.be.true
|
expect(dateIsValid(comment.createdAt as string)).to.be.true
|
||||||
expect(dateIsValid(comment.updatedAt as string)).to.be.true
|
expect(dateIsValid(comment.updatedAt as string)).to.be.true
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Account } from '../actors'
|
||||||
|
|
||||||
export interface VideoComment {
|
export interface VideoComment {
|
||||||
id: number
|
id: number
|
||||||
url: string
|
url: string
|
||||||
|
@ -8,10 +10,7 @@ export interface VideoComment {
|
||||||
createdAt: Date | string
|
createdAt: Date | string
|
||||||
updatedAt: Date | string
|
updatedAt: Date | string
|
||||||
totalReplies: number
|
totalReplies: number
|
||||||
account: {
|
account: Account
|
||||||
name: string
|
|
||||||
host: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoCommentThreadTree {
|
export interface VideoCommentThreadTree {
|
||||||
|
|
Loading…
Reference in New Issue