From bd56c48cb3ebb6ff583ee11949fba8613bbc1c2b Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Sun, 28 Sep 2025 02:16:26 +0200 Subject: [PATCH 01/52] Improved Hungarian Translation --- src/locales/hu/translation.json | 520 +++++++++++++++++++++++++++++--- 1 file changed, 474 insertions(+), 46 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 0cea87b0..52b17466 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -1,45 +1,71 @@ { "language_name": "Magyar", + "app": { + "successfully_signed_in": "Sikeresen bejelentkezve" + }, "home": { - "featured": "Featured", + "featured": "Kiemelt játék", "surprise_me": "Lepj meg", - "no_results": "Nem található" + "no_results": "Nincs találat", + "start_typing": "Kereséshez gépelj...", + "hot": "Felkapott most", + "weekly": "📅 A hét felkapott játékai", + "achievements": "🏆 Teljesítendő játékok" }, "sidebar": { "catalogue": "Katalógus", "downloads": "Letöltések", "settings": "Beállítások", "my_library": "Könyvtáram", - "downloading_metadata": "{{title}} (Metadata letöltése…)", - "paused": "{{title}} (Szünet)", + "downloading_metadata": "{{title}} (metaadatai letöltése…)", + "paused": "{{title}} (Szüneteltetve)", "downloading": "{{title}} ({{percentage}} - Letöltés…)", "filter": "Könyvtár szűrése", "home": "Főoldal", - "favorites": "Kedvenc játékok" + "queued": "A(z) {{title}} (Várakozósoron van)", + "game_has_no_executable": "A játékhoz nincs kiválasztva futtatható fájl", + "sign_in": "Bejelentkezés", + "friends": "Barátok", + "need_help": "Elakadtál?", + "favorites": "Kedvenc játékok", + "playable_button_title": "Csak az azonnal játszható játékokat mutasd" }, "header": { "search": "Keresés", - "home": "Főoldal", "catalogue": "Katalógus", "downloads": "Letöltések", - "search_results": "Keresési eredmények", - "settings": "Beállítások" + "search_results": "Keresési találatok", + "settings": "Beállítások", + "version_available_install": "A(z) {{version}} verzió elérhető. Kattints ide az újraindításhoz és telepítéshez.", + "version_available_download": "A(z) {{version}} verzió elérhető. A letöltéshez kattints ide." }, "bottom_panel": { - "no_downloads_in_progress": "Nincsenek folyamatban lévő letöltések", + "no_downloads_in_progress": "Nincs folyamatban lévő letöltés", "downloading_metadata": "{{title}} metaadatainak letöltése…", - "downloading": "{{title}} letöltése… ({{percentage}} kész) - Befejezés {{eta}} - {{speed}}" + "downloading": "{{title}} letöltése… ({{percentage}} kész) - Befejezés {{eta}} - {{speed}}", + "calculating_eta": "{{title}} letöltése… ({{percentage}} kész) - Hátralévő idő…", + "checking_files": "A(z) {{title}} fájljaiból… ({{percentage}} kész)", + "installing_common_redist": "{{log}}…", + "installation_complete": "Telepítés befejezve", + "installation_complete_message": "A(z) Alapvető Segédprogramok sikeresen telepítve" }, "catalogue": { - "next_page": "Következő olda", - "previous_page": "Előző olda" + "search": "Szűrés…", + "developers": "Fejlesztők", + "genres": "Műfajok", + "tags": "Címkék", + "publishers": "Kiadók", + "download_sources": "Letöltési források", + "result_count": "{{resultCount}} találatok", + "filter_count": "{{filterCount}} elérhető", + "clear_filters": "{{filterCount}} kiválaszott szűrő törlése" }, "game_details": { - "open_download_options": "Letöltési lehetőségek", - "download_options_zero": "Nincs letöltési lehetőség", - "download_options_one": "{{count}} letöltési lehetőség", - "download_options_other": "{{count}} letöltési lehetőség", + "open_download_options": "Letöltési opciók megnyitása", + "download_options_zero": "Nincs letöltési opció", + "download_options_one": "{{count}} letöltési opció", + "download_options_other": "{{count}} letöltési opció", "updated_at": "Frissítve: {{updated_at}}", "install": "Letöltés", "resume": "Folytatás", @@ -48,11 +74,13 @@ "remove": "Eltávolítás", "space_left_on_disk": "{{space}} szabad hely a lemezen", "eta": "Befejezés {{eta}}", - "downloading_metadata": "Metaadatok letöltése…", + "calculating_eta": "Hátralevő idő kiszámítása…", + "downloading_metadata": "Metaadat letöltése", "filter": "Repackek szűrése", "requirements": "Rendszerkövetelmények", - "minimum": "Minimális", + "minimum": "Minimum", "recommended": "Ajánlott", + "paused": "Szüneteltetve", "release_date": "Megjelenés: {{date}}", "publisher": "Kiadta: {{publisher}}", "hours": "óra", @@ -60,29 +88,157 @@ "amount_hours": "{{amount}} óra", "amount_minutes": "{{amount}} perc", "accuracy": "{{accuracy}}% pontosság", - "add_to_library": "Hozzáadás a könyvtárhoz", + "add_to_library": "Könyvtárba helyezés", + "already_in_library": "Már könyvtárban", "remove_from_library": "Eltávolítás a könyvtárból", "no_downloads": "Nincs elérhető letöltés", "play_time": "Játszva: {{amount}}", - "last_time_played": "Utoljára játszva {{period}}", - "not_played_yet": "{{title}} még nem játszottál", + "last_time_played": "Utoljára játszva: {{period}}", + "not_played_yet": "Ezzel a játékkal még nem játszottál: {{title}}", "next_suggestion": "Következő javaslat", "play": "Játék", "deleting": "Telepítő törlése…", "close": "Bezárás", - "playing_now": "Jelenleg játszva", + "playing_now": "Játékban: ", "change": "Változtatás", - "repacks_modal_description": "Choose the repack you want to download", - "select_folder_hint": "Ahhoz, hogy megváltoztasd a helyet, hozzákell férned a", - "download_now": "Töltsd le most" + "repacks_modal_description": "Válaszd ki a repacket amit leszeretnél tölteni", + "select_folder_hint": "Hogy megváltoztasd a letöltési helyet, menj a <0>Beállítások menüjébe", + "download_now": "Letöltés", + "no_shop_details": "A bolt adatai nem érhetőek el.", + "download_options": "Letöltési opciók", + "download_path": "Letöltis hely", + "previous_screenshot": "Előző screenshot", + "next_screenshot": "Következő screenshot", + "screenshot": "Screenshot {{number}}", + "open_screenshot": "Screenshot megnyitása {{number}}", + "download_settings": "Letöltési beállítások", + "downloader": "Letöltési mód", + "select_executable": "Tallózás", + "no_executable_selected": "Nincs futtatható fájl kiválasztva", + "open_folder": "Mappa megnyitása", + "open_download_location": "Letöltött fájlok megtekintése", + "create_shortcut": "Asztali parancsikon létrehozása", + "clear": "Visszavon", + "remove_files": "Fájlok eltávolítása", + "remove_from_library_title": "Biztos vagy ebben?", + "remove_from_library_description": "Ezzel eltávolítod a játékot {{game}} a könyvtáradból", + "options": "Beállítások", + "executable_section_title": "Futtatható fájl", + "executable_section_description": "A fájl helye amely futtatásra fog kerülni amikor a \"Play\" levan nyomva", + "downloads_section_title": "Letöltések", + "downloads_section_description": "Csekkold le a játék frissítéseit vagy más verzióit", + "danger_zone_section_title": "Veszélyzóna", + "danger_zone_section_description": "Távolítsd el a játékot könyvtáradból, vagy a fájlokat amit a Hydra töltött le", + "download_in_progress": "Letöltés folyamatban", + "download_paused": "Letöltés szüneteltetve", + "last_downloaded_option": "Utoljára letöltött", + "create_steam_shortcut": "Steam parancsikon létrehozása", + "create_shortcut_success": "A parancsikon létrehozása sikeres", + "you_might_need_to_restart_steam": "Lehetséges hogy újrakell indítsd a steamet hogy lásd a változást.", + "create_shortcut_error": "Hiba lépett fel létrehozás közben", + "nsfw_content_title": "Ez a játék nem megfelelő tartalmat tartalmaz.", + "nsfw_content_description": "{{title}} tartalmaz tartalmat amely nem megfelelő minden korosztálynak. Biztosan folytatni szeretnéd?", + "allow_nsfw_content": "Folytatás", + "refuse_nsfw_content": "Vissza", + "stats": "Statisztikák", + "download_count": "Letöltések", + "player_count": "Aktív játékosok", + "download_error": "Ez a letöltési opció nem elérhető", + "download": "Letöltés", + "executable_path_in_use": "Ez a futtatható fájl már használatban van a(z) \"{{game}}\" által", + "warning": "Figyelmeztetés:", + "hydra_needs_to_remain_open": "ehhez a letöltéshez, a Hydrának muszáj nyitva maradnia hogy letöltődjön. Ha a Hydra bezáródik letöltés előtt, a letöltés elveszik.", + "achievements": "Achievementek", + "achievements_count": "Achievementek {{unlockedCount}}/{{achievementsCount}}", + "cloud_save": "Mentés felhőben", + "cloud_save_description": "Mentsd el az előrehaladásodat a felhőben, majd folytasd egy másik eszközön", + "backups": "Biztonsági másolatok", + "install_backup": "Telepít", + "delete_backup": "Töröl", + "create_backup": "Biztonsági másolat létrehozása", + "last_backup_date": "Utolsó biztonsági mentés {{date}}", + "no_backup_preview": "Ehhez a címhez nem található mentett játék", + "restoring_backup": "Biztonsági mentés helyreállítás: ({{progress}} kész)…", + "uploading_backup": "Biztonsági mentés feltöltése…", + "no_backups": "You haven't created any backups for this game yet", + "backup_uploaded": "Biztonsági mentés feltöltve", + "backup_deleted": "Biztonsági mentés törölve", + "backup_restored": "Biztonsági mentés helyreállítva", + "see_all_achievements": "Achievementlista megtekintése", + "sign_in_to_see_achievements": "Jelentkezz be hogy lásd a(z) achievementjeid", + "mapping_method_automatic": "Automatikus", + "mapping_method_manual": "Kézi", + "mapping_method_label": "Térképezési módszer", + "files_automatically_mapped": "Fájlok automatikusan térképezve", + "no_backups_created": "Ehhez a játékhoz nincs biztonsági másolat létrehozva", + "manage_files": "Fájlok kezelése", + "loading_save_preview": "Mentett játék keresése…", + "wine_prefix": "Wine Prefix", + "wine_prefix_description": "A Wine környezet, amiben a játék fut", + "launch_options": "Indítási opciók", + "launch_options_description": "Indítási opciók testreszabása haladó felhasználóknak (kísérleti funkció)", + "launch_options_placeholder": "Nincs paraméter megadva", + "no_download_option_info": "Nincs elérhető információ", + "backup_deletion_failed": "Biztonsági mentés törlése sikertelen", + "max_number_of_artifacts_reached": "A játék biztonsági mentéseinek száma elérte a határt", + "achievements_not_sync": "Tekintsd meg hogyan kell szinkronizálni a achievementjeid", + "manage_files_description": "Kezeld mely fájlokról készül biztonsági másolat, és melyek állíthatók vissza", + "select_folder": "Mappa kiválasztása", + "backup_from": "Biztonsági másolat: {{date}}", + "automatic_backup_from": "Automatikus másolat: {{date}}", + "enable_automatic_cloud_sync": "Automatikus felhőalapú szinkronizálás engedélyezése", + "custom_backup_location_set": "Egyéni biztonsági mentési hely", + "no_directory_selected": "Nincs kiválasztva mappa", + "no_write_permission": "Nem lehet ebbe a mappába letölteni. Kattints ide további információért.", + "reset_achievements": "Achievement nullázása", + "reset_achievements_description": "Ez az összes achievementet nullázza a {{game}} játékhoz", + "reset_achievements_title": "Biztos vagy ebben?", + "reset_achievements_success": "Achievementek sikeresen nullázva", + "reset_achievements_error": "Achievementek nullázása sikertelen", + "download_error_gofile_quota_exceeded": "Túllépted a Gofile havi kvótáját. Kérlek, várd meg amíg a kvóta lejár.", + "download_error_real_debrid_account_not_authorized": "A Real-Debrid fiókod nem jogosult új letöltésekre. Kérlek, ellenőrízd a fiókbeállításaidat, majd próbáld újra.", + "download_error_not_cached_on_real_debrid": "Ez a letöltés nem érhető el a Real-Debridnél, és lekérdezni letöltési állapotot még nem lehet vele.", + "update_playtime_title": "Játékidő frissítése", + "update_playtime_description": "Manuálisan frissíteni a Játékidőt a {{game}} játékhoz", + "update_playtime": "Játékidő frissítése", + "update_playtime_success": "Játékidő sikeresen frissítve", + "update_playtime_error": "A Játékidőnek nem sikerült frissülnie", + "update_game_playtime": "Játékidő frissítése", + "manual_playtime_warning": "Az óráid 'manuálisan frissítve' lesznek megjelölve, és ez nem visszavonható.", + "manual_playtime_tooltip": "Ez a játékidő manuálisan lett frissítve", + "download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", + "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", + "game_removed_from_favorites": "Játék eltávolítva a kedvencek közül", + "game_added_to_favorites": "Játék rögzítve a kedvencek közé", + "game_removed_from_pinned": "Játék eltávolítva a rögzítettekből", + "game_added_to_pinned": "Játék sikeresen rögzítve", + "automatically_extract_downloaded_files": "Automatikus kibontása a letöltött fájloknak", + "create_start_menu_shortcut": "Start menü parancsikon létrehozása", + "invalid_wine_prefix_path": "Érvénytelen Wine prefix elérési útvonal", + "invalid_wine_prefix_path_description": "Az út a Wine prefixhez érvénytelen. Ellenőrízd az elérési utat, majd próbáld újra.", + "missing_wine_prefix": "Wine prefix szükséges a biztonsági másolat létrehozásához Linux rendszeren.", + "artifact_renamed": "Biztonsági mentés sikeresen átnevezve", + "rename_artifact": "Biztonsági mentés átnevezése", + "rename_artifact_description": "Nevezd át a biztonsági másolatot egy leíróbb névre.", + "artifact_name_label": "Biztonsági másolat neve", + "artifact_name_placeholder": "Adj egy nevet a biztonsági mentésnek", + "save_changes": "Változtatások mentése", + "required_field": "Ez a mező kötelező", + "max_length_field": "Ez a mező kevesebb karakter kell legyen mint {{length}}", + "freeze_backup": "Rögzítsd, hogy az automatikus biztonsági mentések ne írják felül", + "unfreeze_backup": "Leválaszt", + "backup_frozen": "Biztonsági mentés rögzítve", + "backup_unfrozen": "Biztonsági mentés leválasztva", + "backup_freeze_failed": "Biztonsági mentés rögzítése sikertelen", + "backup_freeze_failed_description": "Legalább egy szabad helyet kell hagyni az automatikus biztonsági mentéseknek." }, "activation": { - "title": "Hydra Aktiválása", - "installation_id": "Telepítési ID:", - "enter_activation_code": "Add meg az aktiválási kódodat", - "message": "Ha nem tudod, hol kérdezd meg ezt, akkor nem is kellene, hogy legyen ilyened.", + "title": "Hydra aktiválása", + "installation_id": "Telepítési azonosító:", + "enter_activation_code": "Írd be az aktiválási kódod", + "message": "Ha nem tudod kit kell kérdezz efelől, akkor nem is szabadna megkérdezned.", "activate": "Aktiválás", - "loading": "Betöltés…" + "loading": "Töltés…" }, "downloads": { "resume": "Folytatás", @@ -91,46 +247,318 @@ "paused": "Szüneteltetve", "verifying": "Ellenőrzés…", "completed": "Befejezve", + "removed": "Nincs letöltve", "cancel": "Mégse", "filter": "Letöltött játékok szűrése", - "remove": "Eltávolítás", + "remove": "Eltávolít", "downloading_metadata": "Metaadatok letöltése…", "deleting": "Telepítő törlése…", "delete": "Telepítő eltávolítása", - "delete_modal_title": "Biztos vagy benne?", - "delete_modal_description": "Ez eltávolít minden telepítési fájlt a számítógépedről", - "install": "Telepítés" + "delete_modal_title": "Biztos vagy ebben?", + "delete_modal_description": "Ez eltávolítja a telepítési fájlokat a számítógépedről", + "install": "Telepít", + "download_in_progress": "Folyamatban lévő", + "queued_downloads": "Várakozósoron lévő letöltések", + "downloads_completed": "Befejezett", + "queued": "Várakozási sorban", + "no_downloads_title": "Oly üres..", + "no_downloads_description": "Még nem töltöttél le semmit a Hydra segítségével, de soha nem késő elkezdeni.", + "checking_files": "Fájlok ellenőrzése…", + "seeding": "Seedelés", + "stop_seeding": "Seedelés leállítása", + "resume_seeding": "Seedelés folytatása", + "options": "Kezelés", + "extract": "Fájlok kibontása", + "extracting": "Fájlok kibontása…" }, "settings": { - "downloads_path": "Letöltések helye", + "downloads_path": "Letöltési útvonalak", "change": "Frissítés", "notifications": "Értesítések", "enable_download_notifications": "Amikor egy letöltés befejeződik", + "enable_repack_list_notifications": "Amikor új repack kerül feltöltésre", + "real_debrid_api_token_label": "Real-Debrid API token", + "quit_app_instead_hiding": "Hydra elrejtésének tiltása bezáráskor", + "launch_with_system": "Hydra automatikus indítása rendszer indításakor", + "general": "Általános", + "behavior": "Működés", + "download_sources": "Forrás letöltések", + "language": "Nyelv", + "api_token": "API Token", + "enable_real_debrid": "Real-Debrid Bekapcsolása", + "real_debrid_description": "A Real-Debrid egy korlátozásmentes letöltőprogram, lehetővé teszi a fájlok gyors letöltését, és csak az internetkapcsolat sebessége szab határt.", + "debrid_invalid_token": "Érvénytelen API token", + "debrid_api_token_hint": "Az API Tokened itt találhatod <0>here", + "real_debrid_free_account_error": "Ez a fiók: \"{{username}}\" egy ingyenes fiók. Kérlek iratkozz fel a Real-Debrid-re", + "debrid_linked_message": "Fiók összekapcsolva: \"{{username}}\" ", + "save_changes": "Változtatások mentése", + "changes_saved": "Változtatások sikeresen mentve", + "download_sources_description": "A Hydra lefogja tölteni a letöltési linkeket a forrásokból. A forrás URL-nek közvetlen linknek kell lennie egy .json fájlhoz, ami tartalmazza a linkeket.", + "validate_download_source": "Érvényesítés", + "remove_download_source": "Eltávolítás", + "add_download_source": "Forrás hozáadása", + "download_count_zero": "Nincs letöltési opció", + "download_count_one": "{{countFormatted}} letöltési opció", + "download_count_other": "{{countFormatted}} letöltési opció", + "download_source_url": "URL forrás letöltése", + "add_download_source_description": "Helyezd be a .json fájl URL-jét", + "download_source_up_to_date": "Naprakész", + "download_source_errored": "Hiba történt", + "sync_download_sources": "Források szinkronizálása", + "removed_download_source": "Letöltési forrás eltávolítva", + "removed_download_sources": "Letöltési források eltávolítva", "cancel_button_confirmation_delete_all_sources": "Nem", "confirm_button_confirmation_delete_all_sources": "Igen, törölj mindent", - "description_confirmation_delete_all_sources": "Törölni fog minden letöltési forrást", - "title_confirmation_delete_all_sources": "Törölje az összes letöltési forrást", - "removed_download_sources": "Betűtípusok eltávolítva", - "button_delete_all_sources": "Távolítsa el az összes letöltési forrást", - "enable_repack_list_notifications": "Amikor egy új repack hozzáadásra kerül" + "title_confirmation_delete_all_sources": "Az összes letöltési forrás törlése", + "description_confirmation_delete_all_sources": "Az összes letöltési forrást törölni fogod ezáltal", + "button_delete_all_sources": "Összes eltávolítása", + "added_download_source": "Letöltési forrás hozzáadva", + "download_sources_synced": "Az összes letöltési forrás szinkronizálva", + "insert_valid_json_url": "Adj meg egy érvényes JSON url-t", + "found_download_option_zero": "Nincs letöltési opció", + "found_download_option_one": "{{countFormatted}} Letöltési opció találva", + "found_download_option_other": "{{countFormatted}} Letöltési opciók találva", + "import": "Importálás", + "public": "Publikus", + "private": "Privát", + "friends_only": "Csak barátok", + "privacy": "Adatvédelem", + "profile_visibility": "Profil láthatósága", + "profile_visibility_description": "Válaszd ki, ki láthatja a profilod és könyvtárad", + "required_field": "Ez a mező kötelező", + "source_already_exists": "Ez a forrás már használatban", + "must_be_valid_url": "A forrás egy érvényes URL kell legyen", + "blocked_users": "Letiltott felhasználók", + "user_unblocked": "Felhasználó letiltva", + "enable_achievement_notifications": "Amikor egy achievement feloldva", + "launch_minimized": "Hydra indítása minimalizálva", + "disable_nsfw_alert": "NSFW figyelmeztetés kikapcsolása", + "seed_after_download_complete": "Letöltés utáni seedelés", + "show_hidden_achievement_description": "Rejtett achievementek leírásának megjelenítése feloldás előtt", + "account": "Fiók", + "no_users_blocked": "Nincsenek letiltott felhasználóid", + "subscription_active_until": "Hydra Cloud előfizetésed aktív, eddig: {{date}}", + "manage_subscription": "Előfizetés kezelése", + "update_email": "Email változtatása", + "update_password": "Jelszó változtatása", + "current_email": "Jelenlegi email:", + "no_email_account": "Még nincs beállított emailed", + "account_data_updated_successfully": "Fiókadatok változtatása sikeres", + "renew_subscription": "Hydra Cloud Megújítása", + "subscription_expired_at": "Az előfizetésed lejárt, ekkor: {{date}}", + "no_subscription": "Élvezd a Hydrát a lehető legjobb módon", + "become_subscriber": "Légy Hydra Cloud tag", + "subscription_renew_cancelled": "Automatikus megújítás kikapcsolva", + "subscription_renews_on": "Az előfizetésed megújul, ekkor: {{date}}", + "bill_sent_until": "A következő számlát ezen napon küldjük", + "no_themes": "Úgy látom nincs egyetlen témád sem még, de ne aggódj, kattints ide hogy elkészítsd a remekműved.", + "editor_tab_code": "Code", + "editor_tab_info": "Info", + "editor_tab_save": "Mentés", + "web_store": "Web store", + "clear_themes": "Törlés", + "create_theme": "Létrehozás", + "create_theme_modal_title": "Egyéni téma létrehozása", + "create_theme_modal_description": "Hozz létre egy új témát, hogy testreszabhasd a Hydrát", + "theme_name": "Név", + "insert_theme_name": "Téma neve", + "set_theme": "Téma beállítása", + "unset_theme": "Téma visszavonása", + "delete_theme": "Téma törlése", + "edit_theme": "Téma szerkesztése", + "delete_all_themes": "Összes téma törlése", + "delete_all_themes_description": "Ez törölni fogja az összes témádat", + "delete_theme_description": "Ez törölni fogja a(z) {{theme}} témát", + "cancel": "Mégsem", + "appearance": "Megjelenés", + "enable_torbox": "TorBox bekapcsolása", + "torbox_description": "A TorBox egy olyan premium seedbox szolgáltatás, amely még a piacon elérhető legjobb szerverekkel is felveszi a versenyt.", + "torbox_account_linked": "TorBox fiók összekapcsolva", + "create_real_debrid_account": "Kattints ide ha még nincs Real-Debrid fiókod", + "create_torbox_account": "Kattints ide ha még nincs TorBox fiókod", + "real_debrid_account_linked": "Real-Debrid fiók összekapcsolva", + "name_min_length": "A téma neve legalább 3 karakter hosszú legyen", + "import_theme": "Téma importálása", + "import_theme_description": "Ezt a témát fogod importálni a Témaáruház-ból: {{theme}}", + "error_importing_theme": "Hiba lépett fel a téma importálása közben", + "theme_imported": "Téma sikeresen importálva", + "enable_friend_request_notifications": "Amikor ismerősnek jelölnek", + "enable_auto_install": "Frissítések letöltése automatikusan", + "common_redist": "Alapvető Segédprogramok", + "common_redist_description": "Egyes játékok futtatásához alapvető segédprogram fájlok szükségesek. A problémák elkerülése képpen ajánlott telepíteni őket.", + "install_common_redist": "Telepítés", + "installing_common_redist": "Telepítés alatt…", + "show_download_speed_in_megabytes": "Letöltési sebesség megabájt/másodpercben lévő megjelenítése", + "extract_files_by_default": "Fájlok kicsomagolása letöltés után alapértelmezetten", + "enable_steam_achievements": "Steam-achievementek utáni keresés engedélyezése", + "achievement_custom_notification_position": "Achievement-értesítések egyéni elhelyezése", + "top-left": "Bal felső sarok", + "top-center": "Felső közép", + "top-right": "Jobb felső sarok", + "bottom-left": "Bal alsó sarok", + "bottom-center": "Alsó közép", + "bottom-right": "Jobb alsó sarok", + "enable_achievement_custom_notifications": "Egyéni achievement-értesítések bekapcsolása", + "alignment": "Igazítás", + "variation": "Variáció", + "default": "Alapértelmezett", + "rare": "Ritka", + "platinum": "Platinum", + "hidden": "Rejtett", + "test_notification": "Értesítés tesztelése", + "notification_preview": "Achievement-értesítés Előnézete", + "enable_friend_start_game_notifications": "Amikor egy barátod elkezd játszani egy játékot" }, "notifications": { - "download_complete": "Letöltés befejeződött", - "game_ready_to_install": "{{title}} telepítésre kész", + "download_complete": "Letöltés befejezve", + "game_ready_to_install": "A(z) {{title}} telepítésre kész", "repack_list_updated": "Repack lista frissítve", "repack_count_one": "{{count}} repack hozzáadva", - "repack_count_other": "{{count}} repack hozzáadva" + "repack_count_other": "{{count}} repack hozzáadva", + "new_update_available": "A(z) {{version}} verzió elérhető", + "restart_to_install_update": "Indítsd újra a Hydrát a frissítés telepítéséhez", + "notification_achievement_unlocked_title": "Achievement feloldva: {{game}}", + "notification_achievement_unlocked_body": "{{achievement}} és további {{count}} feloldva", + "new_friend_request_description": "{{displayName}} küldött neked egy barátfelkérést", + "new_friend_request_title": "Új barátfelkérés", + "extraction_complete": "Kicsomagolás befejezve", + "game_extracted": "{{title}} sikeresen kicsomagolva", + "friend_started_playing_game": "{{displayName}} játszani kezdett", + "test_achievement_notification_title": "Ez egy teszt értesítés", + "test_achievement_notification_description": "Elég menő, mi?" }, "system_tray": { "open": "Hydra megnyitása", "quit": "Kilépés" }, "game_card": { + "available_one": "Elérhető", + "available_other": "Elérhető", "no_downloads": "Nincs elérhető letöltés" }, "binary_not_found_modal": { "title": "A programok nincsenek telepítve", - "description": "A Wine vagy a Lutris végrehajtható fájljai nem találhatók a rendszereden", - "instructions": "Ellenőrizd a megfelelő telepítési módot bármelyiküknek a Linux disztribúciódon, hogy a játék normálisan fusson" + "description": "Wine vagy Lutris futtatható fájlok nem találhatók a rendszereden", + "instructions": "Ellenőrízd, hogy melyiket kell helyesen telepíteni a Linux disztribúcióra, hogy a játék normálisan fusson" + }, + "modal": { + "close": "Bezárás gomb" + }, + "forms": { + "toggle_password_visibility": "Jelszó láthatóságának állítása" + }, + "user_profile": { + "amount_hours": "{{amount}} óra", + "amount_minutes": "{{amount}} perc", + "last_time_played": "Utoljára játszva {{period}}", + "activity": "Legutóbbi tevékenység", + "library": "Könyvtár", + "pinned": "Kitűzve", + "total_play_time": "Teljes játszottidő", + "manual_playtime_tooltip": "Ez a játszottidő manuálisan lett frissítve", + "no_recent_activity_title": "Hmmm… itt semmi sincs", + "no_recent_activity_description": "Mostanában nem játszottál semmivel. Hát ideje ezt megváltoztatni!", + "display_name": "Profilnév", + "saving": "Mentésben", + "save": "Mentés", + "edit_profile": "Profil Szerkesztése", + "saved_successfully": "Sikeresen elmentve", + "try_again": "Kérlek, próbálkozz újra", + "sign_out_modal_title": "Biztos vagy ebben?", + "cancel": "Mégsem", + "successfully_signed_out": "Sikeresen kijelentkezve", + "sign_out": "Kijelentkezés", + "playing_for": "Játékban: {{amount}}", + "sign_out_modal_text": "A könyvtár a jelenlegi fiókodhoz van csatolva. Kijelentkezéskor a könyvtár többé nem lesz látható, és az eddigi előrehaladás nem lesz mentve. Folytatod a kijelentkezést?", + "add_friends": "Barát bejelölés", + "add": "Elküld", + "friend_code": "Barát kód", + "see_profile": "Profil megtekintése", + "sending": "Küldés..", + "friend_request_sent": "Barátfelkérés elküldve", + "friends": "Barátok", + "friends_list": "Barát lista", + "user_not_found": "Felhasználó nem találva", + "block_user": "Felhasználó letiltása", + "add_friend": "Barát bejelölése", + "request_sent": "Kérés elküldve", + "request_received": "Barátfelkérést kaptál", + "accept_request": "Kérés elfogadása", + "ignore_request": "Kérés ignorálása", + "cancel_request": "Kérés visszavonása", + "undo_friendship": "Barát eltávolítása", + "request_accepted": "Barátfelkérés elfogadva", + "user_blocked_successfully": "Felhasználó sikeresen letiltva", + "user_block_modal_text": "Ez által letiltod őt: {{displayName}}", + "blocked_users": "Letiltott felhasználók", + "unblock": "Tiltás feloldása", + "no_friends_added": "Nincs bejelölt barátod", + "pending": "Függőben", + "no_pending_invites": "Nincs függőben lévő barátfelkérésed", + "no_blocked_users": "Nincs letiltott felhasználó", + "friend_code_copied": "Barát kód kimásolva", + "undo_friendship_modal_text": "Ezáltal megszünteted a barátságod vele: {{displayName}}", + "privacy_hint": "Hogy beállítsd ki láthassa ezt, menj a <0>Beállítások menüjébe", + "locked_profile": "Ez a profil privát", + "image_process_failure": "Hiba a kép feldolgozása közben", + "required_field": "Ez a mező kötelező", + "displayname_min_length": "A megjelenített névnek legalább 3 karakter hosszúnak kell lennie", + "displayname_max_length": "A megjelenített név hossza legfeljebb 50 karakter lehet", + "report_profile": "Profil bejelentése", + "report_reason": "Miért jelented ezt a profilt?", + "report_description": "További információ", + "report_description_placeholder": "További információ", + "report": "Bejelentés", + "report_reason_hate": "Gyűlöletbeszéd", + "report_reason_sexual_content": "Szexuális tartalom", + "report_reason_violence": "Fenyegető", + "report_reason_spam": "Spam", + "report_reason_other": "Egyéb", + "profile_reported": "Profil jelentve", + "your_friend_code": "A barát kódod:", + "upload_banner": "Banner feltöltés", + "uploading_banner": "Banner feltöltése…", + "background_image_updated": "Banner frissítve", + "stats": "Statisztikák", + "achievements": "achievementek", + "games": "Játékok", + "top_percentile": "Top {{percentile}}%", + "ranking_updated_weekly": "A rangsor hetente frissül.", + "playing": "Játékban: {{game}}", + "achievements_unlocked": "Achievementek feloldva", + "earned_points": "Megszerzett pontok", + "show_achievements_on_profile": "Mutasd az achievementjeid a profilodon", + "show_points_on_profile": "Mutasd a megszerzett pontjaid a profilodon", + "error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barátkódot", + "friend_code_length_error": "A barátkódnak 8 karakterből kell állnia" + }, + "achievement": { + "achievement_unlocked": "Achievement feloldva", + "user_achievements": "{{displayName}} Achievementjei", + "your_achievements": "A te Achievementjeid", + "unlocked_at": "Feloldva ekkor: {{date}}", + "subscription_needed": "A tartalom megtekintéséhez Hydra Cloud előfizetés szükséges", + "new_achievements_unlocked": "{{achievementCount}} új achievementet oldottál fel {{gameCount}} játékban", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementek", + "achievements_unlocked_for_game": "{{achievementCount}} új achievementet oldottál fel a(z) {{gameTitle}} játékban", + "hidden_achievement_tooltip": "Ez egy rejtett achievement", + "achievement_earn_points": "Szerezz be {{points}} pontot ezzel az achievement-el", + "earned_points": "Megszerzett pontok:", + "available_points": "Elérhető pontok:", + "how_to_earn_achievements_points": "Hogy lehet elérni achievement pontokat?" + }, + "hydra_cloud": { + "subscription_tour_title": "Hydra Cloud Előfizetés", + "subscribe_now": "Előfizetés", + "cloud_saving": "Felhőmentés", + "cloud_achievements": "Mentsd az achievementjeid el a felhőben", + "animated_profile_picture": "Animált profilkép", + "premium_support": "Premium Támogatás", + "show_and_compare_achievements": "Mutasd és hasonlítsd az elért achievementjeid másokéhoz", + "animated_profile_banner": "Animált profil banner", + "hydra_cloud": "Hydra Cloud", + "hydra_cloud_feature_found": "Épp felfedeztél egy Hydra Cloud funkciót!", + "learn_more": "Tudj meg többet", + "debrid_description": "Akár 4x gyorsabb letöltés a Nimbusszal" } -} +} \ No newline at end of file From 7ba65b335705c13d0aab1d5ae2d3460b84dfe0d0 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Sun, 28 Sep 2025 02:20:33 +0200 Subject: [PATCH 02/52] Slight Adjustment to 'Hot Now' --- src/locales/hu/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 52b17466..b5cce7bc 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -8,7 +8,7 @@ "surprise_me": "Lepj meg", "no_results": "Nincs találat", "start_typing": "Kereséshez gépelj...", - "hot": "Felkapott most", + "hot": "Most felkapott", "weekly": "📅 A hét felkapott játékai", "achievements": "🏆 Teljesítendő játékok" }, From 1d5be940f9416ebe11639cbc0e56aede3dcc7e68 Mon Sep 17 00:00:00 2001 From: caduHD4 Date: Sun, 28 Sep 2025 01:00:02 -0300 Subject: [PATCH 03/52] feat: add filter by source in modal repacks --- src/locales/en/translation.json | 3 +- src/locales/pt-BR/translation.json | 3 +- .../game-details/modals/repacks-modal.scss | 140 +++++++++++++- .../game-details/modals/repacks-modal.tsx | 180 ++++++++++++++---- 4 files changed, 286 insertions(+), 40 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 781cd946..4542d448 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -244,7 +244,8 @@ "supported_languages": "Supported languages", "language": "Language", "caption": "Caption", - "audio": "Audio" + "audio": "Audio", + "filter_by_source": "Filter by source" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index fd6fbd97..20e21fd5 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -221,7 +221,8 @@ "supported_languages": "Idiomas suportados", "language": "Idioma", "caption": "Legenda", - "audio": "Áudio" + "audio": "Áudio", + "filter_by_source": "Filtrar por fonte" }, "activation": { "title": "Ativação", diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.scss b/src/renderer/src/pages/game-details/modals/repacks-modal.scss index 2eb2512d..c1f3b3ba 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.scss +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.scss @@ -2,7 +2,48 @@ .repacks-modal { &__filter-container { - margin-bottom: calc(globals.$spacing-unit * 2); + margin-bottom: 1rem; + transition: min-height 0.3s ease; + + &--drawer-open { + min-height: 250px; + } + } + + &__filter-top { + margin-bottom: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + &__filter-toggle { + align-self: flex-start; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: globals.$small-font-size; + font-weight: 600; + color: var(--color-text-secondary); + padding: 0.5rem 0.75rem; + border-radius: 6px; + transition: background-color 0.2s ease; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + &__filter-toggle { + align-self: flex-start; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: globals.$small-font-size; + font-weight: 600; + color: var(--color-text-secondary); + padding: 0.5rem 0.75rem; + border-radius: 6px; + transition: background-color 0.2s ease; } &__repacks { @@ -29,4 +70,101 @@ &__repack-info { font-size: globals.$small-font-size; } + + &__no-results { + width: 100%; + padding: calc(globals.$spacing-unit * 4) 0; + text-align: center; + color: globals.$muted-color; + font-size: globals.$small-font-size; + display: flex; + align-items: center; + justify-content: center; + } + + &__no-results-content { + display: flex; + flex-direction: column; + align-items: center; + gap: calc(globals.$spacing-unit * 1.5); + max-width: 480px; + width: 100%; + } + + &__no-results-text { + color: globals.$muted-color; + font-size: globals.$small-font-size; + text-align: center; + } + + &__no-results-button { + display: flex; + justify-content: center; + width: 100%; + } + + &__download-sources { + padding: 0; + background-color: var(--color-background-light); + border-radius: 8px; + margin-bottom: 1rem; + margin-top: calc(globals.$spacing-unit * 0.5); + max-height: 0; + overflow: hidden; + transition: + max-height 0.3s ease, + padding 0.3s ease; + + &--open { + padding: 0.75rem; + max-height: 250px; + } + } + + &__filter-label { + display: none; + font-size: globals.$small-font-size; + font-weight: 600; + margin-bottom: 0.75rem; + color: var(--color-text-secondary); + width: 100%; + } + + &__source-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.5rem; + max-height: 200px; + overflow-y: auto; + overflow-x: hidden; + align-items: start; + padding-right: 0.25rem; + } + + &__source-item { + padding: 0.35rem 0.5rem; + background: var(--color-surface, rgba(0, 0, 0, 0.03)); + border: 1px solid var(--color-border); + border-radius: 6px; + display: flex; + align-items: center; + min-height: auto; + box-sizing: border-box; + width: 100%; + } + + &__source-item :global(.checkbox-field) { + width: 100%; + min-width: 0; + } + + &__source-item :global(.checkbox-field__label) { + white-space: normal; + overflow: visible; + text-overflow: unset; + display: block; + font-size: 0.85rem; + width: 100%; + word-break: break-word; + } } diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx index d38c7856..b2c55d4f 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx @@ -1,5 +1,11 @@ import { useContext, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { + PlusCircleIcon, + ChevronDownIcon, + ChevronUpIcon, +} from "@primer/octicons-react"; import { Badge, @@ -7,7 +13,10 @@ import { DebridBadge, Modal, TextField, + CheckboxField, } from "@renderer/components"; +import { downloadSourcesTable } from "@renderer/dexie"; +import type { DownloadSource } from "@types"; import type { GameRepack } from "@types"; import { DownloadSettingsModal } from "./download-settings-modal"; @@ -36,6 +45,11 @@ export function RepacksModal({ const [filteredRepacks, setFilteredRepacks] = useState([]); const [repack, setRepack] = useState(null); const [showSelectFolderModal, setShowSelectFolderModal] = useState(false); + const [downloadSources, setDownloadSources] = useState([]); + const [selectedFingerprints, setSelectedFingerprints] = useState( + [] + ); + const [filterTerm, setFilterTerm] = useState(""); const [hashesInDebrid, setHashesInDebrid] = useState>( {} @@ -46,6 +60,7 @@ export function RepacksModal({ const { t } = useTranslation("game_details"); const { formatDate } = useDate(); + const navigate = useNavigate(); const getHashFromMagnet = (magnet: string) => { if (!magnet || typeof magnet !== "string") { @@ -90,8 +105,37 @@ export function RepacksModal({ }, [repacks, hashesInDebrid]); useEffect(() => { - setFilteredRepacks(sortedRepacks); - }, [sortedRepacks, visible, game]); + downloadSourcesTable.toArray().then((sources) => { + const uniqueRepackers = new Set(sortedRepacks.map((r) => r.repacker)); + const filteredSources = sources.filter( + (s) => s.name && uniqueRepackers.has(s.name) && !!s.fingerprint + ); + setDownloadSources(filteredSources); + }); + }, [sortedRepacks]); + + useEffect(() => { + const term = filterTerm.trim().toLowerCase(); + + const byTerm = sortedRepacks.filter((repack) => { + if (!term) return true; + const lowerTitle = repack.title.toLowerCase(); + const lowerRepacker = repack.repacker.toLowerCase(); + return lowerTitle.includes(term) || lowerRepacker.includes(term); + }); + + const bySource = byTerm.filter((repack) => { + if (selectedFingerprints.length === 0) return true; + + return downloadSources.some( + (src) => + selectedFingerprints.includes(src.fingerprint) && + src.name === repack.repacker + ); + }); + + setFilteredRepacks(bySource); + }, [sortedRepacks, filterTerm, selectedFingerprints, downloadSources]); const handleRepackClick = (repack: GameRepack) => { setRepack(repack); @@ -99,17 +143,14 @@ export function RepacksModal({ }; const handleFilter: React.ChangeEventHandler = (event) => { - const term = event.target.value.toLocaleLowerCase(); + setFilterTerm(event.target.value); + }; - setFilteredRepacks( - sortedRepacks.filter((repack) => { - const lowerCaseTitle = repack.title.toLowerCase(); - const lowerCaseRepacker = repack.repacker.toLowerCase(); - - return [lowerCaseTitle, lowerCaseRepacker].some((value) => - value.includes(term) - ); - }) + const toggleFingerprint = (fingerprint: string) => { + setSelectedFingerprints((prev) => + prev.includes(fingerprint) + ? prev.filter((f) => f !== fingerprint) + : [...prev, fingerprint] ); }; @@ -118,6 +159,8 @@ export function RepacksModal({ return repack.uris.some((uri) => uri.includes(game.download!.uri)); }; + const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false); + return ( <> -
- +
+
+ + +
+ +
+
+ {downloadSources.map((source) => { + const label = source.name || source.url; + const truncatedLabel = + label.length > 16 ? label.substring(0, 16) + "..." : label; + return ( +
+ toggleFingerprint(source.fingerprint)} + /> +
+ ); + })} +
+
- {filteredRepacks.map((repack) => { - const isLastDownloadedOption = checkIfLastDownloadedOption(repack); + {filteredRepacks.length === 0 ? ( +
+
+
+ {t("no_repacks_found")} +
+
+ +
+
+
+ ) : ( + filteredRepacks.map((repack) => { + const isLastDownloadedOption = + checkIfLastDownloadedOption(repack); - return ( - - ); - })} + {hashesInDebrid[getHashFromMagnet(repack.uris[0]) ?? ""] && ( + + )} + + ); + }) + )}
From 6f5baf0df59eef0c33500d701b11e4d27ac3eeab Mon Sep 17 00:00:00 2001 From: caduHD4 Date: Sun, 28 Sep 2025 01:16:33 -0300 Subject: [PATCH 04/52] refactor: duplicate code --- .../pages/game-details/modals/repacks-modal.scss | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.scss b/src/renderer/src/pages/game-details/modals/repacks-modal.scss index c1f3b3ba..cecadb91 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.scss +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.scss @@ -17,22 +17,6 @@ gap: 0.5rem; } - &__filter-toggle { - align-self: flex-start; - display: flex; - align-items: center; - gap: 0.5rem; - font-size: globals.$small-font-size; - font-weight: 600; - color: var(--color-text-secondary); - padding: 0.5rem 0.75rem; - border-radius: 6px; - transition: background-color 0.2s ease; - display: flex; - flex-direction: column; - gap: 0.5rem; - } - &__filter-toggle { align-self: flex-start; display: flex; From 81f001ade455b747a9cdc87cdb2551a52f94e934 Mon Sep 17 00:00:00 2001 From: caduHD4 Date: Sun, 28 Sep 2025 02:18:35 -0300 Subject: [PATCH 05/52] fix: add 'no sources found for this game' message in translation files --- src/locales/en/translation.json | 3 ++- src/locales/pt-BR/translation.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 4542d448..52a9473d 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -245,7 +245,8 @@ "language": "Language", "caption": "Caption", "audio": "Audio", - "filter_by_source": "Filter by source" + "filter_by_source": "Filter by source", + "no_repacks_found": "No sources found for this game" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 20e21fd5..d7e67eb1 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -222,7 +222,8 @@ "language": "Idioma", "caption": "Legenda", "audio": "Áudio", - "filter_by_source": "Filtrar por fonte" + "filter_by_source": "Filtrar por fonte", + "no_repacks_found": "Nenhuma fonte encontrada para este jogo" }, "activation": { "title": "Ativação", From e43f80b830fa5f0d0dd5fdb18554d8ad6ab98381 Mon Sep 17 00:00:00 2001 From: Stormm232 <96331770+Stormm232@users.noreply.github.com> Date: Sun, 28 Sep 2025 15:47:50 +0200 Subject: [PATCH 06/52] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c54c431c..943a03b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -on: pull_request +on: workflow_dispatch: jobs: build: From 4f3113002bd91fb516b870e6e22c63369afbdf5d Mon Sep 17 00:00:00 2001 From: Stormm232 <96331770+Stormm232@users.noreply.github.com> Date: Sun, 28 Sep 2025 15:48:15 +0200 Subject: [PATCH 07/52] Update build.yml --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 943a03b0..97bcf16e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -on: workflow_dispatch: +on: + workflow_dispatch: jobs: build: From 35ce07c5f4e084740b5fb1c9ade2da68bac0c5df Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Sun, 28 Sep 2025 16:47:48 +0200 Subject: [PATCH 08/52] Webstore was left untranslated --- src/locales/hu/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index b5cce7bc..24750c8f 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -22,7 +22,7 @@ "downloading": "{{title}} ({{percentage}} - Letöltés…)", "filter": "Könyvtár szűrése", "home": "Főoldal", - "queued": "A(z) {{title}} (Várakozósoron van)", + "queued": "A(z) {{title}} (Várakozósorban van)", "game_has_no_executable": "A játékhoz nincs kiválasztva futtatható fájl", "sign_in": "Bejelentkezés", "friends": "Barátok", @@ -355,7 +355,7 @@ "editor_tab_code": "Code", "editor_tab_info": "Info", "editor_tab_save": "Mentés", - "web_store": "Web store", + "web_store": "Webáruház", "clear_themes": "Törlés", "create_theme": "Létrehozás", "create_theme_modal_title": "Egyéni téma létrehozása", From 0e2cc2b25c7b1d367c0e78d904c51a6e5a8720fa Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Sun, 28 Sep 2025 19:18:43 +0200 Subject: [PATCH 09/52] Adjustment to the translation --- src/locales/hu/translation.json | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 24750c8f..c6449f88 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -124,7 +124,7 @@ "remove_from_library_description": "Ezzel eltávolítod a játékot {{game}} a könyvtáradból", "options": "Beállítások", "executable_section_title": "Futtatható fájl", - "executable_section_description": "A fájl helye amely futtatásra fog kerülni amikor a \"Play\" levan nyomva", + "executable_section_description": "A fájl helye amely futtatásra fog kerülni amikor a \"Játék\" levan nyomva", "downloads_section_title": "Letöltések", "downloads_section_description": "Csekkold le a játék frissítéseit vagy más verzióit", "danger_zone_section_title": "Veszélyzóna", @@ -151,7 +151,7 @@ "achievements": "Achievementek", "achievements_count": "Achievementek {{unlockedCount}}/{{achievementsCount}}", "cloud_save": "Mentés felhőben", - "cloud_save_description": "Mentsd el az előrehaladásodat a felhőben, majd folytasd egy másik eszközön", + "cloud_save_description": "Mentsd el az előrehaladásod a felhőben, majd folytasd egy másik eszközön", "backups": "Biztonsági másolatok", "install_backup": "Telepít", "delete_backup": "Töröl", @@ -165,7 +165,7 @@ "backup_deleted": "Biztonsági mentés törölve", "backup_restored": "Biztonsági mentés helyreállítva", "see_all_achievements": "Achievementlista megtekintése", - "sign_in_to_see_achievements": "Jelentkezz be hogy lásd a(z) achievementjeid", + "sign_in_to_see_achievements": "Jelentkezz be hogy lásd az achievementjeid", "mapping_method_automatic": "Automatikus", "mapping_method_manual": "Kézi", "mapping_method_label": "Térképezési módszer", @@ -181,7 +181,7 @@ "no_download_option_info": "Nincs elérhető információ", "backup_deletion_failed": "Biztonsági mentés törlése sikertelen", "max_number_of_artifacts_reached": "A játék biztonsági mentéseinek száma elérte a határt", - "achievements_not_sync": "Tekintsd meg hogyan kell szinkronizálni a achievementjeid", + "achievements_not_sync": "Tekintsd meg hogyan kell szinkronizálni az achievementjeid", "manage_files_description": "Kezeld mely fájlokról készül biztonsági másolat, és melyek állíthatók vissza", "select_folder": "Mappa kiválasztása", "backup_from": "Biztonsági másolat: {{date}}", @@ -190,7 +190,7 @@ "custom_backup_location_set": "Egyéni biztonsági mentési hely", "no_directory_selected": "Nincs kiválasztva mappa", "no_write_permission": "Nem lehet ebbe a mappába letölteni. Kattints ide további információért.", - "reset_achievements": "Achievement nullázása", + "reset_achievements": "Achievementek nullázása", "reset_achievements_description": "Ez az összes achievementet nullázza a {{game}} játékhoz", "reset_achievements_title": "Biztos vagy ebben?", "reset_achievements_success": "Achievementek sikeresen nullázva", @@ -209,9 +209,9 @@ "download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", "game_removed_from_favorites": "Játék eltávolítva a kedvencek közül", - "game_added_to_favorites": "Játék rögzítve a kedvencek közé", - "game_removed_from_pinned": "Játék eltávolítva a rögzítettekből", - "game_added_to_pinned": "Játék sikeresen rögzítve", + "game_added_to_favorites": "Játék hozzáadva a kedvencekhez", + "game_removed_from_pinned": "Játék eltávolítva a kitűzöttek közül", + "game_added_to_pinned": "Játék sikeresen kitűzve", "automatically_extract_downloaded_files": "Automatikus kibontása a letöltött fájloknak", "create_start_menu_shortcut": "Start menü parancsikon létrehozása", "invalid_wine_prefix_path": "Érvénytelen Wine prefix elérési útvonal", @@ -282,13 +282,13 @@ "launch_with_system": "Hydra automatikus indítása rendszer indításakor", "general": "Általános", "behavior": "Működés", - "download_sources": "Forrás letöltések", + "download_sources": "Letöltési források", "language": "Nyelv", "api_token": "API Token", "enable_real_debrid": "Real-Debrid Bekapcsolása", "real_debrid_description": "A Real-Debrid egy korlátozásmentes letöltőprogram, lehetővé teszi a fájlok gyors letöltését, és csak az internetkapcsolat sebessége szab határt.", "debrid_invalid_token": "Érvénytelen API token", - "debrid_api_token_hint": "Az API Tokened itt találhatod <0>here", + "debrid_api_token_hint": "Az API tokened <0>itt található", "real_debrid_free_account_error": "Ez a fiók: \"{{username}}\" egy ingyenes fiók. Kérlek iratkozz fel a Real-Debrid-re", "debrid_linked_message": "Fiók összekapcsolva: \"{{username}}\" ", "save_changes": "Változtatások mentése", @@ -300,7 +300,7 @@ "download_count_zero": "Nincs letöltési opció", "download_count_one": "{{countFormatted}} letöltési opció", "download_count_other": "{{countFormatted}} letöltési opció", - "download_source_url": "URL forrás letöltése", + "download_source_url": "URL forrás:", "add_download_source_description": "Helyezd be a .json fájl URL-jét", "download_source_up_to_date": "Naprakész", "download_source_errored": "Hiba történt", @@ -334,7 +334,7 @@ "launch_minimized": "Hydra indítása minimalizálva", "disable_nsfw_alert": "NSFW figyelmeztetés kikapcsolása", "seed_after_download_complete": "Letöltés utáni seedelés", - "show_hidden_achievement_description": "Rejtett achievementek leírásának megjelenítése feloldás előtt", + "show_hidden_achievement_description": "Rejtett achievementek leírása megjelenítése feloldás előtt", "account": "Fiók", "no_users_blocked": "Nincsenek letiltott felhasználóid", "subscription_active_until": "Hydra Cloud előfizetésed aktív, eddig: {{date}}", @@ -351,7 +351,7 @@ "subscription_renew_cancelled": "Automatikus megújítás kikapcsolva", "subscription_renews_on": "Az előfizetésed megújul, ekkor: {{date}}", "bill_sent_until": "A következő számlát ezen napon küldjük", - "no_themes": "Úgy látom nincs egyetlen témád sem még, de ne aggódj, kattints ide hogy elkészítsd a remekműved.", + "no_themes": "Úgy látom nincs egyetlen témád sem még, de ne aggódj, kattints ide hogy elkészítsd a remekművedet.", "editor_tab_code": "Code", "editor_tab_info": "Info", "editor_tab_save": "Mentés", @@ -359,9 +359,9 @@ "clear_themes": "Törlés", "create_theme": "Létrehozás", "create_theme_modal_title": "Egyéni téma létrehozása", - "create_theme_modal_description": "Hozz létre egy új témát, hogy testreszabhasd a Hydrát", - "theme_name": "Név", - "insert_theme_name": "Téma neve", + "create_theme_modal_description": "Hozz létre egy új témát, hogy testreszabd a Hydrát", + "theme_name": "Téma neve", + "insert_theme_name": "Adj a témádnak nevet", "set_theme": "Téma beállítása", "unset_theme": "Téma visszavonása", "delete_theme": "Téma törlése", @@ -389,7 +389,7 @@ "install_common_redist": "Telepítés", "installing_common_redist": "Telepítés alatt…", "show_download_speed_in_megabytes": "Letöltési sebesség megabájt/másodpercben lévő megjelenítése", - "extract_files_by_default": "Fájlok kicsomagolása letöltés után alapértelmezetten", + "extract_files_by_default": "Fájlok kicsomagolása letöltés után", "enable_steam_achievements": "Steam-achievementek utáni keresés engedélyezése", "achievement_custom_notification_position": "Achievement-értesítések egyéni elhelyezése", "top-left": "Bal felső sarok", @@ -406,7 +406,7 @@ "platinum": "Platinum", "hidden": "Rejtett", "test_notification": "Értesítés tesztelése", - "notification_preview": "Achievement-értesítés Előnézete", + "notification_preview": "Achievement-értesítés - előnézet", "enable_friend_start_game_notifications": "Amikor egy barátod elkezd játszani egy játékot" }, "notifications": { @@ -503,7 +503,7 @@ "image_process_failure": "Hiba a kép feldolgozása közben", "required_field": "Ez a mező kötelező", "displayname_min_length": "A megjelenített névnek legalább 3 karakter hosszúnak kell lennie", - "displayname_max_length": "A megjelenített név hossza legfeljebb 50 karakter lehet", + "displayname_max_length" : "A megjelenített név hossza legfeljebb 50 karakter lehet", "report_profile": "Profil bejelentése", "report_reason": "Miért jelented ezt a profilt?", "report_description": "További információ", @@ -529,8 +529,8 @@ "earned_points": "Megszerzett pontok", "show_achievements_on_profile": "Mutasd az achievementjeid a profilodon", "show_points_on_profile": "Mutasd a megszerzett pontjaid a profilodon", - "error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barátkódot", - "friend_code_length_error": "A barátkódnak 8 karakterből kell állnia" + "error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barát kódot", + "friend_code_length_error": "A barát kódnak 8 karakterből kell állnia" }, "achievement": { "achievement_unlocked": "Achievement feloldva", @@ -554,7 +554,7 @@ "cloud_achievements": "Mentsd az achievementjeid el a felhőben", "animated_profile_picture": "Animált profilkép", "premium_support": "Premium Támogatás", - "show_and_compare_achievements": "Mutasd és hasonlítsd az elért achievementjeid másokéhoz", + "show_and_compare_achievements": "Jelenítsd és hasonlítsd az elért achievementjeid másokéhoz", "animated_profile_banner": "Animált profil banner", "hydra_cloud": "Hydra Cloud", "hydra_cloud_feature_found": "Épp felfedeztél egy Hydra Cloud funkciót!", From dfd640ebdac8d2c203781f6b070c7fe857eabfee Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 00:14:47 +0300 Subject: [PATCH 10/52] feat: add revert title for non-custom games --- .../game-details/modals/edit-game-modal.tsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 04a27779..7bfc48fa 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -157,6 +157,24 @@ export function EditGameModal({ setAssetPath(assetType, ""); }; + const getOriginalTitle = (): string => { + if (!game) return ""; + + // For non-custom games, the original title is from shopDetails assets + return shopDetails?.assets?.title || game.title || ""; + }; + + const handleRestoreDefaultTitle = () => { + const originalTitle = getOriginalTitle(); + setGameName(originalTitle); + }; + + const isTitleChanged = (): boolean => { + if (!game || isCustomGame(game)) return false; + const originalTitle = getOriginalTitle(); + return gameName.trim() !== originalTitle.trim(); + }; + const [dragOverTarget, setDragOverTarget] = useState(null); const handleDragOver = (e: React.DragEvent) => { @@ -489,6 +507,19 @@ export function EditGameModal({ onChange={handleGameNameChange} theme="dark" disabled={isUpdating} + rightContent={ + isTitleChanged() && ( + + ) + } />
From 5bf5ca35047b2dce5fa9203ac7e59809f31389d3 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Mon, 29 Sep 2025 00:07:49 +0200 Subject: [PATCH 11/52] Adjustment --- src/locales/hu/translation.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index c6449f88..4d1a65d3 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -124,7 +124,7 @@ "remove_from_library_description": "Ezzel eltávolítod a játékot {{game}} a könyvtáradból", "options": "Beállítások", "executable_section_title": "Futtatható fájl", - "executable_section_description": "A fájl helye amely futtatásra fog kerülni amikor a \"Játék\" levan nyomva", + "executable_section_description": "A fájl amely futtatásra fog kerülni amikor a \"Játék\" lenyomásra kerül", "downloads_section_title": "Letöltések", "downloads_section_description": "Csekkold le a játék frissítéseit vagy más verzióit", "danger_zone_section_title": "Veszélyzóna", @@ -219,7 +219,7 @@ "missing_wine_prefix": "Wine prefix szükséges a biztonsági másolat létrehozásához Linux rendszeren.", "artifact_renamed": "Biztonsági mentés sikeresen átnevezve", "rename_artifact": "Biztonsági mentés átnevezése", - "rename_artifact_description": "Nevezd át a biztonsági másolatot egy leíróbb névre.", + "rename_artifact_description": "Nevezd át a biztonsági másolatot egy leíróbb névre", "artifact_name_label": "Biztonsági másolat neve", "artifact_name_placeholder": "Adj egy nevet a biztonsági mentésnek", "save_changes": "Változtatások mentése", @@ -359,7 +359,7 @@ "clear_themes": "Törlés", "create_theme": "Létrehozás", "create_theme_modal_title": "Egyéni téma létrehozása", - "create_theme_modal_description": "Hozz létre egy új témát, hogy testreszabd a Hydrát", + "create_theme_modal_description": "Hozz létre egy új témát, hogy testreszabhasd a Hydrát ahogy szeretnéd", "theme_name": "Téma neve", "insert_theme_name": "Adj a témádnak nevet", "set_theme": "Téma beállítása", From d7d920a7b7ab21beff5557b0491b320f9c27668f Mon Sep 17 00:00:00 2001 From: Stormm232 <96331770+Stormm232@users.noreply.github.com> Date: Mon, 29 Sep 2025 02:52:28 +0200 Subject: [PATCH 12/52] Add files via upload --- MAIN_VITE_AUTH_URL.env | 1 + 1 file changed, 1 insertion(+) create mode 100644 MAIN_VITE_AUTH_URL.env diff --git a/MAIN_VITE_AUTH_URL.env b/MAIN_VITE_AUTH_URL.env new file mode 100644 index 00000000..9d34da54 --- /dev/null +++ b/MAIN_VITE_AUTH_URL.env @@ -0,0 +1 @@ +MAIN_VITE_AUTH_URL=https://auth.hydralauncher.gg \ No newline at end of file From e678cc38015aba8a494f37a369ab651e7d07dee9 Mon Sep 17 00:00:00 2001 From: Stormm232 <96331770+Stormm232@users.noreply.github.com> Date: Mon, 29 Sep 2025 04:38:20 +0200 Subject: [PATCH 13/52] MAIN_VITE_AUTH_URL.env --- MAIN_VITE_AUTH_URL.env | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MAIN_VITE_AUTH_URL.env b/MAIN_VITE_AUTH_URL.env index 9d34da54..c0df04ae 100644 --- a/MAIN_VITE_AUTH_URL.env +++ b/MAIN_VITE_AUTH_URL.env @@ -1 +1,8 @@ -MAIN_VITE_AUTH_URL=https://auth.hydralauncher.gg \ No newline at end of file +# Environment variables for production + +MAIN_VITE_API_URL=https://hydra-api-us-east-1.losbroxas.org +MAIN_VITE_AUTH_URL=https://auth.hydralauncher.gg +MAIN_VITE_CHECKOUT_URL=https://checkout.hydralauncher.gg +MAIN_VITE_EXTERNAL_RESOURCES_URL=https://assets.hydralauncher.gg +RENDERER_VITE_EXTERNAL_RESOURCES_URL=https://assets.hydralauncher.gg +MAIN_VITE_WS_URL=wss://ws.hydralauncher.gg From f3b4898e9ce75ae653a21d67b3c453023c457beb Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 14:51:08 +0300 Subject: [PATCH 14/52] feat: proper cleanup of unused assets --- .../library/remove-game-from-library.ts | 53 +++++++++++++++++-- src/main/events/library/update-custom-game.ts | 38 +++++++++++++ .../library/update-game-custom-assets.ts | 29 ++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index 6a33ffaf..4868d588 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -1,6 +1,6 @@ import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; -import { gamesSublevel, levelKeys } from "@main/level"; +import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop } from "@types"; const removeGameFromLibrary = async ( @@ -12,15 +12,62 @@ const removeGameFromLibrary = async ( const game = await gamesSublevel.get(gameKey); if (game) { - await gamesSublevel.put(gameKey, { + // Collect asset paths that need to be cleaned up before marking as deleted + const assetPathsToDelete: string[] = []; + + const assetUrls = game.shop === "custom" + ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] + : [game.customIconUrl, game.customLogoImageUrl, game.customHeroImageUrl]; + + assetUrls.forEach(url => { + if (url?.startsWith("local:")) { + assetPathsToDelete.push(url.replace("local:", "")); + } + }); + + + const updatedGame = { ...game, isDeleted: true, executablePath: null, - }); + ...(game.shop !== "custom" && { + customIconUrl: null, + customLogoImageUrl: null, + customHeroImageUrl: null, + }), + }; + + await gamesSublevel.put(gameKey, updatedGame); + + + if (game.shop !== "custom") { + const existingAssets = await gamesShopAssetsSublevel.get(gameKey); + if (existingAssets) { + const resetAssets = { + ...existingAssets, + title: existingAssets.title, + }; + await gamesShopAssetsSublevel.put(gameKey, resetAssets); + } + } if (game?.remoteId) { HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {}); } + + + if (assetPathsToDelete.length > 0) { + const fs = await import("fs"); + for (const assetPath of assetPathsToDelete) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + console.warn(`Failed to delete asset ${assetPath}:`, error); + } + } + } } }; diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 6152c0df..168e0050 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -18,6 +18,30 @@ const updateCustomGame = async ( throw new Error("Game not found"); } + // Collect old asset paths that will be replaced + const oldAssetPaths: string[] = []; + + if (existingGame.iconUrl && iconUrl && existingGame.iconUrl !== iconUrl && existingGame.iconUrl.startsWith("local:")) { + oldAssetPaths.push(existingGame.iconUrl.replace("local:", "")); + } + if (existingGame.iconUrl && !iconUrl && existingGame.iconUrl.startsWith("local:")) { + oldAssetPaths.push(existingGame.iconUrl.replace("local:", "")); + } + + if (existingGame.logoImageUrl && logoImageUrl && existingGame.logoImageUrl !== logoImageUrl && existingGame.logoImageUrl.startsWith("local:")) { + oldAssetPaths.push(existingGame.logoImageUrl.replace("local:", "")); + } + if (existingGame.logoImageUrl && !logoImageUrl && existingGame.logoImageUrl.startsWith("local:")) { + oldAssetPaths.push(existingGame.logoImageUrl.replace("local:", "")); + } + + if (existingGame.libraryHeroImageUrl && libraryHeroImageUrl && existingGame.libraryHeroImageUrl !== libraryHeroImageUrl && existingGame.libraryHeroImageUrl.startsWith("local:")) { + oldAssetPaths.push(existingGame.libraryHeroImageUrl.replace("local:", "")); + } + if (existingGame.libraryHeroImageUrl && !libraryHeroImageUrl && existingGame.libraryHeroImageUrl.startsWith("local:")) { + oldAssetPaths.push(existingGame.libraryHeroImageUrl.replace("local:", "")); + } + const updatedGame = { ...existingGame, title, @@ -43,6 +67,20 @@ const updateCustomGame = async ( await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } + // Manually delete specific old asset files instead of running full cleanup + if (oldAssetPaths.length > 0) { + const fs = await import("fs"); + for (const assetPath of oldAssetPaths) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + console.warn(`Failed to delete old asset ${assetPath}:`, error); + } + } + } + return updatedGame; }; diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 866cd60e..f8206904 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -18,6 +18,21 @@ const updateGameCustomAssets = async ( throw new Error("Game not found"); } + // Collect old custom asset paths that will be replaced + const oldAssetPaths: string[] = []; + + const assetPairs = [ + { existing: existingGame.customIconUrl, new: customIconUrl }, + { existing: existingGame.customLogoImageUrl, new: customLogoImageUrl }, + { existing: existingGame.customHeroImageUrl, new: customHeroImageUrl } + ]; + + assetPairs.forEach(({ existing, new: newUrl }) => { + if (existing && newUrl !== undefined && existing !== newUrl && existing.startsWith("local:")) { + oldAssetPaths.push(existing.replace("local:", "")); + } + }); + const updatedGame = { ...existingGame, title, @@ -39,6 +54,20 @@ const updateGameCustomAssets = async ( await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } + // Manually delete specific old custom asset files instead of running full cleanup + if (oldAssetPaths.length > 0) { + const fs = await import("fs"); + for (const assetPath of oldAssetPaths) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + console.warn(`Failed to delete old custom asset ${assetPath}:`, error); + } + } + } + return updatedGame; }; From 2bed7c0b37c83ff031ebe7b374e77e85458d9ed1 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 14:55:23 +0300 Subject: [PATCH 15/52] fix: cleaned comments and simplified function --- .../library/remove-game-from-library.ts | 22 +++++++------ src/main/events/library/update-custom-game.ts | 31 ++++++------------- .../library/update-game-custom-assets.ts | 18 ++++++----- 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index 4868d588..c4c8be9d 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -14,18 +14,22 @@ const removeGameFromLibrary = async ( if (game) { // Collect asset paths that need to be cleaned up before marking as deleted const assetPathsToDelete: string[] = []; - - const assetUrls = game.shop === "custom" - ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] - : [game.customIconUrl, game.customLogoImageUrl, game.customHeroImageUrl]; - - assetUrls.forEach(url => { + + const assetUrls = + game.shop === "custom" + ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] + : [ + game.customIconUrl, + game.customLogoImageUrl, + game.customHeroImageUrl, + ]; + + assetUrls.forEach((url) => { if (url?.startsWith("local:")) { assetPathsToDelete.push(url.replace("local:", "")); } }); - const updatedGame = { ...game, isDeleted: true, @@ -39,13 +43,12 @@ const removeGameFromLibrary = async ( await gamesSublevel.put(gameKey, updatedGame); - if (game.shop !== "custom") { const existingAssets = await gamesShopAssetsSublevel.get(gameKey); if (existingAssets) { const resetAssets = { ...existingAssets, - title: existingAssets.title, + title: existingAssets.title, }; await gamesShopAssetsSublevel.put(gameKey, resetAssets); } @@ -55,7 +58,6 @@ const removeGameFromLibrary = async ( HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {}); } - if (assetPathsToDelete.length > 0) { const fs = await import("fs"); for (const assetPath of assetPathsToDelete) { diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 168e0050..141e251e 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -18,29 +18,19 @@ const updateCustomGame = async ( throw new Error("Game not found"); } - // Collect old asset paths that will be replaced const oldAssetPaths: string[] = []; - if (existingGame.iconUrl && iconUrl && existingGame.iconUrl !== iconUrl && existingGame.iconUrl.startsWith("local:")) { - oldAssetPaths.push(existingGame.iconUrl.replace("local:", "")); - } - if (existingGame.iconUrl && !iconUrl && existingGame.iconUrl.startsWith("local:")) { - oldAssetPaths.push(existingGame.iconUrl.replace("local:", "")); - } + const assetPairs = [ + { existing: existingGame.iconUrl, new: iconUrl }, + { existing: existingGame.logoImageUrl, new: logoImageUrl }, + { existing: existingGame.libraryHeroImageUrl, new: libraryHeroImageUrl } + ]; - if (existingGame.logoImageUrl && logoImageUrl && existingGame.logoImageUrl !== logoImageUrl && existingGame.logoImageUrl.startsWith("local:")) { - oldAssetPaths.push(existingGame.logoImageUrl.replace("local:", "")); - } - if (existingGame.logoImageUrl && !logoImageUrl && existingGame.logoImageUrl.startsWith("local:")) { - oldAssetPaths.push(existingGame.logoImageUrl.replace("local:", "")); - } - - if (existingGame.libraryHeroImageUrl && libraryHeroImageUrl && existingGame.libraryHeroImageUrl !== libraryHeroImageUrl && existingGame.libraryHeroImageUrl.startsWith("local:")) { - oldAssetPaths.push(existingGame.libraryHeroImageUrl.replace("local:", "")); - } - if (existingGame.libraryHeroImageUrl && !libraryHeroImageUrl && existingGame.libraryHeroImageUrl.startsWith("local:")) { - oldAssetPaths.push(existingGame.libraryHeroImageUrl.replace("local:", "")); - } + assetPairs.forEach(({ existing, new: newUrl }) => { + if (existing?.startsWith("local:") && (!newUrl || existing !== newUrl)) { + oldAssetPaths.push(existing.replace("local:", "")); + } + }); const updatedGame = { ...existingGame, @@ -67,7 +57,6 @@ const updateCustomGame = async ( await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } - // Manually delete specific old asset files instead of running full cleanup if (oldAssetPaths.length > 0) { const fs = await import("fs"); for (const assetPath of oldAssetPaths) { diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index f8206904..392a0923 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -18,17 +18,21 @@ const updateGameCustomAssets = async ( throw new Error("Game not found"); } - // Collect old custom asset paths that will be replaced const oldAssetPaths: string[] = []; - + const assetPairs = [ { existing: existingGame.customIconUrl, new: customIconUrl }, { existing: existingGame.customLogoImageUrl, new: customLogoImageUrl }, - { existing: existingGame.customHeroImageUrl, new: customHeroImageUrl } + { existing: existingGame.customHeroImageUrl, new: customHeroImageUrl }, ]; - + assetPairs.forEach(({ existing, new: newUrl }) => { - if (existing && newUrl !== undefined && existing !== newUrl && existing.startsWith("local:")) { + if ( + existing && + newUrl !== undefined && + existing !== newUrl && + existing.startsWith("local:") + ) { oldAssetPaths.push(existing.replace("local:", "")); } }); @@ -43,18 +47,16 @@ const updateGameCustomAssets = async ( await gamesSublevel.put(gameKey, updatedGame); - // Also update the shop assets for non-custom games const existingAssets = await gamesShopAssetsSublevel.get(gameKey); if (existingAssets) { const updatedAssets = { ...existingAssets, - title, // Update the title in shop assets as well + title, }; await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } - // Manually delete specific old custom asset files instead of running full cleanup if (oldAssetPaths.length > 0) { const fs = await import("fs"); for (const assetPath of oldAssetPaths) { From 3e93a14deb066a3a674a3f2879fc39fbdd45acae Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 19:53:52 +0300 Subject: [PATCH 16/52] Fix: display actual image path in edit game modal --- .../library/update-game-custom-assets.ts | 2 +- .../game-details/modals/edit-game-modal.tsx | 60 ++++++++++++++++--- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 392a0923..1b75e0c4 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -51,7 +51,7 @@ const updateGameCustomAssets = async ( if (existingAssets) { const updatedAssets = { ...existingAssets, - title, + title, }; await gamesShopAssetsSublevel.put(gameKey, updatedAssets); diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 7bfc48fa..0948a749 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { ImageIcon, XIcon } from "@primer/octicons-react"; @@ -32,6 +32,9 @@ export function EditGameModal({ const [iconPath, setIconPath] = useState(""); const [logoPath, setLogoPath] = useState(""); const [heroPath, setHeroPath] = useState(""); + const [iconDisplayPath, setIconDisplayPath] = useState(""); + const [logoDisplayPath, setLogoDisplayPath] = useState(""); + const [heroDisplayPath, setHeroDisplayPath] = useState(""); const [isUpdating, setIsUpdating] = useState(false); const [selectedAssetType, setSelectedAssetType] = useState("icon"); @@ -51,6 +54,10 @@ export function EditGameModal({ setIconPath(extractLocalPath(game.iconUrl)); setLogoPath(extractLocalPath(game.logoImageUrl)); setHeroPath(extractLocalPath(game.libraryHeroImageUrl)); + // For existing assets, show the asset path as display path since we don't have the original + setIconDisplayPath(extractLocalPath(game.iconUrl)); + setLogoDisplayPath(extractLocalPath(game.logoImageUrl)); + setHeroDisplayPath(extractLocalPath(game.libraryHeroImageUrl)); }, []); const setNonCustomGameAssets = useCallback( @@ -58,6 +65,10 @@ export function EditGameModal({ setIconPath(extractLocalPath(game.customIconUrl)); setLogoPath(extractLocalPath(game.customLogoImageUrl)); setHeroPath(extractLocalPath(game.customHeroImageUrl)); + // For existing assets, show the asset path as display path since we don't have the original + setIconDisplayPath(extractLocalPath(game.customIconUrl)); + setLogoDisplayPath(extractLocalPath(game.customLogoImageUrl)); + setHeroDisplayPath(extractLocalPath(game.customHeroImageUrl)); setDefaultIconUrl(shopDetails?.assets?.iconUrl || game.iconUrl || null); setDefaultLogoUrl( @@ -103,6 +114,17 @@ export function EditGameModal({ } }; + const getAssetDisplayPath = (assetType: AssetType): string => { + switch (assetType) { + case "icon": + return iconDisplayPath; + case "logo": + return logoDisplayPath; + case "hero": + return heroDisplayPath; + } + }; + const setAssetPath = (assetType: AssetType, path: string): void => { switch (assetType) { case "icon": @@ -117,6 +139,20 @@ export function EditGameModal({ } }; + const setAssetDisplayPath = (assetType: AssetType, path: string): void => { + switch (assetType) { + case "icon": + setIconDisplayPath(path); + break; + case "logo": + setLogoDisplayPath(path); + break; + case "hero": + setHeroDisplayPath(path); + break; + } + }; + const getDefaultUrl = (assetType: AssetType): string | null => { switch (assetType) { case "icon": @@ -140,21 +176,25 @@ export function EditGameModal({ }); if (filePaths && filePaths.length > 0) { + const originalPath = filePaths[0]; try { const copiedAssetUrl = await window.electron.copyCustomGameAsset( - filePaths[0], + originalPath, assetType ); setAssetPath(assetType, copiedAssetUrl.replace("local:", "")); + setAssetDisplayPath(assetType, originalPath); } catch (error) { console.error(`Failed to copy ${assetType} asset:`, error); - setAssetPath(assetType, filePaths[0]); + setAssetPath(assetType, originalPath); + setAssetDisplayPath(assetType, originalPath); } } }; const handleRestoreDefault = (assetType: AssetType) => { setAssetPath(assetType, ""); + setAssetDisplayPath(assetType, ""); }; const getOriginalTitle = (): string => { @@ -169,11 +209,11 @@ export function EditGameModal({ setGameName(originalTitle); }; - const isTitleChanged = (): boolean => { + const isTitleChanged = useMemo((): boolean => { if (!game || isCustomGame(game)) return false; const originalTitle = getOriginalTitle(); return gameName.trim() !== originalTitle.trim(); - }; + }, [game, gameName, shopDetails]); const [dragOverTarget, setDragOverTarget] = useState(null); @@ -250,6 +290,7 @@ export function EditGameModal({ const assetPath = copiedAssetUrl.replace("local:", ""); setAssetPath(assetType, assetPath); + setAssetDisplayPath(assetType, filePath); showSuccessToast( `${assetType.charAt(0).toUpperCase() + assetType.slice(1)} updated successfully!` @@ -361,7 +402,7 @@ export function EditGameModal({ }; // Helper function to reset form to initial state - const resetFormToInitialState = (game: LibraryGame | Game) => { + const resetFormToInitialState = useCallback((game: LibraryGame | Game) => { setGameName(game.title || ""); if (isCustomGame(game)) { @@ -373,7 +414,7 @@ export function EditGameModal({ } else { setNonCustomGameAssets(game as LibraryGame); } - }; + }, [setCustomGameAssets, setNonCustomGameAssets]); const handleClose = () => { if (!isUpdating && game) { @@ -396,6 +437,7 @@ export function EditGameModal({ const renderImageSection = (assetType: AssetType) => { const assetPath = getAssetPath(assetType); + const assetDisplayPath = getAssetDisplayPath(assetType); const defaultUrl = getDefaultUrl(assetType); const hasImage = assetPath || (game && !isCustomGame(game) && defaultUrl); const isDragOver = dragOverTarget === assetType; @@ -408,7 +450,7 @@ export function EditGameModal({
Date: Mon, 29 Sep 2025 20:09:04 +0300 Subject: [PATCH 17/52] fix: using for...of instead of forEach --- src/main/events/library/remove-game-from-library.ts | 4 ++-- src/main/events/library/update-custom-game.ts | 10 +++++----- src/main/events/library/update-game-custom-assets.ts | 6 +++--- .../src/pages/game-details/modals/edit-game-modal.tsx | 2 -- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index c4c8be9d..92539650 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -24,11 +24,11 @@ const removeGameFromLibrary = async ( game.customHeroImageUrl, ]; - assetUrls.forEach((url) => { + for (const url of assetUrls) { if (url?.startsWith("local:")) { assetPathsToDelete.push(url.replace("local:", "")); } - }); + } const updatedGame = { ...game, diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 141e251e..39f3551b 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -19,18 +19,18 @@ const updateCustomGame = async ( } const oldAssetPaths: string[] = []; - + const assetPairs = [ { existing: existingGame.iconUrl, new: iconUrl }, { existing: existingGame.logoImageUrl, new: logoImageUrl }, - { existing: existingGame.libraryHeroImageUrl, new: libraryHeroImageUrl } + { existing: existingGame.libraryHeroImageUrl, new: libraryHeroImageUrl }, ]; - - assetPairs.forEach(({ existing, new: newUrl }) => { + + for (const { existing, new: newUrl } of assetPairs) { if (existing?.startsWith("local:") && (!newUrl || existing !== newUrl)) { oldAssetPaths.push(existing.replace("local:", "")); } - }); + } const updatedGame = { ...existingGame, diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 1b75e0c4..166b2641 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -26,7 +26,7 @@ const updateGameCustomAssets = async ( { existing: existingGame.customHeroImageUrl, new: customHeroImageUrl }, ]; - assetPairs.forEach(({ existing, new: newUrl }) => { + for (const { existing, new: newUrl } of assetPairs) { if ( existing && newUrl !== undefined && @@ -35,7 +35,7 @@ const updateGameCustomAssets = async ( ) { oldAssetPaths.push(existing.replace("local:", "")); } - }); + } const updatedGame = { ...existingGame, @@ -51,7 +51,7 @@ const updateGameCustomAssets = async ( if (existingAssets) { const updatedAssets = { ...existingAssets, - title, + title, }; await gamesShopAssetsSublevel.put(gameKey, updatedAssets); diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 0948a749..5e8e2311 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -54,7 +54,6 @@ export function EditGameModal({ setIconPath(extractLocalPath(game.iconUrl)); setLogoPath(extractLocalPath(game.logoImageUrl)); setHeroPath(extractLocalPath(game.libraryHeroImageUrl)); - // For existing assets, show the asset path as display path since we don't have the original setIconDisplayPath(extractLocalPath(game.iconUrl)); setLogoDisplayPath(extractLocalPath(game.logoImageUrl)); setHeroDisplayPath(extractLocalPath(game.libraryHeroImageUrl)); @@ -65,7 +64,6 @@ export function EditGameModal({ setIconPath(extractLocalPath(game.customIconUrl)); setLogoPath(extractLocalPath(game.customLogoImageUrl)); setHeroPath(extractLocalPath(game.customHeroImageUrl)); - // For existing assets, show the asset path as display path since we don't have the original setIconDisplayPath(extractLocalPath(game.customIconUrl)); setLogoDisplayPath(extractLocalPath(game.customLogoImageUrl)); setHeroDisplayPath(extractLocalPath(game.customHeroImageUrl)); From a87e04a366eb12985d1fa1282d634cd95cf74c05 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 20:14:36 +0300 Subject: [PATCH 18/52] Fix: using node:fs instead of fs --- .../library/remove-game-from-library.ts | 2 +- src/main/events/library/update-custom-game.ts | 2 +- .../library/update-game-custom-assets.ts | 4 +-- .../game-details/modals/edit-game-modal.tsx | 27 ++++++++++--------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index 92539650..a9c0d272 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -59,7 +59,7 @@ const removeGameFromLibrary = async ( } if (assetPathsToDelete.length > 0) { - const fs = await import("fs"); + const fs = await import("node:fs"); for (const assetPath of assetPathsToDelete) { try { if (fs.existsSync(assetPath)) { diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 39f3551b..47641a6e 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -58,7 +58,7 @@ const updateCustomGame = async ( } if (oldAssetPaths.length > 0) { - const fs = await import("fs"); + const fs = await import("node:fs"); for (const assetPath of oldAssetPaths) { try { if (fs.existsSync(assetPath)) { diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 166b2641..4e86bbc0 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -51,14 +51,14 @@ const updateGameCustomAssets = async ( if (existingAssets) { const updatedAssets = { ...existingAssets, - title, + title, }; await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } if (oldAssetPaths.length > 0) { - const fs = await import("fs"); + const fs = await import("node:fs"); for (const assetPath of oldAssetPaths) { try { if (fs.existsSync(assetPath)) { diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 5e8e2311..3979d051 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -400,19 +400,22 @@ export function EditGameModal({ }; // Helper function to reset form to initial state - const resetFormToInitialState = useCallback((game: LibraryGame | Game) => { - setGameName(game.title || ""); + const resetFormToInitialState = useCallback( + (game: LibraryGame | Game) => { + setGameName(game.title || ""); - if (isCustomGame(game)) { - setCustomGameAssets(game); - // Clear default URLs for custom games - setDefaultIconUrl(null); - setDefaultLogoUrl(null); - setDefaultHeroUrl(null); - } else { - setNonCustomGameAssets(game as LibraryGame); - } - }, [setCustomGameAssets, setNonCustomGameAssets]); + if (isCustomGame(game)) { + setCustomGameAssets(game); + // Clear default URLs for custom games + setDefaultIconUrl(null); + setDefaultLogoUrl(null); + setDefaultHeroUrl(null); + } else { + setNonCustomGameAssets(game as LibraryGame); + } + }, + [setCustomGameAssets, setNonCustomGameAssets] + ); const handleClose = () => { if (!isUpdating && game) { From 96d6b90356c7612210c5358937d872f6d320bdca Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 20:20:58 +0300 Subject: [PATCH 19/52] Fix: Refactoring functions to reduce complexity --- .../library/remove-game-from-library.ts | 133 ++++++++++-------- .../library/update-game-custom-assets.ts | 90 ++++++++---- 2 files changed, 141 insertions(+), 82 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index a9c0d272..438aa39a 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -1,7 +1,69 @@ import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; -import type { GameShop } from "@types"; +import type { GameShop, Game } from "@types"; + +const collectAssetPathsToDelete = (game: Game): string[] => { + const assetPathsToDelete: string[] = []; + + const assetUrls = + game.shop === "custom" + ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] + : [ + game.customIconUrl, + game.customLogoImageUrl, + game.customHeroImageUrl, + ]; + + for (const url of assetUrls) { + if (url?.startsWith("local:")) { + assetPathsToDelete.push(url.replace("local:", "")); + } + } + + return assetPathsToDelete; +}; + +const updateGameAsDeleted = async (game: Game, gameKey: string): Promise => { + const updatedGame = { + ...game, + isDeleted: true, + executablePath: null, + ...(game.shop !== "custom" && { + customIconUrl: null, + customLogoImageUrl: null, + customHeroImageUrl: null, + }), + }; + + await gamesSublevel.put(gameKey, updatedGame); +}; + +const resetShopAssets = async (gameKey: string): Promise => { + const existingAssets = await gamesShopAssetsSublevel.get(gameKey); + if (existingAssets) { + const resetAssets = { + ...existingAssets, + title: existingAssets.title, + }; + await gamesShopAssetsSublevel.put(gameKey, resetAssets); + } +}; + +const deleteAssetFiles = async (assetPathsToDelete: string[]): Promise => { + if (assetPathsToDelete.length === 0) return; + + const fs = await import("node:fs"); + for (const assetPath of assetPathsToDelete) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + console.warn(`Failed to delete asset ${assetPath}:`, error); + } + } +}; const removeGameFromLibrary = async ( _event: Electron.IpcMainInvokeEvent, @@ -11,66 +73,21 @@ const removeGameFromLibrary = async ( const gameKey = levelKeys.game(shop, objectId); const game = await gamesSublevel.get(gameKey); - if (game) { - // Collect asset paths that need to be cleaned up before marking as deleted - const assetPathsToDelete: string[] = []; + if (!game) return; - const assetUrls = - game.shop === "custom" - ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] - : [ - game.customIconUrl, - game.customLogoImageUrl, - game.customHeroImageUrl, - ]; + const assetPathsToDelete = collectAssetPathsToDelete(game); + + await updateGameAsDeleted(game, gameKey); - for (const url of assetUrls) { - if (url?.startsWith("local:")) { - assetPathsToDelete.push(url.replace("local:", "")); - } - } - - const updatedGame = { - ...game, - isDeleted: true, - executablePath: null, - ...(game.shop !== "custom" && { - customIconUrl: null, - customLogoImageUrl: null, - customHeroImageUrl: null, - }), - }; - - await gamesSublevel.put(gameKey, updatedGame); - - if (game.shop !== "custom") { - const existingAssets = await gamesShopAssetsSublevel.get(gameKey); - if (existingAssets) { - const resetAssets = { - ...existingAssets, - title: existingAssets.title, - }; - await gamesShopAssetsSublevel.put(gameKey, resetAssets); - } - } - - if (game?.remoteId) { - HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {}); - } - - if (assetPathsToDelete.length > 0) { - const fs = await import("node:fs"); - for (const assetPath of assetPathsToDelete) { - try { - if (fs.existsSync(assetPath)) { - await fs.promises.unlink(assetPath); - } - } catch (error) { - console.warn(`Failed to delete asset ${assetPath}:`, error); - } - } - } + if (game.shop !== "custom") { + await resetShopAssets(gameKey); } + + if (game?.remoteId) { + HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {}); + } + + await deleteAssetFiles(assetPathsToDelete); }; registerEvent("removeGameFromLibrary", removeGameFromLibrary); diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 4e86bbc0..2138af3a 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -1,23 +1,13 @@ import { registerEvent } from "../register-event"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; -import type { GameShop } from "@types"; +import type { GameShop, Game } from "@types"; -const updateGameCustomAssets = async ( - _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string, - title: string, +const collectOldAssetPaths = ( + existingGame: Game, customIconUrl?: string | null, customLogoImageUrl?: string | null, customHeroImageUrl?: string | null -) => { - const gameKey = levelKeys.game(shop, objectId); - - const existingGame = await gamesSublevel.get(gameKey); - if (!existingGame) { - throw new Error("Game not found"); - } - +): string[] => { const oldAssetPaths: string[] = []; const assetPairs = [ @@ -37,6 +27,17 @@ const updateGameCustomAssets = async ( } } + return oldAssetPaths; +}; + +const updateGameData = async ( + gameKey: string, + existingGame: Game, + title: string, + customIconUrl?: string | null, + customLogoImageUrl?: string | null, + customHeroImageUrl?: string | null +): Promise => { const updatedGame = { ...existingGame, title, @@ -46,29 +47,70 @@ const updateGameCustomAssets = async ( }; await gamesSublevel.put(gameKey, updatedGame); + return updatedGame; +}; +const updateShopAssets = async (gameKey: string, title: string): Promise => { const existingAssets = await gamesShopAssetsSublevel.get(gameKey); if (existingAssets) { const updatedAssets = { ...existingAssets, title, }; - await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } +}; - if (oldAssetPaths.length > 0) { - const fs = await import("node:fs"); - for (const assetPath of oldAssetPaths) { - try { - if (fs.existsSync(assetPath)) { - await fs.promises.unlink(assetPath); - } - } catch (error) { - console.warn(`Failed to delete old custom asset ${assetPath}:`, error); +const deleteOldAssetFiles = async (oldAssetPaths: string[]): Promise => { + if (oldAssetPaths.length === 0) return; + + const fs = await import("node:fs"); + for (const assetPath of oldAssetPaths) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); } + } catch (error) { + console.warn(`Failed to delete old custom asset ${assetPath}:`, error); } } +}; + +const updateGameCustomAssets = async ( + _event: Electron.IpcMainInvokeEvent, + shop: GameShop, + objectId: string, + title: string, + customIconUrl?: string | null, + customLogoImageUrl?: string | null, + customHeroImageUrl?: string | null +) => { + const gameKey = levelKeys.game(shop, objectId); + + const existingGame = await gamesSublevel.get(gameKey); + if (!existingGame) { + throw new Error("Game not found"); + } + + const oldAssetPaths = collectOldAssetPaths( + existingGame, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl + ); + + const updatedGame = await updateGameData( + gameKey, + existingGame, + title, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl + ); + + await updateShopAssets(gameKey, title); + + await deleteOldAssetFiles(oldAssetPaths); return updatedGame; }; From 7e22344f77a0cab9bb597d89b50c17ff5b50c4f0 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 20:36:05 +0300 Subject: [PATCH 20/52] Fix: fixed fs import and started using logger.warn --- .../library/remove-game-from-library.ts | 28 ++++++++++--------- src/main/events/library/update-custom-game.ts | 5 ++-- .../library/update-game-custom-assets.ts | 5 ++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index 438aa39a..b7033147 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -2,29 +2,30 @@ import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop, Game } from "@types"; +import fs from "node:fs"; +import { logger } from "@main/services"; const collectAssetPathsToDelete = (game: Game): string[] => { const assetPathsToDelete: string[] = []; - + const assetUrls = game.shop === "custom" ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] - : [ - game.customIconUrl, - game.customLogoImageUrl, - game.customHeroImageUrl, - ]; + : [game.customIconUrl, game.customLogoImageUrl, game.customHeroImageUrl]; for (const url of assetUrls) { if (url?.startsWith("local:")) { assetPathsToDelete.push(url.replace("local:", "")); } } - + return assetPathsToDelete; }; -const updateGameAsDeleted = async (game: Game, gameKey: string): Promise => { +const updateGameAsDeleted = async ( + game: Game, + gameKey: string +): Promise => { const updatedGame = { ...game, isDeleted: true, @@ -50,17 +51,18 @@ const resetShopAssets = async (gameKey: string): Promise => { } }; -const deleteAssetFiles = async (assetPathsToDelete: string[]): Promise => { +const deleteAssetFiles = async ( + assetPathsToDelete: string[] +): Promise => { if (assetPathsToDelete.length === 0) return; - - const fs = await import("node:fs"); + for (const assetPath of assetPathsToDelete) { try { if (fs.existsSync(assetPath)) { await fs.promises.unlink(assetPath); } } catch (error) { - console.warn(`Failed to delete asset ${assetPath}:`, error); + logger.warn(`Failed to delete asset ${assetPath}:`, error); } } }; @@ -76,7 +78,7 @@ const removeGameFromLibrary = async ( if (!game) return; const assetPathsToDelete = collectAssetPathsToDelete(game); - + await updateGameAsDeleted(game, gameKey); if (game.shop !== "custom") { diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 47641a6e..82d7f45f 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -1,6 +1,8 @@ import { registerEvent } from "../register-event"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop } from "@types"; +import fs from "node:fs"; +import { logger } from "@main/services"; const updateCustomGame = async ( _event: Electron.IpcMainInvokeEvent, @@ -58,14 +60,13 @@ const updateCustomGame = async ( } if (oldAssetPaths.length > 0) { - const fs = await import("node:fs"); for (const assetPath of oldAssetPaths) { try { if (fs.existsSync(assetPath)) { await fs.promises.unlink(assetPath); } } catch (error) { - console.warn(`Failed to delete old asset ${assetPath}:`, error); + logger.warn(`Failed to delete old asset ${assetPath}:`, error); } } } diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 2138af3a..1813e8a8 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -1,6 +1,8 @@ import { registerEvent } from "../register-event"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop, Game } from "@types"; +import fs from "node:fs"; +import { logger } from "@main/services"; const collectOldAssetPaths = ( existingGame: Game, @@ -64,14 +66,13 @@ const updateShopAssets = async (gameKey: string, title: string): Promise = const deleteOldAssetFiles = async (oldAssetPaths: string[]): Promise => { if (oldAssetPaths.length === 0) return; - const fs = await import("node:fs"); for (const assetPath of oldAssetPaths) { try { if (fs.existsSync(assetPath)) { await fs.promises.unlink(assetPath); } } catch (error) { - console.warn(`Failed to delete old custom asset ${assetPath}:`, error); + logger.warn(`Failed to delete old custom asset ${assetPath}:`, error); } } }; From a39f9ebb70abab4e598d820b9f793c64ef73d2e5 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 20:39:58 +0300 Subject: [PATCH 21/52] fix: multiple imports --- src/main/events/library/remove-game-from-library.ts | 3 +-- src/main/events/library/update-game-custom-assets.ts | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index b7033147..fbb60ab2 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -1,9 +1,8 @@ import { registerEvent } from "../register-event"; -import { HydraApi } from "@main/services"; +import { HydraApi, logger } from "@main/services"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop, Game } from "@types"; import fs from "node:fs"; -import { logger } from "@main/services"; const collectAssetPathsToDelete = (game: Game): string[] => { const assetPathsToDelete: string[] = []; diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 1813e8a8..4bd4e517 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -52,7 +52,10 @@ const updateGameData = async ( return updatedGame; }; -const updateShopAssets = async (gameKey: string, title: string): Promise => { +const updateShopAssets = async ( + gameKey: string, + title: string +): Promise => { const existingAssets = await gamesShopAssetsSublevel.get(gameKey); if (existingAssets) { const updatedAssets = { From bd053a1635fae69049688b80d6d04c702cfe6e57 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Mon, 29 Sep 2025 22:04:01 +0300 Subject: [PATCH 22/52] fix: favorite and pin button overlapping playtime --- src/locales/en/translation.json | 2 + src/locales/ru/translation.json | 2 + .../user-library-game-card.scss | 2 +- .../user-library-game-card.tsx | 56 ++++++++++++++++++- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index ce8b4de1..93fd5b0a 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -506,6 +506,8 @@ "user_profile": { "amount_hours": "{{amount}} hours", "amount_minutes": "{{amount}} minutes", + "amount_hours_short": "{{amount}}h", + "amount_minutes_short": "{{amount}}m", "last_time_played": "Last played {{period}}", "activity": "Recent Activity", "library": "Library", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 58235989..c92d7902 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -486,6 +486,8 @@ "user_profile": { "amount_hours": "{{amount}} часов", "amount_minutes": "{{amount}} минут", + "amount_hours_short": "{{amount}}ч", + "amount_minutes_short": "{{amount}}м", "last_time_played": "Последняя игра {{period}}", "activity": "Недавняя активность", "library": "Библиотека", diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index f072fdd5..e40061de 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -86,7 +86,7 @@ top: 8px; right: 8px; display: flex; - gap: 6px; + gap: 4px; z-index: 2; } diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index 860c6758..d30dbf8a 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -2,7 +2,7 @@ import { UserGame } from "@types"; import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; import { useFormat, useToast } from "@renderer/hooks"; import { useNavigate } from "react-router-dom"; -import { useCallback, useContext, useState } from "react"; +import { useCallback, useContext, useState, useEffect, useRef } from "react"; import { buildGameAchievementPath, buildGameDetailsPath, @@ -43,6 +43,9 @@ export function UserLibraryGameCard({ const navigate = useNavigate(); const [isTooltipHovered, setIsTooltipHovered] = useState(false); const [isPinning, setIsPinning] = useState(false); + const [useShortFormat, setUseShortFormat] = useState(false); + const cardRef = useRef(null); + const playtimeRef = useRef(null); const getStatsItemCount = useCallback(() => { let statsCount = 1; @@ -94,6 +97,50 @@ export function UserLibraryGameCard({ [numberFormatter, t] ); + const formatPlayTimeShort = useCallback( + (playTimeInSeconds = 0) => { + const minutes = playTimeInSeconds / 60; + + if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { + return t("amount_minutes_short", { + amount: minutes.toFixed(0), + }); + } + + const hours = minutes / 60; + return t("amount_hours_short", { amount: Math.floor(hours) }); + }, + [t] + ); + + const checkForOverlap = useCallback(() => { + if (!cardRef.current || !playtimeRef.current) return; + + const cardWidth = cardRef.current.offsetWidth; + const hasButtons = game.isFavorite || isMe; + + if (hasButtons && cardWidth < 180) { + setUseShortFormat(true); + } else { + setUseShortFormat(false); + } + }, [game.isFavorite, isMe]); + + useEffect(() => { + checkForOverlap(); + + const handleResize = () => { + checkForOverlap(); + }; + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, [checkForOverlap]); + + useEffect(() => { + checkForOverlap(); + }, [game.isFavorite, isMe, checkForOverlap]); + const toggleGamePinned = async () => { setIsPinning(true); @@ -119,6 +166,7 @@ export function UserLibraryGameCard({ return ( <>
  • )} )} - {formatPlayTime(game.playTimeInSeconds)} + {useShortFormat + ? formatPlayTimeShort(game.playTimeInSeconds) + : formatPlayTime(game.playTimeInSeconds) + } {userProfile?.hasActiveSubscription && From 9689c19863c1ddfc1fce0f05767c98c68d3e5d61 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 00:52:46 +0300 Subject: [PATCH 23/52] fix: state fix and remake checking for overlap --- .../game-details/modals/edit-game-modal.tsx | 144 +++++++----------- .../user-library-game-card.scss | 20 +++ .../user-library-game-card.tsx | 73 ++------- 3 files changed, 93 insertions(+), 144 deletions(-) diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 3979d051..2413cb9e 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -29,19 +29,24 @@ export function EditGameModal({ const { showSuccessToast, showErrorToast } = useToast(); const [gameName, setGameName] = useState(""); - const [iconPath, setIconPath] = useState(""); - const [logoPath, setLogoPath] = useState(""); - const [heroPath, setHeroPath] = useState(""); - const [iconDisplayPath, setIconDisplayPath] = useState(""); - const [logoDisplayPath, setLogoDisplayPath] = useState(""); - const [heroDisplayPath, setHeroDisplayPath] = useState(""); + const [assetPaths, setAssetPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [assetDisplayPaths, setAssetDisplayPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [defaultUrls, setDefaultUrls] = useState({ + icon: null as string | null, + logo: null as string | null, + hero: null as string | null, + }); const [isUpdating, setIsUpdating] = useState(false); const [selectedAssetType, setSelectedAssetType] = useState("icon"); - const [defaultIconUrl, setDefaultIconUrl] = useState(null); - const [defaultLogoUrl, setDefaultLogoUrl] = useState(null); - const [defaultHeroUrl, setDefaultHeroUrl] = useState(null); - const isCustomGame = (game: LibraryGame | Game): boolean => { return game.shop === "custom"; }; @@ -51,32 +56,36 @@ export function EditGameModal({ }; const setCustomGameAssets = useCallback((game: LibraryGame | Game) => { - setIconPath(extractLocalPath(game.iconUrl)); - setLogoPath(extractLocalPath(game.logoImageUrl)); - setHeroPath(extractLocalPath(game.libraryHeroImageUrl)); - setIconDisplayPath(extractLocalPath(game.iconUrl)); - setLogoDisplayPath(extractLocalPath(game.logoImageUrl)); - setHeroDisplayPath(extractLocalPath(game.libraryHeroImageUrl)); + setAssetPaths({ + icon: extractLocalPath(game.iconUrl), + logo: extractLocalPath(game.logoImageUrl), + hero: extractLocalPath(game.libraryHeroImageUrl), + }); + setAssetDisplayPaths({ + icon: extractLocalPath(game.iconUrl), + logo: extractLocalPath(game.logoImageUrl), + hero: extractLocalPath(game.libraryHeroImageUrl), + }); }, []); const setNonCustomGameAssets = useCallback( (game: LibraryGame) => { - setIconPath(extractLocalPath(game.customIconUrl)); - setLogoPath(extractLocalPath(game.customLogoImageUrl)); - setHeroPath(extractLocalPath(game.customHeroImageUrl)); - setIconDisplayPath(extractLocalPath(game.customIconUrl)); - setLogoDisplayPath(extractLocalPath(game.customLogoImageUrl)); - setHeroDisplayPath(extractLocalPath(game.customHeroImageUrl)); + setAssetPaths({ + icon: extractLocalPath(game.customIconUrl), + logo: extractLocalPath(game.customLogoImageUrl), + hero: extractLocalPath(game.customHeroImageUrl), + }); + setAssetDisplayPaths({ + icon: extractLocalPath(game.customIconUrl), + logo: extractLocalPath(game.customLogoImageUrl), + hero: extractLocalPath(game.customHeroImageUrl), + }); - setDefaultIconUrl(shopDetails?.assets?.iconUrl || game.iconUrl || null); - setDefaultLogoUrl( - shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null - ); - setDefaultHeroUrl( - shopDetails?.assets?.libraryHeroImageUrl || - game.libraryHeroImageUrl || - null - ); + setDefaultUrls({ + icon: shopDetails?.assets?.iconUrl || game.iconUrl || null, + logo: shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null, + hero: shopDetails?.assets?.libraryHeroImageUrl || game.libraryHeroImageUrl || null, + }); }, [shopDetails] ); @@ -102,64 +111,23 @@ export function EditGameModal({ }; const getAssetPath = (assetType: AssetType): string => { - switch (assetType) { - case "icon": - return iconPath; - case "logo": - return logoPath; - case "hero": - return heroPath; - } + return assetPaths[assetType]; }; const getAssetDisplayPath = (assetType: AssetType): string => { - switch (assetType) { - case "icon": - return iconDisplayPath; - case "logo": - return logoDisplayPath; - case "hero": - return heroDisplayPath; - } + return assetDisplayPaths[assetType]; }; const setAssetPath = (assetType: AssetType, path: string): void => { - switch (assetType) { - case "icon": - setIconPath(path); - break; - case "logo": - setLogoPath(path); - break; - case "hero": - setHeroPath(path); - break; - } + setAssetPaths(prev => ({ ...prev, [assetType]: path })); }; const setAssetDisplayPath = (assetType: AssetType, path: string): void => { - switch (assetType) { - case "icon": - setIconDisplayPath(path); - break; - case "logo": - setLogoDisplayPath(path); - break; - case "hero": - setHeroDisplayPath(path); - break; - } + setAssetDisplayPaths(prev => ({ ...prev, [assetType]: path })); }; const getDefaultUrl = (assetType: AssetType): string | null => { - switch (assetType) { - case "icon": - return defaultIconUrl; - case "logo": - return defaultLogoUrl; - case "hero": - return defaultHeroUrl; - } + return defaultUrls[assetType]; }; const handleSelectAsset = async (assetType: AssetType) => { @@ -324,10 +292,10 @@ export function EditGameModal({ // Helper function to prepare custom game assets const prepareCustomGameAssets = (game: LibraryGame | Game) => { - const iconUrl = iconPath ? `local:${iconPath}` : game.iconUrl; - const logoImageUrl = logoPath ? `local:${logoPath}` : game.logoImageUrl; - const libraryHeroImageUrl = heroPath - ? `local:${heroPath}` + const iconUrl = assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl; + const logoImageUrl = assetPaths.logo ? `local:${assetPaths.logo}` : game.logoImageUrl; + const libraryHeroImageUrl = assetPaths.hero + ? `local:${assetPaths.hero}` : game.libraryHeroImageUrl; return { iconUrl, logoImageUrl, libraryHeroImageUrl }; @@ -336,9 +304,9 @@ export function EditGameModal({ // Helper function to prepare non-custom game assets const prepareNonCustomGameAssets = () => { return { - customIconUrl: iconPath ? `local:${iconPath}` : null, - customLogoImageUrl: logoPath ? `local:${logoPath}` : null, - customHeroImageUrl: heroPath ? `local:${heroPath}` : null, + customIconUrl: assetPaths.icon ? `local:${assetPaths.icon}` : null, + customLogoImageUrl: assetPaths.logo ? `local:${assetPaths.logo}` : null, + customHeroImageUrl: assetPaths.hero ? `local:${assetPaths.hero}` : null, }; }; @@ -407,9 +375,11 @@ export function EditGameModal({ if (isCustomGame(game)) { setCustomGameAssets(game); // Clear default URLs for custom games - setDefaultIconUrl(null); - setDefaultLogoUrl(null); - setDefaultHeroUrl(null); + setDefaultUrls({ + icon: null, + logo: null, + hero: null, + }); } else { setNonCustomGameAssets(game as LibraryGame); } diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index e40061de..dccd9dd1 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -8,6 +8,7 @@ display: flex; transition: all ease 0.2s; cursor: grab; + container-type: inline-size; &:hover { transform: scale(1.05); @@ -160,6 +161,25 @@ transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } + + &-long { + display: inline; + } + + &-short { + display: none; + } + + // When the card is narrow (less than 180px), show short format + @container (max-width: 180px) { + &-long { + display: none; + } + + &-short { + display: inline; + } + } } &__manual-playtime { color: globals.$warning-color; diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index d30dbf8a..ec7736e0 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -2,7 +2,7 @@ import { UserGame } from "@types"; import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; import { useFormat, useToast } from "@renderer/hooks"; import { useNavigate } from "react-router-dom"; -import { useCallback, useContext, useState, useEffect, useRef } from "react"; +import { useCallback, useContext, useState } from "react"; import { buildGameAchievementPath, buildGameDetailsPath, @@ -43,9 +43,7 @@ export function UserLibraryGameCard({ const navigate = useNavigate(); const [isTooltipHovered, setIsTooltipHovered] = useState(false); const [isPinning, setIsPinning] = useState(false); - const [useShortFormat, setUseShortFormat] = useState(false); - const cardRef = useRef(null); - const playtimeRef = useRef(null); + const getStatsItemCount = useCallback(() => { let statsCount = 1; @@ -82,64 +80,25 @@ export function UserLibraryGameCard({ }; const formatPlayTime = useCallback( - (playTimeInSeconds = 0) => { + (playTimeInSeconds = 0, isShort = false) => { const minutes = playTimeInSeconds / 60; if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t("amount_minutes", { + return t(isShort ? "amount_minutes_short" : "amount_minutes", { amount: minutes.toFixed(0), }); } const hours = minutes / 60; - return t("amount_hours", { amount: numberFormatter.format(hours) }); + const hoursKey = isShort ? "amount_hours_short" : "amount_hours"; + const hoursAmount = isShort ? Math.floor(hours) : numberFormatter.format(hours); + + return t(hoursKey, { amount: hoursAmount }); }, [numberFormatter, t] ); - const formatPlayTimeShort = useCallback( - (playTimeInSeconds = 0) => { - const minutes = playTimeInSeconds / 60; - if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t("amount_minutes_short", { - amount: minutes.toFixed(0), - }); - } - - const hours = minutes / 60; - return t("amount_hours_short", { amount: Math.floor(hours) }); - }, - [t] - ); - - const checkForOverlap = useCallback(() => { - if (!cardRef.current || !playtimeRef.current) return; - - const cardWidth = cardRef.current.offsetWidth; - const hasButtons = game.isFavorite || isMe; - - if (hasButtons && cardWidth < 180) { - setUseShortFormat(true); - } else { - setUseShortFormat(false); - } - }, [game.isFavorite, isMe]); - - useEffect(() => { - checkForOverlap(); - - const handleResize = () => { - checkForOverlap(); - }; - - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, [checkForOverlap]); - - useEffect(() => { - checkForOverlap(); - }, [game.isFavorite, isMe, checkForOverlap]); const toggleGamePinned = async () => { setIsPinning(true); @@ -166,7 +125,6 @@ export function UserLibraryGameCard({ return ( <>
  • )} - )} - {useShortFormat - ? formatPlayTimeShort(game.playTimeInSeconds) - : formatPlayTime(game.playTimeInSeconds) - } - + + {formatPlayTime(game.playTimeInSeconds)} + + + {formatPlayTime(game.playTimeInSeconds, true)} + +
  • {userProfile?.hasActiveSubscription && game.achievementCount > 0 && ( From 0f3d6ef76f36e4b10f8d9dd4060d4f3bcf6d8320 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 01:18:44 +0300 Subject: [PATCH 24/52] fix: state fix and remake checking for overlap --- .../pages/game-details/modals/edit-game-modal.tsx | 13 +++++++++---- .../profile-content/user-library-game-card.tsx | 11 +++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 2413cb9e..e55772c6 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -84,7 +84,10 @@ export function EditGameModal({ setDefaultUrls({ icon: shopDetails?.assets?.iconUrl || game.iconUrl || null, logo: shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null, - hero: shopDetails?.assets?.libraryHeroImageUrl || game.libraryHeroImageUrl || null, + hero: + shopDetails?.assets?.libraryHeroImageUrl || + game.libraryHeroImageUrl || + null, }); }, [shopDetails] @@ -119,11 +122,11 @@ export function EditGameModal({ }; const setAssetPath = (assetType: AssetType, path: string): void => { - setAssetPaths(prev => ({ ...prev, [assetType]: path })); + setAssetPaths((prev) => ({ ...prev, [assetType]: path })); }; const setAssetDisplayPath = (assetType: AssetType, path: string): void => { - setAssetDisplayPaths(prev => ({ ...prev, [assetType]: path })); + setAssetDisplayPaths((prev) => ({ ...prev, [assetType]: path })); }; const getDefaultUrl = (assetType: AssetType): string | null => { @@ -293,7 +296,9 @@ export function EditGameModal({ // Helper function to prepare custom game assets const prepareCustomGameAssets = (game: LibraryGame | Game) => { const iconUrl = assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl; - const logoImageUrl = assetPaths.logo ? `local:${assetPaths.logo}` : game.logoImageUrl; + const logoImageUrl = assetPaths.logo + ? `local:${assetPaths.logo}` + : game.logoImageUrl; const libraryHeroImageUrl = assetPaths.hero ? `local:${assetPaths.hero}` : game.libraryHeroImageUrl; diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index ec7736e0..251a3bc7 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -44,7 +44,6 @@ export function UserLibraryGameCard({ const [isTooltipHovered, setIsTooltipHovered] = useState(false); const [isPinning, setIsPinning] = useState(false); - const getStatsItemCount = useCallback(() => { let statsCount = 1; if (game.achievementsPointsEarnedSum > 0) statsCount++; @@ -91,15 +90,15 @@ export function UserLibraryGameCard({ const hours = minutes / 60; const hoursKey = isShort ? "amount_hours_short" : "amount_hours"; - const hoursAmount = isShort ? Math.floor(hours) : numberFormatter.format(hours); - + const hoursAmount = isShort + ? Math.floor(hours) + : numberFormatter.format(hours); + return t(hoursKey, { amount: hoursAmount }); }, [numberFormatter, t] ); - - const toggleGamePinned = async () => { setIsPinning(true); @@ -162,7 +161,7 @@ export function UserLibraryGameCard({ )}
    )} -
    Date: Tue, 30 Sep 2025 01:43:13 +0300 Subject: [PATCH 25/52] fix: playtime font-size increased --- .../pages/profile/profile-content/user-library-game-card.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index dccd9dd1..a19961fd 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -164,10 +164,12 @@ &-long { display: inline; + font-size: 12px; } &-short { display: none; + font-size: 12px; } // When the card is narrow (less than 180px), show short format From 959bed746b75f74217ee9fc052427f742a4d0f40 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 02:09:19 +0300 Subject: [PATCH 26/52] fix: original path to image not showing in modal after updating game asset --- src/main/events/library/update-custom-game.ts | 8 ++++- .../library/update-game-custom-assets.ts | 18 +++++++++-- src/preload/index.ts | 20 +++++++++--- src/renderer/src/declaration.d.ts | 10 ++++-- .../game-details/modals/edit-game-modal.tsx | 32 +++++++++++++++++-- src/types/level.types.ts | 6 ++++ 6 files changed, 81 insertions(+), 13 deletions(-) diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 82d7f45f..62473e54 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -11,7 +11,10 @@ const updateCustomGame = async ( title: string, iconUrl?: string, logoImageUrl?: string, - libraryHeroImageUrl?: string + libraryHeroImageUrl?: string, + originalIconPath?: string, + originalLogoPath?: string, + originalHeroPath?: string ) => { const gameKey = levelKeys.game(shop, objectId); @@ -40,6 +43,9 @@ const updateCustomGame = async ( iconUrl: iconUrl || null, logoImageUrl: logoImageUrl || null, libraryHeroImageUrl: libraryHeroImageUrl || null, + originalIconPath: originalIconPath || existingGame.originalIconPath || null, + originalLogoPath: originalLogoPath || existingGame.originalLogoPath || null, + originalHeroPath: originalHeroPath || existingGame.originalHeroPath || null, }; await gamesSublevel.put(gameKey, updatedGame); diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 4bd4e517..8cfc79f0 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -38,7 +38,10 @@ const updateGameData = async ( title: string, customIconUrl?: string | null, customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null + customHeroImageUrl?: string | null, + customOriginalIconPath?: string | null, + customOriginalLogoPath?: string | null, + customOriginalHeroPath?: string | null ): Promise => { const updatedGame = { ...existingGame, @@ -46,6 +49,9 @@ const updateGameData = async ( ...(customIconUrl !== undefined && { customIconUrl }), ...(customLogoImageUrl !== undefined && { customLogoImageUrl }), ...(customHeroImageUrl !== undefined && { customHeroImageUrl }), + ...(customOriginalIconPath !== undefined && { customOriginalIconPath }), + ...(customOriginalLogoPath !== undefined && { customOriginalLogoPath }), + ...(customOriginalHeroPath !== undefined && { customOriginalHeroPath }), }; await gamesSublevel.put(gameKey, updatedGame); @@ -87,7 +93,10 @@ const updateGameCustomAssets = async ( title: string, customIconUrl?: string | null, customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null + customHeroImageUrl?: string | null, + customOriginalIconPath?: string | null, + customOriginalLogoPath?: string | null, + customOriginalHeroPath?: string | null ) => { const gameKey = levelKeys.game(shop, objectId); @@ -109,7 +118,10 @@ const updateGameCustomAssets = async ( title, customIconUrl, customLogoImageUrl, - customHeroImageUrl + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath ); await updateShopAssets(gameKey, title); diff --git a/src/preload/index.ts b/src/preload/index.ts index e536f8c7..e4a29c90 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -158,7 +158,10 @@ contextBridge.exposeInMainWorld("electron", { title: string, iconUrl?: string, logoImageUrl?: string, - libraryHeroImageUrl?: string + libraryHeroImageUrl?: string, + originalIconPath?: string, + originalLogoPath?: string, + originalHeroPath?: string ) => ipcRenderer.invoke( "updateCustomGame", @@ -167,7 +170,10 @@ contextBridge.exposeInMainWorld("electron", { title, iconUrl, logoImageUrl, - libraryHeroImageUrl + libraryHeroImageUrl, + originalIconPath, + originalLogoPath, + originalHeroPath ), updateGameCustomAssets: ( shop: GameShop, @@ -175,7 +181,10 @@ contextBridge.exposeInMainWorld("electron", { title: string, customIconUrl?: string | null, customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null + customHeroImageUrl?: string | null, + customOriginalIconPath?: string | null, + customOriginalLogoPath?: string | null, + customOriginalHeroPath?: string | null ) => ipcRenderer.invoke( "updateGameCustomAssets", @@ -184,7 +193,10 @@ contextBridge.exposeInMainWorld("electron", { title, customIconUrl, customLogoImageUrl, - customHeroImageUrl + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath ), createGameShortcut: ( shop: GameShop, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 81d18940..9477edb5 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -125,7 +125,10 @@ declare global { title: string, iconUrl?: string, logoImageUrl?: string, - libraryHeroImageUrl?: string + libraryHeroImageUrl?: string, + originalIconPath?: string, + originalLogoPath?: string, + originalHeroPath?: string ) => Promise; copyCustomGameAsset: ( sourcePath: string, @@ -141,7 +144,10 @@ declare global { title: string, customIconUrl?: string | null, customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null + customHeroImageUrl?: string | null, + customOriginalIconPath?: string | null, + customOriginalLogoPath?: string | null, + customOriginalHeroPath?: string | null ) => Promise; createGameShortcut: ( shop: GameShop, diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index e55772c6..37801c6f 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -39,6 +39,11 @@ export function EditGameModal({ logo: "", hero: "", }); + const [originalAssetPaths, setOriginalAssetPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); const [defaultUrls, setDefaultUrls] = useState({ icon: null as string | null, logo: null as string | null, @@ -66,6 +71,11 @@ export function EditGameModal({ logo: extractLocalPath(game.logoImageUrl), hero: extractLocalPath(game.libraryHeroImageUrl), }); + setOriginalAssetPaths({ + icon: (game as any).originalIconPath || extractLocalPath(game.iconUrl), + logo: (game as any).originalLogoPath || extractLocalPath(game.logoImageUrl), + hero: (game as any).originalHeroPath || extractLocalPath(game.libraryHeroImageUrl), + }); }, []); const setNonCustomGameAssets = useCallback( @@ -80,6 +90,11 @@ export function EditGameModal({ logo: extractLocalPath(game.customLogoImageUrl), hero: extractLocalPath(game.customHeroImageUrl), }); + setOriginalAssetPaths({ + icon: (game as any).customOriginalIconPath || extractLocalPath(game.customIconUrl), + logo: (game as any).customOriginalLogoPath || extractLocalPath(game.customLogoImageUrl), + hero: (game as any).customOriginalHeroPath || extractLocalPath(game.customHeroImageUrl), + }); setDefaultUrls({ icon: shopDetails?.assets?.iconUrl || game.iconUrl || null, @@ -118,7 +133,8 @@ export function EditGameModal({ }; const getAssetDisplayPath = (assetType: AssetType): string => { - return assetDisplayPaths[assetType]; + // Use original path if available, otherwise fall back to display path + return originalAssetPaths[assetType] || assetDisplayPaths[assetType]; }; const setAssetPath = (assetType: AssetType, path: string): void => { @@ -153,10 +169,13 @@ export function EditGameModal({ ); setAssetPath(assetType, copiedAssetUrl.replace("local:", "")); setAssetDisplayPath(assetType, originalPath); + // Store the original path for display purposes + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: originalPath })); } catch (error) { console.error(`Failed to copy ${assetType} asset:`, error); setAssetPath(assetType, originalPath); setAssetDisplayPath(assetType, originalPath); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: originalPath })); } } }; @@ -164,6 +183,7 @@ export function EditGameModal({ const handleRestoreDefault = (assetType: AssetType) => { setAssetPath(assetType, ""); setAssetDisplayPath(assetType, ""); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); }; const getOriginalTitle = (): string => { @@ -326,7 +346,10 @@ export function EditGameModal({ gameName.trim(), iconUrl || undefined, logoImageUrl || undefined, - libraryHeroImageUrl || undefined + libraryHeroImageUrl || undefined, + originalAssetPaths.icon || undefined, + originalAssetPaths.logo || undefined, + originalAssetPaths.hero || undefined ); }; @@ -341,7 +364,10 @@ export function EditGameModal({ gameName.trim(), customIconUrl, customLogoImageUrl, - customHeroImageUrl + customHeroImageUrl, + originalAssetPaths.icon || undefined, + originalAssetPaths.logo || undefined, + originalAssetPaths.hero || undefined ); }; diff --git a/src/types/level.types.ts b/src/types/level.types.ts index 73fce370..8a6c56a0 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -38,6 +38,12 @@ export interface Game { customIconUrl?: string | null; customLogoImageUrl?: string | null; customHeroImageUrl?: string | null; + originalIconPath?: string | null; + originalLogoPath?: string | null; + originalHeroPath?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; playTimeInMilliseconds: number; unsyncedDeltaPlayTimeInMilliseconds?: number; lastTimePlayed: Date | null; From ceb236c40c0bae6e3d97ef0f844d54d9f40baa13 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 02:17:17 +0300 Subject: [PATCH 27/52] fix: async error function had too many arguments --- src/main/events/library/update-custom-game.ts | 33 ++++++--- .../library/update-game-custom-assets.ts | 72 +++++++++++++------ src/preload/index.ts | 68 ++++++------------ src/renderer/src/declaration.d.ts | 44 ++++++------ .../game-details/modals/edit-game-modal.tsx | 67 ++++++++++------- 5 files changed, 160 insertions(+), 124 deletions(-) diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 62473e54..8129fc57 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -4,18 +4,33 @@ import type { GameShop } from "@types"; import fs from "node:fs"; import { logger } from "@main/services"; +interface UpdateCustomGameParams { + shop: GameShop; + objectId: string; + title: string; + iconUrl?: string; + logoImageUrl?: string; + libraryHeroImageUrl?: string; + originalIconPath?: string; + originalLogoPath?: string; + originalHeroPath?: string; +} + const updateCustomGame = async ( _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string, - title: string, - iconUrl?: string, - logoImageUrl?: string, - libraryHeroImageUrl?: string, - originalIconPath?: string, - originalLogoPath?: string, - originalHeroPath?: string + params: UpdateCustomGameParams ) => { + const { + shop, + objectId, + title, + iconUrl, + logoImageUrl, + libraryHeroImageUrl, + originalIconPath, + originalLogoPath, + originalHeroPath, + } = params; const gameKey = levelKeys.game(shop, objectId); const existingGame = await gamesSublevel.get(gameKey); diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 8cfc79f0..57b14775 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -32,17 +32,32 @@ const collectOldAssetPaths = ( return oldAssetPaths; }; +interface UpdateGameDataParams { + gameKey: string; + existingGame: Game; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; +} + const updateGameData = async ( - gameKey: string, - existingGame: Game, - title: string, - customIconUrl?: string | null, - customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null, - customOriginalIconPath?: string | null, - customOriginalLogoPath?: string | null, - customOriginalHeroPath?: string | null + params: UpdateGameDataParams ): Promise => { + const { + gameKey, + existingGame, + title, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath, + } = params; const updatedGame = { ...existingGame, title, @@ -86,18 +101,33 @@ const deleteOldAssetFiles = async (oldAssetPaths: string[]): Promise => { } }; +interface UpdateGameCustomAssetsParams { + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; +} + const updateGameCustomAssets = async ( _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string, - title: string, - customIconUrl?: string | null, - customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null, - customOriginalIconPath?: string | null, - customOriginalLogoPath?: string | null, - customOriginalHeroPath?: string | null + params: UpdateGameCustomAssetsParams ) => { + const { + shop, + objectId, + title, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath, + } = params; const gameKey = levelKeys.game(shop, objectId); const existingGame = await gamesSublevel.get(gameKey); @@ -112,7 +142,7 @@ const updateGameCustomAssets = async ( customHeroImageUrl ); - const updatedGame = await updateGameData( + const updatedGame = await updateGameData({ gameKey, existingGame, title, @@ -121,8 +151,8 @@ const updateGameCustomAssets = async ( customHeroImageUrl, customOriginalIconPath, customOriginalLogoPath, - customOriginalHeroPath - ); + customOriginalHeroPath, + }); await updateShopAssets(gameKey, title); diff --git a/src/preload/index.ts b/src/preload/index.ts index e4a29c90..b92ba137 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -152,52 +152,28 @@ contextBridge.exposeInMainWorld("electron", { deleteTempFile: (filePath: string) => ipcRenderer.invoke("deleteTempFile", filePath), cleanupUnusedAssets: () => ipcRenderer.invoke("cleanupUnusedAssets"), - updateCustomGame: ( - shop: GameShop, - objectId: string, - title: string, - iconUrl?: string, - logoImageUrl?: string, - libraryHeroImageUrl?: string, - originalIconPath?: string, - originalLogoPath?: string, - originalHeroPath?: string - ) => - ipcRenderer.invoke( - "updateCustomGame", - shop, - objectId, - title, - iconUrl, - logoImageUrl, - libraryHeroImageUrl, - originalIconPath, - originalLogoPath, - originalHeroPath - ), - updateGameCustomAssets: ( - shop: GameShop, - objectId: string, - title: string, - customIconUrl?: string | null, - customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null, - customOriginalIconPath?: string | null, - customOriginalLogoPath?: string | null, - customOriginalHeroPath?: string | null - ) => - ipcRenderer.invoke( - "updateGameCustomAssets", - shop, - objectId, - title, - customIconUrl, - customLogoImageUrl, - customHeroImageUrl, - customOriginalIconPath, - customOriginalLogoPath, - customOriginalHeroPath - ), + updateCustomGame: (params: { + shop: GameShop; + objectId: string; + title: string; + iconUrl?: string; + logoImageUrl?: string; + libraryHeroImageUrl?: string; + originalIconPath?: string; + originalLogoPath?: string; + originalHeroPath?: string; + }) => ipcRenderer.invoke("updateCustomGame", params), + updateGameCustomAssets: (params: { + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; + }) => ipcRenderer.invoke("updateGameCustomAssets", params), createGameShortcut: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 9477edb5..e6277888 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -119,17 +119,17 @@ declare global { logoImageUrl?: string, libraryHeroImageUrl?: string ) => Promise; - updateCustomGame: ( - shop: GameShop, - objectId: string, - title: string, - iconUrl?: string, - logoImageUrl?: string, - libraryHeroImageUrl?: string, - originalIconPath?: string, - originalLogoPath?: string, - originalHeroPath?: string - ) => Promise; + updateCustomGame: (params: { + shop: GameShop; + objectId: string; + title: string; + iconUrl?: string; + logoImageUrl?: string; + libraryHeroImageUrl?: string; + originalIconPath?: string; + originalLogoPath?: string; + originalHeroPath?: string; + }) => Promise; copyCustomGameAsset: ( sourcePath: string, assetType: "icon" | "logo" | "hero" @@ -138,17 +138,17 @@ declare global { deletedCount: number; errors: string[]; }>; - updateGameCustomAssets: ( - shop: GameShop, - objectId: string, - title: string, - customIconUrl?: string | null, - customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null, - customOriginalIconPath?: string | null, - customOriginalLogoPath?: string | null, - customOriginalHeroPath?: string | null - ) => Promise; + updateGameCustomAssets: (params: { + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; + }) => Promise; createGameShortcut: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 37801c6f..0f6df95d 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -73,8 +73,11 @@ export function EditGameModal({ }); setOriginalAssetPaths({ icon: (game as any).originalIconPath || extractLocalPath(game.iconUrl), - logo: (game as any).originalLogoPath || extractLocalPath(game.logoImageUrl), - hero: (game as any).originalHeroPath || extractLocalPath(game.libraryHeroImageUrl), + logo: + (game as any).originalLogoPath || extractLocalPath(game.logoImageUrl), + hero: + (game as any).originalHeroPath || + extractLocalPath(game.libraryHeroImageUrl), }); }, []); @@ -91,9 +94,15 @@ export function EditGameModal({ hero: extractLocalPath(game.customHeroImageUrl), }); setOriginalAssetPaths({ - icon: (game as any).customOriginalIconPath || extractLocalPath(game.customIconUrl), - logo: (game as any).customOriginalLogoPath || extractLocalPath(game.customLogoImageUrl), - hero: (game as any).customOriginalHeroPath || extractLocalPath(game.customHeroImageUrl), + icon: + (game as any).customOriginalIconPath || + extractLocalPath(game.customIconUrl), + logo: + (game as any).customOriginalLogoPath || + extractLocalPath(game.customLogoImageUrl), + hero: + (game as any).customOriginalHeroPath || + extractLocalPath(game.customHeroImageUrl), }); setDefaultUrls({ @@ -170,12 +179,18 @@ export function EditGameModal({ setAssetPath(assetType, copiedAssetUrl.replace("local:", "")); setAssetDisplayPath(assetType, originalPath); // Store the original path for display purposes - setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: originalPath })); + setOriginalAssetPaths((prev) => ({ + ...prev, + [assetType]: originalPath, + })); } catch (error) { console.error(`Failed to copy ${assetType} asset:`, error); setAssetPath(assetType, originalPath); setAssetDisplayPath(assetType, originalPath); - setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: originalPath })); + setOriginalAssetPaths((prev) => ({ + ...prev, + [assetType]: originalPath, + })); } } }; @@ -340,17 +355,17 @@ export function EditGameModal({ const { iconUrl, logoImageUrl, libraryHeroImageUrl } = prepareCustomGameAssets(game); - return window.electron.updateCustomGame( - game.shop, - game.objectId, - gameName.trim(), - iconUrl || undefined, - logoImageUrl || undefined, - libraryHeroImageUrl || undefined, - originalAssetPaths.icon || undefined, - originalAssetPaths.logo || undefined, - originalAssetPaths.hero || undefined - ); + return window.electron.updateCustomGame({ + shop: game.shop, + objectId: game.objectId, + title: gameName.trim(), + iconUrl: iconUrl || undefined, + logoImageUrl: logoImageUrl || undefined, + libraryHeroImageUrl: libraryHeroImageUrl || undefined, + originalIconPath: originalAssetPaths.icon || undefined, + originalLogoPath: originalAssetPaths.logo || undefined, + originalHeroPath: originalAssetPaths.hero || undefined, + }); }; // Helper function to update non-custom game @@ -358,17 +373,17 @@ export function EditGameModal({ const { customIconUrl, customLogoImageUrl, customHeroImageUrl } = prepareNonCustomGameAssets(); - return window.electron.updateGameCustomAssets( - game.shop, - game.objectId, - gameName.trim(), + return window.electron.updateGameCustomAssets({ + shop: game.shop, + objectId: game.objectId, + title: gameName.trim(), customIconUrl, customLogoImageUrl, customHeroImageUrl, - originalAssetPaths.icon || undefined, - originalAssetPaths.logo || undefined, - originalAssetPaths.hero || undefined - ); + customOriginalIconPath: originalAssetPaths.icon || undefined, + customOriginalLogoPath: originalAssetPaths.logo || undefined, + customOriginalHeroPath: originalAssetPaths.hero || undefined, + }); }; const handleUpdateGame = async () => { From e5646240abdbb08a6972c49078afdfc8ca45c679 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 02:18:22 +0300 Subject: [PATCH 28/52] fix: async error function had too many arguments --- .../library/update-game-custom-assets.ts | 4 +--- src/preload/index.ts | 20 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 57b14775..1f912901 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -44,9 +44,7 @@ interface UpdateGameDataParams { customOriginalHeroPath?: string | null; } -const updateGameData = async ( - params: UpdateGameDataParams -): Promise => { +const updateGameData = async (params: UpdateGameDataParams): Promise => { const { gameKey, existingGame, diff --git a/src/preload/index.ts b/src/preload/index.ts index b92ba137..17c1225f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -164,16 +164,16 @@ contextBridge.exposeInMainWorld("electron", { originalHeroPath?: string; }) => ipcRenderer.invoke("updateCustomGame", params), updateGameCustomAssets: (params: { - shop: GameShop; - objectId: string; - title: string; - customIconUrl?: string | null; - customLogoImageUrl?: string | null; - customHeroImageUrl?: string | null; - customOriginalIconPath?: string | null; - customOriginalLogoPath?: string | null; - customOriginalHeroPath?: string | null; - }) => ipcRenderer.invoke("updateGameCustomAssets", params), + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; + }) => ipcRenderer.invoke("updateGameCustomAssets", params), createGameShortcut: ( shop: GameShop, objectId: string, From de4b039d105332fad4bc36c58d1e4dd323675f56 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 02:46:58 +0300 Subject: [PATCH 29/52] Fix: deleted favorite icon from profile game card and fixed the media for showing short format of hours --- .../user-library-game-card.scss | 24 +---------- .../user-library-game-card.tsx | 40 ++++++++----------- 2 files changed, 17 insertions(+), 47 deletions(-) diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index a19961fd..5d0d7f2c 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -91,28 +91,6 @@ z-index: 2; } - &__favorite-icon { - color: rgba(255, 255, 255, 0.8); - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - border: solid 1px rgba(255, 255, 255, 0.15); - border-radius: 50%; - padding: 6px; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - transition: all ease 0.2s; - - &:hover { - background: rgba(0, 0, 0, 0.5); - border-color: rgba(255, 255, 255, 0.25); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); - } - } - &__pin-button { color: rgba(255, 255, 255, 0.8); background: rgba(0, 0, 0, 0.4); @@ -173,7 +151,7 @@ } // When the card is narrow (less than 180px), show short format - @container (max-width: 180px) { + @container (max-width: 140px) { &-long { display: none; } diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index 251a3bc7..eac0912d 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -13,7 +13,6 @@ import { ClockIcon, TrophyIcon, AlertFillIcon, - HeartFillIcon, PinIcon, PinSlashIcon, } from "@primer/octicons-react"; @@ -135,30 +134,23 @@ export function UserLibraryGameCard({ onClick={() => navigate(buildUserGameDetailsPath(game))} >
    - {(game.isFavorite || isMe) && ( + {isMe && (
    - {game.isFavorite && ( -
    - -
    - )} - {isMe && ( - - )} +
    )}
    Date: Mon, 29 Sep 2025 22:15:53 +0100 Subject: [PATCH 30/52] feat: sync with main --- .../profile-content/profile-content.tsx | 154 +++--------------- 1 file changed, 27 insertions(+), 127 deletions(-) diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index 8de16d3d..41b11ba3 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -5,7 +5,6 @@ import { useAppDispatch, useFormat } from "@renderer/hooks"; import { setHeaderTitle } from "@renderer/features"; import { TelescopeIcon, ChevronRightIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; -import { UserGame } from "@types"; import { LockedProfile } from "./locked-profile"; import { ReportProfile } from "../report-profile/report-profile"; import { FriendsBox } from "./friends-box"; @@ -17,8 +16,6 @@ import { useSectionCollapse } from "@renderer/hooks/use-section-collapse"; import { motion, AnimatePresence } from "framer-motion"; import { sectionVariants, - gameCardVariants, - gameGridVariants, chevronVariants, GAME_STATS_ANIMATION_DURATION_IN_MS, } from "./profile-animations"; @@ -38,8 +35,6 @@ export function ProfileContent() { const [statsIndex, setStatsIndex] = useState(0); const [isAnimationRunning, setIsAnimationRunning] = useState(true); const [sortBy, setSortBy] = useState("playedRecently"); - const [prevLibraryGames, setPrevLibraryGames] = useState([]); - const [prevPinnedGames, setPrevPinnedGames] = useState([]); const statsAnimation = useRef(-1); const { toggleSection, isPinnedCollapsed } = useSectionCollapse(); @@ -92,27 +87,6 @@ export function ProfileContent() { const { numberFormatter } = useFormat(); - const gamesHaveChanged = ( - current: UserGame[], - previous: UserGame[] - ): boolean => { - if (current.length !== previous.length) return true; - return current.some( - (game, index) => game.objectId !== previous[index]?.objectId - ); - }; - - const shouldAnimateLibrary = gamesHaveChanged(libraryGames, prevLibraryGames); - const shouldAnimatePinned = gamesHaveChanged(pinnedGames, prevPinnedGames); - - useEffect(() => { - setPrevLibraryGames(libraryGames); - }, [libraryGames]); - - useEffect(() => { - setPrevPinnedGames(pinnedGames); - }, [pinnedGames]); - const usersAreFriends = useMemo(() => { return userProfile?.relation?.status === "ACCEPTED"; }, [userProfile]); @@ -192,57 +166,21 @@ export function ProfileContent() { exit="collapsed" layout > - - {shouldAnimatePinned ? ( - - {pinnedGames?.map((game, index) => ( - - - - ))} - - ) : ( - pinnedGames?.map((game) => ( -
  • - -
  • - )) - )} -
    +
      + {pinnedGames?.map((game) => ( +
    • + +
    • + ))} +
    )} @@ -262,54 +200,18 @@ export function ProfileContent() {
    - - {shouldAnimateLibrary ? ( - - {libraryGames?.map((game, index) => ( - - - - ))} - - ) : ( - libraryGames?.map((game) => ( -
  • - -
  • - )) - )} -
    +
      + {libraryGames?.map((game) => ( +
    • + +
    • + ))} +
    )}
    @@ -338,8 +240,6 @@ export function ProfileContent() { pinnedGames, isPinnedCollapsed, toggleSection, - shouldAnimateLibrary, - shouldAnimatePinned, sortBy, ]); From 26dfb6db8edd4eb07b604fda0df6ec7b03f24472 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 30 Sep 2025 01:07:45 +0100 Subject: [PATCH 31/52] feat: new style for sidebar on game page --- src/locales/ar/translation.json | 1 - src/locales/be/translation.json | 7 +-- src/locales/bg/translation.json | 1 - src/locales/ca/translation.json | 7 +-- src/locales/cs/translation.json | 1 - src/locales/da/translation.json | 7 +-- src/locales/de/translation.json | 5 +-- src/locales/et/translation.json | 6 +-- src/locales/fa/translation.json | 7 +-- src/locales/fr/translation.json | 1 - src/locales/hu/translation.json | 7 +-- src/locales/id/translation.json | 7 +-- src/locales/it/translation.json | 7 +-- src/locales/kk/translation.json | 7 +-- src/locales/ko/translation.json | 7 +-- src/locales/nb/translation.json | 7 +-- src/locales/nl/translation.json | 7 +-- src/locales/pl/translation.json | 7 +-- src/locales/pt-BR/translation.json | 43 +++++++++++++++++-- src/locales/pt-PT/translation.json | 10 +---- src/locales/ro/translation.json | 7 +-- src/locales/ru/translation.json | 4 +- src/locales/sv/translation.json | 1 - src/locales/tr/translation.json | 1 - src/locales/uk/translation.json | 1 - src/locales/uz/translation.json | 1 - src/locales/zh/translation.json | 3 -- .../description-header.scss | 6 +-- .../gallery-slider/gallery-slider.scss | 41 ++++++++++++++++++ .../game-details/game-details-content.tsx | 23 ++++++++++ .../src/pages/game-details/game-details.scss | 30 ++++++++++--- .../sidebar-section/sidebar-section.scss | 6 +++ .../pages/game-details/sidebar/sidebar.scss | 7 ++- src/renderer/src/pages/home/home.scss | 9 ++++ src/renderer/src/pages/home/home.tsx | 2 +- 35 files changed, 169 insertions(+), 125 deletions(-) diff --git a/src/locales/ar/translation.json b/src/locales/ar/translation.json index 782d6b51..034d0cbd 100644 --- a/src/locales/ar/translation.json +++ b/src/locales/ar/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "تم تسجيل الدخول بنجاح" }, "home": { - "featured": "مميز", "surprise_me": "مفاجئني", "no_results": "لم يتم العثور على نتائج", "start_typing": "ابدأ بالكتابة للبحث...", diff --git a/src/locales/be/translation.json b/src/locales/be/translation.json index c9d49626..8d67e693 100644 --- a/src/locales/be/translation.json +++ b/src/locales/be/translation.json @@ -1,7 +1,6 @@ { "language_name": "беларуская мова", "home": { - "featured": "Рэкамэндаванае", "surprise_me": "Здзіві мяне", "no_results": "Няма вынікаў" }, @@ -17,7 +16,6 @@ "home": "Галоўная", "favorites": "Улюбленыя" }, - "header": { "search": "Пошук", "home": "Галоўная", @@ -31,10 +29,7 @@ "downloading_metadata": "Сцягванне мэтаданых {{title}}…", "downloading": "Сцягванне {{title}}… ({{percentage}} скончана) - Канчатак {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Наступная старонка", - "previous_page": "Папярэдняя старонка" - }, + "catalogue": {}, "game_details": { "open_download_options": "Адкрыць варыянты сцягвання", "download_options_zero": "Няма варыянтаў сцягвання", diff --git a/src/locales/bg/translation.json b/src/locales/bg/translation.json index 458b9e36..3e289700 100644 --- a/src/locales/bg/translation.json +++ b/src/locales/bg/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Успешно влизане" }, "home": { - "featured": "Препоръчани", "surprise_me": "Изненадай ме", "no_results": "Няма намерени резултати", "start_typing": "Започнете да пишете за търсене...", diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json index aa69001f..96eb67e2 100644 --- a/src/locales/ca/translation.json +++ b/src/locales/ca/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Has entrat correctament" }, "home": { - "featured": "Destacats", "surprise_me": "Sorprèn-me", "no_results": "No s'ha trobat res" }, @@ -25,7 +24,6 @@ }, "header": { "search": "Cerca jocs", - "home": "Inici", "catalogue": "Catàleg", "downloads": "Baixades", @@ -41,10 +39,7 @@ "calculating_eta": "Descarregant {{title}}… ({{percentage}} completat) - Calculant el temps restant…", "checking_files": "Comprovant els fitxers de {{title}}… ({{percentage}} completat)" }, - "catalogue": { - "next_page": "Pàgina següent", - "previous_page": "Pàgina anterior" - }, + "catalogue": {}, "game_details": { "open_download_options": "Obre les opcions de baixada", "download_options_zero": "No hi ha opcions de baixada", diff --git a/src/locales/cs/translation.json b/src/locales/cs/translation.json index 9b501b54..6bcc8944 100644 --- a/src/locales/cs/translation.json +++ b/src/locales/cs/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Úspěšně přihlášen" }, "home": { - "featured": "Doporučené", "surprise_me": "Překvap mě", "no_results": "Výsledek nenalezen", "start_typing": "Začni psát pro vyhledávání...", diff --git a/src/locales/da/translation.json b/src/locales/da/translation.json index 618f085c..21a92f72 100644 --- a/src/locales/da/translation.json +++ b/src/locales/da/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Loggede ind successfuldt" }, "home": { - "featured": "Anbefalet", "surprise_me": "Overrask mig", "no_results": "Ingen resultater fundet", "start_typing": "Begynd at skrive for at søge...", @@ -29,7 +28,6 @@ }, "header": { "search": "Søg efter spil", - "home": "Hjem", "catalogue": "Katalog", "downloads": "Downloads", @@ -45,10 +43,7 @@ "calculating_eta": "Downloader {{title}}… ({{percentage}} færdig) - Udregner resterende tid…", "checking_files": "Checker {{title}} filer… ({{percentage}} færdig)" }, - "catalogue": { - "next_page": "Næste side", - "previous_page": "Forrige side" - }, + "catalogue": {}, "game_details": { "open_download_options": "Åben download muligheder", "download_options_zero": "Ingen download mulighed", diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index 5101f459..fb285ee0 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Erfolgreich angemeldet" }, "home": { - "featured": "Empfohlen", "surprise_me": "Überrasche mich", "no_results": "Keine Ergebnisse gefunden", "start_typing": "Tippe, um zu suchen...", @@ -59,9 +58,7 @@ "download_sources": "Download-Quellen", "result_count": "{{resultCount}} Ergebnisse", "filter_count": "{{filterCount}} verfügbar", - "clear_filters": "{{filterCount}} ausgewählte löschen", - "next_page": "Nächste Seite", - "previous_page": "Vorherige Seite" + "clear_filters": "{{filterCount}} ausgewählte löschen" }, "game_details": { "open_download_options": "Download-Optionen öffnen", diff --git a/src/locales/et/translation.json b/src/locales/et/translation.json index 119e1aab..c5566eeb 100644 --- a/src/locales/et/translation.json +++ b/src/locales/et/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Edukalt sisse logitud" }, "home": { - "featured": "Esile toodud", "surprise_me": "Üllata mind", "no_results": "Tulemusi ei leitud", "start_typing": "Alusta otsimiseks kirjutamist...", @@ -45,10 +44,7 @@ "calculating_eta": "{{title}} allalaadimine… ({{percentage}} valmis) - Järelejäänud aja arvutamine…", "checking_files": "{{title}} failide kontrollimine… ({{percentage}} valmis)" }, - "catalogue": { - "next_page": "Järgmine leht", - "previous_page": "Eelmine leht" - }, + "catalogue": {}, "game_details": { "open_download_options": "Ava allalaadimise valikud", "download_options_zero": "Allalaadimise valikuid pole", diff --git a/src/locales/fa/translation.json b/src/locales/fa/translation.json index be18263a..69a49b79 100644 --- a/src/locales/fa/translation.json +++ b/src/locales/fa/translation.json @@ -1,7 +1,6 @@ { "language_name": "فارسی", "home": { - "featured": "پیشنهادی", "surprise_me": "سوپرایزم کن", "no_results": "اتمام‌ای پیدا نشد" }, @@ -17,7 +16,6 @@ "home": "خانه", "favorites": "علاقه‌مندی‌ها" }, - "header": { "search": "جستجوی بازی‌ها", "home": "خانه", @@ -31,10 +29,7 @@ "downloading_metadata": "درحال دانلود متادیتاهای {{title}}…", "downloading": "در حال دانلود {{title}}… ({{percentage}} تکمیل شده) - اتمام {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "صفحه‌ی بعدی", - "previous_page": "صفحه‌ی قبلی" - }, + "catalogue": {}, "game_details": { "open_download_options": "بازکردن آپشن‌های دانلود", "download_options_zero": "هیچ آپشن دانلودی وجود ندارد", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index 1c129a64..8fc07722 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Connecté avec succès" }, "home": { - "featured": "En vedette", "surprise_me": "Surprenez-moi", "no_results": "Aucun résultat trouvé", "start_typing": "Commencez à taper pour rechercher...", diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 0cea87b0..efed5e2d 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -1,7 +1,6 @@ { "language_name": "Magyar", "home": { - "featured": "Featured", "surprise_me": "Lepj meg", "no_results": "Nem található" }, @@ -19,7 +18,6 @@ }, "header": { "search": "Keresés", - "home": "Főoldal", "catalogue": "Katalógus", "downloads": "Letöltések", @@ -31,10 +29,7 @@ "downloading_metadata": "{{title}} metaadatainak letöltése…", "downloading": "{{title}} letöltése… ({{percentage}} kész) - Befejezés {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Következő olda", - "previous_page": "Előző olda" - }, + "catalogue": {}, "game_details": { "open_download_options": "Letöltési lehetőségek", "download_options_zero": "Nincs letöltési lehetőség", diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json index 4fa347fc..fc72fc51 100644 --- a/src/locales/id/translation.json +++ b/src/locales/id/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Berhasil masuk" }, "home": { - "featured": "Unggulan", "surprise_me": "Kejutkan saya", "no_results": "Tidak ada hasil ditemukan" }, @@ -25,7 +24,6 @@ }, "header": { "search": "Cari game", - "home": "Beranda", "catalogue": "Katalog", "downloads": "Unduhan", @@ -41,10 +39,7 @@ "calculating_eta": "Mengunduh {{title}}… ({{percentage}} selesai) - Menghitung waktu yang tersisa…", "checking_files": "Memeriksa file {{title}}… ({{percentage}} selesai)" }, - "catalogue": { - "next_page": "Halaman Berikutnya", - "previous_page": "Halaman Sebelumnya" - }, + "catalogue": {}, "game_details": { "open_download_options": "Buka opsi unduhan", "download_options_zero": "Tidak ada opsi unduhan", diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index b23d1244..ac37ffe9 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -1,7 +1,6 @@ { "language_name": "Italiano", "home": { - "featured": "In primo piano", "surprise_me": "Sorprendimi", "no_results": "Nessun risultato trovato" }, @@ -20,7 +19,6 @@ }, "header": { "search": "Cerca", - "home": "Home", "catalogue": "Catalogo", "downloads": "Download", @@ -32,10 +30,7 @@ "downloading_metadata": "Scaricamento metadati di {{title}}…", "downloading": "Download di {{title}}… ({{percentage}} completato) - Conclusione {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Pagina successiva", - "previous_page": "Pagina precedente" - }, + "catalogue": {}, "game_details": { "open_download_options": "Apri opzioni di download", "download_options_zero": "Nessuna opzione di download", diff --git a/src/locales/kk/translation.json b/src/locales/kk/translation.json index bfb009a7..48fb8181 100644 --- a/src/locales/kk/translation.json +++ b/src/locales/kk/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Сәтті кіру" }, "home": { - "featured": "Ұсынылған", "surprise_me": "Таңқалдыр", "no_results": "Ештеңе табылмады" }, @@ -23,7 +22,6 @@ "sign_in": "Кіру", "favorites": "Таңдаулылар" }, - "header": { "search": "Іздеу", "home": "Басты бет", @@ -40,10 +38,7 @@ "downloading": "Жүктеу {{title}}… ({{percentage}} аяқталды) - Аяқтау {{eta}} - {{speed}}", "calculating_eta": "Жүктеу {{title}}… ({{percentage}} аяқталды) - Қалған уақытты есептеу…" }, - "catalogue": { - "next_page": "Келесі бет", - "previous_page": "Алдыңғы бет" - }, + "catalogue": {}, "game_details": { "open_download_options": "Жүктеу нұсқаларын ашу", "download_options_zero": "Жүктеу нұсқалары жоқ", diff --git a/src/locales/ko/translation.json b/src/locales/ko/translation.json index 9ec389b1..a9b9c0e5 100644 --- a/src/locales/ko/translation.json +++ b/src/locales/ko/translation.json @@ -1,7 +1,6 @@ { "language_name": "한국어", "home": { - "featured": "추천", "surprise_me": "무작위 추천", "no_results": "결과 없음" }, @@ -17,7 +16,6 @@ "home": "홈", "favorites": "즐겨찾기" }, - "header": { "search": "게임 검색하기", "home": "홈", @@ -31,10 +29,7 @@ "downloading_metadata": "{{title}}의 메타데이터를 다운로드 중…", "downloading": "{{title}}의 파일들을 다운로드 중… ({{percentage}} 완료) - 완료까지 {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "다음 페이지", - "previous_page": "이전 페이지" - }, + "catalogue": {}, "game_details": { "open_download_options": "다운로드 선택지 열기", "download_options_zero": "다운로드 선택지 없음", diff --git a/src/locales/nb/translation.json b/src/locales/nb/translation.json index 8898ec7b..95bda8fe 100644 --- a/src/locales/nb/translation.json +++ b/src/locales/nb/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Logget inn vellykket" }, "home": { - "featured": "Anbefalinger", "surprise_me": "Overrask meg", "no_results": "Ingen resultater fundet", "start_typing": "Begynn å skrive for å søke...", @@ -29,7 +28,6 @@ }, "header": { "search": "Søk efter spill", - "home": "Hjem", "catalogue": "Katalog", "downloads": "Nedlastinger", @@ -45,10 +43,7 @@ "calculating_eta": "Laster ned {{title}}… ({{percentage}} ferdig) - Regner ut resterende tid…", "checking_files": "Sjekker {{title}} filer… ({{percentage}} ferdig)" }, - "catalogue": { - "next_page": "Neste side", - "previous_page": "Forrige side" - }, + "catalogue": {}, "game_details": { "open_download_options": "Åpne nedlastingsmuligheter", "download_options_zero": "Ingen nedlastingsmulighet", diff --git a/src/locales/nl/translation.json b/src/locales/nl/translation.json index 72d20c74..baa6df6e 100644 --- a/src/locales/nl/translation.json +++ b/src/locales/nl/translation.json @@ -1,7 +1,6 @@ { "language_name": "Nederlands", "home": { - "featured": "Uitgelicht", "surprise_me": "Verrasing", "no_results": "Geen resultaten gevonden" }, @@ -19,7 +18,6 @@ }, "header": { "search": "Zoek spellen", - "home": "Home", "catalogue": "Bibliotheek", "downloads": "Downloads", @@ -31,10 +29,7 @@ "downloading_metadata": "Downloading {{title}} metadata…", "downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Volgende Pagina", - "previous_page": "Vorige Pagina" - }, + "catalogue": {}, "game_details": { "open_download_options": "Open download Instellingen", "download_options_zero": "Geen download Instellingen", diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json index 86751b0e..2e0d1696 100644 --- a/src/locales/pl/translation.json +++ b/src/locales/pl/translation.json @@ -1,7 +1,6 @@ { "language_name": "Polski", "home": { - "featured": "Wyróżnione", "surprise_me": "Zaskocz mnie", "no_results": "Nie znaleziono wyników" }, @@ -20,7 +19,6 @@ }, "header": { "search": "Szukaj", - "home": "Główna", "catalogue": "Katalog", "downloads": "Pobrane", @@ -32,10 +30,7 @@ "downloading_metadata": "Pobieranie {{title}} metadata…", "downloading": "Pobieranie {{title}}… (ukończone w {{percentage}}) - Podsumowanie {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Następna strona", - "previous_page": "Poprzednia strona" - }, + "catalogue": {}, "game_details": { "open_download_options": "Otwórz opcje pobierania", "download_options_zero": "Brak opcji pobierania", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 7f7f8cc1..b88c38a5 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -26,7 +26,22 @@ "sign_in": "Login", "friends": "Amigos", "need_help": "Precisa de ajuda?", - "favorites": "Favoritos" + "favorites": "Favoritos", + "add_custom_game_tooltip": "Adicionar jogo personalizado", + "custom_game_modal": "Adicionar jogo personalizado", + "edit_game_modal_title": "Título", + "playable_button_title": "", + "custom_game_modal_add": "Adicionar Jogo", + "custom_game_modal_adding": "Adicionando...", + "custom_game_modal_browse": "Buscar", + "custom_game_modal_cancel": "Cancelar", + "edit_game_modal_assets": "Imagens", + "edit_game_modal_icon": "Ícone", + "edit_game_modal_browse": "Buscar", + "edit_game_modal_cancel": "Cancelar", + "edit_game_modal_enter_title": "Insira o título", + "edit_game_modal_logo": "Logo", + "edit_game_modal": "Personalizar detalhes" }, "header": { "search": "Buscar jogos", @@ -219,7 +234,18 @@ "historical_keyshop": "Preço histórico em keyshops", "language": "Idioma", "caption": "Legenda", - "audio": "Áudio" + "audio": "Áudio", + "edit_game_modal_button": "Alterar detalhes do jogo", + "game_added_to_pinned": "Jogo adicionado aos fixados", + "game_removed_from_pinned": "Jogo removido dos fixados", + "manual_playtime_tooltip": "Este tempo de jogo foi atualizado manualmente", + "manual_playtime_warning": "As suas horas de jogo serão marcadas como atualizadas manualmente. Esta ação não pode ser desfeita.", + "missing_wine_prefix": "Um prefixo Wine é necessário para criar um backup no Linux", + "update_game_playtime": "Modificar tempo de jogo", + "update_playtime": "Modificar tempo de jogo", + "update_playtime_description": "Atualizar manualmente o tempo de jogo de {{game}}", + "update_playtime_error": "Falha ao atualizar tempo de jogo", + "update_playtime_title": "Atualizar tempo de jogo" }, "activation": { "title": "Ativação", @@ -394,7 +420,8 @@ "hidden": "Oculta", "test_notification": "Testar notificação", "notification_preview": "Prévia da Notificação de Conquistas", - "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" + "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo", + "editor_tab_code": "Código" }, "notifications": { "download_complete": "Download concluído", @@ -523,7 +550,15 @@ "show_achievements_on_profile": "Exiba suas conquistas no perfil", "show_points_on_profile": "Exiba seus pontos ganhos no perfil", "error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido", - "friend_code_length_error": "Código de amigo deve ter 8 caracteres" + "friend_code_length_error": "Código de amigo deve ter 8 caracteres", + "top_percentile": "Top {{percentile}}%", + "playtime": "Tempo de jogo", + "played_recently": "Jogado recentemente", + "pinned": "Fixado", + "amount_minutes_short": "{{amount}}h", + "amount_hours_short": "{{amount}}h", + "game_added_to_pinned": "Jogo adicionado aos fixados", + "achievements_earned": "Conquistas recebidas" }, "achievement": { "achievement_unlocked": "Conquista desbloqueada", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index 6c32b35b..654e94ec 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Sessão iniciada com sucesso" }, "home": { - "featured": "Destaques", "hot": "Populares", "weekly": "📅 Mais descarregados esta semana", "achievements": "🏆 Para completar", @@ -26,7 +25,8 @@ "game_has_no_executable": "O jogo não tem um executável selecionado", "sign_in": "Iniciar sessão", "friends": "Amigos", - "favorites": "Favoritos" + "favorites": "Favoritos", + "edit_game_modal_cancel": "Cancelar" }, "header": { "search": "Procurar jogos", @@ -247,9 +247,6 @@ "download_count_zero": "Sem downloads na lista", "download_count_one": "{{countFormatted}} download na lista", "download_count_other": "{{countFormatted}} downloads na lista", - "download_options_zero": "Sem downloads disponíveis", - "download_options_one": "{{countFormatted}} download disponível", - "download_options_other": "{{countFormatted}} downloads disponíveis", "download_source_url": "URL da fonte", "add_download_source_description": "Insere o URL que contém o ficheiro .json", "download_source_up_to_date": "Sincronizada", @@ -359,8 +356,6 @@ "instructions": "Verifica a forma correta de instalar algum deles na tua distribuição Linux, para garantir a execução normal do jogo" }, "catalogue": { - "next_page": "Página seguinte", - "previous_page": "Página anterior", "search": "Filtrar…", "developers": "Desenvolvedores", "genres": "Géneros", @@ -427,7 +422,6 @@ "friend_code_copied": "Código de amigo copiado", "undo_friendship_modal_text": "Isto vai remover a tua amizade com {{displayName}}", "privacy_hint": "Para controlar quem pode ver o teu perfil, acede às <0>Definições", - "profile_locked": "Este perfil é privado", "image_process_failure": "Falha ao processar a imagem", "required_field": "Este campo é obrigatório", "displayname_min_length": "O nome de apresentação deve ter pelo menos 3 caracteres", diff --git a/src/locales/ro/translation.json b/src/locales/ro/translation.json index c5a81881..8ed6fd39 100644 --- a/src/locales/ro/translation.json +++ b/src/locales/ro/translation.json @@ -1,7 +1,6 @@ { "language_name": "Română", "home": { - "featured": "Recomandate", "surprise_me": "Surprinde-mă", "no_results": "Niciun rezultat găsit" }, @@ -19,7 +18,6 @@ }, "header": { "search": "Caută jocuri", - "home": "Acasă", "catalogue": "Catalog", "downloads": "Descărcări", @@ -32,10 +30,7 @@ "downloading": "Se descarcă {{title}}... ({{percentage}} complet) - Concluzie {{eta}} - {{speed}}", "calculating_eta": "Se descarcă {{title}}... ({{percentage}} complet) - Calculare timp rămas..." }, - "catalogue": { - "next_page": "Pagina următoare", - "previous_page": "Pagina anterioară" - }, + "catalogue": {}, "game_details": { "open_download_options": "Deschide opțiunile de descărcare", "download_options_zero": "Nicio opțiune de descărcare", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index c92d7902..03413554 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -4,14 +4,12 @@ "successfully_signed_in": "Успешный вход" }, "home": { - "featured": "Рекомендации", "surprise_me": "Удиви меня", "no_results": "Ничего не найдено", "hot": "Сейчас популярно", "start_typing": "Начинаю вводить текст...", "weekly": "📅 Лучшие игры недели", - "achievements": "🏆 Игры с достижениями", - "already_in_library": "Уже в библиотеке" + "achievements": "🏆 Игры с достижениями" }, "sidebar": { "catalogue": "Каталог", diff --git a/src/locales/sv/translation.json b/src/locales/sv/translation.json index 0972effa..901e4ca7 100644 --- a/src/locales/sv/translation.json +++ b/src/locales/sv/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Inloggningen lyckades" }, "home": { - "featured": "Utvalt", "surprise_me": "Överraska mig", "no_results": "Inga resultat hittades", "start_typing": "Börja skriva för att söka...", diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index c3fe2081..e8e1cb2b 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Başarıyla giriş yapıldı" }, "home": { - "featured": "Öne Çıkanlar", "surprise_me": "Beni Şaşırt", "no_results": "Sonuç bulunamadı", "start_typing": "Aramak için yazmaya başlayın...", diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json index 48a3972d..26aa8aae 100644 --- a/src/locales/uk/translation.json +++ b/src/locales/uk/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Успішний вхід в систему" }, "home": { - "featured": "Рекомендоване", "surprise_me": "Здивуй мене", "no_results": "Результатів не знайдено", "start_typing": "Почніть набирати текст для пошуку...", diff --git a/src/locales/uz/translation.json b/src/locales/uz/translation.json index d20a9677..24e508af 100644 --- a/src/locales/uz/translation.json +++ b/src/locales/uz/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Kirish muvaffaqiyatli amalga oshirildi" }, "home": { - "featured": "Tavsiya etilgan", "surprise_me": "Hayratda qoldir", "no_results": "Natijalar topilmadi", "hot": "Eng mashhur", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 0323d991..7cdd0c92 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "已成功登录" }, "home": { - "featured": "特色推荐", "surprise_me": "向我推荐", "no_results": "没有找到结果", "start_typing": "键入以开始搜素...", @@ -51,8 +50,6 @@ "installing_common_redist": "{{log}}…" }, "catalogue": { - "next_page": "下一页", - "previous_page": "上一页", "clear_filters": "清除已选的 {{filterCount}} 项", "developers": "开发商", "download_sources": "下载源", diff --git a/src/renderer/src/pages/game-details/description-header/description-header.scss b/src/renderer/src/pages/game-details/description-header/description-header.scss index 920e8068..8ca78eaa 100644 --- a/src/renderer/src/pages/game-details/description-header/description-header.scss +++ b/src/renderer/src/pages/game-details/description-header/description-header.scss @@ -2,7 +2,7 @@ .description-header { width: calc(100% - calc(globals.$spacing-unit * 2)); - margin: calc(globals.$spacing-unit * 1) auto; + margin: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 1); padding: calc(globals.$spacing-unit * 1.5); display: flex; justify-content: space-between; @@ -10,8 +10,8 @@ background-color: globals.$background-color; height: 72px; border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.03); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); &__info { display: flex; diff --git a/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss b/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss index f66da32b..9483b50e 100644 --- a/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss +++ b/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss @@ -7,6 +7,15 @@ display: flex; flex-direction: column; align-items: center; + max-height: 80vh; + + @media (min-width: 1024px) { + max-height: 70vh; + } + + @media (min-width: 1280px) { + max-height: 60vh; + } } &__viewport { @@ -16,8 +25,19 @@ overflow: hidden; border-radius: 8px; + @media (min-width: 1024px) { + width: 80%; + max-height: 400px; + } + @media (min-width: 1280px) { width: 60%; + max-height: 500px; + } + + @media (min-width: 1536px) { + width: 50%; + max-height: 600px; } } @@ -52,10 +72,18 @@ overflow-y: hidden; gap: calc(globals.$spacing-unit / 2); + @media (min-width: 1024px) { + width: 80%; + } + @media (min-width: 1280px) { width: 60%; } + @media (min-width: 1536px) { + width: 50%; + } + &::-webkit-scrollbar-thumb { width: 20%; } @@ -79,6 +107,19 @@ border: solid 1px globals.$border-color; overflow: hidden; position: relative; + aspect-ratio: 16/9; + + @media (min-width: 1024px) { + width: 15%; + } + + @media (min-width: 1280px) { + width: 12%; + } + + @media (min-width: 1536px) { + width: 10%; + } &:hover { opacity: 0.8; diff --git a/src/renderer/src/pages/game-details/game-details-content.tsx b/src/renderer/src/pages/game-details/game-details-content.tsx index 347e5a1c..4e9ecf14 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -43,6 +43,29 @@ export function GameDetailsContent() { const $images = Array.from(document.querySelectorAll("img")); $images.forEach(($image) => { $image.loading = "lazy"; + // Remove any inline width/height styles that might cause overflow + $image.removeAttribute("width"); + $image.removeAttribute("height"); + $image.removeAttribute("style"); + // Set max-width to prevent overflow + $image.style.maxWidth = "100%"; + $image.style.width = "auto"; + $image.style.height = "auto"; + $image.style.boxSizing = "border-box"; + }); + + // Handle videos the same way + const $videos = Array.from(document.querySelectorAll("video")); + $videos.forEach(($video) => { + // Remove any inline width/height styles that might cause overflow + $video.removeAttribute("width"); + $video.removeAttribute("height"); + $video.removeAttribute("style"); + // Set max-width to prevent overflow + $video.style.maxWidth = "100%"; + $video.style.width = "auto"; + $video.style.height = "auto"; + $video.style.boxSizing = "border-box"; }); return document.body.outerHTML; diff --git a/src/renderer/src/pages/game-details/game-details.scss b/src/renderer/src/pages/game-details/game-details.scss index 786a8d30..6b02dde5 100644 --- a/src/renderer/src/pages/game-details/game-details.scss +++ b/src/renderer/src/pages/game-details/game-details.scss @@ -186,9 +186,10 @@ $hero-height: 300px; &__description-content { width: 100%; - height: 100%; + min-height: 100%; min-width: 0; flex: 1; + overflow-x: hidden; } &__description { @@ -199,6 +200,8 @@ $hero-height: 300px; width: 100%; margin-left: auto; margin-right: auto; + overflow-x: auto; + min-height: auto; @media (min-width: 768px) { padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2); @@ -206,20 +209,30 @@ $hero-height: 300px; @media (min-width: 1024px) { padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 2); + width: 80%; } @media (min-width: 1280px) { width: 60%; } - img { + @media (min-width: 1536px) { + width: 50%; + } + + img, + video { border-radius: 5px; margin-top: globals.$spacing-unit; margin-bottom: calc(globals.$spacing-unit * 3); - display: block; - width: 100%; - height: auto; - object-fit: cover; + display: block !important; + max-width: 100% !important; + width: auto !important; + height: auto !important; + object-fit: contain !important; + box-sizing: border-box !important; + word-wrap: break-word; + overflow-wrap: break-word; } a { @@ -247,12 +260,17 @@ $hero-height: 300px; @media (min-width: 1024px) { padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 2); + width: 80%; } @media (min-width: 1280px) { width: 60%; line-height: 22px; } + + @media (min-width: 1536px) { + width: 50%; + } } &__randomizer-button { diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss index 8674b044..f86db399 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss @@ -1,6 +1,12 @@ @use "../../../scss/globals.scss"; .sidebar-section { + background-color: globals.$dark-background-color; + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + overflow: hidden; + &__button { padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2); display: flex; diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.scss b/src/renderer/src/pages/game-details/sidebar/sidebar.scss index d1c54f84..4d212440 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.scss +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.scss @@ -1,11 +1,14 @@ @use "../../../scss/globals.scss"; .content-sidebar { - border-left: solid 1px globals.$border-color; - background-color: globals.$dark-background-color; + background-color: transparent; height: 100%; flex-shrink: 0; width: 280px; + padding: calc(globals.$spacing-unit * 1); + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 1.5); @media (min-width: 1024px) { width: 320px; diff --git a/src/renderer/src/pages/home/home.scss b/src/renderer/src/pages/home/home.scss index 497f074e..478e96a1 100644 --- a/src/renderer/src/pages/home/home.scss +++ b/src/renderer/src/pages/home/home.scss @@ -76,6 +76,15 @@ width: 24px; height: 24px; position: relative; + display: flex; + align-items: center; + justify-content: center; + } + + &__title-flame-icon { + width: 32px; + height: 32px; + object-fit: contain; } &__title { diff --git a/src/renderer/src/pages/home/home.tsx b/src/renderer/src/pages/home/home.tsx index e2f66283..0b762882 100644 --- a/src/renderer/src/pages/home/home.tsx +++ b/src/renderer/src/pages/home/home.tsx @@ -158,7 +158,7 @@ export default function Home() { Flame animation )} From d7e47323e5b3d2f6f00e28e3007ec031f58059e6 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 02:38:09 +0200 Subject: [PATCH 32/52] Update to latest --- src/locales/hu/translation.json | 81 +++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 4d1a65d3..e9d0a016 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -1,16 +1,15 @@ { "language_name": "Magyar", "app": { - "successfully_signed_in": "Sikeresen bejelentkezve" + "successfully_signed_in": "Sikeresen bejelentkeztél" }, "home": { - "featured": "Kiemelt játék", "surprise_me": "Lepj meg", "no_results": "Nincs találat", "start_typing": "Kereséshez gépelj...", "hot": "Most felkapott", "weekly": "📅 A hét felkapott játékai", - "achievements": "🏆 Teljesítendő játékok" + "achievements": "🏆 Platinázható játékok" }, "sidebar": { "catalogue": "Katalógus", @@ -29,6 +28,49 @@ "need_help": "Elakadtál?", "favorites": "Kedvenc játékok", "playable_button_title": "Csak az azonnal játszható játékokat mutasd" + "add_custom_game_tooltip": "Saját játék hozzáadása", + "show_playable_only_tooltip": "Csak játszható játék mutatása", + "custom_game_modal": "Saját játék hozzáadása", + "custom_game_modal_description": "Saját játék hozzáadása egy futtatható fájl által", + "custom_game_modal_executable_path": "Futtatható fájl útvonala", + "custom_game_modal_select_executable": "Futtatható fájl kiválasztása", + "custom_game_modal_title": "Játékcím", + "custom_game_modal_enter_title": "Játék elnevezése", + "custom_game_modal_browse": "Böngészés", + "custom_game_modal_cancel": "Mégse", + "custom_game_modal_add": "Játék hozzáadása", + "custom_game_modal_adding": "Játék hozzáadása...", + "custom_game_modal_success": "Saját játék sikeresen hozzáadva", + "custom_game_modal_failed": "Saját játék hozzáadása sikertelen", + "custom_game_modal_executable": "Futtatható fájl", + "edit_game_modal": "Customize Assets", + "edit_game_modal_description": "Képek és játékadatok módosítása", + "edit_game_modal_title": "Játékcím", + "edit_game_modal_enter_title": "Játék elnevezése", + "edit_game_modal_image": "Kép", + "edit_game_modal_select_image": "Kép kiválasztása", + "edit_game_modal_browse": "Böngészés", + "edit_game_modal_image_preview": "Kép előnézete", + "edit_game_modal_icon": "Ikon", + "edit_game_modal_select_icon": "Ikon kiválasztása", + "edit_game_modal_icon_preview": "Ikon előnézete", + "edit_game_modal_logo": "Logó", + "edit_game_modal_select_logo": "Logó kiválasztása", + "edit_game_modal_logo_preview": "Logó előnézete", + "edit_game_modal_hero": "Játék főképe", + "edit_game_modal_select_hero": "Válaszd ki a játék főképét", + "edit_game_modal_hero_preview": "Játék főkép előnézete", + "edit_game_modal_cancel": "Mégse", + "edit_game_modal_update": "Frissít", + "edit_game_modal_updating": "Frissítés...", + "edit_game_modal_fill_required": "Kérlek töltsd ki az összes kötelező mezőt", + "edit_game_modal_success": "Képek és játékadatok frissítése sikeres", + "edit_game_modal_failed": "Képek és játékadatok frissítése sikertelen", + "edit_game_modal_image_filter": "Kép", + "edit_game_modal_icon_resolution": "Ajánlott felbontás: 256x256px", + "edit_game_modal_logo_resolution": "Ajánlott felbontás: 640x360px", + "edit_game_modal_hero_resolution": "Ajánlott felbontás: 1920x620px", + "edit_game_modal_assets": "Játékadatok" }, "header": { "search": "Keresés", @@ -48,7 +90,7 @@ "checking_files": "A(z) {{title}} fájljaiból… ({{percentage}} kész)", "installing_common_redist": "{{log}}…", "installation_complete": "Telepítés befejezve", - "installation_complete_message": "A(z) Alapvető Segédprogramok sikeresen telepítve" + "installation_complete_message": "A(z) Alapvető segédprogramok sikeresen telepítve" }, "catalogue": { "search": "Szűrés…", @@ -102,7 +144,7 @@ "playing_now": "Játékban: ", "change": "Változtatás", "repacks_modal_description": "Válaszd ki a repacket amit leszeretnél tölteni", - "select_folder_hint": "Hogy megváltoztasd a letöltési helyet, menj a <0>Beállítások menüjébe", + "select_folder_hint": "Hogy megváltoztasd a letöltési mappát, menj a <0>Beállítások menüjébe", "download_now": "Letöltés", "no_shop_details": "A bolt adatai nem érhetőek el.", "download_options": "Letöltési opciók", @@ -134,7 +176,7 @@ "last_downloaded_option": "Utoljára letöltött", "create_steam_shortcut": "Steam parancsikon létrehozása", "create_shortcut_success": "A parancsikon létrehozása sikeres", - "you_might_need_to_restart_steam": "Lehetséges hogy újrakell indítsd a steamet hogy lásd a változást.", + "you_might_need_to_restart_steam": "Lehetséges hogy újrakell indítsd a Steamet hogy lásd a változást.", "create_shortcut_error": "Hiba lépett fel létrehozás közben", "nsfw_content_title": "Ez a játék nem megfelelő tartalmat tartalmaz.", "nsfw_content_description": "{{title}} tartalmaz tartalmat amely nem megfelelő minden korosztálynak. Biztosan folytatni szeretnéd?", @@ -160,7 +202,7 @@ "no_backup_preview": "Ehhez a címhez nem található mentett játék", "restoring_backup": "Biztonsági mentés helyreállítás: ({{progress}} kész)…", "uploading_backup": "Biztonsági mentés feltöltése…", - "no_backups": "You haven't created any backups for this game yet", + "no_backups": "Még nem hoztál létre biztonsági másolatot ehhez a játékhoz", "backup_uploaded": "Biztonsági mentés feltöltve", "backup_deleted": "Biztonsági mentés törölve", "backup_restored": "Biztonsági mentés helyreállítva", @@ -189,7 +231,7 @@ "enable_automatic_cloud_sync": "Automatikus felhőalapú szinkronizálás engedélyezése", "custom_backup_location_set": "Egyéni biztonsági mentési hely", "no_directory_selected": "Nincs kiválasztva mappa", - "no_write_permission": "Nem lehet ebbe a mappába letölteni. Kattints ide további információért.", + "no_write_permission": "Nem lehet a mappába letölteni. Kattints ide további információért.", "reset_achievements": "Achievementek nullázása", "reset_achievements_description": "Ez az összes achievementet nullázza a {{game}} játékhoz", "reset_achievements_title": "Biztos vagy ebben?", @@ -231,12 +273,26 @@ "backup_unfrozen": "Biztonsági mentés leválasztva", "backup_freeze_failed": "Biztonsági mentés rögzítése sikertelen", "backup_freeze_failed_description": "Legalább egy szabad helyet kell hagyni az automatikus biztonsági mentéseknek." + "edit_game_modal_button": "Játékadatok testreszabása", + "game_details": "Játék leírása", + "currency_symbol": "Ft", + "currency_country": "hu", + "prices": "Árak", + "no_prices_found": "Nincsenek található árak", + "view_all_prices": "Összes ár megtekintése", + "retail_price": "Bolti ár", + "keyshop_price": "Nem hivatalos ár", + "historical_retail": "Korábbi bolti ár", + "historical_keyshop": "Korábbi nem hivatalos ár", + "language": "Nyelv", + "caption": "Felirat", + "audio": "Hang" }, "activation": { "title": "Hydra aktiválása", "installation_id": "Telepítési azonosító:", "enter_activation_code": "Írd be az aktiválási kódod", - "message": "Ha nem tudod kit kell kérdezz efelől, akkor nem is szabadna megkérdezned.", + "message": "Ha nem tudod kit kérdezz efelől, akkor nem kéne nálad legyen.", "activate": "Aktiválás", "loading": "Töltés…" }, @@ -450,10 +506,15 @@ "user_profile": { "amount_hours": "{{amount}} óra", "amount_minutes": "{{amount}} perc", + "amount_hours_short": "{{amount}}ó", + "amount_minutes_short": "{{amount}}p", "last_time_played": "Utoljára játszva {{period}}", "activity": "Legutóbbi tevékenység", "library": "Könyvtár", "pinned": "Kitűzve", + "achievements_earned": "Elért achievementek", + "played_recently": "Nemrég játszva", + "playtime": "Játszottidő", "total_play_time": "Teljes játszottidő", "manual_playtime_tooltip": "Ez a játszottidő manuálisan lett frissítve", "no_recent_activity_title": "Hmmm… itt semmi sincs", @@ -531,6 +592,8 @@ "show_points_on_profile": "Mutasd a megszerzett pontjaid a profilodon", "error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barát kódot", "friend_code_length_error": "A barát kódnak 8 karakterből kell állnia" + "game_removed_from_pinned": "Játék eltávolítva a kitűzöttek közül", + "game_added_to_pinned": "Játék hozzáadva a kitűzöttekhez" }, "achievement": { "achievement_unlocked": "Achievement feloldva", From 5648e393bfed8f03756ddf04bc3dc2b5bd19ce8c Mon Sep 17 00:00:00 2001 From: Stormm232 <96331770+Stormm232@users.noreply.github.com> Date: Tue, 30 Sep 2025 02:39:24 +0200 Subject: [PATCH 33/52] Deleted file. --- MAIN_VITE_AUTH_URL.env | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 MAIN_VITE_AUTH_URL.env diff --git a/MAIN_VITE_AUTH_URL.env b/MAIN_VITE_AUTH_URL.env deleted file mode 100644 index c0df04ae..00000000 --- a/MAIN_VITE_AUTH_URL.env +++ /dev/null @@ -1,8 +0,0 @@ -# Environment variables for production - -MAIN_VITE_API_URL=https://hydra-api-us-east-1.losbroxas.org -MAIN_VITE_AUTH_URL=https://auth.hydralauncher.gg -MAIN_VITE_CHECKOUT_URL=https://checkout.hydralauncher.gg -MAIN_VITE_EXTERNAL_RESOURCES_URL=https://assets.hydralauncher.gg -RENDERER_VITE_EXTERNAL_RESOURCES_URL=https://assets.hydralauncher.gg -MAIN_VITE_WS_URL=wss://ws.hydralauncher.gg From e14c125a43e64c1b07f9de20261899ad3e90baf2 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 02:42:58 +0200 Subject: [PATCH 34/52] Small typo --- src/locales/hu/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index e9d0a016..350977d1 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -27,8 +27,8 @@ "friends": "Barátok", "need_help": "Elakadtál?", "favorites": "Kedvenc játékok", - "playable_button_title": "Csak az azonnal játszható játékokat mutasd" - "add_custom_game_tooltip": "Saját játék hozzáadása", + "playable_button_title": "Csak az azonnal játszható játékokat mutasd", + "add_custom_game_tooltip": "Saját játék hozzáadása", "show_playable_only_tooltip": "Csak játszható játék mutatása", "custom_game_modal": "Saját játék hozzáadása", "custom_game_modal_description": "Saját játék hozzáadása egy futtatható fájl által", From f9f110bd1c967d73339f2c4bc918819ec88a6a96 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 02:50:32 +0200 Subject: [PATCH 35/52] Typo --- src/locales/hu/translation.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 350977d1..20ce9e43 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -272,9 +272,9 @@ "backup_frozen": "Biztonsági mentés rögzítve", "backup_unfrozen": "Biztonsági mentés leválasztva", "backup_freeze_failed": "Biztonsági mentés rögzítése sikertelen", - "backup_freeze_failed_description": "Legalább egy szabad helyet kell hagyni az automatikus biztonsági mentéseknek." - "edit_game_modal_button": "Játékadatok testreszabása", - "game_details": "Játék leírása", + "backup_freeze_failed_description": "Legalább egy szabad helyet kell hagyni az automatikus biztonsági mentéseknek.", + "edit_game_modal_button": "Játékadatok testreszabása", + "game_details": "Játék leírása", "currency_symbol": "Ft", "currency_country": "hu", "prices": "Árak", From 46491af53962d3dc31a6396ff947ccf52bc7e048 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 03:08:39 +0200 Subject: [PATCH 36/52] Fix to the translation --- src/locales/hu/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 20ce9e43..864ab1bf 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -591,7 +591,7 @@ "show_achievements_on_profile": "Mutasd az achievementjeid a profilodon", "show_points_on_profile": "Mutasd a megszerzett pontjaid a profilodon", "error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barát kódot", - "friend_code_length_error": "A barát kódnak 8 karakterből kell állnia" + "friend_code_length_error": "A barát kódnak 8 karakterből kell állnia", "game_removed_from_pinned": "Játék eltávolítva a kitűzöttek közül", "game_added_to_pinned": "Játék hozzáadva a kitűzöttekhez" }, From 0df5486fecb7563ec0015df157c1597b4d39328b Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 30 Sep 2025 03:59:08 +0100 Subject: [PATCH 37/52] fix: fixing how long to beat broken logic --- .../game-details/sidebar/how-long-to-beat-section.tsx | 2 +- src/renderer/src/pages/game-details/sidebar/sidebar.scss | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx b/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx index 61c90389..9a29f150 100644 --- a/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx @@ -25,7 +25,7 @@ export function HowLongToBeatSection({ return `${value} ${t(durationTranslation[unit])}`; }; - if (!howLongToBeatData && !isLoading) return null; + if (!howLongToBeatData || !isLoading) return null; return ( diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.scss b/src/renderer/src/pages/game-details/sidebar/sidebar.scss index 4d212440..d1c54f84 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.scss +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.scss @@ -1,14 +1,11 @@ @use "../../../scss/globals.scss"; .content-sidebar { - background-color: transparent; + border-left: solid 1px globals.$border-color; + background-color: globals.$dark-background-color; height: 100%; flex-shrink: 0; width: 280px; - padding: calc(globals.$spacing-unit * 1); - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit * 1.5); @media (min-width: 1024px) { width: 320px; From 4f5c345c421b7447ea4aa4a01706e485f5f641e7 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 30 Sep 2025 05:00:22 +0100 Subject: [PATCH 38/52] ci: testing webhook From 701226d25d871205fc8b8d26e01537183ab55024 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 06:04:04 +0200 Subject: [PATCH 39/52] Correction of Translation for upcoming update --- src/locales/hu/translation.json | 58 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 864ab1bf..4cf5ee45 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -9,7 +9,7 @@ "start_typing": "Kereséshez gépelj...", "hot": "Most felkapott", "weekly": "📅 A hét felkapott játékai", - "achievements": "🏆 Platinázható játékok" + "achievements": "🏆 Achievement támogatott" }, "sidebar": { "catalogue": "Katalógus", @@ -22,7 +22,7 @@ "filter": "Könyvtár szűrése", "home": "Főoldal", "queued": "A(z) {{title}} (Várakozósorban van)", - "game_has_no_executable": "A játékhoz nincs kiválasztva futtatható fájl", + "game_has_no_executable": "A játékhoz nincs tallózva futtatható fájl", "sign_in": "Bejelentkezés", "friends": "Barátok", "need_help": "Elakadtál?", @@ -30,47 +30,47 @@ "playable_button_title": "Csak az azonnal játszható játékokat mutasd", "add_custom_game_tooltip": "Saját játék hozzáadása", "show_playable_only_tooltip": "Csak játszható játék mutatása", - "custom_game_modal": "Saját játék hozzáadása", - "custom_game_modal_description": "Saját játék hozzáadása egy futtatható fájl által", + "custom_game_modal": "Saját játék hozzáadása:", + "custom_game_modal_description": "Adj meg egy futtatható fájlt", "custom_game_modal_executable_path": "Futtatható fájl útvonala", - "custom_game_modal_select_executable": "Futtatható fájl kiválasztása", + "custom_game_modal_select_executable": "A fájl helye", "custom_game_modal_title": "Játékcím", "custom_game_modal_enter_title": "Játék elnevezése", - "custom_game_modal_browse": "Böngészés", + "custom_game_modal_browse": "Tallózás", "custom_game_modal_cancel": "Mégse", "custom_game_modal_add": "Játék hozzáadása", "custom_game_modal_adding": "Játék hozzáadása...", "custom_game_modal_success": "Saját játék sikeresen hozzáadva", "custom_game_modal_failed": "Saját játék hozzáadása sikertelen", "custom_game_modal_executable": "Futtatható fájl", - "edit_game_modal": "Customize Assets", - "edit_game_modal_description": "Képek és játékadatok módosítása", + "edit_game_modal": "Játékmegjelenés", + "edit_game_modal_description": "Játékcím és vizuális elemek módosítása", "edit_game_modal_title": "Játékcím", "edit_game_modal_enter_title": "Játék elnevezése", "edit_game_modal_image": "Kép", - "edit_game_modal_select_image": "Kép kiválasztása", - "edit_game_modal_browse": "Böngészés", + "edit_game_modal_select_image": "Kép útvonala", + "edit_game_modal_browse": "Tallózás", "edit_game_modal_image_preview": "Kép előnézete", "edit_game_modal_icon": "Ikon", - "edit_game_modal_select_icon": "Ikon kiválasztása", + "edit_game_modal_select_icon": "Ikon útvonala", "edit_game_modal_icon_preview": "Ikon előnézete", "edit_game_modal_logo": "Logó", - "edit_game_modal_select_logo": "Logó kiválasztása", + "edit_game_modal_select_logo": "Logó útvonala", "edit_game_modal_logo_preview": "Logó előnézete", - "edit_game_modal_hero": "Játék főképe", - "edit_game_modal_select_hero": "Válaszd ki a játék főképét", - "edit_game_modal_hero_preview": "Játék főkép előnézete", + "edit_game_modal_hero": "Borítókép", + "edit_game_modal_select_hero": "Borítókép útvonala", + "edit_game_modal_hero_preview": "Borítókép előnézete", "edit_game_modal_cancel": "Mégse", "edit_game_modal_update": "Frissít", "edit_game_modal_updating": "Frissítés...", "edit_game_modal_fill_required": "Kérlek töltsd ki az összes kötelező mezőt", - "edit_game_modal_success": "Képek és játékadatok frissítése sikeres", - "edit_game_modal_failed": "Képek és játékadatok frissítése sikertelen", + "edit_game_modal_success": "Játék megjelenés frissítése sikeres", + "edit_game_modal_failed": "Játék megjelenés frissítése sikertelen", "edit_game_modal_image_filter": "Kép", "edit_game_modal_icon_resolution": "Ajánlott felbontás: 256x256px", "edit_game_modal_logo_resolution": "Ajánlott felbontás: 640x360px", "edit_game_modal_hero_resolution": "Ajánlott felbontás: 1920x620px", - "edit_game_modal_assets": "Játékadatok" + "edit_game_modal_assets": "Vizuális elemek:" }, "header": { "search": "Keresés", @@ -156,7 +156,7 @@ "download_settings": "Letöltési beállítások", "downloader": "Letöltési mód", "select_executable": "Tallózás", - "no_executable_selected": "Nincs futtatható fájl kiválasztva", + "no_executable_selected": "Nincs futtatható fájl tallózva", "open_folder": "Mappa megnyitása", "open_download_location": "Letöltött fájlok megtekintése", "create_shortcut": "Asztali parancsikon létrehozása", @@ -225,12 +225,12 @@ "max_number_of_artifacts_reached": "A játék biztonsági mentéseinek száma elérte a határt", "achievements_not_sync": "Tekintsd meg hogyan kell szinkronizálni az achievementjeid", "manage_files_description": "Kezeld mely fájlokról készül biztonsági másolat, és melyek állíthatók vissza", - "select_folder": "Mappa kiválasztása", + "select_folder": "Mappa tallózása", "backup_from": "Biztonsági másolat: {{date}}", "automatic_backup_from": "Automatikus másolat: {{date}}", "enable_automatic_cloud_sync": "Automatikus felhőalapú szinkronizálás engedélyezése", "custom_backup_location_set": "Egyéni biztonsági mentési hely", - "no_directory_selected": "Nincs kiválasztva mappa", + "no_directory_selected": "Nincs mappa tallózva", "no_write_permission": "Nem lehet a mappába letölteni. Kattints ide további információért.", "reset_achievements": "Achievementek nullázása", "reset_achievements_description": "Ez az összes achievementet nullázza a {{game}} játékhoz", @@ -349,7 +349,7 @@ "debrid_linked_message": "Fiók összekapcsolva: \"{{username}}\" ", "save_changes": "Változtatások mentése", "changes_saved": "Változtatások sikeresen mentve", - "download_sources_description": "A Hydra lefogja tölteni a letöltési linkeket a forrásokból. A forrás URL-nek közvetlen linknek kell lennie egy .json fájlhoz, ami tartalmazza a linkeket.", + "download_sources_description": "A Hydra lefogja tölteni a letöltési linkeket a forrásokból. Az URL forrásnak közvetlen linknek kell lennie egy .json fájlhoz, ami tartalmazza a linkeket.", "validate_download_source": "Érvényesítés", "remove_download_source": "Eltávolítás", "add_download_source": "Forrás hozáadása", @@ -386,11 +386,11 @@ "must_be_valid_url": "A forrás egy érvényes URL kell legyen", "blocked_users": "Letiltott felhasználók", "user_unblocked": "Felhasználó letiltva", - "enable_achievement_notifications": "Amikor egy achievement feloldva", + "enable_achievement_notifications": "Amikor egy achievement feloldódik", "launch_minimized": "Hydra indítása minimalizálva", "disable_nsfw_alert": "NSFW figyelmeztetés kikapcsolása", "seed_after_download_complete": "Letöltés utáni seedelés", - "show_hidden_achievement_description": "Rejtett achievementek leírása megjelenítése feloldás előtt", + "show_hidden_achievement_description": "Rejtett achievementek leírásának megjelenítése feloldás előtt", "account": "Fiók", "no_users_blocked": "Nincsenek letiltott felhasználóid", "subscription_active_until": "Hydra Cloud előfizetésed aktív, eddig: {{date}}", @@ -462,7 +462,7 @@ "platinum": "Platinum", "hidden": "Rejtett", "test_notification": "Értesítés tesztelése", - "notification_preview": "Achievement-értesítés - előnézet", + "notification_preview": "Achievement Értesítés Előnézete", "enable_friend_start_game_notifications": "Amikor egy barátod elkezd játszani egy játékot" }, "notifications": { @@ -577,9 +577,9 @@ "report_reason_other": "Egyéb", "profile_reported": "Profil jelentve", "your_friend_code": "A barát kódod:", - "upload_banner": "Banner feltöltés", - "uploading_banner": "Banner feltöltése…", - "background_image_updated": "Banner frissítve", + "upload_banner": "Borítókép feltöltés", + "uploading_banner": "Borítókép feltöltése…", + "background_image_updated": "Borítókép frissítve", "stats": "Statisztikák", "achievements": "achievementek", "games": "Játékok", @@ -618,7 +618,7 @@ "animated_profile_picture": "Animált profilkép", "premium_support": "Premium Támogatás", "show_and_compare_achievements": "Jelenítsd és hasonlítsd az elért achievementjeid másokéhoz", - "animated_profile_banner": "Animált profil banner", + "animated_profile_banner": "Animált profil borítókép", "hydra_cloud": "Hydra Cloud", "hydra_cloud_feature_found": "Épp felfedeztél egy Hydra Cloud funkciót!", "learn_more": "Tudj meg többet", From 300cff2be65d710a86f4c83622e58c9573e581f1 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 30 Sep 2025 05:37:01 +0100 Subject: [PATCH 40/52] ci: changing windows version --- .github/workflows/build.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c54c431c..0f3e0a66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: build: strategy: matrix: - os: [windows-latest, ubuntu-latest] + os: [windows-2022, ubuntu-latest] fail-fast: false runs-on: ${{ matrix.os }} @@ -58,7 +58,7 @@ jobs: RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }} - name: Build Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows-2022' run: yarn build:win env: MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2536c33..babfb565 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: build: strategy: matrix: - os: [windows-latest, ubuntu-latest] + os: [windows-2022, ubuntu-latest] runs-on: ${{ matrix.os }} @@ -59,7 +59,7 @@ jobs: RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }} - name: Build Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows-2022' run: yarn build:win env: MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }} From a625541125eeb4f927a9cdb379ca88c06c90619d Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 30 Sep 2025 06:52:48 +0100 Subject: [PATCH 41/52] fix: fixing modal issue --- src/locales/pt-BR/translation.json | 2 +- src/main/services/steam-250.ts | 2 +- .../description-header.scss | 4 +-- .../game-details/game-details-content.tsx | 34 +++++++++++-------- .../src/pages/game-details/game-details.scss | 12 ++----- .../sidebar-section/sidebar-section.scss | 5 +-- .../pages/game-details/sidebar/sidebar.scss | 5 +-- .../profile-content/profile-content.tsx | 2 ++ .../user-library-game-card.tsx | 4 ++- 9 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index b88c38a5..497ca10d 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -555,7 +555,7 @@ "playtime": "Tempo de jogo", "played_recently": "Jogado recentemente", "pinned": "Fixado", - "amount_minutes_short": "{{amount}}h", + "amount_minutes_short": "{{amount}}m", "amount_hours_short": "{{amount}}h", "game_added_to_pinned": "Jogo adicionado aos fixados", "achievements_earned": "Conquistas recebidas" diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts index 0abc2f14..5652b0d3 100644 --- a/src/main/services/steam-250.ts +++ b/src/main/services/steam-250.ts @@ -10,7 +10,7 @@ export const requestSteam250 = async (path: string) => { const { window } = new JSDOM(response.data); const { document } = window; - return Array.from(document.querySelectorAll(".appline .title a")) + return Array.from(document.querySelectorAll("a[data-title]")) .map(($title) => { const steamGameUrl = ($title as HTMLAnchorElement).href; if (!steamGameUrl) return null; diff --git a/src/renderer/src/pages/game-details/description-header/description-header.scss b/src/renderer/src/pages/game-details/description-header/description-header.scss index 8ca78eaa..1af1480d 100644 --- a/src/renderer/src/pages/game-details/description-header/description-header.scss +++ b/src/renderer/src/pages/game-details/description-header/description-header.scss @@ -1,8 +1,7 @@ @use "../../../scss/globals.scss"; .description-header { - width: calc(100% - calc(globals.$spacing-unit * 2)); - margin: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 1); + width: 100%; padding: calc(globals.$spacing-unit * 1.5); display: flex; justify-content: space-between; @@ -12,6 +11,7 @@ border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + margin-bottom: calc(globals.$spacing-unit * 1); &__info { display: flex; diff --git a/src/renderer/src/pages/game-details/game-details-content.tsx b/src/renderer/src/pages/game-details/game-details-content.tsx index 4e9ecf14..ca2ca023 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -191,14 +191,16 @@ export function GameDetailsContent() { {renderGameLogo()}
    - + {game && ( + + )} {game?.shop !== "custom" && (
    ); } diff --git a/src/renderer/src/pages/game-details/game-details.scss b/src/renderer/src/pages/game-details/game-details.scss index 6b02dde5..e1140d31 100644 --- a/src/renderer/src/pages/game-details/game-details.scss +++ b/src/renderer/src/pages/game-details/game-details.scss @@ -182,6 +182,8 @@ $hero-height: 300px; globals.$background-color 50%, globals.$dark-background-color 100% ); + padding: calc(globals.$spacing-unit * 1.5); + gap: calc(globals.$spacing-unit * 1.5); } &__description-content { @@ -196,22 +198,12 @@ $hero-height: 300px; user-select: text; line-height: 22px; font-size: globals.$body-font-size; - padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 1.5); width: 100%; margin-left: auto; margin-right: auto; overflow-x: auto; min-height: auto; - @media (min-width: 768px) { - padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2); - } - - @media (min-width: 1024px) { - padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 2); - width: 80%; - } - @media (min-width: 1280px) { width: 60%; } diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss index f86db399..69083f14 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss @@ -1,7 +1,7 @@ @use "../../../scss/globals.scss"; .sidebar-section { - background-color: globals.$dark-background-color; + background-color: globals.$background-color; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); @@ -21,7 +21,7 @@ font-weight: bold; &:hover { - background-color: rgba(255, 255, 255, 0.05); + background-color: rgba(255, 255, 255, 0.1); } &:active { @@ -40,6 +40,7 @@ &__content { overflow: hidden; transition: max-height 0.4s cubic-bezier(0, 1, 0, 1); + background-color: globals.$dark-background-color; position: relative; } } diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.scss b/src/renderer/src/pages/game-details/sidebar/sidebar.scss index d1c54f84..06519f6c 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.scss +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.scss @@ -1,11 +1,12 @@ @use "../../../scss/globals.scss"; .content-sidebar { - border-left: solid 1px globals.$border-color; - background-color: globals.$dark-background-color; height: 100%; flex-shrink: 0; width: 280px; + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 1.5); @media (min-width: 1024px) { width: 320px; diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index 41b11ba3..56f7d20b 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -177,6 +177,7 @@ export function ProfileContent() { statIndex={statsIndex} onMouseEnter={handleOnMouseEnterGameCard} onMouseLeave={handleOnMouseLeaveGameCard} + sortBy={sortBy} /> ))} @@ -208,6 +209,7 @@ export function ProfileContent() { statIndex={statsIndex} onMouseEnter={handleOnMouseEnterGameCard} onMouseLeave={handleOnMouseLeaveGameCard} + sortBy={sortBy} /> ))} diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index eac0912d..a3d24958 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -26,6 +26,7 @@ interface UserLibraryGameCardProps { statIndex: number; onMouseEnter: () => void; onMouseLeave: () => void; + sortBy?: string; } export function UserLibraryGameCard({ @@ -33,6 +34,7 @@ export function UserLibraryGameCard({ statIndex, onMouseEnter, onMouseLeave, + sortBy, }: UserLibraryGameCardProps) { const { userProfile, isMe, getUserLibraryGames } = useContext(userProfileContext); @@ -108,7 +110,7 @@ export function UserLibraryGameCard({ !game.isPinned ); - await getUserLibraryGames(); + await getUserLibraryGames(sortBy); if (game.isPinned) { showSuccessToast(t("game_removed_from_pinned")); From 3826294337f18747f1dff8e1defb46ab7c18b7bf Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 12:42:29 +0200 Subject: [PATCH 42/52] Fixes --- src/locales/hu/translation.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 2d38690c..c264b9e6 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -92,7 +92,6 @@ "installation_complete": "Telepítés befejezve", "installation_complete_message": "A(z) Alapvető segédprogramok sikeresen telepítve" }, -<<<<<<< HEAD "catalogue": { "search": "Szűrés…", "developers": "Fejlesztők", @@ -104,9 +103,7 @@ "filter_count": "{{filterCount}} elérhető", "clear_filters": "{{filterCount}} kiválaszott szűrő törlése" }, -======= "catalogue": {}, ->>>>>>> 4f5c345c421b7447ea4aa4a01706e485f5f641e7 "game_details": { "open_download_options": "Letöltési opciók megnyitása", "download_options_zero": "Nincs letöltési opció", From cb0fc826443fae60005f7b8614863f0a6eaf0c76 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 14:24:00 +0300 Subject: [PATCH 43/52] feat: added ability to reset assets for custom game --- .../game-details/modals/edit-game-modal.tsx | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 0f6df95d..93a8ff6f 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -4,6 +4,7 @@ import { ImageIcon, XIcon } from "@primer/octicons-react"; import { Modal, TextField, Button } from "@renderer/components"; import { useToast } from "@renderer/hooks"; +import { generateRandomGradient } from "@renderer/helpers"; import type { LibraryGame, Game, ShopDetailsWithAssets } from "@types"; import "./edit-game-modal.scss"; @@ -44,6 +45,11 @@ export function EditGameModal({ logo: "", hero: "", }); + const [removedAssets, setRemovedAssets] = useState({ + icon: false, + logo: false, + hero: false, + }); const [defaultUrls, setDefaultUrls] = useState({ icon: null as string | null, logo: null as string | null, @@ -158,6 +164,21 @@ export function EditGameModal({ return defaultUrls[assetType]; }; + const getOriginalAssetUrl = (assetType: AssetType): string | null => { + if (!game || !isCustomGame(game)) return null; + + switch (assetType) { + case "icon": + return game.iconUrl; + case "logo": + return game.logoImageUrl; + case "hero": + return game.libraryHeroImageUrl; + default: + return null; + } + }; + const handleSelectAsset = async (assetType: AssetType) => { const { filePaths } = await window.electron.showOpenDialog({ properties: ["openFile"], @@ -183,6 +204,8 @@ export function EditGameModal({ ...prev, [assetType]: originalPath, })); + // Clear the removed flag when a new asset is selected + setRemovedAssets((prev) => ({ ...prev, [assetType]: false })); } catch (error) { console.error(`Failed to copy ${assetType} asset:`, error); setAssetPath(assetType, originalPath); @@ -191,14 +214,25 @@ export function EditGameModal({ ...prev, [assetType]: originalPath, })); + // Clear the removed flag when a new asset is selected + setRemovedAssets((prev) => ({ ...prev, [assetType]: false })); } } }; const handleRestoreDefault = (assetType: AssetType) => { - setAssetPath(assetType, ""); - setAssetDisplayPath(assetType, ""); - setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); + if (game && isCustomGame(game)) { + // For custom games, mark asset as removed and clear paths + setRemovedAssets((prev) => ({ ...prev, [assetType]: true })); + setAssetPath(assetType, ""); + setAssetDisplayPath(assetType, ""); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); + } else { + // For non-custom games, clear custom assets (restore to shop defaults) + setAssetPath(assetType, ""); + setAssetDisplayPath(assetType, ""); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); + } }; const getOriginalTitle = (): string => { @@ -330,13 +364,19 @@ export function EditGameModal({ // Helper function to prepare custom game assets const prepareCustomGameAssets = (game: LibraryGame | Game) => { - const iconUrl = assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl; - const logoImageUrl = assetPaths.logo - ? `local:${assetPaths.logo}` - : game.logoImageUrl; - const libraryHeroImageUrl = assetPaths.hero - ? `local:${assetPaths.hero}` - : game.libraryHeroImageUrl; + // For custom games, check if asset was explicitly removed + const iconUrl = removedAssets.icon ? null : (assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl); + const logoImageUrl = removedAssets.logo ? null : (assetPaths.logo ? `local:${assetPaths.logo}` : game.logoImageUrl); + + // For hero image, if removed, restore to the original gradient or keep the original + let libraryHeroImageUrl; + if (removedAssets.hero) { + // If the original hero was a gradient (data URL), keep it, otherwise generate a new one + const originalHero = game.libraryHeroImageUrl; + libraryHeroImageUrl = originalHero?.startsWith('data:image/svg+xml') ? originalHero : generateRandomGradient(); + } else { + libraryHeroImageUrl = assetPaths.hero ? `local:${assetPaths.hero}` : game.libraryHeroImageUrl; + } return { iconUrl, logoImageUrl, libraryHeroImageUrl }; }; @@ -418,6 +458,13 @@ export function EditGameModal({ (game: LibraryGame | Game) => { setGameName(game.title || ""); + // Reset removed assets state + setRemovedAssets({ + icon: false, + logo: false, + hero: false, + }); + if (isCustomGame(game)) { setCustomGameAssets(game); // Clear default URLs for custom games @@ -481,7 +528,7 @@ export function EditGameModal({ {t("edit_game_modal_browse")} - {game && !isCustomGame(game) && assetPath && ( + {game && (assetPath || (isCustomGame(game) && getOriginalAssetUrl(assetType))) && ( - {game && (assetPath || (isCustomGame(game) && getOriginalAssetUrl(assetType))) && ( - - )} + {game && + (assetPath || + (isCustomGame(game) && getOriginalAssetUrl(assetType))) && ( + + )} } /> From 01b8f0c3afdd0f94764d939a4fd1b65f17c1ba9e Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 14:21:58 +0200 Subject: [PATCH 45/52] Adjusted again - Drop Hero Image and such r left untranslated due to no lines in json --- src/locales/hu/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index c264b9e6..78ee6336 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -32,7 +32,7 @@ "show_playable_only_tooltip": "Csak játszható játék mutatása", "custom_game_modal": "Saját játék hozzáadása:", "custom_game_modal_description": "Adj meg egy futtatható fájlt", - "custom_game_modal_executable_path": "Futtatható fájl útvonala", + "custom_game_modal_executable_path": "A fájl útvonala", "custom_game_modal_select_executable": "A fájl helye", "custom_game_modal_title": "Játékcím", "custom_game_modal_enter_title": "Játék elnevezése", From 406b4559601aa5e43b3deb53ed20a8b68cf31b34 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" <96331770+Stormm232@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:48:20 +0200 Subject: [PATCH 46/52] Update build.yml --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 906fab06..0f3e0a66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,8 +4,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -on: - workflow_dispatch: +on: pull_request jobs: build: From 937a3d189e8b4db87d9fa8cd0afa0bd3f69347b3 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 15:11:09 +0200 Subject: [PATCH 47/52] Fixed an error --- src/locales/hu/translation.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 78ee6336..3936f45e 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -515,7 +515,7 @@ "pinned": "Kitűzve", "achievements_earned": "Elért achievementek", "played_recently": "Nemrég játszva", - "playtime": "Játszottidő", + "playtime": "Játszottidő", "total_play_time": "Teljes játszottidő", "manual_playtime_tooltip": "Ez a játszottidő manuálisan lett frissítve", "no_recent_activity_title": "Hmmm… itt semmi sincs", @@ -565,7 +565,7 @@ "image_process_failure": "Hiba a kép feldolgozása közben", "required_field": "Ez a mező kötelező", "displayname_min_length": "A megjelenített névnek legalább 3 karakter hosszúnak kell lennie", - "displayname_max_length" : "A megjelenített név hossza legfeljebb 50 karakter lehet", + "displayname_max_length": "A megjelenített név hossza legfeljebb 50 karakter lehet", "report_profile": "Profil bejelentése", "report_reason": "Miért jelented ezt a profilt?", "report_description": "További információ", @@ -594,7 +594,7 @@ "error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barát kódot", "friend_code_length_error": "A barát kódnak 8 karakterből kell állnia", "game_removed_from_pinned": "Játék eltávolítva a kitűzöttek közül", - "game_added_to_pinned": "Játék hozzáadva a kitűzöttekhez" + "game_added_to_pinned": "Játék hozzáadva a kitűzöttekhez" }, "achievement": { "achievement_unlocked": "Achievement feloldva", @@ -625,4 +625,4 @@ "learn_more": "Tudj meg többet", "debrid_description": "Akár 4x gyorsabb letöltés a Nimbusszal" } -} \ No newline at end of file +} From 45903d778ebfcc29e820d1ebac43e328b20498c9 Mon Sep 17 00:00:00 2001 From: "Kiwo.2" Date: Tue, 30 Sep 2025 16:12:29 +0200 Subject: [PATCH 48/52] Adjustment to Translation --- src/locales/hu/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 3936f45e..faff680d 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -33,7 +33,7 @@ "custom_game_modal": "Saját játék hozzáadása:", "custom_game_modal_description": "Adj meg egy futtatható fájlt", "custom_game_modal_executable_path": "A fájl útvonala", - "custom_game_modal_select_executable": "A fájl helye", + "custom_game_modal_select_executable": "Futtatható fájl helye", "custom_game_modal_title": "Játékcím", "custom_game_modal_enter_title": "Játék elnevezése", "custom_game_modal_browse": "Tallózás", From 9c87964e1674bf89f8a9347b1967ff5899e14329 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 19:19:39 +0300 Subject: [PATCH 49/52] fix: added missing translations --- src/locales/en/translation.json | 8 +++++++- src/locales/ru/translation.json | 9 ++++++++- .../src/pages/game-details/modals/edit-game-modal.tsx | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 93fd5b0a..327499d0 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -70,7 +70,13 @@ "edit_game_modal_icon_resolution": "Recommended resolution: 256x256px", "edit_game_modal_logo_resolution": "Recommended resolution: 640x360px", "edit_game_modal_hero_resolution": "Recommended resolution: 1920x620px", - "edit_game_modal_assets": "Assets" + "edit_game_modal_assets": "Assets", + "edit_game_modal_drop_icon_image_here": "Drop icon image here", + "edit_game_modal_drop_logo_image_here": "Drop logo image here", + "edit_game_modal_drop_hero_image_here": "Drop hero image here", + "edit_game_modal_drop_to_replace_icon": "Drop to replace icon", + "edit_game_modal_drop_to_replace_logo": "Drop to replace logo", + "edit_game_modal_drop_to_replace_hero": "Drop to replace hero" }, "header": { "search": "Search games", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 03413554..8992a4a0 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -67,7 +67,14 @@ "edit_game_modal_image_filter": "Изображение", "edit_game_modal_icon_resolution": "Рекомендуемое разрешение: 256x256px", "edit_game_modal_logo_resolution": "Рекомендуемое разрешение: 640x360px", - "edit_game_modal_hero_resolution": "Рекомендуемое разрешение: 1920x620px" + "edit_game_modal_hero_resolution": "Рекомендуемое разрешение: 1920x620px", + "edit_game_modal_assets": "Ресурсы", + "edit_game_modal_drop_icon_image_here": "Перетащите изображение иконки сюда", + "edit_game_modal_drop_logo_image_here": "Перетащите изображение логотипа сюда", + "edit_game_modal_drop_hero_image_here": "Перетащите изображение обложки сюда", + "edit_game_modal_drop_to_replace_icon": "Перетащите для замены иконки", + "edit_game_modal_drop_to_replace_logo": "Перетащите для замены логотипа", + "edit_game_modal_drop_to_replace_hero": "Перетащите для замены обложки" }, "header": { "search": "Поиск", diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 691dfa7c..a31ce400 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -587,7 +587,7 @@ export function EditGameModal({ /> {isDragOver && (
    - Drop to replace {assetType} + {t(`edit_game_modal_drop_to_replace_${assetType}`)}
    )} @@ -610,7 +610,7 @@ export function EditGameModal({ >
    - Drop {assetType} image here + {t(`edit_game_modal_drop_${assetType}_image_here`)}
    )} From 37db88f48fbd9a03c4afff1104c0ee228ddbb8ee Mon Sep 17 00:00:00 2001 From: "Kiwo.2" <96331770+Stormm232@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:44:15 +0200 Subject: [PATCH 50/52] Fix: "catalogue": { was duplicated at game_details --- src/locales/hu/translation.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index faff680d..37ddb78f 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -33,7 +33,7 @@ "custom_game_modal": "Saját játék hozzáadása:", "custom_game_modal_description": "Adj meg egy futtatható fájlt", "custom_game_modal_executable_path": "A fájl útvonala", - "custom_game_modal_select_executable": "Futtatható fájl helye", + "custom_game_modal_select_executable": "Az útvonal", "custom_game_modal_title": "Játékcím", "custom_game_modal_enter_title": "Játék elnevezése", "custom_game_modal_browse": "Tallózás", @@ -103,7 +103,6 @@ "filter_count": "{{filterCount}} elérhető", "clear_filters": "{{filterCount}} kiválaszott szűrő törlése" }, - "catalogue": {}, "game_details": { "open_download_options": "Letöltési opciók megnyitása", "download_options_zero": "Nincs letöltési opció", From e3cd596fb2ed98e18cf492d7fa85b0a0e321233a Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Mariano Garcia Pereira <69264602+caduHD4@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:46:16 -0300 Subject: [PATCH 51/52] Fix "," for 'no_repacks_found' key --- src/locales/pt-BR/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 6dac122f..b361dba1 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -236,7 +236,7 @@ "caption": "Legenda", "audio": "Áudio", "filter_by_source": "Filtrar por fonte", - "no_repacks_found": "Nenhuma fonte encontrada para este jogo" + "no_repacks_found": "Nenhuma fonte encontrada para este jogo", "edit_game_modal_button": "Alterar detalhes do jogo", "game_added_to_pinned": "Jogo adicionado aos fixados", "game_removed_from_pinned": "Jogo removido dos fixados", From 79498abdb5a138f3099aca2c9e5b524a16a9ebfe Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 30 Sep 2025 20:37:44 +0100 Subject: [PATCH 52/52] feat: improving ui for download source filter --- src/main/events/catalogue/get-game-stats.ts | 8 ++-- .../events/catalogue/get-how-long-to-beat.ts | 7 +-- .../events/catalogue/get-trending-games.ts | 2 +- .../events/library/create-steam-shortcut.ts | 2 +- .../events/torrenting/start-game-download.ts | 11 ++--- .../achievements/get-game-achievement-data.ts | 4 +- .../services/ws/events/friend-game-session.ts | 4 +- .../game-details/modals/repacks-modal.scss | 47 ++++++++++--------- .../game-details/modals/repacks-modal.tsx | 20 ++++---- .../user-library-game-card.scss | 7 --- 10 files changed, 46 insertions(+), 66 deletions(-) diff --git a/src/main/events/catalogue/get-game-stats.ts b/src/main/events/catalogue/get-game-stats.ts index 559d3a7d..b836531d 100644 --- a/src/main/events/catalogue/get-game-stats.ts +++ b/src/main/events/catalogue/get-game-stats.ts @@ -21,11 +21,9 @@ const getGameStats = async ( return cachedStats; } - return HydraApi.get( - `/games/stats`, - { objectId, shop }, - { needsAuth: false } - ).then(async (data) => { + return HydraApi.get(`/games/${shop}/${objectId}/stats`, null, { + needsAuth: false, + }).then(async (data) => { await gamesStatsCacheSublevel.put(levelKeys.game(shop, objectId), { ...data, updatedAt: Date.now(), diff --git a/src/main/events/catalogue/get-how-long-to-beat.ts b/src/main/events/catalogue/get-how-long-to-beat.ts index 2a1492ef..0d630164 100644 --- a/src/main/events/catalogue/get-how-long-to-beat.ts +++ b/src/main/events/catalogue/get-how-long-to-beat.ts @@ -8,12 +8,7 @@ const getHowLongToBeat = async ( objectId: string, shop: GameShop ): Promise => { - const params = new URLSearchParams({ - objectId, - shop, - }); - - return HydraApi.get(`/games/how-long-to-beat?${params.toString()}`, null, { + return HydraApi.get(`/games/${shop}/${objectId}/how-long-to-beat`, null, { needsAuth: false, }); }; diff --git a/src/main/events/catalogue/get-trending-games.ts b/src/main/events/catalogue/get-trending-games.ts index 98b09136..4c587f37 100644 --- a/src/main/events/catalogue/get-trending-games.ts +++ b/src/main/events/catalogue/get-trending-games.ts @@ -11,7 +11,7 @@ const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => { .then((language) => language || "en"); const trendingGames = await HydraApi.get( - "/games/featured", + "/catalogue/featured", { language }, { needsAuth: false } ).catch(() => []); diff --git a/src/main/events/library/create-steam-shortcut.ts b/src/main/events/library/create-steam-shortcut.ts index b12d503f..f83dd675 100644 --- a/src/main/events/library/create-steam-shortcut.ts +++ b/src/main/events/library/create-steam-shortcut.ts @@ -87,7 +87,7 @@ const createSteamShortcut = async ( } const { assets } = await HydraApi.get( - `/games/stats?objectId=${objectId}&shop=${shop}` + `/games/${shop}/${objectId}/stats` ); const steamUserIds = await getSteamUsersIds(); diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index 8216b519..79d55ec3 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -93,14 +93,9 @@ const startGameDownload = async ( await Promise.all([ createGame(updatedGame!).catch(() => {}), - HydraApi.post( - "/games/download", - { - objectId, - shop, - }, - { needsAuth: false } - ).catch(() => {}), + HydraApi.post(`/games/${shop}/${objectId}/download`, null, { + needsAuth: false, + }).catch(() => {}), ]); return { ok: true }; diff --git a/src/main/services/achievements/get-game-achievement-data.ts b/src/main/services/achievements/get-game-achievement-data.ts index e2b663d8..72b49bc5 100644 --- a/src/main/services/achievements/get-game-achievement-data.ts +++ b/src/main/services/achievements/get-game-achievement-data.ts @@ -45,10 +45,8 @@ export const getGameAchievementData = async ( .then((language) => language || "en"); return HydraApi.get( - "/games/achievements", + `/games/${shop}/${objectId}/achievements`, { - shop, - objectId, language, }, { diff --git a/src/main/services/ws/events/friend-game-session.ts b/src/main/services/ws/events/friend-game-session.ts index 47d8164e..67967b3c 100644 --- a/src/main/services/ws/events/friend-game-session.ts +++ b/src/main/services/ws/events/friend-game-session.ts @@ -16,9 +16,7 @@ export const friendGameSessionEvent = async (payload: FriendGameSession) => { const [friend, gameStats] = await Promise.all([ HydraApi.get(`/users/${payload.friendId}`), - HydraApi.get( - `/games/stats?objectId=${payload.objectId}&shop=steam` - ), + HydraApi.get(`/games/steam/${payload.objectId}/stats`), ]).catch(() => [null, null]); if (friend && gameStats) { diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.scss b/src/renderer/src/pages/game-details/modals/repacks-modal.scss index cecadb91..ba9778fd 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.scss +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.scss @@ -2,37 +2,33 @@ .repacks-modal { &__filter-container { - margin-bottom: 1rem; - transition: min-height 0.3s ease; - - &--drawer-open { - min-height: 250px; - } + transition: all 0.3s ease; } &__filter-top { - margin-bottom: 1rem; + margin-bottom: calc(globals.$spacing-unit * 2); display: flex; - flex-direction: column; - gap: 0.5rem; + flex-direction: row; + gap: calc(globals.$spacing-unit * 1); + align-items: center; } &__filter-toggle { - align-self: flex-start; display: flex; align-items: center; - gap: 0.5rem; + gap: calc(globals.$spacing-unit * 1); font-size: globals.$small-font-size; font-weight: 600; color: var(--color-text-secondary); - padding: 0.5rem 0.75rem; + padding: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 1.5); border-radius: 6px; transition: background-color 0.2s ease; + white-space: nowrap; } &__repacks { display: flex; - gap: globals.$spacing-unit; + gap: calc(globals.$spacing-unit * 2); flex-direction: column; } @@ -41,7 +37,7 @@ text-align: left; flex-direction: column; align-items: flex-start; - gap: globals.$spacing-unit; + gap: calc(globals.$spacing-unit * 1); color: globals.$body-color; padding: calc(globals.$spacing-unit * 2); } @@ -91,8 +87,8 @@ padding: 0; background-color: var(--color-background-light); border-radius: 8px; - margin-bottom: 1rem; - margin-top: calc(globals.$spacing-unit * 0.5); + margin-bottom: calc(globals.$spacing-unit * 2); + margin-top: calc(globals.$spacing-unit * 1); max-height: 0; overflow: hidden; transition: @@ -100,8 +96,7 @@ padding 0.3s ease; &--open { - padding: 0.75rem; - max-height: 250px; + max-height: 280px; } } @@ -117,24 +112,30 @@ &__source-grid { display: grid; grid-template-columns: repeat(3, 1fr); - gap: 0.5rem; + gap: calc(globals.$spacing-unit * 1); max-height: 200px; overflow-y: auto; overflow-x: hidden; align-items: start; - padding-right: 0.25rem; + padding: calc(globals.$spacing-unit * 0.5) calc(globals.$spacing-unit * 0.5) + calc(globals.$spacing-unit * 0.5) 0; } &__source-item { - padding: 0.35rem 0.5rem; + padding: calc(globals.$spacing-unit * 0.75) calc(globals.$spacing-unit * 1); background: var(--color-surface, rgba(0, 0, 0, 0.03)); - border: 1px solid var(--color-border); + border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 6px; display: flex; align-items: center; - min-height: auto; + min-height: calc(globals.$spacing-unit * 5); box-sizing: border-box; width: 100%; + transition: border-color 0.2s ease; + + &:hover { + border-color: rgba(255, 255, 255, 0.2); + } } &__source-item :global(.checkbox-field) { diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx index b2c55d4f..97b8b1b5 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx @@ -181,15 +181,17 @@ export function RepacksModal({ >
    - + {downloadSources.length > 0 && ( + + )}