Merge branch 'main' into fix/linux-game-tracking

This commit is contained in:
Zamitto 2024-12-13 11:31:05 -03:00 committed by GitHub
commit b2d6d7e736
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 286 additions and 77 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "hydralauncher", "name": "hydralauncher",
"version": "3.0.5", "version": "3.0.8",
"description": "Hydra", "description": "Hydra",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "Los Broxas", "author": "Los Broxas",
@ -36,7 +36,6 @@
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.0.22", "@fontsource/noto-sans": "^5.0.22",
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.0",
"@intercom/messenger-js-sdk": "^0.0.14",
"@primer/octicons-react": "^19.9.0", "@primer/octicons-react": "^19.9.0",
"@reduxjs/toolkit": "^2.2.3", "@reduxjs/toolkit": "^2.2.3",
"@vanilla-extract/css": "^1.14.2", "@vanilla-extract/css": "^1.14.2",

View File

@ -29,7 +29,7 @@
"need_help": "Имате нужда от помощ??" "need_help": "Имате нужда от помощ??"
}, },
"header": { "header": {
"search": "Търси игри", "search": "Търсене",
"home": "Начало", "home": "Начало",
"catalogue": "Каталог", "catalogue": "Каталог",
"downloads": "Изтегляния", "downloads": "Изтегляния",
@ -65,7 +65,7 @@
"calculating_eta": "Калкулиране на оставащо време…", "calculating_eta": "Калкулиране на оставащо време…",
"downloading_metadata": "Изтегляне на метадата…", "downloading_metadata": "Изтегляне на метадата…",
"filter": "Филтрирай repacks", "filter": "Филтрирай repacks",
"requirements": "Състемни изисквания", "requirements": "Системни изисквания",
"minimum": "Минимални", "minimum": "Минимални",
"recommended": "Препоръчителни", "recommended": "Препоръчителни",
"paused": "Паузирано", "paused": "Паузирано",
@ -79,8 +79,8 @@
"add_to_library": "Добави в библиотеката", "add_to_library": "Добави в библиотеката",
"remove_from_library": "Премахни от библиотеката", "remove_from_library": "Премахни от библиотеката",
"no_downloads": "Няма налични изтегляния", "no_downloads": "Няма налични изтегляния",
"play_time": "Играно {{amount}}", "play_time": "Игрално време {{amount}}",
"last_time_played": "Последно играно {{period}}", "last_time_played": "Последно пускане {{period}}",
"not_played_yet": "Не сте играли {{title}} все още", "not_played_yet": "Не сте играли {{title}} все още",
"next_suggestion": "Следващо предложение", "next_suggestion": "Следващо предложение",
"play": "Пускане", "play": "Пускане",
@ -110,7 +110,7 @@
"remove_from_library_description": "Това ще премахне {{game}} от Библиотеката", "remove_from_library_description": "Това ще премахне {{game}} от Библиотеката",
"options": "Опции", "options": "Опции",
"executable_section_title": "Стартиращ файл", "executable_section_title": "Стартиращ файл",
"executable_section_description": "Пътят на файла, който ще се изпълни, когато се щракне върху \"Играй\"", "executable_section_description": "Пътят на файла, който ще се изпълни, когато се щракне върху \"Пускане\"",
"downloads_secion_title": "Свалени", "downloads_secion_title": "Свалени",
"downloads_section_description": "Вижте актуализации или други версии на тази игра", "downloads_section_description": "Вижте актуализации или други версии на тази игра",
"danger_zone_section_title": "Опасна зона", "danger_zone_section_title": "Опасна зона",
@ -162,7 +162,7 @@
"no_download_option_info": "Няма налични данни", "no_download_option_info": "Няма налични данни",
"backup_deletion_failed": "Неуспешно изтриване на резервно копие", "backup_deletion_failed": "Неуспешно изтриване на резервно копие",
"max_number_of_artifacts_reached": "Достигнат максимален брой резервни копия за тази игра", "max_number_of_artifacts_reached": "Достигнат максимален брой резервни копия за тази игра",
"achievements_not_sync": "Постиженията ви не са синхронизирани", "achievements_not_sync": "Постиженията не са синхронизирани",
"manage_files_description": "Управлявайте кои файлове ще бъдат архивирани и възстановени", "manage_files_description": "Управлявайте кои файлове ще бъдат архивирани и възстановени",
"select_folder": "Избери папка", "select_folder": "Избери папка",
"backup_from": "Резервно копие от {{date}}", "backup_from": "Резервно копие от {{date}}",
@ -198,7 +198,7 @@
"downloads_completed": "Приключени", "downloads_completed": "Приключени",
"queued": "В опашка", "queued": "В опашка",
"no_downloads_title": "Толкова е празно", "no_downloads_title": "Толкова е празно",
"no_downloads_description": "Все още не сте изтеглили нищо с Hydra, но никога не е късно да започнете..", "no_downloads_description": "Все още не сте изтеглили нищо с Hydra, но никога не е късно да започнете...",
"checking_files": "Проверка на файлове…" "checking_files": "Проверка на файлове…"
}, },
"settings": { "settings": {
@ -331,7 +331,7 @@
"blocked_users": "Блокирани потребители", "blocked_users": "Блокирани потребители",
"unblock": "Отблокирай", "unblock": "Отблокирай",
"no_friends_added": "Не сте добавили приятели", "no_friends_added": "Не сте добавили приятели",
"pending": "Чакащо", "pending": "Чакащи",
"no_pending_invites": "Нямате чакащи покани", "no_pending_invites": "Нямате чакащи покани",
"no_blocked_users": "Нямате блокирани потребители", "no_blocked_users": "Нямате блокирани потребители",
"friend_code_copied": "Приятелския код е копиран", "friend_code_copied": "Приятелския код е копиран",

View File

@ -6,7 +6,11 @@
"home": { "home": {
"featured": "Doporučené", "featured": "Doporučené",
"surprise_me": "Překvap mě", "surprise_me": "Překvap mě",
"no_results": "Výsledek nenalezen" "no_results": "Výsledek nenalezen",
"start_typing": "Začni psát pro vyhledávání...",
"hot": "Teď populární",
"weekly": "📅 Nejlepší hry týdne",
"achievements": "🏆 Hry k překonání"
}, },
"sidebar": { "sidebar": {
"catalogue": "Katalog", "catalogue": "Katalog",
@ -20,7 +24,9 @@
"home": "Domov", "home": "Domov",
"queued": "{{title}} (V řadě)", "queued": "{{title}} (V řadě)",
"game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor", "game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor",
"sign_in": "Přihlásit se" "sign_in": "Přihlásit se",
"friends": "Přátelé",
"need_help": "Potřebujete pomoc?"
}, },
"header": { "header": {
"search": "Vyhledat hry", "search": "Vyhledat hry",
@ -113,7 +119,54 @@
"download_paused": "Stahování pozastaveno", "download_paused": "Stahování pozastaveno",
"last_downloaded_option": "Poslední stažená možnost", "last_downloaded_option": "Poslední stažená možnost",
"create_shortcut_success": "Zástupce vytvořen úspěšně", "create_shortcut_success": "Zástupce vytvořen úspěšně",
"create_shortcut_error": "Chyba při pokusu vytvořit zástupce" "create_shortcut_error": "Chyba při pokusu vytvořit zástupce",
"nsfw_content_title": "Tahle hra obsahuje nevhodný obsah",
"nsfw_content_description": "{{title}} obsahuje obsah, který by nemusel být vhodný pro všechny věkové skupiny. Jste si jisti, že chcete pokračovat?",
"allow_nsfw_content": "Pokračovat",
"refuse_nsfw_content": "Jít zpět",
"stats": "Statistiky",
"download_count": "Stažení",
"player_count": "Aktivní hráči",
"download_error": "Tahle možnost stažení není dostupná",
"download": "Stáhnout",
"executable_path_in_use": "Spustitelný soubor již používá \"{{game}}\"",
"warning": "Varování",
"hydra_needs_to_remain_open": "Pro tohle stažení, musí Hydra zůstat otevřená až do konce stahování. Pokud Hydru zavřete dříve, postup stahování bude ztracen.",
"achievements": "Achievementy",
"achievements_count": "Achievementy {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "Uložení v cloudu",
"cloud_save_description": "Uložte si svůj postup v cloud a pokračujte v hraní na jakémkoliv zářízení",
"backups": "Zálohy",
"install_backup": "Nainstalovat",
"delete_backup": "Smazat",
"create_backup": "Vytvořit zálohu",
"last_backup_date": "Poslední záloha vytvořena {{date}}",
"no_backup_preview": "Žádné zálohy nebyly nalezeny pro tuhle hru",
"restoring_backup": "Obnovuji zálohu ({{progress}} hotovo)...",
"uploading_backup": "Nahrávání zálohy...",
"no_backups": "Nemáte zatím vytvořeny žádné zálohy pro tuto hru",
"backup_uploaded": "Záloha nahrána",
"backup_deleted": "Záloha odstraněna",
"backup_restored": "Záloha obnovena",
"see_all_achievements": "Zobrazit všechny achievementy",
"sign_in_to_see_achievements": "Musíte se přihlásit pro zobrazení achievementů",
"mapping_method_automatic": "Automaticky",
"mapping_method_manual": "Manuálně",
"mapping_method_label": "Metoda mapování",
"files_automatically_mapped": "Soubory automaticky zmapovány",
"no_backups_created": "Žádné zálohy nebyly vytvořeny pro tuto hru",
"manage_files": "Spravovat soubory",
"loading_save_preview": "Hledání uložených her...",
"wine_prefix": "Wine Prefix",
"wine_prefix_description": "Wine Prefix použit pro spuštění této hry",
"no_download_option_info": "Žádné informace nejsou dostupny",
"backup_deletion_failed": "Nepovedlo se odstranit zálohu",
"max_number_of_artifacts_reached": "Dosáhli jste maximálního počtu záloh pro tuto hru",
"achievements_not_sync": "Vaše achievementy nejsou synchronizovány",
"manage_files_description": "Spravovat, které soubory budou zálohovány a obnoveny",
"select_folder": "Vybrat složku",
"backup_from": "Zálohy z {{date}}",
"custom_backup_location_set": "Vlastní umístění záloh nastaveno"
}, },
"activation": { "activation": {
"title": "Aktivovat hydru", "title": "Aktivovat hydru",
@ -189,7 +242,21 @@
"found_download_option_zero": "Nenalezena žádná možnost stahování", "found_download_option_zero": "Nenalezena žádná možnost stahování",
"found_download_option_one": "Nalezena {{countFormatted}} možnost stahování", "found_download_option_one": "Nalezena {{countFormatted}} možnost stahování",
"found_download_option_other": "Nalezeny {{countFormatted}} možnosti stahování", "found_download_option_other": "Nalezeny {{countFormatted}} možnosti stahování",
"import": "Importovat" "import": "Importovat",
"public": "Veřejné",
"private": "Soukromé",
"friends_only": "Pouze přátelé",
"privacy": "Soukromí",
"profile_visibility": "Viditelnost profilu",
"profile_visibility_description": "Vyberte si, kdo může vidět váš profil a knihovnu",
"required_field": "Toto pole je povinné",
"source_already_exists": "Tento zdroj byl již přidán",
"must_be_valid_url": "Zdroj musí být platký odkaz URL",
"blocked_users": "Zablokovaní uživatelé",
"user_unblocked": "Uživatel byl odblokován",
"enable_achievement_notifications": "Když je odemknut achievement",
"launch_minimized": "Spustit v minimalizovaném režimu",
"disable_nsfw_alert": "Deaktivovat upozornění na nevhodný obsah"
}, },
"notifications": { "notifications": {
"download_complete": "Stahování dokončeno", "download_complete": "Stahování dokončeno",
@ -198,7 +265,9 @@
"repack_count_one": "{{count}} repack přidán", "repack_count_one": "{{count}} repack přidán",
"repack_count_other": "{{count}} repacky přidány", "repack_count_other": "{{count}} repacky přidány",
"new_update_available": "Version {{version}} je dostupná", "new_update_available": "Version {{version}} je dostupná",
"restart_to_install_update": "Restartuj Hydru pro aktualizaci" "restart_to_install_update": "Restartuj Hydru pro aktualizaci",
"notification_achievement_unlocked_title": "Achievement pro {{game}} byl odemknut",
"notification_achievement_unlocked_body": "{{achievement}} a dalších {{count}} byly odemknuty"
}, },
"system_tray": { "system_tray": {
"open": "Otevřít Hydru", "open": "Otevřít Hydru",
@ -266,6 +335,47 @@
"no_pending_invites": "Nemáte žádné příchozí žádosti", "no_pending_invites": "Nemáte žádné příchozí žádosti",
"no_blocked_users": "Nemáte nikoho zablokovaného", "no_blocked_users": "Nemáte nikoho zablokovaného",
"friend_code_copied": "Kód přítele zkopírován", "friend_code_copied": "Kód přítele zkopírován",
"undo_friendship_modal_text": "Tímto zrušíte své přátelství s {{displayName}}" "undo_friendship_modal_text": "Tímto zrušíte své přátelství s {{displayName}}",
"privacy_hint": "Pro změnu toho, kdo tohle může vidět, jděte do <0>Nastavení</0>",
"locked_profile": "Tento profil je soukromý",
"image_process_failure": "Nastala chyba při zpracování obrázku",
"required_field": "Toto pole je povinné",
"displayname_min_length": "Uživatelské jméno musí být minimálně 3 znaky dlouhé",
"displayname_max_length": "Uživatelské jméno musí být maximálně 50 znaků dlouhé",
"report_profile": "Nahlásit profil",
"report_reason": "Proč nahlašujete tento profil?",
"report_description": "Přídavné informace",
"report_description_placeholder": "Přídavné informace",
"report": "Nahlásit",
"report_reason_hate": "Nenávistné projevy",
"report_reason_sexual_content": "Sexuální obsah",
"report_reason_violence": "Násilí",
"report_reason_spam": "Spam",
"report_reason_other": "Ostatní",
"profile_reported": "Profil nahlášen",
"your_friend_code": "Tvůj kód přítele:",
"upload_banner": "Nahrát banner profilu",
"uploading_banner": "Nahrávání banneru",
"background_image_updated": "Obrázek pozadí byl změněn"
},
"achievement": {
"achievement_unlocked": "Achievement odemčen",
"user_achievements": "Achievementy uživatele {{displayName}}",
"your_achievements": "Vaše achievementy",
"unlocked_at": "Odemčeno:",
"subscription_needed": "Je vyžadováno předplatné Hydra Cloud pro zobrazení tohoto obsahu",
"new_achievements_unlocked": "Odemčeno {{achievementCount}} nových achievementů z {{gameCount}} her",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementů",
"achievements_unlocked_for_game": "Odemčeno {{achievementCount}} nových achievementů pro {{gameTitle}}"
},
"tour": {
"subscription_tour_title": "Předplatné Hydra Cloud",
"subscribe_now": "Připojit se",
"cloud_saving": "Ukládání v cloudu",
"cloud_achievements": "Ukládejte vaše achievementy do cloudu",
"animated_profile_picture": "Animované profilové obrázky",
"premium_support": "Prémiová podpora",
"show_and_compare_achievements": "Zobraz a porovnej achievementy s ostatními uživateli",
"animated_profile_banner": "Animovaný banner na profilu"
} }
} }

View File

@ -105,6 +105,7 @@
"open_folder": "Open folder", "open_folder": "Open folder",
"open_download_location": "See downloaded files", "open_download_location": "See downloaded files",
"create_shortcut": "Create desktop shortcut", "create_shortcut": "Create desktop shortcut",
"clear": "Clear",
"remove_files": "Remove files", "remove_files": "Remove files",
"remove_from_library_title": "Are you sure?", "remove_from_library_title": "Are you sure?",
"remove_from_library_description": "This will remove {{game}} from your library", "remove_from_library_description": "This will remove {{game}} from your library",
@ -166,7 +167,8 @@
"manage_files_description": "Manage which files will be backed up and restored", "manage_files_description": "Manage which files will be backed up and restored",
"select_folder": "Select folder", "select_folder": "Select folder",
"backup_from": "Backup from {{date}}", "backup_from": "Backup from {{date}}",
"custom_backup_location_set": "Custom backup location set" "custom_backup_location_set": "Custom backup location set",
"no_directory_selected": "No directory selected"
}, },
"activation": { "activation": {
"title": "Activate Hydra", "title": "Activate Hydra",

View File

@ -100,7 +100,7 @@
"open_screenshot": "Abrir captura {{number}}", "open_screenshot": "Abrir captura {{number}}",
"download_settings": "Ajustes de descarga", "download_settings": "Ajustes de descarga",
"downloader": "Método de descarga", "downloader": "Método de descarga",
"select_executable": "Seleccionar ejecutable", "select_executable": "Seleccionar",
"no_executable_selected": "No se seleccionó un ejecutable", "no_executable_selected": "No se seleccionó un ejecutable",
"open_folder": "Abrir carpeta", "open_folder": "Abrir carpeta",
"open_download_location": "Ver archivos descargados", "open_download_location": "Ver archivos descargados",
@ -166,7 +166,9 @@
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados", "manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
"select_folder": "Seleccionar carpeta", "select_folder": "Seleccionar carpeta",
"backup_from": "Copia de seguridad de {{date}}", "backup_from": "Copia de seguridad de {{date}}",
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad" "custom_backup_location_set": "Se configuró la carpeta de copia de seguridad",
"clear": "Limpiar",
"no_directory_selected": "No se seleccionó un directório"
}, },
"activation": { "activation": {
"title": "Activar Hydra", "title": "Activar Hydra",
@ -254,7 +256,9 @@
"must_be_valid_url": "La fuente debe ser una URL válida.", "must_be_valid_url": "La fuente debe ser una URL válida.",
"blocked_users": "Usuarios bloqueados", "blocked_users": "Usuarios bloqueados",
"user_unblocked": "El usuario ha sido desbloqueado", "user_unblocked": "El usuario ha sido desbloqueado",
"enable_achievement_notifications": "Cuando un logro se desbloquea" "enable_achievement_notifications": "Cuando un logro se desbloquea",
"launch_minimized": "Iniciar Hydra minimizado",
"disable_nsfw_alert": "Desactivar alerta NSFW"
}, },
"notifications": { "notifications": {
"download_complete": "Descarga completada", "download_complete": "Descarga completada",
@ -361,8 +365,10 @@
"user_achievements": "Logros de {{displayName}}", "user_achievements": "Logros de {{displayName}}",
"your_achievements": "Tus Logros", "your_achievements": "Tus Logros",
"unlocked_at": "Desbloqueado el:", "unlocked_at": "Desbloqueado el:",
"subscription_needed": "Se necesita una suscripción a Hydra Cloud se necesita para ver este contenido", "subscription_needed": "Se necesita una suscripción a Hydra Cloud necesita para ver este contenido",
"new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos" "new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} logros",
"achievements_unlocked_for_game": "Se han desbloqueado {{achievementCount}} nuevos logros de {{gameTitle}}"
}, },
"tour": { "tour": {
"subscription_tour_title": "Suscripción Hydra Cloud", "subscription_tour_title": "Suscripción Hydra Cloud",

View File

@ -162,7 +162,9 @@
"backup_from": "Backup de {{date}}", "backup_from": "Backup de {{date}}",
"custom_backup_location_set": "Localização customizada selecionada", "custom_backup_location_set": "Localização customizada selecionada",
"select_folder": "Selecione a pasta", "select_folder": "Selecione a pasta",
"manage_files_description": "Gerencie quais arquivos serão feitos backup" "manage_files_description": "Gerencie quais arquivos serão feitos backup",
"clear": "Limpar",
"no_directory_selected": "Nenhum diretório selecionado"
}, },
"activation": { "activation": {
"title": "Ativação", "title": "Ativação",

View File

@ -5,9 +5,9 @@ import { registerEvent } from "../register-event";
const selectGameWinePrefix = async ( const selectGameWinePrefix = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
id: number, id: number,
winePrefixPath: string winePrefixPath: string | null
) => { ) => {
return gameRepository.update({ id }, { winePrefixPath }); return gameRepository.update({ id }, { winePrefixPath: winePrefixPath });
}; };
registerEvent("selectGameWinePrefix", selectGameWinePrefix); registerEvent("selectGameWinePrefix", selectGameWinePrefix);

View File

@ -6,14 +6,18 @@ import { parseExecutablePath } from "../helpers/parse-executable-path";
const updateExecutablePath = async ( const updateExecutablePath = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
id: number, id: number,
executablePath: string executablePath: string | null
) => { ) => {
const parsedPath = executablePath
? parseExecutablePath(executablePath)
: null;
return gameRepository.update( return gameRepository.update(
{ {
id, id,
}, },
{ {
executablePath: parseExecutablePath(executablePath), executablePath: parsedPath,
} }
); );
}; };

View File

@ -174,8 +174,10 @@ export class PythonInstance {
.then((response) => response.data); .then((response) => response.data);
} }
private static async handleRpcError(_error: unknown) { private static async handleRpcError(error: unknown) {
await this.rpc.get("/healthcheck").catch(() => { logger.error(error);
return this.rpc.get("/healthcheck").catch(() => {
logger.error( logger.error(
"RPC healthcheck failed. Killing process and starting again" "RPC healthcheck failed. Killing process and starting again"
); );

View File

@ -153,21 +153,26 @@ export class HydraApi {
(error) => { (error) => {
logger.error(" ---- RESPONSE ERROR -----"); logger.error(" ---- RESPONSE ERROR -----");
const { config } = error; const { config } = error;
const data = JSON.parse(config.data);
logger.error( logger.error(
config.method, config.method,
config.baseURL, config.baseURL,
config.url, config.url,
config.headers, omit(config.headers, ["accessToken", "refreshToken"]),
config.data Array.isArray(data)
? data
: omit(data, ["accessToken", "refreshToken"])
); );
if (error.response) { if (error.response) {
logger.error( logger.error(
"Response", "Response error:",
error.response.status, error.response.status,
error.response.data error.response.data
); );
} else if (error.request) { } else if (error.request) {
logger.error("Request", error.request); const errorData = error.toJSON();
logger.error("Request error:", errorData.message);
} else { } else {
logger.error("Error", error.message); logger.error("Error", error.message);
} }

View File

@ -44,7 +44,7 @@ export const getUserData = () => {
if (err instanceof UserNotLoggedInError) { if (err instanceof UserNotLoggedInError) {
return null; return null;
} }
logger.error("Failed to get logged user", err); logger.error("Failed to get logged user");
const loggedUser = await userAuthRepository.findOne({ const loggedUser = await userAuthRepository.findOne({
where: { id: 1 }, where: { id: 1 },
relations: { subscription: true }, relations: { subscription: true },

View File

@ -85,7 +85,11 @@ export class WindowManager {
return callback(details); return callback(details);
} }
if (details.url.includes("intercom.io")) { if (details.url.includes("featurebase")) {
return callback(details);
}
if (details.url.includes("chatwoot")) {
return callback(details); return callback(details);
} }
@ -191,7 +195,7 @@ export class WindowManager {
this.mainWindow?.focus(); this.mainWindow?.focus();
} }
public static createSystemTray(language: string) { public static async createSystemTray(language: string) {
let tray: Tray; let tray: Tray;
if (process.platform === "darwin") { if (process.platform === "darwin") {
@ -259,6 +263,7 @@ export class WindowManager {
}, },
]); ]);
tray.setContextMenu(contextMenu);
return contextMenu; return contextMenu;
}; };
@ -270,6 +275,8 @@ export class WindowManager {
tray.setToolTip("Hydra"); tray.setToolTip("Hydra");
if (process.platform !== "darwin") { if (process.platform !== "darwin") {
await updateSystemTray();
tray.addListener("click", () => { tray.addListener("click", () => {
if (this.mainWindow) { if (this.mainWindow) {
if ( if (

View File

@ -87,9 +87,9 @@ contextBridge.exposeInMainWorld("electron", {
ipcRenderer.invoke("addGameToLibrary", objectId, title, shop), ipcRenderer.invoke("addGameToLibrary", objectId, title, shop),
createGameShortcut: (id: number) => createGameShortcut: (id: number) =>
ipcRenderer.invoke("createGameShortcut", id), ipcRenderer.invoke("createGameShortcut", id),
updateExecutablePath: (id: number, executablePath: string) => updateExecutablePath: (id: number, executablePath: string | null) =>
ipcRenderer.invoke("updateExecutablePath", id, executablePath), ipcRenderer.invoke("updateExecutablePath", id, executablePath),
selectGameWinePrefix: (id: number, winePrefixPath: string) => selectGameWinePrefix: (id: number, winePrefixPath: string | null) =>
ipcRenderer.invoke("selectGameWinePrefix", id, winePrefixPath), ipcRenderer.invoke("selectGameWinePrefix", id, winePrefixPath),
verifyExecutablePathInUse: (executablePath: string) => verifyExecutablePathInUse: (executablePath: string) =>
ipcRenderer.invoke("verifyExecutablePathInUse", executablePath), ipcRenderer.invoke("verifyExecutablePathInUse", executablePath),

View File

@ -6,7 +6,7 @@
<title>Hydra</title> <title>Hydra</title>
<meta <meta
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="default-src 'self' 'unsafe-inline' *;" content="default-src 'self' 'unsafe-inline' * data: local:;"
/> />
</head> </head>
<body> <body>

View File

@ -119,25 +119,13 @@ export function App() {
if (response) { if (response) {
updateUserDetails(response); updateUserDetails(response);
syncFriendRequests(); 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(() => { .finally(() => {
if (document.getElementById("external-resources")) return;
const $script = document.createElement("script"); const $script = document.createElement("script");
$script.id = "external-resources";
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}?t=${Date.now()}`; $script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}?t=${Date.now()}`;
document.head.appendChild($script); document.head.appendChild($script);
}); });

View File

@ -0,0 +1,46 @@
export function addCookieInterceptor(isStaging: boolean) {
const cookieKey = isStaging ? "cookies-staging" : "cookies";
Object.defineProperty(document, "cookie", {
enumerable: true,
configurable: true,
get() {
return localStorage.getItem(cookieKey) || "";
},
set(cookieString) {
try {
const [cookieName, cookieValue] = cookieString.split(";")[0].split("=");
const currentCookies = localStorage.getItem(cookieKey) || "";
const cookiesObject = parseCookieStringsToObjects(currentCookies);
cookiesObject[cookieName] = cookieValue;
const newString = Object.entries(cookiesObject)
.map(([key, value]) => {
return key + "=" + value;
})
.join("; ");
localStorage.setItem(cookieKey, newString);
} catch (err) {
console.error(err);
}
},
});
}
const parseCookieStringsToObjects = (
cookieStrings: string
): { [key: string]: string } => {
const result = {};
if (cookieStrings === "") return result;
cookieStrings.split(";").forEach((cookieString) => {
const [name, value] = cookieString.split("=");
result[name.trim()] = value.trim();
});
return result;
};

View File

@ -80,8 +80,14 @@ declare global {
shop: GameShop shop: GameShop
) => Promise<void>; ) => Promise<void>;
createGameShortcut: (id: number) => Promise<boolean>; createGameShortcut: (id: number) => Promise<boolean>;
updateExecutablePath: (id: number, executablePath: string) => Promise<void>; updateExecutablePath: (
selectGameWinePrefix: (id: number, winePrefixPath: string) => Promise<void>; id: number,
executablePath: string | null
) => Promise<void>;
selectGameWinePrefix: (
id: number,
winePrefixPath: string | null
) => Promise<void>;
verifyExecutablePathInUse: (executablePath: string) => Promise<Game>; verifyExecutablePathInUse: (executablePath: string) => Promise<Game>;
getLibrary: () => Promise<LibraryGame[]>; getLibrary: () => Promise<LibraryGame[]>;
openGameInstaller: (gameId: number) => Promise<boolean>; openGameInstaller: (gameId: number) => Promise<boolean>;

View File

@ -56,6 +56,8 @@ export function useUserDetails() {
clearUserDetails(); clearUserDetails();
} }
window["userDetails"] = userDetails;
return userDetails; return userDetails;
}); });
}, [clearUserDetails]); }, [clearUserDetails]);

View File

@ -20,6 +20,8 @@ import resources from "@locales";
import { RepacksContextProvider } from "./context"; import { RepacksContextProvider } from "./context";
import { SuspenseWrapper } from "./components"; import { SuspenseWrapper } from "./components";
import { logger } from "./logger";
import { addCookieInterceptor } from "./cookies";
const Home = React.lazy(() => import("./pages/home/home")); const Home = React.lazy(() => import("./pages/home/home"));
const GameDetails = React.lazy( const GameDetails = React.lazy(
@ -34,6 +36,11 @@ const Achievements = React.lazy(
() => import("./pages/achievements/achievements") () => import("./pages/achievements/achievements")
); );
console.log = logger.log;
const isStaging = await window.electron.isStaging();
addCookieInterceptor(isStaging);
i18n i18n
.use(LanguageDetector) .use(LanguageDetector)
.use(initReactI18next) .use(initReactI18next)

View File

@ -95,6 +95,11 @@ export function GameOptionsModal({
await window.electron.openGameExecutablePath(game.id); await window.electron.openGameExecutablePath(game.id);
}; };
const handleClearExecutablePath = async () => {
await window.electron.updateExecutablePath(game.id, null);
updateGame();
};
const handleChangeWinePrefixPath = async () => { const handleChangeWinePrefixPath = async () => {
const { filePaths } = await window.electron.showOpenDialog({ const { filePaths } = await window.electron.showOpenDialog({
properties: ["openDirectory"], properties: ["openDirectory"],
@ -106,6 +111,11 @@ export function GameOptionsModal({
} }
}; };
const handleClearWinePrefixPath = async () => {
await window.electron.selectGameWinePrefix(game.id, null);
updateGame();
};
const shouldShowWinePrefixConfiguration = const shouldShowWinePrefixConfiguration =
window.electron.platform === "linux"; window.electron.platform === "linux";
@ -145,6 +155,7 @@ export function GameOptionsModal({
disabled disabled
placeholder={t("no_executable_selected")} placeholder={t("no_executable_selected")}
rightContent={ rightContent={
<>
<Button <Button
type="button" type="button"
theme="outline" theme="outline"
@ -153,6 +164,12 @@ export function GameOptionsModal({
<FileIcon /> <FileIcon />
{t("select_executable")} {t("select_executable")}
</Button> </Button>
{game.executablePath && (
<Button onClick={handleClearExecutablePath} theme="outline">
{t("clear")}
</Button>
)}
</>
} }
/> />
@ -186,6 +203,7 @@ export function GameOptionsModal({
disabled disabled
placeholder={t("no_directory_selected")} placeholder={t("no_directory_selected")}
rightContent={ rightContent={
<>
<Button <Button
type="button" type="button"
theme="outline" theme="outline"
@ -194,6 +212,15 @@ export function GameOptionsModal({
<FileDirectoryIcon /> <FileDirectoryIcon />
{t("select_executable")} {t("select_executable")}
</Button> </Button>
{game.winePrefixPath && (
<Button
onClick={handleClearWinePrefixPath}
theme="outline"
>
{t("clear")}
</Button>
)}
</>
} }
/> />
</div> </div>

View File

@ -241,6 +241,7 @@ export interface Subscription {
status: SubscriptionStatus; status: SubscriptionStatus;
plan: { id: string; name: string }; plan: { id: string; name: string };
expiresAt: string | null; expiresAt: string | null;
paymentMethod: "pix" | "paypal";
} }
export interface UserDetails { export interface UserDetails {

View File

@ -1066,11 +1066,6 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
"@intercom/messenger-js-sdk@^0.0.14":
version "0.0.14"
resolved "https://registry.yarnpkg.com/@intercom/messenger-js-sdk/-/messenger-js-sdk-0.0.14.tgz#a27999370cc0a82a2a57a779426df25a57891863"
integrity sha512-2dH4BDAh9EI90K7hUkAdZ76W79LM45Sd1OBX7t6Vzy8twpNiQ5X+7sH9G5hlJlkSGnf+vFWlFcy9TOYAyEs1hA==
"@isaacs/cliui@^8.0.2": "@isaacs/cliui@^8.0.2":
version "8.0.2" version "8.0.2"
resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz"