mirror of https://github.com/Chocobozzz/PeerTube
				
				
				
			refactor subscribe button and comment-add for visitor-interact UX (#1100)
* refactor subscribe button for visitor-subscribe UX * refactor comment-add for visitor-interact UXpull/1119/head
							parent
							
								
									ba6a98b8fa
								
							
						
					
					
						commit
						660d11e91e
					
				| 
						 | 
					@ -9,7 +9,7 @@
 | 
				
			||||||
          <div class="actor-display-name">{{ videoChannel.displayName }}</div>
 | 
					          <div class="actor-display-name">{{ videoChannel.displayName }}</div>
 | 
				
			||||||
          <div class="actor-name">{{ videoChannel.nameWithHost }}</div>
 | 
					          <div class="actor-name">{{ videoChannel.nameWithHost }}</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <my-subscribe-button #subscribeButton *ngIf="isUserLoggedIn()" [videoChannel]="videoChannel"></my-subscribe-button>
 | 
					          <my-subscribe-button #subscribeButton [videoChannel]="videoChannel"></my-subscribe-button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
 | 
					        <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@
 | 
				
			||||||
        <div i18n class="video-channel-followers">{{ result.followersCount }} subscribers</div>
 | 
					        <div i18n class="video-channel-followers">{{ result.followersCount }} subscribers</div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <my-subscribe-button *ngIf="isUserLoggedIn()" [videoChannel]="result"></my-subscribe-button>
 | 
					      <my-subscribe-button [videoChannel]="result"></my-subscribe-button>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div *ngIf="isVideo(result)" class="entry video">
 | 
					    <div *ngIf="isVideo(result)" class="entry video">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@ import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.c
 | 
				
			||||||
import { VideoImportService } from '@app/shared/video-import/video-import.service'
 | 
					import { VideoImportService } from '@app/shared/video-import/video-import.service'
 | 
				
			||||||
import { ActionDropdownComponent } from '@app/shared/buttons/action-dropdown.component'
 | 
					import { ActionDropdownComponent } from '@app/shared/buttons/action-dropdown.component'
 | 
				
			||||||
import { NgbDropdownModule, NgbModalModule, NgbPopoverModule, NgbTabsetModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
 | 
					import { NgbDropdownModule, NgbModalModule, NgbPopoverModule, NgbTabsetModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { SubscribeButtonComponent, UserSubscriptionService } from '@app/shared/user-subscription'
 | 
					import { SubscribeButtonComponent, RemoteSubscribeComponent, UserSubscriptionService } from '@app/shared/user-subscription'
 | 
				
			||||||
import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-features-table.component'
 | 
					import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-features-table.component'
 | 
				
			||||||
import { OverviewService } from '@app/shared/overview'
 | 
					import { OverviewService } from '@app/shared/overview'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,6 +93,7 @@ import { OverviewService } from '@app/shared/overview'
 | 
				
			||||||
    ReactiveFileComponent,
 | 
					    ReactiveFileComponent,
 | 
				
			||||||
    PeertubeCheckboxComponent,
 | 
					    PeertubeCheckboxComponent,
 | 
				
			||||||
    SubscribeButtonComponent,
 | 
					    SubscribeButtonComponent,
 | 
				
			||||||
 | 
					    RemoteSubscribeComponent,
 | 
				
			||||||
    InstanceFeaturesTableComponent
 | 
					    InstanceFeaturesTableComponent
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,6 +128,7 @@ import { OverviewService } from '@app/shared/overview'
 | 
				
			||||||
    ReactiveFileComponent,
 | 
					    ReactiveFileComponent,
 | 
				
			||||||
    PeertubeCheckboxComponent,
 | 
					    PeertubeCheckboxComponent,
 | 
				
			||||||
    SubscribeButtonComponent,
 | 
					    SubscribeButtonComponent,
 | 
				
			||||||
 | 
					    RemoteSubscribeComponent,
 | 
				
			||||||
    InstanceFeaturesTableComponent,
 | 
					    InstanceFeaturesTableComponent,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    NumberFormatterPipe,
 | 
					    NumberFormatterPipe,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,2 +1,3 @@
 | 
				
			||||||
export * from './user-subscription.service'
 | 
					export * from './user-subscription.service'
 | 
				
			||||||
export * from './subscribe-button.component'
 | 
					export * from './subscribe-button.component'
 | 
				
			||||||
 | 
					export * from './remote-subscribe.component'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					<form novalidate [formGroup]="form" 
 | 
				
			||||||
 | 
					      (ngSubmit)="formValidated()">
 | 
				
			||||||
 | 
					  <div class="form-group">
 | 
				
			||||||
 | 
					    <input type="email"
 | 
				
			||||||
 | 
					      formControlName="text"
 | 
				
			||||||
 | 
					      class="form-control"
 | 
				
			||||||
 | 
					      (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()"
 | 
				
			||||||
 | 
					      placeholder="jane_doe@example.com">
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <button type="submit"
 | 
				
			||||||
 | 
					    [disabled]="!form.valid"
 | 
				
			||||||
 | 
					    class="btn btn-sm btn-remote-follow"
 | 
				
			||||||
 | 
					    i18n>
 | 
				
			||||||
 | 
					    <span *ngIf="!interact">Remote subscribe</span>
 | 
				
			||||||
 | 
					    <span *ngIf="interact">Remote interact</span>
 | 
				
			||||||
 | 
					  </button>
 | 
				
			||||||
 | 
					  <my-help *ngIf="!interact && showHelp"
 | 
				
			||||||
 | 
					           helpType="custom"
 | 
				
			||||||
 | 
					           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.">
 | 
				
			||||||
 | 
					  </my-help>
 | 
				
			||||||
 | 
					  <my-help *ngIf="showHelp && interact"
 | 
				
			||||||
 | 
					           helpType="custom"
 | 
				
			||||||
 | 
					           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.">
 | 
				
			||||||
 | 
					  </my-help>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					@import '_mixins';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-remote-follow {
 | 
				
			||||||
 | 
					  @include orange-button;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					import { Component, Input, OnInit } from '@angular/core'
 | 
				
			||||||
 | 
					import { FormReactive } from '@app/shared/forms/form-reactive'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  FormValidatorService,
 | 
				
			||||||
 | 
					  UserValidatorsService
 | 
				
			||||||
 | 
					} from '@app/shared/forms/form-validators'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  selector: 'my-remote-subscribe',
 | 
				
			||||||
 | 
					  templateUrl: './remote-subscribe.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./remote-subscribe.component.scss']
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RemoteSubscribeComponent extends FormReactive implements OnInit {
 | 
				
			||||||
 | 
					  @Input() account: string
 | 
				
			||||||
 | 
					  @Input() interact = false
 | 
				
			||||||
 | 
					  @Input() showHelp = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor (
 | 
				
			||||||
 | 
					    protected formValidatorService: FormValidatorService,
 | 
				
			||||||
 | 
					    private userValidatorsService: UserValidatorsService
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    super()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnInit () {
 | 
				
			||||||
 | 
					    this.buildForm({
 | 
				
			||||||
 | 
					      text: this.userValidatorsService.USER_EMAIL
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onValidKey () {
 | 
				
			||||||
 | 
					    this.onValueChanged()
 | 
				
			||||||
 | 
					    if (!this.form.valid) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.formValidated()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  formValidated () {
 | 
				
			||||||
 | 
					    const address = this.form.value['text']
 | 
				
			||||||
 | 
					    const [ , hostname ] = address.split('@')
 | 
				
			||||||
 | 
					    window.open(`https://${hostname}/authorize_interaction?acct=${this.account}`)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,45 @@
 | 
				
			||||||
<span i18n *ngIf="subscribed === false" class="subscribe-button" [ngClass]="size" role="button" (click)="subscribe()">
 | 
					<div class="btn-group-subscribe btn-group"
 | 
				
			||||||
  <span>Subscribe</span>
 | 
					    [ngClass]="{'subscribe-button': subscribed !== true, 'unsubscribe-button': subscribed === true}">
 | 
				
			||||||
 | 
					  <button *ngIf="subscribed === false && isUserLoggedIn()" type="button"
 | 
				
			||||||
 | 
					          class="btn btn-sm" role="button"
 | 
				
			||||||
 | 
					          (click)="subscribe()" i18n>
 | 
				
			||||||
 | 
					    <span>
 | 
				
			||||||
 | 
					      Subscribe
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
    <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count">
 | 
					    <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count">
 | 
				
			||||||
      {{ videoChannel.followersCount | myNumberFormatter }}
 | 
					      {{ videoChannel.followersCount | myNumberFormatter }}
 | 
				
			||||||
    </span>
 | 
					    </span>
 | 
				
			||||||
</span>
 | 
					  </button>
 | 
				
			||||||
 | 
					  <button *ngIf="subscribed === true" type="button"
 | 
				
			||||||
<span *ngIf="subscribed === true" class="unsubscribe-button" [ngClass]="size" role="button" (click)="unsubscribe()">
 | 
					          class="btn btn-sm" role="button"
 | 
				
			||||||
  <span class="subscribed" i18n>Subscribed</span>
 | 
					          (click)="unsubscribe()" i18n>Unsubscribe</button>
 | 
				
			||||||
  <span class="unsubscribe" i18n>Unsubscribe</span>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div class="btn-group" ngbDropdown autoClose="outside" 
 | 
				
			||||||
 | 
					       placement="bottom-right" role="group"
 | 
				
			||||||
 | 
					       aria-label="Multiple ways to subscribe to the current channel">
 | 
				
			||||||
 | 
					    <button class="btn btn-sm dropdown-toggle-split" ngbDropdownToggle>
 | 
				
			||||||
 | 
					      <span *ngIf="!isUserLoggedIn()">
 | 
				
			||||||
 | 
					        Subscribe
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
      <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count">
 | 
					      <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count">
 | 
				
			||||||
        {{ videoChannel.followersCount | myNumberFormatter }}
 | 
					        {{ videoChannel.followersCount | myNumberFormatter }}
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
</span>
 | 
					    </button>
 | 
				
			||||||
 | 
					    <div class="dropdown-menu" ngbDropdownMenu>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <h6 class="dropdown-header" i18n>Using an ActivityPub-compatible account</h6>
 | 
				
			||||||
 | 
					      <button class="dropdown-item" (click)="subscribe()"
 | 
				
			||||||
 | 
					              *ngIf="subscribed === false">
 | 
				
			||||||
 | 
					        <span *ngIf="!isUserLoggedIn()" i18n>Subscribe with an account on {{ videoChannel.host }}</span>
 | 
				
			||||||
 | 
					        <span *ngIf="isUserLoggedIn()" i18n>Subscribe with your local account</span>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					      <button class="dropdown-item" i18n>Subscribe with a remote account:</button>
 | 
				
			||||||
 | 
					      <my-remote-subscribe showHelp="true" account="{{ uriAccount }}"></my-remote-subscribe>
 | 
				
			||||||
 | 
					      <div class="dropdown-divider"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <h6 class="dropdown-header" i18n>Using a syndication feed</h6>
 | 
				
			||||||
 | 
					      <button (click)="rssOpen()" class="dropdown-item" i18n>Subscribe via RSS</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -1,51 +1,46 @@
 | 
				
			||||||
@import '_variables';
 | 
					@import '_variables';
 | 
				
			||||||
@import '_mixins';
 | 
					@import '_mixins';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.subscribe-button {
 | 
					.btn-group-subscribe {
 | 
				
			||||||
  @include peertube-button;
 | 
					  @include peertube-button;
 | 
				
			||||||
 | 
					  @include disable-default-a-behaviour;
 | 
				
			||||||
 | 
					  float: right;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.btn-group > .btn:not(.dropdown-toggle) {
 | 
				
			||||||
 | 
					    padding-right: 5px;
 | 
				
			||||||
 | 
					    font-size: 15px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  &.btn-group > .btn-group:not(:first-child) > .btn {
 | 
				
			||||||
 | 
					    padding-left: 2px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.subscribe-button {
 | 
				
			||||||
 | 
					    .btn {
 | 
				
			||||||
      @include orange-button;
 | 
					      @include orange-button;
 | 
				
			||||||
}
 | 
					      font-weight: 600;
 | 
				
			||||||
 | 
					 | 
				
			||||||
.unsubscribe-button {
 | 
					 | 
				
			||||||
  @include peertube-button;
 | 
					 | 
				
			||||||
  @include grey-button
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.subscribe-button,
 | 
					 | 
				
			||||||
.unsubscribe-button {
 | 
					 | 
				
			||||||
  display: inline-block;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &.small {
 | 
					 | 
				
			||||||
    min-width: 75px;
 | 
					 | 
				
			||||||
    height: 20px;
 | 
					 | 
				
			||||||
    line-height: 20px;
 | 
					 | 
				
			||||||
    font-size: 13px;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.normal {
 | 
					    span.followers-count {
 | 
				
			||||||
    min-width: 120px;
 | 
					      padding-left:5px;
 | 
				
			||||||
    height: 30px;
 | 
					    }
 | 
				
			||||||
    line-height: 30px;
 | 
					  }
 | 
				
			||||||
    font-size: 16px;
 | 
					  &.unsubscribe-button {
 | 
				
			||||||
  }
 | 
					    .btn {
 | 
				
			||||||
}
 | 
					      @include grey-button;
 | 
				
			||||||
 | 
					      font-weight: 600;
 | 
				
			||||||
.unsubscribe-button {
 | 
					    }
 | 
				
			||||||
  .subscribed {
 | 
					  }
 | 
				
			||||||
    display: inline;
 | 
					
 | 
				
			||||||
  }
 | 
					  .dropdown-header {
 | 
				
			||||||
 | 
					    padding-left: 1rem;
 | 
				
			||||||
  .unsubscribe {
 | 
					  }
 | 
				
			||||||
    display: none;
 | 
					
 | 
				
			||||||
  }
 | 
					  /deep/ form {
 | 
				
			||||||
 | 
					    padding: 0.25rem 1rem;
 | 
				
			||||||
  &:hover {
 | 
					  }
 | 
				
			||||||
    .subscribed {
 | 
					
 | 
				
			||||||
      display: none;
 | 
					  input {
 | 
				
			||||||
    }
 | 
					    @include peertube-input-text(100%);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    .unsubscribe {
 | 
					 | 
				
			||||||
      display: inline;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
import { Component, Input, OnInit } from '@angular/core'
 | 
					import { Component, Input, OnInit } from '@angular/core'
 | 
				
			||||||
 | 
					import { Router } from '@angular/router'
 | 
				
			||||||
 | 
					import { AuthService } from '@app/core'
 | 
				
			||||||
import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service'
 | 
					import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service'
 | 
				
			||||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
 | 
					import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
 | 
				
			||||||
import { NotificationsService } from 'angular2-notifications'
 | 
					import { NotificationsService } from 'angular2-notifications'
 | 
				
			||||||
| 
						 | 
					@ -17,6 +19,8 @@ export class SubscribeButtonComponent implements OnInit {
 | 
				
			||||||
  subscribed: boolean
 | 
					  subscribed: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor (
 | 
					  constructor (
 | 
				
			||||||
 | 
					    private authService: AuthService,
 | 
				
			||||||
 | 
					    private router: Router,
 | 
				
			||||||
    private notificationsService: NotificationsService,
 | 
					    private notificationsService: NotificationsService,
 | 
				
			||||||
    private userSubscriptionService: UserSubscriptionService,
 | 
					    private userSubscriptionService: UserSubscriptionService,
 | 
				
			||||||
    private i18n: I18n
 | 
					    private i18n: I18n
 | 
				
			||||||
| 
						 | 
					@ -26,7 +30,12 @@ export class SubscribeButtonComponent implements OnInit {
 | 
				
			||||||
    return this.videoChannel.name + '@' + this.videoChannel.host
 | 
					    return this.videoChannel.name + '@' + this.videoChannel.host
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get uriAccount () {
 | 
				
			||||||
 | 
					    return this.videoChannel.ownerAccount.name + '@' + this.videoChannel.host
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit () {
 | 
					  ngOnInit () {
 | 
				
			||||||
 | 
					    if (this.isUserLoggedIn()) {
 | 
				
			||||||
      this.userSubscriptionService.isSubscriptionExists(this.uri)
 | 
					      this.userSubscriptionService.isSubscriptionExists(this.uri)
 | 
				
			||||||
        .subscribe(
 | 
					        .subscribe(
 | 
				
			||||||
          res => this.subscribed = res[this.uri],
 | 
					          res => this.subscribed = res[this.uri],
 | 
				
			||||||
| 
						 | 
					@ -34,8 +43,17 @@ export class SubscribeButtonComponent implements OnInit {
 | 
				
			||||||
          err => this.notificationsService.error(this.i18n('Error'), err.message)
 | 
					          err => this.notificationsService.error(this.i18n('Error'), err.message)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  subscribe () {
 | 
					  subscribe () {
 | 
				
			||||||
 | 
					    if (this.isUserLoggedIn()) {
 | 
				
			||||||
 | 
					      this.localSubscribe()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.gotoLogin()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  localSubscribe () {
 | 
				
			||||||
    this.userSubscriptionService.addSubscription(this.uri)
 | 
					    this.userSubscriptionService.addSubscription(this.uri)
 | 
				
			||||||
      .subscribe(
 | 
					      .subscribe(
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
| 
						 | 
					@ -52,6 +70,12 @@ export class SubscribeButtonComponent implements OnInit {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  unsubscribe () {
 | 
					  unsubscribe () {
 | 
				
			||||||
 | 
					    if (this.isUserLoggedIn()) {
 | 
				
			||||||
 | 
					      this.localUnsubscribe()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  localUnsubscribe () {
 | 
				
			||||||
    this.userSubscriptionService.deleteSubscription(this.uri)
 | 
					    this.userSubscriptionService.deleteSubscription(this.uri)
 | 
				
			||||||
        .subscribe(
 | 
					        .subscribe(
 | 
				
			||||||
          () => {
 | 
					          () => {
 | 
				
			||||||
| 
						 | 
					@ -66,4 +90,16 @@ export class SubscribeButtonComponent implements OnInit {
 | 
				
			||||||
          err => this.notificationsService.error(this.i18n('Error'), err.message)
 | 
					          err => this.notificationsService.error(this.i18n('Error'), err.message)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  isUserLoggedIn () {
 | 
				
			||||||
 | 
					    return this.authService.isLoggedIn()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gotoLogin () {
 | 
				
			||||||
 | 
					    this.router.navigate([ '/login' ])
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rssOpen () {
 | 
				
			||||||
 | 
					    window.open('')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,11 @@
 | 
				
			||||||
<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
 | 
					<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
 | 
				
			||||||
  <div class="avatar-and-textarea">
 | 
					  <div class="avatar-and-textarea">
 | 
				
			||||||
    <img [src]="user.accountAvatarUrl" alt="Avatar" />
 | 
					    <img [src]="getAvatarUrl()" alt="Avatar" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="form-group">
 | 
					    <div class="form-group">
 | 
				
			||||||
      <textarea i18n-placeholder placeholder="Add comment..." autosize
 | 
					      <textarea i18n-placeholder placeholder="Add comment..." autosize
 | 
				
			||||||
 | 
					                [readonly]="(user === null) ? true : false"
 | 
				
			||||||
 | 
					                (click)="openVisitorModal($event)"
 | 
				
			||||||
                formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
 | 
					                formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
 | 
				
			||||||
                (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea>
 | 
					                (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,3 +22,25 @@
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</form>
 | 
					</form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-template #visitorModal let-modal>
 | 
				
			||||||
 | 
					  <div class="modal-header">
 | 
				
			||||||
 | 
					    <h4 class="modal-title" id="modal-basic-title" i18n>You are one step away from commenting</h4>
 | 
				
			||||||
 | 
					    <button type="button" class="close" aria-label="Close" (click)="hideVisitorModal()"></button>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div class="modal-body" i18n>
 | 
				
			||||||
 | 
					    <span i18n>
 | 
				
			||||||
 | 
					      If you have an account on this instance, you can login:
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					    <span class="btn btn-sm mx-3" role="button" (click)="gotoLogin()" i18n>login to comment</span>
 | 
				
			||||||
 | 
					    <span i18n>
 | 
				
			||||||
 | 
					      Otherwise you can comment using an account on an ActivityPub-compatible instance:
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					    <my-remote-subscribe [interact]="true" account="{{ uri }}"></my-remote-subscribe>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div class="modal-footer inputs">
 | 
				
			||||||
 | 
					    <span i18n class="action-button action-button-cancel" role="button" (click)="hideVisitorModal()">
 | 
				
			||||||
 | 
					      Cancel
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</ng-template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ form {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  button {
 | 
					  button {
 | 
				
			||||||
    @include peertube-button;
 | 
					    @include peertube-button;
 | 
				
			||||||
    @include orange-button
 | 
					    @include orange-button;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,3 +45,15 @@ form {
 | 
				
			||||||
    font-size: 14px !important;
 | 
					    font-size: 14px !important;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.modal-body {
 | 
				
			||||||
 | 
					  .btn {
 | 
				
			||||||
 | 
					    @include peertube-button;
 | 
				
			||||||
 | 
					    @include orange-button;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  span {
 | 
				
			||||||
 | 
					    float: left;
 | 
				
			||||||
 | 
					    margin-bottom: 20px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
 | 
					import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
 | 
				
			||||||
 | 
					import { Router } from '@angular/router'
 | 
				
			||||||
import { NotificationsService } from 'angular2-notifications'
 | 
					import { NotificationsService } from 'angular2-notifications'
 | 
				
			||||||
import { Observable } from 'rxjs'
 | 
					import { Observable } from 'rxjs'
 | 
				
			||||||
import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model'
 | 
					import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model'
 | 
				
			||||||
| 
						 | 
					@ -10,6 +11,8 @@ import { VideoCommentService } from './video-comment.service'
 | 
				
			||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
 | 
					import { I18n } from '@ngx-translate/i18n-polyfill'
 | 
				
			||||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
 | 
					import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
 | 
				
			||||||
import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service'
 | 
					import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service'
 | 
				
			||||||
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
 | 
					import { AuthService } from '@app/core/auth'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'my-video-comment-add',
 | 
					  selector: 'my-video-comment-add',
 | 
				
			||||||
| 
						 | 
					@ -25,15 +28,20 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output() commentCreated = new EventEmitter<VideoCommentCreate>()
 | 
					  @Output() commentCreated = new EventEmitter<VideoCommentCreate>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ViewChild('visitorModal') visitorModal: NgbModal
 | 
				
			||||||
  @ViewChild('textarea') private textareaElement: ElementRef
 | 
					  @ViewChild('textarea') private textareaElement: ElementRef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private addingComment = false
 | 
					  private addingComment = false
 | 
				
			||||||
 | 
					  private uri: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor (
 | 
					  constructor (
 | 
				
			||||||
    protected formValidatorService: FormValidatorService,
 | 
					    protected formValidatorService: FormValidatorService,
 | 
				
			||||||
    private videoCommentValidatorsService: VideoCommentValidatorsService,
 | 
					    private videoCommentValidatorsService: VideoCommentValidatorsService,
 | 
				
			||||||
    private notificationsService: NotificationsService,
 | 
					    private notificationsService: NotificationsService,
 | 
				
			||||||
    private videoCommentService: VideoCommentService,
 | 
					    private videoCommentService: VideoCommentService,
 | 
				
			||||||
 | 
					    private authService: AuthService,
 | 
				
			||||||
 | 
					    private modalService: NgbModal,
 | 
				
			||||||
 | 
					    private router: Router,
 | 
				
			||||||
    private i18n: I18n
 | 
					    private i18n: I18n
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
| 
						 | 
					@ -44,6 +52,9 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
 | 
				
			||||||
      text: this.videoCommentValidatorsService.VIDEO_COMMENT_TEXT
 | 
					      text: this.videoCommentValidatorsService.VIDEO_COMMENT_TEXT
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.uri = this.router.url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.user) {
 | 
				
			||||||
      if (this.focusOnInit === true) {
 | 
					      if (this.focusOnInit === true) {
 | 
				
			||||||
        this.textareaElement.nativeElement.focus()
 | 
					        this.textareaElement.nativeElement.focus()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -59,6 +70,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
 | 
				
			||||||
        this.form.patchValue({ text: mentionsText })
 | 
					        this.form.patchValue({ text: mentionsText })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onValidKey () {
 | 
					  onValidKey () {
 | 
				
			||||||
    this.onValueChanged()
 | 
					    this.onValueChanged()
 | 
				
			||||||
| 
						 | 
					@ -67,6 +79,20 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
 | 
				
			||||||
    this.formValidated()
 | 
					    this.formValidated()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  openVisitorModal (event) {
 | 
				
			||||||
 | 
					    if (this.user === null) { // we only open it for visitors
 | 
				
			||||||
 | 
					      // fixing ng-bootstrap ModalService and the "Expression Changed After It Has Been Checked" Error
 | 
				
			||||||
 | 
					      event.srcElement.blur()
 | 
				
			||||||
 | 
					      event.preventDefault()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.modalService.open(this.visitorModal)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hideVisitorModal () {
 | 
				
			||||||
 | 
					    this.modalService.dismissAll()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  formValidated () {
 | 
					  formValidated () {
 | 
				
			||||||
    // If we validate very quickly the comment form, we might comment twice
 | 
					    // If we validate very quickly the comment form, we might comment twice
 | 
				
			||||||
    if (this.addingComment) return
 | 
					    if (this.addingComment) return
 | 
				
			||||||
| 
						 | 
					@ -101,6 +127,17 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
 | 
				
			||||||
    return this.form.value['text']
 | 
					    return this.form.value['text']
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getAvatarUrl () {
 | 
				
			||||||
 | 
					    if (this.user) return this.user.accountAvatarUrl
 | 
				
			||||||
 | 
					    return window.location.origin + '/client/assets/images/default-avatar.png'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gotoLogin () {
 | 
				
			||||||
 | 
					    this.hideVisitorModal()
 | 
				
			||||||
 | 
					    this.authService.redirectUrl = this.router.url
 | 
				
			||||||
 | 
					    this.router.navigate([ '/login' ])
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ng-template [ngIf]="video.commentsEnabled === true">
 | 
					  <ng-template [ngIf]="video.commentsEnabled === true">
 | 
				
			||||||
    <my-video-comment-add
 | 
					    <my-video-comment-add
 | 
				
			||||||
      *ngIf="isUserLoggedIn()"
 | 
					 | 
				
			||||||
      [video]="video"
 | 
					      [video]="video"
 | 
				
			||||||
      [user]="user"
 | 
					      [user]="user"
 | 
				
			||||||
      (commentCreated)="onCommentThreadCreated($event)"
 | 
					      (commentCreated)="onCommentThreadCreated($event)"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,7 +126,7 @@
 | 
				
			||||||
                <img [src]="video.videoChannelAvatarUrl" alt="Video channel avatar" />
 | 
					                <img [src]="video.videoChannelAvatarUrl" alt="Video channel avatar" />
 | 
				
			||||||
              </a>
 | 
					              </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <my-subscribe-button #subscribeButton *ngIf="isUserLoggedIn()" [videoChannel]="video.channel" size="small"></my-subscribe-button>
 | 
					              <my-subscribe-button #subscribeButton [videoChannel]="video.channel" size="small"></my-subscribe-button>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class="video-info-by">
 | 
					            <div class="video-info-by">
 | 
				
			||||||
| 
						 | 
					@ -134,8 +134,6 @@
 | 
				
			||||||
                <span i18n>By {{ video.byAccount }}</span>
 | 
					                <span i18n>By {{ video.byAccount }}</span>
 | 
				
			||||||
                <img [src]="video.accountAvatarUrl" alt="Account avatar" />
 | 
					                <img [src]="video.accountAvatarUrl" alt="Account avatar" />
 | 
				
			||||||
              </a>
 | 
					              </a>
 | 
				
			||||||
 | 
					 | 
				
			||||||
              <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>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -199,9 +199,18 @@ label {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .dropdown-item {
 | 
					  .dropdown-item {
 | 
				
			||||||
    padding: 3px 15px;
 | 
					    padding: 3px 15px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:active {
 | 
				
			||||||
 | 
					      color: #000 !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  button {
 | 
				
			||||||
 | 
					    @include disable-default-a-behaviour;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  a {
 | 
					  a {
 | 
				
			||||||
 | 
					    @include disable-default-a-behaviour;
 | 
				
			||||||
    color: #000 !important;
 | 
					    color: #000 !important;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,7 +46,7 @@ $nav-pills-link-active-color: #000;
 | 
				
			||||||
@import '~bootstrap/scss/buttons';
 | 
					@import '~bootstrap/scss/buttons';
 | 
				
			||||||
//@import '~bootstrap/scss/transitions';
 | 
					//@import '~bootstrap/scss/transitions';
 | 
				
			||||||
@import '~bootstrap/scss/dropdown';
 | 
					@import '~bootstrap/scss/dropdown';
 | 
				
			||||||
//@import '~bootstrap/scss/button-group';
 | 
					@import '~bootstrap/scss/button-group';
 | 
				
			||||||
@import '~bootstrap/scss/input-group';
 | 
					@import '~bootstrap/scss/input-group';
 | 
				
			||||||
//@import '~bootstrap/scss/custom-forms';
 | 
					//@import '~bootstrap/scss/custom-forms';
 | 
				
			||||||
@import '~bootstrap/scss/nav';
 | 
					@import '~bootstrap/scss/nav';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue