diff --git a/package.json b/package.json index ab9a268a..d40a2663 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@electron-toolkit/utils": "^3.0.0", "@fontsource/noto-sans": "^5.0.22", "@hookform/resolvers": "^3.9.0", + "@intercom/messenger-js-sdk": "^0.0.14", "@primer/octicons-react": "^19.9.0", "@reduxjs/toolkit": "^2.2.3", "@vanilla-extract/css": "^1.14.2", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index abecb50d..e70eb519 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -25,7 +25,8 @@ "queued": "{{title}} (Queued)", "game_has_no_executable": "Game has no executable selected", "sign_in": "Sign in", - "friends": "Friends" + "friends": "Friends", + "need_help": "Need help?" }, "header": { "search": "Search games", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index d155bcd6..2830eb0c 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -25,7 +25,8 @@ "queued": "{{title}} (En cola)", "game_has_no_executable": "El juego no tiene un ejecutable seleccionado", "sign_in": "Iniciar sesión", - "friends": "Amigos" + "friends": "Amigos", + "need_help": "¿Necesitas ayuda?" }, "header": { "search": "Buscar juegos", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 41e60338..b61a08be 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -25,7 +25,8 @@ "queued": "{{title}} (Na fila)", "game_has_no_executable": "Jogo não possui executável selecionado", "sign_in": "Login", - "friends": "Amigos" + "friends": "Amigos", + "need_help": "Precisa de ajuda?" }, "header": { "search": "Buscar jogos", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index c7299479..1616bfd6 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -24,7 +24,8 @@ "queued": "{{title}} (В очереди)", "game_has_no_executable": "Файл запуска игры не выбран", "sign_in": "Войти", - "friends": "Друзья" + "friends": "Друзья", + "need_help": "Нужна помощь?" }, "header": { "search": "Поиск", diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 8da2dd5e..4f65ef2a 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -85,6 +85,10 @@ export class WindowManager { return callback(details); } + if (details.url.includes("intercom.io")) { + return callback(details); + } + const headers = { "access-control-allow-origin": ["*"], "access-control-allow-methods": ["GET, POST, PUT, DELETE, OPTIONS"], diff --git a/src/renderer/index.html b/src/renderer/index.html index b53595ff..c1d8f50c 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -6,7 +6,7 @@ Hydra diff --git a/src/renderer/src/app.css.ts b/src/renderer/src/app.css.ts index a52d81f6..25c453c8 100644 --- a/src/renderer/src/app.css.ts +++ b/src/renderer/src/app.css.ts @@ -126,3 +126,9 @@ export const titleBar = style({ zIndex: "4", borderBottom: `1px solid ${vars.color.border}`, } as ComplexStyleRule); + +export const cloudText = style({ + background: "linear-gradient(270deg, #16B195 50%, #3E62C0 100%)", + backgroundClip: "text", + color: "transparent", +}); diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 7c572a56..11522273 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -2,6 +2,8 @@ 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, @@ -34,6 +36,10 @@ export interface AppProps { children: React.ReactNode; } +Intercom({ + app_id: "pq96v8fh", +}); + export function App() { const contentRef = useRef(null); const { updateLibrary, library } = useLibrary(); @@ -54,8 +60,13 @@ export function App() { hideFriendsModal, } = useUserDetails(); - const { userDetails, fetchUserDetails, updateUserDetails, clearUserDetails } = - useUserDetails(); + const { + userDetails, + hasActiveSubscription, + fetchUserDetails, + updateUserDetails, + clearUserDetails, + } = useUserDetails(); const dispatch = useAppDispatch(); @@ -204,7 +215,9 @@ export function App() { useEffect(() => { new MutationObserver(() => { - const modal = document.body.querySelector("[role=dialog]"); + const modal = document.body.querySelector( + "[role=dialog]:not([data-intercom-frame='true'])" + ); dispatch(toggleDraggingDisabled(Boolean(modal))); }).observe(document.body, { @@ -270,7 +283,12 @@ export function App() { <> {window.electron.platform === "win32" && (
-

Hydra

+

+ Hydra + {hasActiveSubscription && ( + Cloud + )} +

)} diff --git a/src/renderer/src/components/badge/badge.scss b/src/renderer/src/components/badge/badge.scss index f1ed89e9..f32c398f 100644 --- a/src/renderer/src/components/badge/badge.scss +++ b/src/renderer/src/components/badge/badge.scss @@ -4,7 +4,7 @@ color: globals.$muted-color; font-size: 10px; padding: calc(globals.$spacing-unit / 2) globals.$spacing-unit; - border: solid 1px globals.$border-color; + border: solid 1px globals.$muted-color; border-radius: 4px; display: flex; align-items: center; diff --git a/src/renderer/src/components/sidebar/sidebar.css.ts b/src/renderer/src/components/sidebar/sidebar.css.ts index bfdb4eea..9b061d68 100644 --- a/src/renderer/src/components/sidebar/sidebar.css.ts +++ b/src/renderer/src/components/sidebar/sidebar.css.ts @@ -124,3 +124,29 @@ export const section = style({ flexDirection: "column", paddingBottom: `${SPACING_UNIT}px`, }); + +export const helpButton = style({ + color: vars.color.muted, + padding: `${SPACING_UNIT}px ${SPACING_UNIT * 2}px`, + gap: "9px", + display: "flex", + alignItems: "center", + cursor: "pointer", + boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.6)", + borderTop: `solid 1px ${vars.color.border}`, + transition: "background-color ease 0.1s", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, +}); + +export const helpButtonIcon = style({ + background: "linear-gradient(0deg, #16B195 50%, #3E62C0 100%)", + width: "24px", + height: "24px", + display: "flex", + alignItems: "center", + justifyContent: "center", + color: "#fff", + borderRadius: "50%", +}); diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx index 3159ecac..d529e942 100644 --- a/src/renderer/src/components/sidebar/sidebar.tsx +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -5,7 +5,12 @@ import { useLocation, useNavigate } from "react-router-dom"; import type { LibraryGame } from "@types"; import { TextField } from "@renderer/components"; -import { useDownload, useLibrary, useToast } from "@renderer/hooks"; +import { + useDownload, + useLibrary, + useToast, + useUserDetails, +} from "@renderer/hooks"; import { routes } from "./routes"; @@ -15,6 +20,9 @@ import { buildGameDetailsPath } from "@renderer/helpers"; import SteamLogo from "@renderer/assets/steam-logo.svg?react"; 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; @@ -42,6 +50,20 @@ 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, + "Subscription expiration date": userDetails?.subscription?.expiresAt, + "Payment status": userDetails?.subscription?.status, + }); + } + }, [userDetails, hasActiveSubscription]); + const { lastPacket, progress } = useDownload(); const { showWarningToast } = useToast(); @@ -237,6 +259,15 @@ export function Sidebar() { + {hasActiveSubscription && ( + + )} +