Merge branch 'release/2.1.0' into develop
[oweals/peertube.git] / client / src / app / shared / user-subscription / subscribe-button.component.ts
1 import { Component, Input, OnInit, OnChanges } from '@angular/core'
2 import { Router } from '@angular/router'
3 import { AuthService, Notifier } from '@app/core'
4 import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service'
5 import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
6 import { I18n } from '@ngx-translate/i18n-polyfill'
7 import { VideoService } from '@app/shared/video/video.service'
8 import { FeedFormat } from '../../../../../shared/models/feeds'
9 import { Account } from '@app/shared/account/account.model'
10 import { concat, forkJoin, merge } from 'rxjs'
11
12 @Component({
13   selector: 'my-subscribe-button',
14   templateUrl: './subscribe-button.component.html',
15   styleUrls: [ './subscribe-button.component.scss' ]
16 })
17 export class SubscribeButtonComponent implements OnInit, OnChanges {
18   /**
19    * SubscribeButtonComponent can be used with a single VideoChannel passed as [VideoChannel],
20    * or with an account and a full list of that account's videoChannels. The latter is intended
21    * to allow mass un/subscription from an account's page, while keeping the channel-centric
22    * subscription model.
23    */
24   @Input() account: Account
25   @Input() videoChannels: VideoChannel[]
26   @Input() displayFollowers = false
27   @Input() size: 'small' | 'normal' = 'normal'
28
29   subscribed = new Map<string, boolean>()
30
31   constructor (
32     private authService: AuthService,
33     private router: Router,
34     private notifier: Notifier,
35     private userSubscriptionService: UserSubscriptionService,
36     private i18n: I18n,
37     private videoService: VideoService
38   ) { }
39
40   get handle () {
41     return this.account
42       ? this.account.nameWithHost
43       : this.videoChannel.name + '@' + this.videoChannel.host
44   }
45
46   get channelHandle () {
47     return this.getChannelHandler(this.videoChannel)
48   }
49
50   get uri () {
51     return this.account
52       ? this.account.url
53       : this.videoChannels[0].url
54   }
55
56   get rssUri () {
57     const rssFeed = this.account
58       ? this.videoService
59           .getAccountFeedUrls(this.account.id)
60           .find(i => i.format === FeedFormat.RSS)
61       : this.videoService
62           .getVideoChannelFeedUrls(this.videoChannels[0].id)
63           .find(i => i.format === FeedFormat.RSS)
64
65     return rssFeed.url
66   }
67
68   get videoChannel () {
69     return this.videoChannels[0]
70   }
71
72   get isAllChannelsSubscribed () {
73     return this.subscribeStatus(true).length === this.videoChannels.length
74   }
75
76   get isAtLeastOneChannelSubscribed () {
77     return this.subscribeStatus(true).length > 0
78   }
79
80   get isBigButton () {
81     return this.isUserLoggedIn() && this.videoChannels.length > 1 && this.isAtLeastOneChannelSubscribed
82   }
83
84   ngOnInit () {
85     this.loadSubscribedStatus()
86   }
87
88   ngOnChanges () {
89     this.ngOnInit()
90   }
91
92   subscribe () {
93     if (this.isUserLoggedIn()) {
94       return this.localSubscribe()
95     }
96
97     return this.gotoLogin()
98   }
99
100   localSubscribe () {
101     const subscribedStatus = this.subscribeStatus(false)
102
103     const observableBatch = this.videoChannels
104       .map(videoChannel => this.getChannelHandler(videoChannel))
105       .filter(handle => subscribedStatus.includes(handle))
106       .map(handle => this.userSubscriptionService.addSubscription(handle))
107
108     forkJoin(observableBatch)
109       .subscribe(
110         () => {
111           this.notifier.success(
112             this.account
113               ? this.i18n(
114                   'Subscribed to all current channels of {{nameWithHost}}. You will be notified of all their new videos.',
115                   { nameWithHost: this.account.displayName }
116                 )
117               : this.i18n(
118                   'Subscribed to {{nameWithHost}}. You will be notified of all their new videos.',
119                   { nameWithHost: this.videoChannels[0].displayName }
120                 )
121             ,
122             this.i18n('Subscribed')
123           )
124         },
125
126           err => this.notifier.error(err.message)
127       )
128   }
129
130   unsubscribe () {
131     if (this.isUserLoggedIn()) {
132       this.localUnsubscribe()
133     }
134   }
135
136   localUnsubscribe () {
137     const subscribeStatus = this.subscribeStatus(true)
138
139     const observableBatch = this.videoChannels
140                                 .map(videoChannel => this.getChannelHandler(videoChannel))
141                                 .filter(handle => subscribeStatus.includes(handle))
142                                 .map(handle => this.userSubscriptionService.deleteSubscription(handle))
143
144     concat(...observableBatch)
145       .subscribe({
146         complete: () => {
147           this.notifier.success(
148             this.account
149               ? this.i18n('Unsubscribed from all channels of {{nameWithHost}}', { nameWithHost: this.account.nameWithHost })
150               : this.i18n('Unsubscribed from {{nameWithHost}}', { nameWithHost: this.videoChannels[ 0 ].nameWithHost })
151             ,
152             this.i18n('Unsubscribed')
153           )
154         },
155
156         error: err => this.notifier.error(err.message)
157       })
158   }
159
160   isUserLoggedIn () {
161     return this.authService.isLoggedIn()
162   }
163
164   gotoLogin () {
165     this.router.navigate([ '/login' ])
166   }
167
168   subscribeStatus (subscribed: boolean) {
169     const accumulator: string[] = []
170     for (const [key, value] of this.subscribed.entries()) {
171       if (value === subscribed) accumulator.push(key)
172     }
173
174     return accumulator
175   }
176
177   private getChannelHandler (videoChannel: VideoChannel) {
178     return videoChannel.name + '@' + videoChannel.host
179   }
180
181   private loadSubscribedStatus () {
182     if (!this.isUserLoggedIn()) return
183
184     for (const videoChannel of this.videoChannels) {
185       const handle = this.getChannelHandler(videoChannel)
186       this.subscribed.set(handle, false)
187
188       merge(
189         this.userSubscriptionService.listenToSubscriptionCacheChange(handle),
190         this.userSubscriptionService.doesSubscriptionExist(handle)
191       ).subscribe(
192         res => this.subscribed.set(handle, res),
193
194         err => this.notifier.error(err.message)
195       )
196     }
197   }
198 }