diff --git a/app/components-react/widgets/common/useWidget.tsx b/app/components-react/widgets/common/useWidget.tsx index 5c35d9b35d98..0dd58936094a 100644 --- a/app/components-react/widgets/common/useWidget.tsx +++ b/app/components-react/widgets/common/useWidget.tsx @@ -451,9 +451,7 @@ export class WidgetModule { } private setStaticConfig(resp: unknown) { - this.state.mutate(state => { - state.staticConfig = resp; - }); + this.state.setStaticConfig(resp); } } diff --git a/app/components-react/windows/source-showcase/SourceGrid.m.less b/app/components-react/windows/source-showcase/SourceGrid.m.less new file mode 100644 index 000000000000..4f6c6df20f7c --- /dev/null +++ b/app/components-react/windows/source-showcase/SourceGrid.m.less @@ -0,0 +1,39 @@ +.source-grid { + :global(.ant-collapse-content-box) { + padding-left: 0; + padding-right: 0; + } + + :global(.collapse-section) { + display: flex; + flex: 1; + flex-wrap: wrap; + + } + + :global(.collapse-section .ant-col-8) { + margin-bottom: 8px; + } + + :global(.ant-collapse-header) { + padding-left: 0 !important; + padding-right: 0 !important; + font-size: 20px; + + + .ant-collapse-arrow { + font-size: 16px !important; + } + } + + // Override disabled styles from antd, we use this for the essential section + :global(.ant-collapse-item-disabled .ant-collapse-header) { + cursor: default !important; + color: var(--title) !important; + } + + :global(.ant-collapse-content-box) { + padding-left: 0 !important; + padding-right: 0 !important; + } +} diff --git a/app/components-react/windows/source-showcase/SourceGrid.tsx b/app/components-react/windows/source-showcase/SourceGrid.tsx index 4bd387d7479e..c8de9b44ccad 100644 --- a/app/components-react/windows/source-showcase/SourceGrid.tsx +++ b/app/components-react/windows/source-showcase/SourceGrid.tsx @@ -1,11 +1,11 @@ -import React, { useMemo } from 'react'; -import { Empty, Row, Col, PageHeader, Button } from 'antd'; +import React, { useMemo, useState } from 'react'; +import { Empty, Row, Col, PageHeader, Button, Collapse } from 'antd'; import Scrollable from 'components-react/shared/Scrollable'; import { Services } from 'components-react/service-provider'; import { useVuex } from 'components-react/hooks'; import { IObsListOption } from 'components/obs/inputs/ObsInput'; import { WidgetDisplayData, WidgetType } from 'services/widgets'; -import { TSourceType } from 'services/sources'; +import { TSourceType, SourceDisplayData } from 'services/sources'; import { getPlatformService } from 'services/platforms'; import { $i } from 'services/utils'; import { byOS, getOS, OS } from 'util/operating-systems'; @@ -14,6 +14,7 @@ import SourceTag from './SourceTag'; import { useSourceShowcaseSettings } from './useSourceShowcase'; import { EAvailableFeatures } from 'services/incremental-rollout'; import { useRealmObject } from 'components-react/hooks/realm'; +import styles from './SourceGrid.m.less'; export default function SourceGrid(p: { activeTab: string }) { const { @@ -25,6 +26,23 @@ export default function SourceGrid(p: { activeTab: string }) { IncrementalRolloutService, } = Services; + // TODO: persistence + const [expandedSections, setExpandedSections] = useState([ + 'essentialSources', + 'captureSources', + 'avSources', + 'mediaSources', + 'widgets', + 'apps', + ]); + + const [widgetSections, setWidgetExpandedSections] = useState([ + 'essentialWidgets', + 'interactive', + 'goals', + 'flair', + ]); + const { isLoggedIn, linkedPlatforms, primaryPlatform } = useVuex(() => ({ isLoggedIn: UserService.views.isLoggedIn, linkedPlatforms: UserService.views.linkedPlatforms, @@ -91,22 +109,40 @@ export default function SourceGrid(p: { activeTab: string }) { }); }, []); + const essentialSourcesOrder = ['game_capture', 'dshow_input', 'ffmpeg_source']; + // Stream Label is last, we don't have a widget type for it + const essentialWidgetsOrder = [ + WidgetType.AlertBox, + WidgetType.ChatBox, + WidgetType.EventList, + WidgetType.ViewerCount, + ]; + + function customOrder(orderArray: T[], getter: (a: U) => T) { + return (s1: U, s2: U): number => + orderArray.indexOf(getter(s1)) - orderArray.indexOf(getter(s2)); + } + const essentialSources = useMemo(() => { - const essentialDefaults = availableSources.filter(source => - [ - 'dshow_input', - 'ffmpeg_source', - byOS({ [OS.Windows]: 'screen_capture', [OS.Mac]: 'window_capture' }), - ].includes(source.value), - ); + const essentialDefaults = availableSources + .filter(source => + [ + 'dshow_input', + 'ffmpeg_source', + 'game_capture', + //byOS({ [OS.Windows]: 'screen_capture', [OS.Mac]: 'window_capture' }), + ].includes(source.value), + ) + .sort(customOrder(essentialSourcesOrder, s => s.value)); + const essentialWidgets = iterableWidgetTypes.filter(type => - [WidgetType.AlertBox, WidgetType.EventList].includes(WidgetType[type]), + [WidgetType.AlertBox, WidgetType.ChatBox].includes(WidgetType[type]), ); return { essentialDefaults, essentialWidgets }; }, []); function showContent(key: string) { - const correctKey = ['all', key].includes(p.activeTab); + const correctKey = key === p.activeTab; if (key === 'apps') { return correctKey && availableAppSources.length > 0; } @@ -126,110 +162,317 @@ export default function SourceGrid(p: { activeTab: string }) { return !essentialSources.essentialDefaults.find(s => s.value === source.value); } - return ( - - - {showContent('all') && ( + const { Panel } = Collapse; + + const toSourceEl = (source: IObsListOption) => ( + + ); + + // TODO: restrict type + // Hide widget descriptions on non-general tab + const toWidgetEl = (widget: string) => ( + + ); + + const essentialSourcesList = useMemo( + () => ( + <> + {essentialSources.essentialDefaults.map(source => ( + + ))} + + {isLoggedIn && + essentialSources.essentialWidgets.map(widgetType => ( + + ))} + {isLoggedIn && ( + + )} + + ), + [essentialSources, isLoggedIn, excludeWrap], + ); + + const sourceDisplayData = useMemo(() => SourceDisplayData(), []); + const widgetDisplayData = useMemo(() => WidgetDisplayData(), []); + + const byGroup = (group: 'capture' | 'av' | 'media') => (source: IObsListOption) => { + const displayData = sourceDisplayData[source.value]; + if (!displayData) { + return true; + } + + return displayData.group === group; + }; + + const byWidgetGroup = (group: string) => (widget: string) => { + const displayData = widgetDisplayData[WidgetType[widget]]; + if (!displayData) { + return true; + } + + return displayData.group === group; + }; + + const captureSourcesList = useMemo( + () => availableSources.filter(byGroup('capture')).map(toSourceEl), + [availableSources, excludeWrap], + ); + + const avSourcesList = useMemo(() => availableSources.filter(byGroup('av')).map(toSourceEl), [ + availableSources, + excludeWrap, + ]); + + const mediaSourcesList = useMemo( + () => ( + <> + {availableSources.filter(byGroup('media')).map(toSourceEl)} + + {designerMode && ( + + )} + + ), + [availableSources, excludeWrap, designerMode], + ); + + const widgetList = useMemo( + () => ( + <> + {!isLoggedIn ? ( + + + + ) : ( <> - - - - {essentialSources.essentialDefaults.map(source => ( + {iterableWidgetTypes.filter(filterEssential).map(widgetType => ( ))} - {isLoggedIn && - essentialSources.essentialWidgets.map(widgetType => ( - - ))} - {isLoggedIn && ( + {p.activeTab !== 'all' && ( )} )} - {showContent('general') && ( - <> - - - - {availableSources.filter(filterEssential).map(source => ( - - ))} - - {designerMode && ( - - )} - + + ), + [isLoggedIn, iterableWidgetTypes, p.activeTab, excludeWrap], + ); + + const widgetGroupedList = useMemo(() => { + // TODO: restrict types + const widgetsInGroup = (group: string, sorter?: (s1: string, s2: string) => number) => { + const widgets = iterableWidgetTypes + .filter(byWidgetGroup(group)) + // Sort lexographically by default, if sorter is not provided + .sort(sorter); + + return widgets.map(toWidgetEl); + }; + + // Using essentials as a group for widgets since we wanna display more + const essentialWidgets = ( + <> + {widgetsInGroup( + 'essential', + customOrder(essentialWidgetsOrder, x => WidgetType[x]), )} + + + ); - {showContent('widgets') && ( - <> - - - - {!isLoggedIn ? ( - - - - ) : ( - <> - {iterableWidgetTypes.filter(filterEssential).map(widgetType => ( - - ))} - {p.activeTab !== 'all' && ( - - )} - - )} - + const interactiveWidgets = widgetsInGroup('interactive'); + const goalWidgets = widgetsInGroup('goals'); + const flairWidgets = <>{widgetsInGroup('flair')}; + const charityWidgets = widgetsInGroup('charity'); + + return ( + <> + {!isLoggedIn ? ( + + + + ) : ( + setWidgetExpandedSections(xs as string[])} + > + +
+ {essentialWidgets} +
+
+ +
+ {interactiveWidgets} +
+
+ +
+ {goalWidgets} +
+
+ +
+ {flairWidgets} +
+
+ {/* TODO: we don't have any charity widgets on Desktop + +
+ {charityWidgets} +
+
+ */} +
)} - {showContent('apps') && ( + + ); + }, [widgetSections, isLoggedIn, iterableWidgetTypes, excludeWrap]); + + const appsList = useMemo( + () => ( + <> + {availableAppSources.map(app => ( + + ))} + + ), + [availableAppSources, excludeWrap], + ); + + const groupedSources = useMemo( + () => ( + <> + +
{captureSourcesList}
+
+ +
{avSourcesList}
+
+ +
{mediaSourcesList}
+
+ + ), + [captureSourcesList, avSourcesList, mediaSourcesList], + ); + + const individualTab = useMemo(() => { + /* + * TODO: general is called media now, should probably rename in code. + * It is the same as the All Sources tab except for widgets and apps. + */ + if (showContent('general')) { + return ( + <> + + setExpandedSections(xs as string[])} + > + {groupedSources} + + + + ); + } else if (showContent('widgets')) { + return ( + <> + {widgetGroupedList} + + ); + } else if (showContent('apps')) { + return ( + <> + + + + {appsList} + + ); + } + }, [p.activeTab, availableAppSources, appsList, widgetList]); + + return ( + + + {p.activeTab === 'all' ? ( <> - + setExpandedSections(xs as string[])} + > + +
+ {essentialSourcesList} +
+
+ {groupedSources} + +
+ {widgetList} +
+
+ +
+ {appsList} +
+
+
- {availableAppSources.map(app => ( - - ))} + ) : ( + individualTab )}
diff --git a/app/components-react/windows/source-showcase/SourceShowcase.m.less b/app/components-react/windows/source-showcase/SourceShowcase.m.less index b5c7986b773d..039c1d9ab5c8 100644 --- a/app/components-react/windows/source-showcase/SourceShowcase.m.less +++ b/app/components-react/windows/source-showcase/SourceShowcase.m.less @@ -24,6 +24,7 @@ text-overflow: ellipsis; min-height: 56px; display: flex; + flex-direction: column; i { margin-right: 8px; diff --git a/app/components-react/windows/source-showcase/SourceTag.tsx b/app/components-react/windows/source-showcase/SourceTag.tsx index dd6fe1616077..2c99cf2dd863 100644 --- a/app/components-react/windows/source-showcase/SourceTag.tsx +++ b/app/components-react/windows/source-showcase/SourceTag.tsx @@ -15,6 +15,7 @@ export default function SourceTag(p: { appSourceId?: string; essential?: boolean; excludeWrap?: boolean; + hideShortDescription?: boolean; }) { const { inspectSource, selectInspectedSource, store } = useSourceShowcaseSettings(); @@ -39,7 +40,7 @@ export default function SourceTag(p: { return (
selectInspectedSource()} data-name={displayData?.name || p.name} > -
- {displayData?.icon && } -
-
- {displayData?.name || p.name} - {p.essential &&
{displayData?.shortDesc}
} +
+
+ {displayData?.icon && } +
+
{displayData?.name || p.name}
+ {displayData?.shortDesc && !p.hideShortDescription && ( +
+ {displayData?.shortDesc} +
+ )}
); diff --git a/app/components-react/windows/source-showcase/index.tsx b/app/components-react/windows/source-showcase/index.tsx index f76a512e054a..6add1c8d5373 100644 --- a/app/components-react/windows/source-showcase/index.tsx +++ b/app/components-react/windows/source-showcase/index.tsx @@ -51,8 +51,8 @@ function SourcesShowcaseModal() { mode="horizontal" style={{ marginBottom: '16px' }} > - {$t('All')} - {$t('General')} + {$t('All Sources')} + {$t('Media Categories')} {$t('Widgets')} {availableAppSources.length > 0 && {$t('Apps')}} diff --git a/app/i18n/en-US/source-props.json b/app/i18n/en-US/source-props.json index 3298443bd71b..e753a1323722 100644 --- a/app/i18n/en-US/source-props.json +++ b/app/i18n/en-US/source-props.json @@ -1,17 +1,18 @@ { - "Image": "Image", - "Color Block": "Color Block", - "Browser Source": "Browser Source", - "Media File": "Media File", - "Image Slide Show": "Image Slide Show", - "Text (GDI+)": "Text (GDI+)", - "Text (FreeType 2)": "Text (FreeType 2)", - "Display Capture": "Display Capture", - "Window Capture": "Window Capture", - "Game Capture": "Game Capture", - "Video Capture Device": "Video Capture Device", - "Audio Input Capture": "Audio Input Capture", - "Audio Output Capture": "Audio Output Capture", - "Blackmagic Device": "Blackmagic Device", - "NDI Source": "NDI Source" + "Image": "Image", + "Color Block": "Color Block", + "Browser Source": "Browser Source", + "Media File": "Media File", + "Image Slide Show": "Image Slide Show", + "Text (GDI+)": "Text (GDI+)", + "Text (FreeType 2)": "Text (FreeType 2)", + "Display Capture": "Display Capture", + "Window Capture": "Window Capture", + "Game Capture": "Game Capture", + "Video Capture Device": "Video Capture Device", + "Camera / Capture Card": "Camera / Capture Card", + "Audio Input Capture": "Audio Input Capture", + "Audio Output Capture": "Audio Output Capture", + "Blackmagic Device": "Blackmagic Device", + "NDI Source": "NDI Source" } diff --git a/app/i18n/en-US/sources.json b/app/i18n/en-US/sources.json index ce1315db3da7..93f307082417 100644 --- a/app/i18n/en-US/sources.json +++ b/app/i18n/en-US/sources.json @@ -1,30 +1,42 @@ { "Add Source": "Add Source", "Add images to your scene.": "Add images to your scene.", + "Add image to scene": "Add image to scene", "Add a slideshow of images to your scene.": "Add a slideshow of images to your scene.", + "Add a slideshow": "Add a slideshow", "Add videos or sound clips to your scene.": "Add videos or sound clips to your scene.", + "Videos or sound clips": "Videos or sound clips", "Capture a specific window that's open on your computer.": "Capture a specific window that's open on your computer.", + "Share specific window": "Share specific window", "Compatible with most modern browsers and programs": "Compatible with most modern browsers and programs", "Display video from webcams, capture cards, and other devices.": "Display video from webcams, capture cards, and other devices.", "Built in webcam": "Built in webcam", "Logitech webcam": "Logitech webcam", + "Video Capture": "Video Capture", "Capture cards (Elgato, Avermedia, BlackMagic)": "Capture cards (Elgato, Avermedia, BlackMagic)", "Captures your desktop audio for the purpose of playing sound, such as music or speech.": "Captures your desktop audio for the purpose of playing sound, such as music or speech.", + "Capture desktop audio": "Capture desktop audio", "Desktop audio": "Desktop audio", "Add a color to the background of your whole scene or just a part.": "Add a color to the background of your whole scene or just a part.", "Allows you to add web-based content as a source, such as web pages, widgets, and streaming video.": "Allows you to add web-based content as a source, such as web pages, widgets, and streaming video.", + "Your web-based content": "Your web-based content", "Websites": "Websites", "Third party widget": "Third party widget", "Add text to your scene and adjust its style.": "Add text to your scene and adjust its style.", + "Add text to scene": "Add text to scene", "System Fonts": "System Fonts", "System Sizes": "System Sizes", "Capture your entire computer monitor.": "Capture your entire computer monitor.", + "Share monitor": "Share monitor", "Primary monitor": "Primary monitor", "Secondary monitor": "Secondary monitor", "Capture a game you're playing on your computer.": "Capture a game you're playing on your computer.", + "Capture your game": "Capture your game", "Built in works with most modern computer games": "Built in works with most modern computer games", "Allow you to capture NDI output streams.": "Allow you to capture NDI output streams.", + "Capture NDI output": "Capture NDI output", "Any device that attaches to a computer for the purpose of capturing sound, such as music or speech.": "Any device that attaches to a computer for the purpose of capturing sound, such as music or speech.", + "Capture microphone": "Capture microphone", "Built in microphones": "Built in microphones", "USB microphones": "USB microphones", "Other USB devices": "Other USB devices", @@ -38,9 +50,21 @@ "Hosts": "Hosts", "Show off your most recent donations to your viewers.": "Show off your most recent donations to your viewers.", "Include your channel's most recent events into your stream.": "Include your channel's most recent events into your stream.", + "Display your channel's most recent events on stream.": "Display your channel's most recent events on stream.", "Redemptions": "Redemptions", "Tip Goal": "Tip Goal", "Set a goal for your viewers to help you reach.": "Set a goal for your viewers to help you reach.", + "Set a subscriber goal for your viewers to help you reach.": "Set a subscriber goal for your viewers to help you reach.", + "Set a bit goal for your viewers to help you reach.": "Set a bit goal for your viewers to help you reach.", + "Set a follower goal for your viewers to help you reach.": "Set a follower goal for your viewers to help you reach.", + "Set a stars goal for your viewers to help you reach.": "Set a stars goal for your viewers to help you reach.", + "Set a charity donation goal for your viewers to help you reach.": "Set a charity donation goal for your viewers to help you reach.", + "Set a super chat goal for your viewers to help you reach.": "Set a super chat goal for your viewers to help you reach.", + "Facebook Supporters": "Facebook Supporters", + "Trovo Subscribers": "Trovo Subscribers", + "Facebook Followers": "Facebook Followers", + "Trovo Followers": "Trovo Followers", + "Set a tip goal to encourage your viewers to contribute toward something.": "Set a tip goal to encourage your viewers to contribute toward something.", "Follower Goal": "Follower Goal", "Twitch Follows": "Twitch Follows", "YouTube Follows": "YouTube Follows", @@ -50,27 +74,37 @@ "YouTube Subscribers": "YouTube Subscribers", "Chat Box": "Chat Box", "Include your channel's chat into your stream.": "Include your channel's chat into your stream.", + "Display your chat on stream and set up sound notifications for new chats.": "Display your chat on stream and set up sound notifications for new chats.", + "Display chatters on screen": "Display chatters on screen", "Twitch chat": "Twitch chat", "YouTube chat": "YouTube chat", "Trovo chat": "Trovo chat", "The Jar": "The Jar", "The jar that catches bits, tips, and more.": "The jar that catches bits, tips, and more.", + "A jar that catches bits, tips, subs and more.": "A jar that catches bits, tips, subs and more.", + "Platform support: Twitch, YouTube, Facebook, Trovo": "Platform support: Twitch, YouTube, Facebook, Trovo", "Viewer Count": "Viewer Count", "Show off your viewers from multiple platforms.": "Show off your viewers from multiple platforms.", + "Show off the count of live viewers by platform.": "Show off the count of live viewers by platform.", "Stream Boss": "Stream Boss", "Battle with bits to be the boss of the stream!": "Battle with bits to be the boss of the stream!", + "Let your viewers duke it out by subbing, tipping and more to become the Stream Boss.": "Let your viewers duke it out by subbing, tipping and more to become the Stream Boss.", "Credits": "Credits", "Rolling credits to play at the end of your stream.": "Rolling credits to play at the end of your stream.", + "Roll credits at the end of your broadcast to thank supporters for tipping, following, subbing, modding and more.": "Roll credits at the end of your broadcast to thank supporters for tipping, following, subbing, modding and more.", "New Followers": "New Followers", "New Subscribers": "New Subscribers", "Cheers": "Cheers", "Spin Wheel": "Spin Wheel", "Spin the wheel to make a decision.": "Spin the wheel to make a decision.", + "Let your viewers spin a custom spin wheel to win prizes or incentives.": "Let your viewers spin a custom spin wheel to win prizes or incentives.", "The streamer manually triggers a spin anytime while they are live.": "The streamer manually triggers a spin anytime while they are live.", "Allows you to add existing scene as a source": "Allows you to add existing scene as a source", "Stream Label": "Stream Label", "This is a placeholder description for Stream Labels.": "Include text into your stream, such as follower count, last donation, and many others.", "Application Audio Capture (BETA)": "Application Audio Capture (BETA)", + "Application Audio": "Application Audio", + "Beta": "Beta", "Capture the audio coming from a specific application.": "Capture the audio coming from a specific application.", "New Cheers": "New Cheers", "New Donations": "New Donations", @@ -93,6 +127,7 @@ "Unable to add a source: the scene you are trying to add already contains your current scene": "Unable to add a source: the scene you are trying to add already contains your current scene", "Add New Source": "Add New Source", "Please enter the name of the source": "Please enter the name of the source", + "Existing scene": "Existing scene", "Add Existing Source": "Add Existing Source", "Recommended": "Recommended", "Please enter the name of the folder": "Please enter the name of the folder", @@ -151,10 +186,12 @@ "There are no existing sources of this type.": "There are no existing sources of this type.", "Instant Replay": "Instant Replay", "Include text into your stream, such as follower count, last donation, and many others.": "Include text into your stream, such as follower count, last donation, and many others.", + "Include text into your stream, such as follower count, latest tip, last sub and more.": "Include text into your stream, such as follower count, latest tip, last sub and more.", "Automatically plays your most recently captured replay in your stream.": "Automatically plays your most recently captured replay in your stream.", "New filter": "New filter", "Third party widgets": "Third party widgets", "OpenVR Capture": "OpenVR Capture", + "Capture VR games": "Capture VR games", "Directly capture the OpenVR monitoring video buffer of your HMD.": "Directly capture the OpenVR monitoring video buffer of your HMD.", "LIV Client Capture": "LIV Client Capture", "Directly capture the LIV compositor output, reducing load and simplifying setup for Mixed Reality.": "Directly capture the LIV compositor output, reducing load and simplifying setup for Mixed Reality.", @@ -162,6 +199,7 @@ "Specified window is not a game": "Specified window is not a game", "VLC Source": "VLC Source", "Add playlists of videos to your scene.": "Add playlists of videos to your scene.", + "Video playlist": "Video playlist", "Settings for %{sourceName}": "Settings for %{sourceName}", "Visible on both Stream and Recording": "Visible on both Stream and Recording", "Only visible on Stream": "Only visible on Stream", @@ -205,13 +243,22 @@ "Capture Window": "Capture Window", "Supports:": "Supports:", "Essential Sources": "Essential Sources", + "Essentials": "Essentials", + "All Sources": "All Sources", + "Media Categories": "Media Categories", "General Sources": "General Sources", + "Capture Sources": "Capture Sources", + "Media": "Media", + "Video and Audio": "Video and Audio", "Add media": "Add media", "Your webcam device": "Your webcam device", + "Display device video": "Display device video", "Capture games and apps": "Capture games and apps", + "Capture screen": "Capture screen", "Dynamic, live alerts": "Dynamic, live alerts", + "Dynamic live alerts": "Dynamic live alerts", "Display recent events": "Display recent events", - "Viewer shoutouts": "Viewer shoutouts", + "Stream stats and events": "Stream stats and events", "Capture an application window": "Capture an application window", "Folder Expansion": "Folder Expansion", "Wondering how to expand your folders? Just click on the icon": "Wondering how to expand your folders? Just click on the icon", @@ -363,6 +410,8 @@ "Composite mode": "Composite mode", "Poll time for new senders": "Poll time for new senders", "Spout2 capture (Vtube support)": "Spout2 capture (Vtube support)", + "Spout2 capture": "Spout2 capture", + "Vtube support": "Vtube support", "Virtual avatar capture source. Enables high resolution, zero-compression and zero-latency video capture from VTube third-party software.": "Virtual avatar capture source. Enables high resolution, zero-compression and zero-latency video capture from VTube third-party software.", "Learn how to set it up": "Learn how to set it up", "Lock/Unlock Source": "Lock/Unlock Source", @@ -372,5 +421,12 @@ "Hide from Vertical": "Hide from Vertical", "Show in Vertical": "Show in Vertical", "Streamlabs Charity Donation Goal": "Streamlabs Charity Donation Goal", - "macOS Screen Capture": "macOS Screen Capture" + "macOS Screen Capture": "macOS Screen Capture", + "Stream with guests": "Stream with guests", + "Display latest replay": "Display latest replay", + "Add color to scene": "Add color to scene", + "New Tips": "New Tips", + "All-Time Top Tipper": "All-Time Top Tipper", + "Weekly Top Tipper": "Weekly Top Tipper", + "And more": "And more" } diff --git a/app/i18n/en-US/streaming.json b/app/i18n/en-US/streaming.json index 562fc9096355..83b1e1c5f33b 100644 --- a/app/i18n/en-US/streaming.json +++ b/app/i18n/en-US/streaming.json @@ -279,5 +279,6 @@ "Share stream link": "Share stream link", "Copy stream link": "Copy stream link", "Copy %{platform} link": "Copy %{platform} link", - "Copied to clipboard": "Copied to clipboard" + "Copied to clipboard": "Copied to clipboard", + "Twitch Followers": "Twitch Followers" } diff --git a/app/i18n/en-US/widget-alertbox.json b/app/i18n/en-US/widget-alertbox.json index dfb1b459c782..77a1b3cf8519 100644 --- a/app/i18n/en-US/widget-alertbox.json +++ b/app/i18n/en-US/widget-alertbox.json @@ -154,5 +154,6 @@ "This applies to all alerts. If enabled all alerts need to be approved manually.": "This applies to all alerts. If enabled all alerts need to be approved manually.", "Use unlimited delay": "Use unlimited delay", "When enabled new alerts will interrupt the on screen alert": "When enabled new alerts will interrupt the on screen alert", - "Text Highlight Color": "Text Highlight Color" + "Text Highlight Color": "Text Highlight Color", + "Platforms: Twitch, YouTube, Facebook, Trovo & integrations": "Platforms: Twitch, YouTube, Facebook, Trovo & integrations" } diff --git a/app/i18n/en-US/widget-chat-highlight.json b/app/i18n/en-US/widget-chat-highlight.json index 184f11ef61c8..3e60f83e7101 100644 --- a/app/i18n/en-US/widget-chat-highlight.json +++ b/app/i18n/en-US/widget-chat-highlight.json @@ -12,6 +12,7 @@ "Name Text Color": "Name Text Color", "Name Background Color": "Name Background Color", "Highlight chat messages from your viewers on your stream.": "Highlight chat messages from your viewers on your stream.", + "Pin and highlight chat messages from your viewers on your stream.": "Pin and highlight chat messages from your viewers on your stream.", "A duration of 0 is indefinite": "A duration of 0 is indefinite", "Use this widget by hovering over the right side of a chat message to see a thumbtack icon. Clicking the icon will pin the message to the widget for its duration, or until you click the unpin button.": "Use this widget by hovering over the right side of a chat message to see a thumbtack icon. Clicking the icon will pin the message to the widget for its duration, or until you click the unpin button." } diff --git a/app/i18n/en-US/widget-emote-wall.json b/app/i18n/en-US/widget-emote-wall.json index d8e6e52d1261..fc3773147a6b 100644 --- a/app/i18n/en-US/widget-emote-wall.json +++ b/app/i18n/en-US/widget-emote-wall.json @@ -1,10 +1,11 @@ { "Emote Wall": "Emote Wall", "Display and animate emotes that are seen in chat, improving chat participation via positive feedback.": "Display and animate emotes that are seen in chat, improving chat participation via positive feedback.", + "Display and animate emotes from chat, improving chat participation.": "Display and animate emotes from chat, improving chat participation.", "Wall Settings": "Wall Settings", "Emote Scale": "Emote Scale", "Combo Required": "Combo Required", "Combo Count": "Combo Count", "Combo Timeframe": "Combo Timeframe", "Ignore Duplicates": "Ignore Duplicates" -} \ No newline at end of file +} diff --git a/app/i18n/en-US/widget-game.json b/app/i18n/en-US/widget-game.json index d31d3fc59e52..752ff5bf9c14 100644 --- a/app/i18n/en-US/widget-game.json +++ b/app/i18n/en-US/widget-game.json @@ -1,6 +1,7 @@ { "Game Widget": "Game Widget", "Let your viewers play a game in chat": "Let your viewers play a game in chat", + "Allow your chat to play games together while you are live.": "Allow your chat to play games together while you are live.", "Chat Decision Time": "Chat Decision Time", "Trigger Command": "Trigger Command", "Chat Response Timer": "Chat Response Timer", diff --git a/app/i18n/en-US/widget-goal.json b/app/i18n/en-US/widget-goal.json index 330ca8788239..7995ec819d3b 100644 --- a/app/i18n/en-US/widget-goal.json +++ b/app/i18n/en-US/widget-goal.json @@ -23,5 +23,6 @@ "Facebook chat": "Facebook chat", "Member Goal": "Member Goal", "Streamlabs Charity Goal": "Streamlabs Charity Goal", - "Streamlabs Charity Donations": "Streamlabs Charity Donations" + "Streamlabs Charity Donations": "Streamlabs Charity Donations", + "Charity donations": "Charity donations" } diff --git a/app/i18n/en-US/widget-sponsor-banner.json b/app/i18n/en-US/widget-sponsor-banner.json index 9a9c7dde49b1..c5d24acd9e74 100644 --- a/app/i18n/en-US/widget-sponsor-banner.json +++ b/app/i18n/en-US/widget-sponsor-banner.json @@ -21,5 +21,6 @@ "Visual Settings": "Visual Settings", "Image Set ": "Image Set ", "No Image": "No Image", - "Set up a sponsor banner to be able to edit (add, remove, update) rotating sponsor logos on streamer channel.": "Set up a sponsor banner to be able to edit (add, remove, update) rotating sponsor logos on streamer channel." + "Set up a sponsor banner to be able to edit (add, remove, update) rotating sponsor logos on streamer channel.": "Set up a sponsor banner to be able to edit (add, remove, update) rotating sponsor logos on streamer channel.", + "Show off rotating media to display your socials, sponsorships, CTAs or just cute animations.": "Show off rotating media to display your socials, sponsorships, CTAs or just cute animations." } diff --git a/app/i18n/en-US/widgets.json b/app/i18n/en-US/widgets.json index 0042675c48d9..01d1c90ed1b5 100644 --- a/app/i18n/en-US/widgets.json +++ b/app/i18n/en-US/widgets.json @@ -2,10 +2,12 @@ "Add Widgets": "Add Widgets", "Select the Streamlabs widgets you would like to activate for your stream. These will get automatically added to your scene.": "Select the Streamlabs widgets you would like to activate for your stream. These will get automatically added to your scene.", "Thanks viewers with notification popups": "Thanks viewers with notification popups", + "Custom on-screen alerts to thank your loyal viewers for following, subbing, tipping and more.": "Custom on-screen alerts to thank your loyal viewers for following, subbing, tipping and more.", "Most recent events into your stream": "Most recent events into your stream", "A jar that catches bits, tips, and more": "A jar that catches bits, tips, and more", "Your channel's chat in your stream": "Your channel's chat in your stream", "Show off most recent donations to viewers": "Show off most recent donations to viewers", + "Show off the most recent tips from your viewers": "Show off the most recent tips from your viewers", "Give your viewers a donation target to help you reach": "Give your viewers a donation target to help you reach", "Give your viewers a follower target to help you reach": "Give your viewers a follower target to help you reach", "Give your viewers a bit target to help you reach": "Give your viewers a bit target to help you reach", @@ -70,6 +72,7 @@ "Ember amount is at least ": "Ember amount is at least ", "Ember amount is exactly ": "Ember amount is exactly ", "Allow your viewers to donate to share media on your stream.": "Allow your viewers to donate to share media on your stream.", + "Allow your viewers to tip to share videos on your stream.": "Allow your viewers to tip to share videos on your stream.", "Twitch Subscribers": "Twitch Subscribers", "YouTube Members": "YouTube Members", "Media Share": "Media Share", @@ -188,5 +191,9 @@ "Superchat Goal": "Superchat Goal", "YouTube Superchats": "YouTube Superchats", "Custom Widget": "Custom Widget", - "Use HTML, CSS, and JavaScript to create a widget with custom functionality": "Use HTML, CSS, and JavaScript to create a widget with custom functionality" + "Use HTML, CSS, and JavaScript to create a widget with custom functionality": "Use HTML, CSS, and JavaScript to create a widget with custom functionality", + "Interactive": "Interactive", + "Goals": "Goals", + "Flair": "Flair", + "Charity": "Charity" } diff --git a/app/services/sources/sources-api.ts b/app/services/sources/sources-api.ts index a4df6d179cdf..b852cca69878 100644 --- a/app/services/sources/sources-api.ts +++ b/app/services/sources/sources-api.ts @@ -173,4 +173,6 @@ export interface ISourceDisplayData { shortDesc?: string; link?: string; linkText?: string; + // TODO: make required if none are missing + group?: string; } diff --git a/app/services/sources/sources-data.ts b/app/services/sources/sources-data.ts index 4599313ca388..77532fc2d39b 100644 --- a/app/services/sources/sources-data.ts +++ b/app/services/sources/sources-data.ts @@ -22,72 +22,89 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ image_source: { name: $t('Image'), description: $t('Add images to your scene.'), + shortDesc: $t('Add image to scene'), demoFilename: 'image.png', supportList: imageSupport, icon: 'icon-image', + group: 'media', }, color_source: { name: $t('Color Block'), + shortDesc: $t('Add color to scene'), description: $t('Add a color to the background of your whole scene or just a part.'), demoFilename: 'color-source.png', supportList: colorSupport, icon: 'fas fa-fill', + group: 'media', }, browser_source: { name: $t('Browser Source'), description: $t( 'Allows you to add web-based content as a source, such as web pages, widgets, and streaming video.', ), + shortDesc: $t('Your web-based content'), demoFilename: 'browser-source.png', supportList: [$t('Websites'), $t('Third party widgets'), 'HTML'], icon: 'fas fa-globe', + group: 'capture', }, ffmpeg_source: { name: $t('Media File'), description: $t('Add videos or sound clips to your scene.'), + shortDesc: $t('Videos or sound clips'), demoFilename: 'media.png', supportList: mediaSupport, icon: 'far fa-file-video', - shortDesc: $t('Add media'), + group: 'media', }, slideshow: { name: $t('Image Slide Show'), description: $t('Add a slideshow of images to your scene.'), + shortDesc: $t('Add a slideshow'), demoFilename: 'image-slide-show.png', supportList: imageSupport, icon: 'icon-image', + group: 'media', }, text_gdiplus: { name: $t('Text (GDI+)'), + shortDesc: $t('Add text to scene'), description: $t('Add text to your scene and adjust its style.'), demoFilename: 'text.png', supportList: [...colorSupport, $t('System Fonts'), $t('System Sizes')], icon: 'fas fa-font', + group: 'media', }, monitor_capture: { name: $t('Display Capture'), + shortDesc: $t('Share monitor'), description: $t('Capture your entire computer monitor.'), demoFilename: 'display-capture.png', supportList: [$t('Primary monitor'), $t('Secondary monitor')], icon: 'fas fa-desktop', + group: 'capture', }, window_capture: { name: $t('Window Capture'), + shortDesc: $t('Share specific window'), description: $t("Capture a specific window that's open on your computer."), demoFilename: 'window-capture.png', supportList: [$t('Compatible with most modern browsers and programs')], icon: 'icon-editor-9', - shortDesc: $t('Capture an application window'), + group: 'capture', }, game_capture: { name: $t('Game Capture'), description: $t("Capture a game you're playing on your computer."), + shortDesc: $t('Capture your game'), demoFilename: 'game-capture.png', supportList: [$t('Built in works with most modern computer games')], icon: 'fas fa-gamepad', + group: 'capture', }, dshow_input: { - name: $t('Video Capture Device'), + name: $t('Video Capture'), + shortDesc: $t('Camera / Capture Card'), description: $t('Display video from webcams, capture cards, and other devices.'), demoFilename: 'video-capture.png', supportList: [ @@ -96,12 +113,14 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ $t('Capture cards (Elgato, Avermedia, BlackMagic)'), ], icon: 'icon-webcam', - shortDesc: $t('Your webcam device'), + group: 'av', }, ndi_source: { name: $t('NDI source'), + shortDesc: $t('Capture NDI output'), description: $t('Allow you to capture NDI output streams.'), icon: 'fas fa-file', + group: 'capture', }, 'decklink-input': { name: $t('Blackmagic Device'), @@ -113,25 +132,29 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ openvr_capture: { name: $t('OpenVR Capture'), description: $t('Directly capture the OpenVR monitoring video buffer of your HMD.'), + shortDesc: $t('Capture VR games'), demoFilename: 'vr-capture.png', supportList: ['OpenVR', 'SteamVR'], icon: 'fab fa-simplybuilt fa-rotate-180', + group: 'capture', }, screen_capture: { name: $t('Screen Capture'), description: $t('Capture your game, other applications, or your entire monitor'), + shortDesc: $t('Capture screen'), demoFilename: 'window-capture.png', supportList: [$t('Most games, apps, displays')], icon: 'icon-group', - shortDesc: $t('Capture games and apps'), + group: 'capture', }, mac_screen_capture: { name: $t('macOS Screen Capture'), + shortDesc: $t('Capture screen'), description: $t('Capture your game, other applications, or your entire monitor'), demoFilename: 'window-capture.png', supportList: [$t('Most games, apps, displays')], icon: 'icon-group', - shortDesc: $t('Capture games and apps'), + group: 'capture', }, liv_capture: { name: $t('LIV Client Capture'), @@ -141,30 +164,37 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ demoFilename: 'vr-capture.png', supportList: ['LIV'], icon: 'fab fa-simplybuilt fa-rotate-180', + group: 'capture', }, wasapi_input_capture: { name: $t('Audio Input Capture'), description: $t( 'Any device that attaches to a computer for the purpose of capturing sound, such as music or speech.', ), + shortDesc: $t('Capture microphone'), demoFilename: 'audio-input.png', supportList: [$t('Built in microphones'), $t('USB microphones'), $t('Other USB devices')], icon: 'icon-mic', + group: 'av', }, wasapi_output_capture: { name: $t('Audio Output Capture'), + shortDesc: $t('Capture desktop audio'), description: $t( 'Captures your desktop audio for the purpose of playing sound, such as music or speech.', ), demoFilename: 'audio-output.png', supportList: [$t('Desktop audio')], icon: 'icon-audio', + group: 'av', }, scene: { name: $t('Scene'), + shortDesc: $t('Existing scene'), description: $t('Allows you to add existing scene as a source'), demoFilename: 'scene.png', icon: 'far fa-object-group', + group: 'media', }, text_ft2_source: { name: $t('Text (FreeType 2)'), @@ -172,37 +202,46 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ demoFilename: 'text.png', supportList: [...colorSupport, $t('System Fonts'), $t('System Sizes')], icon: 'fas fa-font', + group: 'media', }, ovrstream_dc_source: { name: 'OvrStream', description: '', icon: 'fas fa-file', + group: 'capture', }, vlc_source: { name: $t('VLC Source'), + shortDesc: $t('Video playlist'), description: $t('Add playlists of videos to your scene.'), icon: 'fas fa-play', + group: 'media', }, coreaudio_input_capture: { name: $t('Audio Input Capture'), + shortDesc: $t('Capture microphone'), description: $t( 'Any device that attaches to a computer for the purpose of capturing sound, such as music or speech.', ), demoFilename: 'audio-input.png', supportList: [$t('Built in microphones'), $t('USB microphones'), $t('Other USB devices')], icon: 'icon-mic', + group: 'av', }, coreaudio_output_capture: { name: $t('Audio Output Capture'), + shortDesc: $t('Capture desktop audio'), description: $t( 'Captures your desktop audio for the purpose of playing sound, such as music or speech.', ), demoFilename: 'audio-output.png', supportList: [$t('Desktop audio')], icon: 'icon-audio', + group: 'av', }, av_capture_input: { - name: $t('Video Capture Device'), + name: $t('Video Capture'), + shortDesc: $t('Camera / Capture Card'), description: $t('Display video from webcams, capture cards, and other devices.'), demoFilename: 'video-capture.png', supportList: [ @@ -211,25 +250,31 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ $t('Capture cards (Elgato, Avermedia, BlackMagic)'), ], icon: 'icon-webcam', + group: 'av', }, display_capture: { name: $t('Display Capture'), + shortDesc: $t('Share monitor'), description: $t('Capture your entire computer monitor.'), demoFilename: 'display-capture.png', supportList: [$t('Primary monitor'), $t('Secondary monitor')], icon: 'fas fa-desktop', + group: 'capture', }, 'syphon-input': { name: $t('Game Capture'), + shortDesc: $t('Capture your game'), description: $t("Capture a game you're playing on your computer."), demoFilename: 'game-capture.png', supportList: [$t('Built in works with most modern computer games')], icon: 'fas fa-gamepad', + group: 'capture', }, audio_line: { name: $t('JACK Input Client'), description: $t(''), icon: 'fas fa-file', + group: 'av', }, soundtrack_source: { name: $t('Twitch Soundtrack'), @@ -240,9 +285,11 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ }, replay: { name: $t('Instant Replay'), + shortDesc: $t('Display latest replay'), description: $t('Automatically plays your most recently captured replay in your stream.'), demoFilename: 'media.png', icon: 'icon-replay-buffer', + group: 'media', }, icon_library: { name: $t('Custom Icon'), @@ -253,37 +300,42 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ streamlabel: { name: $t('Stream Label'), description: $t( - 'Include text into your stream, such as follower count, last donation, and many others.', + 'Include text into your stream, such as follower count, latest tip, last sub and more.', ), demoFilename: 'source-stream-labels.png', supportList: [ $t('New Followers'), $t('New Subscribers'), $t('New Cheers'), - $t('New Donations'), - $t('All-Time Top Donator'), - $t('Weekly Top Donator'), + $t('New Tips'), + $t('All-Time Top Tipper'), + $t('Weekly Top Tipper'), $t('Monthly Follows'), - $t('Many more'), + $t('And more'), ], icon: 'fas fa-file', - shortDesc: $t('Viewer shoutouts'), + shortDesc: $t('Stream stats and events'), }, mediasoupconnector: { name: $t('Collab Cam'), + shortDesc: $t('Stream with guests'), description: $t( 'Invite guests to join your stream from a web browser or their Streamlabs Desktop. Stream games or IRL broadcasts together, use as additional camera angles and more.', ), demoFilename: 'source-collab-cam.png', icon: 'icon-team-2', + group: 'av', }, wasapi_process_output_capture: { - name: $t('Application Audio Capture (BETA)'), + name: $t('Application Audio'), + shortDesc: $t('Beta'), description: $t('Capture the audio coming from a specific application.'), icon: 'fas fa-user', + group: 'av', }, spout_capture: { - name: $t('Spout2 capture (Vtube support)'), + name: $t('Spout2 capture'), + shortDesc: $t('Vtube support'), description: $t( 'Virtual avatar capture source. Enables high resolution, zero-compression and zero-latency video capture from VTube third-party software.', ), @@ -292,5 +344,6 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({ icon: 'icon-face-masks-3', link: 'https://streamlabs.com/content-hub/post/vtuber-support-on-streamlabs-desktop', linkText: $t('Learn how to set it up'), + group: 'capture', }, }); diff --git a/app/services/widgets/widgets-data.ts b/app/services/widgets/widgets-data.ts index 32235b64ecaa..bc504a042017 100644 --- a/app/services/widgets/widgets-data.ts +++ b/app/services/widgets/widgets-data.ts @@ -15,7 +15,11 @@ export interface IWidgetDisplayData { shortDesc?: string; link?: string; linkText?: string; + group: TWidgetGroup; } + +export type TWidgetGroup = 'essential' | 'interactive' | 'goals' | 'flair' | 'charity'; + // Do not alter the order of this enum, it is coupled to the user's local config export enum WidgetType { AlertBox = 0, @@ -538,61 +542,80 @@ export const WidgetDefinitions: { [x: number]: IWidget } = { export const WidgetDisplayData = (platform?: string): { [x: number]: IWidgetDisplayData } => ({ [WidgetType.AlertBox]: { name: $t('Alert Box'), - description: $t('Thanks viewers with notification popups.'), + description: $t( + 'Custom on-screen alerts to thank your loyal viewers for following, subbing, tipping and more.', + ), demoVideo: false, demoFilename: 'source-alertbox.png', - supportList: [$t('Donations'), $t('Subscriptions'), $t('Follows'), $t('Bits')], + supportList: [ + $t('Tips'), + $t('Subscriptions'), + $t('Follows'), + $t('Bits'), + $t('Platforms: Twitch, YouTube, Facebook, Trovo & integrations'), + ], icon: 'icon-alert-box', - shortDesc: $t('Dynamic, live alerts'), + shortDesc: $t('Dynamic live alerts'), + group: 'essential', }, [WidgetType.DonationGoal]: { name: $t('Tip Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a tip goal to encourage your viewers to contribute toward something.'), demoVideo: false, demoFilename: 'source-donation-goal.gif', supportList: [$t('Tips')], icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.FollowerGoal]: { name: platform === 'youtube' ? $t('Subscription Goal') : $t('Follower Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a follower goal for your viewers to help you reach.'), demoVideo: false, demoFilename: 'source-follower-goal.png', platforms: new Set(['twitch', 'facebook', 'youtube', 'trovo']), supportList: [ - $t('Twitch Follows'), - $t('Facebook Follows'), + $t('Twitch Followers'), $t('YouTube Subscribers'), - $t('Trovo Follows'), + $t('Facebook Followers'), + $t('Trovo Followers'), ], icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.SubGoal]: { name: platform === 'youtube' ? $t('Member Goal') : $t('Subscription Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a subscriber goal for your viewers to help you reach.'), demoVideo: false, demoFilename: 'source-follower-goal.png', - supportList: [$t('Twitch Subscribers'), $t('YouTube Members')], + supportList: [ + $t('Twitch Subscribers'), + $t('YouTube Members'), + $t('Facebook Supporters'), + $t('Trovo Subscribers'), + ], platforms: new Set(['twitch', 'youtube']), icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.BitGoal]: { name: $t('Bit Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a bit goal for your viewers to help you reach.'), demoVideo: false, demoFilename: 'source-bit-goal.png', supportList: [$t('Twitch Bits')], platforms: new Set(['twitch']), icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.StarsGoal]: { name: $t('Stars Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a stars goal for your viewers to help you reach.'), demoVideo: false, demoFilename: 'source-bit-goal.png', supportList: [$t('Facebook Stars')], platforms: new Set(['facebook']), icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.SupporterGoal]: { name: $t('Supporter Goal'), @@ -602,132 +625,166 @@ export const WidgetDisplayData = (platform?: string): { [x: number]: IWidgetDisp supportList: [$t('Facebook Supporters')], platforms: new Set(['facebook']), icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.CharityGoal]: { name: $t('Streamlabs Charity Donation Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a charity donation goal for your viewers to help you reach.'), demoVideo: false, demoFilename: 'source-charity-goal.gif', supportList: [$t('Streamlabs Charity Donations')], icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.SuperchatGoal]: { name: $t('Superchat Goal'), - description: $t('Set a goal for your viewers to help you reach.'), + description: $t('Set a super chat goal for your viewers to help you reach.'), demoVideo: false, demoFilename: 'source-follower-goal.png', supportList: [$t('YouTube Superchats')], platforms: new Set(['youtube']), icon: 'fas fa-calendar', + group: 'goals', }, [WidgetType.DonationTicker]: { name: $t('Tip Ticker'), - description: $t('Show off your most recent donations to your viewers.'), + // TODO: show/display/showcase, "show off" is just so... + description: $t('Show off the most recent tips from your viewers'), demoVideo: false, demoFilename: 'source-tip-ticker.png', - supportList: [$t('Donations')], + supportList: [$t('Tips')], icon: 'fas fa-ellipsis-h', + group: 'flair', }, [WidgetType.ChatBox]: { name: $t('Chat Box'), - description: $t("Include your channel's chat into your stream."), + description: $t('Display your chat on stream and set up sound notifications for new chats.'), + shortDesc: $t('Display chatters on screen'), demoVideo: false, demoFilename: 'source-chatbox.png', supportList: [$t('Twitch chat'), $t('YouTube chat'), $t('Facebook chat'), $t('Trovo chat')], icon: 'fas fa-comments', + group: 'essential', }, [WidgetType.EventList]: { name: $t('Event List'), - description: $t("Include your channel's most recent events into your stream."), + description: $t("Display your channel's most recent events on stream."), demoVideo: false, demoFilename: 'source-eventlist.png', supportList: [ - $t('Donations'), - $t('Subscriptions'), - $t('Follows'), + $t('Tips'), + $t('Subscribers'), + $t('Followers'), $t('Bits'), - $t('Redemptions'), + $t('Super Chats'), + $t('Supporters'), + $t('Charity donations'), + $t('Platform support: Twitch, YouTube, Facebook, Trovo'), ], icon: 'fas fa-th-list', shortDesc: $t('Display recent events'), + group: 'essential', }, [WidgetType.TipJar]: { name: $t('The Jar'), - description: $t('The jar that catches bits, tips, and more.'), + description: $t('A jar that catches bits, tips, subs and more.'), demoVideo: false, demoFilename: 'source-jar.png', - supportList: [$t('Donations'), $t('Subscriptions'), $t('Follows'), $t('Bits')], + supportList: [ + $t('Tips'), + $t('Subscriptions'), + $t('Follows'), + $t('Bits'), + $t('Platform support: Twitch, YouTube, Facebook, Trovo'), + ], icon: 'fas fa-beer', + group: 'interactive', }, [WidgetType.ViewerCount]: { name: $t('Viewer Count'), - description: $t('Show off your viewers from multiple platforms.'), + description: $t('Show off the count of live viewers by platform.'), demoVideo: false, demoFilename: 'source-viewer-count.png', supportList: ['YouTube', 'Twitch', 'Facebook', 'Trovo'], icon: 'fas fa-eye', + group: 'essential', }, [WidgetType.StreamBoss]: { name: $t('Stream Boss'), - description: $t('Battle with bits to be the boss of the stream!'), + description: $t( + 'Let your viewers duke it out by subbing, tipping and more to become the Stream Boss.', + ), demoVideo: false, demoFilename: 'source-streamboss.png', - supportList: [$t('Twitch Bits')], + supportList: [$t('Twitch Bits'), $t('Platform support: Twitch, YouTube, Facebook, Trovo')], icon: 'fas fa-gavel', + group: 'interactive', }, [WidgetType.Credits]: { name: $t('Credits'), - description: $t('Rolling credits to play at the end of your stream.'), + description: $t( + 'Roll credits at the end of your broadcast to thank supporters for tipping, following, subbing, modding and more.', + ), demoVideo: false, demoFilename: 'source-credits.png', - supportList: [$t('New Followers'), $t('New Subscribers'), $t('Cheers'), $t('Donations')], + supportList: [ + $t('New Followers'), + $t('New Subscribers'), + $t('Cheers'), + $t('Tips'), + $t('Platform support: Twitch, YouTube, Facebook, Trovo'), + ], platforms: new Set(['twitch', 'youtube']), icon: 'fas fa-align-center', + group: 'flair', }, [WidgetType.SponsorBanner]: { name: $t('Sponsor Banner'), description: $t( - 'Set up a sponsor banner to be able to edit (add, remove, update) rotating sponsor logos on streamer channel.', + 'Show off rotating media to display your socials, sponsorships, CTAs or just cute animations.', ), demoVideo: false, demoFilename: 'source-sponsor-banner.png', supportList: [$t('The streamer manually adds images of sponsors.')], icon: 'fas fa-heart', + group: 'flair', }, [WidgetType.SpinWheel]: { name: $t('Spin Wheel'), - description: $t('Spin the wheel to make a decision.'), + description: $t('Let your viewers spin a custom spin wheel to win prizes or incentives.'), demoVideo: false, demoFilename: 'source-wheel.png', supportList: [$t('The streamer manually triggers a spin anytime while they are live.')], icon: 'fas fa-chart-pie', + group: 'interactive', }, [WidgetType.MediaShare]: { name: $t('Media Share'), - description: $t('Allow your viewers to donate to share media on your stream.'), + description: $t('Allow your viewers to tip to share videos on your stream.'), demoVideo: false, demoFilename: 'media.png', supportList: [], icon: 'icon-share', + group: 'interactive', }, [WidgetType.Poll]: { name: $t('Poll'), description: $t('Let your viewers vote on a result'), demoVideo: false, demoFilename: 'poll.png', - supportList: [], + supportList: [$t('Twitch'), $t('YouTube')], icon: 'icon-text-align-left', + group: 'interactive', }, [WidgetType.EmoteWall]: { name: $t('Emote Wall'), - description: $t( - 'Display and animate emotes that are seen in chat, improving chat participation via positive feedback.', - ), + description: $t('Display and animate emotes from chat, improving chat participation.'), demoVideo: false, demoFilename: 'emote-wall.gif', supportList: [], platforms: new Set(['twitch']), icon: 'icon-smile', + group: 'interactive', }, [WidgetType.ChatHighlight]: { name: $t('Chat Highlight'), @@ -737,15 +794,17 @@ export const WidgetDisplayData = (platform?: string): { [x: number]: IWidgetDisp supportList: [], platforms: new Set(['twitch']), icon: 'icon-community', + group: 'interactive', }, [WidgetType.GameWidget]: { name: $t('Game Widget'), - description: $t('Let your viewers play a game in chat'), + description: $t('Allow your chat to play games together while you are live.'), demoVideo: false, demoFilename: 'game-widget.png', supportList: [], platforms: new Set(['twitch', 'youtube']), icon: 'icon-face-masks', + group: 'interactive', }, [WidgetType.CustomWidget]: { name: $t('Custom Widget'), @@ -754,5 +813,6 @@ export const WidgetDisplayData = (platform?: string): { [x: number]: IWidgetDisp demoFilename: '', // do not show an image supportList: [], icon: 'icon-developer', + group: 'flair', }, }); diff --git a/test/helpers/modules/sources.ts b/test/helpers/modules/sources.ts index b3a4ead79e62..56d7edd6f6b8 100644 --- a/test/helpers/modules/sources.ts +++ b/test/helpers/modules/sources.ts @@ -60,7 +60,9 @@ export async function addSource( await clickAddSource(); await focusChild(); - await waitForDisplayed('span=Essential Sources'); + await waitForDisplayed('[data-testid=essential-sources]'); + // to ensure async rendering of panels don't cause issues + await waitForDisplayed('[data-testid=widget-sources]'); await click(`[data-name="${type}"]`); await clickButton('Add Source'); diff --git a/test/regular/sources.ts b/test/regular/sources.ts index c6ef4168c163..014fce4ce057 100644 --- a/test/regular/sources.ts +++ b/test/regular/sources.ts @@ -264,9 +264,9 @@ test('Create/Remove Game Capture and view Source Properties', async t => { }); test('Create/Remove Video Capture Device and view Source Properties', async t => { - const sourceName = 'Video Capture Device'; + const sourceName = 'Video Capture'; - await addSource('Video Capture Device', sourceName); + await addSource('Video Capture', sourceName); await focusMain(); await selectSource(sourceName);