Refactor my-subscribe-button to support full account subscription
authorRigel Kent <sendmemail@rigelk.eu>
Tue, 7 Jan 2020 22:51:14 +0000 (23:51 +0100)
committerChocobozzz <chocobozzz@cpy.re>
Fri, 10 Jan 2020 09:12:09 +0000 (10:12 +0100)
client/src/app/+accounts/account-video-channels/account-video-channels.component.html
client/src/app/+accounts/accounts.component.html
client/src/app/+accounts/accounts.component.scss
client/src/app/+accounts/accounts.component.ts
client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html
client/src/app/+video-channels/video-channels.component.html
client/src/app/search/search.component.html
client/src/app/shared/user-subscription/subscribe-button.component.html
client/src/app/shared/user-subscription/subscribe-button.component.ts
client/src/app/shared/video/video-miniature.component.html
client/src/app/videos/+video-watch/video-watch.component.html

index 4ebad514cd6febf9b5c2cae2d51f2902b77b7fb6..93f43a35046ecd8dfe617bd67145b98bd9ca23d2 100644 (file)
@@ -9,14 +9,18 @@
           <img [src]="videoChannel.avatarUrl" alt="Avatar" />
 
           <div>{{ videoChannel.displayName }}</div>
-          <div i18n class="followers">{{ videoChannel.followersCount }} subscribers</div>
+          <div class="followers">{{ videoChannel.followersCount }} 
+            <ng-container *ngIf="videoChannel.followersCount === 1; then single; else multiple"></ng-container>
+            <ng-template i18n #single>subscriber</ng-template>
+            <ng-template i18n #multiple>subscribers</ng-template>
+          </div>
         </a>
 
-        <my-subscribe-button [videoChannel]="videoChannel"></my-subscribe-button>
+        <my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button>
       </div>
 
       <div *ngIf="getVideosOf(videoChannel)" class="videos">
-        <div class="no-results" i18n *ngIf="getVideosOf(videoChannel).length === 0">This channel does not have videos.</div>
+        <div class="no-results my-5" i18n *ngIf="getVideosOf(videoChannel).length === 0">This channel doesn't have any videos.</div>
 
         <my-video-miniature
           *ngFor="let video of getVideosOf(videoChannel)"
index 70257162d5b48d569591a588f8e74fa745400dab..1b6eb480ebb13df1ba2650f34a98ffa4a0f7c1bb 100644 (file)
           >
           </my-user-moderation-dropdown>
         </div>
-        <div i18n class="actor-followers">{{ account.followersCount }} subscribers</div>
+        <div class="actor-followers">
+          {{ account.followersCount }} 
+          <ng-container *ngIf="account.followersCount === 1; then single; else multiple"></ng-container>
+          <ng-template i18n #single>subscriber</ng-template>
+          <ng-template i18n #multiple>subscribers</ng-template>
+        </div>
       </div>
+
+      <my-subscribe-button *ngIf="videoChannels" [account]="account" [videoChannels]="videoChannels"></my-subscribe-button>
     </div>
 
     <div class="links">
index 273e5c43a2665f02aec439b819e6f9150e1d0d2a..ab3aece54a90f679f0fc8641e02e77a61efed163 100644 (file)
@@ -3,6 +3,16 @@
 
 .sub-menu {
   @include sub-menu-with-actor;
+
+  .actor {
+    width: 100%;
+  }
+}
+
+my-subscribe-button {
+  height: max-content;
+  margin-left: auto;
+  margin-top: 20px;
 }
 
 my-user-moderation-dropdown,
index 3118d7562d657b8804a121daf780661314dbc653..e3a503f4cb8ac97d91d9e033dbfd55293d2a9c06 100644 (file)
@@ -8,6 +8,8 @@ import { Subscription } from 'rxjs'
 import { AuthService, Notifier, RedirectService } from '@app/core'
 import { User, UserRight } from '../../../../shared'
 import { I18n } from '@ngx-translate/i18n-polyfill'
+import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
+import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
 
 @Component({
   templateUrl: './accounts.component.html',
@@ -16,6 +18,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
 export class AccountsComponent implements OnInit, OnDestroy {
   account: Account
   user: User
+  videoChannels: VideoChannel[]
 
   private routeSub: Subscription
 
@@ -23,6 +26,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
     private route: ActivatedRoute,
     private userService: UserService,
     private accountService: AccountService,
+    private videoChannelService: VideoChannelService,
     private notifier: Notifier,
     private restExtractor: RestExtractor,
     private redirectService: RedirectService,
@@ -40,7 +44,11 @@ export class AccountsComponent implements OnInit, OnDestroy {
         catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))
       )
       .subscribe(
-        account => this.account = account,
+        account => {
+          this.account = account
+          this.videoChannelService.listAccountVideoChannels(account)
+            .subscribe(videoChannels => this.videoChannels = videoChannels.data)
+        },
 
         err => this.notifier.error(err.message)
       )
index ce43ed6ddfffcb48e2a55bcfce481667c3d3e78a..b87a4c00c23da1dfe4432e16d5d314752acaacf6 100644 (file)
@@ -20,6 +20,6 @@
       </a>
     </div>
 
-    <my-subscribe-button [videoChannel]="videoChannel"></my-subscribe-button>
+    <my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button>
   </div>
 </div>
index f8b7fa583839017c7f30f34abfa44c564cc5f16e..f4b944c599a5b236d70bb1810a21d18c5f03aaee 100644 (file)
@@ -15,7 +15,7 @@
             </button>
           </div>
 
-          <my-subscribe-button #subscribeButton [videoChannel]="videoChannel"></my-subscribe-button>
+          <my-subscribe-button #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button>
         </div>
         <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
 
index 055f64cc8ee081eab67d43f7bfeeba2002724af0..496ee970ab076d03ddda523d9c749da4ece45b71 100644 (file)
@@ -44,7 +44,7 @@
         <div i18n class="video-channel-followers">{{ result.followersCount }} subscribers</div>
       </div>
 
-      <my-subscribe-button [videoChannel]="result"></my-subscribe-button>
+      <my-subscribe-button [videoChannels]="[result]"></my-subscribe-button>
     </div>
 
     <div *ngIf="isVideo(result)" class="entry video">
index 25f3be2155613bc1a68959e68e554effad71d79e..a119beff6f92651cfcd6d19b516df65987aa2787 100644 (file)
@@ -1,34 +1,46 @@
 <div class="btn-group-subscribe btn-group"
-    [ngClass]="{'subscribe-button': subscribed !== true, 'unsubscribe-button': subscribed === true}">
-  <button *ngIf="subscribed === false && isUserLoggedIn()" type="button"
-          class="btn btn-sm" role="button"
-          (click)="subscribe()">
-    <span i18n>
-      Subscribe
+    [ngClass]="{'subscribe-button': !isAllChannelsSubscribed(), 'unsubscribe-button': isAllChannelsSubscribed()}">
+
+  <ng-template #userLoggedOut>
+    <span>
+      <ng-container *ngIf="account; then multiple; else single"></ng-container>
+      <ng-template i18n #single>Subscribe</ng-template>
+      <ng-template i18n #multiple>Subscribe to all channels</ng-template>
     </span>
     <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count">
       {{ videoChannel.followersCount | myNumberFormatter }}
     </span>
-  </button>
+  </ng-template>
+
+  <ng-template #userLoggedIn>
+    <button *ngIf="!isAllChannelsSubscribed()" type="button"
+            class="btn btn-sm" role="button"
+            (click)="subscribe()">
+      <ng-template [ngTemplateOutlet]="userLoggedOut"></ng-template>
+    </button>
+  
+    <button
+      *ngIf="isAllChannelsSubscribed()" type="button"
+      class="btn btn-sm" role="button"
+      (click)="unsubscribe()" i18n
+    >
+      <ng-container *ngIf="account; then multiple; else single"></ng-container>
+      <ng-template i18n #single>Unsubscribe</ng-template>
+      <ng-template i18n #multiple>Unsubscribe from all channels</ng-template>
+    </button>
+  </ng-template>
 
-  <button
-    *ngIf="subscribed === true" type="button"
-    class="btn btn-sm" role="button"
-    (click)="unsubscribe()" i18n
-  >
-    Unsubscribe
-  </button>
+  <ng-container
+    *ngIf="isUserLoggedIn(); then userLoggedIn">
+  </ng-container>
 
   <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()" i18n>
-        Subscribe
-      </span>
-      <span *ngIf="displayFollowers && videoChannel.followersCount !== 0" class="followers-count">
-        {{ videoChannel.followersCount | myNumberFormatter }}
-      </span>
+      <ng-container
+        *ngIf="!isUserLoggedIn(); then userLoggedOut">
+      </ng-container>
     </button>
 
     <div class="dropdown-menu" ngbDropdownMenu>
index 0407856cbd1d89827e404e99974a6fddcc23062d..0c1f71c6c2062cb671fda641efddbc4655be72c4 100644 (file)
@@ -6,6 +6,8 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { VideoService } from '@app/shared/video/video.service'
 import { FeedFormat } from '../../../../../shared/models/feeds'
+import { Account } from '@app/shared/account/account.model'
+import { forkJoin } from 'rxjs'
 
 @Component({
   selector: 'my-subscribe-button',
@@ -13,11 +15,12 @@ import { FeedFormat } from '../../../../../shared/models/feeds'
   styleUrls: [ './subscribe-button.component.scss' ]
 })
 export class SubscribeButtonComponent implements OnInit {
-  @Input() videoChannel: VideoChannel
+  @Input() account: Account
+  @Input() videoChannels: VideoChannel[]
   @Input() displayFollowers = false
   @Input() size: 'small' | 'normal' = 'normal'
 
-  subscribed: boolean
+  subscribed: Map<string, boolean>
 
   constructor (
     private authService: AuthService,
@@ -26,32 +29,51 @@ export class SubscribeButtonComponent implements OnInit {
     private userSubscriptionService: UserSubscriptionService,
     private i18n: I18n,
     private videoService: VideoService
-  ) { }
+  ) {
+    this.subscribed = new Map<string, boolean>()
+  }
+
+  get handle () {
+    return this.account
+      ? this.account.nameWithHost
+      : this.videoChannels[0].name + '@' + this.videoChannels[0].host
+  }
 
   get channelHandle () {
-    return this.videoChannel.name + '@' + this.videoChannel.host
+    return this.getChannelHandler(this.videoChannels[0])
   }
 
-  get channelUri () {
-    return this.videoChannel.url
+  get uri () {
+    return this.account
+      ? this.account.url
+      : this.videoChannels[0].url
   }
 
   get rssUri () {
-    const rssFeed = this.videoService
-                      .getVideoChannelFeedUrls(this.videoChannel.id)
-                      .find(i => i.format === FeedFormat.RSS)
+    const rssFeed = this.account
+      ? this.videoService
+          .getAccountFeedUrls(this.account.id)
+          .find(i => i.format === FeedFormat.RSS)
+      : this.videoService
+          .getVideoChannelFeedUrls(this.videoChannels[0].id)
+          .find(i => i.format === FeedFormat.RSS)
 
     return rssFeed.url
   }
 
   ngOnInit () {
     if (this.isUserLoggedIn()) {
-      this.userSubscriptionService.doesSubscriptionExist(this.channelHandle)
-        .subscribe(
-          res => this.subscribed = res[this.channelHandle],
 
-          err => this.notifier.error(err.message)
-        )
+      forkJoin(this.videoChannels.map(videoChannel => {
+        const handle = this.getChannelHandler(videoChannel)
+        this.subscribed.set(handle, false)
+        this.userSubscriptionService.doesSubscriptionExist(handle)
+          .subscribe(
+            res => this.subscribed.set(handle, res[handle]),
+
+            err => this.notifier.error(err.message)
+          )
+      }))
     }
   }
 
@@ -64,15 +86,34 @@ export class SubscribeButtonComponent implements OnInit {
   }
 
   localSubscribe () {
-    this.userSubscriptionService.addSubscription(this.channelHandle)
+    const observableBatch: any = []
+
+    this.videoChannels
+      .filter(videoChannel => this.subscribeStatus(false).includes(this.getChannelHandler(videoChannel)))
+      .forEach(videoChannel => observableBatch.push(
+        this.userSubscriptionService.addSubscription(this.getChannelHandler(videoChannel))
+      ))
+
+    forkJoin(observableBatch)
       .subscribe(
         () => {
-          this.subscribed = true
+          [...this.subscribed.keys()].forEach((key) => {
+            this.subscribed.set(key, true)
+          })
 
           this.notifier.success(
-            this.i18n('Subscribed to {{nameWithHost}}. You will be notified of all their new videos.',
-              { nameWithHost: this.videoChannel.displayName }
-            ),
+            this.account
+              ? this.i18n(
+                  'Subscribed to all current channels of {{nameWithHost}}. ' +
+                  'You will be notified of all their new videos.',
+                  { nameWithHost: this.account.displayName }
+                )
+              : this.i18n(
+                  'Subscribed to {{nameWithHost}}. ' +
+                  'You will be notified of all their new videos.',
+                  { nameWithHost: this.videoChannels[0].displayName }
+                )
+            ,
             this.i18n('Subscribed')
           )
         },
@@ -88,13 +129,26 @@ export class SubscribeButtonComponent implements OnInit {
   }
 
   localUnsubscribe () {
-    this.userSubscriptionService.deleteSubscription(this.channelHandle)
+    const observableBatch: any = []
+
+    this.videoChannels
+      .filter(videoChannel => this.subscribeStatus(true).includes(this.getChannelHandler(videoChannel)))
+      .forEach(videoChannel => observableBatch.push(
+        this.userSubscriptionService.deleteSubscription(this.getChannelHandler(videoChannel))
+      ))
+
+    forkJoin(observableBatch)
         .subscribe(
           () => {
-            this.subscribed = false
+            [...this.subscribed.keys()].forEach((key) => {
+              this.subscribed.set(key, false)
+            })
 
             this.notifier.success(
-              this.i18n('Unsubscribed from {{nameWithHost}}', { nameWithHost: this.videoChannel.displayName }),
+              this.account
+                ? this.i18n('Unsubscribed from all channels of {{nameWithHost}}', { nameWithHost: this.account.nameWithHost })
+                : this.i18n('Unsubscribed from {{nameWithHost}}', { nameWithHost: this.videoChannels[0].nameWithHost })
+              ,
               this.i18n('Unsubscribed')
             )
           },
@@ -107,7 +161,23 @@ export class SubscribeButtonComponent implements OnInit {
     return this.authService.isLoggedIn()
   }
 
+  isAllChannelsSubscribed () {
+    return !Array.from(this.subscribed.values()).includes(false)
+  }
+
   gotoLogin () {
     this.router.navigate([ '/login' ])
   }
+
+  private getChannelHandler (videoChannel: VideoChannel) {
+    return videoChannel.name + '@' + videoChannel.host
+  }
+
+  private subscribeStatus (subscribed: boolean) {
+    const accumulator = []
+    for (const [key, value] of this.subscribed.entries()) {
+      if (value === subscribed) accumulator.push(key)
+    }
+    return accumulator
+  }
 }
index 036825e6131700583c8831c725841ac02dd14fa9..a31165a415ba47cf6c9435a98a9e66fdfbcf776f 100644 (file)
 
         <span class="views">
           <ng-container *ngIf="displayOptions.date && displayOptions.views"> • </ng-container>
-          <ng-container i18n *ngIf="displayOptions.views">{{ video.views | myNumberFormatter }} views</ng-container>
+          <ng-container i18n *ngIf="displayOptions.views">{{ video.views | myNumberFormatter }} 
+            <ng-container *ngIf="video.views === 1; then single; else multiple"></ng-container>
+            <ng-template i18n #single>view</ng-template>
+            <ng-template i18n #multiple>views</ng-template>
+          </ng-container>
         </span>
       </span>
 
index 908611ddfe61790c484a2c94f412ed8e3893ba74..9f1b51b2d974f4f6b68db98bf25564de77bb0f28 100644 (file)
               </div>
             </div>
 
-            <my-subscribe-button #subscribeButton [videoChannel]="video.channel" size="small"></my-subscribe-button>
+            <my-subscribe-button #subscribeButton [videoChannels]="[video.channel]" size="small"></my-subscribe-button>
           </div>
         </div>