Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Highlighter: ui updates installing - uninstalling highlighter #5282

Merged
merged 17 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions app/components-react/highlighter/ClipsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function ClipsView({
emitSetView: (data: IViewState) => void;
}) {
const { HighlighterService, UsageStatisticsService, IncrementalRolloutService } = Services;
const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled(
const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled(
EAvailableFeatures.aiHighlighter,
);
const clipsAmount = useVuex(() => HighlighterService.views.clips.length);
Expand Down Expand Up @@ -215,7 +215,7 @@ export default function ClipsView({
}}
/>
{streamId &&
aiHighlighterEnabled &&
aiHighlighterFeatureEnabled &&
HighlighterService.getClips(HighlighterService.views.clips, props.id)
.filter(clip => clip.source === 'AiClip')
.every(clip => (clip as IAiClip).aiInfo.metadata?.round) && (
Expand Down
42 changes: 32 additions & 10 deletions app/components-react/highlighter/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function SettingsView({
HighlighterService,
IncrementalRolloutService,
} = Services;
const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled(
const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled(
EAvailableFeatures.aiHighlighter,
);
const [hotkey, setHotkey] = useState<IHotkey | null>(null);
Expand All @@ -38,6 +38,7 @@ export default function SettingsView({
settingsValues: SettingsService.views.values,
isStreaming: StreamingService.isStreaming,
useAiHighlighter: HighlighterService.views.useAiHighlighter,
highlighterVersion: HighlighterService.views.highlighterVersion,
}));

const correctlyConfigured =
Expand Down Expand Up @@ -124,7 +125,7 @@ export default function SettingsView({
</p>
</div>
<div style={{ display: 'flex', gap: '16px' }}>
{aiHighlighterEnabled && (
{aiHighlighterFeatureEnabled && (
<Button type="primary" onClick={() => emitSetView({ view: EHighlighterView.STREAM })}>
{$t('Stream Highlights')}
</Button>
Expand All @@ -139,7 +140,7 @@ export default function SettingsView({
<Scrollable style={{ flexGrow: 1, padding: '20px 20px 20px 20px', width: '100%' }}>
<div className={styles.innerScrollWrapper}>
<div className={styles.cardWrapper}>
{aiHighlighterEnabled && (
{aiHighlighterFeatureEnabled && (
<div className={styles.highlighterCard}>
<div className={styles.cardHeaderbarWrapper}>
<div className={styles.cardHeaderbar}>
Expand All @@ -152,21 +153,42 @@ export default function SettingsView({
<p style={{ margin: 0 }}>
{$t(
'Automatically capture the best moments from your livestream and turn them into a highlight video.',
)}{' '}
{v.highlighterVersion !== '' && (
<span>
{$t(
'The AI Highlighter App can be managed in the Apps Manager tab or in Settings > Installed apps.',
)}
</span>
)}
</p>

<SwitchInput
style={{ margin: 0, marginLeft: '-10px' }}
size="default"
value={v.useAiHighlighter}
onChange={toggleUseAiHighlighter}
/>
{v.highlighterVersion !== '' ? (
<SwitchInput
style={{ margin: 0, marginLeft: '-10px' }}
size="default"
value={v.useAiHighlighter}
onChange={toggleUseAiHighlighter}
/>
) : (
<Button
style={{ width: 'fit-content' }}
type="primary"
onClick={() => {
HighlighterService.actions.installAiHighlighter(true);
}}
>
{$t('Install AI Highlighter App')}
</Button>
)}
<div className={styles.recommendedCorner}>{$t('Recommended')}</div>
</div>
)}
<div className={styles.manualCard}>
<h3 className={styles.cardHeaderTitle}>
{aiHighlighterEnabled ? 'Or use the manual highlighter ' : 'Manual highlighter'}
{aiHighlighterFeatureEnabled
? $t('Or, use the built-in manual highlighter')
: $t('Built-in manual highlighter')}
</h3>
<p>
{$t(
Expand Down
33 changes: 20 additions & 13 deletions app/components-react/highlighter/StreamView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default function StreamView({ emitSetView }: { emitSetView: (data: IViewS
exportInfo: HighlighterService.views.exportInfo,
error: HighlighterService.views.error,
uploadInfo: HighlighterService.views.uploadInfo,
highlighterVersion: HighlighterService.views.highlighterVersion,
}));

// Below is only used because useVueX doesnt work as expected
Expand Down Expand Up @@ -205,6 +206,8 @@ export default function StreamView({ emitSetView }: { emitSetView: (data: IViewS
}

function onDrop(e: React.DragEvent<HTMLDivElement>) {
if (v.highlighterVersion === '') return;

const extensions = SUPPORTED_FILE_TYPES.map(e => `.${e}`);
const files: string[] = [];
let fi = e.dataTransfer.files.length;
Expand Down Expand Up @@ -237,18 +240,20 @@ export default function StreamView({ emitSetView }: { emitSetView: (data: IViewS
<h1 style={{ margin: 0 }}>{$t('My Stream Highlights')}</h1>
</div>
<div style={{ display: 'flex', gap: '16px' }}>
<div
className={styles.uploadWrapper}
style={{
opacity: aiDetectionInProgress ? '0.7' : '1',
cursor: aiDetectionInProgress ? 'not-allowed' : 'pointer',
}}
onClick={() => !aiDetectionInProgress && setShowModal({ type: 'upload' })}
>
<FortniteIcon />
{$t('Select your Fortnite recording')}
<Button disabled={aiDetectionInProgress === true}>{$t('Import')}</Button>
</div>
{v.highlighterVersion !== '' && (
<div
className={styles.uploadWrapper}
style={{
opacity: aiDetectionInProgress ? '0.7' : '1',
cursor: aiDetectionInProgress ? 'not-allowed' : 'pointer',
}}
onClick={() => !aiDetectionInProgress && setShowModal({ type: 'upload' })}
>
<FortniteIcon />
{$t('Select your Fortnite recording')}
<Button disabled={aiDetectionInProgress === true}>{$t('Import')}</Button>
</div>
)}
<Button onClick={() => emitSetView({ view: EHighlighterView.SETTINGS })}>
{$t('Settings')}
</Button>
Expand Down Expand Up @@ -297,7 +302,9 @@ export default function StreamView({ emitSetView }: { emitSetView: (data: IViewS
keyboard={false}
>
{!!v.error && <Alert message={v.error} type="error" showIcon />}
{showModal?.type === 'upload' && <ImportStreamModal close={closeModal} />}
{showModal?.type === 'upload' && v.highlighterVersion !== '' && (
<ImportStreamModal close={closeModal} />
)}
{showModal?.type === 'export' && <ExportModal close={closeModal} streamId={showModal.id} />}
{showModal?.type === 'preview' && (
<PreviewModal close={closeModal} streamId={showModal.id} />
Expand Down
16 changes: 10 additions & 6 deletions app/components-react/pages/Highlighter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EAvailableFeatures } from 'services/incremental-rollout';

export default function Highlighter(props: { params?: { view: string } }) {
const { HighlighterService, IncrementalRolloutService } = Services;
const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled(
const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled(
EAvailableFeatures.aiHighlighter,
);
const v = useVuex(() => ({
Expand All @@ -24,7 +24,11 @@ export default function Highlighter(props: { params?: { view: string } }) {

let initialViewState: IViewState;

if (v.streamAmount > 0 && v.clipsAmount > 0 && aiHighlighterEnabled) {
if (props.params?.view) {
const view =
props.params?.view === 'settings' ? EHighlighterView.SETTINGS : EHighlighterView.STREAM;
initialViewState = { view };
} else if (v.streamAmount > 0 && v.clipsAmount > 0 && aiHighlighterFeatureEnabled) {
initialViewState = { view: EHighlighterView.STREAM };
} else if (v.clipsAmount > 0) {
initialViewState = { view: EHighlighterView.CLIPS, id: undefined };
Expand All @@ -37,7 +41,7 @@ export default function Highlighter(props: { params?: { view: string } }) {
async function shouldUpdate() {
if (!HighlighterService.aiHighlighterUpdater) return false;
const versionAvailable = await HighlighterService.aiHighlighterUpdater.isNewVersionAvailable();
return versionAvailable && aiHighlighterEnabled && v.useAiHighlighter;
return versionAvailable && aiHighlighterFeatureEnabled && v.useAiHighlighter;
}

shouldUpdate().then(shouldUpdate => {
Expand All @@ -58,7 +62,7 @@ export default function Highlighter(props: { params?: { view: string } }) {
case EHighlighterView.STREAM:
return (
<>
{aiHighlighterEnabled && updaterModal}
{aiHighlighterFeatureEnabled && updaterModal}
<StreamView
emitSetView={data => {
setViewFromEmit(data);
Expand All @@ -69,7 +73,7 @@ export default function Highlighter(props: { params?: { view: string } }) {
case EHighlighterView.CLIPS:
return (
<>
{aiHighlighterEnabled && updaterModal}
{aiHighlighterFeatureEnabled && updaterModal}
<ClipsView
emitSetView={data => {
setViewFromEmit(data);
Expand All @@ -86,7 +90,7 @@ export default function Highlighter(props: { params?: { view: string } }) {
default:
return (
<>
{aiHighlighterEnabled && updaterModal}
{aiHighlighterFeatureEnabled && updaterModal}
<SettingsView
close={() => {
HighlighterService.actions.dismissTutorial();
Expand Down
35 changes: 35 additions & 0 deletions app/components-react/pages/PlatformAppStore.m.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import '../../styles/index.less';

.browser-view {
position: absolute;
top: 0;
right: 0;
left: 0;
}

.other-installed-apps-wrapper {
display: flex;
gap: 16px;
position: absolute;
bottom: 0;
height: 72px;
align-items: center;
padding: 8px;
}

.other-app-wrapper {
border-radius: 8px;
border: 1px solid #30383d;
display: flex;
gap: 16px;
padding: 8px;
padding-left: 16px;
justify-content: space-between;
align-items: center;
}

.text-wrapper {
display: flex;
gap: 8px;
align-items: center;
}
80 changes: 72 additions & 8 deletions app/components-react/pages/PlatformAppStore.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import Utils from 'services/utils';
import urlLib from 'url';
import BrowserView from 'components-react/shared/BrowserView';
import { GuestApiHandler } from 'util/guest-api-handler';
import * as remote from '@electron/remote';
import { Services } from 'components-react/service-provider';
import { Button } from 'antd';
import { EMenuItemKey } from 'services/side-nav';
import { $t } from 'services/i18n';
import styles from './PlatformAppStore.m.less';

export default function PlatformAppStore(p: { params: { appId?: string; type?: string } }) {
const { UserService, PlatformAppsService, PlatformAppStoreService, NavigationService } = Services;
const {
UserService,
PlatformAppsService,
PlatformAppStoreService,
NavigationService,
HighlighterService,
} = Services;
const [highlighterInstalled, setHighlighterInstalled] = useState<boolean>(
HighlighterService.views.highlighterVersion !== '',
);
const [platformAppsUrl, setPlatformAppsUrl] = useState('');
const [currentUrl, setCurrentUrl] = useState<string>('');

useEffect(() => {
async function getPlatformAppsUrl() {
Expand Down Expand Up @@ -69,11 +83,61 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s

if (!platformAppsUrl) return <></>;
return (
<BrowserView
style={{ position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 }}
src={platformAppsUrl}
onReady={onBrowserViewReady}
enableGuestApi
/>
<>
<BrowserView
className={styles.browserView}
style={{
height: `calc(100% - ${
currentUrl.includes('installed-apps') &&
HighlighterService.views.highlighterVersion !== ''
? '72'
: '0'
}px)`,
}}
src={platformAppsUrl}
onReady={onBrowserViewReady}
enableGuestApi
emitUrlChange={url => {
setCurrentUrl(url);
}}
/>
{currentUrl.includes('installed-apps') && highlighterInstalled && (
<div className={styles.otherInstalledAppsWrapper}>
<div>{$t('Other installed apps:')}</div>
<div className={styles.otherAppWrapper}>
<div className={styles.textWrapper}>
<h3 style={{ margin: 0 }}>AI Highlighter</h3>
<p style={{ opacity: 0.3, margin: 0 }}>by Streamlabs</p>
</div>
<div style={{ display: 'flex', gap: '8px' }}>
<Button
size="middle"
type="default"
onClick={() => {
setHighlighterInstalled(false);
HighlighterService.uninstallAiHighlighter();
}}
>
{$t('Uninstall')}
</Button>

<Button
size="middle"
type="primary"
onClick={() => {
NavigationService.actions.navigate(
'Highlighter',
{ view: 'settings' },
EMenuItemKey.Highlighter,
);
}}
>
{$t('Open')}
</Button>
</div>
</div>
</div>
)}
</>
);
}
Loading
Loading