diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html index 700e6f51e8c..80ec572ca0e 100644 --- a/client/src/app/+video-channels/video-channels.component.html +++ b/client/src/app/+video-channels/video-channels.component.html @@ -69,6 +69,7 @@

{{ videoChannel.displayName }}

@{{ videoChannel.nameWithHost }} + {{ video.name }} By {{ video.byAccount }} + diff --git a/client/src/app/+video-watch/video-watch.component.ts b/client/src/app/+video-watch/video-watch.component.ts index 0317985e754..d849bfc2517 100644 --- a/client/src/app/+video-watch/video-watch.component.ts +++ b/client/src/app/+video-watch/video-watch.component.ts @@ -77,6 +77,7 @@ import { VideoDescriptionComponent } from './shared/metadata/video-description.c import { VideoTranscriptionComponent } from './shared/player-widgets/video-transcription.component' import { VideoWatchPlaylistComponent } from './shared/player-widgets/video-watch-playlist.component' import { RecommendedVideosComponent } from './shared/recommendations/recommended-videos.component' +import { GlobalIconComponent } from "@app/shared/shared-icons/global-icon.component" const debugLogger = debug('peertube:watch:VideoWatchComponent') @@ -124,8 +125,9 @@ type URLOptions = { PrivacyConcernsComponent, PlayerStylesComponent, VideoWatchPlaylistComponent, - VideoTranscriptionComponent - ] + VideoTranscriptionComponent, + GlobalIconComponent +] }) export class VideoWatchComponent implements OnInit, OnDestroy { private route = inject(ActivatedRoute) @@ -295,6 +297,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return genericChannelDisplayName.includes(this.video.channel.displayName) } + isChannelApproved () { + return this.video.channel.isApproved ?? false + } + displayOtherVideosAsRow () { // Use the same value as in the SASS file return this.screenService.getWindowInnerWidth() <= 1100 diff --git a/client/src/app/shared/shared-icons/global-icon.component.ts b/client/src/app/shared/shared-icons/global-icon.component.ts index 94ab5c3d62c..d42e2e791c4 100644 --- a/client/src/app/shared/shared-icons/global-icon.component.ts +++ b/client/src/app/shared/shared-icons/global-icon.component.ts @@ -7,6 +7,7 @@ const icons = { 'language': require('../../../assets/images/misc/language.svg'), 'video-lang': require('../../../assets/images/misc/video-lang.svg'), 'support': require('../../../assets/images/misc/support.svg'), + 'green-check': require('../../../assets/images/misc/green-check.svg'), 'peertube-x': require('../../../assets/images/misc/peertube-x.svg'), 'robot': require('../../../assets/images/misc/miscellaneous-services.svg'), // material ui 'playlist-add': require('../../../assets/images/misc/playlist-add.svg'), // material ui diff --git a/client/src/app/shared/shared-main/channel/video-channel.model.ts b/client/src/app/shared/shared-main/channel/video-channel.model.ts index 348ada8eb06..798d135c733 100644 --- a/client/src/app/shared/shared-main/channel/video-channel.model.ts +++ b/client/src/app/shared/shared-main/channel/video-channel.model.ts @@ -24,6 +24,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel { viewsPerDay?: ViewsPerDate[] totalViews?: number + isApproved?: boolean static GET_ACTOR_AVATAR_URL ( actor: { @@ -85,6 +86,10 @@ export class VideoChannel extends Actor implements ServerVideoChannel { this.totalViews = hash.totalViews } + if (hash.isApproved !== null && hash.isApproved !== undefined) { + this.isApproved = hash.isApproved + } + if (hash.ownerAccount) { this.ownerAccount = hash.ownerAccount this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) diff --git a/client/src/app/shared/standalone-channels/video-channel-edit.component.html b/client/src/app/shared/standalone-channels/video-channel-edit.component.html index 5582c43f9b1..6692d8ee39a 100644 --- a/client/src/app/shared/standalone-channels/video-channel-edit.component.html +++ b/client/src/app/shared/standalone-channels/video-channel-edit.component.html @@ -66,6 +66,15 @@

UPDATE CHANNEL

+
+ +
+
diff --git a/client/src/app/shared/standalone-channels/video-channel-edit.component.ts b/client/src/app/shared/standalone-channels/video-channel-edit.component.ts index 687d55556e1..dbbdb8503bc 100644 --- a/client/src/app/shared/standalone-channels/video-channel-edit.component.ts +++ b/client/src/app/shared/standalone-channels/video-channel-edit.component.ts @@ -23,7 +23,8 @@ import { MarkdownHintComponent } from '../shared-main/text/markdown-hint.compone type Form = { name: FormControl displayName: FormControl - description: FormControl + description: FormControl, + isApproved: FormControl, support: FormControl playerTheme: FormControl bulkVideosSupportUpdate: FormControl @@ -41,7 +42,8 @@ export type FormValidatedOutput = { name: string displayName: string description: string - support: string + support: string, + isApproved: boolean, bulkVideosSupportUpdate: boolean } } @@ -96,6 +98,7 @@ export class VideoChannelEditComponent implements OnInit { displayName: VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, description: VIDEO_CHANNEL_DESCRIPTION_VALIDATOR, support: VIDEO_CHANNEL_SUPPORT_VALIDATOR, + isApproved: null, bulkVideosSupportUpdate: null, playerTheme: null } @@ -104,6 +107,7 @@ export class VideoChannelEditComponent implements OnInit { displayName: this.channel().displayName, description: this.channel().description, support: this.channel().support, + isApproved: this.channel().isApproved, playerTheme: this.rawPlayerSettings().theme } @@ -168,6 +172,7 @@ export class VideoChannelEditComponent implements OnInit { displayName: body.displayName, description: body.description || null, support: body.support || null, + isApproved: body.isApproved || false, bulkVideosSupportUpdate: this.mode() === 'update' ? body.bulkVideosSupportUpdate || false diff --git a/client/src/app/shared/standalone-channels/video-channel-update.component.ts b/client/src/app/shared/standalone-channels/video-channel-update.component.ts index f61033d184b..bbfa10bca69 100644 --- a/client/src/app/shared/standalone-channels/video-channel-update.component.ts +++ b/client/src/app/shared/standalone-channels/video-channel-update.component.ts @@ -78,6 +78,7 @@ export class VideoChannelUpdateComponent implements OnInit, AfterViewInit, OnDes displayName: output.channel.displayName, description: output.channel.description, support: output.channel.support, + isApproved: output.channel.isApproved, bulkVideosSupportUpdate: output.channel.bulkVideosSupportUpdate } diff --git a/client/src/assets/images/misc/green-check.svg b/client/src/assets/images/misc/green-check.svg new file mode 100644 index 00000000000..9d19a4e3d69 --- /dev/null +++ b/client/src/assets/images/misc/green-check.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/models/src/videos/channel/video-channel-update.model.ts b/packages/models/src/videos/channel/video-channel-update.model.ts index 8dde9188bb2..472ec70bbdc 100644 --- a/packages/models/src/videos/channel/video-channel-update.model.ts +++ b/packages/models/src/videos/channel/video-channel-update.model.ts @@ -1,7 +1,8 @@ export interface VideoChannelUpdate { displayName?: string description?: string - support?: string + support?: string, + isApproved?: boolean bulkVideosSupportUpdate?: boolean } diff --git a/packages/models/src/videos/channel/video-channel.model.ts b/packages/models/src/videos/channel/video-channel.model.ts index bb10f6da58d..66e03f22101 100644 --- a/packages/models/src/videos/channel/video-channel.model.ts +++ b/packages/models/src/videos/channel/video-channel.model.ts @@ -19,6 +19,7 @@ export interface VideoChannel extends Actor { videosCount?: number viewsPerDay?: ViewsPerDate[] // chronologically ordered totalViews?: number + isApproved?: boolean banners: ActorImage[] } diff --git a/server/core/controllers/api/video-channel.ts b/server/core/controllers/api/video-channel.ts index 97918208765..23a6a96d0da 100644 --- a/server/core/controllers/api/video-channel.ts +++ b/server/core/controllers/api/video-channel.ts @@ -308,7 +308,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response) await sequelizeTypescript.transaction(async t => { if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description - + if (videoChannelInfoToUpdate.isApproved !== undefined) videoChannelInstance.isApproved = videoChannelInfoToUpdate.isApproved if (videoChannelInfoToUpdate.support !== undefined) { const oldSupportField = videoChannelInstance.support videoChannelInstance.support = videoChannelInfoToUpdate.support diff --git a/server/core/initializers/constants.ts b/server/core/initializers/constants.ts index faf76160e4b..6874fa13054 100644 --- a/server/core/initializers/constants.ts +++ b/server/core/initializers/constants.ts @@ -52,7 +52,7 @@ import { CONFIG, registerConfigChangedHandler } from './config.js' // --------------------------------------------------------------------------- -export const LAST_MIGRATION_VERSION = 930 +export const LAST_MIGRATION_VERSION = 935 // --------------------------------------------------------------------------- diff --git a/server/core/initializers/migrations/0935-channel-approval.ts b/server/core/initializers/migrations/0935-channel-approval.ts new file mode 100644 index 00000000000..497c1e49076 --- /dev/null +++ b/server/core/initializers/migrations/0935-channel-approval.ts @@ -0,0 +1,26 @@ +import * as Sequelize from 'sequelize' + +async function up (utils: { + transaction: Sequelize.Transaction + queryInterface: Sequelize.QueryInterface + sequelize: Sequelize.Sequelize +}): Promise { + const { transaction } = utils + + { + await utils.queryInterface.addColumn('videoChannel', 'isApproved', { + type: Sequelize.BOOLEAN, + defaultValue: false, + allowNull: false + }, { transaction }) + } +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + down, + up +} diff --git a/server/core/models/video/sql/video/shared/video-table-attributes.ts b/server/core/models/video/sql/video/shared/video-table-attributes.ts index 55fcea2a38f..80de0198513 100644 --- a/server/core/models/video/sql/video/shared/video-table-attributes.ts +++ b/server/core/models/video/sql/video/shared/video-table-attributes.ts @@ -20,6 +20,7 @@ export class VideoTableAttributes { if (this.mode === 'get') { attributeKeys = attributeKeys.concat([ + 'isApproved', 'support', 'createdAt', 'updatedAt' diff --git a/server/core/models/video/video-channel.ts b/server/core/models/video/video-channel.ts index e7d87e8ff7b..336b4dcba1c 100644 --- a/server/core/models/video/video-channel.ts +++ b/server/core/models/video/video-channel.ts @@ -371,6 +371,11 @@ export class VideoChannelModel extends SequelizeModel { @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max)) declare support: string + @AllowNull(false) + @Default(false) + @Column + declare isApproved: boolean + @CreatedAt declare createdAt: Date @@ -836,6 +841,7 @@ export class VideoChannelModel extends SequelizeModel { videosCount, viewsPerDay, totalViews, + isApproved: this.isApproved, avatars: actor.avatars } diff --git a/server/core/types/models/video/video-channel.ts b/server/core/types/models/video/video-channel.ts index 4a92e9b3825..300e50c34ea 100644 --- a/server/core/types/models/video/video-channel.ts +++ b/server/core/types/models/video/video-channel.ts @@ -154,7 +154,7 @@ export type MChannelAccountSummaryFormattable = export type MChannelFormattable = & FunctionProperties - & Pick + & Pick & Use<'Actor', MActorFormattable> & PickWithOpt diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index d4991e13882..f5812b5f7d4 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -11114,6 +11114,8 @@ components: description: Channel description support: description: How to support/fund the channel + isApproved: + description: Channel Approval VideoChannelCreate: allOf: