Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
fcc690c
Port LiveDock Component
gettinToasty Dec 18, 2023
677b054
Fix import errors and tabs
gettinToasty Dec 18, 2023
d7ac74b
Add to ReactComponentList
gettinToasty Dec 18, 2023
e21b426
Fix strict nulls
gettinToasty Dec 18, 2023
967c98b
Fix import error
gettinToasty Dec 19, 2023
4be201a
Fix reactivity issues
gettinToasty Dec 19, 2023
24067d1
Fix positioning and styling
gettinToasty Dec 19, 2023
ce276db
Begin 1 to 1 port
gettinToasty Dec 19, 2023
4e7aaa1
Define variables and functions
gettinToasty Jan 8, 2024
5886f13
Add missing classNames
gettinToasty Jan 8, 2024
19a03fb
Fix non-modal redlines
gettinToasty Jan 8, 2024
019812a
Add modals to react
gettinToasty Jan 9, 2024
16f70db
Import components
gettinToasty Jan 9, 2024
8880d0a
Fix rendering bugs
gettinToasty Jan 9, 2024
36dc9db
Add misssing classes
gettinToasty Jan 10, 2024
79a5563
Change scope of windowWidth
gettinToasty Jan 10, 2024
c32edc2
Remove props from main window
gettinToasty Jan 19, 2024
a850c3c
Move to native react rendering
gettinToasty Jan 23, 2024
a5e7821
Revert to Vue wrapper component
gettinToasty Jan 24, 2024
8cd5bba
Merge branch 'master' into sb_port_livedock
gettinToasty Jan 30, 2024
edbf015
Merge branch 'sb_port_livedock' into sb_port_main_window
gettinToasty Jan 30, 2024
130b661
Fix stale style blockers
gettinToasty Feb 5, 2024
e6a836c
Merge branch 'master' into sb_port_main_window
gettinToasty Feb 9, 2024
fea5cc4
Add css aliases
gettinToasty Feb 9, 2024
c57626c
Merge branch 'master' into sb_port_main_window
gettinToasty Feb 15, 2024
64dc313
Begin livedock restructure
gettinToasty Feb 22, 2024
861f572
Fix rendering livedock
gettinToasty Feb 22, 2024
5be1d3d
Merge branch 'master' into sb_port_main_window
gettinToasty Mar 20, 2024
2be9d84
Merge branch 'master' into sb_port_main_window
gettinToasty Apr 5, 2024
95b343e
Use Realm
gettinToasty Apr 9, 2024
e601b61
Merge branch 'master' into sb_port_main_window
Jun 5, 2024
82cca15
Fix compilation error
Jun 5, 2024
20c9e84
Fix Livedock breaking render
Jun 5, 2024
1a95e10
Use realm props binding
Jun 5, 2024
9603e10
fix: pass theme to `Loader`
blackxored Jul 22, 2024
8fb450a
chore: uncoment main rendering
blackxored Jul 22, 2024
cd4baa7
fix(main): restore wrappers, should fix resizing behavior
blackxored Jul 24, 2024
3c95157
Merge branch 'master' into sb_port_main_window
gettinToasty Sep 11, 2024
050f4ae
Re-add ChatTabs
gettinToasty Sep 11, 2024
4edb751
Merge branch 'sb_port_main_window' of https://github.com/stream-labs/…
gettinToasty Sep 11, 2024
5999165
Merge branch 'master' into sb_port_main_window
gettinToasty Sep 25, 2024
ca1fe13
Fix index typing for customization service
gettinToasty Sep 25, 2024
e2c562d
Fix typing in Studio.tsx
gettinToasty Sep 25, 2024
60c5637
Merge branch 'master' into sb_port_main_window
gettinToasty Jan 14, 2025
42bee56
Fix resizing problem
gettinToasty Jan 16, 2025
fc697f7
Fix css on resize bar
gettinToasty Feb 4, 2025
2325a33
Merge branch 'master' into sb_port_main_window
gettinToasty Feb 14, 2025
f3c0699
Fix footer clipping at small sizes
gettinToasty Feb 14, 2025
28d130e
Fix livedock collapse
gettinToasty Feb 14, 2025
4a296a7
Refactor resizebar into main window
gettinToasty Mar 3, 2025
882b515
Get resize working
gettinToasty Mar 4, 2025
78ff7ee
Refactor to add collapse
gettinToasty Mar 4, 2025
060778a
Fix dock resizing
gettinToasty Mar 12, 2025
149fe58
Add perf optimizations
gettinToasty Mar 12, 2025
70cd33f
Merge branch 'master' into sb_port_main_window
gettinToasty Mar 18, 2025
b1b3937
Add animation
gettinToasty Mar 19, 2025
77e3540
Fix non-editor size weirdness
gettinToasty Mar 21, 2025
dd961b9
Fix chat disappearing
gettinToasty Mar 21, 2025
e6bf4e4
Merge branch 'master' into sb_port_main_window
gettinToasty Mar 24, 2025
e58ab96
Merge branch 'master' into sb_port_main_window
gettinToasty Apr 3, 2025
533890e
Merge branch 'master' into sb_port_main_window
gettinToasty Apr 23, 2025
573ae5a
Merge branch 'sb_port_main_window' of https://github.com/stream-labs/…
gettinToasty Apr 23, 2025
80b05ab
Fix strict nulls issue
gettinToasty Apr 23, 2025
4e7caee
Fix strict nulls again
gettinToasty Apr 23, 2025
699e375
Fix strict nulls last time pls
gettinToasty Apr 23, 2025
2503751
Merge branch 'master' into sb_port_main_window
gettinToasty May 5, 2025
985d41b
Address CR
gettinToasty May 5, 2025
84a6ba7
Clean up platform logic dealing with lviedock features
gettinToasty May 5, 2025
ab3b6fb
Merge branch 'master' into sb_livedock_refactor
gettinToasty Aug 21, 2025
ad65657
Fix tests
gettinToasty Aug 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 24 additions & 29 deletions app/components-react/root/LiveDock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import pick from 'lodash/pick';
import { initStore, useController } from 'components-react/hooks/zustand';
import { EStreamingState } from 'services/streaming';
import { EAppPageSlot, ILoadedApp } from 'services/platform-apps';
import { getPlatformService, TPlatform } from 'services/platforms';
import { getPlatformService, TLiveDockFeature, TPlatform } from 'services/platforms';
import { $t } from 'services/i18n';
import { Services } from '../service-provider';
import Chat from './Chat';
Expand All @@ -22,11 +22,6 @@ const LiveDockCtx = React.createContext<LiveDockController | null>(null);

class LiveDockController {
private streamingService = Services.StreamingService;
private youtubeService = Services.YoutubeService;
private facebookService = Services.FacebookService;
private trovoService = Services.TrovoService;
private kickService = Services.KickService;
private tiktokService = Services.TikTokService;
private userService = Services.UserService;
private customizationService = Services.CustomizationService;
private platformAppsService = Services.PlatformAppsService;
Expand Down Expand Up @@ -80,6 +75,11 @@ class LiveDockController {
return this.userService.platform?.type;
}

get platformService() {
if (!this.platform) return;
return getPlatformService(this.platform);
}

get offlineImageSrc() {
const mode = this.customizationService.isDarkTheme ? 'night' : 'day';
return $i(`images/sleeping-kevin-${mode}.png`);
Expand Down Expand Up @@ -189,21 +189,14 @@ class LiveDockController {
}

openPlatformStream() {
let url = '';
if (this.platform === 'youtube') url = this.youtubeService.streamPageUrl;
if (this.platform === 'facebook') url = this.facebookService.streamPageUrl;
if (this.platform === 'trovo') url = this.trovoService.streamPageUrl;
if (this.platform === 'kick') url = this.kickService.streamPageUrl;
if (this.platform === 'tiktok') url = this.tiktokService.streamPageUrl;
const url = this.platformService?.streamPageUrl;
if (!url) return;
remote.shell.openExternal(url);
}

openPlatformDash() {
let url = '';
if (this.platform === 'youtube') url = this.youtubeService.dashboardUrl;
if (this.platform === 'facebook') url = this.facebookService.streamDashboardUrl;
if (this.platform === 'tiktok') url = this.tiktokService.dashboardUrl;
if (this.platform === 'kick') url = this.kickService.dashboardUrl;
const url = this.platformService?.dashboardUrl;
if (!url) return;
remote.shell.openExternal(url);
}

Expand Down Expand Up @@ -239,6 +232,10 @@ class LiveDockController {
showMultistreamChatInfo() {
this.chatService.actions.showMultistreamChatWindow();
}

hasLiveDockFeature(feature: TLiveDockFeature) {
return this.platformService?.hasLiveDockFeature(feature);
}
}

export default function LiveDockWithContext() {
Expand All @@ -258,6 +255,7 @@ function LiveDock() {

const {
isPlatform,
hasLiveDockFeature,
isStreaming,
isRestreaming,
hasChatTabs,
Expand All @@ -267,7 +265,6 @@ function LiveDock() {
currentViewers,
pageSlot,
liveText,
isPopOutAllowed,
streamingStatus,
} = useVuex(() =>
pick(ctrl, [
Expand All @@ -281,7 +278,7 @@ function LiveDock() {
'pageSlot',
'currentViewers',
'liveText',
'isPopOutAllowed',
'hasLiveDockFeature',
'streamingStatus',
]),
);
Expand Down Expand Up @@ -384,7 +381,7 @@ function LiveDock() {
<i onClick={() => ctrl.showEditStreamInfo()} className="icon-edit" />
</Tooltip>
)}
{isPlatform(['youtube', 'facebook', 'trovo', 'tiktok', 'kick']) && isStreaming && (
{hasLiveDockFeature('view-stream') && isStreaming && (
<Tooltip
title={$t('View your live stream in a web browser')}
placement="right"
Expand All @@ -393,8 +390,7 @@ function LiveDock() {
<i onClick={() => ctrl.openPlatformStream()} className="icon-studio" />
</Tooltip>
)}
{isStreaming && <ShareStreamLink />}
{isPlatform(['youtube', 'facebook', 'tiktok']) && isStreaming && (
{hasLiveDockFeature('dashboard') && isStreaming && (
<Tooltip
title={$t('Go to Live Dashboard')}
placement="right"
Expand All @@ -405,16 +401,16 @@ function LiveDock() {
)}
</div>
<div className="flex">
{(isPlatform(['twitch', 'trovo', 'facebook', 'kick']) ||
(isPlatform(['youtube', 'twitter']) && isStreaming) ||
(isPlatform(['tiktok']) && isRestreaming)) && (
{(hasLiveDockFeature('refresh-chat') ||
(hasLiveDockFeature('refresh-chat-streaming') && isStreaming) ||
(hasLiveDockFeature('refresh-chat-restreaming') && isRestreaming)) && (
<a onClick={() => ctrl.refreshChat()}>{$t('Refresh Chat')}</a>
)}
</div>
</div>
{!hideStyleBlockers &&
(isPlatform(['twitch', 'trovo']) ||
(isStreaming && isPlatform(['youtube', 'facebook', 'twitter', 'tiktok', 'kick']))) && (
(hasLiveDockFeature('chat-offline') ||
(isStreaming && hasLiveDockFeature('chat-streaming'))) && (
<div className={styles.liveDockChat}>
{hasChatTabs && <ChatTabs visibleChat={visibleChat} setChat={setChat} />}
{!applicationLoading && !collapsed && chat}
Expand All @@ -429,8 +425,7 @@ function LiveDock() {
</div>
)}
</div>
{(!ctrl.platform ||
(isPlatform(['youtube', 'facebook', 'twitter', 'tiktok', 'kick']) && !isStreaming)) && (
{(!ctrl.platform || (hasLiveDockFeature('chat-streaming') && !isStreaming)) && (
<div className={cx('flex flex--center flex--column', styles.liveDockChatOffline)}>
<img className={styles.liveDockChatImgOffline} src={ctrl.offlineImageSrc} />
{!hideStyleBlockers && <span>{$t('Your chat is currently offline')}</span>}
Expand Down
8 changes: 8 additions & 0 deletions app/services/platforms/base-platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TPlatformCapability,
TStartStreamOptions,
TPlatformCapabilityMap,
TLiveDockFeature,
} from './index';
import { StreamingService } from 'services/streaming';
import { UserService } from 'services/user';
Expand Down Expand Up @@ -41,11 +42,18 @@ export abstract class BasePlatformService<T extends IPlatformState> extends Stat

abstract capabilities: Set<TPlatformCapability>;

abstract liveDockFeatures: Set<TLiveDockFeature>;

@ExecuteInCurrentWindow()
hasCapability<T extends TPlatformCapability>(capability: T): this is TPlatformCapabilityMap[T] {
return this.capabilities.has(capability);
}

@ExecuteInCurrentWindow()
hasLiveDockFeature(feature: TLiveDockFeature) {
return this.liveDockFeatures.has(feature);
}

get mergeUrl() {
const host = this.hostsService.streamlabs;
const token = this.userService.apiToken;
Expand Down
17 changes: 15 additions & 2 deletions app/services/platforms/facebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import moment from 'moment';
import flatten from 'lodash/flatten';
import * as remote from '@electron/remote';
import { mutation, InheritMutations, ViewHandler } from '../core/stateful-service';
import { IPlatformService, IGame, TPlatformCapability, IPlatformRequest, IPlatformState } from '.';
import {
IPlatformService,
IGame,
TPlatformCapability,
IPlatformRequest,
IPlatformState,
TLiveDockFeature,
} from '.';
import { HostsService } from 'services/hosts';
import { Inject } from 'services/core/injector';
import { authorizedHeaders } from 'util/requests';
Expand Down Expand Up @@ -169,6 +176,12 @@ export class FacebookService
'themes',
'viewerCount',
]);
readonly liveDockFeatures = new Set<TLiveDockFeature>([
'chat-streaming',
'refresh-chat',
'dashboard',
'view-stream',
]);

authWindowOptions: Electron.BrowserWindowConstructorOptions = { width: 800, height: 800 };

Expand Down Expand Up @@ -248,7 +261,7 @@ export class FacebookService
return this.state.streamPageUrl;
}

get streamDashboardUrl(): string {
get dashboardUrl(): string {
return this.state.streamDashboardUrl;
}

Expand Down
11 changes: 11 additions & 0 deletions app/services/platforms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export interface IPlatformState {
export interface IPlatformService {
capabilities: Set<TPlatformCapability>;
hasCapability<T extends TPlatformCapability>(capability: T): this is TPlatformCapabilityMap[T];
hasLiveDockFeature(feature: TLiveDockFeature): boolean;

authWindowOptions: Electron.BrowserWindowConstructorOptions;

Expand Down Expand Up @@ -222,6 +223,7 @@ export interface IPlatformService {
readonly mergeUrl: string;
readonly streamPageUrl: string;
readonly chatUrl: string;
readonly dashboardUrl?: string;

/**
* the list of widgets supported by the platform
Expand All @@ -245,6 +247,15 @@ export interface IUserInfo {
username?: string;
}

export type TLiveDockFeature =
| 'chat-offline'
| 'chat-streaming'
| 'dashboard'
| 'view-stream'
| 'refresh-chat'
| 'refresh-chat-streaming'
| 'refresh-chat-restreaming';

export enum EPlatform {
Twitch = 'twitch',
YouTube = 'youtube',
Expand Down
2 changes: 2 additions & 0 deletions app/services/platforms/instagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
TPlatformCapability,
TStartStreamOptions,
EPlatformCallResult,
TLiveDockFeature,
} from '.';
import { BasePlatformService } from './base-platform';
import { IGoLiveSettings } from 'services/streaming';
Expand Down Expand Up @@ -40,6 +41,7 @@ export class InstagramService
readonly platform = 'instagram';
readonly displayName = 'Instagram';
readonly capabilities = new Set<TPlatformCapability>(['resolutionPreset', 'title']);
readonly liveDockFeatures = new Set<TLiveDockFeature>();

static initialState: IInstagramServiceState = {
...BasePlatformService.initialState,
Expand Down
6 changes: 6 additions & 0 deletions app/services/platforms/kick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IPlatformState,
TPlatform,
TPlatformCapability,
TLiveDockFeature,
} from './index';
import { authorizedHeaders, jfetch } from '../../util/requests';
import { StreamError, throwStreamError } from '../streaming/stream-error';
Expand Down Expand Up @@ -123,6 +124,11 @@ export class KickService
readonly platform = 'kick';
readonly displayName = 'Kick';
readonly capabilities = new Set<TPlatformCapability>(['title', 'chat', 'game', 'viewerCount']);
readonly liveDockFeatures = new Set<TLiveDockFeature>([
'view-stream',
'refresh-chat',
'chat-streaming',
]);

authWindowOptions: Electron.BrowserWindowConstructorOptions = {
width: 600,
Expand Down
7 changes: 7 additions & 0 deletions app/services/platforms/tiktok.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IPlatformService,
IPlatformState,
TPlatformCapability,
TLiveDockFeature,
} from './index';
import { authorizedHeaders, jfetch } from '../../util/requests';
import {
Expand Down Expand Up @@ -119,6 +120,12 @@ export class TikTokService
readonly platform = 'tiktok';
readonly displayName = 'TikTok';
readonly capabilities = new Set<TPlatformCapability>(['title', 'viewerCount']);
readonly liveDockFeatures = new Set<TLiveDockFeature>([
'view-stream',
'dashboard',
'refresh-chat-restreaming',
'chat-streaming',
]);

authWindowOptions: Electron.BrowserWindowConstructorOptions = {
width: 600,
Expand Down
6 changes: 6 additions & 0 deletions app/services/platforms/trovo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IPlatformService,
IPlatformState,
TPlatformCapability,
TLiveDockFeature,
} from './index';
import { authorizedHeaders, jfetch } from '../../util/requests';
import { throwStreamError } from '../streaming/stream-error';
Expand Down Expand Up @@ -62,6 +63,11 @@ export class TrovoService
'streamlabels',
'viewerCount',
]);
readonly liveDockFeatures = new Set<TLiveDockFeature>([
'chat-offline',
'refresh-chat',
'view-stream',
]);
readonly apiBase = 'https://open-api.trovo.live/openplatform';
readonly rtmpServer = 'rtmp://livepush.trovo.live/live/';
readonly platform = 'trovo';
Expand Down
3 changes: 3 additions & 0 deletions app/services/platforms/twitch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
IPlatformRequest,
IPlatformService,
IPlatformState,
TLiveDockFeature,
TPlatformCapability,
} from '.';
import { HostsService } from 'services/hosts';
Expand Down Expand Up @@ -120,6 +121,8 @@ export class TwitchService
'viewerCount',
]);

readonly liveDockFeatures = new Set<TLiveDockFeature>(['chat-offline', 'refresh-chat']);

authWindowOptions: Electron.BrowserWindowConstructorOptions = {
width: 600,
height: 800,
Expand Down
12 changes: 11 additions & 1 deletion app/services/platforms/twitter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { InheritMutations, Inject, mutation, Service } from '../core';
import { BasePlatformService } from './base-platform';
import { IPlatformRequest, IPlatformService, IPlatformState, TPlatformCapability } from './index';
import {
IPlatformRequest,
IPlatformService,
IPlatformState,
TPlatformCapability,
TLiveDockFeature,
} from './index';
import { authorizedHeaders, jfetch } from '../../util/requests';
import { throwStreamError } from '../streaming/stream-error';
import { platformAuthorizedRequest } from './utils';
Expand Down Expand Up @@ -49,6 +55,10 @@ export class TwitterPlatformService
};

readonly capabilities = new Set<TPlatformCapability>(['title', 'viewerCount']);
readonly liveDockFeatures = new Set<TLiveDockFeature>([
'refresh-chat-streaming',
'chat-streaming',
]);
readonly apiBase = 'https://api.x.com/2';
readonly domain = 'https://x.com';
readonly platform = 'twitter';
Expand Down
7 changes: 7 additions & 0 deletions app/services/platforms/youtube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EPlatformCallResult,
IPlatformRequest,
IPlatformState,
TLiveDockFeature,
} from '.';
import { Inject } from 'services/core/injector';
import { authorizedHeaders, jfetch } from 'util/requests';
Expand Down Expand Up @@ -195,6 +196,12 @@ export class YoutubeService
'viewerCount',
'dualStream',
]);
readonly liveDockFeatures = new Set<TLiveDockFeature>([
'view-stream',
'dashboard',
'refresh-chat-streaming',
'chat-streaming',
]);

static initialState: IYoutubeServiceState = {
...BasePlatformService.initialState,
Expand Down
Loading