Fix UI
This commit is contained in:
parent
8435cabcec
commit
7e78d5c0ae
6 changed files with 87 additions and 52 deletions
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, desktopCapturer, dialog, ipcMain, session, shell } from "electron";
|
||||
import { app, dialog, ipcMain, session, shell } from "electron";
|
||||
import { existsSync, readFileSync, watch } from "fs";
|
||||
import { open, readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
@ -75,8 +75,6 @@ ipcMain.handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
|
|||
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.CAPTURER_GET_SOURCES, (_, options) => desktopCapturer.getSources(options));
|
||||
|
||||
ipcMain.handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
const res = await dialog.showOpenDialog(mainWin!, {
|
||||
properties: ["openDirectory"]
|
||||
|
|
|
@ -4,9 +4,22 @@
|
|||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { desktopCapturer, session } from "electron";
|
||||
import { desktopCapturer, ipcMain, session, Streams } from "electron";
|
||||
import type { StreamPick } from "renderer/components/ScreenSharePicker";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
|
||||
export function registerScreenShareHandler() {
|
||||
ipcMain.handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ["window", "screen"],
|
||||
thumbnailSize: {
|
||||
width: 1920,
|
||||
height: 1080
|
||||
}
|
||||
});
|
||||
return sources.find(s => s.id === id)?.thumbnail.toDataURL();
|
||||
});
|
||||
|
||||
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ["window", "screen"],
|
||||
|
@ -24,17 +37,19 @@ export function registerScreenShareHandler() {
|
|||
|
||||
const choice = await request.frame
|
||||
.executeJavaScript(`VencordDesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
|
||||
.catch(() => "cancelled");
|
||||
.then(e => e as StreamPick)
|
||||
.catch(() => null);
|
||||
|
||||
if (choice === "cancelled") {
|
||||
callback({});
|
||||
return;
|
||||
}
|
||||
if (!choice) return callback({});
|
||||
|
||||
const source = sources.find(s => s.id === choice);
|
||||
callback({
|
||||
video: source,
|
||||
audio: "loopback"
|
||||
});
|
||||
const source = sources.find(s => s.id === choice.id);
|
||||
if (!source) return callback({});
|
||||
|
||||
const streams: Streams = {
|
||||
video: source
|
||||
};
|
||||
if (choice.audio) streams.audio = "loopback";
|
||||
|
||||
callback(streams);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import type { DesktopCapturerSource, SourcesOptions } from "electron";
|
||||
import type { Settings } from "shared/settings";
|
||||
import type { LiteralUnion } from "type-fest";
|
||||
|
||||
|
@ -37,13 +36,6 @@ export const VencordDesktopNative = {
|
|||
focus: () => invoke<void>(IpcEvents.FOCUS)
|
||||
},
|
||||
capturer: {
|
||||
getSources: async (options?: SourcesOptions) => {
|
||||
const res = await invoke<DesktopCapturerSource[]>(IpcEvents.CAPTURER_GET_SOURCES, options);
|
||||
return res.map(({ id, name, thumbnail }) => ({
|
||||
id,
|
||||
name,
|
||||
url: thumbnail.toDataURL()
|
||||
}));
|
||||
}
|
||||
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
import "./screenSharePicker.css";
|
||||
|
||||
import { Modals, openModal } from "@vencord/types/utils";
|
||||
import { closeModal, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
||||
import { Button, Card, Forms, Switch, Text, useState } from "@vencord/types/webpack/common";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
const StreamResolutions = ["720", "1080", "1440", "Source"] as const;
|
||||
const StreamFps = ["15", "30", "60"] as const;
|
||||
|
@ -22,6 +22,10 @@ interface StreamSettings {
|
|||
audio: boolean;
|
||||
}
|
||||
|
||||
export interface StreamPick extends StreamSettings {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface Source {
|
||||
id: string;
|
||||
name: string;
|
||||
|
@ -29,18 +33,26 @@ interface Source {
|
|||
}
|
||||
|
||||
export function openScreenSharePicker(screens: Source[]) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
openModal(props => (
|
||||
<ModalComponent
|
||||
screens={screens}
|
||||
modalProps={props}
|
||||
submit={resolve}
|
||||
close={() => {
|
||||
props.onClose();
|
||||
reject(new Error("Aborted"));
|
||||
}}
|
||||
/>
|
||||
));
|
||||
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");
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -68,11 +80,16 @@ function StreamSettings({
|
|||
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={source.url} alt="" />
|
||||
<img src={thumb} alt="" />
|
||||
<Text variant="text-sm/normal">{source.name}</Text>
|
||||
</Card>
|
||||
|
||||
|
@ -85,7 +102,7 @@ function StreamSettings({
|
|||
<div className="vcd-screen-picker-radios">
|
||||
{StreamResolutions.map(res => (
|
||||
<label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}>
|
||||
<Forms.FormTitle>{res}</Forms.FormTitle>
|
||||
<Text variant="text-sm/bold">{res}</Text>
|
||||
<input
|
||||
type="radio"
|
||||
name="resolution"
|
||||
|
@ -103,7 +120,7 @@ function StreamSettings({
|
|||
<div className="vcd-screen-picker-radios">
|
||||
{StreamFps.map(fps => (
|
||||
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
||||
<Forms.FormTitle>{fps}</Forms.FormTitle>
|
||||
<Text variant="text-sm/bold">{fps}</Text>
|
||||
<input
|
||||
type="radio"
|
||||
name="fps"
|
||||
|
@ -138,7 +155,7 @@ function ModalComponent({
|
|||
}: {
|
||||
screens: Source[];
|
||||
modalProps: any;
|
||||
submit: (id: string) => void;
|
||||
submit: (data: StreamPick) => void;
|
||||
close: () => void;
|
||||
}) {
|
||||
const [selected, setSelected] = useState<string>();
|
||||
|
@ -150,8 +167,8 @@ function ModalComponent({
|
|||
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps}>
|
||||
<Modals.ModalHeader>
|
||||
<Forms.FormTitle tag="h2">Screen Picker</Forms.FormTitle>
|
||||
<Modals.ModalHeader className="vcd-screen-picker-header">
|
||||
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
|
||||
<Modals.ModalCloseButton onClick={close} />
|
||||
</Modals.ModalHeader>
|
||||
|
||||
|
@ -167,11 +184,14 @@ function ModalComponent({
|
|||
)}
|
||||
</Modals.ModalContent>
|
||||
|
||||
<Modals.ModalFooter>
|
||||
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||
<Button
|
||||
disabled={!selected}
|
||||
onClick={() => {
|
||||
submit(selected!);
|
||||
submit({
|
||||
id: selected!,
|
||||
...settings
|
||||
});
|
||||
close();
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -2,7 +2,17 @@
|
|||
padding: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid { display: grid;
|
||||
.vcd-screen-picker-header h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-footer {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2em 1em;
|
||||
}
|
||||
|
@ -13,7 +23,8 @@
|
|||
}
|
||||
|
||||
.vcd-screen-picker-selected img {
|
||||
outline: 2px solid var(--brand-experiment);
|
||||
border: 2px solid var(--brand-experiment);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid label {
|
||||
|
@ -43,7 +54,7 @@
|
|||
|
||||
.vcd-screen-picker-preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-preview {
|
||||
|
@ -51,8 +62,7 @@
|
|||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radio input {
|
||||
|
|
|
@ -28,9 +28,9 @@ export const enum IpcEvents {
|
|||
|
||||
SPELLCHECK_SET_LANGUAGES = "VCD_SPELLCHECK_SET_LANGUAGES",
|
||||
|
||||
CAPTURER_GET_SOURCES = "VCD_CAPTURER_GET_SOURCES",
|
||||
CAPTURER_GET_LARGE_THUMBNAIL = "VCD_CAPTURER_GET_LARGE_THUMBNAIL",
|
||||
|
||||
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
|
||||
ENABLE_AUTOSTART = "VCD_ENABLE_AUTOSTART",
|
||||
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART",
|
||||
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART"
|
||||
}
|
||||
|
|
Reference in a new issue