Better live notification label
This commit is contained in:
parent
d5c4cc2b44
commit
82246a0c8d
|
@ -66,6 +66,7 @@
|
|||
</div>
|
||||
|
||||
<my-user-notifications
|
||||
#userNotifications
|
||||
[ignoreLoadingBar]="true" [infiniteScroll]="false" [itemsPerPage]="10"
|
||||
[markAllAsReadSubject]="markAllAsReadSubject" (notificationsLoaded)="onNotificationLoaded()"
|
||||
></my-user-notifications>
|
||||
|
|
|
@ -35,6 +35,7 @@ export class NotificationDropdownComponent implements OnInit, OnDestroy {
|
|||
opened = false
|
||||
|
||||
markAllAsReadSubject = new Subject<boolean>()
|
||||
userNotificationReload = new Subject<boolean>()
|
||||
|
||||
private notificationSub: Subscription
|
||||
private routeSub: Subscription
|
||||
|
@ -78,6 +79,8 @@ export class NotificationDropdownComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onDropdownShown () {
|
||||
if (this.loaded) this.userNotificationReload.next(true)
|
||||
|
||||
this.opened = true
|
||||
}
|
||||
|
||||
|
|
|
@ -63,11 +63,15 @@
|
|||
<ng-container *ngSwitchCase="15"> <!-- UserNotificationType.ABUSE_STATE_CHANGE -->
|
||||
<my-global-icon iconName="flag" aria-hidden="true"></my-global-icon>
|
||||
|
||||
<div class="message" i18n>
|
||||
<a (click)="markAsRead(notification)" [routerLink]="notification.abuseUrl" [queryParams]="notification.abuseQueryParams">Your abuse {{ notification.abuse.id }}</a> has been
|
||||
<ng-container *ngIf="isAccepted(notification)">accepted</ng-container>
|
||||
<ng-container *ngIf="!isAccepted(notification)">rejected</ng-container>
|
||||
</div>
|
||||
@if (isAbuseAccepted(notification)) {
|
||||
<div class="message" i18n>
|
||||
<a (click)="markAsRead(notification)" [routerLink]="notification.abuseUrl" [queryParams]="notification.abuseQueryParams">Your abuse {{ notification.abuse.id }}</a> has been accepted
|
||||
</div>
|
||||
} @else {
|
||||
<div class="message" i18n>
|
||||
<a (click)="markAsRead(notification)" [routerLink]="notification.abuseUrl" [queryParams]="notification.abuseQueryParams">Your abuse {{ notification.abuse.id }}</a> has been accepted
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="16"> <!-- UserNotificationType.ABUSE_NEW_MESSAGE -->
|
||||
|
@ -227,9 +231,15 @@
|
|||
<img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.video.channel.avatarUrl" />
|
||||
</a>
|
||||
|
||||
<div class="message" i18n>
|
||||
{{ notification.video.channel.displayName }} is live streaming in <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
|
||||
</div>
|
||||
@if (isVideoPublished(notification)) {
|
||||
<div class="message" i18n>
|
||||
{{ notification.video.channel.displayName }} is live streaming: <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="message" i18n>
|
||||
{{ notification.video.channel.displayName }} went live: <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Subject } from 'rxjs'
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
||||
import { ComponentPagination, hasMoreItems, Notifier } from '@app/core'
|
||||
import { AbuseState } from '@peertube/peertube-models'
|
||||
import { AbuseState, VideoState } from '@peertube/peertube-models'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { GlobalIconComponent } from '../shared-icons/global-icon.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
|
@ -22,6 +22,7 @@ export class UserNotificationsComponent implements OnInit {
|
|||
@Input() infiniteScroll = true
|
||||
@Input() itemsPerPage = 20
|
||||
@Input() markAllAsReadSubject: Subject<boolean>
|
||||
@Input() userNotificationReload: Subject<boolean>
|
||||
|
||||
@Output() notificationsLoaded = new EventEmitter()
|
||||
|
||||
|
@ -49,9 +50,15 @@ export class UserNotificationsComponent implements OnInit {
|
|||
if (this.markAllAsReadSubject) {
|
||||
this.markAllAsReadSubject.subscribe(() => this.markAllAsRead())
|
||||
}
|
||||
|
||||
if (this.userNotificationReload) {
|
||||
this.userNotificationReload.subscribe(() => this.loadNotifications(true))
|
||||
}
|
||||
}
|
||||
|
||||
loadNotifications (reset?: boolean) {
|
||||
if (reset) this.componentPagination.currentPage = 1
|
||||
|
||||
const options = {
|
||||
pagination: this.componentPagination,
|
||||
ignoreLoadingBar: this.ignoreLoadingBar,
|
||||
|
@ -123,7 +130,11 @@ export class UserNotificationsComponent implements OnInit {
|
|||
this.loadNotifications(true)
|
||||
}
|
||||
|
||||
isAccepted (notification: UserNotification) {
|
||||
isAbuseAccepted (notification: UserNotification) {
|
||||
return notification.abuse.state === AbuseState.ACCEPTED
|
||||
}
|
||||
|
||||
isVideoPublished (notification: UserNotification) {
|
||||
return notification.video.state.id === VideoState.PUBLISHED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { FollowState } from '../actors/index.js'
|
|||
import { AbuseStateType } from '../moderation/index.js'
|
||||
import { PluginType_Type } from '../plugins/index.js'
|
||||
import { VideoConstant } from '../videos/video-constant.model.js'
|
||||
import { VideoStateType } from '../videos/video-state.enum.js'
|
||||
|
||||
export const UserNotificationType = {
|
||||
NEW_VIDEO_FROM_SUBSCRIPTION: 1,
|
||||
|
@ -49,6 +50,10 @@ export interface VideoInfo {
|
|||
uuid: string
|
||||
shortUUID: string
|
||||
name: string
|
||||
state: {
|
||||
id: VideoStateType
|
||||
label: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface AvatarInfo {
|
||||
|
|
|
@ -966,6 +966,7 @@ function checkVideo (video: any, videoName?: string, shortUUID?: string) {
|
|||
expect(video.shortUUID).to.equal(shortUUID)
|
||||
}
|
||||
|
||||
expect(video.state).to.exist
|
||||
expect(video.id).to.be.a('number')
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ export class NewVideoOrLiveForSubscribers extends AbstractNotification <MVideoAc
|
|||
return {
|
||||
to,
|
||||
subject: channelName + ' is live streaming',
|
||||
text: `Your subscription ${channelName} is live streaming in "${this.payload.name}".`,
|
||||
text: `Your subscription ${channelName} is live streaming: "${this.payload.name}".`,
|
||||
locals: {
|
||||
title: 'New content ',
|
||||
action: {
|
||||
|
|
|
@ -80,6 +80,7 @@ export class UserNotificationListQueryBuilder extends AbstractRunQuery {
|
|||
"Video"."id" AS "Video.id",
|
||||
"Video"."uuid" AS "Video.uuid",
|
||||
"Video"."name" AS "Video.name",
|
||||
"Video"."state" AS "Video.state",
|
||||
"Video->VideoChannel"."id" AS "Video.VideoChannel.id",
|
||||
"Video->VideoChannel"."name" AS "Video.VideoChannel.name",
|
||||
"Video->VideoChannel->Actor"."id" AS "Video.VideoChannel.Actor.id",
|
||||
|
@ -106,18 +107,21 @@ export class UserNotificationListQueryBuilder extends AbstractRunQuery {
|
|||
"VideoComment->Video"."id" AS "VideoComment.Video.id",
|
||||
"VideoComment->Video"."uuid" AS "VideoComment.Video.uuid",
|
||||
"VideoComment->Video"."name" AS "VideoComment.Video.name",
|
||||
"VideoComment->Video"."state" AS "VideoComment.Video.state",
|
||||
"Abuse"."id" AS "Abuse.id",
|
||||
"Abuse"."state" AS "Abuse.state",
|
||||
"Abuse->VideoAbuse"."id" AS "Abuse.VideoAbuse.id",
|
||||
"Abuse->VideoAbuse->Video"."id" AS "Abuse.VideoAbuse.Video.id",
|
||||
"Abuse->VideoAbuse->Video"."uuid" AS "Abuse.VideoAbuse.Video.uuid",
|
||||
"Abuse->VideoAbuse->Video"."name" AS "Abuse.VideoAbuse.Video.name",
|
||||
"Abuse->VideoAbuse->Video"."state" AS "Abuse.VideoAbuse.Video.state",
|
||||
"Abuse->VideoCommentAbuse"."id" AS "Abuse.VideoCommentAbuse.id",
|
||||
"Abuse->VideoCommentAbuse->VideoComment"."id" AS "Abuse.VideoCommentAbuse.VideoComment.id",
|
||||
"Abuse->VideoCommentAbuse->VideoComment"."originCommentId" AS "Abuse.VideoCommentAbuse.VideoComment.originCommentId",
|
||||
"Abuse->VideoCommentAbuse->VideoComment->Video"."id" AS "Abuse.VideoCommentAbuse.VideoComment.Video.id",
|
||||
"Abuse->VideoCommentAbuse->VideoComment->Video"."name" AS "Abuse.VideoCommentAbuse.VideoComment.Video.name",
|
||||
"Abuse->VideoCommentAbuse->VideoComment->Video"."uuid" AS "Abuse.VideoCommentAbuse.VideoComment.Video.uuid",
|
||||
"Abuse->VideoCommentAbuse->VideoComment->Video"."state" AS "Abuse.VideoCommentAbuse.VideoComment.Video.state",
|
||||
"Abuse->FlaggedAccount"."id" AS "Abuse.FlaggedAccount.id",
|
||||
"Abuse->FlaggedAccount"."name" AS "Abuse.FlaggedAccount.name",
|
||||
"Abuse->FlaggedAccount"."description" AS "Abuse.FlaggedAccount.description",
|
||||
|
@ -138,6 +142,7 @@ export class UserNotificationListQueryBuilder extends AbstractRunQuery {
|
|||
"VideoBlacklist->Video"."id" AS "VideoBlacklist.Video.id",
|
||||
"VideoBlacklist->Video"."uuid" AS "VideoBlacklist.Video.uuid",
|
||||
"VideoBlacklist->Video"."name" AS "VideoBlacklist.Video.name",
|
||||
"VideoBlacklist->Video"."state" AS "VideoBlacklist.Video.name",
|
||||
"VideoImport"."id" AS "VideoImport.id",
|
||||
"VideoImport"."magnetUri" AS "VideoImport.magnetUri",
|
||||
"VideoImport"."targetUrl" AS "VideoImport.targetUrl",
|
||||
|
@ -145,6 +150,7 @@ export class UserNotificationListQueryBuilder extends AbstractRunQuery {
|
|||
"VideoImport->Video"."id" AS "VideoImport.Video.id",
|
||||
"VideoImport->Video"."uuid" AS "VideoImport.Video.uuid",
|
||||
"VideoImport->Video"."name" AS "VideoImport.Video.name",
|
||||
"VideoImport->Video"."state" AS "VideoImport.Video.name",
|
||||
"Plugin"."id" AS "Plugin.id",
|
||||
"Plugin"."name" AS "Plugin.name",
|
||||
"Plugin"."type" AS "Plugin.type",
|
||||
|
@ -188,7 +194,8 @@ export class UserNotificationListQueryBuilder extends AbstractRunQuery {
|
|||
"VideoCaption"."language" AS "VideoCaption.language",
|
||||
"VideoCaption->Video"."id" AS "VideoCaption.Video.id",
|
||||
"VideoCaption->Video"."uuid" AS "VideoCaption.Video.uuid",
|
||||
"VideoCaption->Video"."name" AS "VideoCaption.Video.name"`
|
||||
"VideoCaption->Video"."name" AS "VideoCaption.Video.name",
|
||||
"VideoCaption->Video"."state" AS "VideoCaption.Video.state"`
|
||||
}
|
||||
|
||||
private getJoins () {
|
||||
|
|
|
@ -12,6 +12,7 @@ import { ActorFollowModel } from '../actor/actor-follow.js'
|
|||
import { ApplicationModel } from '../application/application.js'
|
||||
import { PluginModel } from '../server/plugin.js'
|
||||
import { SequelizeModel, throwIfNotValid } from '../shared/index.js'
|
||||
import { getStateLabel } from '../video/formatter/video-api-format.js'
|
||||
import { VideoBlacklistModel } from '../video/video-blacklist.js'
|
||||
import { VideoCaptionModel } from '../video/video-caption.js'
|
||||
import { VideoCommentModel } from '../video/video-comment.js'
|
||||
|
@ -490,7 +491,11 @@ export class UserNotificationModel extends SequelizeModel<UserNotificationModel>
|
|||
id: video.id,
|
||||
uuid: video.uuid,
|
||||
shortUUID: uuidToShort(video.uuid),
|
||||
name: video.name
|
||||
name: video.name,
|
||||
state: {
|
||||
id: video.state,
|
||||
label: getStateLabel(video.state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,12 +505,7 @@ export class UserNotificationModel extends SequelizeModel<UserNotificationModel>
|
|||
threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(),
|
||||
|
||||
video: abuse.VideoCommentAbuse.VideoComment.Video
|
||||
? {
|
||||
id: abuse.VideoCommentAbuse.VideoComment.Video.id,
|
||||
name: abuse.VideoCommentAbuse.VideoComment.Video.name,
|
||||
shortUUID: uuidToShort(abuse.VideoCommentAbuse.VideoComment.Video.uuid),
|
||||
uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
|
||||
}
|
||||
? this.formatVideo(abuse.VideoCommentAbuse.VideoComment.Video)
|
||||
: undefined
|
||||
}
|
||||
: undefined
|
||||
|
|
|
@ -30,14 +30,16 @@ import { SequelizeModel, buildWhereIdOrUUID, throwIfNotValid } from '../shared/i
|
|||
import { VideoModel } from './video.js'
|
||||
|
||||
export enum ScopeNames {
|
||||
WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE'
|
||||
CAPTION_WITH_VIDEO = 'CAPTION_WITH_VIDEO'
|
||||
}
|
||||
|
||||
const videoAttributes = [ 'id', 'name', 'remote', 'uuid', 'url', 'state' ]
|
||||
|
||||
@Scopes(() => ({
|
||||
[ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: {
|
||||
[ScopeNames.CAPTION_WITH_VIDEO]: {
|
||||
include: [
|
||||
{
|
||||
attributes: [ 'id', 'uuid', 'remote' ],
|
||||
attributes: videoAttributes,
|
||||
model: VideoModel.unscoped(),
|
||||
required: true
|
||||
}
|
||||
|
@ -130,17 +132,13 @@ export class VideoCaptionModel extends SequelizeModel<VideoCaptionModel> {
|
|||
static loadByVideoIdAndLanguage (videoId: string | number, language: string, transaction?: Transaction): Promise<MVideoCaptionVideo> {
|
||||
const videoInclude = {
|
||||
model: VideoModel.unscoped(),
|
||||
attributes: [ 'id', 'name', 'remote', 'uuid', 'url' ],
|
||||
attributes: videoAttributes,
|
||||
where: buildWhereIdOrUUID(videoId)
|
||||
}
|
||||
|
||||
const query = {
|
||||
where: {
|
||||
language
|
||||
},
|
||||
include: [
|
||||
videoInclude
|
||||
],
|
||||
where: { language },
|
||||
include: [ videoInclude ],
|
||||
transaction
|
||||
}
|
||||
|
||||
|
@ -155,7 +153,7 @@ export class VideoCaptionModel extends SequelizeModel<VideoCaptionModel> {
|
|||
include: [
|
||||
{
|
||||
model: VideoModel.unscoped(),
|
||||
attributes: [ 'id', 'remote', 'uuid' ]
|
||||
attributes: videoAttributes
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -186,7 +184,7 @@ export class VideoCaptionModel extends SequelizeModel<VideoCaptionModel> {
|
|||
transaction
|
||||
}
|
||||
|
||||
return VideoCaptionModel.scope(ScopeNames.WITH_VIDEO_UUID_AND_REMOTE).findAll(query)
|
||||
return VideoCaptionModel.scope(ScopeNames.CAPTION_WITH_VIDEO).findAll(query)
|
||||
}
|
||||
|
||||
static async listCaptionsOfMultipleVideos (videoIds: number[], transaction?: Transaction) {
|
||||
|
@ -200,7 +198,7 @@ export class VideoCaptionModel extends SequelizeModel<VideoCaptionModel> {
|
|||
transaction
|
||||
}
|
||||
|
||||
const captions = await VideoCaptionModel.scope(ScopeNames.WITH_VIDEO_UUID_AND_REMOTE).findAll<MVideoCaptionVideo>(query)
|
||||
const captions = await VideoCaptionModel.scope(ScopeNames.CAPTION_WITH_VIDEO).findAll<MVideoCaptionVideo>(query)
|
||||
const result: { [ id: number ]: MVideoCaptionVideo[] } = {}
|
||||
|
||||
for (const id of videoIds) {
|
||||
|
|
|
@ -25,7 +25,7 @@ type Use<K extends keyof UserNotificationModel, M> = PickWith<UserNotificationMo
|
|||
export module UserNotificationIncludes {
|
||||
export type ActorImageInclude = Pick<ActorImageModel, 'createdAt' | 'filename' | 'getStaticPath' | 'width' | 'updatedAt'>
|
||||
|
||||
export type VideoInclude = Pick<VideoModel, 'id' | 'uuid' | 'name'>
|
||||
export type VideoInclude = Pick<VideoModel, 'id' | 'uuid' | 'name' | 'state'>
|
||||
export type VideoIncludeChannel =
|
||||
VideoInclude &
|
||||
PickWith<VideoModel, 'VideoChannel', VideoChannelIncludeActor>
|
||||
|
@ -58,7 +58,7 @@ export module UserNotificationIncludes {
|
|||
Pick<VideoCommentAbuseModel, 'id'> &
|
||||
PickWith<VideoCommentAbuseModel, 'VideoComment',
|
||||
Pick<VideoCommentModel, 'id' | 'originCommentId' | 'getThreadId'> &
|
||||
PickWith<VideoCommentModel, 'Video', Pick<VideoModel, 'id' | 'name' | 'uuid'>>>
|
||||
PickWith<VideoCommentModel, 'Video', Pick<VideoModel, 'id' | 'name' | 'uuid' | 'state'>>>
|
||||
|
||||
export type AbuseInclude =
|
||||
Pick<AbuseModel, 'id' | 'state'> &
|
||||
|
|
|
@ -17,7 +17,7 @@ export type MVideoCaptionLanguageUrl =
|
|||
|
||||
export type MVideoCaptionVideo =
|
||||
MVideoCaption &
|
||||
Use<'Video', Pick<MVideo, 'id' | 'name' | 'remote' | 'uuid' | 'url' | 'getWatchStaticPath'>>
|
||||
Use<'Video', Pick<MVideo, 'id' | 'name' | 'remote' | 'uuid' | 'url' | 'state' | 'getWatchStaticPath'>>
|
||||
|
||||
// ############################################################################
|
||||
|
||||
|
|
|
@ -8252,6 +8252,8 @@ components:
|
|||
$ref: '#/components/schemas/Video/properties/uuid'
|
||||
name:
|
||||
$ref: '#/components/schemas/Video/properties/name'
|
||||
state:
|
||||
$ref: '#/components/schemas/Video/properties/state'
|
||||
Video:
|
||||
properties:
|
||||
id:
|
||||
|
|
Loading…
Reference in New Issue