This commit is contained in:
ading2210 2024-10-26 13:58:32 +02:00 committed by GitHub
commit 67e0048fef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 142 additions and 18 deletions

View file

@ -44,6 +44,7 @@ export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes");
export const VESKTOP_SPLASH_DIR = join(DATA_DIR, "splash");
// needs to be inline require because of circular dependency
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised

View file

@ -6,10 +6,11 @@
import "./ipc";
import { app, BrowserWindow, nativeTheme } from "electron";
import { join } from "path";
import { app, BrowserWindow, nativeTheme, net, protocol, session } from "electron";
import { autoUpdater } from "electron-updater";
import { DATA_DIR } from "./constants";
import { DATA_DIR, VESKTOP_SPLASH_DIR } from "./constants";
import { createFirstLaunchTour } from "./firstLaunch";
import { createWindows, mainWin } from "./mainWindow";
import { registerMediaPermissionsHandler } from "./mediaPermissions";
@ -84,6 +85,36 @@ function init() {
registerScreenShareHandler();
registerMediaPermissionsHandler();
//register file handler so we can load the custom splash animation from the user's filesystem
protocol.handle("splash-animation", () => {
const { splashAnimationPath } = Settings.store;
const fullPath = join(VESKTOP_SPLASH_DIR, splashAnimationPath as string);
return net.fetch("file:///"+fullPath);
});
//this patches the discord csp to allow the splash-animation:// protocol
//the vencord:// protocol is already whitelisted, but the code for doing that is in the
//vencord repo, not the vesktop one. hopefully in the future, the splash image functionality
//can be added to the vencord:// protocol handler, or the vencord:// protocol handler can be moved here
let otherHandler: any = null;
session.defaultSession.webRequest.onHeadersReceived(({responseHeaders, resourceType}, callback) => {
if (responseHeaders && resourceType === "mainFrame" && responseHeaders["content-security-policy"]) {
let csp = responseHeaders["content-security-policy"][0];
csp = csp.replace("img-src", "img-src splash-animation:");
responseHeaders["content-security-policy"] = [csp];
}
if (otherHandler) {
otherHandler({responseHeaders, resourceType}, callback);
}
else {
callback({ cancel: false, responseHeaders });
}
});
//we need to overwrite onHeadersReceived because normally electron only allows one handler to be active at a time
session.defaultSession.webRequest.onHeadersReceived = (handler) => {
otherHandler = handler;
}
bootstrap();
app.on("activate", () => {

View file

@ -8,16 +8,17 @@ if (process.platform === "linux") import("./venmic");
import { execFile } from "child_process";
import { app, BrowserWindow, clipboard, dialog, nativeImage, RelaunchOptions, session, shell } from "electron";
import { mkdirSync, readFileSync, watch } from "fs";
import { open, readFile } from "fs/promises";
import { mkdirSync, readFileSync, watch, existsSync } from "fs";
import { open, readFile, copyFile, mkdir, rmdir } from "fs/promises";
import { release } from "os";
import { join } from "path";
import { randomBytes } from "crypto";
import { join, extname } from "path";
import { debounce } from "shared/utils/debounce";
import { IpcEvents } from "../shared/IpcEvents";
import { setBadgeCount } from "./appBadge";
import { autoStart } from "./autoStart";
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR, VESKTOP_SPLASH_DIR } from "./constants";
import { mainWin } from "./mainWindow";
import { Settings, State } from "./settings";
import { handle, handleSync } from "./utils/ipcWrappers";
@ -126,6 +127,29 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async (_e, value?: null) => {
return "ok";
});
handle(IpcEvents.SELECT_IMAGE_PATH, async () => {
const res = await dialog.showOpenDialog(mainWin!, {
properties: ["openFile"],
filters: [
{name: "Images", extensions: ["apng", "avif", "gif", "jpeg", "png", "svg", "webp"]}
]
});
if (!res.filePaths.length) return "cancelled";
const originalPath = res.filePaths[0];
const uuid = randomBytes(16).toString("hex");
const imageName = "splash_" + uuid + extname(originalPath);
const destPath = join(VESKTOP_SPLASH_DIR, imageName);
if (existsSync(VESKTOP_SPLASH_DIR)) {
await rmdir(VESKTOP_SPLASH_DIR, {recursive: true});
}
await mkdir(VESKTOP_SPLASH_DIR, {recursive: true});
await copyFile(originalPath, destPath);
return imageName;
});
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string) => {

View file

@ -12,6 +12,8 @@ import { ICON_PATH, VIEW_DIR } from "shared/paths";
import { Settings } from "./settings";
export function createSplashWindow(startMinimized = false) {
const { splashBackground, splashColor, splashTheming, splashAnimationPath } = Settings.store;
const splash = new BrowserWindow({
...SplashProps,
icon: ICON_PATH,
@ -20,8 +22,6 @@ export function createSplashWindow(startMinimized = false) {
splash.loadFile(join(VIEW_DIR, "splash.html"));
const { splashBackground, splashColor, splashTheming } = Settings.store;
if (splashTheming) {
if (splashColor) {
const semiTransparentSplashColor = splashColor.replace("rgb(", "rgba(").replace(")", ", 0.2)");
@ -35,5 +35,17 @@ export function createSplashWindow(startMinimized = false) {
}
}
if (splashAnimationPath) {
splash.webContents.executeJavaScript(`
document.getElementById("animation").src = "splash-animation://img";
`);
}
else {
splash.webContents.insertCSS(`img {image-rendering: pixelated}`)
splash.webContents.executeJavaScript(`
document.getElementById("animation").src = "../shiggy.gif";
`);
}
return splash;
}

View file

@ -34,7 +34,8 @@ export const VesktopNative = {
fileManager: {
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
getVencordDir: () => sendSync<string | undefined>(IpcEvents.GET_VENCORD_DIR),
selectVencordDir: (value?: null) => invoke<"cancelled" | "invalid" | "ok">(IpcEvents.SELECT_VENCORD_DIR, value)
selectVencordDir: (value?: null) => invoke<"cancelled" | "invalid" | "ok">(IpcEvents.SELECT_VENCORD_DIR, value),
selectImagePath: () => invoke<"cancelled" | string>(IpcEvents.SELECT_IMAGE_PATH)
},
settings: {
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),

View file

@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { Button, Forms } from "@vencord/types/webpack/common";
import { SettingsComponent } from "./Settings";
export const CustomSplashAnimation: SettingsComponent = ({ settings }) => {
return (
<>
<Forms.FormText>
{settings.splashAnimationPath ? (
<div style={{
display: "flex",
alignItems: "center",
gap: "16px"
}}>
{/* adding the Math.random() here ensures that a new image is fetched when the user changes the path */}
<img src={"splash-animation:///" + Math.random()} width="64px" height="64px"></img>
<p>The custom splash animation is enabled.</p>
</div>
) : (
"A custom splash animation is not set."
)}
</Forms.FormText>
<div className="vcd-location-btns" style={{marginBottom: 20}}>
<Button
size={Button.Sizes.SMALL}
onClick={async () => {
const choice = await VesktopNative.fileManager.selectImagePath();
if (choice === "cancelled") return;
settings.splashAnimationPath = choice;
}}
>
Change
</Button>
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.RED}
onClick={() => {
//todo: delete the image after resetting the path?
settings.splashAnimationPath = undefined
}}
>
Reset
</Button>
</div>
</>
);
};

View file

@ -16,6 +16,7 @@ import { DiscordBranchPicker } from "./DiscordBranchPicker";
import { NotificationBadgeToggle } from "./NotificationBadgeToggle";
import { VencordLocationPicker } from "./VencordLocationPicker";
import { WindowsTransparencyControls } from "./WindowsTransparencyControls";
import { CustomSplashAnimation } from "./CustomSplashAnimation";
interface BooleanSetting {
key: keyof typeof Settings.store;
@ -118,6 +119,7 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
defaultValue: false
}
],
"Custom Splash Animation":[CustomSplashAnimation],
"Vencord Location": [VencordLocationPicker]
};

View file

@ -25,6 +25,7 @@ export const enum IpcEvents {
GET_VENCORD_DIR = "VCD_GET_VENCORD_DIR",
SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR",
SELECT_IMAGE_PATH= "VCD_SELECT_IMAGE_PATH",
UPDATER_GET_DATA = "VCD_UPDATER_GET_DATA",
UPDATER_DOWNLOAD = "VCD_UPDATER_DOWNLOAD",

View file

@ -24,6 +24,7 @@ export interface Settings {
splashTheming?: boolean;
splashColor?: string;
splashAnimationPath?: string;
splashBackground?: string;
spellCheckLanguages?: string[];

View file

@ -25,19 +25,15 @@
img {
width: 128px;
height: 128px;
image-rendering: pixelated;
}
</style>
</head>
<body>
<div class="wrapper">
<img
draggable="false"
src="../shiggy.gif"
alt="shiggy"
role="presentation"
/>
<!-- the data url is here to ensure there isn't an empty frame before the image is loaded -->
<img id="animation" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
draggable="false" alt="animation" role="presentation" />
<p>Loading Vesktop...</p>
</div>
</body>