Skip to content

Commit 650b02e

Browse files
authored
Merge pull request #1703 from hydralauncher/fix/HYD-827
fix: shadow dom to isolate achievements window and custom css refactor
2 parents 4485f62 + 93929ae commit 650b02e

File tree

13 files changed

+105
-68
lines changed

13 files changed

+105
-68
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hydralauncher",
3-
"version": "3.5.0",
3+
"version": "3.5.1",
44
"description": "Hydra",
55
"main": "./out/main/index.js",
66
"author": "Los Broxas",
@@ -69,6 +69,7 @@
6969
"react-loading-skeleton": "^3.4.0",
7070
"react-redux": "^9.1.1",
7171
"react-router-dom": "^6.22.3",
72+
"react-shadow": "^20.6.0",
7273
"react-tooltip": "^5.28.0",
7374
"sound-play": "^1.1.0",
7475
"steam-shortcut-editor": "https://github.com/hydralauncher/steam-shortcut-editor",

src/main/events/themes/toggle-custom-theme.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { themesSublevel } from "@main/level";
22
import { registerEvent } from "../register-event";
3+
import { WindowManager } from "@main/services";
34

45
const toggleCustomTheme = async (
56
_event: Electron.IpcMainInvokeEvent,
@@ -17,6 +18,8 @@ const toggleCustomTheme = async (
1718
isActive,
1819
updatedAt: new Date(),
1920
});
21+
22+
WindowManager.notificationWindow?.webContents.send("on-custom-theme-updated");
2023
};
2124

2225
registerEvent("toggleCustomTheme", toggleCustomTheme);

src/main/events/themes/update-custom-theme.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ const updateCustomTheme = async (
2020
});
2121

2222
if (theme.isActive) {
23-
WindowManager.mainWindow?.webContents.send("css-injected", code);
24-
WindowManager.notificationWindow?.webContents.send("css-injected", code);
23+
WindowManager.mainWindow?.webContents.send("on-custom-theme-updated");
24+
WindowManager.notificationWindow?.webContents.send(
25+
"on-custom-theme-updated"
26+
);
2527
}
2628
};
2729

src/main/services/window-manager.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,13 @@ export class WindowManager {
289289
const display = screen.getPrimaryDisplay();
290290
const { width, height } = display.workAreaSize;
291291

292+
if (position === "bottom-left") {
293+
return {
294+
x: 0,
295+
y: height - this.NOTIFICATION_WINDOW_HEIGHT,
296+
};
297+
}
298+
292299
if (position === "bottom-center") {
293300
return {
294301
x: (width - this.NOTIFICATION_WINDOW_WIDTH) / 2,
@@ -310,13 +317,6 @@ export class WindowManager {
310317
};
311318
}
312319

313-
if (position === "bottom-left") {
314-
return {
315-
x: 0,
316-
y: height - this.NOTIFICATION_WINDOW_HEIGHT,
317-
};
318-
}
319-
320320
if (position === "top-right") {
321321
return {
322322
x: width - this.NOTIFICATION_WINDOW_WIDTH,
@@ -356,6 +356,7 @@ export class WindowManager {
356356
maximizable: false,
357357
autoHideMenuBar: true,
358358
minimizable: false,
359+
backgroundColor: "#00000000",
359360
focusable: false,
360361
skipTaskbar: true,
361362
frame: false,
@@ -372,8 +373,13 @@ export class WindowManager {
372373
// this.notificationWindow.setVisibleOnAllWorkspaces(true, {
373374
// visibleOnFullScreen: true,
374375
// });
376+
375377
this.notificationWindow.setAlwaysOnTop(true, "screen-saver", 1);
376378
this.loadNotificationWindowURL();
379+
380+
if (isStaging) {
381+
this.notificationWindow.webContents.openDevTools();
382+
}
377383
}
378384

379385
public static async showAchievementTestNotification() {

src/preload/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,11 +462,11 @@ contextBridge.exposeInMainWorld("electron", {
462462
/* Editor */
463463
openEditorWindow: (themeId: string) =>
464464
ipcRenderer.invoke("openEditorWindow", themeId),
465-
onCssInjected: (cb: (cssString: string) => void) => {
466-
const listener = (_event: Electron.IpcRendererEvent, cssString: string) =>
467-
cb(cssString);
468-
ipcRenderer.on("css-injected", listener);
469-
return () => ipcRenderer.removeListener("css-injected", listener);
465+
onCustomThemeUpdated: (cb: () => void) => {
466+
const listener = (_event: Electron.IpcRendererEvent) => cb();
467+
ipcRenderer.on("on-custom-theme-updated", listener);
468+
return () =>
469+
ipcRenderer.removeListener("on-custom-theme-updated", listener);
470470
},
471471
closeEditorWindow: (themeId?: string) =>
472472
ipcRenderer.invoke("closeEditorWindow", themeId),

src/renderer/src/app.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { downloadSourcesTable } from "./dexie";
2828
import { useSubscription } from "./hooks/use-subscription";
2929
import { HydraCloudModal } from "./pages/shared-modals/hydra-cloud/hydra-cloud-modal";
3030

31-
import { injectCustomCss } from "./helpers";
31+
import { injectCustomCss, removeCustomCss } from "./helpers";
3232
import "./app.scss";
3333

3434
export interface AppProps {
@@ -246,16 +246,26 @@ export function App() {
246246
};
247247
}, [updateRepacks]);
248248

249-
useEffect(() => {
250-
const loadAndApplyTheme = async () => {
251-
const activeTheme = await window.electron.getActiveCustomTheme();
249+
const loadAndApplyTheme = useCallback(async () => {
250+
const activeTheme = await window.electron.getActiveCustomTheme();
251+
if (activeTheme?.code) {
252+
injectCustomCss(activeTheme.code);
253+
} else {
254+
removeCustomCss();
255+
}
256+
}, []);
252257

253-
if (activeTheme?.code) {
254-
injectCustomCss(activeTheme.code);
255-
}
256-
};
258+
useEffect(() => {
257259
loadAndApplyTheme();
258-
}, []);
260+
}, [loadAndApplyTheme]);
261+
262+
useEffect(() => {
263+
const unsubscribe = window.electron.onCustomThemeUpdated(() => {
264+
loadAndApplyTheme();
265+
});
266+
267+
return () => unsubscribe();
268+
}, [loadAndApplyTheme]);
259269

260270
const playAudio = useCallback(() => {
261271
const audio = new Audio(achievementSound);
@@ -273,14 +283,6 @@ export function App() {
273283
};
274284
}, [playAudio]);
275285

276-
useEffect(() => {
277-
const unsubscribe = window.electron.onCssInjected((cssString) => {
278-
injectCustomCss(cssString);
279-
});
280-
281-
return () => unsubscribe();
282-
}, []);
283-
284286
const handleToastClose = useCallback(() => {
285287
dispatch(closeToast());
286288
}, [dispatch]);

src/renderer/src/components/achievements/notification/achievement-notification.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ $margin-bottom: 28px;
141141

142142
.achievement-notification {
143143
width: 360px;
144-
height: 192px;
144+
height: 140px;
145145
display: flex;
146146

147147
&--top-left {

src/renderer/src/components/achievements/notification/achievement-notification.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import {
33
AchievementNotificationInfo,
44
} from "@types";
55
import cn from "classnames";
6-
import "./achievement-notification.scss";
76
import HydraIcon from "@renderer/assets/icons/hydra.svg?react";
87
import { EyeClosedIcon } from "@primer/octicons-react";
98
import Ellipses from "@renderer/assets/icons/ellipses.png";
9+
import "./achievement-notification.scss";
1010

1111
interface AchievementNotificationProps {
1212
position: AchievementCustomNotificationPosition;

src/renderer/src/declaration.d.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,7 @@ declare global {
352352

353353
/* Editor */
354354
openEditorWindow: (themeId: string) => Promise<void>;
355-
onCssInjected: (
356-
cb: (cssString: string) => void
357-
) => () => Electron.IpcRenderer;
355+
onCustomThemeUpdated: (cb: () => void) => () => Electron.IpcRenderer;
358356
closeEditorWindow: (themeId?: string) => Promise<void>;
359357
}
360358

src/renderer/src/helpers.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,35 +55,32 @@ export const buildGameAchievementPath = (
5555
export const darkenColor = (color: string, amount: number, alpha: number = 1) =>
5656
new Color(color).darken(amount).alpha(alpha).toString();
5757

58-
export const injectCustomCss = (css: string) => {
58+
export const injectCustomCss = (
59+
css: string,
60+
target: HTMLElement = document.head
61+
) => {
5962
try {
60-
const currentCustomCss = document.getElementById("custom-css");
61-
if (currentCustomCss) {
62-
currentCustomCss.remove();
63-
}
63+
target.querySelector("#custom-css")?.remove();
6464

6565
if (css.startsWith(THEME_WEB_STORE_URL)) {
6666
const link = document.createElement("link");
6767
link.id = "custom-css";
6868
link.rel = "stylesheet";
6969
link.href = css;
70-
document.head.appendChild(link);
70+
target.appendChild(link);
7171
} else {
7272
const style = document.createElement("style");
7373
style.id = "custom-css";
7474
style.textContent = `
7575
${css}
7676
`;
77-
document.head.appendChild(style);
77+
target.appendChild(style);
7878
}
7979
} catch (error) {
8080
console.error("failed to inject custom css:", error);
8181
}
8282
};
8383

84-
export const removeCustomCss = () => {
85-
const currentCustomCss = document.getElementById("custom-css");
86-
if (currentCustomCss) {
87-
currentCustomCss.remove();
88-
}
84+
export const removeCustomCss = (target: HTMLElement = document.head) => {
85+
target.querySelector("#custom-css")?.remove();
8986
};

0 commit comments

Comments
 (0)