PeerTube/client/src/app/+accounts/accounts.component.ts
Chocobozzz 3a4992633e
Migrate server to ESM
Sorry for the very big commit that may lead to git log issues and merge
conflicts, but it's a major step forward:

 * Server can be faster at startup because imports() are async and we can
   easily lazy import big modules
 * Angular doesn't seem to support ES import (with .js extension), so we
   had to correctly organize peertube into a monorepo:
    * Use yarn workspace feature
    * Use typescript reference projects for dependencies
    * Shared projects have been moved into "packages", each one is now a
      node module (with a dedicated package.json/tsconfig.json)
    * server/tools have been moved into apps/ and is now a dedicated app
      bundled and published on NPM so users don't have to build peertube
      cli tools manually
    * server/tests have been moved into packages/ so we don't compile
      them every time we want to run the server
 * Use isolatedModule option:
   * Had to move from const enum to const
     (https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums)
   * Had to explictely specify "type" imports when used in decorators
 * Prefer tsx (that uses esbuild under the hood) instead of ts-node to
   load typescript files (tests with mocha or scripts):
     * To reduce test complexity as esbuild doesn't support decorator
       metadata, we only test server files that do not import server
       models
     * We still build tests files into js files for a faster CI
 * Remove unmaintained peertube CLI import script
 * Removed some barrels to speed up execution (less imports)
2023-08-11 15:02:33 +02:00

218 lines
6.2 KiB
TypeScript

import { Subscription } from 'rxjs'
import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService, MarkdownService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core'
import {
Account,
AccountService,
DropdownAction,
ListOverflowItem,
VideoChannel,
VideoChannelService,
VideoService
} from '@app/shared/shared-main'
import { AccountReportComponent, BlocklistService } from '@app/shared/shared-moderation'
import { HttpStatusCode, User, UserRight } from '@peertube/peertube-models'
@Component({
templateUrl: './accounts.component.html',
styleUrls: [ './accounts.component.scss' ]
})
export class AccountsComponent implements OnInit, OnDestroy {
@ViewChild('accountReportModal') accountReportModal: AccountReportComponent
account: Account
accountUser: User
videoChannels: VideoChannel[] = []
links: ListOverflowItem[] = []
hideMenu = false
accountVideosCount: number
accountDescriptionHTML = ''
accountDescriptionExpanded = false
prependModerationActions: DropdownAction<any>[] = []
private routeSub: Subscription
constructor (
private route: ActivatedRoute,
private router: Router,
private userService: UserService,
private accountService: AccountService,
private videoChannelService: VideoChannelService,
private notifier: Notifier,
private restExtractor: RestExtractor,
private redirectService: RedirectService,
private authService: AuthService,
private videoService: VideoService,
private markdown: MarkdownService,
private blocklist: BlocklistService,
private screenService: ScreenService
) {
}
ngOnInit () {
this.routeSub = this.route.params
.pipe(
map(params => params['accountId']),
distinctUntilChanged(),
switchMap(accountId => this.accountService.getAccount(accountId)),
tap(account => this.onAccount(account)),
switchMap(account => this.videoChannelService.listAccountVideoChannels({ account })),
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [
HttpStatusCode.BAD_REQUEST_400,
HttpStatusCode.NOT_FOUND_404
]))
)
.subscribe({
next: videoChannels => {
this.videoChannels = videoChannels.data
},
error: err => this.notifier.error(err.message)
})
this.links = [
{ label: $localize`CHANNELS`, routerLink: 'video-channels' },
{ label: $localize`VIDEOS`, routerLink: 'videos' }
]
}
ngOnDestroy () {
if (this.routeSub) this.routeSub.unsubscribe()
}
naiveAggregatedSubscribers () {
return this.videoChannels.reduce(
(acc, val) => acc + val.followersCount,
this.account.followersCount // accumulator starts with the base number of subscribers the account has
)
}
isUserLoggedIn () {
return this.authService.isLoggedIn()
}
isInSmallView () {
return this.screenService.isInSmallView()
}
isManageable () {
if (!this.isUserLoggedIn()) return false
return this.account?.userId === this.authService.getUser().id
}
onUserChanged () {
this.loadUserIfNeeded(this.account)
}
onUserDeleted () {
this.redirectService.redirectToHomepage()
}
searchChanged (search: string) {
const queryParams = { search }
this.router.navigate([ './videos' ], { queryParams, relativeTo: this.route, queryParamsHandling: 'merge' })
}
onSearchInputDisplayChanged (displayed: boolean) {
this.hideMenu = this.isInSmallView() && displayed
}
hasVideoChannels () {
return this.videoChannels.length !== 0
}
hasShowMoreDescription () {
return !this.accountDescriptionExpanded && this.accountDescriptionHTML.length > 100
}
isOnChannelPage () {
return this.route.children[0].snapshot.url[0].path === 'video-channels'
}
private async onAccount (account: Account) {
this.accountDescriptionHTML = await this.markdown.textMarkdownToHTML({
markdown: account.description,
withEmoji: true,
withHtml: true
})
// After the markdown renderer to avoid layout changes
this.account = account
this.updateModerationActions()
this.loadUserIfNeeded(account)
this.loadAccountVideosCount()
this.loadAccountBlockStatus()
}
private showReportModal () {
this.accountReportModal.show(this.account)
}
private loadUserIfNeeded (account: Account) {
if (!account.userId || !this.authService.isLoggedIn()) return
const user = this.authService.getUser()
if (user.hasRight(UserRight.MANAGE_USERS)) {
this.userService.getUser(account.userId)
.subscribe({
next: accountUser => {
this.accountUser = accountUser
},
error: err => this.notifier.error(err.message)
})
}
}
private updateModerationActions () {
this.prependModerationActions = []
if (!this.authService.isLoggedIn()) return
this.authService.userInformationLoaded.subscribe(
() => {
if (this.isManageable()) return
// It's not our account, we can report it
this.prependModerationActions = [
{
label: $localize`Report`,
isHeader: true
},
{
label: $localize`Report this account`,
handler: () => this.showReportModal()
}
]
}
)
}
private loadAccountVideosCount () {
this.videoService.getAccountVideos({
account: this.account,
videoPagination: {
currentPage: 1,
itemsPerPage: 0
},
sort: '-publishedAt'
}).subscribe(res => {
this.accountVideosCount = res.total
})
}
private loadAccountBlockStatus () {
this.blocklist.getStatus({ accounts: [ this.account.nameWithHostForced ], hosts: [ this.account.host ] })
.subscribe(status => this.account.updateBlockStatus(status))
}
}