diff options
Diffstat (limited to 'client/src')
17 files changed, 292 insertions, 76 deletions
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html index 8d6f81e1b..c65b5713d 100644 --- a/client/src/app/+video-channels/video-channels.component.html +++ b/client/src/app/+video-channels/video-channels.component.html | |||
@@ -9,7 +9,7 @@ | |||
9 | <div class="actor-display-name">{{ videoChannel.displayName }}</div> | 9 | <div class="actor-display-name">{{ videoChannel.displayName }}</div> |
10 | <div class="actor-name">{{ videoChannel.nameWithHost }}</div> | 10 | <div class="actor-name">{{ videoChannel.nameWithHost }}</div> |
11 | 11 | ||
12 | <my-subscribe-button #subscribeButton *ngIf="isUserLoggedIn()" [videoChannel]="videoChannel"></my-subscribe-button> | 12 | <my-subscribe-button #subscribeButton [videoChannel]="videoChannel"></my-subscribe-button> |
13 | </div> | 13 | </div> |
14 | <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div> | 14 | <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div> |
15 | 15 | ||
diff --git a/client/src/app/search/search.component.html b/client/src/app/search/search.component.html index c4072b291..216905cf0 100644 --- a/client/src/app/search/search.component.html +++ b/client/src/app/search/search.component.html | |||
@@ -41,7 +41,7 @@ | |||
41 | <div i18n class="video-channel-followers">{{ result.followersCount }} subscribers</div> | 41 | <div i18n class="video-channel-followers">{{ result.followersCount }} subscribers</div> |
42 | </div> | 42 | </div> |
43 | 43 | ||
44 | <my-subscribe-button *ngIf="isUserLoggedIn()" [videoChannel]="result"></my-subscribe-button> | 44 | <my-subscribe-button [videoChannel]="result"></my-subscribe-button> |
45 | </div> | 45 | </div> |
46 | 46 | ||
47 | <div *ngIf="isVideo(result)" class="entry video"> | 47 | <div *ngIf="isVideo(result)" class="entry video"> |
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 1e71feb86..076f1d275 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts | |||
@@ -53,7 +53,7 @@ import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.c | |||
53 | import { VideoImportService } from '@app/shared/video-import/video-import.service' | 53 | import { VideoImportService } from '@app/shared/video-import/video-import.service' |
54 | import { ActionDropdownComponent } from '@app/shared/buttons/action-dropdown.component' | 54 | import { ActionDropdownComponent } from '@app/shared/buttons/action-dropdown.component' |
55 | import { NgbDropdownModule, NgbModalModule, NgbPopoverModule, NgbTabsetModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap' | 55 | import { NgbDropdownModule, NgbModalModule, NgbPopoverModule, NgbTabsetModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap' |
56 | import { SubscribeButtonComponent, UserSubscriptionService } from '@app/shared/user-subscription' | 56 | import { SubscribeButtonComponent, RemoteSubscribeComponent, UserSubscriptionService } from '@app/shared/user-subscription' |
57 | import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-features-table.component' | 57 | import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-features-table.component' |
58 | import { OverviewService } from '@app/shared/overview' | 58 | import { OverviewService } from '@app/shared/overview' |
59 | 59 | ||
@@ -93,6 +93,7 @@ import { OverviewService } from '@app/shared/overview' | |||
93 | ReactiveFileComponent, | 93 | ReactiveFileComponent, |
94 | PeertubeCheckboxComponent, | 94 | PeertubeCheckboxComponent, |
95 | SubscribeButtonComponent, | 95 | SubscribeButtonComponent, |
96 | RemoteSubscribeComponent, | ||
96 | InstanceFeaturesTableComponent | 97 | InstanceFeaturesTableComponent |
97 | ], | 98 | ], |
98 | 99 | ||
@@ -127,6 +128,7 @@ import { OverviewService } from '@app/shared/overview' | |||
127 | ReactiveFileComponent, | 128 | ReactiveFileComponent, |
128 | PeertubeCheckboxComponent, | 129 | PeertubeCheckboxComponent, |
129 | SubscribeButtonComponent, | 130 | SubscribeButtonComponent, |
131 | RemoteSubscribeComponent, | ||
130 | InstanceFeaturesTableComponent, | 132 | InstanceFeaturesTableComponent, |
131 | 133 | ||
132 | NumberFormatterPipe, | 134 | NumberFormatterPipe, |
diff --git a/client/src/app/shared/user-subscription/index.ts b/client/src/app/shared/user-subscription/index.ts index faddae66a..e76940f7b 100644 --- a/client/src/app/shared/user-subscription/index.ts +++ b/client/src/app/shared/user-subscription/index.ts | |||
@@ -1,2 +1,3 @@ | |||
1 | export * from './user-subscription.service' | 1 | export * from './user-subscription.service' |
2 | export * from './subscribe-button.component' | 2 | export * from './subscribe-button.component' |
3 | export * from './remote-subscribe.component' | ||
diff --git a/client/src/app/shared/user-subscription/remote-subscribe.component.html b/client/src/app/shared/user-subscription/remote-subscribe.component.html new file mode 100644 index 000000000..3762142f2 --- /dev/null +++ b/client/src/app/shared/user-subscription/remote-subscribe.component.html | |||
@@ -0,0 +1,25 @@ | |||
1 | <form novalidate [formGroup]="form" | ||
2 | (ngSubmit)="formValidated()"> | ||
3 | <div class="form-group"> | ||
4 | <input type="email" | ||
5 | formControlName="text" | ||
6 | class="form-control" | ||
7 | (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" | ||
8 | placeholder="jane_doe@example.com"> | ||
9 | </div> | ||
10 | <button type="submit" | ||
11 | [disabled]="!form.valid" | ||
12 | class="btn btn-sm btn-remote-follow" | ||
13 | i18n> | ||
14 | <span *ngIf="!interact">Remote subscribe</span> | ||
15 | <span *ngIf="interact">Remote interact</span> | ||
16 | </button> | ||
17 | <my-help *ngIf="!interact && showHelp" | ||
18 | helpType="custom" | ||
19 | i18n-customHtml customHtml="You can subscribe to the channel via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type the channel URL in the search box and subscribe there."> | ||
20 | </my-help> | ||
21 | <my-help *ngIf="showHelp && interact" | ||
22 | helpType="custom" | ||
23 | i18n-customHtml customHtml="You can interact with this via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type the current URL in the search box and interact with it there."> | ||
24 | </my-help> | ||
25 | </form> \ No newline at end of file | ||
diff --git a/client/src/app/shared/user-subscription/remote-subscribe.component.scss b/client/src/app/shared/user-subscription/remote-subscribe.component.scss new file mode 100644 index 000000000..e49271079 --- /dev/null +++ b/client/src/app/shared/user-subscription/remote-subscribe.component.scss | |||
@@ -0,0 +1,5 @@ | |||
1 | @import '_mixins'; | ||
2 | |||
3 | .btn-remote-follow { | ||
4 | @include orange-button; | ||
5 | } \ No newline at end of file | ||
diff --git a/client/src/app/shared/user-subscription/remote-subscribe.component.ts b/client/src/app/shared/user-subscription/remote-subscribe.component.ts new file mode 100644 index 000000000..7a81108cd --- /dev/null +++ b/client/src/app/shared/user-subscription/remote-subscribe.component.ts | |||
@@ -0,0 +1,43 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | ||
2 | import { FormReactive } from '@app/shared/forms/form-reactive' | ||
3 | import { | ||
4 | FormValidatorService, | ||
5 | UserValidatorsService | ||
6 | } from '@app/shared/forms/form-validators' | ||
7 | |||
8 | @Component({ | ||
9 | selector: 'my-remote-subscribe', | ||
10 | templateUrl: './remote-subscribe.component.html', | ||
11 | styleUrls: ['./remote-subscribe.component.scss'] | ||
12 | }) | ||
13 | export class RemoteSubscribeComponent extends FormReactive implements OnInit { | ||
14 | @Input() account: string | ||
15 | @Input() interact = false | ||
16 | @Input() showHelp = false | ||
17 | |||
18 | constructor ( | ||
19 | protected formValidatorService: FormValidatorService, | ||
20 | private userValidatorsService: UserValidatorsService | ||
21 | ) { | ||
22 | super() | ||
23 | } | ||
24 | |||
25 | ngOnInit () { | ||
26 | this.buildForm({ | ||
27 | text: this.userValidatorsService.USER_EMAIL | ||
28 | }) | ||
29 | } | ||
30 | |||
31 | onValidKey () { | ||
32 | this.onValueChanged() | ||
33 | if (!this.form.valid) return | ||
34 | |||
35 | this.formValidated() | ||
36 | } | ||
37 | |||
38 | formValidated () { | ||
39 | const address = this.form.value['text'] | ||
40 | const [ , hostname ] = address.split('@') | ||
41 | window.open(`https://${hostname}/authorize_interaction?acct=${this.account}`) | ||
42 | } | ||
43 | } | ||
diff --git a/client/src/app/shared/user-subscription/subscribe-button.component.html b/client/src/app/shared/user-subscription/subscribe-button.component.html index 34c024c17..b62fb7dfa 100644 --- a/client/src/app/shared/user-subscription/subscribe-button.component.html +++ b/client/src/app/shared/user-subscription/subscribe-button.component.html | |||
@@ -1,15 +1,45 @@ | |||
1 | <span i18n *ngIf="subscribed === false" class="subscribe-button" [ngClass]="size" role="button" (click)="subscribe()"> | 1 | <div class="btn-group-subscribe btn-group" |
2 | <span>Subscribe</span> | 2 | [ngClass]="{'subscribe-button': subscribed !== true, 'unsubscribe-button': subscribed === true}"> |
3 | <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count"> | 3 | <button *ngIf="subscribed === false && isUserLoggedIn()" type="button" |
4 | {{ videoChannel.followersCount | myNumberFormatter }} | 4 | class="btn btn-sm" role="button" |
5 | </span> | 5 | (click)="subscribe()" i18n> |
6 | </span> | 6 | <span> |
7 | 7 | Subscribe | |
8 | <span *ngIf="subscribed === true" class="unsubscribe-button" [ngClass]="size" role="button" (click)="unsubscribe()"> | 8 | </span> |
9 | <span class="subscribed" i18n>Subscribed</span> | 9 | <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count"> |
10 | <span class="unsubscribe" i18n>Unsubscribe</span> | 10 | {{ videoChannel.followersCount | myNumberFormatter }} |
11 | 11 | </span> | |
12 | <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count"> | 12 | </button> |
13 | {{ videoChannel.followersCount | myNumberFormatter }} | 13 | <button *ngIf="subscribed === true" type="button" |
14 | </span> | 14 | class="btn btn-sm" role="button" |
15 | </span> | 15 | (click)="unsubscribe()" i18n>Unsubscribe</button> |
16 | |||
17 | <div class="btn-group" ngbDropdown autoClose="outside" | ||
18 | placement="bottom-right" role="group" | ||
19 | aria-label="Multiple ways to subscribe to the current channel"> | ||
20 | <button class="btn btn-sm dropdown-toggle-split" ngbDropdownToggle> | ||
21 | <span *ngIf="!isUserLoggedIn()"> | ||
22 | Subscribe | ||
23 | </span> | ||
24 | <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count"> | ||
25 | {{ videoChannel.followersCount | myNumberFormatter }} | ||
26 | </span> | ||
27 | </button> | ||
28 | <div class="dropdown-menu" ngbDropdownMenu> | ||
29 | |||
30 | <h6 class="dropdown-header" i18n>Using an ActivityPub-compatible account</h6> | ||
31 | <button class="dropdown-item" (click)="subscribe()" | ||
32 | *ngIf="subscribed === false"> | ||
33 | <span *ngIf="!isUserLoggedIn()" i18n>Subscribe with an account on {{ videoChannel.host }}</span> | ||
34 | <span *ngIf="isUserLoggedIn()" i18n>Subscribe with your local account</span> | ||
35 | </button> | ||
36 | <button class="dropdown-item" i18n>Subscribe with a remote account:</button> | ||
37 | <my-remote-subscribe showHelp="true" account="{{ uriAccount }}"></my-remote-subscribe> | ||
38 | <div class="dropdown-divider"></div> | ||
39 | |||
40 | <h6 class="dropdown-header" i18n>Using a syndication feed</h6> | ||
41 | <button (click)="rssOpen()" class="dropdown-item" i18n>Subscribe via RSS</button> | ||
42 | |||
43 | </div> | ||
44 | </div> | ||
45 | </div> \ No newline at end of file | ||
diff --git a/client/src/app/shared/user-subscription/subscribe-button.component.scss b/client/src/app/shared/user-subscription/subscribe-button.component.scss index b78d2f59c..7a8a8ee08 100644 --- a/client/src/app/shared/user-subscription/subscribe-button.component.scss +++ b/client/src/app/shared/user-subscription/subscribe-button.component.scss | |||
@@ -1,51 +1,46 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .subscribe-button { | 4 | .btn-group-subscribe { |
5 | @include peertube-button; | 5 | @include peertube-button; |
6 | @include orange-button; | 6 | @include disable-default-a-behaviour; |
7 | } | 7 | float: right; |
8 | padding: 0; | ||
8 | 9 | ||
9 | .unsubscribe-button { | 10 | &.btn-group > .btn:not(.dropdown-toggle) { |
10 | @include peertube-button; | 11 | padding-right: 5px; |
11 | @include grey-button | 12 | font-size: 15px; |
12 | } | 13 | } |
14 | &.btn-group > .btn-group:not(:first-child) > .btn { | ||
15 | padding-left: 2px; | ||
16 | } | ||
13 | 17 | ||
14 | .subscribe-button, | 18 | &.subscribe-button { |
15 | .unsubscribe-button { | 19 | .btn { |
16 | display: inline-block; | 20 | @include orange-button; |
21 | font-weight: 600; | ||
22 | } | ||
17 | 23 | ||
18 | &.small { | 24 | span.followers-count { |
19 | min-width: 75px; | 25 | padding-left:5px; |
20 | height: 20px; | 26 | } |
21 | line-height: 20px; | ||
22 | font-size: 13px; | ||
23 | } | 27 | } |
24 | 28 | &.unsubscribe-button { | |
25 | &.normal { | 29 | .btn { |
26 | min-width: 120px; | 30 | @include grey-button; |
27 | height: 30px; | 31 | font-weight: 600; |
28 | line-height: 30px; | 32 | } |
29 | font-size: 16px; | ||
30 | } | 33 | } |
31 | } | ||
32 | 34 | ||
33 | .unsubscribe-button { | 35 | .dropdown-header { |
34 | .subscribed { | 36 | padding-left: 1rem; |
35 | display: inline; | ||
36 | } | 37 | } |
37 | 38 | ||
38 | .unsubscribe { | 39 | /deep/ form { |
39 | display: none; | 40 | padding: 0.25rem 1rem; |
40 | } | 41 | } |
41 | 42 | ||
42 | &:hover { | 43 | input { |
43 | .subscribed { | 44 | @include peertube-input-text(100%); |
44 | display: none; | ||
45 | } | ||
46 | |||
47 | .unsubscribe { | ||
48 | display: inline; | ||
49 | } | ||
50 | } | 45 | } |
51 | } \ No newline at end of file | 46 | } |
diff --git a/client/src/app/shared/user-subscription/subscribe-button.component.ts b/client/src/app/shared/user-subscription/subscribe-button.component.ts index b5873a2ee..e3c758942 100644 --- a/client/src/app/shared/user-subscription/subscribe-button.component.ts +++ b/client/src/app/shared/user-subscription/subscribe-button.component.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { Router } from '@angular/router' | ||
3 | import { AuthService } from '@app/core' | ||
2 | import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service' | 4 | import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service' |
3 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | 5 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' |
4 | import { NotificationsService } from 'angular2-notifications' | 6 | import { NotificationsService } from 'angular2-notifications' |
@@ -17,6 +19,8 @@ export class SubscribeButtonComponent implements OnInit { | |||
17 | subscribed: boolean | 19 | subscribed: boolean |
18 | 20 | ||
19 | constructor ( | 21 | constructor ( |
22 | private authService: AuthService, | ||
23 | private router: Router, | ||
20 | private notificationsService: NotificationsService, | 24 | private notificationsService: NotificationsService, |
21 | private userSubscriptionService: UserSubscriptionService, | 25 | private userSubscriptionService: UserSubscriptionService, |
22 | private i18n: I18n | 26 | private i18n: I18n |
@@ -26,16 +30,30 @@ export class SubscribeButtonComponent implements OnInit { | |||
26 | return this.videoChannel.name + '@' + this.videoChannel.host | 30 | return this.videoChannel.name + '@' + this.videoChannel.host |
27 | } | 31 | } |
28 | 32 | ||
33 | get uriAccount () { | ||
34 | return this.videoChannel.ownerAccount.name + '@' + this.videoChannel.host | ||
35 | } | ||
36 | |||
29 | ngOnInit () { | 37 | ngOnInit () { |
30 | this.userSubscriptionService.isSubscriptionExists(this.uri) | 38 | if (this.isUserLoggedIn()) { |
31 | .subscribe( | 39 | this.userSubscriptionService.isSubscriptionExists(this.uri) |
32 | res => this.subscribed = res[this.uri], | 40 | .subscribe( |
41 | res => this.subscribed = res[this.uri], | ||
33 | 42 | ||
34 | err => this.notificationsService.error(this.i18n('Error'), err.message) | 43 | err => this.notificationsService.error(this.i18n('Error'), err.message) |
35 | ) | 44 | ) |
45 | } | ||
36 | } | 46 | } |
37 | 47 | ||
38 | subscribe () { | 48 | subscribe () { |
49 | if (this.isUserLoggedIn()) { | ||
50 | this.localSubscribe() | ||
51 | } else { | ||
52 | this.gotoLogin() | ||
53 | } | ||
54 | } | ||
55 | |||
56 | localSubscribe () { | ||
39 | this.userSubscriptionService.addSubscription(this.uri) | 57 | this.userSubscriptionService.addSubscription(this.uri) |
40 | .subscribe( | 58 | .subscribe( |
41 | () => { | 59 | () => { |
@@ -52,6 +70,12 @@ export class SubscribeButtonComponent implements OnInit { | |||
52 | } | 70 | } |
53 | 71 | ||
54 | unsubscribe () { | 72 | unsubscribe () { |
73 | if (this.isUserLoggedIn()) { | ||
74 | this.localUnsubscribe() | ||
75 | } | ||
76 | } | ||
77 | |||
78 | localUnsubscribe () { | ||
55 | this.userSubscriptionService.deleteSubscription(this.uri) | 79 | this.userSubscriptionService.deleteSubscription(this.uri) |
56 | .subscribe( | 80 | .subscribe( |
57 | () => { | 81 | () => { |
@@ -66,4 +90,16 @@ export class SubscribeButtonComponent implements OnInit { | |||
66 | err => this.notificationsService.error(this.i18n('Error'), err.message) | 90 | err => this.notificationsService.error(this.i18n('Error'), err.message) |
67 | ) | 91 | ) |
68 | } | 92 | } |
93 | |||
94 | isUserLoggedIn () { | ||
95 | return this.authService.isLoggedIn() | ||
96 | } | ||
97 | |||
98 | gotoLogin () { | ||
99 | this.router.navigate([ '/login' ]) | ||
100 | } | ||
101 | |||
102 | rssOpen () { | ||
103 | window.open('') | ||
104 | } | ||
69 | } | 105 | } |
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/videos/+video-watch/comment/video-comment-add.component.html index f65a88d20..9441edfb1 100644 --- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.html +++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.html | |||
@@ -1,9 +1,11 @@ | |||
1 | <form novalidate [formGroup]="form" (ngSubmit)="formValidated()"> | 1 | <form novalidate [formGroup]="form" (ngSubmit)="formValidated()"> |
2 | <div class="avatar-and-textarea"> | 2 | <div class="avatar-and-textarea"> |
3 | <img [src]="user.accountAvatarUrl" alt="Avatar" /> | 3 | <img [src]="getAvatarUrl()" alt="Avatar" /> |
4 | 4 | ||
5 | <div class="form-group"> | 5 | <div class="form-group"> |
6 | <textarea i18n-placeholder placeholder="Add comment..." autosize | 6 | <textarea i18n-placeholder placeholder="Add comment..." autosize |
7 | [readonly]="(user === null) ? true : false" | ||
8 | (click)="openVisitorModal($event)" | ||
7 | formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" | 9 | formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" |
8 | (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea> | 10 | (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea> |
9 | 11 | ||
@@ -20,3 +22,25 @@ | |||
20 | </button> | 22 | </button> |
21 | </div> | 23 | </div> |
22 | </form> | 24 | </form> |
25 | |||
26 | <ng-template #visitorModal let-modal> | ||
27 | <div class="modal-header"> | ||
28 | <h4 class="modal-title" id="modal-basic-title" i18n>You are one step away from commenting</h4> | ||
29 | <button type="button" class="close" aria-label="Close" (click)="hideVisitorModal()"></button> | ||
30 | </div> | ||
31 | <div class="modal-body" i18n> | ||
32 | <span i18n> | ||
33 | If you have an account on this instance, you can login: | ||
34 | </span> | ||
35 | <span class="btn btn-sm mx-3" role="button" (click)="gotoLogin()" i18n>login to comment</span> | ||
36 | <span i18n> | ||
37 | Otherwise you can comment using an account on an ActivityPub-compatible instance: | ||
38 | </span> | ||
39 | <my-remote-subscribe [interact]="true" account="{{ uri }}"></my-remote-subscribe> | ||
40 | </div> | ||
41 | <div class="modal-footer inputs"> | ||
42 | <span i18n class="action-button action-button-cancel" role="button" (click)="hideVisitorModal()"> | ||
43 | Cancel | ||
44 | </span> | ||
45 | </div> | ||
46 | </ng-template> | ||
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.scss b/client/src/app/videos/+video-watch/comment/video-comment-add.component.scss index bb809296a..e998cba5e 100644 --- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.scss +++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.scss | |||
@@ -36,7 +36,7 @@ form { | |||
36 | 36 | ||
37 | button { | 37 | button { |
38 | @include peertube-button; | 38 | @include peertube-button; |
39 | @include orange-button | 39 | @include orange-button; |
40 | } | 40 | } |
41 | } | 41 | } |
42 | 42 | ||
@@ -44,4 +44,16 @@ form { | |||
44 | textarea, .submit-comment button { | 44 | textarea, .submit-comment button { |
45 | font-size: 14px !important; | 45 | font-size: 14px !important; |
46 | } | 46 | } |
47 | } | ||
48 | |||
49 | .modal-body { | ||
50 | .btn { | ||
51 | @include peertube-button; | ||
52 | @include orange-button; | ||
53 | } | ||
54 | |||
55 | span { | ||
56 | float: left; | ||
57 | margin-bottom: 20px; | ||
58 | } | ||
47 | } \ No newline at end of file | 59 | } \ No newline at end of file |
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts index 9998685e8..91af113d2 100644 --- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts +++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Router } from '@angular/router' | ||
2 | import { NotificationsService } from 'angular2-notifications' | 3 | import { NotificationsService } from 'angular2-notifications' |
3 | import { Observable } from 'rxjs' | 4 | import { Observable } from 'rxjs' |
4 | import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model' | 5 | import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model' |
@@ -10,6 +11,8 @@ import { VideoCommentService } from './video-comment.service' | |||
10 | import { I18n } from '@ngx-translate/i18n-polyfill' | 11 | import { I18n } from '@ngx-translate/i18n-polyfill' |
11 | import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' | 12 | import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' |
12 | import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service' | 13 | import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service' |
14 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||
15 | import { AuthService } from '@app/core/auth' | ||
13 | 16 | ||
14 | @Component({ | 17 | @Component({ |
15 | selector: 'my-video-comment-add', | 18 | selector: 'my-video-comment-add', |
@@ -25,15 +28,20 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit { | |||
25 | 28 | ||
26 | @Output() commentCreated = new EventEmitter<VideoCommentCreate>() | 29 | @Output() commentCreated = new EventEmitter<VideoCommentCreate>() |
27 | 30 | ||
31 | @ViewChild('visitorModal') visitorModal: NgbModal | ||
28 | @ViewChild('textarea') private textareaElement: ElementRef | 32 | @ViewChild('textarea') private textareaElement: ElementRef |
29 | 33 | ||
30 | private addingComment = false | 34 | private addingComment = false |
35 | private uri: string | ||
31 | 36 | ||
32 | constructor ( | 37 | constructor ( |
33 | protected formValidatorService: FormValidatorService, | 38 | protected formValidatorService: FormValidatorService, |
34 | private videoCommentValidatorsService: VideoCommentValidatorsService, | 39 | private videoCommentValidatorsService: VideoCommentValidatorsService, |
35 | private notificationsService: NotificationsService, | 40 | private notificationsService: NotificationsService, |
36 | private videoCommentService: VideoCommentService, | 41 | private videoCommentService: VideoCommentService, |
42 | private authService: AuthService, | ||
43 | private modalService: NgbModal, | ||
44 | private router: Router, | ||
37 | private i18n: I18n | 45 | private i18n: I18n |
38 | ) { | 46 | ) { |
39 | super() | 47 | super() |
@@ -44,19 +52,23 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit { | |||
44 | text: this.videoCommentValidatorsService.VIDEO_COMMENT_TEXT | 52 | text: this.videoCommentValidatorsService.VIDEO_COMMENT_TEXT |
45 | }) | 53 | }) |
46 | 54 | ||
47 | if (this.focusOnInit === true) { | 55 | this.uri = this.router.url |
48 | this.textareaElement.nativeElement.focus() | ||
49 | } | ||
50 | 56 | ||
51 | if (this.parentComment) { | 57 | if (this.user) { |
52 | const mentions = this.parentComments | 58 | if (this.focusOnInit === true) { |
53 | .filter(c => c.account.id !== this.user.account.id) // Don't add mention of ourselves | 59 | this.textareaElement.nativeElement.focus() |
54 | .map(c => '@' + c.by) | 60 | } |
61 | |||
62 | if (this.parentComment) { | ||
63 | const mentions = this.parentComments | ||
64 | .filter(c => c.account.id !== this.user.account.id) // Don't add mention of ourselves | ||
65 | .map(c => '@' + c.by) | ||
55 | 66 | ||
56 | const mentionsSet = new Set(mentions) | 67 | const mentionsSet = new Set(mentions) |
57 | const mentionsText = Array.from(mentionsSet).join(' ') + ' ' | 68 | const mentionsText = Array.from(mentionsSet).join(' ') + ' ' |
58 | 69 | ||
59 | this.form.patchValue({ text: mentionsText }) | 70 | this.form.patchValue({ text: mentionsText }) |
71 | } | ||
60 | } | 72 | } |
61 | } | 73 | } |
62 | 74 | ||
@@ -67,6 +79,20 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit { | |||
67 | this.formValidated() | 79 | this.formValidated() |
68 | } | 80 | } |
69 | 81 | ||
82 | openVisitorModal (event) { | ||
83 | if (this.user === null) { // we only open it for visitors | ||
84 | // fixing ng-bootstrap ModalService and the "Expression Changed After It Has Been Checked" Error | ||
85 | event.srcElement.blur() | ||
86 | event.preventDefault() | ||
87 | |||
88 | this.modalService.open(this.visitorModal) | ||
89 | } | ||
90 | } | ||
91 | |||
92 | hideVisitorModal () { | ||
93 | this.modalService.dismissAll() | ||
94 | } | ||
95 | |||
70 | formValidated () { | 96 | formValidated () { |
71 | // If we validate very quickly the comment form, we might comment twice | 97 | // If we validate very quickly the comment form, we might comment twice |
72 | if (this.addingComment) return | 98 | if (this.addingComment) return |
@@ -101,6 +127,17 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit { | |||
101 | return this.form.value['text'] | 127 | return this.form.value['text'] |
102 | } | 128 | } |
103 | 129 | ||
130 | getAvatarUrl () { | ||
131 | if (this.user) return this.user.accountAvatarUrl | ||
132 | return window.location.origin + '/client/assets/images/default-avatar.png' | ||
133 | } | ||
134 | |||
135 | gotoLogin () { | ||
136 | this.hideVisitorModal() | ||
137 | this.authService.redirectUrl = this.router.url | ||
138 | this.router.navigate([ '/login' ]) | ||
139 | } | ||
140 | |||
104 | private addCommentReply (commentCreate: VideoCommentCreate) { | 141 | private addCommentReply (commentCreate: VideoCommentCreate) { |
105 | return this.videoCommentService | 142 | return this.videoCommentService |
106 | .addCommentReply(this.video.id, this.parentComment.id, commentCreate) | 143 | .addCommentReply(this.video.id, this.parentComment.id, commentCreate) |
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.html b/client/src/app/videos/+video-watch/comment/video-comments.component.html index bf6706ed3..9919ac615 100644 --- a/client/src/app/videos/+video-watch/comment/video-comments.component.html +++ b/client/src/app/videos/+video-watch/comment/video-comments.component.html | |||
@@ -10,7 +10,6 @@ | |||
10 | 10 | ||
11 | <ng-template [ngIf]="video.commentsEnabled === true"> | 11 | <ng-template [ngIf]="video.commentsEnabled === true"> |
12 | <my-video-comment-add | 12 | <my-video-comment-add |
13 | *ngIf="isUserLoggedIn()" | ||
14 | [video]="video" | 13 | [video]="video" |
15 | [user]="user" | 14 | [user]="user" |
16 | (commentCreated)="onCommentThreadCreated($event)" | 15 | (commentCreated)="onCommentThreadCreated($event)" |
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index 630345a35..770785d02 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html | |||
@@ -126,7 +126,7 @@ | |||
126 | <img [src]="video.videoChannelAvatarUrl" alt="Video channel avatar" /> | 126 | <img [src]="video.videoChannelAvatarUrl" alt="Video channel avatar" /> |
127 | </a> | 127 | </a> |
128 | 128 | ||
129 | <my-subscribe-button #subscribeButton *ngIf="isUserLoggedIn()" [videoChannel]="video.channel" size="small"></my-subscribe-button> | 129 | <my-subscribe-button #subscribeButton [videoChannel]="video.channel" size="small"></my-subscribe-button> |
130 | </div> | 130 | </div> |
131 | 131 | ||
132 | <div class="video-info-by"> | 132 | <div class="video-info-by"> |
@@ -134,8 +134,6 @@ | |||
134 | <span i18n>By {{ video.byAccount }}</span> | 134 | <span i18n>By {{ video.byAccount }}</span> |
135 | <img [src]="video.accountAvatarUrl" alt="Account avatar" /> | 135 | <img [src]="video.accountAvatarUrl" alt="Account avatar" /> |
136 | </a> | 136 | </a> |
137 | |||
138 | <my-help helpType="custom" i18n-customHtml customHtml="You can subscribe to this account via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box <strong>@{{video.account.name}}@{{video.account.host}}</strong> and subscribe there."></my-help> | ||
139 | </div> | 137 | </div> |
140 | </div> | 138 | </div> |
141 | 139 | ||
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 760be3822..0029c22ef 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -199,9 +199,18 @@ label { | |||
199 | 199 | ||
200 | .dropdown-item { | 200 | .dropdown-item { |
201 | padding: 3px 15px; | 201 | padding: 3px 15px; |
202 | |||
203 | &:active { | ||
204 | color: #000 !important; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | button { | ||
209 | @include disable-default-a-behaviour; | ||
202 | } | 210 | } |
203 | 211 | ||
204 | a { | 212 | a { |
213 | @include disable-default-a-behaviour; | ||
205 | color: #000 !important; | 214 | color: #000 !important; |
206 | } | 215 | } |
207 | } | 216 | } |
diff --git a/client/src/sass/include/_bootstrap.scss b/client/src/sass/include/_bootstrap.scss index 4abc887ad..7bce85c37 100644 --- a/client/src/sass/include/_bootstrap.scss +++ b/client/src/sass/include/_bootstrap.scss | |||
@@ -46,7 +46,7 @@ $nav-pills-link-active-color: #000; | |||
46 | @import '~bootstrap/scss/buttons'; | 46 | @import '~bootstrap/scss/buttons'; |
47 | //@import '~bootstrap/scss/transitions'; | 47 | //@import '~bootstrap/scss/transitions'; |
48 | @import '~bootstrap/scss/dropdown'; | 48 | @import '~bootstrap/scss/dropdown'; |
49 | //@import '~bootstrap/scss/button-group'; | 49 | @import '~bootstrap/scss/button-group'; |
50 | @import '~bootstrap/scss/input-group'; | 50 | @import '~bootstrap/scss/input-group'; |
51 | //@import '~bootstrap/scss/custom-forms'; | 51 | //@import '~bootstrap/scss/custom-forms'; |
52 | @import '~bootstrap/scss/nav'; | 52 | @import '~bootstrap/scss/nav'; |