222 lines
7.6 KiB
TypeScript
222 lines
7.6 KiB
TypeScript
/*
|
|
* SPDX-License-Identifier: GPL-3.0
|
|
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
|
*/
|
|
|
|
import "./screenSharePicker.css";
|
|
|
|
import { classes, closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
|
import { findByPropsLazy } from "@vencord/types/webpack";
|
|
import { Button, Card, Forms, Switch, Text, useState } from "@vencord/types/webpack/common";
|
|
import type { Dispatch, SetStateAction } from "react";
|
|
|
|
const StreamResolutions = ["720", "1080", "1440", "Source"] as const;
|
|
const StreamFps = ["15", "30", "60"] as const;
|
|
|
|
const WarningIconClasses = findByPropsLazy("warning", "error", "container");
|
|
|
|
export type StreamResolution = (typeof StreamResolutions)[number];
|
|
export type StreamFps = (typeof StreamFps)[number];
|
|
|
|
interface StreamSettings {
|
|
resolution: StreamResolution;
|
|
fps: StreamFps;
|
|
audio: boolean;
|
|
}
|
|
|
|
export interface StreamPick extends StreamSettings {
|
|
id: string;
|
|
}
|
|
|
|
interface Source {
|
|
id: string;
|
|
name: string;
|
|
url: string;
|
|
}
|
|
|
|
export function openScreenSharePicker(screens: Source[]) {
|
|
return new Promise<StreamPick>((resolve, reject) => {
|
|
const key = openModal(
|
|
props => (
|
|
<ModalComponent
|
|
screens={screens}
|
|
modalProps={props}
|
|
submit={resolve}
|
|
close={() => {
|
|
props.onClose();
|
|
reject("Aborted");
|
|
}}
|
|
/>
|
|
),
|
|
{
|
|
onCloseRequest() {
|
|
closeModal(key);
|
|
reject("Aborted");
|
|
}
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
function ScreenPicker({ screens, onPick }: { screens: Source[]; onPick: (id: string) => void; }) {
|
|
return (
|
|
<div className="vcd-screen-picker-grid">
|
|
{screens.map(({ id, name, url }) => (
|
|
<label key={id}>
|
|
<input type="radio" name="screen" value={id} onChange={() => onPick(id)} />
|
|
|
|
<img src={url} alt="" />
|
|
<Text variant="text-sm/normal">{name}</Text>
|
|
</label>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function StreamSettings({
|
|
source,
|
|
settings,
|
|
setSettings
|
|
}: {
|
|
source: Source;
|
|
settings: StreamSettings;
|
|
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
|
}) {
|
|
const [thumb] = useAwaiter(() => VencordDesktopNative.capturer.getLargeThumbnail(source.id), {
|
|
fallbackValue: source.url,
|
|
deps: [source.id]
|
|
});
|
|
|
|
return (
|
|
<div>
|
|
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
|
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
|
<img src={thumb} alt="" />
|
|
<Text variant="text-sm/normal">{source.name}</Text>
|
|
</Card>
|
|
|
|
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
|
|
|
<Card className="vcd-screen-picker-card">
|
|
<Card className={classes(WarningIconClasses.container, WarningIconClasses.warning, Margins.bottom8)}>
|
|
<Forms.FormText>
|
|
Resolution and Frame Rate aren't implemented for now. Locked to 720p 30fps
|
|
</Forms.FormText>
|
|
</Card>
|
|
|
|
<div className="vcd-screen-picker-quality">
|
|
<section>
|
|
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
|
<div className="vcd-screen-picker-radios">
|
|
{StreamResolutions.map(res => (
|
|
<label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}>
|
|
<Text variant="text-sm/bold">{res}</Text>
|
|
<input
|
|
type="radio"
|
|
name="resolution"
|
|
value={res}
|
|
checked={settings.resolution === res}
|
|
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
|
/>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
|
<div className="vcd-screen-picker-radios">
|
|
{StreamFps.map(fps => (
|
|
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
|
<Text variant="text-sm/bold">{fps}</Text>
|
|
<input
|
|
type="radio"
|
|
name="fps"
|
|
value={fps}
|
|
checked={settings.fps === fps}
|
|
onChange={() => setSettings(s => ({ ...s, fps }))}
|
|
/>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<Switch
|
|
value={settings.audio}
|
|
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
|
|
hideBorder
|
|
className="vcd-screen-picker-audio"
|
|
>
|
|
Stream With Audio
|
|
</Switch>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ModalComponent({
|
|
screens,
|
|
modalProps,
|
|
submit,
|
|
close
|
|
}: {
|
|
screens: Source[];
|
|
modalProps: any;
|
|
submit: (data: StreamPick) => void;
|
|
close: () => void;
|
|
}) {
|
|
const [selected, setSelected] = useState<string>();
|
|
const [settings, setSettings] = useState<StreamSettings>({
|
|
resolution: "1080",
|
|
fps: "60",
|
|
audio: true
|
|
});
|
|
|
|
return (
|
|
<Modals.ModalRoot {...modalProps}>
|
|
<Modals.ModalHeader className="vcd-screen-picker-header">
|
|
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
|
|
<Modals.ModalCloseButton onClick={close} />
|
|
</Modals.ModalHeader>
|
|
|
|
<Modals.ModalContent className="vcd-screen-picker-modal">
|
|
{!selected ? (
|
|
<ScreenPicker screens={screens} onPick={setSelected} />
|
|
) : (
|
|
<StreamSettings
|
|
source={screens.find(s => s.id === selected)!}
|
|
settings={settings}
|
|
setSettings={setSettings}
|
|
/>
|
|
)}
|
|
</Modals.ModalContent>
|
|
|
|
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
|
<Button
|
|
disabled={!selected}
|
|
onClick={() => {
|
|
submit({
|
|
id: selected!,
|
|
...settings
|
|
});
|
|
close();
|
|
}}
|
|
>
|
|
Go Live
|
|
</Button>
|
|
|
|
{selected ? (
|
|
<Button color={Button.Colors.TRANSPARENT} onClick={() => setSelected(void 0)}>
|
|
Back
|
|
</Button>
|
|
) : (
|
|
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
|
Cancel
|
|
</Button>
|
|
)}
|
|
</Modals.ModalFooter>
|
|
</Modals.ModalRoot>
|
|
);
|
|
}
|