From 68a6af9b2c2e4e5a31b2d6f5c5add6c238e5113e Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Thu, 11 Sep 2025 20:51:57 +0200 Subject: [PATCH] feat(parser): Add ListView, ListItemView and SubscribeButtonView (#1025) --- src/parser/classes/DialogView.ts | 5 +- src/parser/classes/ListItemView.ts | 32 +++++++++++ src/parser/classes/ListView.ts | 18 ++++++ src/parser/classes/SubscribeButtonView.ts | 70 +++++++++++++++++++++++ src/parser/nodes.ts | 3 + 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/parser/classes/ListItemView.ts create mode 100644 src/parser/classes/ListView.ts create mode 100644 src/parser/classes/SubscribeButtonView.ts diff --git a/src/parser/classes/DialogView.ts b/src/parser/classes/DialogView.ts index 0a1dd481..51b70445 100644 --- a/src/parser/classes/DialogView.ts +++ b/src/parser/classes/DialogView.ts @@ -3,6 +3,7 @@ import { Parser, type RawNode } from '../index.js'; import DialogHeaderView from './DialogHeaderView.js'; import FormFooterView from './FormFooterView.js'; import CreatePlaylistDialogFormView from './CreatePlaylistDialogFormView.js'; +import ListView from './ListView.js'; import PanelFooterView from './PanelFooterView.js'; export default class DialogView extends YTNode { @@ -10,12 +11,12 @@ export default class DialogView extends YTNode { public header: DialogHeaderView | null; public footer: FormFooterView | PanelFooterView | null; - public custom_content: CreatePlaylistDialogFormView | null; + public custom_content: CreatePlaylistDialogFormView | ListView | null; constructor (data: RawNode) { super(); this.header = Parser.parseItem(data.header, DialogHeaderView); this.footer = Parser.parseItem(data.footer, [ FormFooterView, PanelFooterView ]); - this.custom_content = Parser.parseItem(data.customContent, CreatePlaylistDialogFormView); + this.custom_content = Parser.parseItem(data.customContent, [ CreatePlaylistDialogFormView, ListView ]); } } \ No newline at end of file diff --git a/src/parser/classes/ListItemView.ts b/src/parser/classes/ListItemView.ts new file mode 100644 index 00000000..48aa98ac --- /dev/null +++ b/src/parser/classes/ListItemView.ts @@ -0,0 +1,32 @@ +import type { ObservedArray } from '../helpers.js'; +import type { RawNode } from '../types/RawResponse.js'; +import { YTNode } from '../helpers.js'; +import { Parser } from '../index.js'; + +import AvatarView from './AvatarView.js'; +import RendererContext from './misc/RendererContext.js'; +import SubscribeButtonView from './SubscribeButtonView.js'; +import Text from './misc/Text.js'; + +export default class ListItemView extends YTNode { + static type = 'ListItemView'; + + public title: Text; + public subtitle: Text; + public leading_accessory: AvatarView | null; + public renderer_context: RendererContext; + public trailing_buttons?: ObservedArray; + + constructor(data: RawNode) { + super(); + + this.title = Text.fromAttributed(data.title); + this.subtitle = Text.fromAttributed(data.subtitle); + this.leading_accessory = Parser.parseItem(data.leadingAccessory, AvatarView); + this.renderer_context = new RendererContext(data.rendererContext); + + if ('trailingButtons' in data) { + this.trailing_buttons = Parser.parseArray(data.trailingButtons.buttons, SubscribeButtonView); + } + } +} \ No newline at end of file diff --git a/src/parser/classes/ListView.ts b/src/parser/classes/ListView.ts new file mode 100644 index 00000000..c9231130 --- /dev/null +++ b/src/parser/classes/ListView.ts @@ -0,0 +1,18 @@ +import type { ObservedArray } from '../helpers.js'; +import type { RawNode } from '../types/RawResponse.js'; +import { YTNode } from '../helpers.js'; +import { Parser } from '../index.js'; + +import ListItemView from './ListItemView.js'; + +export default class ListView extends YTNode { + static type = 'ListView'; + + public items: ObservedArray; + + constructor(data: RawNode) { + super(); + + this.items = Parser.parseArray(data.listItems, ListItemView); + } +} \ No newline at end of file diff --git a/src/parser/classes/SubscribeButtonView.ts b/src/parser/classes/SubscribeButtonView.ts new file mode 100644 index 00000000..98e0740f --- /dev/null +++ b/src/parser/classes/SubscribeButtonView.ts @@ -0,0 +1,70 @@ +import type { RawNode } from '../types/RawResponse.js'; +import { YTNode } from '../helpers.js'; +import NavigationEndpoint from './NavigationEndpoint.js'; + +interface ButtonContent { + button_text: string; + accessibility_text: string; + image_name: string; + subscribe_state_subscribed: boolean; + endpoint: NavigationEndpoint; +} + +export default class SubscribeButtonView extends YTNode { + static type = 'SubscribeButtonView'; + + public subscribe_button_content: ButtonContent; + public unsubscribe_button_content: ButtonContent; + public disable_notification_bell: boolean; + public button_style: { + unsubscribed_state_style: string; + subscribed_state_style: string; + }; + public is_signed_out: boolean; + public background_style: string; + public disable_subscribe_button: boolean; + public on_show_subscription_options: NavigationEndpoint; + public channel_id: string; + public enable_subscribe_button_post_click_animation: boolean; + public bell_accessiblity_data: { + off_label: string; + all_label: string; + occasional_label: string; + disabled_label: string; + }; + + constructor(data: RawNode) { + super(); + + this.subscribe_button_content = this.#parseButtonContent(data.subscribeButtonContent); + this.unsubscribe_button_content = this.#parseButtonContent(data.unsubscribeButtonContent); + + this.disable_notification_bell = data.disableNotificationBell; + this.button_style = { + unsubscribed_state_style: data.buttonStyle.unsubscribedStateStyle, + subscribed_state_style: data.buttonStyle.subscribedStateStyle + }; + this.is_signed_out = data.isSignedOut; + this.background_style = data.backgroundStyle; + this.disable_subscribe_button = data.disableSubscribeButton; + this.on_show_subscription_options = new NavigationEndpoint(data.onShowSubscriptionOptions); + this.channel_id = data.channelId; + this.enable_subscribe_button_post_click_animation = data.enableSubscribeButtonPostClickAnimation; + this.bell_accessiblity_data = { + off_label: data.bellAccessibilityData.offLabel, + all_label: data.bellAccessibilityData.allLabel, + occasional_label: data.bellAccessibilityData.occasionalLabel, + disabled_label: data.bellAccessibilityData.disabledLabel + }; + } + + #parseButtonContent(data: RawNode): ButtonContent { + return { + button_text: data.buttonText, + accessibility_text: data.accessibilityText, + image_name: data.imageName, + subscribe_state_subscribed: data.subscribeState.subscribed, + endpoint: new NavigationEndpoint(data.onTapCommand) + }; + } +} \ No newline at end of file diff --git a/src/parser/nodes.ts b/src/parser/nodes.ts index 6c4403f7..027dec57 100644 --- a/src/parser/nodes.ts +++ b/src/parser/nodes.ts @@ -224,6 +224,8 @@ export { default as ItemSectionTab } from './classes/ItemSectionTab.js'; export { default as ItemSectionTabbedHeader } from './classes/ItemSectionTabbedHeader.js'; export { default as LikeButton } from './classes/LikeButton.js'; export { default as LikeButtonView } from './classes/LikeButtonView.js'; +export { default as ListItemView } from './classes/ListItemView.js'; +export { default as ListView } from './classes/ListView.js'; export { default as LiveChat } from './classes/LiveChat.js'; export { default as AddBannerToLiveChatCommand } from './classes/livechat/AddBannerToLiveChatCommand.js'; export { default as AddChatItemAction } from './classes/livechat/AddChatItemAction.js'; @@ -457,6 +459,7 @@ export { default as StructuredDescriptionPlaylistLockup } from './classes/Struct export { default as SubFeedOption } from './classes/SubFeedOption.js'; export { default as SubFeedSelector } from './classes/SubFeedSelector.js'; export { default as SubscribeButton } from './classes/SubscribeButton.js'; +export { default as SubscribeButtonView } from './classes/SubscribeButtonView.js'; export { default as SubscriptionNotificationToggleButton } from './classes/SubscriptionNotificationToggleButton.js'; export { default as Tab } from './classes/Tab.js'; export { default as Tabbed } from './classes/Tabbed.js';