mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
Compare commits
28 Commits
v3.7.2
...
release/3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51c4e4f5b3 | ||
|
|
c71f5947ba | ||
|
|
1af69465c1 | ||
|
|
a83a96f214 | ||
|
|
aadbda770b | ||
|
|
bd059cc7fa | ||
|
|
bbbf861594 | ||
|
|
80e0adcd49 | ||
|
|
aadf648a2b | ||
|
|
87dbd548d0 | ||
|
|
459bf73121 | ||
|
|
a2ef0f304d | ||
|
|
b04561986e | ||
|
|
1bd88e6c6e | ||
|
|
4ff8dc4fa7 | ||
|
|
dcc671f999 | ||
|
|
6e76111e23 | ||
|
|
3fce26f1f7 | ||
|
|
90c5ccb796 | ||
|
|
41092c2dd4 | ||
|
|
6383b728bc | ||
|
|
4dd28bbbf1 | ||
|
|
21074322fa | ||
|
|
0e7e53478a | ||
|
|
65e49550ad | ||
|
|
0990951183 | ||
|
|
53c162f0e4 | ||
|
|
8203399eda |
@@ -27,3 +27,11 @@
|
||||
- Follow TypeScript strict mode conventions
|
||||
- Use async/await instead of promises when possible
|
||||
- Prefer named exports over default exports for utilities and services
|
||||
|
||||
## Comments
|
||||
|
||||
- Keep comments concise and purposeful; avoid verbose explanations.
|
||||
- Focus on the "why" or non-obvious context, not restating the code.
|
||||
- Prefer self-explanatory naming and structure over excessive comments.
|
||||
- Do not comment every line or obvious behavior; remove stale comments.
|
||||
- Use docblocks only where they add value (public APIs, complex logic).
|
||||
|
||||
4
.github/workflows/update-aur.yml
vendored
4
.github/workflows/update-aur.yml
vendored
@@ -98,6 +98,7 @@ jobs:
|
||||
# Update pkgver in PKGBUILD
|
||||
cd hydra-launcher-bin
|
||||
NEW_VERSION="${{ steps.get-version.outputs.version }}"
|
||||
NEW_VERSION="${NEW_VERSION#v}"
|
||||
|
||||
echo "Updating PKGBUILD pkgver to $NEW_VERSION"
|
||||
|
||||
@@ -137,6 +138,9 @@ jobs:
|
||||
COMMIT_MSG="v${{ steps.get-version.outputs.version }}"
|
||||
|
||||
git commit -m "$COMMIT_MSG"
|
||||
|
||||
export GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa -F ~/.ssh/config -o UserKnownHostsFile=$SSH_PATH/known_hosts"
|
||||
|
||||
git push origin master
|
||||
echo "Successfully updated AUR package to version ${{ steps.get-version.outputs.version }}"
|
||||
fi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydralauncher",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"description": "Hydra",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Los Broxas",
|
||||
|
||||
@@ -541,7 +541,9 @@
|
||||
"notification_preview": "Probar notificación de logro",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "Los servicios Debrid son descargadores premium sin restricciones que te dejan descargar más rápido archivos alojados en servicios de alojamiento siendo que la única limitación es tu velocidad de internet.",
|
||||
"enable_friend_start_game_notifications": "Cuando un amigo está jugando un juego"
|
||||
"enable_friend_start_game_notifications": "Cuando un amigo está jugando un juego",
|
||||
"autoplay_trailers_on_game_page": "Reproducir trailers automáticamente en la página del juego",
|
||||
"hide_to_tray_on_game_start": "Ocultar Hydra en la bandeja al iniciar un juego"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Descarga completada",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"no_results": "Nincs találat",
|
||||
"start_typing": "Kereséshez gépelj...",
|
||||
"hot": "Most felkapott",
|
||||
"weekly": "📅 A hét felkapott játékai",
|
||||
"weekly": "📅 A hét felkapottjai",
|
||||
"achievements": "🏆 Achievement támogatott"
|
||||
},
|
||||
"sidebar": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"sign_in": "Bejelentkezés",
|
||||
"friends": "Barátok",
|
||||
"need_help": "Elakadtál?",
|
||||
"favorites": "Kedvenc játékok",
|
||||
"favorites": "Kedvenc Játékaim",
|
||||
"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",
|
||||
@@ -224,7 +224,7 @@
|
||||
"show_less": "Mutass kevesebbet",
|
||||
"reviews": "Vélemények",
|
||||
"leave_a_review": "Hagyd itt a véleményed",
|
||||
"write_review_placeholder": "Oszd meg a gondolataid a játékról...",
|
||||
"write_review_placeholder": "Oszd meg gondolatod a játékról...",
|
||||
"sort_newest": "Legújabb",
|
||||
"no_reviews_yet": "Még nem lett vélemény megosztva",
|
||||
"be_first_to_review": "Légy az első, aki megossza a véleményét a játékról!",
|
||||
@@ -252,7 +252,7 @@
|
||||
"you_seemed_to_enjoy_this_game": "Úgy látszik élvezted ezt a játékot",
|
||||
"would_you_recommend_this_game": "Szeretnél véleményt írni erről a játékról?",
|
||||
"yes": "Igen",
|
||||
"maybe_later": "Talán Később",
|
||||
"maybe_later": "Talán később",
|
||||
"cloud_save": "Mentés felhőben",
|
||||
"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",
|
||||
@@ -356,13 +356,18 @@
|
||||
"delete_review_modal_title": "Biztos vagy abban hogy törölni szeretnéd a véleményed?",
|
||||
"delete_review_modal_description": "Ez a lépés nem vonható vissza.",
|
||||
"delete_review_modal_delete_button": "Törlés",
|
||||
"delete_review_modal_cancel_button": "Mégse"
|
||||
"delete_review_modal_cancel_button": "Mégse",
|
||||
"vote_failed": "A szavazatod nem regisztrálódott. Kérlek próbáld újra.",
|
||||
"show_original": "Eredeti megjelenítése",
|
||||
"show_translation": "Fordítás megjelenítése",
|
||||
"show_original_translated_from": "Eredeti megjelenítése (fordítva: {{language}})",
|
||||
"hide_original": "Eredeti elrejtése"
|
||||
},
|
||||
"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 hol kérdezz efelől, akkor nem kéne ilyened legyen.",
|
||||
"message": "Ha nem tudod merre kérdezz efelől, akkor nem kéne ilyened legyen.",
|
||||
"activate": "Aktiválás",
|
||||
"loading": "Töltés…"
|
||||
},
|
||||
@@ -386,7 +391,7 @@
|
||||
"download_in_progress": "Folyamatban lévő",
|
||||
"queued_downloads": "Várakozósoron lévő letöltések",
|
||||
"downloads_completed": "Befejezett",
|
||||
"queued": "Várakozási sorban",
|
||||
"queued": "Várakozásban",
|
||||
"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…",
|
||||
@@ -419,20 +424,30 @@
|
||||
"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. Az URL forrásnak 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",
|
||||
"adding": "Hozzáadás…",
|
||||
"failed_add_download_source": "Letöltési forrás hozzáadása sikertelen. Kérlek próbáld újra.",
|
||||
"download_source_already_exists": "Ez a letöltési forrás URL már létezik.",
|
||||
"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:",
|
||||
"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",
|
||||
"download_source_pending_matching": "Frissítés hamarosan",
|
||||
"download_source_matched": "Naprakész",
|
||||
"download_source_matching": "Frissítés..",
|
||||
"download_source_failed": "Hiba",
|
||||
"download_source_no_information": "Nincs elérhető információ",
|
||||
"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",
|
||||
"removed_all_download_sources": "Összes letöltési forrás eltávolítva",
|
||||
"download_sources_synced_successfully": "Az összes letöltési forrás szinkronizálva",
|
||||
"cancel_button_confirmation_delete_all_sources": "Nem",
|
||||
"confirm_button_confirmation_delete_all_sources": "Igen, törölj mindent",
|
||||
"title_confirmation_delete_all_sources": "Az összes letöltési forrás törlése",
|
||||
@@ -445,6 +460,7 @@
|
||||
"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",
|
||||
"importing": "Importálás...",
|
||||
"public": "Publikus",
|
||||
"private": "Privát",
|
||||
"friends_only": "Csak barátok",
|
||||
@@ -462,6 +478,7 @@
|
||||
"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",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"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",
|
||||
@@ -498,14 +515,14 @@
|
||||
"cancel": "Mégsem",
|
||||
"appearance": "Megjelenés",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "A Debrid szolgáltatások prémium szolgáltatások amelyek lehetővé teszik, hogy gyorsan letölts különböző fájltároló szolgáltatásokon tárolt fájlokat, csak az internet sebességed szab határt.",
|
||||
"debrid_description": "A Debrid szolgáltatások prémium szolgáltatások amelyek lehetővé teszik, hogy gyorsan letölts különböző fájltároló szolgáltatásokon tárolt fájlokat, és csak az internet sebességed szab határt.",
|
||||
"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",
|
||||
"name_min_length": "A téma neve legalább 3 karakter hosszú kell 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",
|
||||
@@ -535,7 +552,9 @@
|
||||
"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"
|
||||
"enable_friend_start_game_notifications": "Amikor egy barátod elkezd játszani egy játékot",
|
||||
"autoplay_trailers_on_game_page": "Játékelőzetes automatikus lejátszása a játék oldalán",
|
||||
"hide_to_tray_on_game_start": "Hydra elrejtése játék elindításakor a tálcára"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Letöltés befejezve",
|
||||
@@ -563,10 +582,10 @@
|
||||
"available_one": "Elérhető",
|
||||
"available_other": "Elérhető",
|
||||
"no_downloads": "Nincs elérhető letöltés",
|
||||
"calculating": "Feldolgozás"
|
||||
"calculating": "Számítás alatt.."
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "A programok nincsenek telepítve",
|
||||
"title": "Hiányzó programok",
|
||||
"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ódra, hogy a játék megfelelően fusson"
|
||||
},
|
||||
@@ -585,6 +604,7 @@
|
||||
"activity": "Legutóbbi tevékenység",
|
||||
"library": "Könyvtár",
|
||||
"pinned": "Kitűzve",
|
||||
"sort_by": "Rendezés:",
|
||||
"achievements_earned": "Elért achievementek",
|
||||
"played_recently": "Nemrég játszva",
|
||||
"playtime": "Játszottidő",
|
||||
@@ -654,7 +674,7 @@
|
||||
"uploading_banner": "Borítókép feltöltése…",
|
||||
"background_image_updated": "Borítókép frissítve",
|
||||
"stats": "Statisztikák",
|
||||
"achievements": "achievementek",
|
||||
"achievements": "achievement",
|
||||
"games": "Játékok",
|
||||
"top_percentile": "Top {{percentile}}%",
|
||||
"ranking_updated_weekly": "A rangsor hetente frissül.",
|
||||
@@ -669,7 +689,7 @@
|
||||
"game_added_to_pinned": "Játék hozzáadva a kitűzöttekhez",
|
||||
"karma": "Karma",
|
||||
"karma_count": "karma",
|
||||
"karma_description": "Pozitív értékelésekre kapott pontok alapján"
|
||||
"karma_description": "Pozitív értékelésekkel szerzett pontok"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Achievement feloldva",
|
||||
@@ -678,7 +698,7 @@
|
||||
"unlocked_at": "Feloldva: {{date}}",
|
||||
"subscription_needed": "A tartalom megtekintéséhez Hydra Cloud előfizetés szükséges",
|
||||
"new_achievements_unlocked": "{{achievementCount}} új achievement feloldva {{gameCount}} játékban",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementek",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievement",
|
||||
"achievements_unlocked_for_game": "{{achievementCount}} új achievement feloldva itt: {{gameTitle}}",
|
||||
"hidden_achievement_tooltip": "Ez egy rejtett achievement",
|
||||
"achievement_earn_points": "Szerezz be {{points}} pontot ezzel az achievement-el",
|
||||
|
||||
@@ -538,7 +538,9 @@
|
||||
"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",
|
||||
"autoplay_trailers_on_game_page": "Reproduzir trailers automaticamente na página do jogo",
|
||||
"hide_to_tray_on_game_start": "Ocultar o Hydra na bandeja ao iniciar um jogo"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download concluído",
|
||||
|
||||
@@ -212,6 +212,7 @@
|
||||
"stats": "Статистика",
|
||||
"download_count": "Загрузки",
|
||||
"player_count": "Активные игроки",
|
||||
"rating_count": "Оценка",
|
||||
"download_error": "Этот вариант загрузки недоступен",
|
||||
"download": "Скачать",
|
||||
"executable_path_in_use": "Исполняемый файл уже используется \"{{game}}\"",
|
||||
@@ -252,17 +253,6 @@
|
||||
"would_you_recommend_this_game": "Хотите оставить отзыв об этой игре?",
|
||||
"yes": "Да",
|
||||
"maybe_later": "Возможно позже",
|
||||
"rating_count": "Оценка",
|
||||
"delete_review": "Удалить отзыв",
|
||||
"remove_review": "Удалить отзыв",
|
||||
"delete_review_modal_title": "Вы уверены, что хотите удалить свой отзыв?",
|
||||
"delete_review_modal_description": "Это действие нельзя отменить.",
|
||||
"delete_review_modal_delete_button": "Удалить",
|
||||
"delete_review_modal_cancel_button": "Отмена",
|
||||
"show_original": "Показать оригинал",
|
||||
"show_translation": "Показать перевод",
|
||||
"show_original_translated_from": "Показать оригинал (переведено с {{language}})",
|
||||
"hide_original": "Скрыть оригинал",
|
||||
"cloud_save": "Облачное сохранение",
|
||||
"cloud_save_description": "Сохраняйте ваш прогресс в облаке и продолжайте играть на любом устройстве",
|
||||
"backups": "Резервные копии",
|
||||
@@ -360,7 +350,18 @@
|
||||
"caption": "Субтитры",
|
||||
"audio": "Аудио",
|
||||
"filter_by_source": "Фильтр по источнику",
|
||||
"no_repacks_found": "Источники для этой игры не найдены"
|
||||
"no_repacks_found": "Источники для этой игры не найдены",
|
||||
"delete_review": "Удалить отзыв",
|
||||
"remove_review": "Удалить отзыв",
|
||||
"delete_review_modal_title": "Вы уверены, что хотите удалить свой отзыв?",
|
||||
"delete_review_modal_description": "Это действие нельзя отменить.",
|
||||
"delete_review_modal_delete_button": "Удалить",
|
||||
"delete_review_modal_cancel_button": "Отмена",
|
||||
"vote_failed": "Не удалось зарегистрировать ваш голос. Пожалуйста, попробуйте снова.",
|
||||
"show_original": "Показать оригинал",
|
||||
"show_translation": "Показать перевод",
|
||||
"show_original_translated_from": "Показать оригинал (переведено с {{language}})",
|
||||
"hide_original": "Скрыть оригинал"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Активировать Hydra",
|
||||
@@ -427,6 +428,9 @@
|
||||
"validate_download_source": "Проверить",
|
||||
"remove_download_source": "Удалить",
|
||||
"add_download_source": "Добавить источник",
|
||||
"adding": "Добавление…",
|
||||
"failed_add_download_source": "Не удалось добавить источник. Пожалуйста, попробуйте снова.",
|
||||
"download_source_already_exists": "Этот URL источника уже существует.",
|
||||
"download_count_zero": "В списке нет загрузок",
|
||||
"download_count_one": "{{countFormatted}} загрузка в списке",
|
||||
"download_count_other": "{{countFormatted}} загрузок в списке",
|
||||
@@ -434,9 +438,16 @@
|
||||
"add_download_source_description": "Вставьте ссылку на .json-файл",
|
||||
"download_source_up_to_date": "Обновлён",
|
||||
"download_source_errored": "Ошибка",
|
||||
"download_source_pending_matching": "Скоро обновится",
|
||||
"download_source_matched": "Обновлен",
|
||||
"download_source_matching": "Обновление",
|
||||
"download_source_failed": "Ошибка",
|
||||
"download_source_no_information": "Информация отсутствует",
|
||||
"sync_download_sources": "Обновить источники",
|
||||
"removed_download_source": "Источник удален",
|
||||
"removed_download_sources": "Источники удалены",
|
||||
"removed_all_download_sources": "Все источники удалены",
|
||||
"download_sources_synced_successfully": "Все источники синхронизированы",
|
||||
"cancel_button_confirmation_delete_all_sources": "Нет",
|
||||
"confirm_button_confirmation_delete_all_sources": "Да, удалить все",
|
||||
"title_confirmation_delete_all_sources": "Удалить все источники",
|
||||
@@ -467,6 +478,7 @@
|
||||
"seed_after_download_complete": "Раздавать после завершения загрузки",
|
||||
"show_hidden_achievement_description": "Показывать описание скрытых достижений перед их получением",
|
||||
"account": "Аккаунт",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"no_users_blocked": "У вас нет заблокированных пользователей",
|
||||
"subscription_active_until": "Ваша подписка на Hydra Cloud активна до {{date}}",
|
||||
"manage_subscription": "Управлять подпиской",
|
||||
@@ -540,7 +552,9 @@
|
||||
"hidden": "Скрытый",
|
||||
"test_notification": "Тестовое уведомление",
|
||||
"notification_preview": "Предварительный просмотр уведомления о достижении",
|
||||
"enable_friend_start_game_notifications": "Когда друг начинает играть в игру"
|
||||
"enable_friend_start_game_notifications": "Когда друг начинает играть в игру",
|
||||
"autoplay_trailers_on_game_page": "Автоматически начинать воспроизведение трейлеров на странице игры",
|
||||
"hide_to_tray_on_game_start": "Скрывать Hydra в трей при запуске игры"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Загрузка завершена",
|
||||
@@ -590,6 +604,7 @@
|
||||
"activity": "Недавняя активность",
|
||||
"library": "Библиотека",
|
||||
"pinned": "Закрепленные",
|
||||
"sort_by": "Сортировать по:",
|
||||
"achievements_earned": "Заработанные достижения",
|
||||
"played_recently": "Недавно сыгранные",
|
||||
"playtime": "Время игры",
|
||||
|
||||
@@ -167,6 +167,8 @@ export class AchievementWatcherManager {
|
||||
shop: GameShop,
|
||||
objectId: string
|
||||
) {
|
||||
if (shop === "custom") return;
|
||||
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
if (this.alreadySyncedGames.get(gameKey)) return;
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ import { HydraApi } from "../hydra-api";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
|
||||
export const createGame = async (game: Game) => {
|
||||
if (game.shop === "custom") {
|
||||
return;
|
||||
}
|
||||
|
||||
return HydraApi.post(`/profile/games`, {
|
||||
objectId: game.objectId,
|
||||
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds ?? 0),
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import type { Game } from "@types";
|
||||
import { HydraApi } from "../hydra-api";
|
||||
|
||||
export const updateGamePlaytime = async (
|
||||
export const trackGamePlaytime = async (
|
||||
game: Game,
|
||||
deltaInMillis: number,
|
||||
lastTimePlayed: Date
|
||||
) => {
|
||||
return HydraApi.put(`/profile/games/${game.remoteId}`, {
|
||||
if (game.shop === "custom") {
|
||||
return;
|
||||
}
|
||||
|
||||
return HydraApi.put(`/profile/games/${game.shop}/${game.objectId}`, {
|
||||
playTimeDeltaInSeconds: Math.trunc(deltaInMillis / 1000),
|
||||
lastTimePlayed,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { createGame, updateGamePlaytime } from "./library-sync";
|
||||
import { createGame, trackGamePlaytime } from "./library-sync";
|
||||
import type { Game, GameRunning, UserPreferences } from "@types";
|
||||
import { PythonRPC } from "./python-rpc";
|
||||
import axios from "axios";
|
||||
@@ -198,11 +198,6 @@ export const watchProcesses = async () => {
|
||||
function onOpenGame(game: Game) {
|
||||
const now = performance.now();
|
||||
|
||||
AchievementWatcherManager.firstSyncWithRemoteIfNeeded(
|
||||
game.shop,
|
||||
game.objectId
|
||||
);
|
||||
|
||||
gamesPlaytime.set(levelKeys.game(game.shop, game.objectId), {
|
||||
lastTick: now,
|
||||
firstTick: now,
|
||||
@@ -220,8 +215,15 @@ function onOpenGame(game: Game) {
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
if (game.shop === "custom") return;
|
||||
|
||||
AchievementWatcherManager.firstSyncWithRemoteIfNeeded(
|
||||
game.shop,
|
||||
game.objectId
|
||||
);
|
||||
|
||||
if (game.remoteId) {
|
||||
updateGamePlaytime(
|
||||
trackGamePlaytime(
|
||||
game,
|
||||
game.unsyncedDeltaPlayTimeInMilliseconds ?? 0,
|
||||
new Date()
|
||||
@@ -255,43 +257,46 @@ function onTickGame(game: Game) {
|
||||
|
||||
const delta = now - gamePlaytime.lastTick;
|
||||
|
||||
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
|
||||
const updatedGame: Game = {
|
||||
...game,
|
||||
playTimeInMilliseconds: (game.playTimeInMilliseconds ?? 0) + delta,
|
||||
lastTimePlayed: new Date(),
|
||||
});
|
||||
};
|
||||
|
||||
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), updatedGame);
|
||||
|
||||
gamesPlaytime.set(levelKeys.game(game.shop, game.objectId), {
|
||||
...gamePlaytime,
|
||||
lastTick: now,
|
||||
});
|
||||
|
||||
if (currentTick % TICKS_TO_UPDATE_API === 0) {
|
||||
if (currentTick % TICKS_TO_UPDATE_API === 0 && game.shop !== "custom") {
|
||||
const deltaToSync =
|
||||
now -
|
||||
gamePlaytime.lastSyncTick +
|
||||
(game.unsyncedDeltaPlayTimeInMilliseconds ?? 0);
|
||||
|
||||
const gamePromise = game.remoteId
|
||||
? updateGamePlaytime(game, deltaToSync, game.lastTimePlayed!)
|
||||
? trackGamePlaytime(game, deltaToSync, game.lastTimePlayed!)
|
||||
: createGame(game);
|
||||
|
||||
gamePromise
|
||||
.then(() => {
|
||||
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
|
||||
...game,
|
||||
...updatedGame,
|
||||
unsyncedDeltaPlayTimeInMilliseconds: 0,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
|
||||
...game,
|
||||
...updatedGame,
|
||||
unsyncedDeltaPlayTimeInMilliseconds: deltaToSync,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
gamesPlaytime.set(levelKeys.game(game.shop, game.objectId), {
|
||||
...gamePlaytime,
|
||||
lastTick: now,
|
||||
lastSyncTick: now,
|
||||
});
|
||||
});
|
||||
@@ -299,11 +304,24 @@ function onTickGame(game: Game) {
|
||||
}
|
||||
|
||||
const onCloseGame = (game: Game) => {
|
||||
const now = performance.now();
|
||||
const gamePlaytime = gamesPlaytime.get(
|
||||
levelKeys.game(game.shop, game.objectId)
|
||||
)!;
|
||||
gamesPlaytime.delete(levelKeys.game(game.shop, game.objectId));
|
||||
|
||||
const delta = now - gamePlaytime.lastTick;
|
||||
|
||||
const updatedGame: Game = {
|
||||
...game,
|
||||
playTimeInMilliseconds: (game.playTimeInMilliseconds ?? 0) + delta,
|
||||
lastTimePlayed: new Date(),
|
||||
};
|
||||
|
||||
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), updatedGame);
|
||||
|
||||
if (game.shop === "custom") return;
|
||||
|
||||
if (game.remoteId) {
|
||||
if (game.automaticCloudSync) {
|
||||
CloudSync.uploadSaveGame(
|
||||
@@ -315,20 +333,20 @@ const onCloseGame = (game: Game) => {
|
||||
}
|
||||
|
||||
const deltaToSync =
|
||||
performance.now() -
|
||||
now -
|
||||
gamePlaytime.lastSyncTick +
|
||||
(game.unsyncedDeltaPlayTimeInMilliseconds ?? 0);
|
||||
|
||||
return updateGamePlaytime(game, deltaToSync, game.lastTimePlayed!)
|
||||
return trackGamePlaytime(game, deltaToSync, game.lastTimePlayed!)
|
||||
.then(() => {
|
||||
return gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
|
||||
...game,
|
||||
...updatedGame,
|
||||
unsyncedDeltaPlayTimeInMilliseconds: 0,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
return gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
|
||||
...game,
|
||||
...updatedGame,
|
||||
unsyncedDeltaPlayTimeInMilliseconds: deltaToSync,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -289,12 +289,6 @@ export class WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static loadNotificationWindowURL() {
|
||||
if (this.notificationWindow) {
|
||||
this.loadWindowURL(this.notificationWindow, "achievement-notification");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly NOTIFICATION_WINDOW_WIDTH = 360;
|
||||
private static readonly NOTIFICATION_WINDOW_HEIGHT = 140;
|
||||
|
||||
@@ -387,7 +381,7 @@ export class WindowManager {
|
||||
this.notificationWindow.setIgnoreMouseEvents(true);
|
||||
|
||||
this.notificationWindow.setAlwaysOnTop(true, "screen-saver", 1);
|
||||
this.loadNotificationWindowURL();
|
||||
this.loadWindowURL(this.notificationWindow, "achievement-notification");
|
||||
|
||||
if (!app.isPackaged || isStaging) {
|
||||
this.notificationWindow.webContents.openDevTools();
|
||||
|
||||
@@ -293,6 +293,8 @@ export function GameDetailsContextProvider({
|
||||
}, [objectId, shop, userDetails]);
|
||||
|
||||
useEffect(() => {
|
||||
if (shop === "custom") return;
|
||||
|
||||
const fetchDownloadSources = async () => {
|
||||
try {
|
||||
const sources = await window.electron.getDownloadSources();
|
||||
|
||||
@@ -29,9 +29,11 @@ function JumpControl({
|
||||
return isOpen ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="number"
|
||||
type="text"
|
||||
min={1}
|
||||
max={totalPages}
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]*"
|
||||
className="pagination__page-input"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
@@ -56,7 +58,7 @@ export function Pagination({
|
||||
page,
|
||||
totalPages,
|
||||
onPageChange,
|
||||
}: PaginationProps) {
|
||||
}: Readonly<PaginationProps>) {
|
||||
const { formatNumber } = useFormat();
|
||||
|
||||
const [isJumpOpen, setIsJumpOpen] = useState(false);
|
||||
@@ -87,13 +89,15 @@ export function Pagination({
|
||||
}
|
||||
|
||||
const onJumpChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const val = e.target.value;
|
||||
if (val === "") {
|
||||
const raw = e.target.value;
|
||||
const digitsOnly = raw.replaceAll(/\D+/g, "");
|
||||
if (digitsOnly === "") {
|
||||
setJumpValue("");
|
||||
return;
|
||||
}
|
||||
const num = Number(val);
|
||||
const num = Number.parseInt(digitsOnly, 10);
|
||||
if (Number.isNaN(num)) {
|
||||
setJumpValue("");
|
||||
return;
|
||||
}
|
||||
if (num < 1) {
|
||||
@@ -104,19 +108,36 @@ export function Pagination({
|
||||
setJumpValue(String(totalPages));
|
||||
return;
|
||||
}
|
||||
setJumpValue(val);
|
||||
setJumpValue(String(num));
|
||||
};
|
||||
|
||||
const onJumpKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
const controlKeys = [
|
||||
"Backspace",
|
||||
"Delete",
|
||||
"Tab",
|
||||
"ArrowLeft",
|
||||
"ArrowRight",
|
||||
"Home",
|
||||
"End",
|
||||
];
|
||||
|
||||
if (controlKeys.includes(e.key) || e.ctrlKey || e.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === "Enter") {
|
||||
if (jumpValue.trim() === "") return;
|
||||
const parsed = Number(jumpValue);
|
||||
const sanitized = jumpValue.replaceAll(/\D+/g, "");
|
||||
if (sanitized.trim() === "") return;
|
||||
const parsed = Number.parseInt(sanitized, 10);
|
||||
if (Number.isNaN(parsed)) return;
|
||||
const target = Math.max(1, Math.min(totalPages, parsed));
|
||||
onPageChange(target);
|
||||
setIsJumpOpen(false);
|
||||
} else if (e.key === "Escape") {
|
||||
setIsJumpOpen(false);
|
||||
} else if (!/^\d$/.test(e.key)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export function SettingsDownloadSources() {
|
||||
try {
|
||||
await window.electron.removeDownloadSource(false, downloadSource.id);
|
||||
const sources = await window.electron.getDownloadSources();
|
||||
setDownloadSources(sources as DownloadSource[]);
|
||||
setDownloadSources(sources);
|
||||
showSuccessToast(t("removed_download_source"));
|
||||
} catch (error) {
|
||||
logger.error("Failed to remove download source:", error);
|
||||
@@ -104,7 +104,7 @@ export function SettingsDownloadSources() {
|
||||
try {
|
||||
await window.electron.removeDownloadSource(true);
|
||||
const sources = await window.electron.getDownloadSources();
|
||||
setDownloadSources(sources as DownloadSource[]);
|
||||
setDownloadSources(sources);
|
||||
showSuccessToast(t("removed_all_download_sources"));
|
||||
} catch (error) {
|
||||
logger.error("Failed to remove all download sources:", error);
|
||||
@@ -117,7 +117,7 @@ export function SettingsDownloadSources() {
|
||||
const handleAddDownloadSource = async () => {
|
||||
try {
|
||||
const sources = await window.electron.getDownloadSources();
|
||||
setDownloadSources(sources as DownloadSource[]);
|
||||
setDownloadSources(sources);
|
||||
} catch (error) {
|
||||
logger.error("Failed to refresh download sources:", error);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export function SettingsDownloadSources() {
|
||||
try {
|
||||
await window.electron.syncDownloadSources();
|
||||
const sources = await window.electron.getDownloadSources();
|
||||
setDownloadSources(sources as DownloadSource[]);
|
||||
setDownloadSources(sources);
|
||||
|
||||
showSuccessToast(t("download_sources_synced_successfully"));
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user