diff --git a/src/main/constants.ts b/src/main/constants.ts index 81cfd53..04a2a7c 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -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 diff --git a/src/main/index.ts b/src/main/index.ts index 4eb863d..6727935 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -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"; @@ -83,6 +84,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(); diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 4fa662c..b135fce 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -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) => { diff --git a/src/main/splash.ts b/src/main/splash.ts index 7c05de9..bde47ae 100644 --- a/src/main/splash.ts +++ b/src/main/splash.ts @@ -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; } diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 7a8b977..824f66b 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -34,7 +34,8 @@ export const VesktopNative = { fileManager: { showItemInFolder: (path: string) => invoke(IpcEvents.SHOW_ITEM_IN_FOLDER, path), getVencordDir: () => sendSync(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(IpcEvents.GET_SETTINGS), diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index 2055067..58fe44d 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -801,4 +801,4 @@ function ModalComponent({ ); -} +} \ No newline at end of file diff --git a/src/renderer/components/settings/CustomSplashAnimation.tsx b/src/renderer/components/settings/CustomSplashAnimation.tsx new file mode 100644 index 0000000..e3e144f --- /dev/null +++ b/src/renderer/components/settings/CustomSplashAnimation.tsx @@ -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 ( + <> + + + {settings.splashAnimationPath ? ( +
+ {/* adding the Math.random() here ensures that a new image is fetched when the user changes the path */} + +

The custom splash animation is enabled.

+
+ ) : ( + "A custom splash animation is not set." + )} +
+
+ + +
+ + + ); +}; diff --git a/src/renderer/components/settings/Settings.tsx b/src/renderer/components/settings/Settings.tsx index 81dfcc3..5f12d48 100644 --- a/src/renderer/components/settings/Settings.tsx +++ b/src/renderer/components/settings/Settings.tsx @@ -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> defaultValue: false } ], + "Custom Splash Animation":[CustomSplashAnimation], "Vencord Location": [VencordLocationPicker] }; diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index 51d2a28..7cea2b0 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -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", diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index f100010..ee488eb 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -24,6 +24,7 @@ export interface Settings { splashTheming?: boolean; splashColor?: string; + splashAnimationPath?: string; splashBackground?: string; spellCheckLanguages?: string[]; diff --git a/static/views/splash.html b/static/views/splash.html index bac2ad2..a44b273 100644 --- a/static/views/splash.html +++ b/static/views/splash.html @@ -25,19 +25,15 @@ img { width: 128px; height: 128px; - image-rendering: pixelated; }
- shiggy + + animation

Loading Vesktop...

- + \ No newline at end of file