mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-28 05:11:02 +00:00
Merge branch 'main' into feature/clearpaths
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<title>Hydra</title>
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src *; style-src 'self' 'unsafe-inline'; img-src 'self' data: local: *; media-src 'self' local: data: *; connect-src *; font-src *;"
|
||||
content="default-src 'self' 'unsafe-inline' *;"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -2,8 +2,6 @@ import { useCallback, useContext, useEffect, useRef } from "react";
|
||||
|
||||
import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components";
|
||||
|
||||
import Intercom from "@intercom/messenger-js-sdk";
|
||||
|
||||
import {
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
@@ -36,10 +34,6 @@ export interface AppProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
Intercom({
|
||||
app_id: import.meta.env.RENDERER_VITE_INTERCOM_APP_ID,
|
||||
});
|
||||
|
||||
export function App() {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const { updateLibrary, library } = useLibrary();
|
||||
@@ -120,12 +114,33 @@ export function App() {
|
||||
dispatch(setProfileBackground(profileBackground));
|
||||
}
|
||||
|
||||
fetchUserDetails().then((response) => {
|
||||
if (response) {
|
||||
updateUserDetails(response);
|
||||
syncFriendRequests();
|
||||
}
|
||||
});
|
||||
fetchUserDetails()
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
updateUserDetails(response);
|
||||
syncFriendRequests();
|
||||
|
||||
const $existingScript = document.getElementById("user-details");
|
||||
|
||||
const content = `window.userDetails = ${JSON.stringify(response)};`;
|
||||
|
||||
if ($existingScript) {
|
||||
$existingScript.textContent = content;
|
||||
} else {
|
||||
const $script = document.createElement("script");
|
||||
$script.id = "user-details";
|
||||
$script.type = "text/javascript";
|
||||
$script.textContent = content;
|
||||
|
||||
document.head.appendChild($script);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
const $script = document.createElement("script");
|
||||
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}?t=${Date.now()}`;
|
||||
document.head.appendChild($script);
|
||||
});
|
||||
}, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]);
|
||||
|
||||
const onSignIn = useCallback(() => {
|
||||
@@ -215,9 +230,7 @@ export function App() {
|
||||
|
||||
useEffect(() => {
|
||||
new MutationObserver(() => {
|
||||
const modal = document.body.querySelector(
|
||||
"[role=dialog]:not([data-intercom-frame='true'])"
|
||||
);
|
||||
const modal = document.body.querySelector("[data-hydra-dialog]");
|
||||
|
||||
dispatch(toggleDraggingDisabled(Boolean(modal)));
|
||||
}).observe(document.body, {
|
||||
|
||||
@@ -107,6 +107,7 @@ export function Modal({
|
||||
aria-labelledby={title}
|
||||
aria-describedby={description}
|
||||
ref={modalContentRef}
|
||||
data-hydra-dialog
|
||||
>
|
||||
<div className={styles.modalHeader}>
|
||||
<div style={{ display: "flex", gap: 4, flexDirection: "column" }}>
|
||||
|
||||
@@ -22,8 +22,6 @@ import { SidebarProfile } from "./sidebar-profile";
|
||||
import { sortBy } from "lodash-es";
|
||||
import { CommentDiscussionIcon } from "@primer/octicons-react";
|
||||
|
||||
import { show, update } from "@intercom/messenger-js-sdk";
|
||||
|
||||
const SIDEBAR_MIN_WIDTH = 200;
|
||||
const SIDEBAR_INITIAL_WIDTH = 250;
|
||||
const SIDEBAR_MAX_WIDTH = 450;
|
||||
@@ -50,20 +48,7 @@ export function Sidebar() {
|
||||
return sortBy(library, (game) => game.title);
|
||||
}, [library]);
|
||||
|
||||
const { userDetails, hasActiveSubscription } = useUserDetails();
|
||||
|
||||
useEffect(() => {
|
||||
if (userDetails) {
|
||||
update({
|
||||
name: userDetails.displayName,
|
||||
Username: userDetails.username,
|
||||
email: userDetails.email ?? undefined,
|
||||
Email: userDetails.email,
|
||||
"Subscription expiration date": userDetails?.subscription?.expiresAt,
|
||||
"Payment status": userDetails?.subscription?.status,
|
||||
});
|
||||
}
|
||||
}, [userDetails, hasActiveSubscription]);
|
||||
const { hasActiveSubscription } = useUserDetails();
|
||||
|
||||
const { lastPacket, progress } = useDownload();
|
||||
|
||||
@@ -266,7 +251,11 @@ export function Sidebar() {
|
||||
</div>
|
||||
|
||||
{hasActiveSubscription && (
|
||||
<button type="button" className={styles.helpButton} onClick={show}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.helpButton}
|
||||
data-open-support-chat
|
||||
>
|
||||
<div className={styles.helpButtonIcon}>
|
||||
<CommentDiscussionIcon size={14} />
|
||||
</div>
|
||||
|
||||
@@ -181,6 +181,7 @@ export function GameDetailsContextProvider({
|
||||
shop,
|
||||
i18n.language,
|
||||
userDetails,
|
||||
userPreferences,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
4
src/renderer/src/declaration.d.ts
vendored
4
src/renderer/src/declaration.d.ts
vendored
@@ -60,7 +60,8 @@ declare global {
|
||||
) => Promise<ShopDetails | null>;
|
||||
getRandomGame: () => Promise<Steam250Game>;
|
||||
getHowLongToBeat: (
|
||||
title: string
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => Promise<HowLongToBeatCategory[] | null>;
|
||||
getGames: (take?: number, skip?: number) => Promise<CatalogueEntry[]>;
|
||||
searchGameRepacks: (query: string) => Promise<GameRepack[]>;
|
||||
@@ -162,6 +163,7 @@ declare global {
|
||||
openExternal: (src: string) => Promise<void>;
|
||||
openCheckout: () => Promise<void>;
|
||||
getVersion: () => Promise<string>;
|
||||
isStaging: () => Promise<boolean>;
|
||||
ping: () => string;
|
||||
getDefaultDownloadsPath: () => Promise<string>;
|
||||
isPortableVersion: () => Promise<boolean>;
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
UserDetails,
|
||||
} from "@types";
|
||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||
import { isFuture, isToday } from "date-fns";
|
||||
|
||||
export function useUserDetails() {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -128,10 +129,8 @@ export function useUserDetails() {
|
||||
const unblockUser = (userId: string) => window.electron.unblockUser(userId);
|
||||
|
||||
const hasActiveSubscription = useMemo(() => {
|
||||
return (
|
||||
userDetails?.subscription?.expiresAt &&
|
||||
new Date(userDetails.subscription.expiresAt) > new Date()
|
||||
);
|
||||
const expiresAt = userDetails?.subscription?.expiresAt;
|
||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
||||
}, [userDetails]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -75,7 +75,7 @@ export function CloudSyncFilesModal({
|
||||
showSuccessToast(t("custom_backup_location_set"));
|
||||
getGameBackupPreview();
|
||||
}
|
||||
}, [objectId, setValue, shop, showSuccessToast, getGameBackupPreview]);
|
||||
}, [objectId, setValue, shop, showSuccessToast, getGameBackupPreview, t]);
|
||||
|
||||
const handleFileMappingMethodClick = useCallback(
|
||||
(mappingOption: FileMappingMethod) => {
|
||||
|
||||
@@ -97,8 +97,10 @@ export function Sidebar() {
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const howLongToBeat =
|
||||
await window.electron.getHowLongToBeat(gameTitle);
|
||||
const howLongToBeat = await window.electron.getHowLongToBeat(
|
||||
objectId,
|
||||
shop
|
||||
);
|
||||
|
||||
if (howLongToBeat) {
|
||||
howLongToBeatEntriesTable.add({
|
||||
|
||||
@@ -45,22 +45,25 @@ export function ProfileContent() {
|
||||
return userProfile?.relation?.status === "ACCEPTED";
|
||||
}, [userProfile]);
|
||||
|
||||
const buildUserGameDetailsPath = (game: UserGame) => {
|
||||
if (!userProfile?.hasActiveSubscription || game.achievementCount === 0) {
|
||||
return buildGameDetailsPath({
|
||||
...game,
|
||||
objectId: game.objectId,
|
||||
});
|
||||
}
|
||||
const buildUserGameDetailsPath = useCallback(
|
||||
(game: UserGame) => {
|
||||
if (!userProfile?.hasActiveSubscription || game.achievementCount === 0) {
|
||||
return buildGameDetailsPath({
|
||||
...game,
|
||||
objectId: game.objectId,
|
||||
});
|
||||
}
|
||||
|
||||
const userParams = userProfile
|
||||
? {
|
||||
userId: userProfile.id,
|
||||
}
|
||||
: undefined;
|
||||
const userParams = userProfile
|
||||
? {
|
||||
userId: userProfile.id,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return buildGameAchievementPath({ ...game }, userParams);
|
||||
};
|
||||
return buildGameAchievementPath({ ...game }, userParams);
|
||||
},
|
||||
[userProfile]
|
||||
);
|
||||
|
||||
const formatPlayTime = useCallback(
|
||||
(playTimeInSeconds = 0) => {
|
||||
@@ -259,6 +262,7 @@ export function ProfileContent() {
|
||||
userStats,
|
||||
numberFormatter,
|
||||
t,
|
||||
buildUserGameDetailsPath,
|
||||
formatPlayTime,
|
||||
navigate,
|
||||
]);
|
||||
|
||||
2
src/renderer/src/vite-env.d.ts
vendored
2
src/renderer/src/vite-env.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
/// <reference types="vite-plugin-svgr/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly RENDERER_VITE_INTERCOM_APP_ID: string;
|
||||
readonly RENDERER_VITE_EXTERNAL_RESOURCES_URL: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
Reference in New Issue
Block a user